commit e42e547e4847db22e060fe261dcc0997a6890660 Author: github-classroom[bot] <66690702+github-classroom[bot]@users.noreply.github.com> Date: Tue Apr 25 11:33:41 2023 +0000 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6e4761 --- /dev/null +++ b/.gitignore @@ -0,0 +1,129 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..44b914e --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# Information Modelling & Analysis: Project 2 + +Student: enter your name here + +Please follow the instructions provided in the project slides +and consider the submission instructions available on iCorsi. + +For your convencience, I the following resources are available in the `resources` folder: +- **defects4j-checkout-closure-1f**: The output of the command `defects4j checkout -p Closure -v 1f -w ...` +- **modified_classes** The list of buggy classes in: `framework/projects/Closure/modified_classes/` \ No newline at end of file diff --git a/evaluate_classifiers.py b/evaluate_classifiers.py new file mode 100644 index 0000000..e69de29 diff --git a/extract_feature_vectors.py b/extract_feature_vectors.py new file mode 100644 index 0000000..e69de29 diff --git a/label_feature_vectors.py b/label_feature_vectors.py new file mode 100644 index 0000000..e69de29 diff --git a/resources/defects4j-checkout-closure-1f/.classpath b/resources/defects4j-checkout-closure-1f/.classpath new file mode 100644 index 0000000..7de8741 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/.classpath @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/defects4j-checkout-closure-1f/.defects4j.config b/resources/defects4j-checkout-closure-1f/.defects4j.config new file mode 100644 index 0000000..2c40fbc --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/.defects4j.config @@ -0,0 +1,3 @@ +#File automatically generated by Defects4J +pid=Closure +vid=1f diff --git a/resources/defects4j-checkout-closure-1f/.gitignore b/resources/defects4j-checkout-closure-1f/.gitignore new file mode 100644 index 0000000..90ec22b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/.gitignore @@ -0,0 +1 @@ +.svn diff --git a/resources/defects4j-checkout-closure-1f/.project b/resources/defects4j-checkout-closure-1f/.project new file mode 100644 index 0000000..902c4ba --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/.project @@ -0,0 +1,17 @@ + + + closure-compiler + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/resources/defects4j-checkout-closure-1f/CONTRIBUTORS b/resources/defects4j-checkout-closure-1f/CONTRIBUTORS new file mode 100644 index 0000000..b38c4c0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/CONTRIBUTORS @@ -0,0 +1,61 @@ + +All contributors to Closure Compiler must sign a CLA. + +What is a CLA? +---------------------------------------- + +A CLA (Contributor License Agreement) basically says that you own the +rights to any code you contribute, and that you give us permission to +use that code in Closure Compiler. You maintain the copyright on that +code. + +Where do I sign up? +---------------------------------------- + +If you own all the rights to your code, you can fill out an individual CLA. +http://code.google.com/legal/individual-cla-v1.0.html + +If your employer has any rights to your code, then they also need to fill +out a corporate CLA. If you don't know if your employer has any rights +to your code, you should ask before signing anything. +http://code.google.com/legal/corporate-cla-v1.0.html + + +CLA Signers +---------------------------------------- + +By default, anyone with an @google.com email address already has a CLA +signed for them. Congratulations! + +We've also received CLAs from the following people: +amattie [gmail.com] (Andrew Mattie) +ambar.lee [gmail.com] +benjamin.j.mccann [gmail.com] +cpeisert [gmail.com] (Christopher Peisert) +chadkillingsworth [missouristate.edu] (Chad Killingsworth) +dunbarb2 [gmail.com] +edo999 [gmail.com] +franck.routier [gmail.com] (Franck Routier) +guido.tapia [gmail.com] (Guido Tapia) +ichaehoi [gmail.com] (Ibrahim Chaehoi) +iliakan [gmail.com] (Ilya Kantor) +ivan.kozik [gmail.com] +jaffathecake [gmail.com] (Jake Archibald) +jamesots [gmail.com] +jhansche [myyearbook.com] (Joe Hansche) +k [ailis.de] (Klaus Reimer) +kosmo.zb [gmail.com] +leeight [gmail.com] +mattcg [gmail.com] +meacer [gmail.com] (Mustafa E. Acer) +mbolin [gmail.com] (Michael Bolin) +neaket [gmail.com] +piaohai [gmail.com] (piaohaiyao) +pimmhogeling [gmail.com] +price.c [gmail.com] (Chris Price) +rictic [gmail.com] (Peter Burns) +robert [gust-bardon.org] (Robert Gust-Bardon) +sheets [ashimaarts.com] +stenrs [gmail.com] +timwintle [gmail.com] (Tim Wintle) +yonathan [gmail.com] (Yonathan Randolph) diff --git a/resources/defects4j-checkout-closure-1f/COPYING b/resources/defects4j-checkout-closure-1f/COPYING new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/resources/defects4j-checkout-closure-1f/README b/resources/defects4j-checkout-closure-1f/README new file mode 100644 index 0000000..270cfc2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/README @@ -0,0 +1,292 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Contents +// + +The Closure Compiler performs checking, instrumentation, and +optimizations on JavaScript code. The purpose of this README is to +explain how to build and run the Closure Compiler. + +The Closure Compiler requires Java 6 or higher. +http://www.java.com/ + + +// +// Building The Closure Compiler +// + +There are three ways to get a Closure Compiler executable. + +1) Use one we built for you. + +Pre-built Closure binaries can be found at +http://code.google.com/p/closure-compiler/downloads/list + + +2) Check out the source and build it with Apache Ant. + +First, check out the full source tree of the Closure Compiler. There +are instructions on how to do this at the project site. +http://code.google.com/p/closure-compiler/source/checkout + +Apache Ant is a cross-platform build tool. +http://ant.apache.org/ + +At the root of the source tree, there is an Ant file named +build.xml. To use it, navigate to the same directory and type the +command + +ant jar + +This will produce a jar file called "build/compiler.jar". + + +3) Check out the source and build it with Eclipse. + +Eclipse is a cross-platform IDE. +http://www.eclipse.org/ + +Under Eclipse's File menu, click "New > Project ..." and create a +"Java Project." You will see an options screen. Give the project a +name, select "Create project from existing source," and choose the +root of the checked-out source tree as the existing directory. Verify +that you are using JRE version 6 or higher. + +Eclipse can use the build.xml file to discover rules. When you +navigate to the build.xml file, you will see all the build rules in +the "Outline" pane. Run the "jar" rule to build the compiler in +build/compiler.jar. + + +// +// Running The Closure Compiler +// + +Once you have the jar binary, running the Closure Compiler is straightforward. + +On the command line, type + +java -jar compiler.jar + +This starts the compiler in interactive mode. Type + +var x = 17 + 25; + +then hit "Enter", then hit "Ctrl-Z" (on Windows) or "Ctrl-D" (on Mac or Linux) +and "Enter" again. The Compiler will respond: + +var x=42; + +The Closure Compiler has many options for reading input from a file, +writing output to a file, checking your code, and running +optimizations. To learn more, type + +java -jar compiler.jar --help + +You can read more detailed documentation about the many flags at +http://code.google.com/closure/compiler/docs/gettingstarted_app.html + + +// +// Compiling Multiple Scripts +// + +If you have multiple scripts, you should compile them all together with +one compile command. + +java -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js + +The Closure Compiler will concatenate the files in the order they're +passed at the command line. + +If you need to compile many, many scripts together, you may start to +run into problems with managing dependencies between scripts. You +should check out the Closure Library. It contains functions for +enforcing dependencies between scripts, and a tool called calcdeps.py +that knows how to give scripts to the Closure Compiler in the right +order. + +http://code.google.com/p/closure-library/ + +// +// Licensing +// + +Unless otherwise stated, all source files are licensed under +the Apache License, Version 2.0. + + +----- +Code under: +src/com/google/javascript/rhino +test/com/google/javascript/rhino + +URL: http://www.mozilla.org/rhino +Version: 1.5R3, with heavy modifications +License: Netscape Public License and MPL / GPL dual license + +Description: A partial copy of Mozilla Rhino. Mozilla Rhino is an +implementation of JavaScript for the JVM. The JavaScript parser and +the parse tree data structures were extracted and modified +significantly for use by Google's JavaScript compiler. + +Local Modifications: The packages have been renamespaced. All code not +relevant to parsing has been removed. A JsDoc parser and static typing +system have been added. + + +----- +Code in: +lib/rhino + +Rhino +URL: http://www.mozilla.org/rhino +Version: Trunk +License: Netscape Public License and MPL / GPL dual license + +Description: Mozilla Rhino is an implementation of JavaScript for the JVM. + +Local Modifications: Minor changes to parsing JSDoc that usually get pushed +up-stream to Rhino trunk. + + +----- +Code in: +lib/args4j.jar + +Args4j +URL: https://args4j.dev.java.net/ +Version: 2.0.16 +License: MIT + +Description: +args4j is a small Java class library that makes it easy to parse command line +options/arguments in your CUI application. + +Local Modifications: None. + + +----- +Code in: +lib/guava.jar + +Guava Libraries +URL: http://code.google.com/p/guava-libraries/ +Version: 13.0.1 +License: Apache License 2.0 + +Description: Google's core Java libraries. + +Local Modifications: None. + + +----- +Code in: +lib/jsr305.jar + +Annotations for software defect detection +URL: http://code.google.com/p/jsr-305/ +Version: svn revision 47 +License: BSD License + +Description: Annotations for software defect detection. + +Local Modifications: None. + + +----- +Code in: +lib/jarjar.jar + +Jar Jar Links +URL: http://jarjar.googlecode.com/ +Version: 1.1 +License: Apache License 2.0 + +Description: +A utility for repackaging Java libraries. + +Local Modifications: None. + + +---- +Code in: +lib/junit.jar + +JUnit +URL: http://sourceforge.net/projects/junit/ +Version: 4.10 +License: Common Public License 1.0 + +Description: A framework for writing and running automated tests in Java. + +Local Modifications: None. + + +--- +Code in: +lib/protobuf-java.jar + +Protocol Buffers +URL: http://code.google.com/p/protobuf/ +Version: 2.4.1 +License: New BSD License + +Description: Supporting libraries for protocol buffers, +an encoding of structured data. + +Local Modifications: None + + +--- +Code in: +lib/ant.jar +lib/ant-launcher.jar + +URL: http://ant.apache.org/bindownload.cgi +Version: 1.8.1 +License: Apache License 2.0 +Description: + Ant is a Java based build tool. In theory it is kind of like "make" + without make's wrinkles and with the full portability of pure java code. + +Local Modifications: None + + +--- +Code in: +lib/json.jar +URL: http://json.org/java/index.html +Version: JSON version 20090211 +License: MIT license +Description: +JSON is a set of java files for use in transmitting data in JSON format. + +Local Modifications: None + +--- +Code in: +tools/maven-ant-tasks-2.1.3.jar +URL: http://maven.apache.org +Version 2.1.3 +License: Apache License 2.0 +Description: + Maven Ant tasks are used to manage dependencies and to install/deploy to + maven repositories. + +Local Modifications: None diff --git a/resources/defects4j-checkout-closure-1f/build.xml b/resources/defects4j-checkout-closure-1f/build.xml new file mode 100644 index 0000000..8484d7c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/build.xml @@ -0,0 +1,505 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/defects4j-checkout-closure-1f/closure-compiler.pom b/resources/defects4j-checkout-closure-1f/closure-compiler.pom new file mode 100644 index 0000000..2dc2f90 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/closure-compiler.pom @@ -0,0 +1,177 @@ + + + 4.0.0 + + com.google.javascript + closure-compiler + jar + + Closure Compiler + r@build.svnVersion@ + + http://code.google.com/p/closure-compiler/ + + Closure Compiler is a JavaScript optimizing compiler. It parses your + JavaScript, analyzes it, removes dead code and rewrites and minimizes + what's left. It also checks syntax, variable references, and types, and + warns about common JavaScript pitfalls. It is used in many of Google's + JavaScript apps, including Gmail, Google Web Search, Google Maps, and + Google Docs. + + 2009 + + + + scm:svn:http://closure-compiler.googlecode.com/svn/trunk + + + scm:svn:https://closure-compiler.googlecode.com/svn/trunk + + + http://code.google.com/p/closure-compiler/source/browse/#svn/trunk + + + + + code.google.com + http://code.google.com/p/closure-compiler/issues + + + + Google + http://www.google.com + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.html + repo + + + + + + central + Maven Repository Switchboard + default + http://repo1.maven.org/maven2 + + false + + + + caja + http://google-caja.googlecode.com/svn/maven + + + + + + johnlenz + John Lenz + concavelenz@gmail.com + + + nicksantos + Nick Santos + nicholas.j.santos@gmail.com + + + + acleung + Alan Leung + acleung@gmail.com + + + + mbolin + Michael Bolin + mbolin@alum.mit.edu + + + + plindner + Paul Lindner + lindner@inuus.com + + + + + + args4j + args4j + 2.0.16 + + + + com.google.guava + guava + 13.0.1 + + + + com.google.protobuf + protobuf-java + 2.4.1 + + + + org.json + json + 20090211 + + + + org.apache.ant + ant + 1.8.2 + compile + + + + com.google.code.findbugs + jsr305 + 1.3.9 + + + + com.googlecode.jarjar + jarjar + 1.1 + compile + + + + junit + junit + 4.10 + test + + + + caja + caja + r4939 + test + + + + diff --git a/resources/defects4j-checkout-closure-1f/contrib/externs/angular.js b/resources/defects4j-checkout-closure-1f/contrib/externs/angular.js new file mode 100644 index 0000000..78904de --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/contrib/externs/angular.js @@ -0,0 +1,1460 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Externs for Angular 1. + * + * TODO: Mocks. + * TODO: Remaining Services: + * $compileProvider + * $controller + * $controllerProvider + * $cookies + * $cookieStore + * $document + * $exceptionHandler + * $filter + * $filterProvider + * $httpBackend + * $interpolate + * $locale + * $parse + * $resource + * $rootElement + * $rootScope + * $rootScopeProvider + * $routeParams + * $sanitize + * $templateCache + * $window + * TODO: Resolve two issues with angular.$http + * 1) angular.$http cannot be declared as a callable type. + * Its helper methods should be used instead. + * 2) angular.$http.delete cannot be added as an extern + * as it is a reserved keyword. + * Its use is potentially not supported in IE. + * It may be aliased as 'remove' in a future version. + * + * @see http://angularjs.org/ + * @externs + */ + +/** + * @typedef {(Window|Document|Element|Array.|string|angular.JQLite| + * NodeList|{length: number})} + */ +var JQLiteSelector; + +/** + * @type {Object} + * @const + */ +var angular = {}; + +/** + * @param {Object} self + * @param {Function} fn + * @param {...*} args + * @return {Function} + */ +angular.bind = function(self, fn, args) {}; + +/** + * @param {Element} element + * @param {Array.=} opt_modules + * @return {function()} + */ +angular.bootstrap = function(element, opt_modules) {}; + +/** + * @param {*} source + * @param {(Object|Array)=} opt_dest + * @return {*} + */ +angular.copy = function(source, opt_dest) {}; + +/** + * @param {(JQLiteSelector|Object)} element + * @param {(JQLiteSelector|Object)=} opt_context + * @return {Object} + */ +angular.element = function(element, opt_context) {}; + +/** + * @param {*} o1 + * @param {*} o2 + * @return {boolean} + */ +angular.equals = function(o1, o2) {}; + +/** + * @param {Object} dest + * @param {...Object} srcs + */ +angular.extend = function(dest, srcs) {}; + +/** + * @param {Object|Array} obj + * @param {Function} iterator + * @param {Object=} opt_context + * @return {Object|Array} + */ +angular.forEach = function(obj, iterator, opt_context) {}; + +/** + * @param {string} json + * @return {Object|Array|Date|string|number} + */ +angular.fromJson = function(json) {}; + +/** + * @param {*} arg + * @return {*} + */ +angular.identity = function(arg) {}; + +/** + * @param {Array.} modules + * @return {angular.$injector} + */ +angular.injector = function(modules) {}; + +/** + * @param {*} value + * @return {boolean} + */ +angular.isArray = function(value) {}; + +/** + * @param {*} value + * @return {boolean} + */ +angular.isDate = function(value) {}; + +/** + * @param {*} value + * @return {boolean} + */ +angular.isDefined = function(value) {}; + +/** + * @param {*} value + * @return {boolean} + */ +angular.isElement = function(value) {}; + +/** + * @param {*} value + * @return {boolean} + */ +angular.isFunction = function(value) {}; + +/** + * @param {*} value + * @return {boolean} + */ +angular.isNumber = function(value) {}; + +/** + * @param {*} value + * @return {boolean} + */ +angular.isObject = function(value) {}; + +/** + * @param {*} value + * @return {boolean} + */ +angular.isString = function(value) {}; + +/** + * @param {*} value + * @return {boolean} + */ +angular.isUndefined = function(value) {}; + +/** + * @param {string} s + * @return {string} + */ +angular.lowercase = function(s) {}; + +angular.mock = {}; + +/** + * @param {string} name + * @param {Array.=} opt_requires + * @param {Function=} opt_configFn + * @return {angular.Module} + */ +angular.module = function(name, opt_requires, opt_configFn) {}; + +/** + * @typedef {{ + * $normalize: function(string): string, + * $observe: function(string, function(*)): function(*), + * $set: function(string, ?(string|boolean), boolean=, string=) + * }} + */ +angular.Attributes; + +/** + * @param {string} name + * @return {string} + */ +angular.Attributes.$normalize = function(name) {}; + +/** + * @param {string} key + * @param {function(*)} fn + * @return {function(*)} + */ +angular.Attributes.$observe = function(key, fn) {}; + +/** + * @param {string} key + * @param {?(string|boolean)} value + * @param {boolean=} opt_writeAttr + * @param {string=} opt_attrName + */ +angular.Attributes.$set = function(key, value, opt_writeAttr, opt_attrName) {}; + +/** + * @typedef {{ + * addClass: function(string): angular.JQLite, + * after: function(JQLiteSelector): angular.JQLite, + * append: function(JQLiteSelector): angular.JQLite, + * attr: function(string, (string|boolean)=): (angular.JQLite|string|boolean), + * bind: function(string, Function): angular.JQLite, + * children: function(): angular.JQLite, + * clone: function(): angular.JQLite, + * contents: function(): angular.JQLite, + * controller: function(string=): Object, + * css: function(string, string=): (angular.JQLite|string), + * data: function(string=, *=): *, + * eq: function(number): angular.JQLite, + * find: function(string): angular.JQLite, + * hasClass: function(string): boolean, + * html: function(string=): (angular.JQLite|string), + * inheritedData: function(string=, *=): *, + * injector: function(): angular.$injector, + * length: number, + * next: function(): angular.JQLite, + * parent: function(): angular.JQLite, + * prepend: function(JQLiteSelector): angular.JQLite, + * prop: function(string, *=): *, + * ready: function(Function): angular.JQLite, + * remove: function(): angular.JQLite, + * removeAttr: function(string): angular.JQLite, + * removeClass: function(string): angular.JQLite, + * removeData: function(): angular.JQLite, + * replaceWith: function(JQLiteSelector): angular.JQLite, + * scope: function(): angular.Scope, + * text: function(string=): (angular.JQLite|string), + * toggleClass: function(string, boolean=): angular.JQLite, + * unbind: function(string=, Function=): angular.JQLite, + * val: function(string=): (angular.JQLite|string), + * wrap: function(JQLiteSelector): angular.JQLite + * }} + */ +angular.JQLite; + +/** + * @param {string} name + * @return {angular.JQLite} + */ +angular.JQLite.addClass = function(name) {}; + +/** + * @param {JQLiteSelector} element + * @return {angular.JQLite} + */ +angular.JQLite.after = function(element) {}; + +/** + * @param {JQLiteSelector} element + * @return {angular.JQLite} + */ +angular.JQLite.append = function(element) {}; + +/** + * @param {string} name + * @param {(string|boolean)=} opt_value + * @return {angular.JQLite|string|boolean} + */ +angular.JQLite.attr = function(name, opt_value) {}; + +/** + * @param {string} type + * @param {Function} fn + * @return {angular.JQLite} + */ +angular.JQLite.bind = function(type, fn) {}; + +/** + * @return {angular.JQLite} + */ +angular.JQLite.children = function() {}; + +/** + * @return {angular.JQLite} + */ +angular.JQLite.clone = function() {}; + +/** + * @return {angular.JQLite} + */ +angular.JQLite.contents = function() {}; + +/** + * @param {string=} opt_name + * @return {Object} + */ +angular.JQLite.controller = function(opt_name) {}; + +/** + * @param {string} name + * @param {string=} opt_value + * @return {angular.JQLite|string} + */ +angular.JQLite.css = function(name, opt_value) {}; + +/** + * @param {string=} opt_key + * @param {*=} opt_value + * @return {*} + */ +angular.JQLite.data = function(opt_key, opt_value) {}; + +/** + * @param {number} index + * @return {angular.JQLite} + */ +angular.JQLite.eq = function(index) {}; + +/** + * @param {string} selector + * @return {angular.JQLite} + */ +angular.JQLite.find = function(selector) {}; + +/** + * @param {string} name + * @return {boolean} + */ +angular.JQLite.hasClass = function(name) {}; + +/** + * @param {string=} opt_value + * @return {angular.JQLite|string} + */ +angular.JQLite.html = function(opt_value) {}; + +/** + * @param {string=} opt_key + * @param {*=} opt_value + * @return {*} + */ +angular.JQLite.inheritedData = function(opt_key, opt_value) {}; + +/** + * @return {angular.$injector} + */ +angular.JQLite.injector = function() {}; + +/** @type {number} */ +angular.JQLite.length; + +/** + * @return {angular.JQLite} + */ +angular.JQLite.next = function() {}; + +/** + * @return {angular.JQLite} + */ +angular.JQLite.parent = function() {}; + +/** + * @param {JQLiteSelector} element + * @return {angular.JQLite} + */ +angular.JQLite.prepend = function(element) {}; + +/** + * @param {string} name + * @param {*=} opt_value + * @return {*} + */ +angular.JQLite.prop = function(name, opt_value) {}; + +/** + * @param {Function} fn + * @return {angular.JQLite} + */ +angular.JQLite.ready = function(fn) {}; + +/** + * @return {angular.JQLite} + */ +angular.JQLite.remove = function() {}; + +/** + * @param {string} name + * @return {angular.JQLite} + */ +angular.JQLite.removeAttr = function(name) {}; + +/** + * @param {string} name + * @return {angular.JQLite} + */ +angular.JQLite.removeClass = function(name) {}; + +/** + * @return {angular.JQLite} + */ +angular.JQLite.removeData = function() {}; + +/** + * @param {JQLiteSelector} element + * @return {angular.JQLite} + */ +angular.JQLite.replaceWith = function(element) {}; + +/** + * @return {angular.Scope} + */ +angular.JQLite.scope = function() {}; + +/** + * @param {string=} opt_value + * @return {angular.JQLite|string} + */ +angular.JQLite.text = function(opt_value) {}; + +/** + * @param {string} name + * @param {boolean=} opt_condition + * @return {angular.JQLite} + */ +angular.JQLite.toggleClass = function(name, opt_condition) {}; + +/** + * @param {string=} opt_type + * @param {Function=} opt_fn + * @return {angular.JQLite} + */ +angular.JQLite.unbind = function(opt_type, opt_fn) {}; + +/** + * @param {string=} opt_value + * @return {angular.JQLite|string} + */ +angular.JQLite.val = function(opt_value) {}; + +/** + * @param {JQLiteSelector} element + * @return {angular.JQLite} + */ +angular.JQLite.wrap = function(element) {}; + +/** + * @typedef {{ + * config: function((Function|Array.)):angular.Module, + * constant: function(string, *):angular.Module, + * controller: + * function(string, (Function|Array.)):angular.Module, + * directive: + * (function(string, (Function|Array.)):angular.Module| + * function(!Object.<(Function|Array.)>):angular.Module), + * factory: + * function(string, (Function|Array.)):angular.Module, + * filter: + * function(string, (Function|Array.)):angular.Module, + * name: string, + * provider: + * function(string, (Function|Array.)):angular.Module, + * requires: Array., + * run: function((Function|Array.)):angular.Module, + * service: + * function(string, (Function|Array.)):angular.Module, + * value: function(string, *):angular.Module + * }} + */ +angular.Module; + +/** + * @param {Function|Array.} configFn + * @return {angular.Module} + */ +angular.Module.config = function(configFn) {}; + +/** + * @param {string} name + * @param {*} object + * @return {angular.Module} + */ +angular.Module.constant = function(name, object) {}; + +/** + * @param {string} name + * @param {Function|Array.} constructor + * @return {angular.Module} + */ +angular.Module.controller = function(name, constructor) {}; + +/** + * @param {string} name + * @param {Function|Array.} directiveFactory + * @return {angular.Module} + */ +angular.Module.directive = function(name, directiveFactory) {}; + +/** + * @param {string} name + * @param {Function|Array.} providerFunction + * @return {angular.Module} + */ +angular.Module.factory = function(name, providerFunction) {}; + +/** + * @param {string} name + * @param {Function|Array.} filterFactory + * @return {angular.Module} + */ +angular.Module.filter = function(name, filterFactory) {}; + +/** + * @param {string} name + * @param {Function|Array.} providerType + * @return {angular.Module} + */ +angular.Module.provider = function(name, providerType) {}; + +/** + * @param {Function|Array.} initializationFn + * @return {angular.Module} + */ +angular.Module.run = function(initializationFn) {}; + +/** + * @param {string} name + * @param {Function|Array.} constructor + * @return {angular.Module} + */ +angular.Module.service = function(name, constructor) {}; + +/** + * @param {string} name + * @param {*} object + * @return {angular.Module} + */ +angular.Module.value = function(name, object) {}; + +/** + * @type {string} + */ +angular.Module.name = ''; + +/** + * @type {Array.} + */ +angular.Module.requires; + +angular.noop = function() {}; + +/** + * @typedef {{ + * $apply: function((string|function(angular.Scope))=):*, + * $broadcast: function(string, ...[*]), + * $destroy: function(), + * $digest: function(), + * $emit: function(string, ...[*]), + * $eval: function((string|function())=, Object=):*, + * $evalAsync: function((string|function())=), + * $id: string, + * $new: function(boolean=):angular.Scope, + * $on: function(string, function(angular.Scope.Event, ...[?])):function(), + * $parent: angular.Scope, + * $watch: function( + * (string|Function), (string|Function)=, boolean=):function() + * }} + */ +angular.Scope; + +/** + * @param {(string|function(angular.Scope))=} opt_exp + * @return {*} + */ +angular.Scope.$apply = function(opt_exp) {}; + +/** + * @param {string} name + * @param {...*} args + */ +angular.Scope.$broadcast = function(name, args) {}; + +angular.Scope.$destroy = function() {}; + +angular.Scope.$digest = function() {}; + +/** + * @param {string} name + * @param {...*} args + */ +angular.Scope.$emit = function(name, args) {}; + +/** + * @param {(string|function())=} opt_exp + * @param {Object=} opt_locals + * @return {*} + */ +angular.Scope.$eval = function(opt_exp, opt_locals) {}; + +/** + * @param {(string|function())=} opt_exp + */ +angular.Scope.$evalAsync = function(opt_exp) {}; + +/** @type {string} */ +angular.Scope.$id; + +/** + * @param {boolean=} opt_isolate + * @return {angular.Scope} + */ +angular.Scope.$new = function(opt_isolate) {}; + +/** + * @param {string} name + * @param {function(angular.Scope.Event, ...[?])} listener + * @return {function()} + */ +angular.Scope.$on = function(name, listener) {}; + +/** @type {angular.Scope} */ +angular.Scope.$parent; + +/** + * @param {string|Function} exp + * @param {(string|Function)=} opt_listener + * @param {boolean=} opt_objectEquality + * @return {function()} + */ +angular.Scope.$watch = function(exp, opt_listener, opt_objectEquality) {}; + +/** + * @typedef {{ + * currentScope: angular.Scope, + * defaultPrevented: boolean, + * name: string, + * preventDefault: function(), + * stopPropagation: function(), + * targetScope: angular.Scope + * }} + */ +angular.Scope.Event; + +/** @type {angular.Scope} */ +angular.Scope.Event.currentScope; + +/** @type {boolean} */ +angular.Scope.Event.defaultPrevented; + +/** @type {string} */ +angular.Scope.Event.name; + +angular.Scope.Event.preventDefault = function() {}; + +angular.Scope.Event.stopPropagation = function() {}; + +/** @type {angular.Scope} */ +angular.Scope.Event.targetScope; + +/** + * @param {Object|Array|Date|string|number} obj + * @param {boolean=} opt_pretty + * @return {string} + */ +angular.toJson = function(obj, opt_pretty) {}; + +/** + * @param {string} s + * @return {string} + */ +angular.uppercase = function(s) {}; + +/** + * @type {Object} + */ +angular.version = {}; + +/** + * @type {string} + */ +angular.version.full = ''; + +/** + * @type {number} + */ +angular.version.major = 0; + +/** + * @type {number} + */ +angular.version.minor = 0; + +/** + * @type {number} + */ +angular.version.dot = 0; + +/** + * @type {string} + */ +angular.version.codeName = ''; + +/****************************************************************************** + * $anchorScroll Service + *****************************************************************************/ + +/** + * @typedef {function()} + */ +angular.$anchorScroll; + +/****************************************************************************** + * $anchorScrollProvider Service + *****************************************************************************/ + +/** + * @typedef {{ + * disableAutoScrolling: function() + * }} + */ +angular.$anchorScrollProvider; + +/** + * @type {function()} + */ +angular.$anchorScrollProvider.disableAutoScrolling = function() {}; + +/****************************************************************************** + * $compile Service + *****************************************************************************/ + +/** + * @typedef { + * function( + * (JQLiteSelector|Object), function(angular.Scope, Function=)=, number=): + * function(angular.Scope, function(Object, angular.Scope=)=): Object} + */ +angular.$compile; + +/****************************************************************************** + * $cacheFactory Service + *****************************************************************************/ + +/** + * @typedef { + * function(string, angular.$cacheFactory.Options=): + * !angular.$cacheFactory.Cache} + */ +angular.$cacheFactory; + +/** @typedef {{capacity: (number|undefined)}} */ +angular.$cacheFactory.Options; + +/** + * @typedef {{ + * info: function():angular.$cacheFactory.Cache.Info, + * put: function(string, *), + * get: function(string):*, + * remove: function(string), + * removeAll: function(), + * destroy: function() + * }} + */ +angular.$cacheFactory.Cache; + +/** + * @typedef {{ + * id: string, + * size: number, + * options: angular.$cacheFactory.Options + * }} + */ +angular.$cacheFactory.Cache.Info; + +/****************************************************************************** + * $http Service + *****************************************************************************/ + +/** + * This is a typedef because the closure compiler does not allow + * defining a type that is a function with properties. + * If you are trying to use the $http service as a function, try + * using one of the helper functions instead. + * @typedef {{ + * delete: function(string, angular.$http.Config=):angular.$http.HttpPromise, + * get: function(string, angular.$http.Config=):angular.$http.HttpPromise, + * head: function(string, angular.$http.Config=):angular.$http.HttpPromise, + * jsonp: function(string, angular.$http.Config=):angular.$http.HttpPromise, + * post: function(string, *, angular.$http.Config=):angular.$http.HttpPromise, + * put: function(string, *, angular.$http.Config=):angular.$http.HttpPromise, + * defaults: angular.$http.Config, + * pendingRequests: Array. + * }} + */ +angular.$http; + +/** + * @typedef {{ + * cache: (boolean|angular.$cacheFactory.Cache|undefined), + * data: (string|Object|undefined), + * headers: (Object|undefined), + * method: (string|undefined), + * params: (Object.<(string|Object)>|undefined), + * timeout: (number|undefined), + * transformRequest: + * (function((string|Object), Object):(string|Object)| + * Array.|undefined), + * url: (string|undefined), + * withCredentials: (boolean|undefined) + * }} + */ +angular.$http.Config; + +// /** +// * This extern is currently incomplete as delete is a reserved word. +// * To use delete, index $http. +// * Example: $http['delete'](url, opt_config); +// * @param {string} url +// * @param {angular.$http.Config=} opt_config +// * @return {angular.$http.HttpPromise} +// */ +// angular.$http.delete = function(url, opt_config) {}; + +/** + * @param {string} url + * @param {angular.$http.Config=} opt_config + * @return {angular.$http.HttpPromise} + */ +angular.$http.get = function(url, opt_config) {}; + +/** + * @param {string} url + * @param {angular.$http.Config=} opt_config + * @return {angular.$http.HttpPromise} + */ +angular.$http.head = function(url, opt_config) {}; + +/** + * @param {string} url + * @param {angular.$http.Config=} opt_config + * @return {angular.$http.HttpPromise} + */ +angular.$http.jsonp = function(url, opt_config) {}; + +/** + * @param {string} url + * @param {*} data + * @param {angular.$http.Config=} opt_config + * @return {angular.$http.HttpPromise} + */ +angular.$http.post = function(url, data, opt_config) {}; + +/** + * @param {string} url + * @param {*} data + * @param {angular.$http.Config=} opt_config + * @return {angular.$http.HttpPromise} + */ +angular.$http.put = function(url, data, opt_config) {}; + +/** + * @type {angular.$http.Config} + */ +angular.$http.defaults; + +/** + * @type {Array.} + * @const + */ +angular.$http.pendingRequests; + +/** + * @typedef {{ + * then: function( + * ?function(!angular.$http.Response), + * function(!angular.$http.Response)=): angular.$http.HttpPromise, + * success: function(function( + * (string|Object), number, function(string=): (string|Object), + * !angular.$http.Config)), + * error: function(function( + * (string|Object), number, function(string=): (string|Object), + * !angular.$http.Config)) + * }} + */ +angular.$http.HttpPromise; + +/** + * @param {?function(!angular.$http.Response)} successCallback + * @param {function(!angular.$http.Response)=} opt_errorCallback + * @return {angular.$http.HttpPromise} + */ +angular.$http.HttpPromise.then = function( + successCallback, opt_errorCallback) {}; + +/** + * @param {function((string|Object), number, + * function(string):string, Object)} callback + */ +angular.$http.HttpPromise.success = function(callback) {}; + +/** + * @param {function((string|Object), number, + * function(string):string, Object)} callback + */ +angular.$http.HttpPromise.error = function(callback) {}; + +/** + * @typedef {{ + * data: (string|Object), + * status: number, + * headers: function(string=): (string|Object), + * config: !angular.$http.Config + * }} + */ +angular.$http.Response; + +/****************************************************************************** + * $injector Service + *****************************************************************************/ + +/** + * @typedef {{ + * annotate: function((Function|Array.)):Array., + * get: function(string):*, + * instantiate: function(Function, Object=):Object, + * invoke: function( + * (Function|Array.), Object=, Object=):* + * }} + */ +angular.$injector; + +/** + * @param {(Function|Array.)} fn + * @return {Array.} + */ +angular.$injector.annotate = function(fn) {}; + +/** + * @param {string} name + * @return {*} + */ +angular.$injector.get = function(name) {}; + +/** + * @param {Function} type + * @param {Object=} opt_locals + * @return {Object} + */ +angular.$injector.instantiate = function(type, opt_locals) {}; + +/** + * @param {(Function|Array.)} fn + * @param {Object=} opt_self + * @param {Object=} opt_locals + * @return {*} + */ +angular.$injector.invoke = function(fn, opt_self, opt_locals) {}; + +/****************************************************************************** + * $interpolateProvider Service + *****************************************************************************/ + +/** + * @typedef {{ + * startSymbol: function(string), + * endSymbol: function(string) + * }} + */ +angular.$interpolateProvider; + +/** @type {function(string)} */ +angular.$interpolateProvider.startSymbol; + +/** @type {function(string)} */ +angular.$interpolateProvider.endSymbol; + +/****************************************************************************** + * $location Service + *****************************************************************************/ + +/** + * @typedef {{ + * absUrl: function():string, + * hash: function(string=):string, + * host: function():string, + * path: function(string=):(string|angular.$location), + * port: function():number, + * protocol: function():string, + * replace: function(), + * search: function((string|Object.)=, ?string=): + * (string|!Object.), + * url: function(string=):string + * }} + */ +angular.$location; + +/** + * @return {string} + */ +angular.$location.absUrl = function() {}; + +/** + * @param {string=} opt_hash + * @return {string} + */ +angular.$location.hash = function(opt_hash) {}; + +/** + * @return {string} + */ +angular.$location.host = function() {}; + +/** + * @param {string=} opt_path + * @return {string|angular.$location} + */ +angular.$location.path = function(opt_path) {}; + +/** + * @return {number} + */ +angular.$location.port = function() {}; + +/** + * @return {string} + */ +angular.$location.protocol = function() {}; + +/** + * @type {function()} + */ +angular.$location.replace = function() {}; + +/** + * @param {(string|Object.)=} opt_search + * @param {?string=} opt_paramValue + * @return {string|!Object.} + */ +angular.$location.search = function(opt_search, opt_paramValue) {}; + +/** + * @param {string=} opt_url + * @return {string} + */ +angular.$location.url = function(opt_url) {}; + +/****************************************************************************** + * $locationProvider Service + *****************************************************************************/ + +/** + * @typedef {{ + * hashPrefix: + * function(string=): (string|angular.$locationProvider), + * html5Mode: + * function(boolean=): (boolean|angular.$locationProvider) + * }} + */ +angular.$locationProvider; + +/** + * @param {string=} opt_prefix + * @return {string|angular.$locationProvider} + */ +angular.$locationProvider.hashPrefix = function(opt_prefix) {}; + +/** + * @param {boolean=} opt_enabled + * @return {boolean|angular.$locationProvider} + */ +angular.$locationProvider.html5Mode = function(opt_enabled) {}; + +/****************************************************************************** + * $log Service + *****************************************************************************/ + +/** + * @typedef {{ + * error: function(...[*]), + * info: function(...[*]), + * log: function(...[*]), + * warn: function(...[*]) + * }} + */ +angular.$log; + +/** + * @param {...*} var_args + */ +angular.$log.error = function(var_args) {}; + +/** + * @param {...*} var_args + */ +angular.$log.info = function(var_args) {}; + +/** + * @param {...*} var_args + */ +angular.$log.log = function(var_args) {}; + +/** + * @param {...*} var_args + */ +angular.$log.warn = function(var_args) {}; + +/****************************************************************************** + * NgModelController + *****************************************************************************/ + +/** + * @constructor + */ +angular.NgModelController = function() {}; + +/** + * @type {?} + */ +angular.NgModelController.prototype.$modelValue; + +/** + * @type {boolean} + */ +angular.NgModelController.prototype.$dirty; + +/** + * @type {!Object.} + */ +angular.NgModelController.prototype.$error; + +/** + * @type {!Array.} + */ +angular.NgModelController.prototype.$formatters; + +/** + * @type {boolean} + */ +angular.NgModelController.prototype.$invalid; + +/** + * @type {!Array.} + */ +angular.NgModelController.prototype.$parsers; + +/** + * @type {boolean} + */ +angular.NgModelController.prototype.$pristine; + +angular.NgModelController.prototype.$render = function() {}; + +/** + * @param {string} key + * @param {boolean} isValid + */ +angular.NgModelController.prototype.$setValidity = function(key, isValid) {}; + +/** + * @param {?} value + */ +angular.NgModelController.prototype.$setViewValue = function(value) {}; + +/** + * @type {boolean} + */ +angular.NgModelController.prototype.$valid; + +/** + * @type {!Array.} + */ +angular.NgModelController.prototype.$viewChangeListener; + +/** + * @type {?} + */ +angular.NgModelController.prototype.$viewValue; + +/****************************************************************************** + * $provide Service + *****************************************************************************/ + +/** + * @typedef {{ + * constant: function(string, *): Object, + * decorator: function(string, (Function|Array.)), + * factory: function(string, (Function|Array.)): Object, + * provider: function(string, (Function|Array.)): Object, + * service: function(string, (Function|Array.)): Object, + * value: function(string, *): Object + * }} + */ +angular.$provide; + +/** + * @param {string} name + * @param {*} object + * @return {Object} + */ +angular.$provide.constant = function(name, object) {}; + +/** + * @param {string} name + * @param {Function|Array.} decorator + */ +angular.$provide.decorator = function(name, decorator) {}; + +/** + * @param {string} name + * @param {Function|Array.} providerFunction + * @return {Object} + */ +angular.$provide.factory = function(name, providerFunction) {}; + +/** + * @param {string} name + * @param {Function|Array.} providerType + * @return {Object} + */ +angular.$provide.provider = function(name, providerType) {}; + +/** + * @param {string} name + * @param {Function|Array.} constructor + * @return {Object} + */ +angular.$provide.service = function(name, constructor) {}; + +/** + * @param {string} name + * @param {*} object + * @return {Object} + */ +angular.$provide.value = function(name, object) {}; + +/****************************************************************************** + * $q Service + *****************************************************************************/ + +/** + * @typedef {{ + * all: function(Array.): angular.$q.Promise, + * defer: function():angular.$q.Deferred, + * reject: function(*):angular.$q.Promise, + * when: function(*):angular.$q.Promise + * }} + */ +angular.$q; + +/** + * @param {Array.} promises + * @return {angular.$q.Promise} + */ +angular.$q.all = function(promises) {}; + +/** + * @return {angular.$q.Deferred} + */ +angular.$q.defer = function() {}; + +/** + * @param {*} reason + * @return {angular.$q.Promise} + */ +angular.$q.reject = function(reason) {}; + +/** + * @param {*} value + * @return {angular.$q.Promise} + */ +angular.$q.when = function(value) {}; + +/** + * @typedef {{ + * resolve: function(*=), + * reject: function(*=), + * promise: angular.$q.Promise + * }} + */ +angular.$q.Deferred; + +/** @param {*=} opt_value */ +angular.$q.Deferred.resolve = function(opt_value) {}; + +/** @param {*=} opt_reason */ +angular.$q.Deferred.reject = function(opt_reason) {}; + +/** @type {angular.$q.Promise} */ +angular.$q.Deferred.promise; + +/** + * @typedef {{then: function(?function(?), function(?)=): angular.$q.Promise}} + */ +angular.$q.Promise; + +/** + * @param {?function(?)} successCallback + * @param {function(?)=} opt_errorCallback + * @return {angular.$q.Promise} + */ +angular.$q.Promise.then = function(successCallback, opt_errorCallback) {}; + +/****************************************************************************** + * $route Service + *****************************************************************************/ + +/** + * @typedef {{ + * reload: function(), + * current: angular.$route.Route, + * routes: Array. + * }} + */ +angular.$route; + +/** @type {function()} */ +angular.$route.reload = function() {}; + +/** @type {angular.$route.Route} */ +angular.$route.current; + +/** @type {Array.} */ +angular.$route.routes; + +/** + * @typedef {{ + * $route: angular.$routeProvider.Params, + * locals: Object., + * params: Object., + * pathParams: Object., + * scope: Object. + * }} + */ +angular.$route.Route; + +/** @type {angular.$routeProvider.Params} */ +angular.$route.Route.$route; + +/** @type {Object.} */ +angular.$route.Route.locals; + +/** @type {Object.} */ +angular.$route.Route.params; + +/** @type {Object.} */ +angular.$route.Route.pathParams; + +/** @type {Object.} */ +angular.$route.Route.scope; + +/****************************************************************************** + * $routeProvider Service + *****************************************************************************/ + +/** + * @typedef {{ + * otherwise: + * function(angular.$routeProvider.Params): angular.$routeProvider, + * when: + * function( + * string, angular.$routeProvider.Params): angular.$routeProvider + * }} + */ +angular.$routeProvider; + +/** + * @param {angular.$routeProvider.Params} params + * @return {angular.$routeProvider} + */ +angular.$routeProvider.otherwise = function(params) {}; + +/** + * @param {string} path + * @param {angular.$routeProvider.Params} route + * @return {angular.$routeProvider} + */ +angular.$routeProvider.when = function(path, route) {}; + +/** + * @typedef {{ + * controller: (Function|Array.|string|undefined), + * template: (string|undefined), + * templateUrl: (string|undefined), + * resolve: (Object.|angular.$q.Promise + * )>|undefined), + * redirectTo: (string|function()|undefined), + * reloadOnSearch: (boolean|undefined) + * }} + */ +angular.$routeProvider.Params; + +/** @type {Function|Array.|string} */ +angular.$routeProvider.Params.controller; + +/** @type {string} */ +angular.$routeProvider.Params.template; + +/** @type {string} */ +angular.$routeProvider.Params.templateUrl; + +/** + * @type { + * Object.|angular.$q.Promise + * )>} + */ +angular.$routeProvider.Params.resolve; + +/** @type {string|function()} */ +angular.$routeProvider.Params.redirectTo; + +/** @type {boolean} */ +angular.$routeProvider.Params.reloadOnSearch; + +/****************************************************************************** + * $timeout Service + *****************************************************************************/ + +/** + * @typedef {function(function(), number=, boolean=):angular.$q.Promise} + */ +angular.$timeout; + +// /** +// * This extern is incomplete. $timeout cannot be a function with properties. +// * To use cancel, index $timeout. +// * Example: $timeout['cancel'](promise); +// * @param {angular.$q.Promise=} opt_promise +// * @return {boolean} +// */ +// angular.$timeout.cancel = function(opt_promise) {}; diff --git a/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/google.container.js b/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/google.container.js new file mode 100755 index 0000000..bbd8296 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/google.container.js @@ -0,0 +1,288 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview External declarations for common container JS. + * + * TODO(uix-eng): May want a way to conveniently generate this externs to avoid + * manual changes when we merge Shindig. + * + * @externs + */ + + +google.container = {}; + +//////////////////////////////////////////////////////////////////////////////// +// Externs from container.js +//////////////////////////////////////////////////////////////////////////////// + +google.container.ContainerConfig = {}; + +google.container.ContainerRender = {}; + +/** + * @param {Object} config + * @constructor + */ +google.container.Container = function(config) {}; + +/** + * @param {Object=} opt_config + */ +google.container.Container.prototype.onConstructed = function(opt_config) {}; + +/** + * Create a new gadget site. + * @param {Element} gadgetEl + * @param {Element=} opt_bufferEl + * @return {google.container.GadgetSite} + */ +google.container.Container.prototype.newGadgetSite = function(gadgetEl, + opt_bufferEl) {}; + +/** + * @param {string} id + * @return {google.container.GadgetSite} + * @nosideeffects + */ +google.container.Container.prototype.getGadgetSite = function(id) {}; + +/** + * @param {string} id + * @return {google.container.GadgetHolder} + * @nosideeffects + */ +google.container.Container.prototype.getGadgetHolder = function(id) {}; + +/** + * @param {google.container.GadgetSite} site + * @param {string} gadgetUrl + * @param {Object} gadgetParams + * @param {Object} renderParams + * @param {Function=} opt_callback + */ +google.container.Container.prototype.navigateGadget = function( + site, gadgetUrl, gadgetParams, renderParams, opt_callback) {}; + +/** + * @param {google.container.GadgetSite} site + */ +google.container.Container.prototype.closeGadget = function(site) {}; + +/** + * @param {Object} request + */ +google.container.Container.prototype.preloadGadgets = function(request) {}; + +/** + * @param {string} url + * @param {function(Object)=} opt_callback + */ +google.container.Container.prototype.getGadgetMetadata = function(url, + opt_callback) {}; + +//////////////////////////////////////////////////////////////////////////////// +// Externs from gadget_site.js +//////////////////////////////////////////////////////////////////////////////// + +/** + * @param {google.container.Service} service + * @param {Element} gadgetEl + * @param {Element=} opt_bufferEl + * @constructor + */ +google.container.GadgetSite = function(service, gadgetEl, opt_bufferEl) {}; + +google.container.GadgetSite.prototype.onConstructed = function() {}; + +/** + * @param {number} height + */ +google.container.GadgetSite.prototype.setHeight = function(height) {}; + +/** + * @param {number} width + */ +google.container.GadgetSite.prototype.setWidth = function(width) {}; + +/** + * @param {number} value + */ +google.container.GadgetSite.prototype.setParentId = function(value) {}; + +/** + * @return {number} + * @nosideeffects + */ +google.container.GadgetSite.prototype.getId = function() {}; + +/** + * @return {google.container.GadgetHolder} + * @nosideeffects + */ +google.container.GadgetSite.prototype.getActiveGadgetHolder = function() {}; + +/** + * @param {string} name + * @param {Object=} opt_gadgetInfo + * @return {Object} + * @nosideeffects + */ +google.container.GadgetSite.prototype.getFeature = function(name, + opt_gadgetInfo) {}; + +/** + * @param {string} id + * @return {google.container.GadgetHolder} + * @nosideeffects + */ +google.container.GadgetSite.prototype.getGadgetHolder = function(id) {}; + +/** + * @return {string} + * @nosideeffects + */ +google.container.GadgetSite.prototype.getParentId = function() {}; + +/** + * @param {string} gadgetUrl + * @param {Object} gadgetParams + * @param {Object} renderParams + * @param {Function=} opt_callback + */ +google.container.GadgetSite.prototype.navigateTo = function(gadgetUrl, + gadgetParams, renderParams, opt_callback) {}; + +/** + * @param {Object} gadgetInfo + * @param {Object} gadgetParams + * @param {Object} renderParams + */ +google.container.GadgetSite.prototype.render = function( + gadgetInfo, gadgetParams, renderParams) {}; + +/** + * @param {string} serviceName + * @param {Function} callback + * @param {...number} var_args + */ +google.container.GadgetSite.prototype.rpcCall = function(serviceName, callback, + var_args) {}; + +google.container.GadgetSite.prototype.close = function() {}; + +//////////////////////////////////////////////////////////////////////////////// +// Externs from gadget_holder.js +//////////////////////////////////////////////////////////////////////////////// + +/** + * @param {number} siteId + * @param {Element} el + * @constructor + */ +google.container.GadgetHolder = function(siteId, el) {}; + +google.container.GadgetHolder.prototype.onConstructed = function() {}; + +/** + * @return {Element} + * @nosideeffects + */ +google.container.GadgetHolder.prototype.getElement = function() {}; + +/** + * @return {string} + * @nosideeffects + */ +google.container.GadgetHolder.prototype.getIframeId = function() {}; + +/** + * @return {Object} + * @nosideeffects + */ +google.container.GadgetHolder.prototype.getGadgetInfo = function() {}; + +google.container.GadgetHolder.prototype.dispose = function() {}; + +/** + * @return {string} + * @nosideeffects + */ +google.container.GadgetHolder.prototype.getUrl = function() {}; + +/** + * @return {string} + * @nosideeffects + */ +google.container.GadgetHolder.prototype.getView = function() {}; + +/** + * @return {Element} + * @nosideeffects + */ +google.container.GadgetHolder.prototype.getIframeElement = function() {}; + +/** + * @param {string} value + */ +google.container.GadgetHolder.prototype.setSecurityToken = function(value) {}; + +/** + * @param {Object} gadgetInfo + * @param {Object} gadgetParams + * @param {Object} renderParams + */ +google.container.GadgetHolder.prototype.render = function(gadgetInfo, + gadgetParams, renderParams) {}; + +//////////////////////////////////////////////////////////////////////////////// +// Externs from service.js +//////////////////////////////////////////////////////////////////////////////// + +google.container.ServiceConfig = {}; + +/** + * @param {Object=} opt_config + * @constructor + */ +google.container.Service = function(opt_config) {}; + +/** + * @param {Object} request + * @param {function(Object)=} opt_callback + */ +google.container.Service.prototype.getGadgetMetadata = function(request, + opt_callback) {}; + +/** + * @param {Object} request + * @param {function(Object)=} opt_callback + */ +google.container.Service.prototype.getGadgetToken = function(request, + opt_callback) {}; + +/** + * @param {string} url + * @return {Object} + */ +google.container.Service.prototype.getCachedGadgetMetadata = function(url) {}; + +/** + * @param {string} url + * @return {Object} + */ +google.container.Service.prototype.getCachedGadgetToken = function(url) {}; diff --git a/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/iframes.js b/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/iframes.js new file mode 100644 index 0000000..4a4f333 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/iframes.js @@ -0,0 +1,342 @@ +/* + * Copyright 2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Externs file for the Iframes library. + * @externs + */ + + +/** + * The namespace for most of the APIs. + * @type {!iframes.Iframes} + * @suppress {checkTypes} In some crazy locations, this is being used as a + * source file. It needs to be assigned to some object so that the + * iframes.Iframes definition doesn't fail. + */ +var iframes = /** @type {!iframes.Iframes} */ ({}); + + +/** + * The namespace for most of the APIs. + * + * This has "|undefined" so that JSCompiler won't optimize out existence checks + * for win.iframes. + * + * @type {!iframes.Iframes|undefined} + */ +Window.prototype.iframes; + + +/** + * The type for the Iframes API. + * @constructor + */ +iframes.Iframes = function() {}; + + +/** + * Gets the handler for a given style. + * + * @param {string} style + * @return {Object|Function} The handler for the given style. + */ +iframes.Iframes.prototype.getHandler = function(style) {}; + +/** + * Sets the handler for a given style. + * + * @param {string} style + * @param {Object|Function} handler The handler for a given style. + * If the style handle is a function, the object returned by this + * function providing the iframe as the parameter is used as the + * actual style handler for every iframe opened in the style it + * handles. + * The follow methods of the handler are significant (note + * that the iframe parameter only presents when the original + * style handler is not a function): + * open(iframe): called to open the iframe. + * call iframe.openInto(el) to perform the open action. + * onready(iframe): called when the iframe is ready. + * close(iframe): called to close the iframe. + */ +iframes.Iframes.prototype.setHandler = function(style, handler) {}; + +/** + * Gets a deferred-loaded style handler. + * @param {string} style The name of the style. + * @return {?function(function())} The deferred loader, if any. + */ +iframes.Iframes.prototype.getDeferredHandler = function(style) {}; + +/** + * Sets a deferred-loaded style handler. + * @param {string} style The name of the style. + * @param {function(function())} loader The method to load the new style + * handler, which should call iframes.setHandler for that style. + * It then needs to call the callback method passed in. + */ +iframes.Iframes.prototype.setDeferredHandler = function(style, loader) {}; + +/** + * This is an internal class to represent an iframe, not the DOM element. + * @constructor + */ +iframes.Iframe = function() {}; + +/** + * Methods used to pass to the iframe being opened. + * @return {Object} + */ +iframes.Iframe.prototype.getMethods = function() {}; + +/** + * Parameters used for opening the widget. + * @return {Object} + */ +iframes.Iframe.prototype.getOpenParams = function() {}; + +/** + * Parameters used to pass to the iframe being opened. + * @return {Object} + */ +iframes.Iframe.prototype.getParams = function() {}; + +/** + * @type {Element} + * @deprecated + */ +iframes.Iframe.prototype.containerDiv; + +/** + * DOM reference to element containing iframe element. + * + * @return {Element} + */ +iframes.Iframe.prototype.getSiteEl = function() {}; + +/** + * @param {Element} element DOM element containing the iframe element. + */ +iframes.Iframe.prototype.setSiteEl = function(element) {}; + +/** + * DOM reference to the iframe element containing the widget. + * + * @return {Element} + */ +iframes.Iframe.prototype.getIframeEl = function() {}; + +/** + * generated ID that will be set on the widget when it is opened + * + * @return {string} + */ +iframes.Iframe.prototype.getId = function() {}; + +/** + * Iframe class instance that opened our Iframe instance. + * + * @return {iframes.Iframe} + */ +iframes.Iframe.prototype.getOpenerIframe = function() {}; + +/** + * Exposes the method so it can be called from iframe object. + * This is supposed be called by the style handler while handling 'open' + * before calling 'openInto'. + * + * @param {string} name Name of the method as being called. + * @param {Function} method The method to be exposed. + */ +iframes.Iframe.prototype.exposeMethod = function(name, method) {}; + +/** + * @param {string|Element} el The DOM element or its ID to open the iframe + * into. + * @param {Object=} opt_iframeAttributes Key-value pairs of iframe attributes. + * @return {iframes.Iframe} This iframe object (not the DOM element). + **/ +iframes.Iframe.prototype.openInto = function(el, opt_iframeAttributes) {}; + +/** + * Close the iframe. + * + * @param {*=} opt_params Data to pass to the callback. + * @return {*} The result from the callback function. + */ +iframes.Iframe.prototype.close = function(opt_params) {}; + +/** + * Removes the iframe element from the DOM tree. + */ +iframes.Iframe.prototype.remove = function() {}; + +/** + * Adds a callback method for a given type of event. + * + * @param {string} type The event type: e.g. 'ready' or 'close'. + * @param {Function} callback The callback method. + */ +iframes.Iframe.prototype.addCallback = function(type, callback) {}; + +/** + * Removes a callback method for a given type of event. + * + * @param {string} type The event type used in 'addCallback'. + * @param {Function} callback The callback method used in 'addCallback'. + */ +iframes.Iframe.prototype.removeCallback = function(type, callback) {}; + +/** + * Allows a global function for all iframes. + * + * @param {string} name The name of the method for the iframes. + * @param {?function(...[*]) : *=} opt_func A optional function, by default + * window[name]. + */ +iframes.Iframes.prototype.allow = function(name, opt_func) {}; + +/** + * Opens an iframe. + * + * WARNING: This API takes ownership of all object parameters and will + * modify them. Make a copy if you want to reuse them. + * + * @param {string} url The URL of the iframe to be opened. + * @param {Object} openParams The parameters for opening the iframe. + * style: specify which handler is used. + * For the default handler, the follow attributes are used: + * element: the element which the iframe would be opened into. + * @param {Object} params The data to be passed to the iframe. + * All properties should be string. + * @param {Object|function(...[*]) : *=} opt_methods: Functions to passed to + * the iframe. All properties should be functions. If no 'callback' + * argument is provided and the argument in this position is a function + * instead of an object, it is considered as the next parameter + * 'callback', not this parameter 'methods'. + * @param {?function(...[*]) : *=} opt_callback: a callback function called + * when the iframe is closed. + * + * @return {iframes.Iframe} The opened iframe. + */ +iframes.Iframes.prototype.open = function( + url, openParams, params, opt_methods, opt_callback) {}; + +/** + * Closes this iframe. + * + * @param {*=} opt_params The parameters to pass back to the parent iframe. + * @param {?function(...[*]) : *=} opt_callback The callback function after + * parent processed the event. + */ +iframes.Iframes.prototype.close = function(opt_params, opt_callback) {}; + +/** + * Indicates that this iframe is ready. The exactly semantic depends on + * the style handler, but generally it means ready to be displayed and + * it should be called the page is drawn and initial data loaded. + * + * @param {Object=} opt_params The parameters to pass back to the parent iframe. + * @param {Object|function(...[*]) : *=} opt_methods: Functions to passed to + * the iframe. All properties should be functions. If no 'callback' + * argument is provided and the argument in this position is a function + * instead of an object, it is considered as the next parameter + * 'callback', not this parameter 'methods'. + * @param {?function(...[*]) : *=} opt_callback The callback function after + * parent processed the event. + */ +iframes.Iframes.prototype.ready = function( + opt_params, opt_methods, opt_callback) {}; + +/** + * Passes the parent's origin and referer to the callback. + * @param {Function} callback Function + * that will get parent info passed to it. + */ +iframes.Iframes.prototype.getParentInfo = function(callback) {}; + +/** + * Export browser events to your opener. + * @param {Array.} events List of events to export. Currently only + * supports mouseover and mouseout. + */ +iframes.Iframes.prototype.propagate = function(events) {}; + +/** + * Asks the parent window to change the width and height of this iframe. + * + * @param {Object} params The width and/or height in number of pixels to be + * resized. Use 'height': 'auto' for current window content height. + */ + +iframes.Iframes.prototype.resize = function(params) {}; + +/** + * @param {Object} params + * @deprecated + */ +iframes.Iframes.prototype.resizeMe = function(params) {}; + +/** + * @return {string} The full URI for the Google Connect JS bundle. + */ +iframes.Iframes.prototype.getGoogleConnectJsUri = function() {}; + +/** + * Allows client to override the Google Connect JS to use. + * @param {string} version The versioned JS file to use. + */ +iframes.Iframes.prototype.setGoogleConnectJsVersion = function(version) {}; + +/** + * Allows the client to use a different JS hint. + * @param {string} hint The JS hint to use. + */ +iframes.Iframes.prototype.setJsHint = function(hint) {}; + +/** + * Allows the client to use a different bootstrap hint. + * @param {string} hint The bootstrap hint to use. + */ +iframes.Iframes.prototype.setBootstrapHint = function(hint) {}; + +/** + * @param {string} version + * @deprecated + */ +iframes.Iframes.prototype.setVersionOverride = function(version) {}; + +/** + * Inside an iframe, the properties of the iframer object are the data and + * functions provided in iframes.open. + * @type {!Object|undefined} + */ +Window.prototype.iframer = {}; + +/** + * Inside an iframe, the properties of the iframer object are the data and + * functions provided in iframes.open. + * @type {!Object|undefined} + */ +iframes.Iframes.prototype.iframer = {}; + +/** + * Inside an iframe, the properties of the iframer object are the data and + * functions provided in iframes.open. + * @type {!Object} + */ +var iframer = {}; diff --git a/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/oauth.js b/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/oauth.js new file mode 100644 index 0000000..4d7c6e6 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/oauth.js @@ -0,0 +1,55 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Externs file for OAuth specific functionality in the OpenSocial + * gadget libraries. Note this may not be comprehensive. + * @see http://code.google.com/apis/gadgets/docs/oauth.html + * @externs + */ + + +// Namespace for OAuth +gadgets.oauth = {}; + + +/** + * An OAuth popup for granting one time access to user data. + * @param {string} destination Target URL for the popup window. + * @param {string} windowOptions Options for window.open, used to specify + * look and feel of the window. + * @param {Function} openCallback Function to call when the window is + * opened. + * @param {Function} closeCallback Function to call when the window is + * closed. + * @constructor + */ +gadgets.oauth.Popup = function(destination, windowOptions, + openCallback, closeCallback) {}; + +/** + * @return {Function} An onclick handler for the "open the approval window" + * link + */ +gadgets.oauth.Popup.prototype.createOpenerOnClick = function() {}; + +/** + * @return {Function} An onclick handler for the "I've approved" link. + * This may not ever be called. If we successfully detect that the + * window was closed, this link is unnecessary. + */ +gadgets.oauth.Popup.prototype.createApprovedOnClick = function() {}; + diff --git a/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/oauth2.js b/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/oauth2.js new file mode 100644 index 0000000..123f892 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/oauth2.js @@ -0,0 +1,26 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Externs file for OAuth2 specific functionality in OpenSocial + * gadget libraries. + * @externs + */ + +// Namespace for OAuth2 +var oauth2 = {}; + + diff --git a/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/opensocial.js b/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/opensocial.js new file mode 100644 index 0000000..0081f31 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/opensocial.js @@ -0,0 +1,598 @@ +/* + * Copyright 2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Externs file for OpenSocial gadget libraries. Legacy API based + * on the {@code _IG_...} is not supported here. + * Note this may not be comprehensive. + * + * @see http://code.google.com/apis/gadgets/docs/reference/ + * @see http://incubator.apache.org/shindig/shindig-1.1.x/shindig-features/jsdoc + * @externs + */ + +// Root namespace for gadget related functionality. +var gadgets = {}; + + +// Namespace providing operations for getting information about and modifying +// the window the gadget is placed in. +gadgets.window = {}; + +/** + * @param {number=} opt_height The new height for the gadget. + */ +gadgets.window.adjustHeight = function(opt_height) {}; + +/** + * @return {Object} Dimensions for the viewport. + */ +gadgets.window.getViewportDimensions = function() {}; + +/** + * @param {string} title The new title for the gadget. + */ +gadgets.window.setTitle = function(title) {}; + +/** + * Calculate inner content height is hard and different between + * browsers rendering in Strict vs. Quirks mode. We use a combination of + * three properties within document.body and document.documentElement: + * - scrollHeight + * - offsetHeight + * - clientHeight + * These values differ significantly between browsers and rendering modes. + * But there are patterns. It just takes a lot of time and persistence + * to figure out. + * @return {number} Height of current window. + */ +gadgets.window.getHeight = function() {}; + +// Namespace providing operations for making remote procedure calls for +// gadget-to-container, container-to-gadget and gadget-to-gadget communication. +gadgets.rpc = {}; + +/** + * Exported constant used by transports. + * @type {string} + */ +gadgets.rpc.ACK; + +/** + * @param {?string} targetId The id of the target gadget, null for container. + * @param {string} serviceName The RPC service to call. + * @param {!Function=} opt_callback Function called when the RPC + * completes. + * @param {...*} var_args Variable arguments that are passed to the RPC handler. + */ +gadgets.rpc.call = function(targetId, serviceName, opt_callback, var_args) {}; + +/** + * Force RPC utilities to fallback to secure mechanisms for type=URL gadgets. + */ +gadgets.rpc.forceParentVerifiable = function() {}; + +/** + * @return {string} The RPC relay mechanism. + */ +gadgets.rpc.getRelayChannel = function() {}; + +/** + * @param {string} targetId The frame ID to get the relay for. + * @return {string} The relay URL of the given gadget (or container). + */ +gadgets.rpc.getRelayUrl = function(targetId) {}; + +/** + * Global init for RPC code. + */ +gadgets.rpc.init = function() {}; + +/** + * Receives and processes an RPC request. (Not to be used directly.) + * Only used by IFPC. + * @param {Array.} fragment An RPC request fragment encoded as + * an array. The first 4 elements are target id, source id & call id, + * total packet number, packet id. The last element stores the actual + * JSON-encoded and URI escaped packet data. + */ +gadgets.rpc.receive = function(fragment) {}; + +/** + * @param {string} serviceName The RPC service to register a handler for. + * @param {!Function} handler Handler for an RPC call, return + * value will be sent back to the caller. + */ +gadgets.rpc.register = function(serviceName, handler) {}; + +/** + * @param {!Function} handler Handler for unknown RPC calls, + * return value will be sent back to the caller. + */ +gadgets.rpc.registerDefault = function(handler) {}; + +/** + * Helper method to retrieve the auth token for a given gadget. + * @param {string} targetId Name of the target frame. + * @return {string} The authentication token registered for this + * target id. + */ +gadgets.rpc.getAuthToken = function(targetId) {}; + +/** + * Helper method returning a canonicalized protocol://host[:port] for + * a given input URL, provided as a string. Used to compute convenient + * relay URLs and to determine whether a call is coming from the same + * domain as its receiver (bypassing the try/catch capability detection + * flow, thereby obviating Firebug and other tools reporting an exception). + * @param {string} url Base URL to canonicalize. + * @return {string} The canonicalized URL. + */ +gadgets.rpc.getOrigin = function(url) {}; + +/** + * Helper method returning a canonicalized protocol://host[:port] for + * the relay URL of a given target frame. + * @param {string} id Name of the target frame. + * @return {string} The canonicalized URL. + */ +gadgets.rpc.getTargetOrigin = function(id) {}; + +/** + * Sets the auth token of a target frame. + * @param {string} targetId Name of the target frame. + * @param {string} token The authentication token to use for all calls to or + * from this target id. + */ +gadgets.rpc.setAuthToken = function(targetId, token) {}; + +/** + * Sets the relay URL of a target frame. + * @param {string} targetId The id of the target gadget. + * @param {string} url Full relay URL of the target frame. + * @param {boolean=} opt_useLegacy True if this relay needs the legacy IFPC + * wire format. + */ +gadgets.rpc.setRelayUrl = function(targetId, url, opt_useLegacy) {}; + +/** + * Setups the gadgets.rpc library to communicate with the receiver. This method + * replaces setAuthToken and setRelayUrl. + * @param {string} targetId The id of the target gadget. + * @param {string=} opt_receiverUrl Full relay URL of the target frame. + * @param {boolean=} opt_authToken null The authentication token to use for all + * calls to or from this target id. + */ +gadgets.rpc.setupReceiver = function(targetId, opt_receiverUrl, opt_authToken) {}; + +/** + * @param {string} serviceName The RPC service to unregister the handler for. + */ +gadgets.rpc.unregister = function(serviceName) {}; + +/** + * Unregisters the default service handler. Future unknown RPC + * calls will fail silently. + */ +gadgets.rpc.unregisterDefault = function() {}; + +/** + * Class providing access to user preferences, module dimensions, and messages. + * @param {string=} opt_moduleId An optional module ID to get preferences for. + * @constructor + */ +gadgets.Prefs = function(opt_moduleId) {}; + +/** + * @param {string} key The user preference key. + * @return {Array.} The array value of the preference. + */ +gadgets.Prefs.prototype.getArray = function(key) {}; + +/** + * @param {string} key The user preference key. + * @return {boolean} The boolean value of the preference. + */ +gadgets.Prefs.prototype.getBool = function(key) {}; + +/** + * @param {string} key The user preference key. + * @return {number} The float value of the preference. + */ +gadgets.Prefs.prototype.getFloat = function(key) {}; + +/** + * @param {string} key The user preference key. + * @return {number} The int value of the preference. + */ +gadgets.Prefs.prototype.getInt = function(key) {}; + +/** + * @param {string} key The user preference key. + * @return {string} The string value of the preference. + */ +gadgets.Prefs.prototype.getString = function(key) {}; + +/** + * @return {string} The country code. + */ +gadgets.Prefs.prototype.getCountry = function() {}; + +/** + * @return {string} The language code. + */ +gadgets.Prefs.prototype.getLang = function() {}; + +/** + * @return {number} The module's id. + */ +gadgets.Prefs.prototype.getModuleId = function() {}; + +/** + * @param {string} key The message key. + * @return {string} The unformatted message. + */ +gadgets.Prefs.prototype.getMsg = function(key) {}; + +/** + * @param {string} key The preference key. + * @param {string|number} value The preference value. + */ +gadgets.Prefs.prototype.set = function(key, value) {}; + +/** + * @param {string} key The preference key. + * @param {Array.} array The preference array. + */ +gadgets.Prefs.prototype.setArray = function(key, array) {}; + + +// Namespace for embedding flash content in gadgets. +gadgets.flash = {}; + +/** + * @return {boolean} Injects the cached Flash file into the DOM tree. + */ +gadgets.flash.embedCachedFlash = function() {}; + +/** + * @return {number} The major version of Flash Player or 0 if Flash is not + * supported. + */ +gadgets.flash.getMajorVersion = function() {}; + +/** + * @param {string} url The URL of the flash to embed. + * @param {string|Element} container The ID or object reference to the + * existing HTML container. + * @param {number} minVersion The minimum version required. + * @param {Object=} opt_params An optional object that may contain any valid + * HTML parameters that will be passed to the Flash movie. + * @return {boolean} Whether the function call completes successfully. + */ +gadgets.flash.embedFlash = function( + url, container, minVersion, opt_params) {}; + +// Namespace for retrieving content from remote servers via the gadgets proxy. +gadgets.io = {}; + +/** + * Gets the proxy version of the passed-in URL. + * + * @param {string} url The URL to get the proxy URL for + * @param {Object=} opt_params Optional Parameter Object. + * The following properties are supported: + * .REFRESH_INTERVAL The number of seconds that this + * content should be cached. Defaults to 3600. + * + * @return {string} The proxied version of the URL + */ +gadgets.io.getProxyUrl = function(url, opt_params) {}; + +/** + * @param {string} url The URL to request data from. + * @param {Function} callback The callback function. + * @param {Object=} opt_params Optional map of parameters sent on request, see + * gadgets.io.RequestParameters. + */ +gadgets.io.makeRequest = function(url, callback, opt_params) {}; + +/** + * Converts an input object into a URL-encoded data string. (key=value&...). + * @param {Object} fields The post fields you wish to encode. + * @return {string} The processed post data; this includes a trailing + * ampersand (&). + */ +gadgets.io.encodeValues = function(fields) {}; + +/** + * Enumeration of request parameters. + * @enum {string} + */ +gadgets.io.RequestParameters = {}; +gadgets.io.RequestParameters.AUTHORIZATION; +gadgets.io.RequestParameters.CONTENT_TYPE; +gadgets.io.RequestParameters.GET_SUMMARIES; +gadgets.io.RequestParameters.HEADERS; +gadgets.io.RequestParameters.METHOD; +gadgets.io.RequestParameters.NUM_ENTRIES; +gadgets.io.RequestParameters.OAUTH_REQUEST_TOKEN; +gadgets.io.RequestParameters.OAUTH_REQUEST_TOKEN_SECRET; +gadgets.io.RequestParameters.OAUTH_SERVICE_NAME; +gadgets.io.RequestParameters.OAUTH_TOKEN_NAME; +gadgets.io.RequestParameters.OAUTH_USE_TOKEN; +gadgets.io.RequestParameters.POST_DATA; +gadgets.io.RequestParameters.REFRESH_INTERVAL; + +/** + * Enumeration of content types that can be specified in + * {@link gadgets.io.RequestParameters.AUTHORIZATION} + * @enum {string} + */ +gadgets.io.AuthorizationType = {}; +gadgets.io.AuthorizationType.NONE; +gadgets.io.AuthorizationType.OAUTH; +gadgets.io.AuthorizationType.SIGNED; + +/** + * Enumeration of content types that can be specified in + * {@link gadgets.io.RequestParameters.CONTENT_TYPE} + * @enum {string} + */ +gadgets.io.ContentType = {}; +gadgets.io.ContentType.DOM; +gadgets.io.ContentType.FEED; +gadgets.io.ContentType.JSON; +gadgets.io.ContentType.TEXT; + +/** + * Enumeration of content types that can be specified in + * {@link gadgets.io.RequestParameters.METHOD} + * @enum {string} + */ +gadgets.io.MethodType = {}; +gadgets.io.MethodType.DELETE; +gadgets.io.MethodType.GET; +gadgets.io.MethodType.HEAD; +gadgets.io.MethodType.POST; +gadgets.io.MethodType.PUT; + +/** + * This is the response object that is passed to the callback of + * gadgets.io.makeRequest. It is never explicitly called "ResponseObject" in the + * documentation, but the documentation does refer to an object that is passed + * to the callback. This is that object. + * @see http://wiki.opensocial.org/index.php?title=Gadgets.io_(v0.9)#gadgets.io.makeRequest + */ +gadgets.io.ResponseObject = {}; +/**@type {Object}*/ gadgets.io.ResponseObject.data; +/**@type {Array.}*/ gadgets.io.ResponseObject.errors; +/**@type {Object}*/ gadgets.io.ResponseObject.headers; +/**@type {number}*/ gadgets.io.ResponseObject.rc; +/**@type {string}*/ gadgets.io.ResponseObject.text; +/**@type {string}*/ gadgets.io.ResponseObject.oauthApprovalUrl; +/**@type {string}*/ gadgets.io.ResponseObject.oauthError; +/**@type {string}*/ gadgets.io.ResponseObject.oauthErrorText; + +// Namespace for views +gadgets.views = {}; + +/** + * Returns the current view. + * @return {gadgets.views.View} The current view. + */ +gadgets.views.getCurrentView = function() {}; + +/** + * Get parameters passed into the view + * @return {Object} Map string -> string of parameters passed in. + */ +gadgets.views.getParams = function() {}; + +/** + * @return {Object} Map + * A map of all the supported views. Keys each gadgets.view.View by its name. + */ +gadgets.views.getSupportedViews = function() {}; + +/** + * @param {gadgets.views.View|string} view The view to navigate to. + * @param {Object=} opt_params Map.: Parameters to pass to the + * gadget after it has been navigated to on the surface. + * @param {string=} opt_ownerId The ID of the owner of the page to navigate to; + * defaults to the current owner. + */ +gadgets.views.requestNavigateTo = function(view, opt_params, opt_ownerId) {}; + +/** + * @constructor + */ +gadgets.views.View = function() {}; + +/** + * @return {string} Returns the name of this view. + */ +gadgets.views.View.prototype.getName = function() {} + +/** + * @return {boolean} True if the gadget is the only visible gadget; otherwise, + * false. + */ +gadgets.views.View.prototype.isOnlyVisibleGadget = function() {}; + +// Namespace for utilities. +gadgets.util = {}; + +/** + * Escapes the input using HTML entities to make it safer. + * @param {string} str The string to escape. + * @return {string} The escaped string. + */ +gadgets.util.escapeString = function(str) {}; + +/** + * Reverses escapeString + * @param {string} str The string to unescape. + * @return {string} The unescaped string. + */ +gadgets.util.unescapeString = function(str) {}; + +/** + * Returns the parameters for a feature. + * @param {string} feature The feature name. + * @return {Object} Parameters for the feature or null. + */ +gadgets.util.getFeatureParameters = function(feature) {}; + +/** + * Returns whether the specified feature is supported. + * @param {string} feature The feature name. + * @return {boolean} Whether the feature is supported. + */ +gadgets.util.hasFeature = function(feature) {}; + +/** + * Registers a function to be called when the gadget loads. + * @param {function()} fn The function to call. + */ +gadgets.util.registerOnLoadHandler = function(fn) {}; + +/** + * Parses the current document's location and returns an object representing + * the parameters in the query string. + * @param {string=} opt_url Optional URL whose parameters to parse. + * Defaults to window's current URL. + * @return {Object} Parameters from the query string. + */ +gadgets.util.getUrlParameters = function(opt_url) {}; + +/** + * Creates a closure that is suitable for passing as a callback. + * Any number of arguments may be passed to the callback; + * they will be received in the order they are passed in. + * + * @param {Object} scope The execution scope; may be null if there is no + * need to associate a specific instance of an object with this + * callback + * @param {Function} callback The callback to invoke when this is run; + * any arguments passed in will be passed after your initial arguments + * @param {...*} var_args Initial arguments to be passed to the callback + * @returns {Function} The closure. + */ +gadgets.util.makeClosure = function(scope, callback, var_args) {}; + +/** + * Attach an event listener to given DOM element (Not a gadget standard) + * + * @param {Object} elem DOM element on which to attach event. + * @param {string} eventName Event type to listen for. + * @param {function()} callback Invoked when specified event occurs. + * @param {boolean} useCapture If true, initiates capture. + */ +gadgets.util.attachBrowserEvent = function( + elem, eventName, callback, useCapture) {}; + +//Namespace for JSON processing. +gadgets.json = {}; + +/** + * Convert a JSON object into a string. + * @param {Object} json + * @return {String} The JSON as a string + */ +gadgets.json.stringify = function(json) {}; + +/** + * Convert a string into a JSON object. + * Uses safe RegEx match before parsing. + * @param {string} s The string + * @return {Object} The JSON + */ +gadgets.json.parse = function(s) {}; + +//Namespace for config. +gadgets.config = {}; + +/** + * Registers a configurable component and its configuration parameters. + * Multiple callbacks may be registered for a single component if needed. + * + * @param {string} component The name of the component to register. Should + * be the same as the fully qualified name of the feature or + * the name of a fully qualified JavaScript object reference + * (e.g. "gadgets.io"). + * @param {Object=} opt_validators Mapping of option name to validation + * functions that take the form function(data) {return isValid(data);} + * @param {Function=} opt_callback A function to be invoked when a + * configuration is registered. If passed, this function will be invoked + * immediately after a call to init has been made. Do not assume that + * dependent libraries have been configured until after init is + * complete. If you rely on this, it is better to defer calling + * dependent libraries until you can be sure that configuration is + * complete. Takes the form function(config), where config will be + * all registered config data for all components. This allows your + * component to read configuration from other components. + * @param {boolean=} opt_callOnUpdate Whether the callback shall be call + * on gadgets.config.update() as well. + */ +gadgets.config.register = function(component, opt_validators, opt_callback, + opt_callOnUpdate) {}; + +/** + * Retrieves configuration data on demand. + * + * @param {string=} opt_component The component to fetch. If not provided + * all configuration will be returned. + * @return {Object} The requested configuration, or an empty object if no + * configuration has been registered for that component. + */ +gadgets.config.get = function(opt_component) {}; + +/** + * Initializes the configuration. + * + * @param {Object} config The full set of configuration data. + * @param {Boolean=} opt_noValidation True if you want to skip validation. + */ +gadgets.config.init = function(config, opt_noValidation) {}; + + +/** + * Creates an HTML or XHTML element. + * @param {string} tagName The type of element to construct. + * @return {Element} The newly constructed element. + */ +gadgets.util.createElement = function(tagName) {}; + + +/** + * Creates an HTML or XHTML iframe element with attributes. + * @param {Object=} opt_attribs Optional set of attributes to attach. The + * only working attributes are spelled the same way in XHTML attribute + * naming (most strict, all-lower-case), HTML attribute naming (less strict, + * case-insensitive), and JavaScript property naming (some properties named + * incompatibly with XHTML/HTML). + * @return {Element} The DOM node representing body. + */ +gadgets.util.createIframeElement = function(opt_attribs) {}; + + +/** + * Gets the HTML or XHTML body element. + * @return {Element} The DOM node representing body. + */ +gadgets.util.getBodyElement = function() {}; + diff --git a/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/plusone.js b/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/plusone.js new file mode 100644 index 0000000..3f01b38 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/contrib/externs/api/gadgets/plusone.js @@ -0,0 +1,64 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Extern declarations for namespaces and functions that the + * plusone widget defines. + * + * Contact sandbar-eng@google.com for any changes. + * + * @see https://code.google.com/apis/+1button/#jsapi + * @externs + */ + +/** + * Namespace associated with Google APIs. + * @type {Object} + */ +var gapi = {}; + + +/** + * Namespace associated with Plusone API. + * @type {Object} + */ +gapi.plusone = {}; + + +/** + * This renders all +1 tags/classes in the specified container, which may be + * either an element (by value) or a string element ID. This function would be + * used only if the + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Class FilePosition

+
+java.lang.Object
+  extended by com.google.debugging.sourcemap.FilePosition
+
+
+
+
public class FilePosition
extends Object
+ + +

+Represents a position in a source file. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
FilePosition(int line, + int column) + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+ intgetColumn() + +
+           
+ intgetLine() + +
+          Returns the line number of this position.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+FilePosition

+
+public FilePosition(int line,
+                    int column)
+
+
+ + + + + + + + +
+Method Detail
+ +

+getLine

+
+public int getLine()
+
+
Returns the line number of this position. + Note: The v1 and v2 source maps use a line number with the first line + being 1, whereas the v3 source map corrects this and uses a first line + number of 0 to be consistent with the column representation. +

+

+
+
+
+
+ +

+getColumn

+
+public int getColumn()
+
+
+ +
Returns:
the character index on the line + of this position, with the first column being 0.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumer.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumer.html new file mode 100644 index 0000000..54aa068 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumer.html @@ -0,0 +1,248 @@ + + + + + +SourceMapConsumer (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Interface SourceMapConsumer

+
+
All Superinterfaces:
SourceMapping
+
+
+
All Known Implementing Classes:
SourceMapConsumerV1, SourceMapConsumerV2, SourceMapConsumerV3
+
+
+
+
public interface SourceMapConsumer
extends SourceMapping
+ + +

+A SourceMapConsumer is a SourceMapping provide that can parse from a raw + string. +

+ +

+


+ +

+ + + + + + + + + + + + +
+Method Summary
+ voidparse(String contents) + +
+          Parses the given contents containing a source map to provide initialize + a class providing SourceMapping.
+ + + + + + + +
Methods inherited from interface com.google.debugging.sourcemap.SourceMapping
getMappingForLine
+  +

+ + + + + + + + +
+Method Detail
+ +

+parse

+
+void parse(String contents)
+           throws SourceMapParseException
+
+
Parses the given contents containing a source map to provide initialize + a class providing SourceMapping. +

+

+
+
+
+ +
Throws: +
SourceMapParseException
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumerFactory.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumerFactory.html new file mode 100644 index 0000000..7b73398 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumerFactory.html @@ -0,0 +1,264 @@ + + + + + +SourceMapConsumerFactory (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Class SourceMapConsumerFactory

+
+java.lang.Object
+  extended by com.google.debugging.sourcemap.SourceMapConsumerFactory
+
+
+
+
public class SourceMapConsumerFactory
extends Object
+ + +

+Detect and parse the provided source map. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Method Summary
+static SourceMappingparse(String contents) + +
+           
+static SourceMappingparse(String contents, + SourceMapSupplier supplier) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+parse

+
+public static SourceMapping parse(String contents)
+                           throws SourceMapParseException
+
+
+
Parameters:
contents - The string representing the source map file contents. +
Returns:
The parsed source map. +
Throws: +
SourceMapParseException
+
+
+
+ +

+parse

+
+public static SourceMapping parse(String contents,
+                                  SourceMapSupplier supplier)
+                           throws SourceMapParseException
+
+
+
Parameters:
contents - The string representing the source map file contents.
supplier - A supplier for any referenced maps. +
Returns:
The parsed source map. +
Throws: +
SourceMapParseException
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumerV1.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumerV1.html new file mode 100644 index 0000000..d55034d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumerV1.html @@ -0,0 +1,309 @@ + + + + + +SourceMapConsumerV1 (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Class SourceMapConsumerV1

+
+java.lang.Object
+  extended by com.google.debugging.sourcemap.SourceMapConsumerV1
+
+
+
All Implemented Interfaces:
SourceMapConsumer, SourceMapping
+
+
+
+
public class SourceMapConsumerV1
extends Object
implements SourceMapConsumer
+ + +

+Class for parsing and representing a SourceMap, as produced by the + Closure Compiler, Caja-Compiler, etc. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
SourceMapConsumerV1() + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+ Mapping.OriginalMappinggetMappingForLine(int lineNumber, + int columnIndex) + +
+          Returns the original mapping for the line number and column position found + in the source map.
+ voidparse(String contents) + +
+          Parses the given contents containing a source map.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SourceMapConsumerV1

+
+public SourceMapConsumerV1()
+
+
+ + + + + + + + +
+Method Detail
+ +

+parse

+
+public void parse(String contents)
+           throws SourceMapParseException
+
+
Parses the given contents containing a source map. +

+

+
Specified by:
parse in interface SourceMapConsumer
+
+
+ +
Throws: +
SourceMapParseException
+
+
+
+ +

+getMappingForLine

+
+public Mapping.OriginalMapping getMappingForLine(int lineNumber,
+                                                 int columnIndex)
+
+
Description copied from interface: SourceMapping
+
Returns the original mapping for the line number and column position found + in the source map. Returns null if none is found. +

+

+
Specified by:
getMappingForLine in interface SourceMapping
+
+
+
Parameters:
lineNumber - The line number, with the first being '1'.
columnIndex - The column index, with the first being '1'.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumerV2.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumerV2.html new file mode 100644 index 0000000..c26ca32 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumerV2.html @@ -0,0 +1,336 @@ + + + + + +SourceMapConsumerV2 (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Class SourceMapConsumerV2

+
+java.lang.Object
+  extended by com.google.debugging.sourcemap.SourceMapConsumerV2
+
+
+
All Implemented Interfaces:
SourceMapConsumer, SourceMapping
+
+
+
+
public class SourceMapConsumerV2
extends Object
implements SourceMapConsumer
+ + +

+Class for parsing version 2 of the SourceMap format, as produced by the + Closure Compiler, etc. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
SourceMapConsumerV2() + +
+           
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ Mapping.OriginalMappinggetMappingForLine(int lineNumber, + int columnIndex) + +
+          Returns the original mapping for the line number and column position found + in the source map.
+ voidparse(org.json.JSONObject sourceMapRoot) + +
+          Parses the given contents containing a source map.
+ voidparse(String contents) + +
+          Parses the given contents containing a source map.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SourceMapConsumerV2

+
+public SourceMapConsumerV2()
+
+
+ + + + + + + + +
+Method Detail
+ +

+parse

+
+public void parse(String contents)
+           throws SourceMapParseException
+
+
Parses the given contents containing a source map. +

+

+
Specified by:
parse in interface SourceMapConsumer
+
+
+ +
Throws: +
SourceMapParseException
+
+
+
+ +

+parse

+
+public void parse(org.json.JSONObject sourceMapRoot)
+           throws SourceMapParseException
+
+
Parses the given contents containing a source map. +

+

+
+
+
+ +
Throws: +
SourceMapParseException
+
+
+
+ +

+getMappingForLine

+
+public Mapping.OriginalMapping getMappingForLine(int lineNumber,
+                                                 int columnIndex)
+
+
Description copied from interface: SourceMapping
+
Returns the original mapping for the line number and column position found + in the source map. Returns null if none is found. +

+

+
Specified by:
getMappingForLine in interface SourceMapping
+
+
+
Parameters:
lineNumber - The line number, with the first being '1'.
columnIndex - The column index, with the first being '1'.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumerV3.EntryVisitor.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumerV3.EntryVisitor.html new file mode 100644 index 0000000..e57a0df --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumerV3.EntryVisitor.html @@ -0,0 +1,229 @@ + + + + + +SourceMapConsumerV3.EntryVisitor (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Interface SourceMapConsumerV3.EntryVisitor

+
+
Enclosing class:
SourceMapConsumerV3
+
+
+
+
public static interface SourceMapConsumerV3.EntryVisitor
+ + +

+


+ +

+ + + + + + + + + + + + +
+Method Summary
+ voidvisit(String sourceName, + String symbolName, + FilePosition sourceStartPosition, + FilePosition startPosition, + FilePosition endPosition) + +
+           
+  +

+ + + + + + + + +
+Method Detail
+ +

+visit

+
+void visit(String sourceName,
+           String symbolName,
+           FilePosition sourceStartPosition,
+           FilePosition startPosition,
+           FilePosition endPosition)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumerV3.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumerV3.html new file mode 100644 index 0000000..b2bfba5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapConsumerV3.html @@ -0,0 +1,490 @@ + + + + + +SourceMapConsumerV3 (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Class SourceMapConsumerV3

+
+java.lang.Object
+  extended by com.google.debugging.sourcemap.SourceMapConsumerV3
+
+
+
All Implemented Interfaces:
SourceMapConsumer, SourceMapping, SourceMappingReversable
+
+
+
+
public class SourceMapConsumerV3
extends Object
implements SourceMapConsumer, SourceMappingReversable
+ + +

+Class for parsing version 3 of the SourceMap format, as produced by the + Closure Compiler, etc. + http://code.google.com/p/closure-compiler/wiki/SourceMaps +

+ +

+


+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static interfaceSourceMapConsumerV3.EntryVisitor + +
+           
+  + + + + + + + + + + +
+Constructor Summary
SourceMapConsumerV3() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ Mapping.OriginalMappinggetMappingForLine(int lineNumber, + int column) + +
+          Returns the original mapping for the line number and column position found + in the source map.
+ Collection<String>getOriginalSources() + +
+           
+ Collection<Mapping.OriginalMapping>getReverseMapping(String originalFile, + int line, + int column) + +
+          Given a source file, line, and column, return the reverse mapping (source --> target).
+ voidparse(org.json.JSONObject sourceMapRoot) + +
+          Parses the given contents containing a source map.
+ voidparse(org.json.JSONObject sourceMapRoot, + SourceMapSupplier sectionSupplier) + +
+          Parses the given contents containing a source map.
+ voidparse(String contents) + +
+          Parses the given contents containing a source map.
+ voidparse(String contents, + SourceMapSupplier sectionSupplier) + +
+          Parses the given contents containing a source map.
+ voidvisitMappings(SourceMapConsumerV3.EntryVisitor visitor) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SourceMapConsumerV3

+
+public SourceMapConsumerV3()
+
+
+ + + + + + + + +
+Method Detail
+ +

+parse

+
+public void parse(String contents)
+           throws SourceMapParseException
+
+
Parses the given contents containing a source map. +

+

+
Specified by:
parse in interface SourceMapConsumer
+
+
+ +
Throws: +
SourceMapParseException
+
+
+
+ +

+parse

+
+public void parse(String contents,
+                  SourceMapSupplier sectionSupplier)
+           throws SourceMapParseException
+
+
Parses the given contents containing a source map. +

+

+
+
+
+ +
Throws: +
SourceMapParseException
+
+
+
+ +

+parse

+
+public void parse(org.json.JSONObject sourceMapRoot)
+           throws SourceMapParseException
+
+
Parses the given contents containing a source map. +

+

+
+
+
+ +
Throws: +
SourceMapParseException
+
+
+
+ +

+parse

+
+public void parse(org.json.JSONObject sourceMapRoot,
+                  SourceMapSupplier sectionSupplier)
+           throws SourceMapParseException
+
+
Parses the given contents containing a source map. +

+

+
+
+
+ +
Throws: +
SourceMapParseException
+
+
+
+ +

+getMappingForLine

+
+public Mapping.OriginalMapping getMappingForLine(int lineNumber,
+                                                 int column)
+
+
Description copied from interface: SourceMapping
+
Returns the original mapping for the line number and column position found + in the source map. Returns null if none is found. +

+

+
Specified by:
getMappingForLine in interface SourceMapping
+
+
+
Parameters:
lineNumber - The line number, with the first being '1'.
column - The column index, with the first being '1'.
+
+
+
+ +

+getOriginalSources

+
+public Collection<String> getOriginalSources()
+
+
+
Specified by:
getOriginalSources in interface SourceMappingReversable
+
+
+ +
Returns:
the collection of original sources in this source mapping
+
+
+
+ +

+getReverseMapping

+
+public Collection<Mapping.OriginalMapping> getReverseMapping(String originalFile,
+                                                             int line,
+                                                             int column)
+
+
Description copied from interface: SourceMappingReversable
+
Given a source file, line, and column, return the reverse mapping (source --> target). + A collection is returned as in some cases (like a function being inlined), one source line + may map to more then one target location. An empty collection is returned if there were + no matches. +

+

+
Specified by:
getReverseMapping in interface SourceMappingReversable
+
+
+
Parameters:
originalFile - the source file
line - the source line
column - the source column +
Returns:
the reverse mapping (source --> target)
+
+
+
+ +

+visitMappings

+
+public void visitMappings(SourceMapConsumerV3.EntryVisitor visitor)
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapFormat.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapFormat.html new file mode 100644 index 0000000..cc08c02 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapFormat.html @@ -0,0 +1,378 @@ + + + + + +SourceMapFormat (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Enum SourceMapFormat

+
+java.lang.Object
+  extended by java.lang.Enum<SourceMapFormat>
+      extended by com.google.debugging.sourcemap.SourceMapFormat
+
+
+
All Implemented Interfaces:
Serializable, Comparable<SourceMapFormat>
+
+
+
+
public enum SourceMapFormat
extends Enum<SourceMapFormat>
+ + +

+A list of currently support SourceMap format revisions. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + +
+Enum Constant Summary
DEFAULT + +
+          The latest "stable" format
V1 + +
+          V1: The original Closure Inspector format
V2 + +
+          V2: A more compact format
V3 + +
+          V3: An even more compact format
+  + + + + + + + + + + + + + + + +
+Method Summary
+static SourceMapFormatvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static SourceMapFormat[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+DEFAULT

+
+public static final SourceMapFormat DEFAULT
+
+
The latest "stable" format +

+

+
+
+
+ +

+V1

+
+public static final SourceMapFormat V1
+
+
V1: The original Closure Inspector format +

+

+
+
+
+ +

+V2

+
+public static final SourceMapFormat V2
+
+
V2: A more compact format +

+

+
+
+
+ +

+V3

+
+public static final SourceMapFormat V3
+
+
V3: An even more compact format +

+

+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static SourceMapFormat[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (SourceMapFormat c : SourceMapFormat.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static SourceMapFormat valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGenerator.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGenerator.html new file mode 100644 index 0000000..0d87d5f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGenerator.html @@ -0,0 +1,392 @@ + + + + + +SourceMapGenerator (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Interface SourceMapGenerator

+
+
All Known Implementing Classes:
SourceMapGeneratorV1, SourceMapGeneratorV2, SourceMapGeneratorV3
+
+
+
+
public interface SourceMapGenerator
+ + +

+Collects information mapping the generated (compiled) source back to + its original source for debugging purposes +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidaddMapping(String sourceName, + String symbolName, + FilePosition sourceStartPosition, + FilePosition outputStartPosition, + FilePosition outputEndPosition) + +
+          Adds a mapping for the given node.
+ voidappendIndexMapTo(Appendable out, + String name, + List<SourceMapSection> sections) + +
+          Appends the index source map to the given buffer.
+ voidappendTo(Appendable out, + String name) + +
+          Appends the source map to the given buffer.
+ voidreset() + +
+          Resets the source map for reuse.
+ voidsetStartingPosition(int offsetLine, + int offsetIndex) + +
+          Sets the source code that exists in the buffer for which the + generated code is being generated.
+ voidsetWrapperPrefix(String prefix) + +
+          Sets the prefix used for wrapping the generated source file before + it is written.
+ voidvalidate(boolean validate) + +
+          Whether to perform additional validation on the source map.
+  +

+ + + + + + + + +
+Method Detail
+ +

+appendTo

+
+void appendTo(Appendable out,
+              String name)
+              throws IOException
+
+
Appends the source map to the given buffer. +

+

+
Parameters:
out - The stream to which the map will be appended.
name - The name of the generated source file that this source map + represents. +
Throws: +
IOException
+
+
+
+ +

+appendIndexMapTo

+
+void appendIndexMapTo(Appendable out,
+                      String name,
+                      List<SourceMapSection> sections)
+                      throws IOException
+
+
Appends the index source map to the given buffer. +

+

+
Parameters:
out - The stream to which the map will be appended.
name - The name of the generated source file that this source map + represents.
sections - An ordered list of map sections to include in the index. +
Throws: +
IOException
+
+
+
+ +

+reset

+
+void reset()
+
+
Resets the source map for reuse. A reset needs to be called between + each generated output file. +

+

+
+
+
+
+ +

+addMapping

+
+void addMapping(String sourceName,
+                @Nullable
+                String symbolName,
+                FilePosition sourceStartPosition,
+                FilePosition outputStartPosition,
+                FilePosition outputEndPosition)
+
+
Adds a mapping for the given node. Mappings must be added in order. +

+

+
Parameters:
sourceName - The file name to use in the generate source map + to represent this source.
symbolName - The symbol name associated with this position in the + source map.
sourceStartPosition - The starting position in the original source for + represented range outputStartPosition to outputEndPosition in the + generated file.
outputStartPosition - The position on the starting line
outputEndPosition - The position on the ending line.
+
+
+
+ +

+setWrapperPrefix

+
+void setWrapperPrefix(String prefix)
+
+
Sets the prefix used for wrapping the generated source file before + it is written. This ensures that the source map is adjusted for the + change in character offsets. +

+

+
Parameters:
prefix - The prefix that is added before the generated source code.
+
+
+
+ +

+setStartingPosition

+
+void setStartingPosition(int offsetLine,
+                         int offsetIndex)
+
+
Sets the source code that exists in the buffer for which the + generated code is being generated. This ensures that the source map + accurately reflects the fact that the source is being appended to + an existing buffer and as such, does not start at line 0, position 0 + but rather some other line and position. +

+

+
Parameters:
offsetLine - The index of the current line being printed.
offsetIndex - The column index of the current character being printed.
+
+
+
+ +

+validate

+
+void validate(boolean validate)
+
+
Whether to perform additional validation on the source map. +

+

+
Parameters:
validate -
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGeneratorFactory.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGeneratorFactory.html new file mode 100644 index 0000000..c39365c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGeneratorFactory.html @@ -0,0 +1,285 @@ + + + + + +SourceMapGeneratorFactory (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Class SourceMapGeneratorFactory

+
+java.lang.Object
+  extended by com.google.debugging.sourcemap.SourceMapGeneratorFactory
+
+
+
+
public class SourceMapGeneratorFactory
extends Object
+ + +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
SourceMapGeneratorFactory() + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static SourceMapGeneratorgetInstance() + +
+           
+static SourceMapGeneratorgetInstance(SourceMapFormat format) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SourceMapGeneratorFactory

+
+public SourceMapGeneratorFactory()
+
+
+ + + + + + + + +
+Method Detail
+ +

+getInstance

+
+public static SourceMapGenerator getInstance()
+
+
+ +
Returns:
The appropriate source map object for the given source map format.
+
+
+
+ +

+getInstance

+
+public static SourceMapGenerator getInstance(SourceMapFormat format)
+
+
+ +
Returns:
The appropriate source map object for the given source map format.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGeneratorV1.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGeneratorV1.html new file mode 100644 index 0000000..ce5c78c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGeneratorV1.html @@ -0,0 +1,457 @@ + + + + + +SourceMapGeneratorV1 (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Class SourceMapGeneratorV1

+
+java.lang.Object
+  extended by com.google.debugging.sourcemap.SourceMapGeneratorV1
+
+
+
All Implemented Interfaces:
SourceMapGenerator
+
+
+
+
public class SourceMapGeneratorV1
extends Object
implements SourceMapGenerator
+ + +

+Collects information mapping the generated (compiled) source back to + its original source for debugging purposes. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
SourceMapGeneratorV1() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidaddMapping(String sourceName, + String symbolName, + FilePosition sourceStartPosition, + FilePosition startPosition, + FilePosition endPosition) + +
+          Adds a mapping for the given node.
+ voidappendIndexMapTo(Appendable out, + String name, + List<SourceMapSection> appSections) + +
+          Appends the index source map to the given buffer.
+ voidappendTo(Appendable out, + String name) + +
+          Appends the source map in LavaBug format to the given buffer.
+ voidreset() + +
+          Resets the source map for reuse for the generation of a new source file.
+ voidsetStartingPosition(int offsetLine, + int offsetIndex) + +
+          Sets the source code that exists in the buffer to which the + generated code is being generated.
+ voidsetWrapperPrefix(String prefix) + +
+          Sets the prefix used for wrapping the generated source file before + it is output.
+ voidvalidate(boolean validate) + +
+          Whether to perform additional validation on the source map.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SourceMapGeneratorV1

+
+public SourceMapGeneratorV1()
+
+
+ + + + + + + + +
+Method Detail
+ +

+addMapping

+
+public void addMapping(String sourceName,
+                       @Nullable
+                       String symbolName,
+                       FilePosition sourceStartPosition,
+                       FilePosition startPosition,
+                       FilePosition endPosition)
+
+
Adds a mapping for the given node. Mappings must be added in order. +

+

+
Specified by:
addMapping in interface SourceMapGenerator
+
+
+
Parameters:
startPosition - The position on the starting line
endPosition - The position on the ending line.
sourceName - The file name to use in the generate source map + to represent this source.
symbolName - The symbol name associated with this position in the + source map.
sourceStartPosition - The starting position in the original source for + represented range outputStartPosition to outputEndPosition in the + generated file.
+
+
+
+ +

+setWrapperPrefix

+
+public void setWrapperPrefix(String prefix)
+
+
Sets the prefix used for wrapping the generated source file before + it is output. This ensures that the source map is adjusted as + needed. +

+

+
Specified by:
setWrapperPrefix in interface SourceMapGenerator
+
+
+
Parameters:
prefix - The prefix that is added before the generated source code.
+
+
+
+ +

+setStartingPosition

+
+public void setStartingPosition(int offsetLine,
+                                int offsetIndex)
+
+
Sets the source code that exists in the buffer to which the + generated code is being generated. This ensures that the source map + accurately reflects the fact that the source is being appended to + an existing buffer and as such, does not start at line 0, position 0 + but rather some other line and position. +

+

+
Specified by:
setStartingPosition in interface SourceMapGenerator
+
+
+
Parameters:
offsetLine - The index of the current line being printed.
offsetIndex - The column index of the current character being printed.
+
+
+
+ +

+reset

+
+public void reset()
+
+
Resets the source map for reuse for the generation of a new source file. +

+

+
Specified by:
reset in interface SourceMapGenerator
+
+
+
+
+
+
+ +

+appendTo

+
+public void appendTo(Appendable out,
+                     String name)
+              throws IOException
+
+
Appends the source map in LavaBug format to the given buffer. +

+

+
Specified by:
appendTo in interface SourceMapGenerator
+
+
+
Parameters:
out - The stream to which the map will be appended.
name - The name of the generated source file that this source map + represents. +
Throws: +
IOException
+
+
+
+ +

+validate

+
+public void validate(boolean validate)
+
+
Description copied from interface: SourceMapGenerator
+
Whether to perform additional validation on the source map. +

+

+
Specified by:
validate in interface SourceMapGenerator
+
+
+
+
+
+
+ +

+appendIndexMapTo

+
+public void appendIndexMapTo(Appendable out,
+                             String name,
+                             List<SourceMapSection> appSections)
+
+
Description copied from interface: SourceMapGenerator
+
Appends the index source map to the given buffer. +

+

+
Specified by:
appendIndexMapTo in interface SourceMapGenerator
+
+
+
Parameters:
out - The stream to which the map will be appended.
name - The name of the generated source file that this source map + represents.
appSections - An ordered list of map sections to include in the index.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGeneratorV2.LineMapEncoder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGeneratorV2.LineMapEncoder.html new file mode 100644 index 0000000..231cc39 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGeneratorV2.LineMapEncoder.html @@ -0,0 +1,329 @@ + + + + + +SourceMapGeneratorV2.LineMapEncoder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Class SourceMapGeneratorV2.LineMapEncoder

+
+java.lang.Object
+  extended by com.google.debugging.sourcemap.SourceMapGeneratorV2.LineMapEncoder
+
+
+
Enclosing class:
SourceMapGeneratorV2
+
+
+
+
public static class SourceMapGeneratorV2.LineMapEncoder
extends Object
+ + +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
SourceMapGeneratorV2.LineMapEncoder() + +
+           
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static voidencodeEntry(Appendable out, + int id, + int lastId, + int reps) + +
+          The source map line map is consists of a series of entries each + representing a map entry and a repetition count of that entry.
+static intgetRelativeMappingId(int id, + int idLength, + int lastId) + +
+           
+static intgetRelativeMappingIdLength(int rawId, + int lastId) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SourceMapGeneratorV2.LineMapEncoder

+
+public SourceMapGeneratorV2.LineMapEncoder()
+
+
+ + + + + + + + +
+Method Detail
+ +

+encodeEntry

+
+public static void encodeEntry(Appendable out,
+                               int id,
+                               int lastId,
+                               int reps)
+                        throws IOException
+
+
The source map line map is consists of a series of entries each + representing a map entry and a repetition count of that entry. +

+

+
Parameters:
out - The entry destination.
id - The id for the entry.
lastId - The previous id written, used to generate a relative + map id.
reps - The number of times the id is repeated in the map. +
Throws: +
IOException
+
+
+
+ +

+getRelativeMappingId

+
+public static int getRelativeMappingId(int id,
+                                       int idLength,
+                                       int lastId)
+
+
+
Parameters:
idLength - the length relative id, when encoded in as a base64 + value. @see #getRelativeMappingIdLength +
Returns:
A value relative to the the lastId. Negative value are + represented as a two-complement value.
+
+
+
+ +

+getRelativeMappingIdLength

+
+public static int getRelativeMappingIdLength(int rawId,
+                                             int lastId)
+
+
+ +
Returns:
The length of the base64 number needed to include the id.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGeneratorV2.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGeneratorV2.html new file mode 100644 index 0000000..14ef3ce --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGeneratorV2.html @@ -0,0 +1,520 @@ + + + + + +SourceMapGeneratorV2 (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Class SourceMapGeneratorV2

+
+java.lang.Object
+  extended by com.google.debugging.sourcemap.SourceMapGeneratorV2
+
+
+
All Implemented Interfaces:
SourceMapGenerator
+
+
+
+
public class SourceMapGeneratorV2
extends Object
implements SourceMapGenerator
+ + +

+Collects information mapping the generated (compiled) source back to + its original source for debugging purposes. +

+ +

+


+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classSourceMapGeneratorV2.LineMapEncoder + +
+           
+  + + + + + + + + + + +
+Constructor Summary
SourceMapGeneratorV2() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidaddMapping(String sourceName, + String symbolName, + FilePosition sourceStartPosition, + FilePosition startPosition, + FilePosition endPosition) + +
+          Adds a mapping for the given node.
+ voidappendIndexMapTo(Appendable out, + String name, + List<SourceMapSection> appSections) + +
+          Appends the index source map to the given buffer.
+ voidappendTo(Appendable out, + String name) + +
+          Writes out the source map in the following format (line numbers are for + reference only and are not part of the format): + + 1.
+ voidreset() + +
+          Resets the source map for reuse.
+ voidsetStartingPosition(int offsetLine, + int offsetIndex) + +
+          Sets the source code that exists in the buffer for which the + generated code is being generated.
+ voidsetWrapperPrefix(String prefix) + +
+          Sets the prefix used for wrapping the generated source file before + it is written.
+ voidvalidate(boolean validate) + +
+          Whether to perform additional validation on the source map.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SourceMapGeneratorV2

+
+public SourceMapGeneratorV2()
+
+
+ + + + + + + + +
+Method Detail
+ +

+reset

+
+public void reset()
+
+
Resets the source map for reuse. A reset needs to be called between + each generated output file. +

+

+
Specified by:
reset in interface SourceMapGenerator
+
+
+
+
+
+
+ +

+validate

+
+public void validate(boolean validate)
+
+
Description copied from interface: SourceMapGenerator
+
Whether to perform additional validation on the source map. +

+

+
Specified by:
validate in interface SourceMapGenerator
+
+
+
Parameters:
validate - Whether to perform (potentially costly) validation on the + generated source map.
+
+
+
+ +

+setWrapperPrefix

+
+public void setWrapperPrefix(String prefix)
+
+
Sets the prefix used for wrapping the generated source file before + it is written. This ensures that the source map is adjusted for the + change in character offsets. +

+

+
Specified by:
setWrapperPrefix in interface SourceMapGenerator
+
+
+
Parameters:
prefix - The prefix that is added before the generated source code.
+
+
+
+ +

+setStartingPosition

+
+public void setStartingPosition(int offsetLine,
+                                int offsetIndex)
+
+
Sets the source code that exists in the buffer for which the + generated code is being generated. This ensures that the source map + accurately reflects the fact that the source is being appended to + an existing buffer and as such, does not start at line 0, position 0 + but rather some other line and position. +

+

+
Specified by:
setStartingPosition in interface SourceMapGenerator
+
+
+
Parameters:
offsetLine - The index of the current line being printed.
offsetIndex - The column index of the current character being printed.
+
+
+
+ +

+addMapping

+
+public void addMapping(String sourceName,
+                       @Nullable
+                       String symbolName,
+                       FilePosition sourceStartPosition,
+                       FilePosition startPosition,
+                       FilePosition endPosition)
+
+
Adds a mapping for the given node. Mappings must be added in order. +

+

+
Specified by:
addMapping in interface SourceMapGenerator
+
+
+
Parameters:
startPosition - The position on the starting line
endPosition - The position on the ending line.
sourceName - The file name to use in the generate source map + to represent this source.
symbolName - The symbol name associated with this position in the + source map.
sourceStartPosition - The starting position in the original source for + represented range outputStartPosition to outputEndPosition in the + generated file.
+
+
+
+ +

+appendTo

+
+public void appendTo(Appendable out,
+                     String name)
+              throws IOException
+
+
Writes out the source map in the following format (line numbers are for + reference only and are not part of the format): + + 1. { + 2. version: 2, + 3. file: "out.js" + 4. lineCount: 2 + 5. lineMaps: [ + 6. "ABAAA", + 7. "ABAA" + 8. ], + 9. sourceRoot: "", + 10. sources: ["foo.js", "bar.js"], + 11. names: ["src", "maps", "are", "fun"], + 12. mappings: [ + 13. [1, 1, 2, 4], + 14. [2, 1, 2, "yack"], + 15. ], + 16. } + + Line 1: The entire file is a single JSON object + Line 2: File revision (always the first entry in the object) + Line 3: The name of the file that this source map is associated with. + Line 4: The number of lines represented in the sourcemap. + Line 5: "lineMaps" field is a JSON array, where each entry represents a + line in the generated text. + Line 6: A line entry, representing a series of line segments, where each + segment encodes an mappings-id and repetition count. + Line 9: An optional source root, useful for relocating source files on a + server or removing repeated prefix values in the "sources" entry. + Line 10: A list of sources used by the "mappings" entry relative to the + sourceRoot. + Line 11: A list of symbol names used by the "mapping" entry. This list + may be incomplete. + Line 12: The mappings field. + Line 13: Each entry represent a block of text in the original source, and + consists four fields: + The source file name + The line in the source file the text begins + The column in the line that the text begins + An optional name (from the original source) that this entry represents. + This can either be an string or index into the "names" field. +

+

+
Specified by:
appendTo in interface SourceMapGenerator
+
+
+
Parameters:
out - The stream to which the map will be appended.
name - The name of the generated source file that this source map + represents. +
Throws: +
IOException
+
+
+
+ +

+appendIndexMapTo

+
+public void appendIndexMapTo(Appendable out,
+                             String name,
+                             List<SourceMapSection> appSections)
+
+
Description copied from interface: SourceMapGenerator
+
Appends the index source map to the given buffer. +

+

+
Specified by:
appendIndexMapTo in interface SourceMapGenerator
+
+
+
Parameters:
out - The stream to which the map will be appended.
name - The name of the generated source file that this source map + represents.
appSections - An ordered list of map sections to include in the index.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGeneratorV3.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGeneratorV3.html new file mode 100644 index 0000000..bd352be --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapGeneratorV3.html @@ -0,0 +1,516 @@ + + + + + +SourceMapGeneratorV3 (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Class SourceMapGeneratorV3

+
+java.lang.Object
+  extended by com.google.debugging.sourcemap.SourceMapGeneratorV3
+
+
+
All Implemented Interfaces:
SourceMapGenerator
+
+
+
+
public class SourceMapGeneratorV3
extends Object
implements SourceMapGenerator
+ + +

+Collects information mapping the generated (compiled) source back to + its original source for debugging purposes. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
SourceMapGeneratorV3() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidaddMapping(String sourceName, + String symbolName, + FilePosition sourceStartPosition, + FilePosition startPosition, + FilePosition endPosition) + +
+          Adds a mapping for the given node.
+ voidappendIndexMapTo(Appendable out, + String name, + List<SourceMapSection> sections) + +
+          Appends the index source map to the given buffer.
+ voidappendTo(Appendable out, + String name) + +
+          Writes out the source map in the following format (line numbers are for + reference only and are not part of the format): + + 1.
+ voidmergeMapSection(int line, + int column, + String mapSectionContents) + +
+           
+ voidreset() + +
+          Resets the source map for reuse.
+ voidsetStartingPosition(int offsetLine, + int offsetIndex) + +
+          Sets the source code that exists in the buffer for which the + generated code is being generated.
+ voidsetWrapperPrefix(String prefix) + +
+          Sets the prefix used for wrapping the generated source file before + it is written.
+ voidvalidate(boolean validate) + +
+          Whether to perform additional validation on the source map.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SourceMapGeneratorV3

+
+public SourceMapGeneratorV3()
+
+
+ + + + + + + + +
+Method Detail
+ +

+reset

+
+public void reset()
+
+
Resets the source map for reuse. A reset needs to be called between + each generated output file. +

+

+
Specified by:
reset in interface SourceMapGenerator
+
+
+
+
+
+
+ +

+validate

+
+public void validate(boolean validate)
+
+
Description copied from interface: SourceMapGenerator
+
Whether to perform additional validation on the source map. +

+

+
Specified by:
validate in interface SourceMapGenerator
+
+
+
Parameters:
validate - Whether to perform (potentially costly) validation on the + generated source map.
+
+
+
+ +

+setWrapperPrefix

+
+public void setWrapperPrefix(String prefix)
+
+
Sets the prefix used for wrapping the generated source file before + it is written. This ensures that the source map is adjusted for the + change in character offsets. +

+

+
Specified by:
setWrapperPrefix in interface SourceMapGenerator
+
+
+
Parameters:
prefix - The prefix that is added before the generated source code.
+
+
+
+ +

+setStartingPosition

+
+public void setStartingPosition(int offsetLine,
+                                int offsetIndex)
+
+
Sets the source code that exists in the buffer for which the + generated code is being generated. This ensures that the source map + accurately reflects the fact that the source is being appended to + an existing buffer and as such, does not start at line 0, position 0 + but rather some other line and position. +

+

+
Specified by:
setStartingPosition in interface SourceMapGenerator
+
+
+
Parameters:
offsetLine - The index of the current line being printed.
offsetIndex - The column index of the current character being printed.
+
+
+
+ +

+addMapping

+
+public void addMapping(String sourceName,
+                       @Nullable
+                       String symbolName,
+                       FilePosition sourceStartPosition,
+                       FilePosition startPosition,
+                       FilePosition endPosition)
+
+
Adds a mapping for the given node. Mappings must be added in order. +

+

+
Specified by:
addMapping in interface SourceMapGenerator
+
+
+
Parameters:
startPosition - The position on the starting line
endPosition - The position on the ending line.
sourceName - The file name to use in the generate source map + to represent this source.
symbolName - The symbol name associated with this position in the + source map.
sourceStartPosition - The starting position in the original source for + represented range outputStartPosition to outputEndPosition in the + generated file.
+
+
+
+ +

+mergeMapSection

+
+public void mergeMapSection(int line,
+                            int column,
+                            String mapSectionContents)
+                     throws SourceMapParseException
+
+
+
+
+
+ +
Throws: +
SourceMapParseException
+
+
+
+ +

+appendTo

+
+public void appendTo(Appendable out,
+                     String name)
+              throws IOException
+
+
Writes out the source map in the following format (line numbers are for + reference only and are not part of the format): + + 1. { + 2. version: 3, + 3. file: "out.js", + 4. lineCount: 2, + 5. sourceRoot: "", + 6. sources: ["foo.js", "bar.js"], + 7. names: ["src", "maps", "are", "fun"], + 8. mappings: "a;;abcde,abcd,a;" + 9. } + + Line 1: The entire file is a single JSON object + Line 2: File revision (always the first entry in the object) + Line 3: The name of the file that this source map is associated with. + Line 4: The number of lines represented in the sourcemap. + Line 5: An optional source root, useful for relocating source files on a + server or removing repeated prefix values in the "sources" entry. + Line 6: A list of sources used by the "mappings" entry relative to the + sourceRoot. + Line 7: A list of symbol names used by the "mapping" entry. This list + may be incomplete. + Line 8: The mappings field. +

+

+
Specified by:
appendTo in interface SourceMapGenerator
+
+
+
Parameters:
out - The stream to which the map will be appended.
name - The name of the generated source file that this source map + represents. +
Throws: +
IOException
+
+
+
+ +

+appendIndexMapTo

+
+public void appendIndexMapTo(Appendable out,
+                             String name,
+                             List<SourceMapSection> sections)
+                      throws IOException
+
+
Appends the index source map to the given buffer. +

+

+
Specified by:
appendIndexMapTo in interface SourceMapGenerator
+
+
+
Parameters:
out - The stream to which the map will be appended.
name - The name of the generated source file that this source map + represents.
sections - An ordered list of map sections to include in the index. +
Throws: +
IOException
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapParseException.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapParseException.html new file mode 100644 index 0000000..03a4d1c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapParseException.html @@ -0,0 +1,256 @@ + + + + + +SourceMapParseException (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Class SourceMapParseException

+
+java.lang.Object
+  extended by java.lang.Throwable
+      extended by java.lang.Exception
+          extended by com.google.debugging.sourcemap.SourceMapParseException
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class SourceMapParseException
extends Exception
+ + +

+Throw if an invalid or unknown source map is encountered. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Constructor Summary
SourceMapParseException(String message) + +
+           
+  + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class java.lang.Throwable
fillInStackTrace, getCause, getLocalizedMessage, getMessage, getStackTrace, initCause, printStackTrace, printStackTrace, printStackTrace, setStackTrace, toString
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SourceMapParseException

+
+public SourceMapParseException(String message)
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapSection.SectionType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapSection.SectionType.html new file mode 100644 index 0000000..58513f3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapSection.SectionType.html @@ -0,0 +1,337 @@ + + + + + +SourceMapSection.SectionType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Enum SourceMapSection.SectionType

+
+java.lang.Object
+  extended by java.lang.Enum<SourceMapSection.SectionType>
+      extended by com.google.debugging.sourcemap.SourceMapSection.SectionType
+
+
+
All Implemented Interfaces:
Serializable, Comparable<SourceMapSection.SectionType>
+
+
+
Enclosing class:
SourceMapSection
+
+
+
+
public static enum SourceMapSection.SectionType
extends Enum<SourceMapSection.SectionType>
+ + +

+


+ +

+ + + + + + + + + + + + + +
+Enum Constant Summary
MAP + +
+           
URL + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static SourceMapSection.SectionTypevalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static SourceMapSection.SectionType[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+URL

+
+public static final SourceMapSection.SectionType URL
+
+
+
+
+
+ +

+MAP

+
+public static final SourceMapSection.SectionType MAP
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static SourceMapSection.SectionType[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (SourceMapSection.SectionType c : SourceMapSection.SectionType.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static SourceMapSection.SectionType valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapSection.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapSection.html new file mode 100644 index 0000000..0bfa324 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapSection.html @@ -0,0 +1,425 @@ + + + + + +SourceMapSection (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Class SourceMapSection

+
+java.lang.Object
+  extended by com.google.debugging.sourcemap.SourceMapSection
+
+
+
+
public class SourceMapSection
extends Object
+ + +

+A class representing a partial source map. +

+ +

+


+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classSourceMapSection.SectionType + +
+           
+  + + + + + + + + + + +
+Constructor Summary
SourceMapSection(String sectionUrl, + int line, + int column) + +
+          Deprecated.  
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static SourceMapSectionforMap(String value, + int line, + int column) + +
+           
+static SourceMapSectionforURL(String value, + int line, + int column) + +
+           
+ intgetColumn() + +
+           
+ intgetLine() + +
+           
+ SourceMapSection.SectionTypegetSectionType() + +
+           
+ StringgetSectionUrl() + +
+          Deprecated.  
+ StringgetSectionValue() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SourceMapSection

+
+@Deprecated
+public SourceMapSection(String sectionUrl,
+                                   int line,
+                                   int column)
+
+
Deprecated.  +

+

+
Parameters:
sectionUrl - The url for the partial sourcemap
line - The number of lines into the file where the represented section + starts.
column - The number of characters into the line where the represented + section starts.
+
+ + + + + + + + +
+Method Detail
+ +

+forMap

+
+public static SourceMapSection forMap(String value,
+                                      int line,
+                                      int column)
+
+
+
+
+
+
+ +

+forURL

+
+public static SourceMapSection forURL(String value,
+                                      int line,
+                                      int column)
+
+
+
+
+
+
+ +

+getSectionType

+
+public SourceMapSection.SectionType getSectionType()
+
+
+
+
+
+
+ +

+getSectionUrl

+
+@Deprecated
+public String getSectionUrl()
+
+
Deprecated.  +

+

+ +
Returns:
the name of the map
+
+
+
+ +

+getSectionValue

+
+public String getSectionValue()
+
+
+ +
Returns:
the value that represents the map for this section.
+
+
+
+ +

+getLine

+
+public int getLine()
+
+
+ +
Returns:
the starting line for this section
+
+
+
+ +

+getColumn

+
+public int getColumn()
+
+
+ +
Returns:
the column for this section
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapSupplier.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapSupplier.html new file mode 100644 index 0000000..cd3d34f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapSupplier.html @@ -0,0 +1,227 @@ + + + + + +SourceMapSupplier (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Interface SourceMapSupplier

+
+
+
public interface SourceMapSupplier
+ + +

+A class for mapping source map names to the actual contents. Used + when parsing index maps. +

+ +

+


+ +

+ + + + + + + + + + + + +
+Method Summary
+ StringgetSourceMap(String url) + +
+           
+  +

+ + + + + + + + +
+Method Detail
+ +

+getSourceMap

+
+String getSourceMap(String url)
+                    throws IOException
+
+
+
Parameters:
url - The url of the source map. +
Returns:
The contents of the map associated with the url +
Throws: +
IOException
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapping.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapping.html new file mode 100644 index 0000000..bd4f247 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMapping.html @@ -0,0 +1,235 @@ + + + + + +SourceMapping (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Interface SourceMapping

+
+
All Known Subinterfaces:
SourceMapConsumer, SourceMappingReversable
+
+
+
All Known Implementing Classes:
SourceMapConsumerV1, SourceMapConsumerV2, SourceMapConsumerV3
+
+
+
+
public interface SourceMapping
+ + +

+Interface for provide a way of mapping (line, column) positions back to + positions in the original (uncompiled) source code. +

+ +

+


+ +

+ + + + + + + + + + + + +
+Method Summary
+ Mapping.OriginalMappinggetMappingForLine(int lineNumber, + int columnIndex) + +
+          Returns the original mapping for the line number and column position found + in the source map.
+  +

+ + + + + + + + +
+Method Detail
+ +

+getMappingForLine

+
+Mapping.OriginalMapping getMappingForLine(int lineNumber,
+                                          int columnIndex)
+
+
Returns the original mapping for the line number and column position found + in the source map. Returns null if none is found. +

+

+
Parameters:
lineNumber - The line number, with the first being '1'.
columnIndex - The column index, with the first being '1'.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMappingReversable.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMappingReversable.html new file mode 100644 index 0000000..0e4a66c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/SourceMappingReversable.html @@ -0,0 +1,274 @@ + + + + + +SourceMappingReversable (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap +
+Interface SourceMappingReversable

+
+
All Superinterfaces:
SourceMapping
+
+
+
All Known Implementing Classes:
SourceMapConsumerV3
+
+
+
+
public interface SourceMappingReversable
extends SourceMapping
+ + +

+A SourceMappingReversable is a SourceMapping that can provide the reverse + (source --> target) source mapping. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Method Summary
+ Collection<String>getOriginalSources() + +
+           
+ Collection<Mapping.OriginalMapping>getReverseMapping(String originalFile, + int line, + int column) + +
+          Given a source file, line, and column, return the reverse mapping (source --> target).
+ + + + + + + +
Methods inherited from interface com.google.debugging.sourcemap.SourceMapping
getMappingForLine
+  +

+ + + + + + + + +
+Method Detail
+ +

+getOriginalSources

+
+Collection<String> getOriginalSources()
+
+
+
+
+
+ +
Returns:
the collection of original sources in this source mapping
+
+
+
+ +

+getReverseMapping

+
+Collection<Mapping.OriginalMapping> getReverseMapping(String originalFile,
+                                                      int line,
+                                                      int column)
+
+
Given a source file, line, and column, return the reverse mapping (source --> target). + A collection is returned as in some cases (like a function being inlined), one source line + may map to more then one target location. An empty collection is returned if there were + no matches. +

+

+
+
+
+
Parameters:
originalFile - the source file
line - the source line
column - the source column +
Returns:
the reverse mapping (source --> target)
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/package-frame.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/package-frame.html new file mode 100644 index 0000000..1cacf89 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/package-frame.html @@ -0,0 +1,95 @@ + + + + + +com.google.debugging.sourcemap (Compiler) + + + + + + + + + + +com.google.debugging.sourcemap + + + + +
+Interfaces  + +
+SourceMapConsumer +
+SourceMapConsumerV3.EntryVisitor +
+SourceMapGenerator +
+SourceMapping +
+SourceMappingReversable +
+SourceMapSupplier
+ + + + + + +
+Classes  + +
+FilePosition +
+SourceMapConsumerFactory +
+SourceMapConsumerV1 +
+SourceMapConsumerV2 +
+SourceMapConsumerV3 +
+SourceMapGeneratorFactory +
+SourceMapGeneratorV1 +
+SourceMapGeneratorV2 +
+SourceMapGeneratorV2.LineMapEncoder +
+SourceMapGeneratorV3 +
+SourceMapSection
+ + + + + + +
+Enums  + +
+SourceMapFormat +
+SourceMapSection.SectionType
+ + + + + + +
+Exceptions  + +
+SourceMapParseException
+ + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/package-summary.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/package-summary.html new file mode 100644 index 0000000..2f48b67 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/package-summary.html @@ -0,0 +1,300 @@ + + + + + +com.google.debugging.sourcemap (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+

+Package com.google.debugging.sourcemap +

+Provides utilities to the creation and use of source maps. +

+See: +
+          Description +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Interface Summary
SourceMapConsumerA SourceMapConsumer is a SourceMapping provide that can parse from a raw + string.
SourceMapConsumerV3.EntryVisitor 
SourceMapGeneratorCollects information mapping the generated (compiled) source back to + its original source for debugging purposes
SourceMappingInterface for provide a way of mapping (line, column) positions back to + positions in the original (uncompiled) source code.
SourceMappingReversableA SourceMappingReversable is a SourceMapping that can provide the reverse + (source --> target) source mapping.
SourceMapSupplierA class for mapping source map names to the actual contents.
+  + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Class Summary
FilePositionRepresents a position in a source file.
SourceMapConsumerFactoryDetect and parse the provided source map.
SourceMapConsumerV1Class for parsing and representing a SourceMap, as produced by the + Closure Compiler, Caja-Compiler, etc.
SourceMapConsumerV2Class for parsing version 2 of the SourceMap format, as produced by the + Closure Compiler, etc.
SourceMapConsumerV3Class for parsing version 3 of the SourceMap format, as produced by the + Closure Compiler, etc.
SourceMapGeneratorFactory 
SourceMapGeneratorV1Collects information mapping the generated (compiled) source back to + its original source for debugging purposes.
SourceMapGeneratorV2Collects information mapping the generated (compiled) source back to + its original source for debugging purposes.
SourceMapGeneratorV2.LineMapEncoder 
SourceMapGeneratorV3Collects information mapping the generated (compiled) source back to + its original source for debugging purposes.
SourceMapSectionA class representing a partial source map.
+  + +

+ + + + + + + + + + + + + +
+Enum Summary
SourceMapFormatA list of currently support SourceMap format revisions.
SourceMapSection.SectionType 
+  + +

+ + + + + + + + + +
+Exception Summary
SourceMapParseExceptionThrow if an invalid or unknown source map is encountered.
+  + +

+

+Package com.google.debugging.sourcemap Description +

+ +

+Provides utilities to the creation and use of source maps. +

+ +

+

+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/package-tree.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/package-tree.html new file mode 100644 index 0000000..b8110bf --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/package-tree.html @@ -0,0 +1,193 @@ + + + + + +com.google.debugging.sourcemap Class Hierarchy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Hierarchy For Package com.google.debugging.sourcemap +

+
+
+
Package Hierarchies:
All Packages
+
+

+Class Hierarchy +

+ +

+Interface Hierarchy +

+ +

+Enum Hierarchy +

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.LineMapping.Builder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.LineMapping.Builder.html new file mode 100644 index 0000000..3c870ab --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.LineMapping.Builder.html @@ -0,0 +1,878 @@ + + + + + +Mapping.LineMapping.Builder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap.proto +
+Class Mapping.LineMapping.Builder

+
+java.lang.Object
+  extended by com.google.protobuf.AbstractMessageLite.Builder<BuilderType>
+      extended by com.google.protobuf.AbstractMessage.Builder<BuilderType>
+          extended by com.google.protobuf.GeneratedMessage.Builder<Mapping.LineMapping.Builder>
+              extended by com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder
+
+
+
All Implemented Interfaces:
Mapping.LineMappingOrBuilder, com.google.protobuf.Message.Builder, com.google.protobuf.MessageLite.Builder, com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder, Cloneable
+
+
+
Enclosing class:
Mapping.LineMapping
+
+
+
+
public static final class Mapping.LineMapping.Builder
extends com.google.protobuf.GeneratedMessage.Builder<Mapping.LineMapping.Builder>
implements Mapping.LineMappingOrBuilder
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ Mapping.LineMappingbuild() + +
+           
+ Mapping.LineMappingbuildPartial() + +
+           
+ Mapping.LineMapping.Builderclear() + +
+           
+ Mapping.LineMapping.BuilderclearColumnPosition() + +
+           
+ Mapping.LineMapping.BuilderclearLineNumber() + +
+           
+ Mapping.LineMapping.BuilderclearOriginalMapping() + +
+           
+ Mapping.LineMapping.Builderclone() + +
+           
+ intgetColumnPosition() + +
+           
+ Mapping.LineMappinggetDefaultInstanceForType() + +
+           
+static com.google.protobuf.Descriptors.DescriptorgetDescriptor() + +
+           
+ com.google.protobuf.Descriptors.DescriptorgetDescriptorForType() + +
+           
+ intgetLineNumber() + +
+           
+ Mapping.OriginalMappinggetOriginalMapping() + +
+           
+ Mapping.OriginalMapping.BuildergetOriginalMappingBuilder() + +
+           
+ Mapping.OriginalMappingOrBuildergetOriginalMappingOrBuilder() + +
+           
+ booleanhasColumnPosition() + +
+           
+ booleanhasLineNumber() + +
+           
+ booleanhasOriginalMapping() + +
+           
+protected  com.google.protobuf.GeneratedMessage.FieldAccessorTableinternalGetFieldAccessorTable() + +
+           
+ booleanisInitialized() + +
+           
+ Mapping.LineMapping.BuildermergeFrom(com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+ Mapping.LineMapping.BuildermergeFrom(Mapping.LineMapping other) + +
+           
+ Mapping.LineMapping.BuildermergeFrom(com.google.protobuf.Message other) + +
+           
+ Mapping.LineMapping.BuildermergeOriginalMapping(Mapping.OriginalMapping value) + +
+           
+ Mapping.LineMapping.BuildersetColumnPosition(int value) + +
+           
+ Mapping.LineMapping.BuildersetLineNumber(int value) + +
+           
+ Mapping.LineMapping.BuildersetOriginalMapping(Mapping.OriginalMapping.Builder builderForValue) + +
+           
+ Mapping.LineMapping.BuildersetOriginalMapping(Mapping.OriginalMapping value) + +
+           
+ + + + + + + +
Methods inherited from class com.google.protobuf.GeneratedMessage.Builder
addRepeatedField, clearField, getAllFields, getField, getParentForChildren, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField, isClean, markClean, mergeUnknownFields, newBuilderForField, onBuilt, onChanged, parseUnknownField, setField, setRepeatedField, setUnknownFields
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessage.Builder
mergeDelimitedFrom, mergeDelimitedFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, newUninitializedMessageException
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessageLite.Builder
addAll, newUninitializedMessageException
+ + + + + + + +
Methods inherited from class java.lang.Object
equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+  +

+ + + + + + + + +
+Method Detail
+ +

+getDescriptor

+
+public static final com.google.protobuf.Descriptors.Descriptor getDescriptor()
+
+
+
+
+
+
+
+
+
+ +

+internalGetFieldAccessorTable

+
+protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable()
+
+
+
Specified by:
internalGetFieldAccessorTable in class com.google.protobuf.GeneratedMessage.Builder<Mapping.LineMapping.Builder>
+
+
+
+
+
+
+ +

+clear

+
+public Mapping.LineMapping.Builder clear()
+
+
+
Specified by:
clear in interface com.google.protobuf.Message.Builder
Specified by:
clear in interface com.google.protobuf.MessageLite.Builder
Overrides:
clear in class com.google.protobuf.GeneratedMessage.Builder<Mapping.LineMapping.Builder>
+
+
+
+
+
+
+ +

+clone

+
+public Mapping.LineMapping.Builder clone()
+
+
+
Specified by:
clone in interface com.google.protobuf.Message.Builder
Specified by:
clone in interface com.google.protobuf.MessageLite.Builder
Overrides:
clone in class com.google.protobuf.GeneratedMessage.Builder<Mapping.LineMapping.Builder>
+
+
+
+
+
+
+ +

+getDescriptorForType

+
+public com.google.protobuf.Descriptors.Descriptor getDescriptorForType()
+
+
+
Specified by:
getDescriptorForType in interface com.google.protobuf.Message.Builder
Specified by:
getDescriptorForType in interface com.google.protobuf.MessageOrBuilder
Overrides:
getDescriptorForType in class com.google.protobuf.GeneratedMessage.Builder<Mapping.LineMapping.Builder>
+
+
+
+
+
+
+ +

+getDefaultInstanceForType

+
+public Mapping.LineMapping getDefaultInstanceForType()
+
+
+
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageLiteOrBuilder
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageOrBuilder
+
+
+
+
+
+
+ +

+build

+
+public Mapping.LineMapping build()
+
+
+
Specified by:
build in interface com.google.protobuf.Message.Builder
Specified by:
build in interface com.google.protobuf.MessageLite.Builder
+
+
+
+
+
+
+ +

+buildPartial

+
+public Mapping.LineMapping buildPartial()
+
+
+
Specified by:
buildPartial in interface com.google.protobuf.Message.Builder
Specified by:
buildPartial in interface com.google.protobuf.MessageLite.Builder
+
+
+
+
+
+
+ +

+mergeFrom

+
+public Mapping.LineMapping.Builder mergeFrom(com.google.protobuf.Message other)
+
+
+
Specified by:
mergeFrom in interface com.google.protobuf.Message.Builder
Overrides:
mergeFrom in class com.google.protobuf.AbstractMessage.Builder<Mapping.LineMapping.Builder>
+
+
+
+
+
+
+ +

+mergeFrom

+
+public Mapping.LineMapping.Builder mergeFrom(Mapping.LineMapping other)
+
+
+
+
+
+
+
+
+
+ +

+isInitialized

+
+public final boolean isInitialized()
+
+
+
Specified by:
isInitialized in interface com.google.protobuf.MessageLiteOrBuilder
Overrides:
isInitialized in class com.google.protobuf.GeneratedMessage.Builder<Mapping.LineMapping.Builder>
+
+
+
+
+
+
+ +

+mergeFrom

+
+public Mapping.LineMapping.Builder mergeFrom(com.google.protobuf.CodedInputStream input,
+                                             com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                      throws IOException
+
+
+
Specified by:
mergeFrom in interface com.google.protobuf.Message.Builder
Specified by:
mergeFrom in interface com.google.protobuf.MessageLite.Builder
Overrides:
mergeFrom in class com.google.protobuf.AbstractMessage.Builder<Mapping.LineMapping.Builder>
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+hasLineNumber

+
+public boolean hasLineNumber()
+
+
+
Specified by:
hasLineNumber in interface Mapping.LineMappingOrBuilder
+
+
+
+
+
+
+ +

+getLineNumber

+
+public int getLineNumber()
+
+
+
Specified by:
getLineNumber in interface Mapping.LineMappingOrBuilder
+
+
+
+
+
+
+ +

+setLineNumber

+
+public Mapping.LineMapping.Builder setLineNumber(int value)
+
+
+
+
+
+
+
+
+
+ +

+clearLineNumber

+
+public Mapping.LineMapping.Builder clearLineNumber()
+
+
+
+
+
+
+
+
+
+ +

+hasColumnPosition

+
+public boolean hasColumnPosition()
+
+
+
Specified by:
hasColumnPosition in interface Mapping.LineMappingOrBuilder
+
+
+
+
+
+
+ +

+getColumnPosition

+
+public int getColumnPosition()
+
+
+
Specified by:
getColumnPosition in interface Mapping.LineMappingOrBuilder
+
+
+
+
+
+
+ +

+setColumnPosition

+
+public Mapping.LineMapping.Builder setColumnPosition(int value)
+
+
+
+
+
+
+
+
+
+ +

+clearColumnPosition

+
+public Mapping.LineMapping.Builder clearColumnPosition()
+
+
+
+
+
+
+
+
+
+ +

+hasOriginalMapping

+
+public boolean hasOriginalMapping()
+
+
+
Specified by:
hasOriginalMapping in interface Mapping.LineMappingOrBuilder
+
+
+
+
+
+
+ +

+getOriginalMapping

+
+public Mapping.OriginalMapping getOriginalMapping()
+
+
+
Specified by:
getOriginalMapping in interface Mapping.LineMappingOrBuilder
+
+
+
+
+
+
+ +

+setOriginalMapping

+
+public Mapping.LineMapping.Builder setOriginalMapping(Mapping.OriginalMapping value)
+
+
+
+
+
+
+
+
+
+ +

+setOriginalMapping

+
+public Mapping.LineMapping.Builder setOriginalMapping(Mapping.OriginalMapping.Builder builderForValue)
+
+
+
+
+
+
+
+
+
+ +

+mergeOriginalMapping

+
+public Mapping.LineMapping.Builder mergeOriginalMapping(Mapping.OriginalMapping value)
+
+
+
+
+
+
+
+
+
+ +

+clearOriginalMapping

+
+public Mapping.LineMapping.Builder clearOriginalMapping()
+
+
+
+
+
+
+
+
+
+ +

+getOriginalMappingBuilder

+
+public Mapping.OriginalMapping.Builder getOriginalMappingBuilder()
+
+
+
+
+
+
+
+
+
+ +

+getOriginalMappingOrBuilder

+
+public Mapping.OriginalMappingOrBuilder getOriginalMappingOrBuilder()
+
+
+
Specified by:
getOriginalMappingOrBuilder in interface Mapping.LineMappingOrBuilder
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.LineMapping.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.LineMapping.html new file mode 100644 index 0000000..b6fdfce --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.LineMapping.html @@ -0,0 +1,1082 @@ + + + + + +Mapping.LineMapping (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap.proto +
+Class Mapping.LineMapping

+
+java.lang.Object
+  extended by com.google.protobuf.AbstractMessageLite
+      extended by com.google.protobuf.AbstractMessage
+          extended by com.google.protobuf.GeneratedMessage
+              extended by com.google.debugging.sourcemap.proto.Mapping.LineMapping
+
+
+
All Implemented Interfaces:
Mapping.LineMappingOrBuilder, com.google.protobuf.Message, com.google.protobuf.MessageLite, com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder, Serializable
+
+
+
Enclosing class:
Mapping
+
+
+
+
public static final class Mapping.LineMapping
extends com.google.protobuf.GeneratedMessage
implements Mapping.LineMappingOrBuilder
+ + +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classMapping.LineMapping.Builder + +
+           
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.protobuf.GeneratedMessage
com.google.protobuf.GeneratedMessage.BuilderParent, com.google.protobuf.GeneratedMessage.ExtendableBuilder<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage,BuilderType extends com.google.protobuf.GeneratedMessage.ExtendableBuilder>, com.google.protobuf.GeneratedMessage.ExtendableMessage<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage>, com.google.protobuf.GeneratedMessage.ExtendableMessageOrBuilder<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage>, com.google.protobuf.GeneratedMessage.FieldAccessorTable, com.google.protobuf.GeneratedMessage.GeneratedExtension<ContainingType extends com.google.protobuf.Message,Type>
+  + + + + + + + + + + + + + + + + + + + +
+Field Summary
+static intCOLUMN_POSITION_FIELD_NUMBER + +
+           
+static intLINE_NUMBER_FIELD_NUMBER + +
+           
+static intORIGINAL_MAPPING_FIELD_NUMBER + +
+           
+ + + + + + + +
Fields inherited from class com.google.protobuf.GeneratedMessage
alwaysUseFieldBuilders
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ intgetColumnPosition() + +
+           
+static Mapping.LineMappinggetDefaultInstance() + +
+           
+ Mapping.LineMappinggetDefaultInstanceForType() + +
+           
+static com.google.protobuf.Descriptors.DescriptorgetDescriptor() + +
+           
+ intgetLineNumber() + +
+           
+ Mapping.OriginalMappinggetOriginalMapping() + +
+           
+ Mapping.OriginalMappingOrBuildergetOriginalMappingOrBuilder() + +
+           
+ intgetSerializedSize() + +
+           
+ booleanhasColumnPosition() + +
+           
+ booleanhasLineNumber() + +
+           
+ booleanhasOriginalMapping() + +
+           
+protected  com.google.protobuf.GeneratedMessage.FieldAccessorTableinternalGetFieldAccessorTable() + +
+           
+ booleanisInitialized() + +
+           
+static Mapping.LineMapping.BuildernewBuilder() + +
+           
+static Mapping.LineMapping.BuildernewBuilder(Mapping.LineMapping prototype) + +
+           
+ Mapping.LineMapping.BuildernewBuilderForType() + +
+           
+protected  Mapping.LineMapping.BuildernewBuilderForType(com.google.protobuf.GeneratedMessage.BuilderParent parent) + +
+           
+static Mapping.LineMappingparseDelimitedFrom(InputStream input) + +
+           
+static Mapping.LineMappingparseDelimitedFrom(InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static Mapping.LineMappingparseFrom(byte[] data) + +
+           
+static Mapping.LineMappingparseFrom(byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static Mapping.LineMappingparseFrom(com.google.protobuf.ByteString data) + +
+           
+static Mapping.LineMappingparseFrom(com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static Mapping.LineMappingparseFrom(com.google.protobuf.CodedInputStream input) + +
+           
+static Mapping.LineMappingparseFrom(com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static Mapping.LineMappingparseFrom(InputStream input) + +
+           
+static Mapping.LineMappingparseFrom(InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+ Mapping.LineMapping.BuildertoBuilder() + +
+           
+protected  ObjectwriteReplace() + +
+           
+ voidwriteTo(com.google.protobuf.CodedOutputStream output) + +
+           
+ + + + + + + +
Methods inherited from class com.google.protobuf.GeneratedMessage
getAllFields, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField, newFileScopedGeneratedExtension, newMessageScopedGeneratedExtension
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessage
equals, hashBoolean, hashCode, hashEnum, hashEnumList, hashFields, hashLong, toString
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessageLite
toByteArray, toByteString, writeDelimitedTo, writeTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageLite
toByteArray, toByteString, writeDelimitedTo, writeTo
+  +

+ + + + + + + + +
+Field Detail
+ +

+LINE_NUMBER_FIELD_NUMBER

+
+public static final int LINE_NUMBER_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+COLUMN_POSITION_FIELD_NUMBER

+
+public static final int COLUMN_POSITION_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+ORIGINAL_MAPPING_FIELD_NUMBER

+
+public static final int ORIGINAL_MAPPING_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+ + + + + + + + +
+Method Detail
+ +

+getDefaultInstance

+
+public static Mapping.LineMapping getDefaultInstance()
+
+
+
+
+
+
+
+
+
+ +

+getDefaultInstanceForType

+
+public Mapping.LineMapping getDefaultInstanceForType()
+
+
+
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageLiteOrBuilder
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageOrBuilder
+
+
+
+
+
+
+ +

+getDescriptor

+
+public static final com.google.protobuf.Descriptors.Descriptor getDescriptor()
+
+
+
+
+
+
+
+
+
+ +

+internalGetFieldAccessorTable

+
+protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable()
+
+
+
Specified by:
internalGetFieldAccessorTable in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+
+ +

+hasLineNumber

+
+public boolean hasLineNumber()
+
+
+
Specified by:
hasLineNumber in interface Mapping.LineMappingOrBuilder
+
+
+
+
+
+
+ +

+getLineNumber

+
+public int getLineNumber()
+
+
+
Specified by:
getLineNumber in interface Mapping.LineMappingOrBuilder
+
+
+
+
+
+
+ +

+hasColumnPosition

+
+public boolean hasColumnPosition()
+
+
+
Specified by:
hasColumnPosition in interface Mapping.LineMappingOrBuilder
+
+
+
+
+
+
+ +

+getColumnPosition

+
+public int getColumnPosition()
+
+
+
Specified by:
getColumnPosition in interface Mapping.LineMappingOrBuilder
+
+
+
+
+
+
+ +

+hasOriginalMapping

+
+public boolean hasOriginalMapping()
+
+
+
Specified by:
hasOriginalMapping in interface Mapping.LineMappingOrBuilder
+
+
+
+
+
+
+ +

+getOriginalMapping

+
+public Mapping.OriginalMapping getOriginalMapping()
+
+
+
Specified by:
getOriginalMapping in interface Mapping.LineMappingOrBuilder
+
+
+
+
+
+
+ +

+getOriginalMappingOrBuilder

+
+public Mapping.OriginalMappingOrBuilder getOriginalMappingOrBuilder()
+
+
+
Specified by:
getOriginalMappingOrBuilder in interface Mapping.LineMappingOrBuilder
+
+
+
+
+
+
+ +

+isInitialized

+
+public final boolean isInitialized()
+
+
+
Specified by:
isInitialized in interface com.google.protobuf.MessageLiteOrBuilder
Overrides:
isInitialized in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+
+ +

+writeTo

+
+public void writeTo(com.google.protobuf.CodedOutputStream output)
+             throws IOException
+
+
+
Specified by:
writeTo in interface com.google.protobuf.MessageLite
Overrides:
writeTo in class com.google.protobuf.AbstractMessage
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+getSerializedSize

+
+public int getSerializedSize()
+
+
+
Specified by:
getSerializedSize in interface com.google.protobuf.MessageLite
Overrides:
getSerializedSize in class com.google.protobuf.AbstractMessage
+
+
+
+
+
+
+ +

+writeReplace

+
+protected Object writeReplace()
+                       throws ObjectStreamException
+
+
+
Overrides:
writeReplace in class com.google.protobuf.GeneratedMessage
+
+
+ +
Throws: +
ObjectStreamException
+
+
+
+ +

+parseFrom

+
+public static Mapping.LineMapping parseFrom(com.google.protobuf.ByteString data)
+                                     throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static Mapping.LineMapping parseFrom(com.google.protobuf.ByteString data,
+                                            com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                     throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static Mapping.LineMapping parseFrom(byte[] data)
+                                     throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static Mapping.LineMapping parseFrom(byte[] data,
+                                            com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                     throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static Mapping.LineMapping parseFrom(InputStream input)
+                                     throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static Mapping.LineMapping parseFrom(InputStream input,
+                                            com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                     throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseDelimitedFrom

+
+public static Mapping.LineMapping parseDelimitedFrom(InputStream input)
+                                              throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseDelimitedFrom

+
+public static Mapping.LineMapping parseDelimitedFrom(InputStream input,
+                                                     com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                              throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static Mapping.LineMapping parseFrom(com.google.protobuf.CodedInputStream input)
+                                     throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static Mapping.LineMapping parseFrom(com.google.protobuf.CodedInputStream input,
+                                            com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                     throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+newBuilder

+
+public static Mapping.LineMapping.Builder newBuilder()
+
+
+
+
+
+
+
+
+
+ +

+newBuilderForType

+
+public Mapping.LineMapping.Builder newBuilderForType()
+
+
+
Specified by:
newBuilderForType in interface com.google.protobuf.Message
Specified by:
newBuilderForType in interface com.google.protobuf.MessageLite
+
+
+
+
+
+
+ +

+newBuilder

+
+public static Mapping.LineMapping.Builder newBuilder(Mapping.LineMapping prototype)
+
+
+
+
+
+
+
+
+
+ +

+toBuilder

+
+public Mapping.LineMapping.Builder toBuilder()
+
+
+
Specified by:
toBuilder in interface com.google.protobuf.Message
Specified by:
toBuilder in interface com.google.protobuf.MessageLite
+
+
+
+
+
+
+ +

+newBuilderForType

+
+protected Mapping.LineMapping.Builder newBuilderForType(com.google.protobuf.GeneratedMessage.BuilderParent parent)
+
+
+
Specified by:
newBuilderForType in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.LineMappingOrBuilder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.LineMappingOrBuilder.html new file mode 100644 index 0000000..7ecd0bd --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.LineMappingOrBuilder.html @@ -0,0 +1,380 @@ + + + + + +Mapping.LineMappingOrBuilder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap.proto +
+Interface Mapping.LineMappingOrBuilder

+
+
All Superinterfaces:
com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder
+
+
+
All Known Implementing Classes:
Mapping.LineMapping, Mapping.LineMapping.Builder
+
+
+
Enclosing class:
Mapping
+
+
+
+
public static interface Mapping.LineMappingOrBuilder
extends com.google.protobuf.MessageOrBuilder
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ intgetColumnPosition() + +
+           
+ intgetLineNumber() + +
+           
+ Mapping.OriginalMappinggetOriginalMapping() + +
+           
+ Mapping.OriginalMappingOrBuildergetOriginalMappingOrBuilder() + +
+           
+ booleanhasColumnPosition() + +
+           
+ booleanhasLineNumber() + +
+           
+ booleanhasOriginalMapping() + +
+           
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getDefaultInstanceForType, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageLiteOrBuilder
isInitialized
+  +

+ + + + + + + + +
+Method Detail
+ +

+hasLineNumber

+
+boolean hasLineNumber()
+
+
+
+
+
+
+
+
+
+ +

+getLineNumber

+
+int getLineNumber()
+
+
+
+
+
+
+
+
+
+ +

+hasColumnPosition

+
+boolean hasColumnPosition()
+
+
+
+
+
+
+
+
+
+ +

+getColumnPosition

+
+int getColumnPosition()
+
+
+
+
+
+
+
+
+
+ +

+hasOriginalMapping

+
+boolean hasOriginalMapping()
+
+
+
+
+
+
+
+
+
+ +

+getOriginalMapping

+
+Mapping.OriginalMapping getOriginalMapping()
+
+
+
+
+
+
+
+
+
+ +

+getOriginalMappingOrBuilder

+
+Mapping.OriginalMappingOrBuilder getOriginalMappingOrBuilder()
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.OriginalMapping.Builder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.OriginalMapping.Builder.html new file mode 100644 index 0000000..102353a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.OriginalMapping.Builder.html @@ -0,0 +1,878 @@ + + + + + +Mapping.OriginalMapping.Builder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap.proto +
+Class Mapping.OriginalMapping.Builder

+
+java.lang.Object
+  extended by com.google.protobuf.AbstractMessageLite.Builder<BuilderType>
+      extended by com.google.protobuf.AbstractMessage.Builder<BuilderType>
+          extended by com.google.protobuf.GeneratedMessage.Builder<Mapping.OriginalMapping.Builder>
+              extended by com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder
+
+
+
All Implemented Interfaces:
Mapping.OriginalMappingOrBuilder, com.google.protobuf.Message.Builder, com.google.protobuf.MessageLite.Builder, com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder, Cloneable
+
+
+
Enclosing class:
Mapping.OriginalMapping
+
+
+
+
public static final class Mapping.OriginalMapping.Builder
extends com.google.protobuf.GeneratedMessage.Builder<Mapping.OriginalMapping.Builder>
implements Mapping.OriginalMappingOrBuilder
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ Mapping.OriginalMappingbuild() + +
+           
+ Mapping.OriginalMappingbuildPartial() + +
+           
+ Mapping.OriginalMapping.Builderclear() + +
+           
+ Mapping.OriginalMapping.BuilderclearColumnPosition() + +
+           
+ Mapping.OriginalMapping.BuilderclearIdentifier() + +
+           
+ Mapping.OriginalMapping.BuilderclearLineNumber() + +
+           
+ Mapping.OriginalMapping.BuilderclearOriginalFile() + +
+           
+ Mapping.OriginalMapping.Builderclone() + +
+           
+ intgetColumnPosition() + +
+           
+ Mapping.OriginalMappinggetDefaultInstanceForType() + +
+           
+static com.google.protobuf.Descriptors.DescriptorgetDescriptor() + +
+           
+ com.google.protobuf.Descriptors.DescriptorgetDescriptorForType() + +
+           
+ StringgetIdentifier() + +
+           
+ intgetLineNumber() + +
+           
+ StringgetOriginalFile() + +
+           
+ booleanhasColumnPosition() + +
+           
+ booleanhasIdentifier() + +
+           
+ booleanhasLineNumber() + +
+           
+ booleanhasOriginalFile() + +
+           
+protected  com.google.protobuf.GeneratedMessage.FieldAccessorTableinternalGetFieldAccessorTable() + +
+           
+ booleanisInitialized() + +
+           
+ Mapping.OriginalMapping.BuildermergeFrom(com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+ Mapping.OriginalMapping.BuildermergeFrom(Mapping.OriginalMapping other) + +
+           
+ Mapping.OriginalMapping.BuildermergeFrom(com.google.protobuf.Message other) + +
+           
+ Mapping.OriginalMapping.BuildersetColumnPosition(int value) + +
+           
+ Mapping.OriginalMapping.BuildersetIdentifier(String value) + +
+           
+ Mapping.OriginalMapping.BuildersetLineNumber(int value) + +
+           
+ Mapping.OriginalMapping.BuildersetOriginalFile(String value) + +
+           
+ + + + + + + +
Methods inherited from class com.google.protobuf.GeneratedMessage.Builder
addRepeatedField, clearField, getAllFields, getField, getParentForChildren, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField, isClean, markClean, mergeUnknownFields, newBuilderForField, onBuilt, onChanged, parseUnknownField, setField, setRepeatedField, setUnknownFields
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessage.Builder
mergeDelimitedFrom, mergeDelimitedFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, newUninitializedMessageException
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessageLite.Builder
addAll, newUninitializedMessageException
+ + + + + + + +
Methods inherited from class java.lang.Object
equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+  +

+ + + + + + + + +
+Method Detail
+ +

+getDescriptor

+
+public static final com.google.protobuf.Descriptors.Descriptor getDescriptor()
+
+
+
+
+
+
+
+
+
+ +

+internalGetFieldAccessorTable

+
+protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable()
+
+
+
Specified by:
internalGetFieldAccessorTable in class com.google.protobuf.GeneratedMessage.Builder<Mapping.OriginalMapping.Builder>
+
+
+
+
+
+
+ +

+clear

+
+public Mapping.OriginalMapping.Builder clear()
+
+
+
Specified by:
clear in interface com.google.protobuf.Message.Builder
Specified by:
clear in interface com.google.protobuf.MessageLite.Builder
Overrides:
clear in class com.google.protobuf.GeneratedMessage.Builder<Mapping.OriginalMapping.Builder>
+
+
+
+
+
+
+ +

+clone

+
+public Mapping.OriginalMapping.Builder clone()
+
+
+
Specified by:
clone in interface com.google.protobuf.Message.Builder
Specified by:
clone in interface com.google.protobuf.MessageLite.Builder
Overrides:
clone in class com.google.protobuf.GeneratedMessage.Builder<Mapping.OriginalMapping.Builder>
+
+
+
+
+
+
+ +

+getDescriptorForType

+
+public com.google.protobuf.Descriptors.Descriptor getDescriptorForType()
+
+
+
Specified by:
getDescriptorForType in interface com.google.protobuf.Message.Builder
Specified by:
getDescriptorForType in interface com.google.protobuf.MessageOrBuilder
Overrides:
getDescriptorForType in class com.google.protobuf.GeneratedMessage.Builder<Mapping.OriginalMapping.Builder>
+
+
+
+
+
+
+ +

+getDefaultInstanceForType

+
+public Mapping.OriginalMapping getDefaultInstanceForType()
+
+
+
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageLiteOrBuilder
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageOrBuilder
+
+
+
+
+
+
+ +

+build

+
+public Mapping.OriginalMapping build()
+
+
+
Specified by:
build in interface com.google.protobuf.Message.Builder
Specified by:
build in interface com.google.protobuf.MessageLite.Builder
+
+
+
+
+
+
+ +

+buildPartial

+
+public Mapping.OriginalMapping buildPartial()
+
+
+
Specified by:
buildPartial in interface com.google.protobuf.Message.Builder
Specified by:
buildPartial in interface com.google.protobuf.MessageLite.Builder
+
+
+
+
+
+
+ +

+mergeFrom

+
+public Mapping.OriginalMapping.Builder mergeFrom(com.google.protobuf.Message other)
+
+
+
Specified by:
mergeFrom in interface com.google.protobuf.Message.Builder
Overrides:
mergeFrom in class com.google.protobuf.AbstractMessage.Builder<Mapping.OriginalMapping.Builder>
+
+
+
+
+
+
+ +

+mergeFrom

+
+public Mapping.OriginalMapping.Builder mergeFrom(Mapping.OriginalMapping other)
+
+
+
+
+
+
+
+
+
+ +

+isInitialized

+
+public final boolean isInitialized()
+
+
+
Specified by:
isInitialized in interface com.google.protobuf.MessageLiteOrBuilder
Overrides:
isInitialized in class com.google.protobuf.GeneratedMessage.Builder<Mapping.OriginalMapping.Builder>
+
+
+
+
+
+
+ +

+mergeFrom

+
+public Mapping.OriginalMapping.Builder mergeFrom(com.google.protobuf.CodedInputStream input,
+                                                 com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                          throws IOException
+
+
+
Specified by:
mergeFrom in interface com.google.protobuf.Message.Builder
Specified by:
mergeFrom in interface com.google.protobuf.MessageLite.Builder
Overrides:
mergeFrom in class com.google.protobuf.AbstractMessage.Builder<Mapping.OriginalMapping.Builder>
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+hasOriginalFile

+
+public boolean hasOriginalFile()
+
+
+
Specified by:
hasOriginalFile in interface Mapping.OriginalMappingOrBuilder
+
+
+
+
+
+
+ +

+getOriginalFile

+
+public String getOriginalFile()
+
+
+
Specified by:
getOriginalFile in interface Mapping.OriginalMappingOrBuilder
+
+
+
+
+
+
+ +

+setOriginalFile

+
+public Mapping.OriginalMapping.Builder setOriginalFile(String value)
+
+
+
+
+
+
+
+
+
+ +

+clearOriginalFile

+
+public Mapping.OriginalMapping.Builder clearOriginalFile()
+
+
+
+
+
+
+
+
+
+ +

+hasLineNumber

+
+public boolean hasLineNumber()
+
+
+
Specified by:
hasLineNumber in interface Mapping.OriginalMappingOrBuilder
+
+
+
+
+
+
+ +

+getLineNumber

+
+public int getLineNumber()
+
+
+
Specified by:
getLineNumber in interface Mapping.OriginalMappingOrBuilder
+
+
+
+
+
+
+ +

+setLineNumber

+
+public Mapping.OriginalMapping.Builder setLineNumber(int value)
+
+
+
+
+
+
+
+
+
+ +

+clearLineNumber

+
+public Mapping.OriginalMapping.Builder clearLineNumber()
+
+
+
+
+
+
+
+
+
+ +

+hasColumnPosition

+
+public boolean hasColumnPosition()
+
+
+
Specified by:
hasColumnPosition in interface Mapping.OriginalMappingOrBuilder
+
+
+
+
+
+
+ +

+getColumnPosition

+
+public int getColumnPosition()
+
+
+
Specified by:
getColumnPosition in interface Mapping.OriginalMappingOrBuilder
+
+
+
+
+
+
+ +

+setColumnPosition

+
+public Mapping.OriginalMapping.Builder setColumnPosition(int value)
+
+
+
+
+
+
+
+
+
+ +

+clearColumnPosition

+
+public Mapping.OriginalMapping.Builder clearColumnPosition()
+
+
+
+
+
+
+
+
+
+ +

+hasIdentifier

+
+public boolean hasIdentifier()
+
+
+
Specified by:
hasIdentifier in interface Mapping.OriginalMappingOrBuilder
+
+
+
+
+
+
+ +

+getIdentifier

+
+public String getIdentifier()
+
+
+
Specified by:
getIdentifier in interface Mapping.OriginalMappingOrBuilder
+
+
+
+
+
+
+ +

+setIdentifier

+
+public Mapping.OriginalMapping.Builder setIdentifier(String value)
+
+
+
+
+
+
+
+
+
+ +

+clearIdentifier

+
+public Mapping.OriginalMapping.Builder clearIdentifier()
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.OriginalMapping.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.OriginalMapping.html new file mode 100644 index 0000000..6d9bfc1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.OriginalMapping.html @@ -0,0 +1,1122 @@ + + + + + +Mapping.OriginalMapping (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap.proto +
+Class Mapping.OriginalMapping

+
+java.lang.Object
+  extended by com.google.protobuf.AbstractMessageLite
+      extended by com.google.protobuf.AbstractMessage
+          extended by com.google.protobuf.GeneratedMessage
+              extended by com.google.debugging.sourcemap.proto.Mapping.OriginalMapping
+
+
+
All Implemented Interfaces:
Mapping.OriginalMappingOrBuilder, com.google.protobuf.Message, com.google.protobuf.MessageLite, com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder, Serializable
+
+
+
Enclosing class:
Mapping
+
+
+
+
public static final class Mapping.OriginalMapping
extends com.google.protobuf.GeneratedMessage
implements Mapping.OriginalMappingOrBuilder
+ + +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classMapping.OriginalMapping.Builder + +
+           
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.protobuf.GeneratedMessage
com.google.protobuf.GeneratedMessage.BuilderParent, com.google.protobuf.GeneratedMessage.ExtendableBuilder<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage,BuilderType extends com.google.protobuf.GeneratedMessage.ExtendableBuilder>, com.google.protobuf.GeneratedMessage.ExtendableMessage<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage>, com.google.protobuf.GeneratedMessage.ExtendableMessageOrBuilder<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage>, com.google.protobuf.GeneratedMessage.FieldAccessorTable, com.google.protobuf.GeneratedMessage.GeneratedExtension<ContainingType extends com.google.protobuf.Message,Type>
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Field Summary
+static intCOLUMN_POSITION_FIELD_NUMBER + +
+           
+static intIDENTIFIER_FIELD_NUMBER + +
+           
+static intLINE_NUMBER_FIELD_NUMBER + +
+           
+static intORIGINAL_FILE_FIELD_NUMBER + +
+           
+ + + + + + + +
Fields inherited from class com.google.protobuf.GeneratedMessage
alwaysUseFieldBuilders
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ intgetColumnPosition() + +
+           
+static Mapping.OriginalMappinggetDefaultInstance() + +
+           
+ Mapping.OriginalMappinggetDefaultInstanceForType() + +
+           
+static com.google.protobuf.Descriptors.DescriptorgetDescriptor() + +
+           
+ StringgetIdentifier() + +
+           
+ intgetLineNumber() + +
+           
+ StringgetOriginalFile() + +
+           
+ intgetSerializedSize() + +
+           
+ booleanhasColumnPosition() + +
+           
+ booleanhasIdentifier() + +
+           
+ booleanhasLineNumber() + +
+           
+ booleanhasOriginalFile() + +
+           
+protected  com.google.protobuf.GeneratedMessage.FieldAccessorTableinternalGetFieldAccessorTable() + +
+           
+ booleanisInitialized() + +
+           
+static Mapping.OriginalMapping.BuildernewBuilder() + +
+           
+static Mapping.OriginalMapping.BuildernewBuilder(Mapping.OriginalMapping prototype) + +
+           
+ Mapping.OriginalMapping.BuildernewBuilderForType() + +
+           
+protected  Mapping.OriginalMapping.BuildernewBuilderForType(com.google.protobuf.GeneratedMessage.BuilderParent parent) + +
+           
+static Mapping.OriginalMappingparseDelimitedFrom(InputStream input) + +
+           
+static Mapping.OriginalMappingparseDelimitedFrom(InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static Mapping.OriginalMappingparseFrom(byte[] data) + +
+           
+static Mapping.OriginalMappingparseFrom(byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static Mapping.OriginalMappingparseFrom(com.google.protobuf.ByteString data) + +
+           
+static Mapping.OriginalMappingparseFrom(com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static Mapping.OriginalMappingparseFrom(com.google.protobuf.CodedInputStream input) + +
+           
+static Mapping.OriginalMappingparseFrom(com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static Mapping.OriginalMappingparseFrom(InputStream input) + +
+           
+static Mapping.OriginalMappingparseFrom(InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+ Mapping.OriginalMapping.BuildertoBuilder() + +
+           
+protected  ObjectwriteReplace() + +
+           
+ voidwriteTo(com.google.protobuf.CodedOutputStream output) + +
+           
+ + + + + + + +
Methods inherited from class com.google.protobuf.GeneratedMessage
getAllFields, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField, newFileScopedGeneratedExtension, newMessageScopedGeneratedExtension
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessage
equals, hashBoolean, hashCode, hashEnum, hashEnumList, hashFields, hashLong, toString
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessageLite
toByteArray, toByteString, writeDelimitedTo, writeTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageLite
toByteArray, toByteString, writeDelimitedTo, writeTo
+  +

+ + + + + + + + +
+Field Detail
+ +

+ORIGINAL_FILE_FIELD_NUMBER

+
+public static final int ORIGINAL_FILE_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+LINE_NUMBER_FIELD_NUMBER

+
+public static final int LINE_NUMBER_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+COLUMN_POSITION_FIELD_NUMBER

+
+public static final int COLUMN_POSITION_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+IDENTIFIER_FIELD_NUMBER

+
+public static final int IDENTIFIER_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+ + + + + + + + +
+Method Detail
+ +

+getDefaultInstance

+
+public static Mapping.OriginalMapping getDefaultInstance()
+
+
+
+
+
+
+
+
+
+ +

+getDefaultInstanceForType

+
+public Mapping.OriginalMapping getDefaultInstanceForType()
+
+
+
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageLiteOrBuilder
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageOrBuilder
+
+
+
+
+
+
+ +

+getDescriptor

+
+public static final com.google.protobuf.Descriptors.Descriptor getDescriptor()
+
+
+
+
+
+
+
+
+
+ +

+internalGetFieldAccessorTable

+
+protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable()
+
+
+
Specified by:
internalGetFieldAccessorTable in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+
+ +

+hasOriginalFile

+
+public boolean hasOriginalFile()
+
+
+
Specified by:
hasOriginalFile in interface Mapping.OriginalMappingOrBuilder
+
+
+
+
+
+
+ +

+getOriginalFile

+
+public String getOriginalFile()
+
+
+
Specified by:
getOriginalFile in interface Mapping.OriginalMappingOrBuilder
+
+
+
+
+
+
+ +

+hasLineNumber

+
+public boolean hasLineNumber()
+
+
+
Specified by:
hasLineNumber in interface Mapping.OriginalMappingOrBuilder
+
+
+
+
+
+
+ +

+getLineNumber

+
+public int getLineNumber()
+
+
+
Specified by:
getLineNumber in interface Mapping.OriginalMappingOrBuilder
+
+
+
+
+
+
+ +

+hasColumnPosition

+
+public boolean hasColumnPosition()
+
+
+
Specified by:
hasColumnPosition in interface Mapping.OriginalMappingOrBuilder
+
+
+
+
+
+
+ +

+getColumnPosition

+
+public int getColumnPosition()
+
+
+
Specified by:
getColumnPosition in interface Mapping.OriginalMappingOrBuilder
+
+
+
+
+
+
+ +

+hasIdentifier

+
+public boolean hasIdentifier()
+
+
+
Specified by:
hasIdentifier in interface Mapping.OriginalMappingOrBuilder
+
+
+
+
+
+
+ +

+getIdentifier

+
+public String getIdentifier()
+
+
+
Specified by:
getIdentifier in interface Mapping.OriginalMappingOrBuilder
+
+
+
+
+
+
+ +

+isInitialized

+
+public final boolean isInitialized()
+
+
+
Specified by:
isInitialized in interface com.google.protobuf.MessageLiteOrBuilder
Overrides:
isInitialized in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+
+ +

+writeTo

+
+public void writeTo(com.google.protobuf.CodedOutputStream output)
+             throws IOException
+
+
+
Specified by:
writeTo in interface com.google.protobuf.MessageLite
Overrides:
writeTo in class com.google.protobuf.AbstractMessage
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+getSerializedSize

+
+public int getSerializedSize()
+
+
+
Specified by:
getSerializedSize in interface com.google.protobuf.MessageLite
Overrides:
getSerializedSize in class com.google.protobuf.AbstractMessage
+
+
+
+
+
+
+ +

+writeReplace

+
+protected Object writeReplace()
+                       throws ObjectStreamException
+
+
+
Overrides:
writeReplace in class com.google.protobuf.GeneratedMessage
+
+
+ +
Throws: +
ObjectStreamException
+
+
+
+ +

+parseFrom

+
+public static Mapping.OriginalMapping parseFrom(com.google.protobuf.ByteString data)
+                                         throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static Mapping.OriginalMapping parseFrom(com.google.protobuf.ByteString data,
+                                                com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                         throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static Mapping.OriginalMapping parseFrom(byte[] data)
+                                         throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static Mapping.OriginalMapping parseFrom(byte[] data,
+                                                com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                         throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static Mapping.OriginalMapping parseFrom(InputStream input)
+                                         throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static Mapping.OriginalMapping parseFrom(InputStream input,
+                                                com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                         throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseDelimitedFrom

+
+public static Mapping.OriginalMapping parseDelimitedFrom(InputStream input)
+                                                  throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseDelimitedFrom

+
+public static Mapping.OriginalMapping parseDelimitedFrom(InputStream input,
+                                                         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                                  throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static Mapping.OriginalMapping parseFrom(com.google.protobuf.CodedInputStream input)
+                                         throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static Mapping.OriginalMapping parseFrom(com.google.protobuf.CodedInputStream input,
+                                                com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                         throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+newBuilder

+
+public static Mapping.OriginalMapping.Builder newBuilder()
+
+
+
+
+
+
+
+
+
+ +

+newBuilderForType

+
+public Mapping.OriginalMapping.Builder newBuilderForType()
+
+
+
Specified by:
newBuilderForType in interface com.google.protobuf.Message
Specified by:
newBuilderForType in interface com.google.protobuf.MessageLite
+
+
+
+
+
+
+ +

+newBuilder

+
+public static Mapping.OriginalMapping.Builder newBuilder(Mapping.OriginalMapping prototype)
+
+
+
+
+
+
+
+
+
+ +

+toBuilder

+
+public Mapping.OriginalMapping.Builder toBuilder()
+
+
+
Specified by:
toBuilder in interface com.google.protobuf.Message
Specified by:
toBuilder in interface com.google.protobuf.MessageLite
+
+
+
+
+
+
+ +

+newBuilderForType

+
+protected Mapping.OriginalMapping.Builder newBuilderForType(com.google.protobuf.GeneratedMessage.BuilderParent parent)
+
+
+
Specified by:
newBuilderForType in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.OriginalMappingOrBuilder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.OriginalMappingOrBuilder.html new file mode 100644 index 0000000..aa05a92 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.OriginalMappingOrBuilder.html @@ -0,0 +1,402 @@ + + + + + +Mapping.OriginalMappingOrBuilder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap.proto +
+Interface Mapping.OriginalMappingOrBuilder

+
+
All Superinterfaces:
com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder
+
+
+
All Known Implementing Classes:
Mapping.OriginalMapping, Mapping.OriginalMapping.Builder
+
+
+
Enclosing class:
Mapping
+
+
+
+
public static interface Mapping.OriginalMappingOrBuilder
extends com.google.protobuf.MessageOrBuilder
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ intgetColumnPosition() + +
+           
+ StringgetIdentifier() + +
+           
+ intgetLineNumber() + +
+           
+ StringgetOriginalFile() + +
+           
+ booleanhasColumnPosition() + +
+           
+ booleanhasIdentifier() + +
+           
+ booleanhasLineNumber() + +
+           
+ booleanhasOriginalFile() + +
+           
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getDefaultInstanceForType, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageLiteOrBuilder
isInitialized
+  +

+ + + + + + + + +
+Method Detail
+ +

+hasOriginalFile

+
+boolean hasOriginalFile()
+
+
+
+
+
+
+
+
+
+ +

+getOriginalFile

+
+String getOriginalFile()
+
+
+
+
+
+
+
+
+
+ +

+hasLineNumber

+
+boolean hasLineNumber()
+
+
+
+
+
+
+
+
+
+ +

+getLineNumber

+
+int getLineNumber()
+
+
+
+
+
+
+
+
+
+ +

+hasColumnPosition

+
+boolean hasColumnPosition()
+
+
+
+
+
+
+
+
+
+ +

+getColumnPosition

+
+int getColumnPosition()
+
+
+
+
+
+
+
+
+
+ +

+hasIdentifier

+
+boolean hasIdentifier()
+
+
+
+
+
+
+
+
+
+ +

+getIdentifier

+
+String getIdentifier()
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.html new file mode 100644 index 0000000..3edf45a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/Mapping.html @@ -0,0 +1,291 @@ + + + + + +Mapping (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.debugging.sourcemap.proto +
+Class Mapping

+
+java.lang.Object
+  extended by com.google.debugging.sourcemap.proto.Mapping
+
+
+
+
public final class Mapping
extends Object
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + +
+Nested Class Summary
+static classMapping.LineMapping + +
+           
+static interfaceMapping.LineMappingOrBuilder + +
+           
+static classMapping.OriginalMapping + +
+           
+static interfaceMapping.OriginalMappingOrBuilder + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static com.google.protobuf.Descriptors.FileDescriptorgetDescriptor() + +
+           
+static voidregisterAllExtensions(com.google.protobuf.ExtensionRegistry registry) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+registerAllExtensions

+
+public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry)
+
+
+
+
+
+
+ +

+getDescriptor

+
+public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/package-frame.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/package-frame.html new file mode 100644 index 0000000..1a395ea --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/package-frame.html @@ -0,0 +1,51 @@ + + + + + +com.google.debugging.sourcemap.proto (Compiler) + + + + + + + + + + +com.google.debugging.sourcemap.proto + + + + +
+Interfaces  + +
+Mapping.LineMappingOrBuilder +
+Mapping.OriginalMappingOrBuilder
+ + + + + + +
+Classes  + +
+Mapping +
+Mapping.LineMapping +
+Mapping.LineMapping.Builder +
+Mapping.OriginalMapping +
+Mapping.OriginalMapping.Builder
+ + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/package-summary.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/package-summary.html new file mode 100644 index 0000000..b7ccd3b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/package-summary.html @@ -0,0 +1,203 @@ + + + + + +com.google.debugging.sourcemap.proto (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+

+Package com.google.debugging.sourcemap.proto +

+ + + + + + + + + + + + + +
+Interface Summary
Mapping.LineMappingOrBuilder 
Mapping.OriginalMappingOrBuilder 
+  + +

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+Class Summary
Mapping 
Mapping.LineMapping 
Mapping.LineMapping.Builder 
Mapping.OriginalMapping 
Mapping.OriginalMapping.Builder 
+  + +

+

+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/package-tree.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/package-tree.html new file mode 100644 index 0000000..b5bae66 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/debugging/sourcemap/proto/package-tree.html @@ -0,0 +1,195 @@ + + + + + +com.google.debugging.sourcemap.proto Class Hierarchy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Hierarchy For Package com.google.debugging.sourcemap.proto +

+
+
+
Package Hierarchies:
All Packages
+
+

+Class Hierarchy +

+ +

+Interface Hierarchy +

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/AbstractCompiler.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/AbstractCompiler.html new file mode 100644 index 0000000..02bbf9d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/AbstractCompiler.html @@ -0,0 +1,504 @@ + + + + + +AbstractCompiler (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class AbstractCompiler

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.AbstractCompiler
+
+
+
All Implemented Interfaces:
SourceExcerptProvider
+
+
+
Direct Known Subclasses:
Compiler
+
+
+
+
public abstract class AbstractCompiler
extends Object
implements SourceExcerptProvider
+ + +

+An abstract compiler, to help remove the circular dependency of + passes on JSCompiler. + + This is an abstract class, so that we can make the methods package-private. +

+ +

+


+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from interface com.google.javascript.jscomp.SourceExcerptProvider
SourceExcerptProvider.ExcerptFormatter, SourceExcerptProvider.SourceExcerpt
+  + + + + + + + + + + + +
+Constructor Summary
AbstractCompiler() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+abstract  CodingConventiongetCodingConvention() + +
+          Gets the current coding convention.
+abstract  ErrorManagergetErrorManager() + +
+          Gets the error manager.
+abstract  CompilerInputgetInput(InputId inputId) + +
+          Looks up an input (possibly an externs input) by name.
+abstract  doublegetProgress() + +
+           
+abstract  com.google.javascript.jscomp.ReverseAbstractInterpretergetReverseAbstractInterpreter() + +
+          Get an interpreter for type analysis.
+abstract  ScopegetTopScope() + +
+          Gets the top scope.
+abstract  JSTypeRegistrygetTypeRegistry() + +
+          Gets a central registry of type information from the compiled JS.
+abstract  voidreport(JSError error) + +
+          Report an error or warning.
+abstract  voidreportCodeChange() + +
+          Report code changes.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.SourceExcerptProvider
getSourceLine, getSourceRegion
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+AbstractCompiler

+
+public AbstractCompiler()
+
+
+ + + + + + + + +
+Method Detail
+ +

+getInput

+
+public abstract CompilerInput getInput(InputId inputId)
+
+
Looks up an input (possibly an externs input) by name. May return null. +

+

+
+
+
+
+
+
+
+ +

+getTypeRegistry

+
+public abstract JSTypeRegistry getTypeRegistry()
+
+
Gets a central registry of type information from the compiled JS. +

+

+
+
+
+
+
+
+
+ +

+getTopScope

+
+public abstract Scope getTopScope()
+
+
Gets the top scope. +

+

+
+
+
+
+
+
+
+ +

+report

+
+public abstract void report(JSError error)
+
+
Report an error or warning. +

+

+
+
+
+
+
+
+
+ +

+getCodingConvention

+
+public abstract CodingConvention getCodingConvention()
+
+
Gets the current coding convention. +

+

+
+
+
+
+
+
+
+ +

+reportCodeChange

+
+public abstract void reportCodeChange()
+
+
Report code changes. +

+

+
+
+
+
+
+
+
+ +

+getReverseAbstractInterpreter

+
+public abstract com.google.javascript.jscomp.ReverseAbstractInterpreter getReverseAbstractInterpreter()
+
+
Get an interpreter for type analysis. +

+

+
+
+
+
+
+
+
+ +

+getErrorManager

+
+public abstract ErrorManager getErrorManager()
+
+
Gets the error manager. +

+

+
+
+
+
+
+
+
+ +

+getProgress

+
+public abstract double getProgress()
+
+
+
+
+
+ +
Returns:
a number in [0,1] range indicating an approximate progress of the + last compile. Note this should only be used as a hint and no assumptions + should be made on accuracy, even a completed compile may choose not to set + this to 1.0 at the end.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/AbstractMessageFormatter.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/AbstractMessageFormatter.html new file mode 100644 index 0000000..c4e1028 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/AbstractMessageFormatter.html @@ -0,0 +1,311 @@ + + + + + +AbstractMessageFormatter (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class AbstractMessageFormatter

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.AbstractMessageFormatter
+
+
+
All Implemented Interfaces:
MessageFormatter
+
+
+
Direct Known Subclasses:
LightweightMessageFormatter
+
+
+
+
public abstract class AbstractMessageFormatter
extends Object
implements MessageFormatter
+ + +

+Abstract message formatter providing default behavior for implementations + of MessageFormatter needing a SourceExcerptProvider. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
AbstractMessageFormatter(SourceExcerptProvider source) + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+protected  SourceExcerptProvidergetSource() + +
+          Get the source excerpt provider.
+ voidsetColorize(boolean colorize) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.MessageFormatter
formatError, formatWarning
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+AbstractMessageFormatter

+
+public AbstractMessageFormatter(SourceExcerptProvider source)
+
+
+ + + + + + + + +
+Method Detail
+ +

+setColorize

+
+public void setColorize(boolean colorize)
+
+
+
+
+
+
+
+
+
+ +

+getSource

+
+protected final SourceExcerptProvider getSource()
+
+
Get the source excerpt provider. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/AnonymousFunctionNamingPolicy.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/AnonymousFunctionNamingPolicy.html new file mode 100644 index 0000000..55fb32b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/AnonymousFunctionNamingPolicy.html @@ -0,0 +1,391 @@ + + + + + +AnonymousFunctionNamingPolicy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum AnonymousFunctionNamingPolicy

+
+java.lang.Object
+  extended by java.lang.Enum<AnonymousFunctionNamingPolicy>
+      extended by com.google.javascript.jscomp.AnonymousFunctionNamingPolicy
+
+
+
All Implemented Interfaces:
Serializable, Comparable<AnonymousFunctionNamingPolicy>
+
+
+
+
public enum AnonymousFunctionNamingPolicy
extends Enum<AnonymousFunctionNamingPolicy>
+ + +

+Strategies for how to do naming of anonymous functions that occur as + r-values in assignments and variable declarations. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Enum Constant Summary
MAPPED + +
+          Generates short unique names and provides a mapping from them back to a + more meaningful name that's based on the left-hand side of the + assignment.
OFF + +
+          Don't give anonymous functions names
UNMAPPED + +
+          Generates names that are based on the left-hand side of the assignment.
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ char[]getReservedCharacters() + +
+          Gets characters that are reserved for use in anonymous function names and + can't be used in variable or property names.
+static AnonymousFunctionNamingPolicyvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static AnonymousFunctionNamingPolicy[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+OFF

+
+public static final AnonymousFunctionNamingPolicy OFF
+
+
Don't give anonymous functions names +

+

+
+
+
+ +

+UNMAPPED

+
+public static final AnonymousFunctionNamingPolicy UNMAPPED
+
+
Generates names that are based on the left-hand side of the assignment. + Runs after variable and property renaming, so that the generated names + will be short and obfuscated. +

+

+
See Also:
NameAnonymousFunctions
+
+
+ +

+MAPPED

+
+public static final AnonymousFunctionNamingPolicy MAPPED
+
+
Generates short unique names and provides a mapping from them back to a + more meaningful name that's based on the left-hand side of the + assignment. +

+

+
See Also:
NameAnonymousFunctionsMapped
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static AnonymousFunctionNamingPolicy[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (AnonymousFunctionNamingPolicy c : AnonymousFunctionNamingPolicy.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static AnonymousFunctionNamingPolicy valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+
+ +

+getReservedCharacters

+
+public char[] getReservedCharacters()
+
+
Gets characters that are reserved for use in anonymous function names and + can't be used in variable or property names. +

+

+ +
Returns:
reserved characters or null if no characters are reserved
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/AstValidator.ViolationHandler.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/AstValidator.ViolationHandler.html new file mode 100644 index 0000000..0813904 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/AstValidator.ViolationHandler.html @@ -0,0 +1,223 @@ + + + + + +AstValidator.ViolationHandler (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface AstValidator.ViolationHandler

+
+
Enclosing class:
AstValidator
+
+
+
+
public static interface AstValidator.ViolationHandler
+ + +

+


+ +

+ + + + + + + + + + + + +
+Method Summary
+ voidhandleViolation(String message, + Node n) + +
+           
+  +

+ + + + + + + + +
+Method Detail
+ +

+handleViolation

+
+void handleViolation(String message,
+                     Node n)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/AstValidator.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/AstValidator.html new file mode 100644 index 0000000..5c8153a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/AstValidator.html @@ -0,0 +1,421 @@ + + + + + +AstValidator (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class AstValidator

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.AstValidator
+
+
+
All Implemented Interfaces:
CompilerPass
+
+
+
+
public class AstValidator
extends Object
implements CompilerPass
+ + +

+This class walks the AST and validates that the structure is correct. +

+ +

+


+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static interfaceAstValidator.ViolationHandler + +
+           
+  + + + + + + + + + + + + + +
+Constructor Summary
AstValidator() + +
+           
AstValidator(AstValidator.ViolationHandler handler) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidprocess(Node externs, + Node root) + +
+          Process the JS with root node root.
+ voidvalidateCodeRoot(Node n) + +
+           
+ voidvalidateExpression(Node n) + +
+           
+ voidvalidateRoot(Node n) + +
+           
+ voidvalidateScript(Node n) + +
+           
+ voidvalidateStatement(Node n) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+AstValidator

+
+public AstValidator(AstValidator.ViolationHandler handler)
+
+
+
+ +

+AstValidator

+
+public AstValidator()
+
+
+ + + + + + + + +
+Method Detail
+ +

+process

+
+public void process(Node externs,
+                    Node root)
+
+
Description copied from interface: CompilerPass
+
Process the JS with root node root. + Can modify the contents of each Node tree +

+

+
Specified by:
process in interface CompilerPass
+
+
+
Parameters:
externs - Top of external JS tree
root - Top of JS tree
+
+
+
+ +

+validateRoot

+
+public void validateRoot(Node n)
+
+
+
+
+
+
+
+
+
+ +

+validateCodeRoot

+
+public void validateCodeRoot(Node n)
+
+
+
+
+
+
+
+
+
+ +

+validateScript

+
+public void validateScript(Node n)
+
+
+
+
+
+
+
+
+
+ +

+validateStatement

+
+public void validateStatement(Node n)
+
+
+
+
+
+
+
+
+
+ +

+validateExpression

+
+public void validateExpression(Node n)
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/BasicErrorManager.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/BasicErrorManager.html new file mode 100644 index 0000000..f9ebddb --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/BasicErrorManager.html @@ -0,0 +1,516 @@ + + + + + +BasicErrorManager (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class BasicErrorManager

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.BasicErrorManager
+
+
+
All Implemented Interfaces:
ErrorHandler, ErrorManager
+
+
+
Direct Known Subclasses:
AntErrorManager, LoggerErrorManager, PrintStreamErrorManager
+
+
+
+
public abstract class BasicErrorManager
extends Object
implements ErrorManager
+ + +

+

A basic error manager that sorts all errors and warnings reported to it to + generate a sorted report when the generateReport() method + is called.

+ +

This error manager does not produce any output, but subclasses can + override the println(CheckLevel, JSError) method to generate custom + output.

+

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
BasicErrorManager() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidgenerateReport() + +
+          Writes a report to an implementation-specific medium.
+ intgetErrorCount() + +
+          Gets the number of reported errors.
+ JSError[]getErrors() + +
+          Gets all the errors.
+ doublegetTypedPercent() + +
+          Gets the percentage of typed expressions.
+ intgetWarningCount() + +
+          Gets the number of reported warnings.
+ JSError[]getWarnings() + +
+          Gets all the warnings.
+abstract  voidprintln(CheckLevel level, + JSError error) + +
+          Print a message with a trailing new line.
+protected abstract  voidprintSummary() + +
+          Print the summary of the compilation - number of errors and warnings.
+ voidreport(CheckLevel level, + JSError error) + +
+          Reports an error.
+ voidsetTypedPercent(double typedPercent) + +
+          Sets the percentage of typed expressions.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+BasicErrorManager

+
+public BasicErrorManager()
+
+
+ + + + + + + + +
+Method Detail
+ +

+report

+
+public void report(CheckLevel level,
+                   JSError error)
+
+
Description copied from interface: ErrorManager
+
Reports an error. The errors will be displayed by the + ErrorManager.generateReport() at the discretion of the implementation. +

+

+
Specified by:
report in interface ErrorHandler
Specified by:
report in interface ErrorManager
+
+
+
Parameters:
level - the reporting level
error - the error to report
+
+
+
+ +

+generateReport

+
+public void generateReport()
+
+
Description copied from interface: ErrorManager
+
Writes a report to an implementation-specific medium. The compiler calls + this method after any and all ErrorManager.report(com.google.javascript.jscomp.CheckLevel, com.google.javascript.jscomp.JSError) calls. +

+

+
Specified by:
generateReport in interface ErrorManager
+
+
+
+
+
+
+ +

+println

+
+public abstract void println(CheckLevel level,
+                             JSError error)
+
+
Print a message with a trailing new line. This method is called by the + generateReport() method when generating messages. +

+

+
+
+
+
+
+
+
+ +

+printSummary

+
+protected abstract void printSummary()
+
+
Print the summary of the compilation - number of errors and warnings. +

+

+
+
+
+
+
+
+
+ +

+getErrorCount

+
+public int getErrorCount()
+
+
Description copied from interface: ErrorManager
+
Gets the number of reported errors. +

+

+
Specified by:
getErrorCount in interface ErrorManager
+
+
+
+
+
+
+ +

+getWarningCount

+
+public int getWarningCount()
+
+
Description copied from interface: ErrorManager
+
Gets the number of reported warnings. +

+

+
Specified by:
getWarningCount in interface ErrorManager
+
+
+
+
+
+
+ +

+getErrors

+
+public JSError[] getErrors()
+
+
Description copied from interface: ErrorManager
+
Gets all the errors. +

+

+
Specified by:
getErrors in interface ErrorManager
+
+
+
+
+
+
+ +

+getWarnings

+
+public JSError[] getWarnings()
+
+
Description copied from interface: ErrorManager
+
Gets all the warnings. +

+

+
Specified by:
getWarnings in interface ErrorManager
+
+
+
+
+
+
+ +

+setTypedPercent

+
+public void setTypedPercent(double typedPercent)
+
+
Description copied from interface: ErrorManager
+
Sets the percentage of typed expressions. +

+

+
Specified by:
setTypedPercent in interface ErrorManager
+
+
+
+
+
+
+ +

+getTypedPercent

+
+public double getTypedPercent()
+
+
Description copied from interface: ErrorManager
+
Gets the percentage of typed expressions. +

+

+
Specified by:
getTypedPercent in interface ErrorManager
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CallGraph.Callsite.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CallGraph.Callsite.html new file mode 100644 index 0000000..79a2103 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CallGraph.Callsite.html @@ -0,0 +1,340 @@ + + + + + +CallGraph.Callsite (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class CallGraph.Callsite

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CallGraph.Callsite
+
+
+
Enclosing class:
CallGraph
+
+
+
+
public class CallGraph.Callsite
extends Object
+ + +

+An inner class that represents call sites in the call graph. + A Callsite knows how to get its AST node, what its containing + Function is, and what its target Functions are. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ NodegetAstNode() + +
+           
+ CallGraph.FunctiongetContainingFunction() + +
+           
+ Collection<CallGraph.Function>getPossibleTargets() + +
+          Returns the possible target functions that this callsite could call.
+ booleanhasExternTarget() + +
+          If true, then this callsite could target a function defined in the + externs.
+ booleanhasUnknownTarget() + +
+          If true, then DefinitionProvider used in callgraph construction + was unable find all target functions of this callsite.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getAstNode

+
+public Node getAstNode()
+
+
+
+
+
+
+ +

+getContainingFunction

+
+public CallGraph.Function getContainingFunction()
+
+
+
+
+
+
+ +

+getPossibleTargets

+
+public Collection<CallGraph.Function> getPossibleTargets()
+
+
Returns the possible target functions that this callsite could call. + + These targets do not include functions defined in externs. If this + callsite could call an extern function, then hasExternTarget() will + return true. + + getKnownTargets() is a best effort only: the collection may include + other functions that are not actual targets and (if hasUnknownTargets() + is true) may be missing actual targets. + + This method should not be called on a Callsite from a CallGraph + that was constructed with computeForwardGraph false. +

+

+
+
+
+
+ +

+hasUnknownTarget

+
+public boolean hasUnknownTarget()
+
+
If true, then DefinitionProvider used in callgraph construction + was unable find all target functions of this callsite. + + If false, then getKnownTargets() contains all the possible targets of + this callsite (and, perhaps, additional targets as well). +

+

+
+
+
+
+ +

+hasExternTarget

+
+public boolean hasExternTarget()
+
+
If true, then this callsite could target a function defined in the + externs. If false, then not. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CallGraph.Function.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CallGraph.Function.html new file mode 100644 index 0000000..5bf4121 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CallGraph.Function.html @@ -0,0 +1,401 @@ + + + + + +CallGraph.Function (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class CallGraph.Function

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CallGraph.Function
+
+
+
Enclosing class:
CallGraph
+
+
+
+
public class CallGraph.Function
extends Object
+ + +

+An inner class that represents functions in the call graph. + A Function knows how to get its AST node and what Callsites + it contains. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ NodegetAstNode() + +
+          Returns the underlying AST node for the function.
+ NodegetBodyNode() + +
+          Returns the AST node for the body of the function.
+ Collection<CallGraph.Callsite>getCallsitesInFunction() + +
+          Returns the callsites in this function.
+ Collection<CallGraph.Callsite>getCallsitesPossiblyTargetingFunction() + +
+          Returns a collection of callsites that might call this function.
+ StringgetName() + +
+          Gets the name of this function.
+ booleanisAliased() + +
+          Returns true if the function is aliased.
+ booleanisExposedToCallOrApply() + +
+          Returns true if the function is ever exposed to ".call" or ".apply".
+ booleanisMain() + +
+          Does this function represent the global "main" function?
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+isMain

+
+public boolean isMain()
+
+
Does this function represent the global "main" function? +

+

+
+
+
+
+ +

+getAstNode

+
+public Node getAstNode()
+
+
Returns the underlying AST node for the function. This usually + has type Token.FUNCTION but in the case of the "main" function + will have type Token.BLOCK. +

+

+
+
+
+
+ +

+getBodyNode

+
+public Node getBodyNode()
+
+
Returns the AST node for the body of the function. If this function + is the main function, it will return the global block. +

+

+
+
+
+
+ +

+getName

+
+public String getName()
+
+
Gets the name of this function. Returns null if the function is + anonymous. +

+

+
+
+
+
+ +

+getCallsitesInFunction

+
+public Collection<CallGraph.Callsite> getCallsitesInFunction()
+
+
Returns the callsites in this function. +

+

+
+
+
+
+ +

+getCallsitesPossiblyTargetingFunction

+
+public Collection<CallGraph.Callsite> getCallsitesPossiblyTargetingFunction()
+
+
Returns a collection of callsites that might call this function. + + getCallsitesPossiblyTargetingFunction() is a best effort only: the + collection may include callsites that do not actually call this function + and if this function is exported or aliased may be missing actual + targets. + + This method should not be called on a Function from a CallGraph + that was constructed with computeBackwardGraph false. +

+

+
+
+
+
+ +

+isAliased

+
+public boolean isAliased()
+
+
Returns true if the function is aliased. +

+

+
+
+
+
+ +

+isExposedToCallOrApply

+
+public boolean isExposedToCallOrApply()
+
+
Returns true if the function is ever exposed to ".call" or ".apply". +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CallGraph.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CallGraph.html new file mode 100644 index 0000000..4f19720 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CallGraph.html @@ -0,0 +1,601 @@ + + + + + +CallGraph (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class CallGraph

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CallGraph
+
+
+
All Implemented Interfaces:
CompilerPass
+
+
+
+
public class CallGraph
extends Object
implements CompilerPass
+ + +

+A pass the uses a DefinitionProvider to compute a call graph for an + AST. + +

A CallGraph connects CallGraph.Functions to CallGraph.Callsites and + vice versa: each function in the graph links to the callsites it contains and + each callsite links to the functions it could call. Similarly, each callsite + links to the function that contains it and each function links to the + callsites that could call it. + +

The callgraph is not precise. That is, a callsite may indicate it can + call a function when in fact it does not do so in the running program. + +

The callgraph is also not complete: in some cases it may be unable to + determine some targets of a callsite. In this case, + Callsite.hasUnknownTarget() will return true. + +

The CallGraph doesn't (currently) have functions for externally defined + functions; however, callsites that target externs will have hasExternTarget() + return true. + +

TODO(dcc): Have CallGraph (optionally?) include functions for externs. +

+ +

+


+ +

+ + + + + + + + + + + + + + + +
+Nested Class Summary
+ classCallGraph.Callsite + +
+          An inner class that represents call sites in the call graph.
+ classCallGraph.Function + +
+          An inner class that represents functions in the call graph.
+ + + + + + + + + + +
+Field Summary
+static StringMAIN_FUNCTION_NAME + +
+          The name we give the main function.
+  + + + + + + + + + + + + + +
+Constructor Summary
CallGraph(AbstractCompiler compiler) + +
+          Creates a call graph object support both forward and backward lookups.
CallGraph(AbstractCompiler compiler, + boolean computeForwardGraph, + boolean computeBackwardGraph) + +
+          Creates a call graph object supporting the specified lookups.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ Collection<CallGraph.Callsite>getAllCallsites() + +
+          Returns a collection of all callsites in the call graph.
+ Collection<CallGraph.Function>getAllFunctions() + +
+          Returns a collection of all functions (including the main function) + in the call graph.
+ DiGraph<CallGraph.Function,CallGraph.Callsite>getBackwardDirectedGraph() + +
+          Constructs and returns a directed graph where the nodes are functions and + the edges are callsites connecting callees to callers.
+ CallGraph.CallsitegetCallsiteForAstNode(Node callsiteNode) + +
+          Returns the call graph Callsite object corresponding to the provided + AST Token.CALL or Token.NEW node, or null if no such object exists.
+ DiGraph<CallGraph.Function,CallGraph.Callsite>getForwardDirectedGraph() + +
+          Constructs and returns a directed graph where the nodes are functions and + the edges are callsites connecting callers to callees.
+ CallGraph.FunctiongetFunctionForAstNode(Node functionNode) + +
+          Returns the call graph Function object corresponding to the provided + AST Token.FUNCTION node, or null if no such object exists.
+ CallGraph.FunctiongetMainFunction() + +
+          Returns a Function object representing the "main" global function.
+ CallGraph.FunctiongetUniqueFunctionWithName(String desiredName) + +
+          Finds a function with the given name.
+ voidprocess(Node externsRoot, + Node jsRoot) + +
+          Builds a call graph for the given externsRoot and jsRoot.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+MAIN_FUNCTION_NAME

+
+public static final String MAIN_FUNCTION_NAME
+
+
The name we give the main function. +

+

+
See Also:
Constant Field Values
+
+ + + + + + + + +
+Constructor Detail
+ +

+CallGraph

+
+public CallGraph(AbstractCompiler compiler,
+                 boolean computeForwardGraph,
+                 boolean computeBackwardGraph)
+
+
Creates a call graph object supporting the specified lookups. + + At leats one (and possibly both) of computeForwardGraph and + computeBackwardGraph must be true. +

+

+
Parameters:
compiler - The compiler
computeForwardGraph - Should the call graph allow lookup of the target + functions a given callsite could call?
computeBackwardGraph - Should the call graph allow lookup of the + callsites that could call a given function?
+
+
+ +

+CallGraph

+
+public CallGraph(AbstractCompiler compiler)
+
+
Creates a call graph object support both forward and backward lookups. +

+

+ + + + + + + + +
+Method Detail
+ +

+process

+
+public void process(Node externsRoot,
+                    Node jsRoot)
+
+
Builds a call graph for the given externsRoot and jsRoot. + This method must not be called more than once per CallGraph instance. +

+

+
Specified by:
process in interface CompilerPass
+
+
+
Parameters:
externsRoot - Top of external JS tree
jsRoot - Top of JS tree
+
+
+
+ +

+getFunctionForAstNode

+
+public CallGraph.Function getFunctionForAstNode(Node functionNode)
+
+
Returns the call graph Function object corresponding to the provided + AST Token.FUNCTION node, or null if no such object exists. +

+

+
+
+
+
+
+
+
+ +

+getMainFunction

+
+public CallGraph.Function getMainFunction()
+
+
Returns a Function object representing the "main" global function. +

+

+
+
+
+
+
+
+
+ +

+getAllFunctions

+
+public Collection<CallGraph.Function> getAllFunctions()
+
+
Returns a collection of all functions (including the main function) + in the call graph. +

+

+
+
+
+
+
+
+
+ +

+getUniqueFunctionWithName

+
+public CallGraph.Function getUniqueFunctionWithName(String desiredName)
+
+
Finds a function with the given name. Throws an exception if + there are no functions or multiple functions with the name. This is + for testing purposes only. +

+

+
+
+
+
+
+
+
+ +

+getCallsiteForAstNode

+
+public CallGraph.Callsite getCallsiteForAstNode(Node callsiteNode)
+
+
Returns the call graph Callsite object corresponding to the provided + AST Token.CALL or Token.NEW node, or null if no such object exists. +

+

+
+
+
+
+
+
+
+ +

+getAllCallsites

+
+public Collection<CallGraph.Callsite> getAllCallsites()
+
+
Returns a collection of all callsites in the call graph. +

+

+
+
+
+
+
+
+
+ +

+getForwardDirectedGraph

+
+public DiGraph<CallGraph.Function,CallGraph.Callsite> getForwardDirectedGraph()
+
+
Constructs and returns a directed graph where the nodes are functions and + the edges are callsites connecting callers to callees. + + It is safe to call this method on both forward and backwardly constructed + CallGraphs. +

+

+
+
+
+
+
+
+
+ +

+getBackwardDirectedGraph

+
+public DiGraph<CallGraph.Function,CallGraph.Callsite> getBackwardDirectedGraph()
+
+
Constructs and returns a directed graph where the nodes are functions and + the edges are callsites connecting callees to callers. + + It is safe to call this method on both forward and backwardly constructed + CallGraphs. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CheckLevel.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CheckLevel.html new file mode 100644 index 0000000..b2143dd --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CheckLevel.html @@ -0,0 +1,357 @@ + + + + + +CheckLevel (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum CheckLevel

+
+java.lang.Object
+  extended by java.lang.Enum<CheckLevel>
+      extended by com.google.javascript.jscomp.CheckLevel
+
+
+
All Implemented Interfaces:
Serializable, Comparable<CheckLevel>
+
+
+
+
public enum CheckLevel
extends Enum<CheckLevel>
+ + +

+Controls checking levels of certain options. For all checks going + forward, this should be used instead of booleans, so teams and + individuals can control which checks are off, which produce only warnings, + and which produce errors, without everyone having to agree. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Enum Constant Summary
ERROR + +
+           
OFF + +
+           
WARNING + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static CheckLevelvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static CheckLevel[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+ERROR

+
+public static final CheckLevel ERROR
+
+
+
+
+
+ +

+WARNING

+
+public static final CheckLevel WARNING
+
+
+
+
+
+ +

+OFF

+
+public static final CheckLevel OFF
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static CheckLevel[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (CheckLevel c : CheckLevel.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static CheckLevel valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CheckLevelLegacy.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CheckLevelLegacy.html new file mode 100644 index 0000000..3d0bded --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CheckLevelLegacy.html @@ -0,0 +1,373 @@ + + + + + +CheckLevelLegacy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum CheckLevelLegacy

+
+java.lang.Object
+  extended by java.lang.Enum<CheckLevelLegacy>
+      extended by com.google.javascript.jscomp.CheckLevelLegacy
+
+
+
All Implemented Interfaces:
Serializable, Comparable<CheckLevelLegacy>
+
+
+
+
public enum CheckLevelLegacy
extends Enum<CheckLevelLegacy>
+ + +

+Enum used in flags to control the behavior of JS compiler checks. These + must be converted to CheckLevel enums before being used to control + options. Only use this for legacy flags. For new flags, simply use + CheckLevel. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + +
+Enum Constant Summary
ERROR + +
+           
LEGACY + +
+           
OFF + +
+           
WARNING + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static CheckLevelLegacyvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static CheckLevelLegacy[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+LEGACY

+
+public static final CheckLevelLegacy LEGACY
+
+
+
+
+
+ +

+OFF

+
+public static final CheckLevelLegacy OFF
+
+
+
+
+
+ +

+WARNING

+
+public static final CheckLevelLegacy WARNING
+
+
+
+
+
+ +

+ERROR

+
+public static final CheckLevelLegacy ERROR
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static CheckLevelLegacy[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (CheckLevelLegacy c : CheckLevelLegacy.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static CheckLevelLegacy valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ClosureCodingConvention.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ClosureCodingConvention.html new file mode 100644 index 0000000..4aba8cb --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ClosureCodingConvention.html @@ -0,0 +1,844 @@ + + + + + +ClosureCodingConvention (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class ClosureCodingConvention

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CodingConventions.Proxy
+      extended by com.google.javascript.jscomp.ClosureCodingConvention
+
+
+
All Implemented Interfaces:
CodingConvention, Serializable
+
+
+
+
public class ClosureCodingConvention
extends CodingConventions.Proxy
+ + +

+This describes the Closure-specific JavaScript coding conventions. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from interface com.google.javascript.jscomp.CodingConvention
CodingConvention.AssertionFunctionSpec, CodingConvention.Bind, CodingConvention.DelegateRelationship, CodingConvention.ObjectLiteralCast, CodingConvention.SubclassRelationship, CodingConvention.SubclassType
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.jscomp.CodingConventions.Proxy
nextConvention
+  + + + + + + + + + + + + + +
+Constructor Summary
ClosureCodingConvention() + +
+           
ClosureCodingConvention(CodingConvention wrapped) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidapplySingletonGetter(FunctionType functionType, + FunctionType getterType, + ObjectType objectType) + +
+          In many JS libraries, the function that adds a singleton getter to a class + adds properties to the class.
+ voidapplySubclassRelationship(FunctionType parentCtor, + FunctionType childCtor, + CodingConvention.SubclassType type) + +
+          Closure's goog.inherits adds a superClass_ property to the + subclass, and a constructor property.
+ CodingConvention.BinddescribeFunctionBind(Node n) + +
+          A Bind instance or null.
+ StringextractClassNameIfProvide(Node node, + Node parent) + +
+          Exctracts X from goog.provide('X'), if the applied Node is goog.
+ StringextractClassNameIfRequire(Node node, + Node parent) + +
+          Exctracts X from goog.require('X'), if the applied Node is goog.
+ StringgetAbstractMethodName() + +
+          Function name for abstract methods.
+ Collection<CodingConvention.AssertionFunctionSpec>getAssertionFunctions() + +
+          Returns the set of AssertionFunction.
+ CodingConvention.SubclassRelationshipgetClassesDefinedByCall(Node callNode) + +
+          Checks if the given method defines a subclass relationship, + and if it does, returns information on that relationship.
+ StringgetExportPropertyFunction() + +
+          Use closure's implementation.
+ StringgetExportSymbolFunction() + +
+          Use closure's implementation.
+ StringgetGlobalObject() + +
+          Gets the name of the global object.
+ CodingConvention.ObjectLiteralCastgetObjectLiteralCast(NodeTraversal t, + Node callNode) + +
+          Checks if the given method performs a object literal cast, and if it does, + returns information on the cast.
+ StringgetSingletonGetterClassName(Node callNode) + +
+          Checks if the given method defines a singleton getter, and if it does, + returns the name of the class with the singleton getter.
+ List<String>identifyTypeDeclarationCall(Node n) + +
+          Checks if the given CALL node is forward-declaring any types, + and returns the name of the types if it is.
+ booleanisOptionalParameter(Node parameter) + +
+          This checks whether a given parameter name should be treated as an + optional parameter as far as type checking or function call arg count + checking is concerned.
+ booleanisPrivate(String name) + +
+          Checks whether a name should be considered private.
+ booleanisPropertyTestFunction(Node call) + +
+          Whether this CALL function is testing for the existence of a property.
+ booleanisSuperClassReference(String propertyName) + +
+          Returns true if passed a string referring to the superclass.
+ booleanisVarArgsParameter(Node parameter) + +
+          This checks whether a given parameter should be treated as a marker + for a variable argument list function.
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.CodingConventions.Proxy
applyDelegateRelationship, checkForCallingConventionDefiningCalls, defineDelegateProxyPrototypeProperties, getDelegateRelationship, getDelegateSuperclassName, isConstant, isConstantKey, isExported, isExported, isPrototypeAlias, isValidEnumKey
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+ClosureCodingConvention

+
+public ClosureCodingConvention()
+
+
+
+ +

+ClosureCodingConvention

+
+public ClosureCodingConvention(CodingConvention wrapped)
+
+
+ + + + + + + + +
+Method Detail
+ +

+applySubclassRelationship

+
+public void applySubclassRelationship(FunctionType parentCtor,
+                                      FunctionType childCtor,
+                                      CodingConvention.SubclassType type)
+
+
Closure's goog.inherits adds a superClass_ property to the + subclass, and a constructor property. +

+

+
Specified by:
applySubclassRelationship in interface CodingConvention
Overrides:
applySubclassRelationship in class CodingConventions.Proxy
+
+
+
+
+
+
+ +

+getClassesDefinedByCall

+
+public CodingConvention.SubclassRelationship getClassesDefinedByCall(Node callNode)
+
+
Checks if the given method defines a subclass relationship, + and if it does, returns information on that relationship. By default, + always returns null. Meant to be overridden by subclasses. + +

Understands several different inheritance patterns that occur in + Google code (various uses of inherits and mixin). +

+

+
Specified by:
getClassesDefinedByCall in interface CodingConvention
Overrides:
getClassesDefinedByCall in class CodingConventions.Proxy
+
+
+
Parameters:
callNode - A CALL node.
+
+
+
+ +

+isSuperClassReference

+
+public boolean isSuperClassReference(String propertyName)
+
+
Description copied from interface: CodingConvention
+
Returns true if passed a string referring to the superclass. The string + will usually be from the string node at the right of a GETPROP, e.g. + this.superClass_. +

+

+
Specified by:
isSuperClassReference in interface CodingConvention
Overrides:
isSuperClassReference in class CodingConventions.Proxy
+
+
+
+
+
+
+ +

+extractClassNameIfProvide

+
+public String extractClassNameIfProvide(Node node,
+                                        Node parent)
+
+
Exctracts X from goog.provide('X'), if the applied Node is goog. +

+

+
Specified by:
extractClassNameIfProvide in interface CodingConvention
Overrides:
extractClassNameIfProvide in class CodingConventions.Proxy
+
+
+ +
Returns:
The extracted class name, or null.
+
+
+
+ +

+extractClassNameIfRequire

+
+public String extractClassNameIfRequire(Node node,
+                                        Node parent)
+
+
Exctracts X from goog.require('X'), if the applied Node is goog. +

+

+
Specified by:
extractClassNameIfRequire in interface CodingConvention
Overrides:
extractClassNameIfRequire in class CodingConventions.Proxy
+
+
+ +
Returns:
The extracted class name, or null.
+
+
+
+ +

+getExportPropertyFunction

+
+public String getExportPropertyFunction()
+
+
Use closure's implementation. +

+

+
Specified by:
getExportPropertyFunction in interface CodingConvention
Overrides:
getExportPropertyFunction in class CodingConventions.Proxy
+
+
+ +
Returns:
closure's function name for exporting properties.
+
+
+
+ +

+getExportSymbolFunction

+
+public String getExportSymbolFunction()
+
+
Use closure's implementation. +

+

+
Specified by:
getExportSymbolFunction in interface CodingConvention
Overrides:
getExportSymbolFunction in class CodingConventions.Proxy
+
+
+ +
Returns:
closure's function name for exporting symbols.
+
+
+
+ +

+identifyTypeDeclarationCall

+
+public List<String> identifyTypeDeclarationCall(Node n)
+
+
Description copied from interface: CodingConvention
+
Checks if the given CALL node is forward-declaring any types, + and returns the name of the types if it is. +

+

+
Specified by:
identifyTypeDeclarationCall in interface CodingConvention
Overrides:
identifyTypeDeclarationCall in class CodingConventions.Proxy
+
+
+
+
+
+
+ +

+getAbstractMethodName

+
+public String getAbstractMethodName()
+
+
Description copied from interface: CodingConvention
+
Function name for abstract methods. An abstract method can be assigned to + an interface method instead of an function expression in order to avoid + linter warnings produced by assigning a function without a return value + where a return value is expected. +

+

+
Specified by:
getAbstractMethodName in interface CodingConvention
Overrides:
getAbstractMethodName in class CodingConventions.Proxy
+
+
+ +
Returns:
function name.
+
+
+
+ +

+getSingletonGetterClassName

+
+public String getSingletonGetterClassName(Node callNode)
+
+
Description copied from interface: CodingConvention
+
Checks if the given method defines a singleton getter, and if it does, + returns the name of the class with the singleton getter. By default, always + returns null. Meant to be overridden by subclasses. +

+

+
Specified by:
getSingletonGetterClassName in interface CodingConvention
Overrides:
getSingletonGetterClassName in class CodingConventions.Proxy
+
+
+
Parameters:
callNode - A CALL node.
+
+
+
+ +

+applySingletonGetter

+
+public void applySingletonGetter(FunctionType functionType,
+                                 FunctionType getterType,
+                                 ObjectType objectType)
+
+
Description copied from interface: CodingConvention
+
In many JS libraries, the function that adds a singleton getter to a class + adds properties to the class. +

+

+
Specified by:
applySingletonGetter in interface CodingConvention
Overrides:
applySingletonGetter in class CodingConventions.Proxy
+
+
+
+
+
+
+ +

+getGlobalObject

+
+public String getGlobalObject()
+
+
Description copied from interface: CodingConvention
+
Gets the name of the global object. +

+

+
Specified by:
getGlobalObject in interface CodingConvention
Overrides:
getGlobalObject in class CodingConventions.Proxy
+
+
+
+
+
+
+ +

+isPropertyTestFunction

+
+public boolean isPropertyTestFunction(Node call)
+
+
Description copied from interface: CodingConvention
+
Whether this CALL function is testing for the existence of a property. +

+

+
Specified by:
isPropertyTestFunction in interface CodingConvention
Overrides:
isPropertyTestFunction in class CodingConventions.Proxy
+
+
+
+
+
+
+ +

+getObjectLiteralCast

+
+public CodingConvention.ObjectLiteralCast getObjectLiteralCast(NodeTraversal t,
+                                                               Node callNode)
+
+
Description copied from interface: CodingConvention
+
Checks if the given method performs a object literal cast, and if it does, + returns information on the cast. By default, always returns null. Meant + to be overridden by subclasses. +

+

+
Specified by:
getObjectLiteralCast in interface CodingConvention
Overrides:
getObjectLiteralCast in class CodingConventions.Proxy
+
+
+
Parameters:
t - The node traversal.
callNode - A CALL node.
+
+
+
+ +

+isOptionalParameter

+
+public boolean isOptionalParameter(Node parameter)
+
+
Description copied from interface: CodingConvention
+
This checks whether a given parameter name should be treated as an + optional parameter as far as type checking or function call arg count + checking is concerned. Note that an optional function parameter may be + declared as a simple type and is automatically converted to a union of the + declared type and Undefined. +

+

+
Specified by:
isOptionalParameter in interface CodingConvention
Overrides:
isOptionalParameter in class CodingConventions.Proxy
+
+
+
Parameters:
parameter - The parameter's node. +
Returns:
true if the parameter should be treated as an optional + parameter.
+
+
+
+ +

+isVarArgsParameter

+
+public boolean isVarArgsParameter(Node parameter)
+
+
Description copied from interface: CodingConvention
+
This checks whether a given parameter should be treated as a marker + for a variable argument list function. A VarArgs parameter must be the + last parameter in a function declaration. +

+

+
Specified by:
isVarArgsParameter in interface CodingConvention
Overrides:
isVarArgsParameter in class CodingConventions.Proxy
+
+
+
Parameters:
parameter - The parameter's node. +
Returns:
true if the parameter should be treated as a variable + length parameter.
+
+
+
+ +

+isPrivate

+
+public boolean isPrivate(String name)
+
+
Description copied from interface: CodingConvention
+
Checks whether a name should be considered private. Private global + variables and functions can only be referenced within the source file in + which they are declared. Private properties and methods should only be + accessed by the class that defines them. +

+

+
Specified by:
isPrivate in interface CodingConvention
Overrides:
isPrivate in class CodingConventions.Proxy
+
+
+
Parameters:
name - The name of a global variable or function, or a method or + property. +
Returns:
true if the name should be considered private.
+
+
+
+ +

+getAssertionFunctions

+
+public Collection<CodingConvention.AssertionFunctionSpec> getAssertionFunctions()
+
+
Description copied from interface: CodingConvention
+
Returns the set of AssertionFunction. +

+

+
Specified by:
getAssertionFunctions in interface CodingConvention
Overrides:
getAssertionFunctions in class CodingConventions.Proxy
+
+
+
+
+
+
+ +

+describeFunctionBind

+
+public CodingConvention.Bind describeFunctionBind(Node n)
+
+
Description copied from interface: CodingConvention
+
A Bind instance or null. +

+

+
Specified by:
describeFunctionBind in interface CodingConvention
Overrides:
describeFunctionBind in class CodingConventions.Proxy
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.AssertionFunctionSpec.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.AssertionFunctionSpec.html new file mode 100644 index 0000000..6a01c77 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.AssertionFunctionSpec.html @@ -0,0 +1,335 @@ + + + + + +CodingConvention.AssertionFunctionSpec (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class CodingConvention.AssertionFunctionSpec

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec
+
+
+
Enclosing interface:
CodingConvention
+
+
+
+
public static class CodingConvention.AssertionFunctionSpec
extends Object
+ + +

+A function that will throw an exception when either: + -One or more of its parameters evaluate to false. + -One or more of its parameters are not of a certain type. +

+ +

+


+ +

+ + + + + + + + + + + + + + +
+Constructor Summary
CodingConvention.AssertionFunctionSpec(String functionName) + +
+           
CodingConvention.AssertionFunctionSpec(String functionName, + JSTypeNative assertedType) + +
+           
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ NodegetAssertedParam(Node firstParam) + +
+          Returns the parameter of the assertion function that is being checked.
+ JSTypeNativegetAssertedType() + +
+          Returns the type for a type assertion, or null if the function asserts + that the node must not be null or undefined.
+ StringgetFunctionName() + +
+          Returns the name of the function.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+CodingConvention.AssertionFunctionSpec

+
+public CodingConvention.AssertionFunctionSpec(String functionName)
+
+
+
+ +

+CodingConvention.AssertionFunctionSpec

+
+public CodingConvention.AssertionFunctionSpec(String functionName,
+                                              JSTypeNative assertedType)
+
+
+ + + + + + + + +
+Method Detail
+ +

+getFunctionName

+
+public String getFunctionName()
+
+
Returns the name of the function. +

+

+
+
+
+
+ +

+getAssertedParam

+
+public Node getAssertedParam(Node firstParam)
+
+
Returns the parameter of the assertion function that is being checked. +

+

+
Parameters:
firstParam - The first parameter of the function call.
+
+
+
+ +

+getAssertedType

+
+public JSTypeNative getAssertedType()
+
+
Returns the type for a type assertion, or null if the function asserts + that the node must not be null or undefined. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.Bind.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.Bind.html new file mode 100644 index 0000000..98ffc78 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.Bind.html @@ -0,0 +1,243 @@ + + + + + +CodingConvention.Bind (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class CodingConvention.Bind

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CodingConvention.Bind
+
+
+
Enclosing interface:
CodingConvention
+
+
+
+
public static class CodingConvention.Bind
extends Object
+ + +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
CodingConvention.Bind(Node target, + Node thisValue, + Node parameters) + +
+           
+  + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+CodingConvention.Bind

+
+public CodingConvention.Bind(Node target,
+                             Node thisValue,
+                             Node parameters)
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.DelegateRelationship.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.DelegateRelationship.html new file mode 100644 index 0000000..eb749e0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.DelegateRelationship.html @@ -0,0 +1,213 @@ + + + + + +CodingConvention.DelegateRelationship (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class CodingConvention.DelegateRelationship

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CodingConvention.DelegateRelationship
+
+
+
Enclosing interface:
CodingConvention
+
+
+
+
public static class CodingConvention.DelegateRelationship
extends Object
+ + +

+Delegates provides a mechanism and structure for identifying where classes + can call out to optional code to augment their functionality. The optional + code is isolated from the base code through the use of a subclass in the + optional code derived from the delegate class in the base code. +

+ +

+


+ +

+ + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ +


+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.ObjectLiteralCast.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.ObjectLiteralCast.html new file mode 100644 index 0000000..fb5ccb9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.ObjectLiteralCast.html @@ -0,0 +1,211 @@ + + + + + +CodingConvention.ObjectLiteralCast (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class CodingConvention.ObjectLiteralCast

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CodingConvention.ObjectLiteralCast
+
+
+
Enclosing interface:
CodingConvention
+
+
+
+
public static class CodingConvention.ObjectLiteralCast
extends Object
+ + +

+An object literal cast provides a mechanism to cast object literals to + other types without a warning. +

+ +

+


+ +

+ + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ +


+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.SubclassRelationship.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.SubclassRelationship.html new file mode 100644 index 0000000..0a7dce5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.SubclassRelationship.html @@ -0,0 +1,243 @@ + + + + + +CodingConvention.SubclassRelationship (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class CodingConvention.SubclassRelationship

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CodingConvention.SubclassRelationship
+
+
+
Enclosing interface:
CodingConvention
+
+
+
+
public static class CodingConvention.SubclassRelationship
extends Object
+ + +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
CodingConvention.SubclassRelationship(CodingConvention.SubclassType type, + Node subclassNode, + Node superclassNode) + +
+           
+  + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+CodingConvention.SubclassRelationship

+
+public CodingConvention.SubclassRelationship(CodingConvention.SubclassType type,
+                                             Node subclassNode,
+                                             Node superclassNode)
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.SubclassType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.SubclassType.html new file mode 100644 index 0000000..170c1d0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.SubclassType.html @@ -0,0 +1,337 @@ + + + + + +CodingConvention.SubclassType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum CodingConvention.SubclassType

+
+java.lang.Object
+  extended by java.lang.Enum<CodingConvention.SubclassType>
+      extended by com.google.javascript.jscomp.CodingConvention.SubclassType
+
+
+
All Implemented Interfaces:
Serializable, Comparable<CodingConvention.SubclassType>
+
+
+
Enclosing interface:
CodingConvention
+
+
+
+
public static enum CodingConvention.SubclassType
extends Enum<CodingConvention.SubclassType>
+ + +

+


+ +

+ + + + + + + + + + + + + +
+Enum Constant Summary
INHERITS + +
+           
MIXIN + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static CodingConvention.SubclassTypevalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static CodingConvention.SubclassType[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+INHERITS

+
+public static final CodingConvention.SubclassType INHERITS
+
+
+
+
+
+ +

+MIXIN

+
+public static final CodingConvention.SubclassType MIXIN
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static CodingConvention.SubclassType[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (CodingConvention.SubclassType c : CodingConvention.SubclassType.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static CodingConvention.SubclassType valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.html new file mode 100644 index 0000000..894f5e9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConvention.html @@ -0,0 +1,1083 @@ + + + + + +CodingConvention (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface CodingConvention

+
+
All Superinterfaces:
Serializable
+
+
+
All Known Implementing Classes:
ClosureCodingConvention, CodingConventions.Proxy, GoogleCodingConvention, JqueryCodingConvention
+
+
+
+
public interface CodingConvention
extends Serializable
+ + +

+CodingConvention defines a set of hooks to customize the behavior of the + Compiler for a specific team/company. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Nested Class Summary
+static classCodingConvention.AssertionFunctionSpec + +
+          A function that will throw an exception when either: + -One or more of its parameters evaluate to false.
+static classCodingConvention.Bind + +
+           
+static classCodingConvention.DelegateRelationship + +
+          Delegates provides a mechanism and structure for identifying where classes + can call out to optional code to augment their functionality.
+static classCodingConvention.ObjectLiteralCast + +
+          An object literal cast provides a mechanism to cast object literals to + other types without a warning.
+static classCodingConvention.SubclassRelationship + +
+           
+static classCodingConvention.SubclassType + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidapplyDelegateRelationship(ObjectType delegateSuperclass, + ObjectType delegateBase, + ObjectType delegator, + FunctionType delegateProxy, + FunctionType findDelegate) + +
+          In many JS libraries, the function that creates a delegate relationship + also adds properties to the delegator and delegate base.
+ voidapplySingletonGetter(FunctionType functionType, + FunctionType getterType, + ObjectType objectType) + +
+          In many JS libraries, the function that adds a singleton getter to a class + adds properties to the class.
+ voidapplySubclassRelationship(FunctionType parentCtor, + FunctionType childCtor, + CodingConvention.SubclassType type) + +
+          In many JS libraries, the function that produces inheritance also + adds properties to the superclass and/or subclass.
+ voidcheckForCallingConventionDefiningCalls(Node n, + Map<String,String> delegateCallingConventions) + +
+          Checks for function calls that set the calling conventions on delegate + methods.
+ voiddefineDelegateProxyPrototypeProperties(JSTypeRegistry registry, + Scope scope, + List<ObjectType> delegateProxyPrototypes, + Map<String,String> delegateCallingConventions) + +
+          Defines the delegate proxy prototype properties.
+ CodingConvention.BinddescribeFunctionBind(Node n) + +
+          A Bind instance or null.
+ StringextractClassNameIfProvide(Node node, + Node parent) + +
+          Convenience method for determining provided dependencies amongst different + js scripts.
+ StringextractClassNameIfRequire(Node node, + Node parent) + +
+          Convenience method for determining required dependencies amongst different + js scripts.
+ StringgetAbstractMethodName() + +
+          Function name for abstract methods.
+ Collection<CodingConvention.AssertionFunctionSpec>getAssertionFunctions() + +
+          Returns the set of AssertionFunction.
+ CodingConvention.SubclassRelationshipgetClassesDefinedByCall(Node callNode) + +
+          Checks if the given method defines a subclass relationship, + and if it does, returns information on that relationship.
+ CodingConvention.DelegateRelationshipgetDelegateRelationship(Node callNode) + +
+           
+ StringgetDelegateSuperclassName() + +
+           
+ StringgetExportPropertyFunction() + +
+          Function name used when exporting properties.
+ StringgetExportSymbolFunction() + +
+          Function name used when exporting symbols.
+ StringgetGlobalObject() + +
+          Gets the name of the global object.
+ CodingConvention.ObjectLiteralCastgetObjectLiteralCast(NodeTraversal t, + Node callNode) + +
+          Checks if the given method performs a object literal cast, and if it does, + returns information on the cast.
+ StringgetSingletonGetterClassName(Node callNode) + +
+          Checks if the given method defines a singleton getter, and if it does, + returns the name of the class with the singleton getter.
+ List<String>identifyTypeDeclarationCall(Node n) + +
+          Checks if the given CALL node is forward-declaring any types, + and returns the name of the types if it is.
+ booleanisConstant(String variableName) + +
+          This checks whether a given variable name, such as a name in all-caps + should be treated as if it had the @const annotation.
+ booleanisConstantKey(String keyName) + +
+          This checks whether a given key of an object literal, such as a + name in all-caps should be treated as if it had the @const + annotation.
+ booleanisExported(String name) + +
+          Should be isExported(name, true) || isExported(name, false);
+ booleanisExported(String name, + boolean local) + +
+          Checks whether a global variable or function name should be treated as + exported, or externally referenceable.
+ booleanisOptionalParameter(Node parameter) + +
+          This checks whether a given parameter name should be treated as an + optional parameter as far as type checking or function call arg count + checking is concerned.
+ booleanisPrivate(String name) + +
+          Checks whether a name should be considered private.
+ booleanisPropertyTestFunction(Node call) + +
+          Whether this CALL function is testing for the existence of a property.
+ booleanisPrototypeAlias(Node getProp) + +
+          Whether this GETPROP node is an alias for an object prototype.
+ booleanisSuperClassReference(String propertyName) + +
+          Returns true if passed a string referring to the superclass.
+ booleanisValidEnumKey(String key) + +
+          This checks that a given key may be used as a key for an enum.
+ booleanisVarArgsParameter(Node parameter) + +
+          This checks whether a given parameter should be treated as a marker + for a variable argument list function.
+  +

+ + + + + + + + +
+Method Detail
+ +

+isConstant

+
+boolean isConstant(String variableName)
+
+
This checks whether a given variable name, such as a name in all-caps + should be treated as if it had the @const annotation. +

+

+
+
+
+
Parameters:
variableName - potentially constant variable name +
Returns:
true if the name should be treated as a constant.
+
+
+
+ +

+isConstantKey

+
+boolean isConstantKey(String keyName)
+
+
This checks whether a given key of an object literal, such as a + name in all-caps should be treated as if it had the @const + annotation. +

+

+
+
+
+
+
+
+
+ +

+isValidEnumKey

+
+boolean isValidEnumKey(String key)
+
+
This checks that a given key may be used as a key for an enum. +

+

+
+
+
+
Parameters:
key - the potential key to an enum +
Returns:
true if the key may be used as an enum key, + false otherwise
+
+
+
+ +

+isOptionalParameter

+
+boolean isOptionalParameter(Node parameter)
+
+
This checks whether a given parameter name should be treated as an + optional parameter as far as type checking or function call arg count + checking is concerned. Note that an optional function parameter may be + declared as a simple type and is automatically converted to a union of the + declared type and Undefined. +

+

+
+
+
+
Parameters:
parameter - The parameter's node. +
Returns:
true if the parameter should be treated as an optional + parameter.
+
+
+
+ +

+isVarArgsParameter

+
+boolean isVarArgsParameter(Node parameter)
+
+
This checks whether a given parameter should be treated as a marker + for a variable argument list function. A VarArgs parameter must be the + last parameter in a function declaration. +

+

+
+
+
+
Parameters:
parameter - The parameter's node. +
Returns:
true if the parameter should be treated as a variable + length parameter.
+
+
+
+ +

+isExported

+
+boolean isExported(String name,
+                   boolean local)
+
+
Checks whether a global variable or function name should be treated as + exported, or externally referenceable. +

+

+
+
+
+
Parameters:
name - A global variable or function name.
local - true if the name is a local variable. +
Returns:
true if the name should be considered exported.
+
+
+
+ +

+isExported

+
+boolean isExported(String name)
+
+
Should be isExported(name, true) || isExported(name, false); +

+

+
+
+
+
+
+
+
+ +

+isPrivate

+
+boolean isPrivate(String name)
+
+
Checks whether a name should be considered private. Private global + variables and functions can only be referenced within the source file in + which they are declared. Private properties and methods should only be + accessed by the class that defines them. +

+

+
+
+
+
Parameters:
name - The name of a global variable or function, or a method or + property. +
Returns:
true if the name should be considered private.
+
+
+
+ +

+getClassesDefinedByCall

+
+CodingConvention.SubclassRelationship getClassesDefinedByCall(Node callNode)
+
+
Checks if the given method defines a subclass relationship, + and if it does, returns information on that relationship. By default, + always returns null. Meant to be overridden by subclasses. +

+

+
+
+
+
Parameters:
callNode - A CALL node.
+
+
+
+ +

+isSuperClassReference

+
+boolean isSuperClassReference(String propertyName)
+
+
Returns true if passed a string referring to the superclass. The string + will usually be from the string node at the right of a GETPROP, e.g. + this.superClass_. +

+

+
+
+
+
+
+
+
+ +

+extractClassNameIfProvide

+
+String extractClassNameIfProvide(Node node,
+                                 Node parent)
+
+
Convenience method for determining provided dependencies amongst different + js scripts. +

+

+
+
+
+
+
+
+
+ +

+extractClassNameIfRequire

+
+String extractClassNameIfRequire(Node node,
+                                 Node parent)
+
+
Convenience method for determining required dependencies amongst different + js scripts. +

+

+
+
+
+
+
+
+
+ +

+getExportPropertyFunction

+
+String getExportPropertyFunction()
+
+
Function name used when exporting properties. + Signature: fn(object, publicName, symbol). +

+

+
+
+
+ +
Returns:
function name.
+
+
+
+ +

+getExportSymbolFunction

+
+String getExportSymbolFunction()
+
+
Function name used when exporting symbols. + Signature: fn(publicPath, object). +

+

+
+
+
+ +
Returns:
function name.
+
+
+
+ +

+identifyTypeDeclarationCall

+
+List<String> identifyTypeDeclarationCall(Node n)
+
+
Checks if the given CALL node is forward-declaring any types, + and returns the name of the types if it is. +

+

+
+
+
+
+
+
+
+ +

+applySubclassRelationship

+
+void applySubclassRelationship(FunctionType parentCtor,
+                               FunctionType childCtor,
+                               CodingConvention.SubclassType type)
+
+
In many JS libraries, the function that produces inheritance also + adds properties to the superclass and/or subclass. +

+

+
+
+
+
+
+
+
+ +

+getAbstractMethodName

+
+String getAbstractMethodName()
+
+
Function name for abstract methods. An abstract method can be assigned to + an interface method instead of an function expression in order to avoid + linter warnings produced by assigning a function without a return value + where a return value is expected. +

+

+
+
+
+ +
Returns:
function name.
+
+
+
+ +

+getSingletonGetterClassName

+
+String getSingletonGetterClassName(Node callNode)
+
+
Checks if the given method defines a singleton getter, and if it does, + returns the name of the class with the singleton getter. By default, always + returns null. Meant to be overridden by subclasses. +

+

+
+
+
+
Parameters:
callNode - A CALL node.
+
+
+
+ +

+applySingletonGetter

+
+void applySingletonGetter(FunctionType functionType,
+                          FunctionType getterType,
+                          ObjectType objectType)
+
+
In many JS libraries, the function that adds a singleton getter to a class + adds properties to the class. +

+

+
+
+
+
+
+
+
+ +

+getDelegateRelationship

+
+CodingConvention.DelegateRelationship getDelegateRelationship(Node callNode)
+
+
+
+
+
+
+
+
+
+ +

+applyDelegateRelationship

+
+void applyDelegateRelationship(ObjectType delegateSuperclass,
+                               ObjectType delegateBase,
+                               ObjectType delegator,
+                               FunctionType delegateProxy,
+                               FunctionType findDelegate)
+
+
In many JS libraries, the function that creates a delegate relationship + also adds properties to the delegator and delegate base. +

+

+
+
+
+
+
+
+
+ +

+getDelegateSuperclassName

+
+String getDelegateSuperclassName()
+
+
+
+
+
+ +
Returns:
the name of the delegate superclass.
+
+
+
+ +

+checkForCallingConventionDefiningCalls

+
+void checkForCallingConventionDefiningCalls(Node n,
+                                            Map<String,String> delegateCallingConventions)
+
+
Checks for function calls that set the calling conventions on delegate + methods. +

+

+
+
+
+
+
+
+
+ +

+defineDelegateProxyPrototypeProperties

+
+void defineDelegateProxyPrototypeProperties(JSTypeRegistry registry,
+                                            Scope scope,
+                                            List<ObjectType> delegateProxyPrototypes,
+                                            Map<String,String> delegateCallingConventions)
+
+
Defines the delegate proxy prototype properties. Their types depend on + properties of the delegate base methods. +

+

+
+
+
+
Parameters:
delegateProxyPrototypes - List of delegate proxy prototypes.
+
+
+
+ +

+getGlobalObject

+
+String getGlobalObject()
+
+
Gets the name of the global object. +

+

+
+
+
+
+
+
+
+ +

+describeFunctionBind

+
+CodingConvention.Bind describeFunctionBind(Node n)
+
+
A Bind instance or null. +

+

+
+
+
+
+
+
+
+ +

+isPropertyTestFunction

+
+boolean isPropertyTestFunction(Node call)
+
+
Whether this CALL function is testing for the existence of a property. +

+

+
+
+
+
+
+
+
+ +

+isPrototypeAlias

+
+boolean isPrototypeAlias(Node getProp)
+
+
Whether this GETPROP node is an alias for an object prototype. +

+

+
+
+
+
+
+
+
+ +

+getObjectLiteralCast

+
+CodingConvention.ObjectLiteralCast getObjectLiteralCast(NodeTraversal t,
+                                                        Node callNode)
+
+
Checks if the given method performs a object literal cast, and if it does, + returns information on the cast. By default, always returns null. Meant + to be overridden by subclasses. +

+

+
+
+
+
Parameters:
t - The node traversal.
callNode - A CALL node.
+
+
+
+ +

+getAssertionFunctions

+
+Collection<CodingConvention.AssertionFunctionSpec> getAssertionFunctions()
+
+
Returns the set of AssertionFunction. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConventions.Proxy.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConventions.Proxy.html new file mode 100644 index 0000000..cfb94c8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConventions.Proxy.html @@ -0,0 +1,1165 @@ + + + + + +CodingConventions.Proxy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class CodingConventions.Proxy

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CodingConventions.Proxy
+
+
+
All Implemented Interfaces:
CodingConvention, Serializable
+
+
+
Direct Known Subclasses:
ClosureCodingConvention, GoogleCodingConvention, JqueryCodingConvention
+
+
+
Enclosing class:
CodingConventions
+
+
+
+
public static class CodingConventions.Proxy
extends Object
implements CodingConvention
+ + +

+A convention that wraps another. + + When you want to support a new library, you should subclass this + delegate, and override the methods that you want to customize. + + This way, a person using jQuery and Closure Library can create a new + coding convention by creating a jQueryCodingConvention that delegates + to a ClosureCodingConvention that delegates to a DefaultCodingConvention. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from interface com.google.javascript.jscomp.CodingConvention
CodingConvention.AssertionFunctionSpec, CodingConvention.Bind, CodingConvention.DelegateRelationship, CodingConvention.ObjectLiteralCast, CodingConvention.SubclassRelationship, CodingConvention.SubclassType
+  + + + + + + + + + + + +
+Field Summary
+protected  CodingConventionnextConvention + +
+           
+  + + + + + + + + + + + +
+Constructor Summary
+protected CodingConventions.Proxy(CodingConvention convention) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidapplyDelegateRelationship(ObjectType delegateSuperclass, + ObjectType delegateBase, + ObjectType delegator, + FunctionType delegateProxy, + FunctionType findDelegate) + +
+          In many JS libraries, the function that creates a delegate relationship + also adds properties to the delegator and delegate base.
+ voidapplySingletonGetter(FunctionType functionType, + FunctionType getterType, + ObjectType objectType) + +
+          In many JS libraries, the function that adds a singleton getter to a class + adds properties to the class.
+ voidapplySubclassRelationship(FunctionType parentCtor, + FunctionType childCtor, + CodingConvention.SubclassType type) + +
+          In many JS libraries, the function that produces inheritance also + adds properties to the superclass and/or subclass.
+ voidcheckForCallingConventionDefiningCalls(Node n, + Map<String,String> delegateCallingConventions) + +
+          Checks for function calls that set the calling conventions on delegate + methods.
+ voiddefineDelegateProxyPrototypeProperties(JSTypeRegistry registry, + Scope scope, + List<ObjectType> delegateProxyPrototypes, + Map<String,String> delegateCallingConventions) + +
+          Defines the delegate proxy prototype properties.
+ CodingConvention.BinddescribeFunctionBind(Node n) + +
+          A Bind instance or null.
+ StringextractClassNameIfProvide(Node node, + Node parent) + +
+          Convenience method for determining provided dependencies amongst different + js scripts.
+ StringextractClassNameIfRequire(Node node, + Node parent) + +
+          Convenience method for determining required dependencies amongst different + js scripts.
+ StringgetAbstractMethodName() + +
+          Function name for abstract methods.
+ Collection<CodingConvention.AssertionFunctionSpec>getAssertionFunctions() + +
+          Returns the set of AssertionFunction.
+ CodingConvention.SubclassRelationshipgetClassesDefinedByCall(Node callNode) + +
+          Checks if the given method defines a subclass relationship, + and if it does, returns information on that relationship.
+ CodingConvention.DelegateRelationshipgetDelegateRelationship(Node callNode) + +
+           
+ StringgetDelegateSuperclassName() + +
+           
+ StringgetExportPropertyFunction() + +
+          Function name used when exporting properties.
+ StringgetExportSymbolFunction() + +
+          Function name used when exporting symbols.
+ StringgetGlobalObject() + +
+          Gets the name of the global object.
+ CodingConvention.ObjectLiteralCastgetObjectLiteralCast(NodeTraversal t, + Node callNode) + +
+          Checks if the given method performs a object literal cast, and if it does, + returns information on the cast.
+ StringgetSingletonGetterClassName(Node callNode) + +
+          Checks if the given method defines a singleton getter, and if it does, + returns the name of the class with the singleton getter.
+ List<String>identifyTypeDeclarationCall(Node n) + +
+          Checks if the given CALL node is forward-declaring any types, + and returns the name of the types if it is.
+ booleanisConstant(String variableName) + +
+          This checks whether a given variable name, such as a name in all-caps + should be treated as if it had the @const annotation.
+ booleanisConstantKey(String keyName) + +
+          This checks whether a given key of an object literal, such as a + name in all-caps should be treated as if it had the @const + annotation.
+ booleanisExported(String name) + +
+          Should be isExported(name, true) || isExported(name, false);
+ booleanisExported(String name, + boolean local) + +
+          Checks whether a global variable or function name should be treated as + exported, or externally referenceable.
+ booleanisOptionalParameter(Node parameter) + +
+          This checks whether a given parameter name should be treated as an + optional parameter as far as type checking or function call arg count + checking is concerned.
+ booleanisPrivate(String name) + +
+          Checks whether a name should be considered private.
+ booleanisPropertyTestFunction(Node call) + +
+          Whether this CALL function is testing for the existence of a property.
+ booleanisPrototypeAlias(Node getProp) + +
+          Whether this GETPROP node is an alias for an object prototype.
+ booleanisSuperClassReference(String propertyName) + +
+          Returns true if passed a string referring to the superclass.
+ booleanisValidEnumKey(String key) + +
+          This checks that a given key may be used as a key for an enum.
+ booleanisVarArgsParameter(Node parameter) + +
+          This checks whether a given parameter should be treated as a marker + for a variable argument list function.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+nextConvention

+
+protected final CodingConvention nextConvention
+
+
+
+
+ + + + + + + + +
+Constructor Detail
+ +

+CodingConventions.Proxy

+
+protected CodingConventions.Proxy(CodingConvention convention)
+
+
+ + + + + + + + +
+Method Detail
+ +

+isConstant

+
+public boolean isConstant(String variableName)
+
+
Description copied from interface: CodingConvention
+
This checks whether a given variable name, such as a name in all-caps + should be treated as if it had the @const annotation. +

+

+
Specified by:
isConstant in interface CodingConvention
+
+
+
Parameters:
variableName - potentially constant variable name +
Returns:
true if the name should be treated as a constant.
+
+
+
+ +

+isConstantKey

+
+public boolean isConstantKey(String keyName)
+
+
Description copied from interface: CodingConvention
+
This checks whether a given key of an object literal, such as a + name in all-caps should be treated as if it had the @const + annotation. +

+

+
Specified by:
isConstantKey in interface CodingConvention
+
+
+
+
+
+
+ +

+isValidEnumKey

+
+public boolean isValidEnumKey(String key)
+
+
Description copied from interface: CodingConvention
+
This checks that a given key may be used as a key for an enum. +

+

+
Specified by:
isValidEnumKey in interface CodingConvention
+
+
+
Parameters:
key - the potential key to an enum +
Returns:
true if the key may be used as an enum key, + false otherwise
+
+
+
+ +

+isOptionalParameter

+
+public boolean isOptionalParameter(Node parameter)
+
+
Description copied from interface: CodingConvention
+
This checks whether a given parameter name should be treated as an + optional parameter as far as type checking or function call arg count + checking is concerned. Note that an optional function parameter may be + declared as a simple type and is automatically converted to a union of the + declared type and Undefined. +

+

+
Specified by:
isOptionalParameter in interface CodingConvention
+
+
+
Parameters:
parameter - The parameter's node. +
Returns:
true if the parameter should be treated as an optional + parameter.
+
+
+
+ +

+isVarArgsParameter

+
+public boolean isVarArgsParameter(Node parameter)
+
+
Description copied from interface: CodingConvention
+
This checks whether a given parameter should be treated as a marker + for a variable argument list function. A VarArgs parameter must be the + last parameter in a function declaration. +

+

+
Specified by:
isVarArgsParameter in interface CodingConvention
+
+
+
Parameters:
parameter - The parameter's node. +
Returns:
true if the parameter should be treated as a variable + length parameter.
+
+
+
+ +

+isExported

+
+public boolean isExported(String name,
+                          boolean local)
+
+
Description copied from interface: CodingConvention
+
Checks whether a global variable or function name should be treated as + exported, or externally referenceable. +

+

+
Specified by:
isExported in interface CodingConvention
+
+
+
Parameters:
name - A global variable or function name.
local - true if the name is a local variable. +
Returns:
true if the name should be considered exported.
+
+
+
+ +

+isExported

+
+public final boolean isExported(String name)
+
+
Description copied from interface: CodingConvention
+
Should be isExported(name, true) || isExported(name, false); +

+

+
Specified by:
isExported in interface CodingConvention
+
+
+
+
+
+
+ +

+isPrivate

+
+public boolean isPrivate(String name)
+
+
Description copied from interface: CodingConvention
+
Checks whether a name should be considered private. Private global + variables and functions can only be referenced within the source file in + which they are declared. Private properties and methods should only be + accessed by the class that defines them. +

+

+
Specified by:
isPrivate in interface CodingConvention
+
+
+
Parameters:
name - The name of a global variable or function, or a method or + property. +
Returns:
true if the name should be considered private.
+
+
+
+ +

+getClassesDefinedByCall

+
+public CodingConvention.SubclassRelationship getClassesDefinedByCall(Node callNode)
+
+
Description copied from interface: CodingConvention
+
Checks if the given method defines a subclass relationship, + and if it does, returns information on that relationship. By default, + always returns null. Meant to be overridden by subclasses. +

+

+
Specified by:
getClassesDefinedByCall in interface CodingConvention
+
+
+
Parameters:
callNode - A CALL node.
+
+
+
+ +

+isSuperClassReference

+
+public boolean isSuperClassReference(String propertyName)
+
+
Description copied from interface: CodingConvention
+
Returns true if passed a string referring to the superclass. The string + will usually be from the string node at the right of a GETPROP, e.g. + this.superClass_. +

+

+
Specified by:
isSuperClassReference in interface CodingConvention
+
+
+
+
+
+
+ +

+extractClassNameIfProvide

+
+public String extractClassNameIfProvide(Node node,
+                                        Node parent)
+
+
Description copied from interface: CodingConvention
+
Convenience method for determining provided dependencies amongst different + js scripts. +

+

+
Specified by:
extractClassNameIfProvide in interface CodingConvention
+
+
+
+
+
+
+ +

+extractClassNameIfRequire

+
+public String extractClassNameIfRequire(Node node,
+                                        Node parent)
+
+
Description copied from interface: CodingConvention
+
Convenience method for determining required dependencies amongst different + js scripts. +

+

+
Specified by:
extractClassNameIfRequire in interface CodingConvention
+
+
+
+
+
+
+ +

+getExportPropertyFunction

+
+public String getExportPropertyFunction()
+
+
Description copied from interface: CodingConvention
+
Function name used when exporting properties. + Signature: fn(object, publicName, symbol). +

+

+
Specified by:
getExportPropertyFunction in interface CodingConvention
+
+
+ +
Returns:
function name.
+
+
+
+ +

+getExportSymbolFunction

+
+public String getExportSymbolFunction()
+
+
Description copied from interface: CodingConvention
+
Function name used when exporting symbols. + Signature: fn(publicPath, object). +

+

+
Specified by:
getExportSymbolFunction in interface CodingConvention
+
+
+ +
Returns:
function name.
+
+
+
+ +

+identifyTypeDeclarationCall

+
+public List<String> identifyTypeDeclarationCall(Node n)
+
+
Description copied from interface: CodingConvention
+
Checks if the given CALL node is forward-declaring any types, + and returns the name of the types if it is. +

+

+
Specified by:
identifyTypeDeclarationCall in interface CodingConvention
+
+
+
+
+
+
+ +

+applySubclassRelationship

+
+public void applySubclassRelationship(FunctionType parentCtor,
+                                      FunctionType childCtor,
+                                      CodingConvention.SubclassType type)
+
+
Description copied from interface: CodingConvention
+
In many JS libraries, the function that produces inheritance also + adds properties to the superclass and/or subclass. +

+

+
Specified by:
applySubclassRelationship in interface CodingConvention
+
+
+
+
+
+
+ +

+getAbstractMethodName

+
+public String getAbstractMethodName()
+
+
Description copied from interface: CodingConvention
+
Function name for abstract methods. An abstract method can be assigned to + an interface method instead of an function expression in order to avoid + linter warnings produced by assigning a function without a return value + where a return value is expected. +

+

+
Specified by:
getAbstractMethodName in interface CodingConvention
+
+
+ +
Returns:
function name.
+
+
+
+ +

+getSingletonGetterClassName

+
+public String getSingletonGetterClassName(Node callNode)
+
+
Description copied from interface: CodingConvention
+
Checks if the given method defines a singleton getter, and if it does, + returns the name of the class with the singleton getter. By default, always + returns null. Meant to be overridden by subclasses. +

+

+
Specified by:
getSingletonGetterClassName in interface CodingConvention
+
+
+
Parameters:
callNode - A CALL node.
+
+
+
+ +

+applySingletonGetter

+
+public void applySingletonGetter(FunctionType functionType,
+                                 FunctionType getterType,
+                                 ObjectType objectType)
+
+
Description copied from interface: CodingConvention
+
In many JS libraries, the function that adds a singleton getter to a class + adds properties to the class. +

+

+
Specified by:
applySingletonGetter in interface CodingConvention
+
+
+
+
+
+
+ +

+getDelegateRelationship

+
+public CodingConvention.DelegateRelationship getDelegateRelationship(Node callNode)
+
+
+
Specified by:
getDelegateRelationship in interface CodingConvention
+
+
+
+
+
+
+ +

+applyDelegateRelationship

+
+public void applyDelegateRelationship(ObjectType delegateSuperclass,
+                                      ObjectType delegateBase,
+                                      ObjectType delegator,
+                                      FunctionType delegateProxy,
+                                      FunctionType findDelegate)
+
+
Description copied from interface: CodingConvention
+
In many JS libraries, the function that creates a delegate relationship + also adds properties to the delegator and delegate base. +

+

+
Specified by:
applyDelegateRelationship in interface CodingConvention
+
+
+
+
+
+
+ +

+getDelegateSuperclassName

+
+public String getDelegateSuperclassName()
+
+
+
Specified by:
getDelegateSuperclassName in interface CodingConvention
+
+
+ +
Returns:
the name of the delegate superclass.
+
+
+
+ +

+checkForCallingConventionDefiningCalls

+
+public void checkForCallingConventionDefiningCalls(Node n,
+                                                   Map<String,String> delegateCallingConventions)
+
+
Description copied from interface: CodingConvention
+
Checks for function calls that set the calling conventions on delegate + methods. +

+

+
Specified by:
checkForCallingConventionDefiningCalls in interface CodingConvention
+
+
+
+
+
+
+ +

+defineDelegateProxyPrototypeProperties

+
+public void defineDelegateProxyPrototypeProperties(JSTypeRegistry registry,
+                                                   Scope scope,
+                                                   List<ObjectType> delegateProxyPrototypes,
+                                                   Map<String,String> delegateCallingConventions)
+
+
Description copied from interface: CodingConvention
+
Defines the delegate proxy prototype properties. Their types depend on + properties of the delegate base methods. +

+

+
Specified by:
defineDelegateProxyPrototypeProperties in interface CodingConvention
+
+
+
delegateProxyPrototypes - List of delegate proxy prototypes.
+
+
+
+ +

+getGlobalObject

+
+public String getGlobalObject()
+
+
Description copied from interface: CodingConvention
+
Gets the name of the global object. +

+

+
Specified by:
getGlobalObject in interface CodingConvention
+
+
+
+
+
+
+ +

+getAssertionFunctions

+
+public Collection<CodingConvention.AssertionFunctionSpec> getAssertionFunctions()
+
+
Description copied from interface: CodingConvention
+
Returns the set of AssertionFunction. +

+

+
Specified by:
getAssertionFunctions in interface CodingConvention
+
+
+
+
+
+
+ +

+describeFunctionBind

+
+public CodingConvention.Bind describeFunctionBind(Node n)
+
+
Description copied from interface: CodingConvention
+
A Bind instance or null. +

+

+
Specified by:
describeFunctionBind in interface CodingConvention
+
+
+
+
+
+
+ +

+isPropertyTestFunction

+
+public boolean isPropertyTestFunction(Node call)
+
+
Description copied from interface: CodingConvention
+
Whether this CALL function is testing for the existence of a property. +

+

+
Specified by:
isPropertyTestFunction in interface CodingConvention
+
+
+
+
+
+
+ +

+isPrototypeAlias

+
+public boolean isPrototypeAlias(Node getProp)
+
+
Description copied from interface: CodingConvention
+
Whether this GETPROP node is an alias for an object prototype. +

+

+
Specified by:
isPrototypeAlias in interface CodingConvention
+
+
+
+
+
+
+ +

+getObjectLiteralCast

+
+public CodingConvention.ObjectLiteralCast getObjectLiteralCast(NodeTraversal t,
+                                                               Node callNode)
+
+
Description copied from interface: CodingConvention
+
Checks if the given method performs a object literal cast, and if it does, + returns information on the cast. By default, always returns null. Meant + to be overridden by subclasses. +

+

+
Specified by:
getObjectLiteralCast in interface CodingConvention
+
+
+
Parameters:
t - The node traversal.
callNode - A CALL node.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConventions.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConventions.html new file mode 100644 index 0000000..5769a2e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CodingConventions.html @@ -0,0 +1,254 @@ + + + + + +CodingConventions (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class CodingConventions

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CodingConventions
+
+
+
+
public class CodingConventions
extends Object
+ + +

+Helper classes for dealing with coding conventions. +

+ +

+


+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classCodingConventions.Proxy + +
+          A convention that wraps another.
+  + + + + + + + + + + + +
+Method Summary
+static CodingConventiongetDefault() + +
+          Gets the default coding convention.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getDefault

+
+public static CodingConvention getDefault()
+
+
Gets the default coding convention. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CommandLineRunner.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CommandLineRunner.html new file mode 100644 index 0000000..d1cf234 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CommandLineRunner.html @@ -0,0 +1,766 @@ + + + + + +CommandLineRunner (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class CommandLineRunner

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CommandLineRunner
+
+
+
+
public class CommandLineRunner
extends Object
+ + +

+CommandLineRunner translates flags into Java API calls on the Compiler. + + This class may be extended and used to create other Java classes + that behave the same as running the Compiler from the command line. If you + want to run the compiler in-process in Java, you should look at this class + for hints on what API calls to make, but you should not use this class + directly. + + Example: +

+ class MyCommandLineRunner extends CommandLineRunner {
+   MyCommandLineRunner(String[] args) {
+     super(args);
+   }
+
+   @Override protected CompilerOptions createOptions() {
+     CompilerOptions options = super.createOptions();
+     addMyCrazyCompilerPassThatOutputsAnExtraFile(options);
+     return options;
+   }
+
+   public static void main(String[] args) {
+     MyCommandLineRunner runner = new MyCommandLineRunner(args);
+     if (runner.shouldRunCompiler()) {
+       runner.run();
+     } else {
+       System.exit(-1);
+     }
+   }
+ }
+ 
+ + This class is totally not thread-safe. +

+ +

+


+ +

+ + + + + + + + + + + + + + + +
+Nested Class Summary
+static classAbstractCommandLineRunner.FlagUsageException + +
+          An exception thrown when command-line flags are used incorrectly.
+protected static classAbstractCommandLineRunner.WarningGuardSpec + +
+          A little helper class to make it easier to collect warning types + from --jscomp_error, --jscomp_warning, and --jscomp_off.
+  + + + + + + + + + + + + + + + +
+Constructor Summary
+protected CommandLineRunner(String[] args) + +
+          Create a new command-line runner.
+protected CommandLineRunner(String[] args, + PrintStream out, + PrintStream err) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+protected  voidcheckModuleName(String name) + +
+          Validates the module name.
+protected  CompilercreateCompiler() + +
+          Returns the instance of the Compiler to use when run() is + called.
+protected  List<SourceFile>createExterns() + +
+           
+protected  List<SourceFile>createInputs(List<String> files, + boolean allowStdIn) + +
+          Creates inputs from a list of files.
+protected  CompilerOptionscreateOptions() + +
+          Returns the instance of the Options to use when run() is called.
+protected  intdoRun() + +
+          Parses command-line arguments and runs the compiler.
+protected  OutputStreamfilenameToOutputStream(String fileName) + +
+          Converts a file name into a Ouputstream.
+protected  com.google.javascript.jscomp.AbstractCommandLineRunner.CommandLineConfiggetCommandLineConfig() + +
+          Get the command line config, so that it can be initialized.
+protected  AgetCompiler() + +
+           
+static List<SourceFile>getDefaultExterns() + +
+           
+protected  DiagnosticGroupsgetDiagnosticGroups() + +
+          The warning classes that are available from the command-line.
+protected  PrintStreamgetErrorPrintStream() + +
+          Returns the PrintStream for writing errors associated with this + AbstractCommandLineRunner.
+protected  voidinitOptionsFromFlags(CompilerOptions options) + +
+          Deprecated. 
+protected  booleanisInTestMode() + +
+          Returns whether we're in test mode.
+static voidmain(String[] args) + +
+          Runs the Compiler.
+ voidrun() + +
+          Runs the Compiler and calls System.exit() with the exit status of the + compiler.
+protected  voidsetRunOptions(CompilerOptions options) + +
+          Sets options based on the configurations set flags API.
+ booleanshouldRunCompiler() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+CommandLineRunner

+
+protected CommandLineRunner(String[] args)
+
+
Create a new command-line runner. You should only need to call + the constructor if you're extending this class. Otherwise, the main + method should instantiate it. +

+

+
+ +

+CommandLineRunner

+
+protected CommandLineRunner(String[] args,
+                            PrintStream out,
+                            PrintStream err)
+
+
+ + + + + + + + +
+Method Detail
+ +

+createOptions

+
+protected CompilerOptions createOptions()
+
+
Returns the instance of the Options to use when run() is called. + createCompiler() is called before createOptions(), so getCompiler() + will not return null when createOptions() is called. +

+

+
+
+
+
+
+
+
+ +

+createCompiler

+
+protected Compiler createCompiler()
+
+
Returns the instance of the Compiler to use when run() is + called. +

+

+
+
+
+
+
+
+
+ +

+createExterns

+
+protected List<SourceFile> createExterns()
+                                  throws AbstractCommandLineRunner.FlagUsageException,
+                                         IOException
+
+
+
+
+
+ +
Throws: +
AbstractCommandLineRunner.FlagUsageException +
IOException
+
+
+
+ +

+getDefaultExterns

+
+public static List<SourceFile> getDefaultExterns()
+                                          throws IOException
+
+
+ +
Returns:
a mutable list +
Throws: +
IOException
+
+
+
+ +

+shouldRunCompiler

+
+public boolean shouldRunCompiler()
+
+
+ +
Returns:
Whether the configuration is valid.
+
+
+
+ +

+main

+
+public static void main(String[] args)
+
+
Runs the Compiler. Exits cleanly in the event of an error. +

+

+
+
+
+
+ +

+isInTestMode

+
+protected boolean isInTestMode()
+
+
Returns whether we're in test mode. +

+

+
+
+
+
+ +

+getCommandLineConfig

+
+protected com.google.javascript.jscomp.AbstractCommandLineRunner.CommandLineConfig getCommandLineConfig()
+
+
Get the command line config, so that it can be initialized. +

+

+
+
+
+
+ +

+getDiagnosticGroups

+
+protected DiagnosticGroups getDiagnosticGroups()
+
+
The warning classes that are available from the command-line. +

+

+
+
+
+
+ +

+initOptionsFromFlags

+
+@Deprecated
+protected void initOptionsFromFlags(CompilerOptions options)
+
+
Deprecated.  +

+

No longer does anything. +

+

+
+
+
+
+ +

+setRunOptions

+
+protected void setRunOptions(CompilerOptions options)
+                      throws AbstractCommandLineRunner.FlagUsageException,
+                             IOException
+
+
Sets options based on the configurations set flags API. + Called during the run() run() method. + If you want to ignore the flags API, or intepret flags your own way, + then you should override this method. +

+

+ +
Throws: +
AbstractCommandLineRunner.FlagUsageException +
IOException
+
+
+
+ +

+getCompiler

+
+protected final A getCompiler()
+
+
+
+
+
+
+ +

+run

+
+public final void run()
+
+
Runs the Compiler and calls System.exit() with the exit status of the + compiler. +

+

+
+
+
+
+ +

+getErrorPrintStream

+
+protected PrintStream getErrorPrintStream()
+
+
Returns the PrintStream for writing errors associated with this + AbstractCommandLineRunner. +

+

+
+
+
+
+ +

+createInputs

+
+protected List<SourceFile> createInputs(List<String> files,
+                                        boolean allowStdIn)
+                                 throws AbstractCommandLineRunner.FlagUsageException,
+                                        IOException
+
+
Creates inputs from a list of files. + + Can be overridden by subclasses who want to pull files from different + places. +

+

+
Parameters:
files - A list of filenames
allowStdIn - Whether '-' is allowed appear as a filename to represent + stdin. If true, '-' is only allowed to appear once. +
Returns:
An array of inputs +
Throws: +
AbstractCommandLineRunner.FlagUsageException +
IOException
+
+
+
+ +

+checkModuleName

+
+protected void checkModuleName(String name)
+                        throws AbstractCommandLineRunner.FlagUsageException
+
+
Validates the module name. Can be overridden by subclasses. +

+

+
Parameters:
name - The module name +
Throws: +
AbstractCommandLineRunner.FlagUsageException - if the validation fails
+
+
+
+ +

+doRun

+
+protected int doRun()
+             throws AbstractCommandLineRunner.FlagUsageException,
+                    IOException
+
+
Parses command-line arguments and runs the compiler. +

+

+ +
Returns:
system exit status +
Throws: +
AbstractCommandLineRunner.FlagUsageException +
IOException
+
+
+
+ +

+filenameToOutputStream

+
+protected OutputStream filenameToOutputStream(String fileName)
+                                       throws IOException
+
+
Converts a file name into a Ouputstream. + Returns null if the file name is null. +

+

+ +
Throws: +
IOException
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilationLevel.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilationLevel.html new file mode 100644 index 0000000..6b188eb --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilationLevel.html @@ -0,0 +1,407 @@ + + + + + +CompilationLevel (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum CompilationLevel

+
+java.lang.Object
+  extended by java.lang.Enum<CompilationLevel>
+      extended by com.google.javascript.jscomp.CompilationLevel
+
+
+
All Implemented Interfaces:
Serializable, Comparable<CompilationLevel>
+
+
+
+
public enum CompilationLevel
extends Enum<CompilationLevel>
+ + +

+A CompilationLevel represents the level of optimization that should be + applied when compiling JavaScript code. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Enum Constant Summary
ADVANCED_OPTIMIZATIONS + +
+          ADVANCED_OPTIMIZATIONS aggressively reduces code size by renaming function + names and variables, removing code which is never called, etc.
SIMPLE_OPTIMIZATIONS + +
+          SIMPLE_OPTIMIZATIONS performs transformations to the input JS that do not + require any changes to JS that depend on the input JS.
WHITESPACE_ONLY + +
+          WHITESPACE_ONLY removes comments and extra whitespace in the input JS.
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidsetDebugOptionsForCompilationLevel(CompilerOptions options) + +
+           
+ voidsetOptionsForCompilationLevel(CompilerOptions options) + +
+           
+static CompilationLevelvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static CompilationLevel[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+WHITESPACE_ONLY

+
+public static final CompilationLevel WHITESPACE_ONLY
+
+
WHITESPACE_ONLY removes comments and extra whitespace in the input JS. +

+

+
+
+
+ +

+SIMPLE_OPTIMIZATIONS

+
+public static final CompilationLevel SIMPLE_OPTIMIZATIONS
+
+
SIMPLE_OPTIMIZATIONS performs transformations to the input JS that do not + require any changes to JS that depend on the input JS. For example, + function arguments are renamed (which should not matter to code that + depends on the input JS), but functions themselves are not renamed (which + would otherwise require external code to change to use the renamed function + names). +

+

+
+
+
+ +

+ADVANCED_OPTIMIZATIONS

+
+public static final CompilationLevel ADVANCED_OPTIMIZATIONS
+
+
ADVANCED_OPTIMIZATIONS aggressively reduces code size by renaming function + names and variables, removing code which is never called, etc. +

+

+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static CompilationLevel[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (CompilationLevel c : CompilationLevel.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static CompilationLevel valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+
+ +

+setOptionsForCompilationLevel

+
+public void setOptionsForCompilationLevel(CompilerOptions options)
+
+
+
+
+
+
+ +

+setDebugOptionsForCompilationLevel

+
+public void setDebugOptionsForCompilationLevel(CompilerOptions options)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Compiler.CodeBuilder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Compiler.CodeBuilder.html new file mode 100644 index 0000000..ab27707 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Compiler.CodeBuilder.html @@ -0,0 +1,298 @@ + + + + + +Compiler.CodeBuilder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class Compiler.CodeBuilder

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.Compiler.CodeBuilder
+
+
+
Enclosing class:
Compiler
+
+
+
+
public static class Compiler.CodeBuilder
extends Object
+ + +

+Stores a buffer of text to which more can be appended. This is just like a + StringBuilder except that we also track the number of lines. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
Compiler.CodeBuilder() + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+ intgetLength() + +
+          Returns the length of the text buffer.
+ StringtoString() + +
+          Returns all text in the text buffer.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+Compiler.CodeBuilder

+
+public Compiler.CodeBuilder()
+
+
+ + + + + + + + +
+Method Detail
+ +

+toString

+
+public String toString()
+
+
Returns all text in the text buffer. +

+

+
Overrides:
toString in class Object
+
+
+
+
+
+
+ +

+getLength

+
+public int getLength()
+
+
Returns the length of the text buffer. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Compiler.IntermediateState.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Compiler.IntermediateState.html new file mode 100644 index 0000000..cc49129 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Compiler.IntermediateState.html @@ -0,0 +1,223 @@ + + + + + +Compiler.IntermediateState (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class Compiler.IntermediateState

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.Compiler.IntermediateState
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
Enclosing class:
Compiler
+
+
+
+
public static class Compiler.IntermediateState
extends Object
implements Serializable
+ + +

+Stores the internal compiler state just before optimization is performed. + This can be saved and restored in order to efficiently optimize multiple + different output targets without having to perform checking multiple times. + + NOTE: This does not include all parts of the compiler's internal state. In + particular, SourceFiles and CompilerOptions are not recorded. In + order to recreate a Compiler instance from scratch, you would need to + call init with the same arguments as in the initial creation before + restoring intermediate state. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ +


+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Compiler.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Compiler.html new file mode 100644 index 0000000..8651c5d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Compiler.html @@ -0,0 +1,1986 @@ + + + + + +Compiler (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class Compiler

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.AbstractCompiler
+      extended by com.google.javascript.jscomp.Compiler
+
+
+
All Implemented Interfaces:
SourceExcerptProvider
+
+
+
+
public class Compiler
extends AbstractCompiler
+ + +

+Compiler (and the other classes in this package) does the following: +

    +
  • parses JS code +
  • checks for undefined variables +
  • performs optimizations such as constant folding and constants inlining +
  • renames variables (to short names) +
  • outputs compact javascript code +
+ + External variables are declared in 'externs' files. For instance, the file + may include definitions for global javascript/browser objects such as + window, document. +

+ +

+


+ +

+ + + + + + + + + + + + + + + +
+Nested Class Summary
+static classCompiler.CodeBuilder + +
+          Stores a buffer of text to which more can be appended.
+static classCompiler.IntermediateState + +
+          Stores the internal compiler state just before optimization is performed.
+ + + + + + + +
Nested classes/interfaces inherited from interface com.google.javascript.jscomp.SourceExcerptProvider
SourceExcerptProvider.ExcerptFormatter, SourceExcerptProvider.SourceExcerpt
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Field Summary
+static DiagnosticTypeMOTION_ITERATIONS_ERROR + +
+           
+static DiagnosticTypeOPTIMIZE_LOOP_ERROR + +
+          Error strings used for reporting JSErrors
+protected  CodeChangeHandler.RecentChangerecentChange + +
+           
+ PerformanceTrackertracker + +
+           
+  + + + + + + + + + + + + + + + + +
+Constructor Summary
Compiler() + +
+          Creates a Compiler that reports errors and warnings to its logger.
Compiler(ErrorManager errorManager) + +
+          Creates a Compiler that uses a custom error manager.
Compiler(PrintStream stream) + +
+          Creates n Compiler that reports errors and warnings to an output + stream.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleanacceptConstKeyword() + +
+           
+ booleanacceptEcmaScript5() + +
+           
+ voidaddNewScript(JsAst ast) + +
+          Adds a new Script AST to the compile state.
+ SymbolTablebuildKnownSymbolTable() + +
+           
+ voidcheck() + +
+           
+ Resultcompile(JSSourceFile[] externs, + JSModule[] modules, + CompilerOptions options) + +
+          Compiles a list of modules.
+ Resultcompile(JSSourceFile[] externs, + JSSourceFile[] inputs, + CompilerOptions options) + +
+          Compiles a list of inputs.
+ Resultcompile(JSSourceFile extern, + JSModule[] modules, + CompilerOptions options) + +
+           
+ + + + + +
+<T1 extends SourceFile,T2 extends SourceFile> +
+Result
+
compile(List<T1> externs, + List<T2> inputs, + CompilerOptions options) + +
+          Compiles a list of inputs.
+ Resultcompile(SourceFile extern, + JSSourceFile[] input, + CompilerOptions options) + +
+           
+ Resultcompile(SourceFile extern, + SourceFile input, + CompilerOptions options) + +
+           
+ + + + + +
+<T extends SourceFile> +
+Result
+
compileModules(List<T> externs, + List<JSModule> modules, + CompilerOptions options) + +
+          Compiles a list of modules.
+ voiddisableThreads() + +
+          Disable threads.
+ StringgetAstDotGraph() + +
+          Gets the DOT graph of the AST generated at the end of compilation.
+ CodingConventiongetCodingConvention() + +
+          Gets the current coding convention.
+protected  DiagnosticGroupsgetDiagnosticGroups() + +
+          The warning classes that are available from the command-line, and + are suppressable by the @suppress annotation.
+ intgetErrorCount() + +
+          Gets the number of errors.
+ CheckLevelgetErrorLevel(JSError error) + +
+           
+ ErrorManagergetErrorManager() + +
+          Gets the error manager.
+ JSError[]getErrors() + +
+          Returns the array of errors (never null).
+ CompilerInputgetInput(InputId id) + +
+          Looks up an input (possibly an externs input) by name.
+ Map<InputId,CompilerInput>getInputsById() + +
+          Returns an unmodifiable view of the compiler inputs indexed by id.
+ JSError[]getMessages() + +
+          Returns an array constructed from errors + temporary warnings.
+ doublegetProgress() + +
+           
+ ResultgetResult() + +
+          Returns the result of the compilation.
+ com.google.javascript.jscomp.ReverseAbstractInterpretergetReverseAbstractInterpreter() + +
+          Get an interpreter for type analysis.
+ NodegetRoot() + +
+          Returns the root node of the AST, which includes both externs and source.
+ StringgetSourceLine(String sourceName, + int lineNumber) + +
+          Get the line indicated by the line number.
+ SourceMapgetSourceMap() + +
+           
+ RegiongetSourceRegion(String sourceName, + int lineNumber) + +
+          Get a region around the indicated line number.
+ Compiler.IntermediateStategetState() + +
+          Returns the current internal state, excluding the input files and modules.
+ ScopegetTopScope() + +
+          Gets the top scope.
+ com.google.javascript.jscomp.MemoizedScopeCreatorgetTypedScopeCreator() + +
+          Gets a memoized scope creator with type information.
+ JSTypeRegistrygetTypeRegistry() + +
+          Gets a central registry of type information from the compiled JS.
+ intgetWarningCount() + +
+          Gets the number of warnings.
+ JSError[]getWarnings() + +
+          Returns the array of warnings (never null).
+ booleanhasErrors() + +
+          Consults the ErrorManager to see if we've encountered errors + that should halt compilation.
+ voidinit(JSSourceFile[] externs, + JSModule[] modules, + CompilerOptions options) + +
+          Initializes the instance state needed for a compile job if the sources + are in modules.
+ voidinit(JSSourceFile[] externs, + JSSourceFile[] inputs, + CompilerOptions options) + +
+          Initializes the instance state needed for a compile job.
+ + + + + +
+<T1 extends SourceFile,T2 extends SourceFile> +
+void
+
init(List<T1> externs, + List<T2> inputs, + CompilerOptions options) + +
+          Initializes the instance state needed for a compile job.
+ + + + + +
+<T extends SourceFile> +
+void
+
initModules(List<T> externs, + List<JSModule> modules, + CompilerOptions options) + +
+          Initializes the instance state needed for a compile job if the sources + are in modules.
+ voidinitOptions(CompilerOptions options) + +
+          Initialize the compiler options.
+ booleanisIdeMode() + +
+          Returns true if compiling in IDE mode.
+ booleanisTypeCheckingEnabled() + +
+          Returns true if type checking is enabled.
+ CompilerOptions.LanguageModelanguageMode() + +
+           
+protected  CompilerOptionsnewCompilerOptions() + +
+          Allow subclasses to override the default CompileOptions object.
+ CompilerInputnewExternInput(String name) + +
+          Creates a new externs file.
+ voidnormalize() + +
+           
+ voidoptimize() + +
+           
+ voidparse() + +
+           
+ Nodeparse(SourceFile file) + +
+           
+ voidprocessDefines() + +
+          Reprocesses the current defines over the AST.
+ voidrebuildInputsFromModules() + +
+          Rebuilds the internal list of inputs by iterating over all modules.
+protected  voidremoveExternInput(InputId id) + +
+          Removes an input file from AST.
+ voidreplaceScript(JsAst ast) + +
+          Replaces one file in a hot-swap mode.
+ voidreport(JSError error) + +
+          Report an error or warning.
+ voidreportCodeChange() + +
+          All passes should call reportCodeChange() when they alter + the JS tree structure.
+ voidsetErrorManager(ErrorManager errorManager) + +
+          Sets the error manager.
+static voidsetLoggingLevel(Level level) + +
+          Sets the logging level for the com.google.javascript.jscomp package.
+ voidsetPassConfig(PassConfig passes) + +
+           
+ voidsetState(Compiler.IntermediateState state) + +
+          Sets the internal state to the capture given.
+ StringtoSource() + +
+          Converts the main parse tree back to js code.
+ voidtoSource(Compiler.CodeBuilder cb, + int inputSeqNum, + Node root) + +
+          Writes out js code from a root node.
+ StringtoSource(JSModule module) + +
+          Converts the parse tree for a module back to js code.
+ String[]toSourceArray() + +
+          Converts the parse tree for each input back to js code.
+ String[]toSourceArray(JSModule module) + +
+          Converts the parse tree for each input in a module back to js code.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+tracker

+
+public PerformanceTracker tracker
+
+
+
+
+
+ +

+OPTIMIZE_LOOP_ERROR

+
+public static final DiagnosticType OPTIMIZE_LOOP_ERROR
+
+
Error strings used for reporting JSErrors +

+

+
+
+
+ +

+MOTION_ITERATIONS_ERROR

+
+public static final DiagnosticType MOTION_ITERATIONS_ERROR
+
+
+
+
+
+ +

+recentChange

+
+protected final CodeChangeHandler.RecentChange recentChange
+
+
+
+
+ + + + + + + + +
+Constructor Detail
+ +

+Compiler

+
+public Compiler()
+
+
Creates a Compiler that reports errors and warnings to its logger. +

+

+
+ +

+Compiler

+
+public Compiler(PrintStream stream)
+
+
Creates n Compiler that reports errors and warnings to an output + stream. +

+

+
+ +

+Compiler

+
+public Compiler(ErrorManager errorManager)
+
+
Creates a Compiler that uses a custom error manager. +

+

+ + + + + + + + +
+Method Detail
+ +

+setErrorManager

+
+public void setErrorManager(ErrorManager errorManager)
+
+
Sets the error manager. +

+

+
Parameters:
errorManager - the error manager, it cannot be null
+
+
+
+ +

+initOptions

+
+public void initOptions(CompilerOptions options)
+
+
Initialize the compiler options. Only necessary if you're not doing + a normal compile() job. +

+

+
+
+
+
+ +

+init

+
+public void init(JSSourceFile[] externs,
+                 JSSourceFile[] inputs,
+                 CompilerOptions options)
+
+
Initializes the instance state needed for a compile job. +

+

+
+
+
+
+ +

+init

+
+public <T1 extends SourceFile,T2 extends SourceFile> void init(List<T1> externs,
+                                                               List<T2> inputs,
+                                                               CompilerOptions options)
+
+
Initializes the instance state needed for a compile job. +

+

+
+
+
+
+ +

+init

+
+public void init(JSSourceFile[] externs,
+                 JSModule[] modules,
+                 CompilerOptions options)
+
+
Initializes the instance state needed for a compile job if the sources + are in modules. +

+

+
+
+
+
+ +

+initModules

+
+public <T extends SourceFile> void initModules(List<T> externs,
+                                               List<JSModule> modules,
+                                               CompilerOptions options)
+
+
Initializes the instance state needed for a compile job if the sources + are in modules. +

+

+
+
+
+
+ +

+rebuildInputsFromModules

+
+public void rebuildInputsFromModules()
+
+
Rebuilds the internal list of inputs by iterating over all modules. + This is necessary if inputs have been added to or removed from a module + after the init(JSSourceFile[], JSModule[], CompilerOptions) call. +

+

+
+
+
+
+ +

+compile

+
+public Result compile(SourceFile extern,
+                      SourceFile input,
+                      CompilerOptions options)
+
+
+
+
+
+
+ +

+compile

+
+public Result compile(SourceFile extern,
+                      JSSourceFile[] input,
+                      CompilerOptions options)
+
+
+
+
+
+
+ +

+compile

+
+public Result compile(JSSourceFile extern,
+                      JSModule[] modules,
+                      CompilerOptions options)
+
+
+
+
+
+
+ +

+compile

+
+public Result compile(JSSourceFile[] externs,
+                      JSSourceFile[] inputs,
+                      CompilerOptions options)
+
+
Compiles a list of inputs. +

+

+
+
+
+
+ +

+compile

+
+public <T1 extends SourceFile,T2 extends SourceFile> Result compile(List<T1> externs,
+                                                                    List<T2> inputs,
+                                                                    CompilerOptions options)
+
+
Compiles a list of inputs. +

+

+
+
+
+
+ +

+compile

+
+public Result compile(JSSourceFile[] externs,
+                      JSModule[] modules,
+                      CompilerOptions options)
+
+
Compiles a list of modules. +

+

+
+
+
+
+ +

+compileModules

+
+public <T extends SourceFile> Result compileModules(List<T> externs,
+                                                    List<JSModule> modules,
+                                                    CompilerOptions options)
+
+
Compiles a list of modules. +

+

+
+
+
+
+ +

+disableThreads

+
+public void disableThreads()
+
+
Disable threads. This is for clients that run on AppEngine and + don't have threads. +

+

+
+
+
+
+ +

+parse

+
+public void parse()
+
+
+
+
+
+
+ +

+setPassConfig

+
+public void setPassConfig(PassConfig passes)
+
+
+
Parameters:
passes - The PassConfig to use with this Compiler. +
Throws: +
NullPointerException - if passes is null +
IllegalStateException - if this.passes has already been assigned
+
+
+
+ +

+check

+
+public void check()
+
+
+
+
+
+
+ +

+getResult

+
+public Result getResult()
+
+
Returns the result of the compilation. +

+

+
+
+
+
+ +

+getMessages

+
+public JSError[] getMessages()
+
+
Returns an array constructed from errors + temporary warnings. +

+

+
+
+
+
+ +

+getErrors

+
+public JSError[] getErrors()
+
+
Returns the array of errors (never null). +

+

+
+
+
+
+ +

+getWarnings

+
+public JSError[] getWarnings()
+
+
Returns the array of warnings (never null). +

+

+
+
+
+
+ +

+getRoot

+
+public Node getRoot()
+
+
Description copied from class: AbstractCompiler
+
Returns the root node of the AST, which includes both externs and source. +

+

+
+
+
+
+
+
+
+ +

+getInput

+
+public CompilerInput getInput(InputId id)
+
+
Description copied from class: AbstractCompiler
+
Looks up an input (possibly an externs input) by name. May return null. +

+

+
Specified by:
getInput in class AbstractCompiler
+
+
+
+
+
+
+ +

+removeExternInput

+
+protected void removeExternInput(InputId id)
+
+
Removes an input file from AST. +

+

+
Parameters:
id - The id of the input to be removed.
+
+
+
+ +

+newExternInput

+
+public CompilerInput newExternInput(String name)
+
+
Description copied from class: AbstractCompiler
+
Creates a new externs file. +

+

+
+
+
+
Parameters:
name - A name for the new externs file.
+
+
+
+ +

+getTypeRegistry

+
+public JSTypeRegistry getTypeRegistry()
+
+
Description copied from class: AbstractCompiler
+
Gets a central registry of type information from the compiled JS. +

+

+
Specified by:
getTypeRegistry in class AbstractCompiler
+
+
+
+
+
+
+ +

+getTypedScopeCreator

+
+public com.google.javascript.jscomp.MemoizedScopeCreator getTypedScopeCreator()
+
+
Description copied from class: AbstractCompiler
+
Gets a memoized scope creator with type information. +

+

+
+
+
+
+
+
+
+ +

+buildKnownSymbolTable

+
+public SymbolTable buildKnownSymbolTable()
+
+
+
+
+
+
+ +

+getTopScope

+
+public Scope getTopScope()
+
+
Description copied from class: AbstractCompiler
+
Gets the top scope. +

+

+
Specified by:
getTopScope in class AbstractCompiler
+
+
+
+
+
+
+ +

+getReverseAbstractInterpreter

+
+public com.google.javascript.jscomp.ReverseAbstractInterpreter getReverseAbstractInterpreter()
+
+
Description copied from class: AbstractCompiler
+
Get an interpreter for type analysis. +

+

+
Specified by:
getReverseAbstractInterpreter in class AbstractCompiler
+
+
+
+
+
+
+ +

+parse

+
+public Node parse(SourceFile file)
+
+
+
+
+
+
+ +

+newCompilerOptions

+
+protected CompilerOptions newCompilerOptions()
+
+
Allow subclasses to override the default CompileOptions object. +

+

+
+
+
+
+ +

+toSource

+
+public String toSource()
+
+
Converts the main parse tree back to js code. +

+

+
+
+
+
+ +

+toSourceArray

+
+public String[] toSourceArray()
+
+
Converts the parse tree for each input back to js code. +

+

+
+
+
+
+ +

+toSource

+
+public String toSource(JSModule module)
+
+
Converts the parse tree for a module back to js code. +

+

+
+
+
+
+ +

+toSourceArray

+
+public String[] toSourceArray(JSModule module)
+
+
Converts the parse tree for each input in a module back to js code. +

+

+
+
+
+
+ +

+toSource

+
+public void toSource(Compiler.CodeBuilder cb,
+                     int inputSeqNum,
+                     Node root)
+
+
Writes out js code from a root node. If printing input delimiters, this + method will attach a comment to the start of the text indicating which + input the output derived from. If there were any preserve annotations + within the root's source, they will also be printed in a block comment + at the beginning of the output. +

+

+
+
+
+
+ +

+optimize

+
+public void optimize()
+
+
+
+
+
+
+ +

+processDefines

+
+public void processDefines()
+
+
Reprocesses the current defines over the AST. This is used by GwtCompiler + to generate N outputs for different targets from the same (checked) AST. + For each target, we apply the target-specific defines by calling + processDefines and then optimize to optimize the AST + specifically for that target. +

+

+
+
+
+
+ +

+normalize

+
+public void normalize()
+
+
+
+
+
+
+ +

+reportCodeChange

+
+public void reportCodeChange()
+
+
All passes should call reportCodeChange() when they alter + the JS tree structure. This is verified by CompilerTestCase. + This allows us to optimize to a fixed point. +

+

+
Specified by:
reportCodeChange in class AbstractCompiler
+
+
+
+
+
+
+ +

+getCodingConvention

+
+public CodingConvention getCodingConvention()
+
+
Description copied from class: AbstractCompiler
+
Gets the current coding convention. +

+

+
Specified by:
getCodingConvention in class AbstractCompiler
+
+
+
+
+
+
+ +

+isIdeMode

+
+public boolean isIdeMode()
+
+
Description copied from class: AbstractCompiler
+
Returns true if compiling in IDE mode. +

+

+
+
+
+
+
+
+
+ +

+acceptEcmaScript5

+
+public boolean acceptEcmaScript5()
+
+
+
+
+
+ +
Returns:
Whether the compiler is in ES5Mode.
+
+
+
+ +

+languageMode

+
+public CompilerOptions.LanguageMode languageMode()
+
+
+
+
+
+
+ +

+acceptConstKeyword

+
+public boolean acceptConstKeyword()
+
+
+
+
+
+ +
Returns:
Whether the compiler accepts `const' keyword.
+
+
+
+ +

+isTypeCheckingEnabled

+
+public boolean isTypeCheckingEnabled()
+
+
Description copied from class: AbstractCompiler
+
Returns true if type checking is enabled. +

+

+
+
+
+
+
+
+
+ +

+getDiagnosticGroups

+
+protected DiagnosticGroups getDiagnosticGroups()
+
+
The warning classes that are available from the command-line, and + are suppressable by the @suppress annotation. +

+

+
+
+
+
+ +

+report

+
+public void report(JSError error)
+
+
Description copied from class: AbstractCompiler
+
Report an error or warning. +

+

+
Specified by:
report in class AbstractCompiler
+
+
+
+
+
+
+ +

+getErrorLevel

+
+public CheckLevel getErrorLevel(JSError error)
+
+
+
+
+
+ +
Returns:
The error level the given error object will be reported at.
+
+
+
+ +

+getErrorCount

+
+public int getErrorCount()
+
+
Gets the number of errors. +

+

+
+
+
+
+ +

+getWarningCount

+
+public int getWarningCount()
+
+
Gets the number of warnings. +

+

+
+
+
+
+ +

+hasErrors

+
+public boolean hasErrors()
+
+
Consults the ErrorManager to see if we've encountered errors + that should halt compilation.

+ + If CompilerOptions.ideMode is true, this function + always returns false without consulting the error manager. The + error manager will continue to be told about new errors and warnings, but + the compiler will complete compilation of all inputs.

+

+

+
+
+
+
+ +

+getSourceLine

+
+public String getSourceLine(String sourceName,
+                            int lineNumber)
+
+
Description copied from interface: SourceExcerptProvider
+
Get the line indicated by the line number. This call will return only the + specific line. +

+

+
lineNumber - the line number, 1 being the first line of the file +
Returns:
the line indicated, or null if it does not exist
+
+
+
+ +

+getSourceRegion

+
+public Region getSourceRegion(String sourceName,
+                              int lineNumber)
+
+
Description copied from interface: SourceExcerptProvider
+
Get a region around the indicated line number. The exact definition of a + region is implementation specific, but it must contain the line indicated + by the line number. A region must not start or end by a carriage return. +

+

+
lineNumber - the line number, 1 being the first line of the file +
Returns:
the region around the line number indicated, or null + if it does not exist
+
+
+
+ +

+getSourceMap

+
+public SourceMap getSourceMap()
+
+
+
+
+
+
+ +

+setLoggingLevel

+
+public static void setLoggingLevel(Level level)
+
+
Sets the logging level for the com.google.javascript.jscomp package. +

+

+
+
+
+
+ +

+getAstDotGraph

+
+public String getAstDotGraph()
+                      throws IOException
+
+
Gets the DOT graph of the AST generated at the end of compilation. +

+

+ +
Throws: +
IOException
+
+
+
+ +

+getErrorManager

+
+public ErrorManager getErrorManager()
+
+
Description copied from class: AbstractCompiler
+
Gets the error manager. +

+

+
Specified by:
getErrorManager in class AbstractCompiler
+
+
+
+
+
+
+ +

+getInputsById

+
+public Map<InputId,CompilerInput> getInputsById()
+
+
Returns an unmodifiable view of the compiler inputs indexed by id. +

+

+
+
+
+
+ +

+getState

+
+public Compiler.IntermediateState getState()
+
+
Returns the current internal state, excluding the input files and modules. +

+

+
+
+
+
+ +

+setState

+
+public void setState(Compiler.IntermediateState state)
+
+
Sets the internal state to the capture given. Note that this assumes that + the input files are already set up. +

+

+
+
+
+
+ +

+getProgress

+
+public double getProgress()
+
+
+
Specified by:
getProgress in class AbstractCompiler
+
+
+ +
Returns:
a number in [0,1] range indicating an approximate progress of the + last compile. Note this should only be used as a hint and no assumptions + should be made on accuracy, even a completed compile may choose not to set + this to 1.0 at the end.
+
+
+
+ +

+replaceScript

+
+public void replaceScript(JsAst ast)
+
+
Replaces one file in a hot-swap mode. The given JsAst should be made + from a new version of a file that already was present in the last compile + call. If the file is new, this will silently ignored. +

+

+
Parameters:
ast - the ast of the file that is being replaced
+
+
+
+ +

+addNewScript

+
+public void addNewScript(JsAst ast)
+
+
Adds a new Script AST to the compile state. If a script for the same file + already exists the script will not be added, instead a call to + #replaceScript should be used. +

+

+
Parameters:
ast - the ast of the new file
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerInput.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerInput.html new file mode 100644 index 0000000..57c7d6f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerInput.html @@ -0,0 +1,874 @@ + + + + + +CompilerInput (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class CompilerInput

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CompilerInput
+
+
+
All Implemented Interfaces:
DependencyInfo, SourceAst, Serializable
+
+
+
+
public class CompilerInput
extends Object
implements SourceAst, DependencyInfo
+ + +

+A class for the internal representation of an input to the compiler. + Wraps a SourceAst and maintain state such as module for the input and + whether the input is an extern. Also calculates provided and required types. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+Constructor Summary
CompilerInput(SourceAst ast) + +
+           
CompilerInput(SourceAst ast, + boolean isExtern) + +
+           
CompilerInput(SourceAst ast, + InputId inputId, + boolean isExtern) + +
+           
CompilerInput(SourceAst ast, + String inputId, + boolean isExtern) + +
+           
CompilerInput(SourceFile file) + +
+           
CompilerInput(SourceFile file, + boolean isExtern) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidclearAst() + +
+          Removes any references to root node of the AST.
+ SourceAstgetAst() + +
+           
+ NodegetAstRoot(AbstractCompiler compiler) + +
+          Gets the root node of the AST for the source file this represents.
+ StringgetCode() + +
+           
+ InputIdgetInputId() + +
+          Returns a name for this input.
+ StringgetLine(int lineNumber) + +
+          Gets the source line for the indicated line number.
+ intgetLineOffset(int lineno) + +
+           
+ JSModulegetModule() + +
+          Returns the module to which the input belongs.
+ StringgetName() + +
+          Returns a name for this input.
+ intgetNumLines() + +
+           
+ StringgetPathRelativeToClosureBase() + +
+          Gets the path relative to closure-base, if one is available.
+ Collection<String>getProvides() + +
+          Gets a list of types provided by this input.
+ RegiongetRegion(int lineNumber) + +
+          Get a region around the indicated line number.
+ Collection<String>getRequires() + +
+          Gets a list of types depended on by this input.
+ SourceAstgetSourceAst() + +
+          Returns the SourceAst object on which this input is based.
+ SourceFilegetSourceFile() + +
+          Returns the source file the generated AST represents.
+ booleanisExtern() + +
+           
+ voidremoveRequire(String require) + +
+           
+ voidsetCompiler(AbstractCompiler compiler) + +
+          Sets an abstract compiler for doing parsing.
+ voidsetModule(JSModule module) + +
+          Sets the module to which the input belongs.
+ voidsetSourceFile(SourceFile file) + +
+          Sets the source file the generated AST represents.
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+CompilerInput

+
+public CompilerInput(SourceAst ast)
+
+
+
+ +

+CompilerInput

+
+public CompilerInput(SourceAst ast,
+                     boolean isExtern)
+
+
+
+ +

+CompilerInput

+
+public CompilerInput(SourceAst ast,
+                     String inputId,
+                     boolean isExtern)
+
+
+
+ +

+CompilerInput

+
+public CompilerInput(SourceAst ast,
+                     InputId inputId,
+                     boolean isExtern)
+
+
+
+ +

+CompilerInput

+
+public CompilerInput(SourceFile file)
+
+
+
+ +

+CompilerInput

+
+public CompilerInput(SourceFile file,
+                     boolean isExtern)
+
+
+ + + + + + + + +
+Method Detail
+ +

+getInputId

+
+public InputId getInputId()
+
+
Returns a name for this input. Must be unique across all inputs. +

+

+
Specified by:
getInputId in interface SourceAst
+
+
+ +
Returns:
The input id associated with this AST
+
+
+
+ +

+getName

+
+public String getName()
+
+
Returns a name for this input. Must be unique across all inputs. +

+

+
Specified by:
getName in interface DependencyInfo
+
+
+
+
+
+
+ +

+getAst

+
+public SourceAst getAst()
+
+
+
+
+
+
+
+
+
+ +

+getPathRelativeToClosureBase

+
+public String getPathRelativeToClosureBase()
+
+
Gets the path relative to closure-base, if one is available. +

+

+
Specified by:
getPathRelativeToClosureBase in interface DependencyInfo
+
+
+
+
+
+
+ +

+getAstRoot

+
+public Node getAstRoot(AbstractCompiler compiler)
+
+
Description copied from interface: SourceAst
+
Gets the root node of the AST for the source file this represents. The AST + is lazily instantiated and cached. +

+

+
Specified by:
getAstRoot in interface SourceAst
+
+
+
+
+
+
+ +

+clearAst

+
+public void clearAst()
+
+
Description copied from interface: SourceAst
+
Removes any references to root node of the AST. If it is requested again, + another parse will be performed. This method is needed to allow the ASTs + to be garbage collected if the inputs are still around after compilation. +

+

+
Specified by:
clearAst in interface SourceAst
+
+
+
+
+
+
+ +

+getSourceFile

+
+public SourceFile getSourceFile()
+
+
Description copied from interface: SourceAst
+
Returns the source file the generated AST represents. +

+

+
Specified by:
getSourceFile in interface SourceAst
+
+
+
+
+
+
+ +

+setSourceFile

+
+public void setSourceFile(SourceFile file)
+
+
Description copied from interface: SourceAst
+
Sets the source file the generated AST represents. This can be called after + deserializing if access to the source file is needed. If a different file + is provided than that with which this was created, an IllegalStateException + will be thrown. +

+

+
Specified by:
setSourceFile in interface SourceAst
+
+
+
+
+
+
+ +

+getSourceAst

+
+public SourceAst getSourceAst()
+
+
Returns the SourceAst object on which this input is based. +

+

+
+
+
+
+
+
+
+ +

+setCompiler

+
+public void setCompiler(AbstractCompiler compiler)
+
+
Sets an abstract compiler for doing parsing. +

+

+
+
+
+
+
+
+
+ +

+getRequires

+
+public Collection<String> getRequires()
+
+
Gets a list of types depended on by this input. +

+

+
Specified by:
getRequires in interface DependencyInfo
+
+
+
+
+
+
+ +

+getProvides

+
+public Collection<String> getProvides()
+
+
Gets a list of types provided by this input. +

+

+
Specified by:
getProvides in interface DependencyInfo
+
+
+
+
+
+
+ +

+removeRequire

+
+public void removeRequire(String require)
+
+
+
+
+
+
+
+
+
+ +

+getLine

+
+public String getLine(int lineNumber)
+
+
Gets the source line for the indicated line number. +

+

+
+
+
+
Parameters:
lineNumber - the line number, 1 being the first line of the file. +
Returns:
The line indicated. Does not include the newline at the end + of the file. Returns null if it does not exist, + or if there was an IO exception.
+
+
+
+ +

+getRegion

+
+public Region getRegion(int lineNumber)
+
+
Get a region around the indicated line number. The exact definition of a + region is implementation specific, but it must contain the line indicated + by the line number. A region must not start or end by a carriage return. +

+

+
+
+
+
Parameters:
lineNumber - the line number, 1 being the first line of the file. +
Returns:
The line indicated. Returns null if it does not exist, + or if there was an IO exception.
+
+
+
+ +

+getCode

+
+public String getCode()
+               throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+getModule

+
+public JSModule getModule()
+
+
Returns the module to which the input belongs. +

+

+
+
+
+
+
+
+
+ +

+setModule

+
+public void setModule(JSModule module)
+
+
Sets the module to which the input belongs. +

+

+
+
+
+
+
+
+
+ +

+isExtern

+
+public boolean isExtern()
+
+
+
+
+
+
+
+
+
+ +

+getLineOffset

+
+public int getLineOffset(int lineno)
+
+
+
+
+
+
+
+
+
+ +

+getNumLines

+
+public int getNumLines()
+
+
+
+
+
+ +
Returns:
The number of lines in this input.
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.AliasTransformation.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.AliasTransformation.html new file mode 100644 index 0000000..5eaab48 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.AliasTransformation.html @@ -0,0 +1,236 @@ + + + + + +CompilerOptions.AliasTransformation (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface CompilerOptions.AliasTransformation

+
+
Enclosing class:
CompilerOptions
+
+
+
+
public static interface CompilerOptions.AliasTransformation
+ + +

+A Role Specific Interface for the JsCompiler to report aliases used to + change the code during a compile. +

+ While aliases defined by goog.scope are expected to by only 1 per file, and + the only top level structure in the file, this is not enforced. +

+ +

+


+ +

+ + + + + + + + + + + + +
+Method Summary
+ voidaddAlias(String alias, + String definition) + +
+          Adds an alias definition to the AliasTransformation instance.
+  +

+ + + + + + + + +
+Method Detail
+ +

+addAlias

+
+void addAlias(String alias,
+              String definition)
+
+
Adds an alias definition to the AliasTransformation instance. +

+ Last definition for a given alias is kept if an alias is inserted + multiple times (since this is generally the behavior in JavaScript code). +

+

+
Parameters:
alias - the name of the alias.
definition - the definition of the alias.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.AliasTransformationHandler.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.AliasTransformationHandler.html new file mode 100644 index 0000000..6147205 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.AliasTransformationHandler.html @@ -0,0 +1,250 @@ + + + + + +CompilerOptions.AliasTransformationHandler (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface CompilerOptions.AliasTransformationHandler

+
+
Enclosing class:
CompilerOptions
+
+
+
+
public static interface CompilerOptions.AliasTransformationHandler
+ + +

+A Role Specific Interface for JsCompiler that represents a data holder + object which is used to store goog.scope alias code changes to code made + during a compile. There is no guarantee that individual alias changes are + invoked in the order they occur during compilation, so implementations + should not assume any relevance to the order changes arrive. +

+ Calls to the mutators are expected to resolve very quickly, so + implementations should not perform expensive operations in the mutator + methods. +

+ +

+


+ +

+ + + + + + + + + + + + +
+Method Summary
+ CompilerOptions.AliasTransformationlogAliasTransformation(String sourceFile, + SourcePosition<CompilerOptions.AliasTransformation> position) + +
+          Builds an AliasTransformation implementation and returns it to the + caller.
+  +

+ + + + + + + + +
+Method Detail
+ +

+logAliasTransformation

+
+CompilerOptions.AliasTransformation logAliasTransformation(String sourceFile,
+                                                           SourcePosition<CompilerOptions.AliasTransformation> position)
+
+
Builds an AliasTransformation implementation and returns it to the + caller. +

+ Callers are allowed to request multiple AliasTransformation instances for + the same file, though it is expected that the first and last char values + for multiple instances will not overlap. +

+ This method is expected to have a side-effect of storing off the created + AliasTransformation, which guarantees that invokers of this interface + cannot leak AliasTransformation to this implementation that the + implementor did not create +

+

+
Parameters:
sourceFile - the source file the aliases re contained in.
position - the region of the source file associated with the + goog.scope call. The item of the SourcePosition is the returned + AliasTransformation
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.LanguageMode.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.LanguageMode.html new file mode 100644 index 0000000..34e7375 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.LanguageMode.html @@ -0,0 +1,363 @@ + + + + + +CompilerOptions.LanguageMode (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum CompilerOptions.LanguageMode

+
+java.lang.Object
+  extended by java.lang.Enum<CompilerOptions.LanguageMode>
+      extended by com.google.javascript.jscomp.CompilerOptions.LanguageMode
+
+
+
All Implemented Interfaces:
Serializable, Comparable<CompilerOptions.LanguageMode>
+
+
+
Enclosing class:
CompilerOptions
+
+
+
+
public static enum CompilerOptions.LanguageMode
extends Enum<CompilerOptions.LanguageMode>
+ + +

+When to do the extra sanity checks +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Enum Constant Summary
ECMASCRIPT3 + +
+          Tranditional JavaScript
ECMASCRIPT5 + +
+          Shiny new JavaScript
ECMASCRIPT5_STRICT + +
+          Nitpicky, shiny new JavaScript
+  + + + + + + + + + + + + + + + +
+Method Summary
+static CompilerOptions.LanguageModevalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static CompilerOptions.LanguageMode[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+ECMASCRIPT3

+
+public static final CompilerOptions.LanguageMode ECMASCRIPT3
+
+
Tranditional JavaScript +

+

+
+
+
+ +

+ECMASCRIPT5

+
+public static final CompilerOptions.LanguageMode ECMASCRIPT5
+
+
Shiny new JavaScript +

+

+
+
+
+ +

+ECMASCRIPT5_STRICT

+
+public static final CompilerOptions.LanguageMode ECMASCRIPT5_STRICT
+
+
Nitpicky, shiny new JavaScript +

+

+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static CompilerOptions.LanguageMode[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (CompilerOptions.LanguageMode c : CompilerOptions.LanguageMode.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static CompilerOptions.LanguageMode valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.Reach.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.Reach.html new file mode 100644 index 0000000..81f8613 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.Reach.html @@ -0,0 +1,353 @@ + + + + + +CompilerOptions.Reach (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum CompilerOptions.Reach

+
+java.lang.Object
+  extended by java.lang.Enum<CompilerOptions.Reach>
+      extended by com.google.javascript.jscomp.CompilerOptions.Reach
+
+
+
All Implemented Interfaces:
Serializable, Comparable<CompilerOptions.Reach>
+
+
+
Enclosing class:
CompilerOptions
+
+
+
+
public static enum CompilerOptions.Reach
extends Enum<CompilerOptions.Reach>
+ + +

+


+ +

+ + + + + + + + + + + + + + + + +
+Enum Constant Summary
ALL + +
+           
LOCAL_ONLY + +
+           
NONE + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static CompilerOptions.ReachvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static CompilerOptions.Reach[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+ALL

+
+public static final CompilerOptions.Reach ALL
+
+
+
+
+
+ +

+LOCAL_ONLY

+
+public static final CompilerOptions.Reach LOCAL_ONLY
+
+
+
+
+
+ +

+NONE

+
+public static final CompilerOptions.Reach NONE
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static CompilerOptions.Reach[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (CompilerOptions.Reach c : CompilerOptions.Reach.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static CompilerOptions.Reach valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.TracerMode.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.TracerMode.html new file mode 100644 index 0000000..b0811f9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.TracerMode.html @@ -0,0 +1,369 @@ + + + + + +CompilerOptions.TracerMode (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum CompilerOptions.TracerMode

+
+java.lang.Object
+  extended by java.lang.Enum<CompilerOptions.TracerMode>
+      extended by com.google.javascript.jscomp.CompilerOptions.TracerMode
+
+
+
All Implemented Interfaces:
Serializable, Comparable<CompilerOptions.TracerMode>
+
+
+
Enclosing class:
CompilerOptions
+
+
+
+
public static enum CompilerOptions.TracerMode
extends Enum<CompilerOptions.TracerMode>
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + +
+Enum Constant Summary
ALL + +
+           
OFF + +
+           
RAW_SIZE + +
+           
TIMING_ONLY + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static CompilerOptions.TracerModevalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static CompilerOptions.TracerMode[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+ALL

+
+public static final CompilerOptions.TracerMode ALL
+
+
+
+
+
+ +

+RAW_SIZE

+
+public static final CompilerOptions.TracerMode RAW_SIZE
+
+
+
+
+
+ +

+TIMING_ONLY

+
+public static final CompilerOptions.TracerMode TIMING_ONLY
+
+
+
+
+
+ +

+OFF

+
+public static final CompilerOptions.TracerMode OFF
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static CompilerOptions.TracerMode[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (CompilerOptions.TracerMode c : CompilerOptions.TracerMode.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static CompilerOptions.TracerMode valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.TweakProcessing.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.TweakProcessing.html new file mode 100644 index 0000000..74ac188 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.TweakProcessing.html @@ -0,0 +1,391 @@ + + + + + +CompilerOptions.TweakProcessing (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum CompilerOptions.TweakProcessing

+
+java.lang.Object
+  extended by java.lang.Enum<CompilerOptions.TweakProcessing>
+      extended by com.google.javascript.jscomp.CompilerOptions.TweakProcessing
+
+
+
All Implemented Interfaces:
Serializable, Comparable<CompilerOptions.TweakProcessing>
+
+
+
Enclosing class:
CompilerOptions
+
+
+
+
public static enum CompilerOptions.TweakProcessing
extends Enum<CompilerOptions.TweakProcessing>
+ + +

+


+ +

+ + + + + + + + + + + + + + + + +
+Enum Constant Summary
CHECK + +
+           
OFF + +
+           
STRIP + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleanisOn() + +
+           
+ booleanshouldStrip() + +
+           
+static CompilerOptions.TweakProcessingvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static CompilerOptions.TweakProcessing[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+OFF

+
+public static final CompilerOptions.TweakProcessing OFF
+
+
+
+
+
+ +

+CHECK

+
+public static final CompilerOptions.TweakProcessing CHECK
+
+
+
+
+
+ +

+STRIP

+
+public static final CompilerOptions.TweakProcessing STRIP
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static CompilerOptions.TweakProcessing[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (CompilerOptions.TweakProcessing c : CompilerOptions.TweakProcessing.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static CompilerOptions.TweakProcessing valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+
+ +

+isOn

+
+public boolean isOn()
+
+
+
+
+
+
+ +

+shouldStrip

+
+public boolean shouldStrip()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.html new file mode 100644 index 0000000..9b0e9f5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerOptions.html @@ -0,0 +1,6656 @@ + + + + + +CompilerOptions (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class CompilerOptions

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CompilerOptions
+
+
+
All Implemented Interfaces:
Serializable, Cloneable
+
+
+
+
public class CompilerOptions
extends Object
implements Serializable, Cloneable
+ + +

+Compiler options +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Nested Class Summary
+static interfaceCompilerOptions.AliasTransformation + +
+          A Role Specific Interface for the JsCompiler to report aliases used to + change the code during a compile.
+static interfaceCompilerOptions.AliasTransformationHandler + +
+          A Role Specific Interface for JsCompiler that represents a data holder + object which is used to store goog.scope alias code changes to code made + during a compile.
+static classCompilerOptions.LanguageMode + +
+          When to do the extra sanity checks
+static classCompilerOptions.Reach + +
+           
+static classCompilerOptions.TracerMode + +
+           
+static classCompilerOptions.TweakProcessing + +
+           
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Field Summary
+ CheckLevelaggressiveVarCheck + +
+           
+ Set<String>aliasableStrings + +
+          If set to a non-empty set, those strings literals will be aliased to a + single global instance per string, to avoid creating more objects than + necessary.
+ booleanaliasAllStrings + +
+          Aliases all string literals to global instances, to avoid creating more + objects than necessary (if true, overrides any set of strings passed in + to aliasableStrings)
+ booleanaliasExternals + +
+          Adds variable aliases for externals to reduce code size
+ booleanaliasKeywords + +
+          Aliases true, false, and null to variables with shorter names.
+ StringaliasStringsBlacklist + +
+          A blacklist in the form of a regular expression to block strings that + contains certain words from being aliased.
+ booleanambiguateProperties + +
+          Rename unrelated properties to the same name to reduce code size.
+ AnonymousFunctionNamingPolicyanonymousFunctionNaming + +
+          Give anonymous functions names for easier debugging
+ CheckLevelbrokenClosureRequiresLevel + +
+           
+ booleancheckControlStructures + +
+          Checks for invalid control structures
+ CheckLevelcheckGlobalNamesLevel + +
+           
+ CheckLevelcheckGlobalThisLevel + +
+           
+ StringcheckMissingGetCssNameBlacklist + +
+          Regex of string literals that may only appear in goog.getCssName arguments.
+ CheckLevelcheckMissingGetCssNameLevel + +
+           
+ CheckLevelcheckMissingReturn + +
+           
+ CheckLevelcheckProvides + +
+           
+ CheckLevelcheckRequires + +
+          Checks for missing goog.require() calls
+ booleancheckSuspiciousCode + +
+          Checks for suspicious statements that have no effect
+ booleancheckSymbols + +
+          Checks that all symbols are defined
+ booleancheckTypes + +
+          Checks types on expressions
+ CheckLevelcheckUnreachableCode + +
+           
+ booleanclosurePass + +
+          Processes goog.provide() and goog.require() calls
+ booleancoalesceVariableNames + +
+          Merge two variables together as one.
+ booleancollapseAnonymousFunctions + +
+          Collapses anonymous function declarations into named function + declarations
+ booleancollapseProperties + +
+          Flattens multi-level property names (e.g.
+ booleancollapseVariableDeclarations + +
+          Collapses multiple variable declarations into one
+ booleancomputeFunctionSideEffects + +
+          Use @nosideeffects annotations, function bodies and name graph + to determine if calls have side effects.
+ booleanconvertToDottedProperties + +
+          Converts quoted property accesses to dot syntax (a['b'] -> a.b)
+ booleancrossModuleCodeMotion + +
+          Move code to a deeper module
+ booleancrossModuleMethodMotion + +
+          Move methds to a deeper module
+ CssRenamingMapcssRenamingMap + +
+          Map used in the renaming of CSS class names.
+ com.google.common.collect.Multimap<CustomPassExecutionTime,CompilerPass>customPasses + +
+          Custom passes
+ booleandeadAssignmentElimination + +
+          Remove assignments to values that can not be referenced
+ booleandevirtualizePrototypeMethods + +
+          Devirtualize prototype method by rewriting them to be static calls that + take the this pointer as their first argument
+ booleandisambiguateProperties + +
+          Rename properties to disambiguate between unrelated fields based on + type information.
+ ErrorFormaterrorFormat + +
+           
+ booleanexportTestFunctions + +
+          Whether to export test functions.
+ booleanextractPrototypeMemberDeclarations + +
+          Extracts common prototype member declarations
+ booleanflowSensitiveInlineVariables + +
+           
+ booleanfoldConstants + +
+          Folds constants (e.g.
+ booleangatherCssNames + +
+          Gather CSS names (requires closurePass)
+ booleangenerateExports + +
+           
+ booleangeneratePseudoNames + +
+          Generate pseudo names for variables and properties for debugging purposes.
+ booleanideMode + +
+          Configures the compiler for use as an IDE backend.
+ booleaninlineConstantVars + +
+          Inlines constants (symbols that are all CAPS)
+ booleaninlineFunctions + +
+          Inlines short functions
+ booleaninlineGetters + +
+          Inlines trivial getters
+ booleaninlineLocalFunctions + +
+          Enhanced function inlining
+ booleaninlineVariables + +
+          Inlines variables
+ StringinputDelimiter + +
+          The string to use as the separator for printInputDelimiter
+ byte[]inputPropertyMapSerialized + +
+          Serialized input property renaming map.
+ byte[]inputVariableMapSerialized + +
+          Serialized input variable renaming map.
+ StringinstrumentationTemplate + +
+          Instrumentation template to use with #recordFunctionInformation
+ booleanjqueryPass + +
+          Processes jQuery aliases
+ booleanlabelRenaming + +
+          Controls label renaming.
+ booleanlineBreak + +
+          Line break the output a bit more aggressively
+ Stringlocale + +
+          Compiling locale
+ booleanmarkAsCompiled + +
+          Sets the special "COMPILED" value to true
+ booleanmarkNoSideEffectCalls + +
+          Mark no side effect calls
+ MessageBundlemessageBundle + +
+          Returns localized replacement for MSG_* variables
+ booleanmoveFunctionDeclarations + +
+          Move top level function declarations to the top
+ booleanoptimizeArgumentsArray + +
+          Provide formal names for elements of arguments array.
+ booleanoptimizeCalls + +
+          Remove unused parameters from call sites.
+ booleanoptimizeParameters + +
+          Remove unused and constant parameters.
+ booleanoptimizeReturns + +
+          Remove unused return values.
+ booleanpreferLineBreakAtEndOfFile + +
+          Prefer line breaks at end of file
+ booleanprettyPrint + +
+          Output in pretty indented format
+ booleanprintInputDelimiter + +
+          Prints a separator comment before each js script
+ PropertyRenamingPolicypropertyRenaming + +
+          Controls which properties get renamed.
+ booleanrecordFunctionInformation + +
+          Record function information
+ booleanremoveDeadCode + +
+          Removes code that will never execute
+ booleanremoveTryCatchFinally + +
+          Removes try...catch...finally blocks for easier debugging
+ booleanremoveUnusedLocalVars + +
+          Removes unused variables in local scope.
+ booleanremoveUnusedPrototypeProperties + +
+          Removes unused member prototypes
+ booleanremoveUnusedPrototypePropertiesInExterns + +
+          Tells AnalyzePrototypeProperties it can remove externed props.
+ booleanremoveUnusedVars + +
+          Removes unused variables
+ StringrenamePrefix + +
+          Specifies a prefix for all globals
+ StringrenamePrefixNamespace + +
+          Specifies the name of an object that will be used to store all non-extern + globals.
+ CheckLevelreportMissingOverride + +
+           
+ booleanreserveRawExports + +
+          Reserve property names on the global this object.
+ booleanrewriteFunctionExpressions + +
+          Reduces the size of common function expressions.
+ booleansmartNameRemoval + +
+          Removes code associated with unused global names
+ SourceMap.DetailLevelsourceMapDetailLevel + +
+          The detail level for the generated source map.
+ SourceMap.FormatsourceMapFormat + +
+          The source map file format
+ List<SourceMap.LocationMapping>sourceMapLocationMappings + +
+           
+ StringsourceMapOutputPath + +
+          The output path for the source map.
+ Set<String>stripNamePrefixes + +
+          Name prefixes that determine which variables and properties to strip
+ Set<String>stripNameSuffixes + +
+          Name suffixes that determine which variables and properties to strip
+ Set<String>stripTypePrefixes + +
+          Qualified type name prefixes that determine which types to strip
+ Set<String>stripTypes + +
+          Names of types to strip
+ StringsyntheticBlockEndMarker + +
+           
+ StringsyntheticBlockStartMarker + +
+           
+ VariableRenamingPolicyvariableRenaming + +
+          Controls which variables get renamed.
+  + + + + + + + + + + +
+Constructor Summary
CompilerOptions() + +
+          Initializes compiler options.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidaddWarningsGuard(WarningsGuard guard) + +
+          Add a guard to the set of warnings guards.
+ booleanassumeClosuresOnlyCaptureReferences() + +
+           
+ booleanassumeStrictThis() + +
+           
+ Objectclone() + +
+           
+ voiddisableRuntimeTypeCheck() + +
+           
+ voidenableExternExports(boolean enabled) + +
+          Deprecated. replaced by setExternExports(boolean)
+ voidenableRuntimeTypeCheck(String logFunction) + +
+          Enable runtime type checking, which adds JS type assertions for debugging.
+ CompilerOptions.AliasTransformationHandlergetAliasTransformationHandler() + +
+           
+ CodingConventiongetCodingConvention() + +
+           
+ Map<String,Node>getDefineReplacements() + +
+          Returns the map of define replacements.
+ booleangetInferTypes() + +
+          Gets the inferTypes flag.
+ CompilerOptions.LanguageModegetLanguageIn() + +
+           
+ CompilerOptions.LanguageModegetLanguageOut() + +
+           
+ CompilerOptions.TracerModegetTracerMode() + +
+           
+ CompilerOptions.TweakProcessinggetTweakProcessing() + +
+           
+ Map<String,Node>getTweakReplacements() + +
+          Returns the map of tweak replacements.
+ booleanisExternExportsEnabled() + +
+           
+ voidresetWarningsGuard() + +
+          Reset the warnings guard.
+ voidsetAcceptConstKeyword(boolean value) + +
+          If true, accept `const' keyword.
+ voidsetAggressiveVarCheck(CheckLevel level) + +
+          Checks for suspicious variable definitions and undefined variables
+ voidsetAliasableGlobals(String names) + +
+          A comma separated white-list of global names.
+ voidsetAliasableStrings(Set<String> aliasableStrings) + +
+           
+ voidsetAliasAllStrings(boolean aliasAllStrings) + +
+           
+ voidsetAliasExternals(boolean aliasExternals) + +
+           
+ voidsetAliasKeywords(boolean aliasKeywords) + +
+           
+ voidsetAliasStringsBlacklist(String aliasStringsBlacklist) + +
+           
+ voidsetAliasTransformationHandler(CompilerOptions.AliasTransformationHandler changes) + +
+           
+ voidsetAmbiguateProperties(boolean ambiguateProperties) + +
+           
+ voidsetAnonymousFunctionNaming(AnonymousFunctionNamingPolicy anonymousFunctionNaming) + +
+           
+ voidsetAppNameStr(String appNameStr) + +
+          App identifier string for use by the instrumentation template's + app_name_setter.
+ voidsetAssumeClosuresOnlyCaptureReferences(boolean enable) + +
+          Whether to assume closures capture only what they reference.
+ voidsetAssumeStrictThis(boolean enable) + +
+          If true, enables enables additional optimizations.
+ voidsetBrokenClosureRequiresLevel(CheckLevel level) + +
+          Sets the check level for bad Closure require calls.
+ voidsetChainCalls(boolean value) + +
+          If true, chain calls to functions that return this.
+ voidsetCheckCaja(boolean check) + +
+           
+ voidsetCheckControlStructures(boolean checkControlStructures) + +
+           
+ voidsetCheckGlobalNamesLevel(CheckLevel level) + +
+          Checks the integrity of references to qualified global names.
+ voidsetCheckGlobalThisLevel(CheckLevel level) + +
+          Checks for certain uses of the this keyword that are considered + unsafe because they are likely to reference the global this + object unintentionally.
+ voidsetCheckMissingGetCssNameBlacklist(String blackList) + +
+           
+ voidsetCheckMissingGetCssNameLevel(CheckLevel level) + +
+          Checks that certain string literals only appear in strings used as + goog.getCssName arguments.
+ voidsetCheckMissingReturn(CheckLevel level) + +
+          Checks for missing return statements
+ voidsetCheckProvides(CheckLevel level) + +
+          Checks for missing goog.provides() calls
+ voidsetCheckRequires(CheckLevel level) + +
+           
+ voidsetCheckSuspiciousCode(boolean checkSuspiciousCode) + +
+           
+ voidsetCheckSymbols(boolean checkSymbols) + +
+           
+ voidsetCheckTypes(boolean checkTypes) + +
+           
+ voidsetCheckUnreachableCode(CheckLevel level) + +
+          Checks for unreachable code
+ voidsetClosurePass(boolean closurePass) + +
+           
+ voidsetCoalesceVariableNames(boolean coalesceVariableNames) + +
+           
+ voidsetCodingConvention(CodingConvention codingConvention) + +
+           
+ voidsetCollapseAnonymousFunctions(boolean enabled) + +
+           
+ voidsetCollapseObjectLiterals(boolean enabled) + +
+           
+ voidsetCollapseProperties(boolean collapseProperties) + +
+           
+ voidsetCollapsePropertiesOnExternTypes(boolean collapse) + +
+          If true, flattens multi-level property names on extern types + (e.g.
+ voidsetCollapseVariableDeclarations(boolean enabled) + +
+           
+ voidsetColorizeErrorOutput(boolean colorizeErrorOutput) + +
+           
+ voidsetCommonJSModulePathPrefix(String commonJSModulePathPrefix) + +
+          Sets a path prefix for Common JS modules.
+ voidsetComputeFunctionSideEffects(boolean computeFunctionSideEffects) + +
+           
+ voidsetConvertToDottedProperties(boolean convertToDottedProperties) + +
+           
+ voidsetCrossModuleCodeMotion(boolean crossModuleCodeMotion) + +
+           
+ voidsetCrossModuleMethodMotion(boolean crossModuleMethodMotion) + +
+           
+ voidsetCssRenamingMap(CssRenamingMap cssRenamingMap) + +
+           
+ voidsetCustomPasses(com.google.common.collect.Multimap<CustomPassExecutionTime,CompilerPass> customPasses) + +
+           
+ voidsetDeadAssignmentElimination(boolean deadAssignmentElimination) + +
+           
+ voidsetDebugFunctionSideEffectsPath(String debugFunctionSideEffectsPath) + +
+           
+ voidsetDefineReplacements(Map<String,Object> defineReplacements) + +
+           
+ voidsetDefineToBooleanLiteral(String defineName, + boolean value) + +
+          Sets the value of the @define variable in JS + to a boolean literal.
+ voidsetDefineToDoubleLiteral(String defineName, + double value) + +
+          Sets the value of the @define variable in JS to a + number literal.
+ voidsetDefineToNumberLiteral(String defineName, + int value) + +
+          Sets the value of the @define variable in JS to a + number literal.
+ voidsetDefineToStringLiteral(String defineName, + String value) + +
+          Sets the value of the @define variable in JS to a + String literal.
+ voidsetDependencyOptions(DependencyOptions options) + +
+          Sets dependency options.
+ voidsetDevirtualizePrototypeMethods(boolean devirtualizePrototypeMethods) + +
+           
+ voidsetDevMode(com.google.javascript.jscomp.CompilerOptions.DevMode devMode) + +
+           
+ voidsetDisambiguateProperties(boolean disambiguateProperties) + +
+           
+ voidsetErrorFormat(ErrorFormat errorFormat) + +
+           
+ voidsetErrorHandler(ErrorHandler handler) + +
+          Set a custom handler for warnings and errors.
+ voidsetExportTestFunctions(boolean exportTestFunctions) + +
+           
+ voidsetExternExports(boolean externExports) + +
+           
+ voidsetExternExportsPath(String externExportsPath) + +
+           
+ voidsetExtraAnnotationNames(Set<String> extraAnnotationNames) + +
+           
+ voidsetExtractPrototypeMemberDeclarations(boolean enabled) + +
+           
+ voidsetFlowSensitiveInlineVariables(boolean enabled) + +
+           
+ voidsetFoldConstants(boolean foldConstants) + +
+           
+ voidsetGatherCssNames(boolean gatherCssNames) + +
+           
+ voidsetGenerateExports(boolean generateExports) + +
+           
+ voidsetGeneratePseudoNames(boolean generatePseudoNames) + +
+           
+ voidsetGroupVariableDeclarations(boolean enabled) + +
+           
+ voidsetIdeMode(boolean ideMode) + +
+           
+ voidsetIdGenerators(Set<String> idGenerators) + +
+          Sets the id generators to replace.
+ voidsetIgnoreCajaProperties(boolean enabled) + +
+          Add code to skip properties that Caja adds to Object.prototype
+ voidsetInferTypes(boolean enable) + +
+          If true, enables type inference.
+ voidsetInlineConstantVars(boolean inlineConstantVars) + +
+           
+ voidsetInlineFunctions(boolean inlineFunctions) + +
+           
+ voidsetInlineFunctions(CompilerOptions.Reach reach) + +
+          Set the function inlining policy for the compiler.
+ voidsetInlineGetters(boolean inlineGetters) + +
+           
+ voidsetInlineLocalFunctions(boolean inlineLocalFunctions) + +
+           
+ voidsetInlineLocalVariables(boolean inlineLocalVariables) + +
+           
+ voidsetInlineVariables(boolean inlineVariables) + +
+           
+ voidsetInlineVariables(CompilerOptions.Reach reach) + +
+          Set the variable inlining policy for the compiler.
+ voidsetInputDelimiter(String inputDelimiter) + +
+           
+ voidsetInputPropertyMapSerialized(byte[] inputPropertyMapSerialized) + +
+           
+ voidsetInputVariableMapSerialized(byte[] inputVariableMapSerialized) + +
+           
+ voidsetInstrumentationTemplate(String instrumentationTemplate) + +
+           
+ voidsetLabelRenaming(boolean labelRenaming) + +
+           
+ voidsetLanguageIn(CompilerOptions.LanguageMode languageIn) + +
+          Sets how goog.tweak calls are processed.
+ voidsetLanguageOut(CompilerOptions.LanguageMode languageOut) + +
+           
+ voidsetLineBreak(boolean lineBreak) + +
+           
+ voidsetLineLengthThreshold(int lineLengthThreshold) + +
+           
+ voidsetLocale(String locale) + +
+           
+ voidsetLooseTypes(boolean looseTypes) + +
+          Whether to include "undefined" in the default types.
+ voidsetManageClosureDependencies(boolean newVal) + +
+          Sort inputs by their goog.provide/goog.require calls, and prune inputs + whose symbols are not required.
+ voidsetManageClosureDependencies(List<String> entryPoints) + +
+          Sort inputs by their goog.provide/goog.require calls.
+ voidsetMarkAsCompiled(boolean markAsCompiled) + +
+           
+ voidsetMarkNoSideEffectCalls(boolean markNoSideEffectCalls) + +
+           
+ voidsetMessageBundle(MessageBundle messageBundle) + +
+           
+ voidsetMoveFunctionDeclarations(boolean moveFunctionDeclarations) + +
+           
+ voidsetNameAnonymousFunctionsOnly(boolean value) + +
+          If true, name anonymous functions only.
+ voidsetNameReferenceGraphPath(String filePath) + +
+          Where to save the name reference graph
+ voidsetNameReferenceReportPath(String filePath) + +
+          Where to save a cross-reference report from the name reference graph
+ voidsetOptimizeArgumentsArray(boolean optimizeArgumentsArray) + +
+           
+ voidsetOptimizeCalls(boolean optimizeCalls) + +
+           
+ voidsetOptimizeParameters(boolean optimizeParameters) + +
+           
+ voidsetOptimizeReturns(boolean optimizeReturns) + +
+           
+ voidsetOutputCharset(String charsetName) + +
+          Sets the output charset by name.
+ voidsetOutputJsStringUsage(boolean outputJsStringUsage) + +
+           
+ voidsetPreferLineBreakAtEndOfFile(boolean lineBreakAtEnd) + +
+           
+ voidsetPrettyPrint(boolean prettyPrint) + +
+           
+ voidsetPrintInputDelimiter(boolean printInputDelimiter) + +
+           
+ voidsetProcessCommonJSModules(boolean processCommonJSModules) + +
+          Rewrites CommonJS modulee so that modules can be concatenated together, + by renaming all globals to avoid conflicting with other modules.
+ voidsetProcessObjectPropertyString(boolean process) + +
+          If true, process goog.testing.ObjectPropertyString instances.
+ voidsetPropertyAffinity(boolean useAffinity) + +
+           
+ voidsetPropertyInvalidationErrors(Map<String,CheckLevel> propertyInvalidationErrors) + +
+          Sets the list of properties that we report property invalidation errors + for.
+ voidsetPropertyRenaming(PropertyRenamingPolicy propertyRenaming) + +
+           
+ voidsetProtectHiddenSideEffects(boolean enable) + +
+          When enabled, assume that apparently side-effect free code is meaningful.
+ voidsetRecordFunctionInformation(boolean recordFunctionInformation) + +
+           
+ voidsetRemoveAbstractMethods(boolean remove) + +
+           
+ voidsetRemoveClosureAsserts(boolean remove) + +
+           
+ voidsetRemoveDeadCode(boolean removeDeadCode) + +
+           
+ voidsetRemoveTryCatchFinally(boolean removeTryCatchFinally) + +
+           
+ voidsetRemoveUnusedLocalVars(boolean removeUnusedLocalVars) + +
+           
+ voidsetRemoveUnusedPrototypeProperties(boolean enabled) + +
+           
+ voidsetRemoveUnusedPrototypePropertiesInExterns(boolean enabled) + +
+           
+ voidsetRemoveUnusedVariable(CompilerOptions.Reach reach) + +
+          Deprecated. 
+ voidsetRemoveUnusedVariables(CompilerOptions.Reach reach) + +
+          Set the variable removal policy for the compiler.
+ voidsetRemoveUnusedVars(boolean removeUnusedVars) + +
+           
+ voidsetRenamePrefix(String renamePrefix) + +
+           
+ voidsetRenamePrefixNamespace(String renamePrefixNamespace) + +
+           
+ voidsetRenamingPolicy(VariableRenamingPolicy newVariablePolicy, + PropertyRenamingPolicy newPropertyPolicy) + +
+          Sets the variable and property renaming policies for the compiler, + in a way that clears warnings about the renaming policy being + uninitialized from flags.
+ voidsetReplaceIdGenerators(boolean replaceIdGenerators) + +
+           
+ voidsetReplaceStringsConfiguration(String placeholderToken, + List<String> functionDescriptors) + +
+          Sets the functions whose debug strings to replace.
+ voidsetReplaceStringsFunctionDescriptions(List<String> replaceStringsFunctionDescriptions) + +
+           
+ voidsetReplaceStringsPlaceholderToken(String replaceStringsPlaceholderToken) + +
+           
+ voidsetReplaceStringsReservedStrings(Set<String> replaceStringsReservedStrings) + +
+           
+ voidsetReportMissingOverride(CheckLevel level) + +
+          Flags a warning if a property is missing the @override annotation, but it + overrides a base class property.
+ voidsetReportPath(String reportPath) + +
+          Where to save a report of global name usage
+ voidsetReportUnknownTypes(CheckLevel level) + +
+          Flags a warning for every node whose type could not be determined.
+ voidsetReserveRawExports(boolean reserveRawExports) + +
+           
+ voidsetRewriteFunctionExpressions(boolean rewriteFunctionExpressions) + +
+           
+ voidsetRewriteNewDateGoogNow(boolean rewrite) + +
+           
+ voidsetRuntimeTypeCheck(boolean runtimeTypeCheck) + +
+           
+ voidsetRuntimeTypeCheckLogFunction(String runtimeTypeCheckLogFunction) + +
+           
+ voidsetShadowVariables(boolean shadow) + +
+          Should shadow outer scope variable name during renaming.
+ voidsetSkipAllPasses(boolean skipAllPasses) + +
+           
+ voidsetSmartNameRemoval(boolean smartNameRemoval) + +
+           
+ voidsetSourceMapDetailLevel(SourceMap.DetailLevel sourceMapDetailLevel) + +
+           
+ voidsetSourceMapFormat(SourceMap.Format sourceMapFormat) + +
+           
+ voidsetSourceMapLocationMappings(List<SourceMap.LocationMapping> sourceMapLocationMappings) + +
+           
+ voidsetSourceMapOutputPath(String sourceMapOutputPath) + +
+           
+ voidsetSpecializeInitialModule(boolean enabled) + +
+          Specialize the initial module at the cost of later modules
+ voidsetStripNamePrefixes(Set<String> stripNamePrefixes) + +
+           
+ voidsetStripNameSuffixes(Set<String> stripNameSuffixes) + +
+           
+ voidsetStripTypePrefixes(Set<String> stripTypePrefixes) + +
+           
+ voidsetStripTypes(Set<String> stripTypes) + +
+           
+ voidsetSummaryDetailLevel(int summaryDetailLevel) + +
+          Controls how detailed the compilation summary is.
+ voidsetSyntheticBlockEndMarker(String syntheticBlockEndMarker) + +
+           
+ voidsetSyntheticBlockStartMarker(String syntheticBlockStartMarker) + +
+           
+ voidsetTightenTypes(boolean tighten) + +
+          Tightens types based on a global analysis.
+ voidsetTracer(CompilerOptions.TracerMode tracer) + +
+           
+ voidsetTracerMode(CompilerOptions.TracerMode mode) + +
+           
+ voidsetTransformAMDToCJSModules(boolean transformAMDToCJSModules) + +
+          Activates transformation of AMD to CJS modules.
+ voidsetTweakProcessing(CompilerOptions.TweakProcessing tweakProcessing) + +
+          Sets how goog.tweak calls are processed.
+ voidsetTweakReplacements(Map<String,Object> tweakReplacements) + +
+           
+ voidsetTweakToBooleanLiteral(String tweakId, + boolean value) + +
+          Sets the value of the tweak in JS + to a boolean literal.
+ voidsetTweakToDoubleLiteral(String tweakId, + double value) + +
+          Sets the value of the tweak in JS to a + number literal.
+ voidsetTweakToNumberLiteral(String tweakId, + int value) + +
+          Sets the value of the tweak in JS to a + number literal.
+ voidsetTweakToStringLiteral(String tweakId, + String value) + +
+          Sets the value of the tweak in JS to a + String literal.
+ voidsetUnaliasableGlobals(String names) + +
+          A comma separated white-list of global names.
+ voidsetVariableRenaming(VariableRenamingPolicy variableRenaming) + +
+           
+ voidsetWarningLevel(DiagnosticGroup type, + CheckLevel level) + +
+          Configure the given type of warning to the given level.
+ voidsetWarningsGuard(ComposeWarningsGuard warningsGuard) + +
+           
+ booleanshouldColorizeErrorOutput() + +
+           
+ voidskipAllCompilerPasses() + +
+          Skip all possible passes, to make the compiler as fast as possible.
+ + + + + + + +
Methods inherited from class java.lang.Object
equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+ideMode

+
+public boolean ideMode
+
+
Configures the compiler for use as an IDE backend. In this mode: +
    +
  • No optimization passes will run.
  • +
  • The last time custom passes are invoked is + CustomPassExecutionTime.BEFORE_OPTIMIZATIONS
  • +
  • The compiler will always try to process all inputs fully, even + if it encounters errors.
  • +
  • The compiler may record more information than is strictly + needed for codegen.
  • +
+

+

+
+
+
+ +

+messageBundle

+
+public transient MessageBundle messageBundle
+
+
Returns localized replacement for MSG_* variables +

+

+
+
+
+ +

+checkSymbols

+
+public boolean checkSymbols
+
+
Checks that all symbols are defined +

+

+
+
+
+ +

+aggressiveVarCheck

+
+public CheckLevel aggressiveVarCheck
+
+
+
+
+
+ +

+checkSuspiciousCode

+
+public boolean checkSuspiciousCode
+
+
Checks for suspicious statements that have no effect +

+

+
+
+
+ +

+checkControlStructures

+
+public boolean checkControlStructures
+
+
Checks for invalid control structures +

+

+
+
+
+ +

+checkTypes

+
+public boolean checkTypes
+
+
Checks types on expressions +

+

+
+
+
+ +

+reportMissingOverride

+
+public CheckLevel reportMissingOverride
+
+
+
+
+
+ +

+checkRequires

+
+public CheckLevel checkRequires
+
+
Checks for missing goog.require() calls +

+

+
+
+
+ +

+checkProvides

+
+public CheckLevel checkProvides
+
+
+
+
+
+ +

+checkGlobalNamesLevel

+
+public CheckLevel checkGlobalNamesLevel
+
+
+
+
+
+ +

+brokenClosureRequiresLevel

+
+public CheckLevel brokenClosureRequiresLevel
+
+
+
+
+
+ +

+checkGlobalThisLevel

+
+public CheckLevel checkGlobalThisLevel
+
+
+
+
+
+ +

+checkMissingGetCssNameLevel

+
+public CheckLevel checkMissingGetCssNameLevel
+
+
+
+
+
+ +

+checkMissingGetCssNameBlacklist

+
+public String checkMissingGetCssNameBlacklist
+
+
Regex of string literals that may only appear in goog.getCssName arguments. +

+

+
+
+
+ +

+foldConstants

+
+public boolean foldConstants
+
+
Folds constants (e.g. (2 + 3) to 5) +

+

+
+
+
+ +

+deadAssignmentElimination

+
+public boolean deadAssignmentElimination
+
+
Remove assignments to values that can not be referenced +

+

+
+
+
+ +

+inlineConstantVars

+
+public boolean inlineConstantVars
+
+
Inlines constants (symbols that are all CAPS) +

+

+
+
+
+ +

+inlineFunctions

+
+public boolean inlineFunctions
+
+
Inlines short functions +

+

+
+
+
+ +

+inlineLocalFunctions

+
+public boolean inlineLocalFunctions
+
+
Enhanced function inlining +

+

+
+
+
+ +

+crossModuleCodeMotion

+
+public boolean crossModuleCodeMotion
+
+
Move code to a deeper module +

+

+
+
+
+ +

+coalesceVariableNames

+
+public boolean coalesceVariableNames
+
+
Merge two variables together as one. +

+

+
+
+
+ +

+crossModuleMethodMotion

+
+public boolean crossModuleMethodMotion
+
+
Move methds to a deeper module +

+

+
+
+
+ +

+inlineGetters

+
+public boolean inlineGetters
+
+
Inlines trivial getters +

+

+
+
+
+ +

+inlineVariables

+
+public boolean inlineVariables
+
+
Inlines variables +

+

+
+
+
+ +

+flowSensitiveInlineVariables

+
+public boolean flowSensitiveInlineVariables
+
+
+
+
+
+ +

+smartNameRemoval

+
+public boolean smartNameRemoval
+
+
Removes code associated with unused global names +

+

+
+
+
+ +

+removeDeadCode

+
+public boolean removeDeadCode
+
+
Removes code that will never execute +

+

+
+
+
+ +

+checkUnreachableCode

+
+public CheckLevel checkUnreachableCode
+
+
+
+
+
+ +

+checkMissingReturn

+
+public CheckLevel checkMissingReturn
+
+
+
+
+
+ +

+extractPrototypeMemberDeclarations

+
+public boolean extractPrototypeMemberDeclarations
+
+
Extracts common prototype member declarations +

+

+
+
+
+ +

+removeUnusedPrototypeProperties

+
+public boolean removeUnusedPrototypeProperties
+
+
Removes unused member prototypes +

+

+
+
+
+ +

+removeUnusedPrototypePropertiesInExterns

+
+public boolean removeUnusedPrototypePropertiesInExterns
+
+
Tells AnalyzePrototypeProperties it can remove externed props. +

+

+
+
+
+ +

+removeUnusedVars

+
+public boolean removeUnusedVars
+
+
Removes unused variables +

+

+
+
+
+ +

+removeUnusedLocalVars

+
+public boolean removeUnusedLocalVars
+
+
Removes unused variables in local scope. +

+

+
+
+
+ +

+aliasExternals

+
+public boolean aliasExternals
+
+
Adds variable aliases for externals to reduce code size +

+

+
+
+
+ +

+collapseVariableDeclarations

+
+public boolean collapseVariableDeclarations
+
+
Collapses multiple variable declarations into one +

+

+
+
+
+ +

+collapseAnonymousFunctions

+
+public boolean collapseAnonymousFunctions
+
+
Collapses anonymous function declarations into named function + declarations +

+

+
+
+
+ +

+aliasableStrings

+
+public Set<String> aliasableStrings
+
+
If set to a non-empty set, those strings literals will be aliased to a + single global instance per string, to avoid creating more objects than + necessary. +

+

+
+
+
+ +

+aliasStringsBlacklist

+
+public String aliasStringsBlacklist
+
+
A blacklist in the form of a regular expression to block strings that + contains certain words from being aliased. + If the value is the empty string, no words are blacklisted. +

+

+
+
+
+ +

+aliasAllStrings

+
+public boolean aliasAllStrings
+
+
Aliases all string literals to global instances, to avoid creating more + objects than necessary (if true, overrides any set of strings passed in + to aliasableStrings) +

+

+
+
+
+ +

+convertToDottedProperties

+
+public boolean convertToDottedProperties
+
+
Converts quoted property accesses to dot syntax (a['b'] -> a.b) +

+

+
+
+
+ +

+rewriteFunctionExpressions

+
+public boolean rewriteFunctionExpressions
+
+
Reduces the size of common function expressions. +

+

+
+
+
+ +

+optimizeParameters

+
+public boolean optimizeParameters
+
+
Remove unused and constant parameters. +

+

+
+
+
+ +

+optimizeReturns

+
+public boolean optimizeReturns
+
+
Remove unused return values. +

+

+
+
+
+ +

+optimizeCalls

+
+public boolean optimizeCalls
+
+
Remove unused parameters from call sites. +

+

+
+
+
+ +

+optimizeArgumentsArray

+
+public boolean optimizeArgumentsArray
+
+
Provide formal names for elements of arguments array. +

+

+
+
+
+ +

+variableRenaming

+
+public VariableRenamingPolicy variableRenaming
+
+
Controls which variables get renamed. +

+

+
+
+
+ +

+propertyRenaming

+
+public PropertyRenamingPolicy propertyRenaming
+
+
Controls which properties get renamed. +

+

+
+
+
+ +

+labelRenaming

+
+public boolean labelRenaming
+
+
Controls label renaming. +

+

+
+
+
+ +

+reserveRawExports

+
+public boolean reserveRawExports
+
+
Reserve property names on the global this object. +

+

+
+
+
+ +

+generatePseudoNames

+
+public boolean generatePseudoNames
+
+
Generate pseudo names for variables and properties for debugging purposes. +

+

+
+
+
+ +

+renamePrefix

+
+public String renamePrefix
+
+
Specifies a prefix for all globals +

+

+
+
+
+ +

+renamePrefixNamespace

+
+public String renamePrefixNamespace
+
+
Specifies the name of an object that will be used to store all non-extern + globals. +

+

+
+
+
+ +

+aliasKeywords

+
+public boolean aliasKeywords
+
+
Aliases true, false, and null to variables with shorter names. +

+

+
+
+
+ +

+collapseProperties

+
+public boolean collapseProperties
+
+
Flattens multi-level property names (e.g. a$b = x) +

+

+
+
+
+ +

+devirtualizePrototypeMethods

+
+public boolean devirtualizePrototypeMethods
+
+
Devirtualize prototype method by rewriting them to be static calls that + take the this pointer as their first argument +

+

+
+
+
+ +

+computeFunctionSideEffects

+
+public boolean computeFunctionSideEffects
+
+
Use @nosideeffects annotations, function bodies and name graph + to determine if calls have side effects. Requires --check_types. +

+

+
+
+
+ +

+disambiguateProperties

+
+public boolean disambiguateProperties
+
+
Rename properties to disambiguate between unrelated fields based on + type information. +

+

+
+
+
+ +

+ambiguateProperties

+
+public boolean ambiguateProperties
+
+
Rename unrelated properties to the same name to reduce code size. +

+

+
+
+
+ +

+anonymousFunctionNaming

+
+public AnonymousFunctionNamingPolicy anonymousFunctionNaming
+
+
Give anonymous functions names for easier debugging +

+

+
+
+
+ +

+inputVariableMapSerialized

+
+public byte[] inputVariableMapSerialized
+
+
Serialized input variable renaming map. +

+

+
+
+
+ +

+inputPropertyMapSerialized

+
+public byte[] inputPropertyMapSerialized
+
+
Serialized input property renaming map. +

+

+
+
+
+ +

+exportTestFunctions

+
+public boolean exportTestFunctions
+
+
Whether to export test functions. +

+

+
+
+
+ +

+syntheticBlockStartMarker

+
+public String syntheticBlockStartMarker
+
+
+
+
+
+ +

+syntheticBlockEndMarker

+
+public String syntheticBlockEndMarker
+
+
+
+
+
+ +

+locale

+
+public String locale
+
+
Compiling locale +

+

+
+
+
+ +

+markAsCompiled

+
+public boolean markAsCompiled
+
+
Sets the special "COMPILED" value to true +

+

+
+
+
+ +

+removeTryCatchFinally

+
+public boolean removeTryCatchFinally
+
+
Removes try...catch...finally blocks for easier debugging +

+

+
+
+
+ +

+closurePass

+
+public boolean closurePass
+
+
Processes goog.provide() and goog.require() calls +

+

+
+
+
+ +

+jqueryPass

+
+public boolean jqueryPass
+
+
Processes jQuery aliases +

+

+
+
+
+ +

+gatherCssNames

+
+public boolean gatherCssNames
+
+
Gather CSS names (requires closurePass) +

+

+
+
+
+ +

+stripTypes

+
+public Set<String> stripTypes
+
+
Names of types to strip +

+

+
+
+
+ +

+stripNameSuffixes

+
+public Set<String> stripNameSuffixes
+
+
Name suffixes that determine which variables and properties to strip +

+

+
+
+
+ +

+stripNamePrefixes

+
+public Set<String> stripNamePrefixes
+
+
Name prefixes that determine which variables and properties to strip +

+

+
+
+
+ +

+stripTypePrefixes

+
+public Set<String> stripTypePrefixes
+
+
Qualified type name prefixes that determine which types to strip +

+

+
+
+
+ +

+customPasses

+
+public transient com.google.common.collect.Multimap<CustomPassExecutionTime,CompilerPass> customPasses
+
+
Custom passes +

+

+
+
+
+ +

+markNoSideEffectCalls

+
+public boolean markNoSideEffectCalls
+
+
Mark no side effect calls +

+

+
+
+
+ +

+moveFunctionDeclarations

+
+public boolean moveFunctionDeclarations
+
+
Move top level function declarations to the top +

+

+
+
+
+ +

+instrumentationTemplate

+
+public String instrumentationTemplate
+
+
Instrumentation template to use with #recordFunctionInformation +

+

+
+
+
+ +

+recordFunctionInformation

+
+public boolean recordFunctionInformation
+
+
Record function information +

+

+
+
+
+ +

+generateExports

+
+public boolean generateExports
+
+
+
+
+
+ +

+cssRenamingMap

+
+public CssRenamingMap cssRenamingMap
+
+
Map used in the renaming of CSS class names. +

+

+
+
+
+ +

+prettyPrint

+
+public boolean prettyPrint
+
+
Output in pretty indented format +

+

+
+
+
+ +

+lineBreak

+
+public boolean lineBreak
+
+
Line break the output a bit more aggressively +

+

+
+
+
+ +

+preferLineBreakAtEndOfFile

+
+public boolean preferLineBreakAtEndOfFile
+
+
Prefer line breaks at end of file +

+

+
+
+
+ +

+printInputDelimiter

+
+public boolean printInputDelimiter
+
+
Prints a separator comment before each js script +

+

+
+
+
+ +

+inputDelimiter

+
+public String inputDelimiter
+
+
The string to use as the separator for printInputDelimiter +

+

+
+
+
+ +

+errorFormat

+
+public ErrorFormat errorFormat
+
+
+
+
+
+ +

+sourceMapOutputPath

+
+public String sourceMapOutputPath
+
+
The output path for the source map. +

+

+
+
+
+ +

+sourceMapDetailLevel

+
+public SourceMap.DetailLevel sourceMapDetailLevel
+
+
The detail level for the generated source map. +

+

+
+
+
+ +

+sourceMapFormat

+
+public SourceMap.Format sourceMapFormat
+
+
The source map file format +

+

+
+
+
+ +

+sourceMapLocationMappings

+
+public List<SourceMap.LocationMapping> sourceMapLocationMappings
+
+
+
+
+ + + + + + + + +
+Constructor Detail
+ +

+CompilerOptions

+
+public CompilerOptions()
+
+
Initializes compiler options. All options are disabled by default. + + Command-line frontends to the compiler should set these properties + like a builder. +

+

+ + + + + + + + +
+Method Detail
+ +

+setAggressiveVarCheck

+
+public void setAggressiveVarCheck(CheckLevel level)
+
+
Checks for suspicious variable definitions and undefined variables +

+

+
+
+
+
+
+
+
+ +

+setTightenTypes

+
+public void setTightenTypes(boolean tighten)
+
+
Tightens types based on a global analysis. Experimental. +

+

+
+
+
+
+
+
+
+ +

+setReportMissingOverride

+
+public void setReportMissingOverride(CheckLevel level)
+
+
Flags a warning if a property is missing the @override annotation, but it + overrides a base class property. +

+

+
+
+
+
+
+
+
+ +

+setReportUnknownTypes

+
+public void setReportUnknownTypes(CheckLevel level)
+
+
Flags a warning for every node whose type could not be determined. +

+

+
+
+
+
+
+
+
+ +

+setCheckRequires

+
+public void setCheckRequires(CheckLevel level)
+
+
+
+
+
+
+
+
+
+ +

+setCheckProvides

+
+public void setCheckProvides(CheckLevel level)
+
+
Checks for missing goog.provides() calls +

+

+
+
+
+
+
+
+
+ +

+setCheckGlobalNamesLevel

+
+public void setCheckGlobalNamesLevel(CheckLevel level)
+
+
Checks the integrity of references to qualified global names. + (e.g. "a.b") +

+

+
+
+
+
+
+
+
+ +

+setBrokenClosureRequiresLevel

+
+public void setBrokenClosureRequiresLevel(CheckLevel level)
+
+
Sets the check level for bad Closure require calls. +

+

+
+
+
+
+
+
+
+ +

+setCheckGlobalThisLevel

+
+public void setCheckGlobalThisLevel(CheckLevel level)
+
+
Checks for certain uses of the this keyword that are considered + unsafe because they are likely to reference the global this + object unintentionally. + + If this is off, but collapseProperties is on, then the compiler will + usually ignore you and run this check anyways. +

+

+
+
+
+
+
+
+
+ +

+setCheckMissingGetCssNameLevel

+
+public void setCheckMissingGetCssNameLevel(CheckLevel level)
+
+
Checks that certain string literals only appear in strings used as + goog.getCssName arguments. +

+

+
+
+
+
+
+
+
+ +

+setCheckCaja

+
+public void setCheckCaja(boolean check)
+
+
+
+
+
+
+
+
+
+ +

+setCheckUnreachableCode

+
+public void setCheckUnreachableCode(CheckLevel level)
+
+
Checks for unreachable code +

+

+
+
+
+
+
+
+
+ +

+setCheckMissingReturn

+
+public void setCheckMissingReturn(CheckLevel level)
+
+
Checks for missing return statements +

+

+
+
+
+
+
+
+
+ +

+setAliasableGlobals

+
+public void setAliasableGlobals(String names)
+
+
A comma separated white-list of global names. When aliasExternals + is enable, if set to a non-empty string, only externals with these names + will be considered for aliasing. +

+

+
+
+
+
+
+
+
+ +

+setUnaliasableGlobals

+
+public void setUnaliasableGlobals(String names)
+
+
A comma separated white-list of global names. When aliasExternals + is enable, these global names will not be aliased. +

+

+
+
+
+
+
+
+
+ +

+setCollapseObjectLiterals

+
+public void setCollapseObjectLiterals(boolean enabled)
+
+
+
+
+
+
+
+
+
+ +

+setSpecializeInitialModule

+
+public void setSpecializeInitialModule(boolean enabled)
+
+
Specialize the initial module at the cost of later modules +

+

+
+
+
+
+
+
+
+ +

+setIgnoreCajaProperties

+
+public void setIgnoreCajaProperties(boolean enabled)
+
+
Add code to skip properties that Caja adds to Object.prototype +

+

+
+
+
+
+
+
+
+ +

+setAppNameStr

+
+public void setAppNameStr(String appNameStr)
+
+
App identifier string for use by the instrumentation template's + app_name_setter. @see #instrumentationTemplate +

+

+
+
+
+
+
+
+
+ +

+setReportPath

+
+public void setReportPath(String reportPath)
+
+
Where to save a report of global name usage +

+

+
+
+
+
+
+
+
+ +

+getTracerMode

+
+public CompilerOptions.TracerMode getTracerMode()
+
+
+
+
+
+
+
+
+
+ +

+setTracerMode

+
+public void setTracerMode(CompilerOptions.TracerMode mode)
+
+
+
+
+
+
+
+
+
+ +

+setNameReferenceReportPath

+
+public void setNameReferenceReportPath(String filePath)
+
+
Where to save a cross-reference report from the name reference graph +

+

+
+
+
+
+
+
+
+ +

+setNameReferenceGraphPath

+
+public void setNameReferenceGraphPath(String filePath)
+
+
Where to save the name reference graph +

+

+
+
+
+
+
+
+
+ +

+setProtectHiddenSideEffects

+
+public void setProtectHiddenSideEffects(boolean enable)
+
+
When enabled, assume that apparently side-effect free code is meaningful. +

+

+
+
+
+
+
+
+
+ +

+getDefineReplacements

+
+public Map<String,Node> getDefineReplacements()
+
+
Returns the map of define replacements. +

+

+
+
+
+
+
+
+
+ +

+getTweakReplacements

+
+public Map<String,Node> getTweakReplacements()
+
+
Returns the map of tweak replacements. +

+

+
+
+
+
+
+
+
+ +

+setDefineToBooleanLiteral

+
+public void setDefineToBooleanLiteral(String defineName,
+                                      boolean value)
+
+
Sets the value of the @define variable in JS + to a boolean literal. +

+

+
+
+
+
+
+
+
+ +

+setDefineToStringLiteral

+
+public void setDefineToStringLiteral(String defineName,
+                                     String value)
+
+
Sets the value of the @define variable in JS to a + String literal. +

+

+
+
+
+
+
+
+
+ +

+setDefineToNumberLiteral

+
+public void setDefineToNumberLiteral(String defineName,
+                                     int value)
+
+
Sets the value of the @define variable in JS to a + number literal. +

+

+
+
+
+
+
+
+
+ +

+setDefineToDoubleLiteral

+
+public void setDefineToDoubleLiteral(String defineName,
+                                     double value)
+
+
Sets the value of the @define variable in JS to a + number literal. +

+

+
+
+
+
+
+
+
+ +

+setTweakToBooleanLiteral

+
+public void setTweakToBooleanLiteral(String tweakId,
+                                     boolean value)
+
+
Sets the value of the tweak in JS + to a boolean literal. +

+

+
+
+
+
+
+
+
+ +

+setTweakToStringLiteral

+
+public void setTweakToStringLiteral(String tweakId,
+                                    String value)
+
+
Sets the value of the tweak in JS to a + String literal. +

+

+
+
+
+
+
+
+
+ +

+setTweakToNumberLiteral

+
+public void setTweakToNumberLiteral(String tweakId,
+                                    int value)
+
+
Sets the value of the tweak in JS to a + number literal. +

+

+
+
+
+
+
+
+
+ +

+setTweakToDoubleLiteral

+
+public void setTweakToDoubleLiteral(String tweakId,
+                                    double value)
+
+
Sets the value of the tweak in JS to a + number literal. +

+

+
+
+
+
+
+
+
+ +

+skipAllCompilerPasses

+
+public void skipAllCompilerPasses()
+
+
Skip all possible passes, to make the compiler as fast as possible. +

+

+
+
+
+
+
+
+
+ +

+setWarningLevel

+
+public void setWarningLevel(DiagnosticGroup type,
+                            CheckLevel level)
+
+
Configure the given type of warning to the given level. +

+

+
+
+
+
+
+
+
+ +

+resetWarningsGuard

+
+public void resetWarningsGuard()
+
+
Reset the warnings guard. +

+

+
+
+
+
+
+
+
+ +

+addWarningsGuard

+
+public void addWarningsGuard(WarningsGuard guard)
+
+
Add a guard to the set of warnings guards. +

+

+
+
+
+
+
+
+
+ +

+setRenamingPolicy

+
+public void setRenamingPolicy(VariableRenamingPolicy newVariablePolicy,
+                              PropertyRenamingPolicy newPropertyPolicy)
+
+
Sets the variable and property renaming policies for the compiler, + in a way that clears warnings about the renaming policy being + uninitialized from flags. +

+

+
+
+
+
+
+
+
+ +

+setPropertyAffinity

+
+public void setPropertyAffinity(boolean useAffinity)
+
+
+
+
+
+
+
+
+
+ +

+setShadowVariables

+
+public void setShadowVariables(boolean shadow)
+
+
Should shadow outer scope variable name during renaming. +

+

+
+
+
+
+
+
+
+ +

+setCollapsePropertiesOnExternTypes

+
+public void setCollapsePropertiesOnExternTypes(boolean collapse)
+
+
If true, flattens multi-level property names on extern types + (e.g. String$f = x). This should only be used with the typed version of + the externs files. +

+

+
+
+
+
+
+
+
+ +

+setProcessObjectPropertyString

+
+public void setProcessObjectPropertyString(boolean process)
+
+
If true, process goog.testing.ObjectPropertyString instances. +

+

+
+
+
+
+
+
+
+ +

+setReplaceIdGenerators

+
+public void setReplaceIdGenerators(boolean replaceIdGenerators)
+
+
+
+
+
+
Parameters:
replaceIdGenerators - the replaceIdGenerators to set
+
+
+
+ +

+setIdGenerators

+
+public void setIdGenerators(Set<String> idGenerators)
+
+
Sets the id generators to replace. +

+

+
+
+
+
+
+
+
+ +

+setInlineFunctions

+
+public void setInlineFunctions(CompilerOptions.Reach reach)
+
+
Set the function inlining policy for the compiler. +

+

+
+
+
+
+
+
+
+ +

+setInlineVariables

+
+public void setInlineVariables(CompilerOptions.Reach reach)
+
+
Set the variable inlining policy for the compiler. +

+

+
+
+
+
+
+
+
+ +

+setRemoveUnusedVariable

+
+@Deprecated
+public void setRemoveUnusedVariable(CompilerOptions.Reach reach)
+
+
Deprecated.  +

+

Set the variable removal policy for the compiler. +

+

+
+
+
+
+
+
+
+ +

+setRemoveUnusedVariables

+
+public void setRemoveUnusedVariables(CompilerOptions.Reach reach)
+
+
Set the variable removal policy for the compiler. +

+

+
+
+
+
+
+
+
+ +

+setReplaceStringsConfiguration

+
+public void setReplaceStringsConfiguration(String placeholderToken,
+                                           List<String> functionDescriptors)
+
+
Sets the functions whose debug strings to replace. +

+

+
+
+
+
+
+
+
+ +

+setRewriteNewDateGoogNow

+
+public void setRewriteNewDateGoogNow(boolean rewrite)
+
+
+
+
+
+
+
+
+
+ +

+setRemoveAbstractMethods

+
+public void setRemoveAbstractMethods(boolean remove)
+
+
+
+
+
+
+
+
+
+ +

+setRemoveClosureAsserts

+
+public void setRemoveClosureAsserts(boolean remove)
+
+
+
+
+
+
+
+
+
+ +

+setNameAnonymousFunctionsOnly

+
+public void setNameAnonymousFunctionsOnly(boolean value)
+
+
If true, name anonymous functions only. All other passes will be skipped. +

+

+
+
+
+
+
+
+
+ +

+setColorizeErrorOutput

+
+public void setColorizeErrorOutput(boolean colorizeErrorOutput)
+
+
+
+
+
+
+
+
+
+ +

+shouldColorizeErrorOutput

+
+public boolean shouldColorizeErrorOutput()
+
+
+
+
+
+
+
+
+
+ +

+setChainCalls

+
+public void setChainCalls(boolean value)
+
+
If true, chain calls to functions that return this. +

+

+
+
+
+
+
+
+
+ +

+setAcceptConstKeyword

+
+public void setAcceptConstKeyword(boolean value)
+
+
If true, accept `const' keyword. +

+

+
+
+
+
+
+
+
+ +

+enableRuntimeTypeCheck

+
+public void enableRuntimeTypeCheck(String logFunction)
+
+
Enable runtime type checking, which adds JS type assertions for debugging. +

+

+
+
+
+
Parameters:
logFunction - A JS function to be used for logging runtime type + assertion failures.
+
+
+
+ +

+disableRuntimeTypeCheck

+
+public void disableRuntimeTypeCheck()
+
+
+
+
+
+
+
+
+
+ +

+setGenerateExports

+
+public void setGenerateExports(boolean generateExports)
+
+
+
+
+
+
+
+
+
+ +

+setCodingConvention

+
+public void setCodingConvention(CodingConvention codingConvention)
+
+
+
+
+
+
+
+
+
+ +

+getCodingConvention

+
+public CodingConvention getCodingConvention()
+
+
+
+
+
+
+
+
+
+ +

+setDependencyOptions

+
+public void setDependencyOptions(DependencyOptions options)
+
+
Sets dependency options. See the DependencyOptions class for more info. + This supercedes manageClosureDependencies. +

+

+
+
+
+
+
+
+
+ +

+setManageClosureDependencies

+
+public void setManageClosureDependencies(boolean newVal)
+
+
Sort inputs by their goog.provide/goog.require calls, and prune inputs + whose symbols are not required. +

+

+
+
+
+
+
+
+
+ +

+setManageClosureDependencies

+
+public void setManageClosureDependencies(List<String> entryPoints)
+
+
Sort inputs by their goog.provide/goog.require calls. +

+

+
+
+
+
Parameters:
entryPoints - Entry points to the program. Must be goog.provide'd + symbols. Any goog.provide'd symbols that are not a transitive + dependency of the entry points will be deleted. + Files without goog.provides, and their dependencies, + will always be left in.
+
+
+
+ +

+setSummaryDetailLevel

+
+public void setSummaryDetailLevel(int summaryDetailLevel)
+
+
Controls how detailed the compilation summary is. Values: + 0 (never print summary), 1 (print summary only if there are + errors or warnings), 2 (print summary if type checking is on, + see --check_types), 3 (always print summary). The default level + is 1 +

+

+
+
+
+
+
+
+
+ +

+enableExternExports

+
+@Deprecated
+public void enableExternExports(boolean enabled)
+
+
Deprecated. replaced by setExternExports(boolean) +

+

+
+
+
+
+
+
+
+ +

+setExtraAnnotationNames

+
+public void setExtraAnnotationNames(Set<String> extraAnnotationNames)
+
+
+
+
+
+
+
+
+
+ +

+isExternExportsEnabled

+
+public boolean isExternExportsEnabled()
+
+
+
+
+
+
+
+
+
+ +

+setOutputCharset

+
+public void setOutputCharset(String charsetName)
+
+
Sets the output charset by name. +

+

+
+
+
+
+
+
+
+ +

+setTweakProcessing

+
+public void setTweakProcessing(CompilerOptions.TweakProcessing tweakProcessing)
+
+
Sets how goog.tweak calls are processed. +

+

+
+
+
+
+
+
+
+ +

+getTweakProcessing

+
+public CompilerOptions.TweakProcessing getTweakProcessing()
+
+
+
+
+
+
+
+
+
+ +

+setLanguageIn

+
+public void setLanguageIn(CompilerOptions.LanguageMode languageIn)
+
+
Sets how goog.tweak calls are processed. +

+

+
+
+
+
+
+
+
+ +

+getLanguageIn

+
+public CompilerOptions.LanguageMode getLanguageIn()
+
+
+
+
+
+
+
+
+
+ +

+getLanguageOut

+
+public CompilerOptions.LanguageMode getLanguageOut()
+
+
+
+
+
+
+
+
+
+ +

+setLooseTypes

+
+public void setLooseTypes(boolean looseTypes)
+
+
Whether to include "undefined" in the default types. + For example: + "{Object}" is normally "Object|null" becomes "Object|null|undefined" + "{?string}" is normally "string|null" becomes "string|null|undefined" + In either case "!" annotated types excluded both null and undefined. +

+

+
+
+
+
+
+
+
+ +

+clone

+
+public Object clone()
+             throws CloneNotSupportedException
+
+
+
Overrides:
clone in class Object
+
+
+ +
Throws: +
CloneNotSupportedException
+
+
+
+ +

+setAliasTransformationHandler

+
+public void setAliasTransformationHandler(CompilerOptions.AliasTransformationHandler changes)
+
+
+
+
+
+
+
+
+
+ +

+getAliasTransformationHandler

+
+public CompilerOptions.AliasTransformationHandler getAliasTransformationHandler()
+
+
+
+
+
+
+
+
+
+ +

+setErrorHandler

+
+public void setErrorHandler(ErrorHandler handler)
+
+
Set a custom handler for warnings and errors. + + This is mostly used for piping the warnings and errors to + a file behind the scenes. + + If you want to filter warnings and errors, you should use a WarningsGuard. + + If you want to change how warnings and errors are reported to the user, + you should set a ErrorManager on the Compiler. An ErrorManager is + intended to summarize the errors for a single compile job. +

+

+
+
+
+
+
+
+
+ +

+setInferTypes

+
+public void setInferTypes(boolean enable)
+
+
If true, enables type inference. If checkTypes is enabled, this flag has + no effect. +

+

+
+
+
+
+
+
+
+ +

+getInferTypes

+
+public boolean getInferTypes()
+
+
Gets the inferTypes flag. Note that if checkTypes is enabled, this flag + is ignored when configuring the compiler. +

+

+
+
+
+
+
+
+
+ +

+assumeStrictThis

+
+public boolean assumeStrictThis()
+
+
+
+
+
+ +
Returns:
Whether assumeStrictThis is set.
+
+
+
+ +

+setAssumeStrictThis

+
+public void setAssumeStrictThis(boolean enable)
+
+
If true, enables enables additional optimizations. +

+

+
+
+
+
+
+
+
+ +

+assumeClosuresOnlyCaptureReferences

+
+public boolean assumeClosuresOnlyCaptureReferences()
+
+
+
+
+
+ +
Returns:
Whether assumeClosuresOnlyCaptureReferences is set.
+
+
+
+ +

+setAssumeClosuresOnlyCaptureReferences

+
+public void setAssumeClosuresOnlyCaptureReferences(boolean enable)
+
+
Whether to assume closures capture only what they reference. This allows + more aggressive function inlining. +

+

+
+
+
+
+
+
+
+ +

+setPropertyInvalidationErrors

+
+public void setPropertyInvalidationErrors(Map<String,CheckLevel> propertyInvalidationErrors)
+
+
Sets the list of properties that we report property invalidation errors + for. +

+

+
+
+
+
+
+
+
+ +

+setLanguageOut

+
+public void setLanguageOut(CompilerOptions.LanguageMode languageOut)
+
+
+
+
+
+
+
+
+
+ +

+setIdeMode

+
+public void setIdeMode(boolean ideMode)
+
+
+
+
+
+
+
+
+
+ +

+setSkipAllPasses

+
+public void setSkipAllPasses(boolean skipAllPasses)
+
+
+
+
+
+
+
+
+
+ +

+setDevMode

+
+public void setDevMode(com.google.javascript.jscomp.CompilerOptions.DevMode devMode)
+
+
+
+
+
+
+
+
+
+ +

+setMessageBundle

+
+public void setMessageBundle(MessageBundle messageBundle)
+
+
+
+
+
+
+
+
+
+ +

+setCheckSymbols

+
+public void setCheckSymbols(boolean checkSymbols)
+
+
+
+
+
+
+
+
+
+ +

+setCheckSuspiciousCode

+
+public void setCheckSuspiciousCode(boolean checkSuspiciousCode)
+
+
+
+
+
+
+
+
+
+ +

+setCheckControlStructures

+
+public void setCheckControlStructures(boolean checkControlStructures)
+
+
+
+
+
+
+
+
+
+ +

+setCheckTypes

+
+public void setCheckTypes(boolean checkTypes)
+
+
+
+
+
+
+
+
+
+ +

+setCheckMissingGetCssNameBlacklist

+
+public void setCheckMissingGetCssNameBlacklist(String blackList)
+
+
+
+
+
+
+
+
+
+ +

+setFoldConstants

+
+public void setFoldConstants(boolean foldConstants)
+
+
+
+
+
+
+
+
+
+ +

+setDeadAssignmentElimination

+
+public void setDeadAssignmentElimination(boolean deadAssignmentElimination)
+
+
+
+
+
+
+
+
+
+ +

+setInlineConstantVars

+
+public void setInlineConstantVars(boolean inlineConstantVars)
+
+
+
+
+
+
+
+
+
+ +

+setInlineFunctions

+
+public void setInlineFunctions(boolean inlineFunctions)
+
+
+
+
+
+
+
+
+
+ +

+setInlineLocalFunctions

+
+public void setInlineLocalFunctions(boolean inlineLocalFunctions)
+
+
+
+
+
+
+
+
+
+ +

+setCrossModuleCodeMotion

+
+public void setCrossModuleCodeMotion(boolean crossModuleCodeMotion)
+
+
+
+
+
+
+
+
+
+ +

+setCoalesceVariableNames

+
+public void setCoalesceVariableNames(boolean coalesceVariableNames)
+
+
+
+
+
+
+
+
+
+ +

+setCrossModuleMethodMotion

+
+public void setCrossModuleMethodMotion(boolean crossModuleMethodMotion)
+
+
+
+
+
+
+
+
+
+ +

+setInlineGetters

+
+public void setInlineGetters(boolean inlineGetters)
+
+
+
+
+
+
+
+
+
+ +

+setInlineVariables

+
+public void setInlineVariables(boolean inlineVariables)
+
+
+
+
+
+
+
+
+
+ +

+setInlineLocalVariables

+
+public void setInlineLocalVariables(boolean inlineLocalVariables)
+
+
+
+
+
+
+
+
+
+ +

+setFlowSensitiveInlineVariables

+
+public void setFlowSensitiveInlineVariables(boolean enabled)
+
+
+
+
+
+
+
+
+
+ +

+setSmartNameRemoval

+
+public void setSmartNameRemoval(boolean smartNameRemoval)
+
+
+
+
+
+
+
+
+
+ +

+setRemoveDeadCode

+
+public void setRemoveDeadCode(boolean removeDeadCode)
+
+
+
+
+
+
+
+
+
+ +

+setExtractPrototypeMemberDeclarations

+
+public void setExtractPrototypeMemberDeclarations(boolean enabled)
+
+
+
+
+
+
+
+
+
+ +

+setRemoveUnusedPrototypeProperties

+
+public void setRemoveUnusedPrototypeProperties(boolean enabled)
+
+
+
+
+
+
+
+
+
+ +

+setRemoveUnusedPrototypePropertiesInExterns

+
+public void setRemoveUnusedPrototypePropertiesInExterns(boolean enabled)
+
+
+
+
+
+
+
+
+
+ +

+setRemoveUnusedVars

+
+public void setRemoveUnusedVars(boolean removeUnusedVars)
+
+
+
+
+
+
+
+
+
+ +

+setRemoveUnusedLocalVars

+
+public void setRemoveUnusedLocalVars(boolean removeUnusedLocalVars)
+
+
+
+
+
+
+
+
+
+ +

+setAliasExternals

+
+public void setAliasExternals(boolean aliasExternals)
+
+
+
+
+
+
+
+
+
+ +

+setCollapseVariableDeclarations

+
+public void setCollapseVariableDeclarations(boolean enabled)
+
+
+
+
+
+
+
+
+
+ +

+setGroupVariableDeclarations

+
+public void setGroupVariableDeclarations(boolean enabled)
+
+
+
+
+
+
+
+
+
+ +

+setCollapseAnonymousFunctions

+
+public void setCollapseAnonymousFunctions(boolean enabled)
+
+
+
+
+
+
+
+
+
+ +

+setAliasableStrings

+
+public void setAliasableStrings(Set<String> aliasableStrings)
+
+
+
+
+
+
+
+
+
+ +

+setAliasStringsBlacklist

+
+public void setAliasStringsBlacklist(String aliasStringsBlacklist)
+
+
+
+
+
+
+
+
+
+ +

+setAliasAllStrings

+
+public void setAliasAllStrings(boolean aliasAllStrings)
+
+
+
+
+
+
+
+
+
+ +

+setOutputJsStringUsage

+
+public void setOutputJsStringUsage(boolean outputJsStringUsage)
+
+
+
+
+
+
+
+
+
+ +

+setConvertToDottedProperties

+
+public void setConvertToDottedProperties(boolean convertToDottedProperties)
+
+
+
+
+
+
+
+
+
+ +

+setRewriteFunctionExpressions

+
+public void setRewriteFunctionExpressions(boolean rewriteFunctionExpressions)
+
+
+
+
+
+
+
+
+
+ +

+setOptimizeParameters

+
+public void setOptimizeParameters(boolean optimizeParameters)
+
+
+
+
+
+
+
+
+
+ +

+setOptimizeReturns

+
+public void setOptimizeReturns(boolean optimizeReturns)
+
+
+
+
+
+
+
+
+
+ +

+setOptimizeCalls

+
+public void setOptimizeCalls(boolean optimizeCalls)
+
+
+
+
+
+
+
+
+
+ +

+setOptimizeArgumentsArray

+
+public void setOptimizeArgumentsArray(boolean optimizeArgumentsArray)
+
+
+
+
+
+
+
+
+
+ +

+setVariableRenaming

+
+public void setVariableRenaming(VariableRenamingPolicy variableRenaming)
+
+
+
+
+
+
+
+
+
+ +

+setPropertyRenaming

+
+public void setPropertyRenaming(PropertyRenamingPolicy propertyRenaming)
+
+
+
+
+
+
+
+
+
+ +

+setLabelRenaming

+
+public void setLabelRenaming(boolean labelRenaming)
+
+
+
+
+
+
+
+
+
+ +

+setReserveRawExports

+
+public void setReserveRawExports(boolean reserveRawExports)
+
+
+
+
+
+
+
+
+
+ +

+setGeneratePseudoNames

+
+public void setGeneratePseudoNames(boolean generatePseudoNames)
+
+
+
+
+
+
+
+
+
+ +

+setRenamePrefix

+
+public void setRenamePrefix(String renamePrefix)
+
+
+
+
+
+
+
+
+
+ +

+setRenamePrefixNamespace

+
+public void setRenamePrefixNamespace(String renamePrefixNamespace)
+
+
+
+
+
+
+
+
+
+ +

+setAliasKeywords

+
+public void setAliasKeywords(boolean aliasKeywords)
+
+
+
+
+
+
+
+
+
+ +

+setCollapseProperties

+
+public void setCollapseProperties(boolean collapseProperties)
+
+
+
+
+
+
+
+
+
+ +

+setDevirtualizePrototypeMethods

+
+public void setDevirtualizePrototypeMethods(boolean devirtualizePrototypeMethods)
+
+
+
+
+
+
+
+
+
+ +

+setComputeFunctionSideEffects

+
+public void setComputeFunctionSideEffects(boolean computeFunctionSideEffects)
+
+
+
+
+
+
+
+
+
+ +

+setDebugFunctionSideEffectsPath

+
+public void setDebugFunctionSideEffectsPath(String debugFunctionSideEffectsPath)
+
+
+
+
+
+
+
+
+
+ +

+setDisambiguateProperties

+
+public void setDisambiguateProperties(boolean disambiguateProperties)
+
+
+
+
+
+
+
+
+
+ +

+setAmbiguateProperties

+
+public void setAmbiguateProperties(boolean ambiguateProperties)
+
+
+
+
+
+
+
+
+
+ +

+setAnonymousFunctionNaming

+
+public void setAnonymousFunctionNaming(AnonymousFunctionNamingPolicy anonymousFunctionNaming)
+
+
+
+
+
+
+
+
+
+ +

+setInputVariableMapSerialized

+
+public void setInputVariableMapSerialized(byte[] inputVariableMapSerialized)
+
+
+
+
+
+
+
+
+
+ +

+setInputPropertyMapSerialized

+
+public void setInputPropertyMapSerialized(byte[] inputPropertyMapSerialized)
+
+
+
+
+
+
+
+
+
+ +

+setExportTestFunctions

+
+public void setExportTestFunctions(boolean exportTestFunctions)
+
+
+
+
+
+
+
+
+
+ +

+setRuntimeTypeCheck

+
+public void setRuntimeTypeCheck(boolean runtimeTypeCheck)
+
+
+
+
+
+
+
+
+
+ +

+setRuntimeTypeCheckLogFunction

+
+public void setRuntimeTypeCheckLogFunction(String runtimeTypeCheckLogFunction)
+
+
+
+
+
+
+
+
+
+ +

+setSyntheticBlockStartMarker

+
+public void setSyntheticBlockStartMarker(String syntheticBlockStartMarker)
+
+
+
+
+
+
+
+
+
+ +

+setSyntheticBlockEndMarker

+
+public void setSyntheticBlockEndMarker(String syntheticBlockEndMarker)
+
+
+
+
+
+
+
+
+
+ +

+setLocale

+
+public void setLocale(String locale)
+
+
+
+
+
+
+
+
+
+ +

+setMarkAsCompiled

+
+public void setMarkAsCompiled(boolean markAsCompiled)
+
+
+
+
+
+
+
+
+
+ +

+setRemoveTryCatchFinally

+
+public void setRemoveTryCatchFinally(boolean removeTryCatchFinally)
+
+
+
+
+
+
+
+
+
+ +

+setClosurePass

+
+public void setClosurePass(boolean closurePass)
+
+
+
+
+
+
+
+
+
+ +

+setGatherCssNames

+
+public void setGatherCssNames(boolean gatherCssNames)
+
+
+
+
+
+
+
+
+
+ +

+setStripTypes

+
+public void setStripTypes(Set<String> stripTypes)
+
+
+
+
+
+
+
+
+
+ +

+setStripNameSuffixes

+
+public void setStripNameSuffixes(Set<String> stripNameSuffixes)
+
+
+
+
+
+
+
+
+
+ +

+setStripNamePrefixes

+
+public void setStripNamePrefixes(Set<String> stripNamePrefixes)
+
+
+
+
+
+
+
+
+
+ +

+setStripTypePrefixes

+
+public void setStripTypePrefixes(Set<String> stripTypePrefixes)
+
+
+
+
+
+
+
+
+
+ +

+setCustomPasses

+
+public void setCustomPasses(com.google.common.collect.Multimap<CustomPassExecutionTime,CompilerPass> customPasses)
+
+
+
+
+
+
+
+
+
+ +

+setMarkNoSideEffectCalls

+
+public void setMarkNoSideEffectCalls(boolean markNoSideEffectCalls)
+
+
+
+
+
+
+
+
+
+ +

+setDefineReplacements

+
+public void setDefineReplacements(Map<String,Object> defineReplacements)
+
+
+
+
+
+
+
+
+
+ +

+setTweakReplacements

+
+public void setTweakReplacements(Map<String,Object> tweakReplacements)
+
+
+
+
+
+
+
+
+
+ +

+setMoveFunctionDeclarations

+
+public void setMoveFunctionDeclarations(boolean moveFunctionDeclarations)
+
+
+
+
+
+
+
+
+
+ +

+setInstrumentationTemplate

+
+public void setInstrumentationTemplate(String instrumentationTemplate)
+
+
+
+
+
+
+
+
+
+ +

+setRecordFunctionInformation

+
+public void setRecordFunctionInformation(boolean recordFunctionInformation)
+
+
+
+
+
+
+
+
+
+ +

+setCssRenamingMap

+
+public void setCssRenamingMap(CssRenamingMap cssRenamingMap)
+
+
+
+
+
+
+
+
+
+ +

+setReplaceStringsFunctionDescriptions

+
+public void setReplaceStringsFunctionDescriptions(List<String> replaceStringsFunctionDescriptions)
+
+
+
+
+
+
+
+
+
+ +

+setReplaceStringsPlaceholderToken

+
+public void setReplaceStringsPlaceholderToken(String replaceStringsPlaceholderToken)
+
+
+
+
+
+
+
+
+
+ +

+setReplaceStringsReservedStrings

+
+public void setReplaceStringsReservedStrings(Set<String> replaceStringsReservedStrings)
+
+
+
+
+
+
+
+
+
+ +

+setPrettyPrint

+
+public void setPrettyPrint(boolean prettyPrint)
+
+
+
+
+
+
+
+
+
+ +

+setLineBreak

+
+public void setLineBreak(boolean lineBreak)
+
+
+
+
+
+
+
+
+
+ +

+setPreferLineBreakAtEndOfFile

+
+public void setPreferLineBreakAtEndOfFile(boolean lineBreakAtEnd)
+
+
+
+
+
+
+
+
+
+ +

+setPrintInputDelimiter

+
+public void setPrintInputDelimiter(boolean printInputDelimiter)
+
+
+
+
+
+
+
+
+
+ +

+setInputDelimiter

+
+public void setInputDelimiter(String inputDelimiter)
+
+
+
+
+
+
+
+
+
+ +

+setTracer

+
+public void setTracer(CompilerOptions.TracerMode tracer)
+
+
+
+
+
+
+
+
+
+ +

+setErrorFormat

+
+public void setErrorFormat(ErrorFormat errorFormat)
+
+
+
+
+
+
+
+
+
+ +

+setWarningsGuard

+
+public void setWarningsGuard(ComposeWarningsGuard warningsGuard)
+
+
+
+
+
+
+
+
+
+ +

+setLineLengthThreshold

+
+public void setLineLengthThreshold(int lineLengthThreshold)
+
+
+
+
+
+
+
+
+
+ +

+setExternExports

+
+public void setExternExports(boolean externExports)
+
+
+
+
+
+
+
+
+
+ +

+setExternExportsPath

+
+public void setExternExportsPath(String externExportsPath)
+
+
+
+
+
+
+
+
+
+ +

+setSourceMapOutputPath

+
+public void setSourceMapOutputPath(String sourceMapOutputPath)
+
+
+
+
+
+
+
+
+
+ +

+setSourceMapDetailLevel

+
+public void setSourceMapDetailLevel(SourceMap.DetailLevel sourceMapDetailLevel)
+
+
+
+
+
+
+
+
+
+ +

+setSourceMapFormat

+
+public void setSourceMapFormat(SourceMap.Format sourceMapFormat)
+
+
+
+
+
+
+
+
+
+ +

+setSourceMapLocationMappings

+
+public void setSourceMapLocationMappings(List<SourceMap.LocationMapping> sourceMapLocationMappings)
+
+
+
+
+
+
+
+
+
+ +

+setTransformAMDToCJSModules

+
+public void setTransformAMDToCJSModules(boolean transformAMDToCJSModules)
+
+
Activates transformation of AMD to CJS modules. +

+

+
+
+
+
+
+
+
+ +

+setProcessCommonJSModules

+
+public void setProcessCommonJSModules(boolean processCommonJSModules)
+
+
Rewrites CommonJS modulee so that modules can be concatenated together, + by renaming all globals to avoid conflicting with other modules. +

+

+
+
+
+
+
+
+
+ +

+setCommonJSModulePathPrefix

+
+public void setCommonJSModulePathPrefix(String commonJSModulePathPrefix)
+
+
Sets a path prefix for Common JS modules. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerPass.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerPass.html new file mode 100644 index 0000000..11768fe --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CompilerPass.html @@ -0,0 +1,240 @@ + + + + + +CompilerPass (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface CompilerPass

+
+
All Known Subinterfaces:
HotSwapCompilerPass
+
+
+
All Known Implementing Classes:
AstValidator, CallGraph, FieldCleanupPass, ObjectPropertyStringPreprocess, ProcessCommonJSModules, TypeCheck
+
+
+
+
public interface CompilerPass
+ + +

+

Interface for classes that can compile JS.

+ +

Class has single function "process", which is passed + the root node of the parsed JS tree, as well as the + root node of the external JS tree (used to provide a public API + and prevent renaming of system functions).

+ +

Use this class to support testing with BaseCompilerTest

+

+ +

+


+ +

+ + + + + + + + + + + + +
+Method Summary
+ voidprocess(Node externs, + Node root) + +
+          Process the JS with root node root.
+  +

+ + + + + + + + +
+Method Detail
+ +

+process

+
+void process(Node externs,
+             Node root)
+
+
Process the JS with root node root. + Can modify the contents of each Node tree +

+

+
Parameters:
externs - Top of external JS tree
root - Top of JS tree
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ComposeWarningsGuard.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ComposeWarningsGuard.html new file mode 100644 index 0000000..6d61740 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ComposeWarningsGuard.html @@ -0,0 +1,410 @@ + + + + + +ComposeWarningsGuard (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class ComposeWarningsGuard

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.WarningsGuard
+      extended by com.google.javascript.jscomp.ComposeWarningsGuard
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class ComposeWarningsGuard
extends WarningsGuard
+ + +

+WarningsGuard that represents just a chain of other guards. For example we + could have following chain + 1) all warnings outside of /foo/ should be suppressed + 2) errors with key JSC_BAR should be marked as warning + 3) the rest should be reported as error + + This class is designed for such behaviour. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.jscomp.WarningsGuard
WarningsGuard.Priority
+  + + + + + + + + + + + + + + +
+Constructor Summary
ComposeWarningsGuard(List<WarningsGuard> guards) + +
+           
ComposeWarningsGuard(WarningsGuard... guards) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleandisables(DiagnosticGroup group) + +
+          Returns whether all warnings in the given diagnostic group will be + filtered out.
+ booleanenables(DiagnosticGroup group) + +
+          Determines whether this guard will "elevate" the status of any disabled + diagnostic type in the group to a warning or an error.
+ CheckLevellevel(JSError error) + +
+          Returns a new check level for a given error.
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.WarningsGuard
getPriority
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+ComposeWarningsGuard

+
+public ComposeWarningsGuard(List<WarningsGuard> guards)
+
+
+
+ +

+ComposeWarningsGuard

+
+public ComposeWarningsGuard(WarningsGuard... guards)
+
+
+ + + + + + + + +
+Method Detail
+ +

+level

+
+public CheckLevel level(JSError error)
+
+
Description copied from class: WarningsGuard
+
Returns a new check level for a given error. OFF - suppress it, ERROR - + report as error. null means that this guard does not know what to do + with the error. Null is extremely helpful when you have a chain of + guards. If current guard returns null, then the next in the chain should + process it. +

+

+
Specified by:
level in class WarningsGuard
+
+
+
Parameters:
error - a reported error. +
Returns:
what level given error should have.
+
+
+
+ +

+disables

+
+public boolean disables(DiagnosticGroup group)
+
+
Description copied from class: WarningsGuard
+
Returns whether all warnings in the given diagnostic group will be + filtered out. Used to determine which passes to skip. +

+

+
Overrides:
disables in class WarningsGuard
+
+
+
Parameters:
group - A group of DiagnosticTypes. +
Returns:
Whether all warnings of these types are disabled by this guard.
+
+
+
+ +

+enables

+
+public boolean enables(DiagnosticGroup group)
+
+
Determines whether this guard will "elevate" the status of any disabled + diagnostic type in the group to a warning or an error. +

+

+
Overrides:
enables in class WarningsGuard
+
+
+
Parameters:
group - A group of DiagnosticTypes. +
Returns:
Whether any warnings of these types are enabled by this guard.
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CssRenamingMap.ByPart.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CssRenamingMap.ByPart.html new file mode 100644 index 0000000..a823924 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CssRenamingMap.ByPart.html @@ -0,0 +1,314 @@ + + + + + +CssRenamingMap.ByPart (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class CssRenamingMap.ByPart

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CssRenamingMap.ByPart
+
+
+
All Implemented Interfaces:
CssRenamingMap
+
+
+
Enclosing interface:
CssRenamingMap
+
+
+
+
public abstract static class CssRenamingMap.ByPart
extends Object
implements CssRenamingMap
+ + +

+


+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from interface com.google.javascript.jscomp.CssRenamingMap
CssRenamingMap.ByPart, CssRenamingMap.ByWhole, CssRenamingMap.Style
+  + + + + + + + + + + + +
+Constructor Summary
CssRenamingMap.ByPart() + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+abstract  Stringget(String value) + +
+           
+ CssRenamingMap.StylegetStyle() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+CssRenamingMap.ByPart

+
+public CssRenamingMap.ByPart()
+
+
+ + + + + + + + +
+Method Detail
+ +

+get

+
+public abstract String get(String value)
+
+
+
Specified by:
get in interface CssRenamingMap
+
+
+
+
+
+
+ +

+getStyle

+
+public CssRenamingMap.Style getStyle()
+
+
+
Specified by:
getStyle in interface CssRenamingMap
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CssRenamingMap.ByWhole.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CssRenamingMap.ByWhole.html new file mode 100644 index 0000000..a433c3a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CssRenamingMap.ByWhole.html @@ -0,0 +1,314 @@ + + + + + +CssRenamingMap.ByWhole (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class CssRenamingMap.ByWhole

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CssRenamingMap.ByWhole
+
+
+
All Implemented Interfaces:
CssRenamingMap
+
+
+
Enclosing interface:
CssRenamingMap
+
+
+
+
public abstract static class CssRenamingMap.ByWhole
extends Object
implements CssRenamingMap
+ + +

+


+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from interface com.google.javascript.jscomp.CssRenamingMap
CssRenamingMap.ByPart, CssRenamingMap.ByWhole, CssRenamingMap.Style
+  + + + + + + + + + + + +
+Constructor Summary
CssRenamingMap.ByWhole() + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+abstract  Stringget(String value) + +
+           
+ CssRenamingMap.StylegetStyle() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+CssRenamingMap.ByWhole

+
+public CssRenamingMap.ByWhole()
+
+
+ + + + + + + + +
+Method Detail
+ +

+get

+
+public abstract String get(String value)
+
+
+
Specified by:
get in interface CssRenamingMap
+
+
+
+
+
+
+ +

+getStyle

+
+public CssRenamingMap.Style getStyle()
+
+
+
Specified by:
getStyle in interface CssRenamingMap
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CssRenamingMap.Style.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CssRenamingMap.Style.html new file mode 100644 index 0000000..e121544 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CssRenamingMap.Style.html @@ -0,0 +1,337 @@ + + + + + +CssRenamingMap.Style (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum CssRenamingMap.Style

+
+java.lang.Object
+  extended by java.lang.Enum<CssRenamingMap.Style>
+      extended by com.google.javascript.jscomp.CssRenamingMap.Style
+
+
+
All Implemented Interfaces:
Serializable, Comparable<CssRenamingMap.Style>
+
+
+
Enclosing interface:
CssRenamingMap
+
+
+
+
public static enum CssRenamingMap.Style
extends Enum<CssRenamingMap.Style>
+ + +

+


+ +

+ + + + + + + + + + + + + +
+Enum Constant Summary
BY_PART + +
+           
BY_WHOLE + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static CssRenamingMap.StylevalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static CssRenamingMap.Style[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+BY_WHOLE

+
+public static final CssRenamingMap.Style BY_WHOLE
+
+
+
+
+
+ +

+BY_PART

+
+public static final CssRenamingMap.Style BY_PART
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static CssRenamingMap.Style[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (CssRenamingMap.Style c : CssRenamingMap.Style.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static CssRenamingMap.Style valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CssRenamingMap.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CssRenamingMap.html new file mode 100644 index 0000000..3c50284 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CssRenamingMap.html @@ -0,0 +1,277 @@ + + + + + +CssRenamingMap (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface CssRenamingMap

+
+
All Known Implementing Classes:
CssRenamingMap.ByPart, CssRenamingMap.ByWhole
+
+
+
+
public interface CssRenamingMap
+ + +

+Interface used by ReplaceCssNames to substitute CSS class names. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + +
+Nested Class Summary
+static classCssRenamingMap.ByPart + +
+           
+static classCssRenamingMap.ByWhole + +
+           
+static classCssRenamingMap.Style + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+ Stringget(String value) + +
+           
+ CssRenamingMap.StylegetStyle() + +
+           
+  +

+ + + + + + + + +
+Method Detail
+ +

+get

+
+String get(String value)
+
+
+
+
+
+
+ +

+getStyle

+
+CssRenamingMap.Style getStyle()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CustomPassExecutionTime.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CustomPassExecutionTime.html new file mode 100644 index 0000000..7b60cff --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/CustomPassExecutionTime.html @@ -0,0 +1,371 @@ + + + + + +CustomPassExecutionTime (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum CustomPassExecutionTime

+
+java.lang.Object
+  extended by java.lang.Enum<CustomPassExecutionTime>
+      extended by com.google.javascript.jscomp.CustomPassExecutionTime
+
+
+
All Implemented Interfaces:
Serializable, Comparable<CustomPassExecutionTime>
+
+
+
+
public enum CustomPassExecutionTime
extends Enum<CustomPassExecutionTime>
+ + +

+Custom pass type. Controls the compilation stage at which each + custom pass executes. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + +
+Enum Constant Summary
AFTER_OPTIMIZATION_LOOP + +
+           
BEFORE_CHECKS + +
+           
BEFORE_OPTIMIZATION_LOOP + +
+           
BEFORE_OPTIMIZATIONS + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static CustomPassExecutionTimevalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static CustomPassExecutionTime[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+BEFORE_CHECKS

+
+public static final CustomPassExecutionTime BEFORE_CHECKS
+
+
+
+
+
+ +

+BEFORE_OPTIMIZATIONS

+
+public static final CustomPassExecutionTime BEFORE_OPTIMIZATIONS
+
+
+
+
+
+ +

+BEFORE_OPTIMIZATION_LOOP

+
+public static final CustomPassExecutionTime BEFORE_OPTIMIZATION_LOOP
+
+
+
+
+
+ +

+AFTER_OPTIMIZATION_LOOP

+
+public static final CustomPassExecutionTime AFTER_OPTIMIZATION_LOOP
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static CustomPassExecutionTime[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (CustomPassExecutionTime c : CustomPassExecutionTime.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static CustomPassExecutionTime valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DefaultPassConfig.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DefaultPassConfig.html new file mode 100644 index 0000000..139bd59 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DefaultPassConfig.html @@ -0,0 +1,310 @@ + + + + + +DefaultPassConfig (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class DefaultPassConfig

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.PassConfig
+      extended by com.google.javascript.jscomp.DefaultPassConfig
+
+
+
+
public class DefaultPassConfig
extends PassConfig
+ + +

+Pass factories and meta-data for native JSCompiler passes. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
DefaultPassConfig(CompilerOptions options) + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+protected  List<PassFactory>getChecks() + +
+          Gets the checking passes to run.
+protected  List<PassFactory>getOptimizations() + +
+          Gets the optimization passes to run.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+DefaultPassConfig

+
+public DefaultPassConfig(CompilerOptions options)
+
+
+ + + + + + + + +
+Method Detail
+ +

+getChecks

+
+protected List<PassFactory> getChecks()
+
+
Description copied from class: PassConfig
+
Gets the checking passes to run. + + Checking passes revolve around emitting warnings and errors. + They also may include pre-processor passes needed to do + error analysis more effectively. + + Clients that only want to analyze code (like IDEs) and not emit + code will only run checks and not optimizations. +

+

+
Specified by:
getChecks in class PassConfig
+
+
+
+
+
+
+ +

+getOptimizations

+
+protected List<PassFactory> getOptimizations()
+
+
Description copied from class: PassConfig
+
Gets the optimization passes to run. + + Optimization passes revolve around producing smaller and faster code. + They should always run after checking passes. +

+

+
Specified by:
getOptimizations in class PassConfig
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DependencyOptions.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DependencyOptions.html new file mode 100644 index 0000000..cc855ff --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DependencyOptions.html @@ -0,0 +1,393 @@ + + + + + +DependencyOptions (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class DependencyOptions

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.DependencyOptions
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class DependencyOptions
extends Object
implements Serializable
+ + +

+Options for how to manage dependencies between input files. + + Dependency information is usually pulled out from the JS code by + looking for primitive dependency functions (like Closure Library's + goog.provide/goog.require). Analysis of this dependency information is + controlled by CodingConvention, which lets you define those + dependency primitives. + + This options class determines how we use that dependency information + to change how code is built. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Constructor Summary
DependencyOptions() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ DependencyOptionssetDependencyPruning(boolean enabled) + +
+          Enables or disables dependency pruning mode.
+ DependencyOptionssetDependencySorting(boolean enabled) + +
+          Enables or disables dependency sorting mode.
+ voidsetEntryPoints(Collection<String> symbols) + +
+          Adds a collection of symbols to always keep.
+ DependencyOptionssetMoocherDropping(boolean enabled) + +
+          Enables or disables moocher dropping mode.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+DependencyOptions

+
+public DependencyOptions()
+
+
+ + + + + + + + +
+Method Detail
+ +

+setDependencySorting

+
+public DependencyOptions setDependencySorting(boolean enabled)
+
+
Enables or disables dependency sorting mode. + + If true, we will sort the input files based on dependency information + in them. Otherwise, we will use the order of files specified + on the command-line. +

+

+
+
+
+ +
Returns:
this for easy building.
+
+
+
+ +

+setDependencyPruning

+
+public DependencyOptions setDependencyPruning(boolean enabled)
+
+
Enables or disables dependency pruning mode. + + In dependency pruning mode, we will look for all files that provide a + symbol. Unless that file is a transitive dependency of a file that + we're using, we will remove it from the compilation job. + + This does not affect how we handle files that do not provide symbols. + See setMoocherDropping for information on how these are handled. +

+

+
+
+
+ +
Returns:
this for easy chaining.
+
+
+
+ +

+setMoocherDropping

+
+public DependencyOptions setMoocherDropping(boolean enabled)
+
+
Enables or disables moocher dropping mode. + + A 'moocher' is a file that does not provide any symbols (though they + may require symbols). This is usually because they don't want to + tie themselves to a particular dependency system (e.g., Closure's + goog.provide, CommonJS modules). So they rely on other people to + manage dependencies on them. + + If true, we drop these files when we prune dependencies. + If false, we always keep these files an anything they depend on. + The default is false. + + Notice that this option only makes sense if dependency pruning is on, + and a set of entry points is specified. +

+

+
+
+
+ +
Returns:
this for easy chaining.
+
+
+
+ +

+setEntryPoints

+
+public void setEntryPoints(Collection<String> symbols)
+
+
Adds a collection of symbols to always keep. + + In dependency pruning mode, we will automatically keep all the + transitive dependencies of these symbols. + + The syntactic form of a symbol depends on the type of dependency + primitives we're using. For example, goog.provide('foo.bar') + provides the symbol 'foo.bar'. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DiagnosticGroup.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DiagnosticGroup.html new file mode 100644 index 0000000..5685195 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DiagnosticGroup.html @@ -0,0 +1,411 @@ + + + + + +DiagnosticGroup (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class DiagnosticGroup

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.DiagnosticGroup
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class DiagnosticGroup
extends Object
implements Serializable
+ + +

+Group a set of related diagnostic types together, so that they can + be toggled on and off as one unit. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + + + + + + + +
+Constructor Summary
DiagnosticGroup(DiagnosticGroup... groups) + +
+          Create a composite group.
DiagnosticGroup(DiagnosticType... types) + +
+          Create a group that matches all errors of the given types.
DiagnosticGroup(String name, + DiagnosticGroup... groups) + +
+          Create a composite group.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static DiagnosticGroupforType(DiagnosticType type) + +
+          Create a diagnostic group that matches only the given type.
+ Iterable<DiagnosticType>getTypes() + +
+          Returns an iterable over all the types in this group.
+ booleanmatches(DiagnosticType type) + +
+          Returns whether the given type matches a type in this group.
+ booleanmatches(JSError error) + +
+          Returns whether the given error's type matches a type + in this group.
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+DiagnosticGroup

+
+public DiagnosticGroup(DiagnosticType... types)
+
+
Create a group that matches all errors of the given types. +

+

+
+ +

+DiagnosticGroup

+
+public DiagnosticGroup(DiagnosticGroup... groups)
+
+
Create a composite group. +

+

+
+ +

+DiagnosticGroup

+
+public DiagnosticGroup(String name,
+                       DiagnosticGroup... groups)
+
+
Create a composite group. +

+

+ + + + + + + + +
+Method Detail
+ +

+forType

+
+public static DiagnosticGroup forType(DiagnosticType type)
+
+
Create a diagnostic group that matches only the given type. +

+

+
+
+
+
+
+
+
+ +

+matches

+
+public boolean matches(JSError error)
+
+
Returns whether the given error's type matches a type + in this group. +

+

+
+
+
+
+
+
+
+ +

+matches

+
+public boolean matches(DiagnosticType type)
+
+
Returns whether the given type matches a type in this group. +

+

+
+
+
+
+
+
+
+ +

+getTypes

+
+public Iterable<DiagnosticType> getTypes()
+
+
Returns an iterable over all the types in this group. +

+

+
+
+
+
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DiagnosticGroupWarningsGuard.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DiagnosticGroupWarningsGuard.html new file mode 100644 index 0000000..33b97e5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DiagnosticGroupWarningsGuard.html @@ -0,0 +1,393 @@ + + + + + +DiagnosticGroupWarningsGuard (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class DiagnosticGroupWarningsGuard

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.WarningsGuard
+      extended by com.google.javascript.jscomp.DiagnosticGroupWarningsGuard
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class DiagnosticGroupWarningsGuard
extends WarningsGuard
+ + +

+Sets the level for a particular DiagnosticGroup. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.jscomp.WarningsGuard
WarningsGuard.Priority
+  + + + + + + + + + + + +
+Constructor Summary
DiagnosticGroupWarningsGuard(DiagnosticGroup group, + CheckLevel level) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleandisables(DiagnosticGroup otherGroup) + +
+          Returns whether all warnings in the given diagnostic group will be + filtered out.
+ booleanenables(DiagnosticGroup otherGroup) + +
+          Returns whether any of the warnings in the given diagnostic group will be + upgraded to a warning or error.
+ CheckLevellevel(JSError error) + +
+          Returns a new check level for a given error.
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.WarningsGuard
getPriority
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+DiagnosticGroupWarningsGuard

+
+public DiagnosticGroupWarningsGuard(DiagnosticGroup group,
+                                    CheckLevel level)
+
+
+ + + + + + + + +
+Method Detail
+ +

+level

+
+public CheckLevel level(JSError error)
+
+
Description copied from class: WarningsGuard
+
Returns a new check level for a given error. OFF - suppress it, ERROR - + report as error. null means that this guard does not know what to do + with the error. Null is extremely helpful when you have a chain of + guards. If current guard returns null, then the next in the chain should + process it. +

+

+
Specified by:
level in class WarningsGuard
+
+
+
Parameters:
error - a reported error. +
Returns:
what level given error should have.
+
+
+
+ +

+disables

+
+public boolean disables(DiagnosticGroup otherGroup)
+
+
Description copied from class: WarningsGuard
+
Returns whether all warnings in the given diagnostic group will be + filtered out. Used to determine which passes to skip. +

+

+
Overrides:
disables in class WarningsGuard
+
+
+
Parameters:
otherGroup - A group of DiagnosticTypes. +
Returns:
Whether all warnings of these types are disabled by this guard.
+
+
+
+ +

+enables

+
+public boolean enables(DiagnosticGroup otherGroup)
+
+
Description copied from class: WarningsGuard
+
Returns whether any of the warnings in the given diagnostic group will be + upgraded to a warning or error. +

+

+
Overrides:
enables in class WarningsGuard
+
+
+
Parameters:
otherGroup - A group of DiagnosticTypes. +
Returns:
Whether any warnings of these types are enabled by this guard.
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DiagnosticGroups.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DiagnosticGroups.html new file mode 100644 index 0000000..0c2a41f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DiagnosticGroups.html @@ -0,0 +1,813 @@ + + + + + +DiagnosticGroups (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class DiagnosticGroups

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.DiagnosticGroups
+
+
+
+
public class DiagnosticGroups
extends Object
+ + +

+Named groups of DiagnosticTypes exposed by Compiler. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Field Summary
+static DiagnosticGroupACCESS_CONTROLS + +
+           
+static DiagnosticGroupAMBIGUOUS_FUNCTION_DECL + +
+           
+static DiagnosticGroupCHECK_PROVIDES + +
+           
+static DiagnosticGroupCHECK_REGEXP + +
+           
+static DiagnosticGroupCHECK_TYPES + +
+           
+static DiagnosticGroupCHECK_USELESS_CODE + +
+           
+static DiagnosticGroupCHECK_VARIABLES + +
+           
+static DiagnosticGroupCONST + +
+           
+static DiagnosticGroupCONSTANT_PROPERTY + +
+           
+static DiagnosticGroupDEBUGGER_STATEMENT_PRESENT + +
+           
+static DiagnosticGroupDEPRECATED + +
+           
+static DiagnosticGroupDUPLICATE_MESSAGE + +
+           
+static DiagnosticGroupDUPLICATE_VARS + +
+           
+static DiagnosticGroupES5_STRICT + +
+           
+static DiagnosticGroupEXTERNS_VALIDATION + +
+           
+static DiagnosticGroupFILEOVERVIEW_JSDOC + +
+           
+static DiagnosticGroupGLOBAL_THIS + +
+           
+static DiagnosticGroupINTERNET_EXPLORER_CHECKS + +
+           
+static DiagnosticGroupINVALID_CASTS + +
+           
+static DiagnosticGroupMISSING_PROPERTIES + +
+           
+static DiagnosticGroupNON_STANDARD_JSDOC + +
+           
+static DiagnosticGroupSTRICT_MODULE_DEP_CHECK + +
+           
+static DiagnosticGroupTWEAKS + +
+           
+static DiagnosticGroupTYPE_INVALIDATION + +
+           
+static DiagnosticGroupUNDEFINED_NAMES + +
+           
+static DiagnosticGroupUNDEFINED_VARIABLES + +
+           
+static DiagnosticGroupUNKNOWN_DEFINES + +
+           
+static DiagnosticGroupVISIBILITY + +
+           
+  + + + + + + + + + + +
+Constructor Summary
DiagnosticGroups() + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+ DiagnosticGroupforName(String name) + +
+          Find the diagnostic group registered under the given name.
+protected  Map<String,DiagnosticGroup>getRegisteredGroups() + +
+          Get the registered diagnostic groups, indexed by name.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+GLOBAL_THIS

+
+public static DiagnosticGroup GLOBAL_THIS
+
+
+
+
+
+ +

+DEPRECATED

+
+public static DiagnosticGroup DEPRECATED
+
+
+
+
+
+ +

+VISIBILITY

+
+public static DiagnosticGroup VISIBILITY
+
+
+
+
+
+ +

+CONSTANT_PROPERTY

+
+public static DiagnosticGroup CONSTANT_PROPERTY
+
+
+
+
+
+ +

+NON_STANDARD_JSDOC

+
+public static DiagnosticGroup NON_STANDARD_JSDOC
+
+
+
+
+
+ +

+ACCESS_CONTROLS

+
+public static DiagnosticGroup ACCESS_CONTROLS
+
+
+
+
+
+ +

+INVALID_CASTS

+
+public static DiagnosticGroup INVALID_CASTS
+
+
+
+
+
+ +

+FILEOVERVIEW_JSDOC

+
+public static DiagnosticGroup FILEOVERVIEW_JSDOC
+
+
+
+
+
+ +

+STRICT_MODULE_DEP_CHECK

+
+public static DiagnosticGroup STRICT_MODULE_DEP_CHECK
+
+
+
+
+
+ +

+EXTERNS_VALIDATION

+
+public static DiagnosticGroup EXTERNS_VALIDATION
+
+
+
+
+
+ +

+AMBIGUOUS_FUNCTION_DECL

+
+public static DiagnosticGroup AMBIGUOUS_FUNCTION_DECL
+
+
+
+
+
+ +

+UNKNOWN_DEFINES

+
+public static DiagnosticGroup UNKNOWN_DEFINES
+
+
+
+
+
+ +

+TWEAKS

+
+public static DiagnosticGroup TWEAKS
+
+
+
+
+
+ +

+MISSING_PROPERTIES

+
+public static DiagnosticGroup MISSING_PROPERTIES
+
+
+
+
+
+ +

+INTERNET_EXPLORER_CHECKS

+
+public static DiagnosticGroup INTERNET_EXPLORER_CHECKS
+
+
+
+
+
+ +

+UNDEFINED_VARIABLES

+
+public static DiagnosticGroup UNDEFINED_VARIABLES
+
+
+
+
+
+ +

+UNDEFINED_NAMES

+
+public static DiagnosticGroup UNDEFINED_NAMES
+
+
+
+
+
+ +

+DEBUGGER_STATEMENT_PRESENT

+
+public static DiagnosticGroup DEBUGGER_STATEMENT_PRESENT
+
+
+
+
+
+ +

+CHECK_REGEXP

+
+public static DiagnosticGroup CHECK_REGEXP
+
+
+
+
+
+ +

+CHECK_TYPES

+
+public static DiagnosticGroup CHECK_TYPES
+
+
+
+
+
+ +

+CHECK_VARIABLES

+
+public static DiagnosticGroup CHECK_VARIABLES
+
+
+
+
+
+ +

+CHECK_USELESS_CODE

+
+public static DiagnosticGroup CHECK_USELESS_CODE
+
+
+
+
+
+ +

+CONST

+
+public static DiagnosticGroup CONST
+
+
+
+
+
+ +

+TYPE_INVALIDATION

+
+public static DiagnosticGroup TYPE_INVALIDATION
+
+
+
+
+
+ +

+DUPLICATE_VARS

+
+public static DiagnosticGroup DUPLICATE_VARS
+
+
+
+
+
+ +

+ES5_STRICT

+
+public static DiagnosticGroup ES5_STRICT
+
+
+
+
+
+ +

+CHECK_PROVIDES

+
+public static DiagnosticGroup CHECK_PROVIDES
+
+
+
+
+
+ +

+DUPLICATE_MESSAGE

+
+public static DiagnosticGroup DUPLICATE_MESSAGE
+
+
+
+
+ + + + + + + + +
+Constructor Detail
+ +

+DiagnosticGroups

+
+public DiagnosticGroups()
+
+
+ + + + + + + + +
+Method Detail
+ +

+getRegisteredGroups

+
+protected Map<String,DiagnosticGroup> getRegisteredGroups()
+
+
Get the registered diagnostic groups, indexed by name. +

+

+
+
+
+
+ +

+forName

+
+public DiagnosticGroup forName(String name)
+
+
Find the diagnostic group registered under the given name. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DiagnosticType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DiagnosticType.html new file mode 100644 index 0000000..8809de9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DiagnosticType.html @@ -0,0 +1,518 @@ + + + + + +DiagnosticType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class DiagnosticType

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.DiagnosticType
+
+
+
All Implemented Interfaces:
Serializable, Comparable<DiagnosticType>
+
+
+
+
public class DiagnosticType
extends Object
implements Comparable<DiagnosticType>, Serializable
+ + +

+The type of a compile or analysis error. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + + + + + + + + + + + + + +
+Field Summary
+ CheckLeveldefaultLevel + +
+          Default level
+ MessageFormatformat + +
+          The default way to format errors
+ Stringkey + +
+          The error type.
+ CheckLevellevel + +
+          Reporting level, initially the defaultLevel but may be changed.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ intcompareTo(DiagnosticType diagnosticType) + +
+           
+static DiagnosticTypedisabled(String name, + String descriptionFormat) + +
+          Create a DiagnosticType at level CheckLevel.OFF
+ booleanequals(Object type) + +
+           
+static DiagnosticTypeerror(String name, + String descriptionFormat) + +
+          Create a DiagnosticType at level CheckLevel.ERROR
+ inthashCode() + +
+           
+static DiagnosticTypemake(String name, + CheckLevel level, + String descriptionFormat) + +
+          Create a DiagnosticType at a given CheckLevel.
+ StringtoString() + +
+           
+static DiagnosticTypewarning(String name, + String descriptionFormat) + +
+          Create a DiagnosticType at level CheckLevel.WARNING
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+key

+
+public final String key
+
+
The error type. Used as the BugPattern and BugInstance types by + BugBot's XML +

+

+
+
+
+ +

+format

+
+public final MessageFormat format
+
+
The default way to format errors +

+

+
+
+
+ +

+defaultLevel

+
+public final CheckLevel defaultLevel
+
+
Default level +

+

+
+
+
+ +

+level

+
+public CheckLevel level
+
+
Reporting level, initially the defaultLevel but may be changed. +

+

+
+
+ + + + + + + + +
+Method Detail
+ +

+error

+
+public static DiagnosticType error(String name,
+                                   String descriptionFormat)
+
+
Create a DiagnosticType at level CheckLevel.ERROR +

+

+
+
+
+
Parameters:
name - An identifier
descriptionFormat - A format string +
Returns:
A new DiagnosticType
+
+
+
+ +

+warning

+
+public static DiagnosticType warning(String name,
+                                     String descriptionFormat)
+
+
Create a DiagnosticType at level CheckLevel.WARNING +

+

+
+
+
+
Parameters:
name - An identifier
descriptionFormat - A format string +
Returns:
A new DiagnosticType
+
+
+
+ +

+disabled

+
+public static DiagnosticType disabled(String name,
+                                      String descriptionFormat)
+
+
Create a DiagnosticType at level CheckLevel.OFF +

+

+
+
+
+
Parameters:
name - An identifier
descriptionFormat - A format string +
Returns:
A new DiagnosticType
+
+
+
+ +

+make

+
+public static DiagnosticType make(String name,
+                                  CheckLevel level,
+                                  String descriptionFormat)
+
+
Create a DiagnosticType at a given CheckLevel. +

+

+
+
+
+
Parameters:
name - An identifier
level - Either CheckLevel.ERROR or CheckLevel.WARNING
descriptionFormat - A format string +
Returns:
A new DiagnosticType
+
+
+
+ +

+equals

+
+public boolean equals(Object type)
+
+
+
Overrides:
equals in class Object
+
+
+
+
+
+
+ +

+hashCode

+
+public int hashCode()
+
+
+
Overrides:
hashCode in class Object
+
+
+
+
+
+
+ +

+compareTo

+
+public int compareTo(DiagnosticType diagnosticType)
+
+
+
Specified by:
compareTo in interface Comparable<DiagnosticType>
+
+
+
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DotFormatter.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DotFormatter.html new file mode 100644 index 0000000..f04c380 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/DotFormatter.html @@ -0,0 +1,269 @@ + + + + + +DotFormatter (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class DotFormatter

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.DotFormatter
+
+
+
+
public class DotFormatter
extends Object
+ + +

+

DotFormatter prints out a dot file of the Abstract Syntax Tree. + For a detailed description of the dot format and visualization tool refer + to Graphviz.

+

Typical usage of this class

+ System.out.println(new DotFormatter().toDot(node)); +

This class is not thread safe and should not be used without proper + external synchronization.

+

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Method Summary
+static StringtoDot(GraphvizGraph graph) + +
+          Outputs a string in DOT format that presents the graph.
+static StringtoDot(Node n) + +
+          Converts an AST to dot representation.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+toDot

+
+public static String toDot(Node n)
+                    throws IOException
+
+
Converts an AST to dot representation. +

+

+
Parameters:
n - the root of the AST described in the dot formatted string +
Returns:
the dot representation of the AST +
Throws: +
IOException
+
+
+
+ +

+toDot

+
+public static String toDot(GraphvizGraph graph)
+
+
Outputs a string in DOT format that presents the graph. +

+

+
Parameters:
graph - Input graph. +
Returns:
A string in Dot format that presents the graph.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/EmptyMessageBundle.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/EmptyMessageBundle.html new file mode 100644 index 0000000..92d5edb --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/EmptyMessageBundle.html @@ -0,0 +1,331 @@ + + + + + +EmptyMessageBundle (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class EmptyMessageBundle

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.EmptyMessageBundle
+
+
+
All Implemented Interfaces:
MessageBundle
+
+
+
+
public class EmptyMessageBundle
extends Object
implements MessageBundle
+ + +

+An implementation of MessageBundle that has no translations. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
EmptyMessageBundle() + +
+           
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ Iterable<JsMessage>getAllMessages() + +
+          Returns an empty list of messages.
+ JsMessagegetMessage(String id) + +
+          Returns null, to indicate it has no message replacements.
+ JsMessage.IdGeneratoridGenerator() + +
+          Gets a dummy message ID generator.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+EmptyMessageBundle

+
+public EmptyMessageBundle()
+
+
+ + + + + + + + +
+Method Detail
+ +

+idGenerator

+
+public JsMessage.IdGenerator idGenerator()
+
+
Gets a dummy message ID generator. +

+

+
Specified by:
idGenerator in interface MessageBundle
+
+
+ +
Returns:
idGenerator instance or null if we do not want to use any custom + id generation. In case if idGenerator is null caller should decide how + to create id by itself. In the most cases using the message key is + enough.
+
+
+
+ +

+getMessage

+
+public JsMessage getMessage(String id)
+
+
Returns null, to indicate it has no message replacements. +

+

+
Specified by:
getMessage in interface MessageBundle
+
+
+
Parameters:
id - the id of the message being replaced; the key is message ID + generated by JsMessage.IdGenerator +
Returns:
the message replacement, which may be null.
+
+
+
+ +

+getAllMessages

+
+public Iterable<JsMessage> getAllMessages()
+
+
Returns an empty list of messages. +

+

+
Specified by:
getAllMessages in interface MessageBundle
+
+
+ +
Returns:
all messages from this bundle.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ErrorFormat.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ErrorFormat.html new file mode 100644 index 0000000..1d77b11 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ErrorFormat.html @@ -0,0 +1,393 @@ + + + + + +ErrorFormat (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum ErrorFormat

+
+java.lang.Object
+  extended by java.lang.Enum<ErrorFormat>
+      extended by com.google.javascript.jscomp.ErrorFormat
+
+
+
All Implemented Interfaces:
Serializable, Comparable<ErrorFormat>
+
+
+
+
public enum ErrorFormat
extends Enum<ErrorFormat>
+ + +

+Error formats available. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + +
+Enum Constant Summary
LEGACY + +
+           
MULTILINE + +
+           
SINGLELINE + +
+           
SOURCELESS + +
+           
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+abstract  MessageFormattertoFormatter(SourceExcerptProvider source, + boolean colorize) + +
+          Convert to a concrete formatter.
+static ErrorFormatvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static ErrorFormat[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+LEGACY

+
+public static final ErrorFormat LEGACY
+
+
+
+
+
+ +

+SINGLELINE

+
+public static final ErrorFormat SINGLELINE
+
+
+
+
+
+ +

+MULTILINE

+
+public static final ErrorFormat MULTILINE
+
+
+
+
+
+ +

+SOURCELESS

+
+public static final ErrorFormat SOURCELESS
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static ErrorFormat[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (ErrorFormat c : ErrorFormat.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static ErrorFormat valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+
+ +

+toFormatter

+
+public abstract MessageFormatter toFormatter(SourceExcerptProvider source,
+                                             boolean colorize)
+
+
Convert to a concrete formatter. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ErrorHandler.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ErrorHandler.html new file mode 100644 index 0000000..05e08a4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ErrorHandler.html @@ -0,0 +1,231 @@ + + + + + +ErrorHandler (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface ErrorHandler

+
+
All Known Subinterfaces:
ErrorManager
+
+
+
All Known Implementing Classes:
AntErrorManager, BasicErrorManager, LoggerErrorManager, PrintStreamErrorManager, WhitelistWarningsGuard.WhitelistBuilder
+
+
+
+
public interface ErrorHandler
+ + +

+The error handler is any generic sink for warnings and errors, + after they've passed through any filtering WarningsGuards. +

+ +

+


+ +

+ + + + + + + + + + + + +
+Method Summary
+ voidreport(CheckLevel level, + JSError error) + +
+           
+  +

+ + + + + + + + +
+Method Detail
+ +

+report

+
+void report(CheckLevel level,
+            JSError error)
+
+
+
Parameters:
level - the reporting level
error - the error to report
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ErrorManager.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ErrorManager.html new file mode 100644 index 0000000..df157dd --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ErrorManager.html @@ -0,0 +1,406 @@ + + + + + +ErrorManager (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface ErrorManager

+
+
All Superinterfaces:
ErrorHandler
+
+
+
All Known Implementing Classes:
AntErrorManager, BasicErrorManager, LoggerErrorManager, PrintStreamErrorManager
+
+
+
+
public interface ErrorManager
extends ErrorHandler
+ + +

+The error manager is in charge of storing, organizing and displaying + errors and warnings generated by the compiler. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidgenerateReport() + +
+          Writes a report to an implementation-specific medium.
+ intgetErrorCount() + +
+          Gets the number of reported errors.
+ JSError[]getErrors() + +
+          Gets all the errors.
+ doublegetTypedPercent() + +
+          Gets the percentage of typed expressions.
+ intgetWarningCount() + +
+          Gets the number of reported warnings.
+ JSError[]getWarnings() + +
+          Gets all the warnings.
+ voidreport(CheckLevel level, + JSError error) + +
+          Reports an error.
+ voidsetTypedPercent(double typedPercent) + +
+          Sets the percentage of typed expressions.
+  +

+ + + + + + + + +
+Method Detail
+ +

+report

+
+void report(CheckLevel level,
+            JSError error)
+
+
Reports an error. The errors will be displayed by the + generateReport() at the discretion of the implementation. +

+

+
Specified by:
report in interface ErrorHandler
+
+
+
Parameters:
level - the reporting level
error - the error to report
+
+
+
+ +

+generateReport

+
+void generateReport()
+
+
Writes a report to an implementation-specific medium. The compiler calls + this method after any and all report(com.google.javascript.jscomp.CheckLevel, com.google.javascript.jscomp.JSError) calls. +

+

+
+
+
+
+
+
+
+ +

+getErrorCount

+
+int getErrorCount()
+
+
Gets the number of reported errors. +

+

+
+
+
+
+
+
+
+ +

+getWarningCount

+
+int getWarningCount()
+
+
Gets the number of reported warnings. +

+

+
+
+
+
+
+
+
+ +

+getErrors

+
+JSError[] getErrors()
+
+
Gets all the errors. +

+

+
+
+
+
+
+
+
+ +

+getWarnings

+
+JSError[] getWarnings()
+
+
Gets all the warnings. +

+

+
+
+
+
+
+
+
+ +

+setTypedPercent

+
+void setTypedPercent(double typedPercent)
+
+
Sets the percentage of typed expressions. +

+

+
+
+
+
+
+
+
+ +

+getTypedPercent

+
+double getTypedPercent()
+
+
Gets the percentage of typed expressions. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FieldCleanupPass.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FieldCleanupPass.html new file mode 100644 index 0000000..1767ab2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FieldCleanupPass.html @@ -0,0 +1,315 @@ + + + + + +FieldCleanupPass (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class FieldCleanupPass

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.FieldCleanupPass
+
+
+
All Implemented Interfaces:
CompilerPass, HotSwapCompilerPass
+
+
+
+
public class FieldCleanupPass
extends Object
implements HotSwapCompilerPass
+ + +

+A CleanupPass implementation that will remove all field declarations on + JSTypes contributed by the original file. +

+ This pass is expected to clear out declarations contributed to any JSType, + even if the constructor declaration is not provided in the file being + updated. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
FieldCleanupPass(AbstractCompiler compiler) + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+ voidhotSwapScript(Node scriptRoot, + Node originalRoot) + +
+          Process the JS with root node root.
+ voidprocess(Node externs, + Node root) + +
+          Process the JS with root node root.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+FieldCleanupPass

+
+public FieldCleanupPass(AbstractCompiler compiler)
+
+
+ + + + + + + + +
+Method Detail
+ +

+hotSwapScript

+
+public void hotSwapScript(Node scriptRoot,
+                          Node originalRoot)
+
+
Description copied from interface: HotSwapCompilerPass
+
Process the JS with root node root. This is supposed to be significantly + faster compared to corresponding full-compiler passes. +

+

+
Specified by:
hotSwapScript in interface HotSwapCompilerPass
+
+
+
Parameters:
scriptRoot - Root node corresponding to the file that is modified, + should be of type Token.SCRIPT.
originalRoot - Root node corresponding to the original version of the + file that is modified. Should be of type token.SCRIPT.
+
+
+
+ +

+process

+
+public void process(Node externs,
+                    Node root)
+
+
Description copied from interface: CompilerPass
+
Process the JS with root node root. + Can modify the contents of each Node tree +

+

+
Specified by:
process in interface CompilerPass
+
+
+
Parameters:
externs - Top of external JS tree
root - Top of JS tree
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FindExportableNodes.GenerateNodeContext.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FindExportableNodes.GenerateNodeContext.html new file mode 100644 index 0000000..f9dd257 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FindExportableNodes.GenerateNodeContext.html @@ -0,0 +1,314 @@ + + + + + +FindExportableNodes.GenerateNodeContext (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class FindExportableNodes.GenerateNodeContext

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.FindExportableNodes.GenerateNodeContext
+
+
+
Enclosing class:
FindExportableNodes
+
+
+
+
public static class FindExportableNodes.GenerateNodeContext
extends Object
+ + +

+Context holding the node references required for generating the export + calls. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
FindExportableNodes.GenerateNodeContext(Node node, + Node scriptNode, + Node contextNode) + +
+           
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ NodegetContextNode() + +
+           
+ NodegetNode() + +
+           
+ NodegetScriptNode() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+FindExportableNodes.GenerateNodeContext

+
+public FindExportableNodes.GenerateNodeContext(Node node,
+                                               Node scriptNode,
+                                               Node contextNode)
+
+
+ + + + + + + + +
+Method Detail
+ +

+getNode

+
+public Node getNode()
+
+
+
+
+
+
+ +

+getScriptNode

+
+public Node getScriptNode()
+
+
+
+
+
+
+ +

+getContextNode

+
+public Node getContextNode()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FindExportableNodes.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FindExportableNodes.html new file mode 100644 index 0000000..7049ab4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FindExportableNodes.html @@ -0,0 +1,342 @@ + + + + + +FindExportableNodes (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class FindExportableNodes

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback
+      extended by com.google.javascript.jscomp.FindExportableNodes
+
+
+
All Implemented Interfaces:
NodeTraversal.Callback
+
+
+
+
public class FindExportableNodes
extends NodeTraversal.AbstractPostOrderCallback
+ + +

+Records all of the symbols and properties that should be exported. + + Currently applies to: + - function foo() {} + - var foo = function() {} + - foo.bar = function() {} + - var FOO = ...; + - foo.BAR = ...; + + FOO = BAR = 5; + and + var FOO = BAR = 5; + are not supported because the annotation is ambigous to whether it applies + to all the variables or only the first one. +

+ +

+


+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classFindExportableNodes.GenerateNodeContext + +
+          Context holding the node references required for generating the export + calls.
+  + + + + + + + + + + +
+Constructor Summary
FindExportableNodes(AbstractCompiler compiler) + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+ LinkedHashMap<String,FindExportableNodes.GenerateNodeContext>getExports() + +
+           
+ voidvisit(NodeTraversal t, + Node n, + Node parent) + +
+          Visits a node in post order (after its children have been visited).
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback
shouldTraverse
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+FindExportableNodes

+
+public FindExportableNodes(AbstractCompiler compiler)
+
+
+ + + + + + + + +
+Method Detail
+ +

+visit

+
+public void visit(NodeTraversal t,
+                  Node n,
+                  Node parent)
+
+
Description copied from interface: NodeTraversal.Callback
+

Visits a node in post order (after its children have been visited). + A node is visited only if all its parents should be traversed + (NodeTraversal.Callback.shouldTraverse(NodeTraversal, Node, Node)).

+

Implementations can have side effects (e.g. modifying the parse + tree).

+

+

+
+
+
+
+ +

+getExports

+
+public LinkedHashMap<String,FindExportableNodes.GenerateNodeContext> getExports()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInfo.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInfo.html new file mode 100644 index 0000000..16f9191 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInfo.html @@ -0,0 +1,250 @@ + + + + + +FunctionInfo (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class FunctionInfo

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.FunctionInfo
+
+
+
+
public final class FunctionInfo
extends Object
+ + +

+


+ +

+ + + + + + + + + + + + + + + + +
+Method Summary
+static com.google.protobuf.Descriptors.FileDescriptorgetDescriptor() + +
+           
+static voidregisterAllExtensions(com.google.protobuf.ExtensionRegistry registry) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+registerAllExtensions

+
+public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry)
+
+
+
+
+
+
+ +

+getDescriptor

+
+public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.Builder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.Builder.html new file mode 100644 index 0000000..37a5c8e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.Builder.html @@ -0,0 +1,1334 @@ + + + + + +FunctionInformationMap.Builder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class FunctionInformationMap.Builder

+
+java.lang.Object
+  extended by com.google.protobuf.AbstractMessageLite.Builder<BuilderType>
+      extended by com.google.protobuf.AbstractMessage.Builder<BuilderType>
+          extended by com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Builder>
+              extended by com.google.javascript.jscomp.FunctionInformationMap.Builder
+
+
+
All Implemented Interfaces:
FunctionInformationMapOrBuilder, com.google.protobuf.Message.Builder, com.google.protobuf.MessageLite.Builder, com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder, Cloneable
+
+
+
Enclosing class:
FunctionInformationMap
+
+
+
+
public static final class FunctionInformationMap.Builder
extends com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Builder>
implements FunctionInformationMapOrBuilder
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ FunctionInformationMap.BuilderaddAllEntry(Iterable<? extends FunctionInformationMap.Entry> values) + +
+           
+ FunctionInformationMap.BuilderaddAllModule(Iterable<? extends FunctionInformationMap.Module> values) + +
+           
+ FunctionInformationMap.BuilderaddEntry(FunctionInformationMap.Entry.Builder builderForValue) + +
+           
+ FunctionInformationMap.BuilderaddEntry(FunctionInformationMap.Entry value) + +
+           
+ FunctionInformationMap.BuilderaddEntry(int index, + FunctionInformationMap.Entry.Builder builderForValue) + +
+           
+ FunctionInformationMap.BuilderaddEntry(int index, + FunctionInformationMap.Entry value) + +
+           
+ FunctionInformationMap.Entry.BuilderaddEntryBuilder() + +
+           
+ FunctionInformationMap.Entry.BuilderaddEntryBuilder(int index) + +
+           
+ FunctionInformationMap.BuilderaddModule(FunctionInformationMap.Module.Builder builderForValue) + +
+           
+ FunctionInformationMap.BuilderaddModule(FunctionInformationMap.Module value) + +
+           
+ FunctionInformationMap.BuilderaddModule(int index, + FunctionInformationMap.Module.Builder builderForValue) + +
+           
+ FunctionInformationMap.BuilderaddModule(int index, + FunctionInformationMap.Module value) + +
+           
+ FunctionInformationMap.Module.BuilderaddModuleBuilder() + +
+           
+ FunctionInformationMap.Module.BuilderaddModuleBuilder(int index) + +
+           
+ FunctionInformationMapbuild() + +
+           
+ FunctionInformationMapbuildPartial() + +
+           
+ FunctionInformationMap.Builderclear() + +
+           
+ FunctionInformationMap.BuilderclearEntry() + +
+           
+ FunctionInformationMap.BuilderclearModule() + +
+           
+ FunctionInformationMap.Builderclone() + +
+           
+ FunctionInformationMapgetDefaultInstanceForType() + +
+           
+static com.google.protobuf.Descriptors.DescriptorgetDescriptor() + +
+           
+ com.google.protobuf.Descriptors.DescriptorgetDescriptorForType() + +
+           
+ FunctionInformationMap.EntrygetEntry(int index) + +
+           
+ FunctionInformationMap.Entry.BuildergetEntryBuilder(int index) + +
+           
+ List<FunctionInformationMap.Entry.Builder>getEntryBuilderList() + +
+           
+ intgetEntryCount() + +
+           
+ List<FunctionInformationMap.Entry>getEntryList() + +
+           
+ FunctionInformationMap.EntryOrBuildergetEntryOrBuilder(int index) + +
+           
+ List<? extends FunctionInformationMap.EntryOrBuilder>getEntryOrBuilderList() + +
+           
+ FunctionInformationMap.ModulegetModule(int index) + +
+           
+ FunctionInformationMap.Module.BuildergetModuleBuilder(int index) + +
+           
+ List<FunctionInformationMap.Module.Builder>getModuleBuilderList() + +
+           
+ intgetModuleCount() + +
+           
+ List<FunctionInformationMap.Module>getModuleList() + +
+           
+ FunctionInformationMap.ModuleOrBuildergetModuleOrBuilder(int index) + +
+           
+ List<? extends FunctionInformationMap.ModuleOrBuilder>getModuleOrBuilderList() + +
+           
+protected  com.google.protobuf.GeneratedMessage.FieldAccessorTableinternalGetFieldAccessorTable() + +
+           
+ booleanisInitialized() + +
+           
+ FunctionInformationMap.BuildermergeFrom(com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+ FunctionInformationMap.BuildermergeFrom(FunctionInformationMap other) + +
+           
+ FunctionInformationMap.BuildermergeFrom(com.google.protobuf.Message other) + +
+           
+ FunctionInformationMap.BuilderremoveEntry(int index) + +
+           
+ FunctionInformationMap.BuilderremoveModule(int index) + +
+           
+ FunctionInformationMap.BuildersetEntry(int index, + FunctionInformationMap.Entry.Builder builderForValue) + +
+           
+ FunctionInformationMap.BuildersetEntry(int index, + FunctionInformationMap.Entry value) + +
+           
+ FunctionInformationMap.BuildersetModule(int index, + FunctionInformationMap.Module.Builder builderForValue) + +
+           
+ FunctionInformationMap.BuildersetModule(int index, + FunctionInformationMap.Module value) + +
+           
+ + + + + + + +
Methods inherited from class com.google.protobuf.GeneratedMessage.Builder
addRepeatedField, clearField, getAllFields, getField, getParentForChildren, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField, isClean, markClean, mergeUnknownFields, newBuilderForField, onBuilt, onChanged, parseUnknownField, setField, setRepeatedField, setUnknownFields
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessage.Builder
mergeDelimitedFrom, mergeDelimitedFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, newUninitializedMessageException
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessageLite.Builder
addAll, newUninitializedMessageException
+ + + + + + + +
Methods inherited from class java.lang.Object
equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+  +

+ + + + + + + + +
+Method Detail
+ +

+getDescriptor

+
+public static final com.google.protobuf.Descriptors.Descriptor getDescriptor()
+
+
+
+
+
+
+
+
+
+ +

+internalGetFieldAccessorTable

+
+protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable()
+
+
+
Specified by:
internalGetFieldAccessorTable in class com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Builder>
+
+
+
+
+
+
+ +

+clear

+
+public FunctionInformationMap.Builder clear()
+
+
+
Specified by:
clear in interface com.google.protobuf.Message.Builder
Specified by:
clear in interface com.google.protobuf.MessageLite.Builder
Overrides:
clear in class com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Builder>
+
+
+
+
+
+
+ +

+clone

+
+public FunctionInformationMap.Builder clone()
+
+
+
Specified by:
clone in interface com.google.protobuf.Message.Builder
Specified by:
clone in interface com.google.protobuf.MessageLite.Builder
Overrides:
clone in class com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Builder>
+
+
+
+
+
+
+ +

+getDescriptorForType

+
+public com.google.protobuf.Descriptors.Descriptor getDescriptorForType()
+
+
+
Specified by:
getDescriptorForType in interface com.google.protobuf.Message.Builder
Specified by:
getDescriptorForType in interface com.google.protobuf.MessageOrBuilder
Overrides:
getDescriptorForType in class com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Builder>
+
+
+
+
+
+
+ +

+getDefaultInstanceForType

+
+public FunctionInformationMap getDefaultInstanceForType()
+
+
+
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageLiteOrBuilder
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageOrBuilder
+
+
+
+
+
+
+ +

+build

+
+public FunctionInformationMap build()
+
+
+
Specified by:
build in interface com.google.protobuf.Message.Builder
Specified by:
build in interface com.google.protobuf.MessageLite.Builder
+
+
+
+
+
+
+ +

+buildPartial

+
+public FunctionInformationMap buildPartial()
+
+
+
Specified by:
buildPartial in interface com.google.protobuf.Message.Builder
Specified by:
buildPartial in interface com.google.protobuf.MessageLite.Builder
+
+
+
+
+
+
+ +

+mergeFrom

+
+public FunctionInformationMap.Builder mergeFrom(com.google.protobuf.Message other)
+
+
+
Specified by:
mergeFrom in interface com.google.protobuf.Message.Builder
Overrides:
mergeFrom in class com.google.protobuf.AbstractMessage.Builder<FunctionInformationMap.Builder>
+
+
+
+
+
+
+ +

+mergeFrom

+
+public FunctionInformationMap.Builder mergeFrom(FunctionInformationMap other)
+
+
+
+
+
+
+
+
+
+ +

+isInitialized

+
+public final boolean isInitialized()
+
+
+
Specified by:
isInitialized in interface com.google.protobuf.MessageLiteOrBuilder
Overrides:
isInitialized in class com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Builder>
+
+
+
+
+
+
+ +

+mergeFrom

+
+public FunctionInformationMap.Builder mergeFrom(com.google.protobuf.CodedInputStream input,
+                                                com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                         throws IOException
+
+
+
Specified by:
mergeFrom in interface com.google.protobuf.Message.Builder
Specified by:
mergeFrom in interface com.google.protobuf.MessageLite.Builder
Overrides:
mergeFrom in class com.google.protobuf.AbstractMessage.Builder<FunctionInformationMap.Builder>
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+getEntryList

+
+public List<FunctionInformationMap.Entry> getEntryList()
+
+
+
Specified by:
getEntryList in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+getEntryCount

+
+public int getEntryCount()
+
+
+
Specified by:
getEntryCount in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+getEntry

+
+public FunctionInformationMap.Entry getEntry(int index)
+
+
+
Specified by:
getEntry in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+setEntry

+
+public FunctionInformationMap.Builder setEntry(int index,
+                                               FunctionInformationMap.Entry value)
+
+
+
+
+
+
+
+
+
+ +

+setEntry

+
+public FunctionInformationMap.Builder setEntry(int index,
+                                               FunctionInformationMap.Entry.Builder builderForValue)
+
+
+
+
+
+
+
+
+
+ +

+addEntry

+
+public FunctionInformationMap.Builder addEntry(FunctionInformationMap.Entry value)
+
+
+
+
+
+
+
+
+
+ +

+addEntry

+
+public FunctionInformationMap.Builder addEntry(int index,
+                                               FunctionInformationMap.Entry value)
+
+
+
+
+
+
+
+
+
+ +

+addEntry

+
+public FunctionInformationMap.Builder addEntry(FunctionInformationMap.Entry.Builder builderForValue)
+
+
+
+
+
+
+
+
+
+ +

+addEntry

+
+public FunctionInformationMap.Builder addEntry(int index,
+                                               FunctionInformationMap.Entry.Builder builderForValue)
+
+
+
+
+
+
+
+
+
+ +

+addAllEntry

+
+public FunctionInformationMap.Builder addAllEntry(Iterable<? extends FunctionInformationMap.Entry> values)
+
+
+
+
+
+
+
+
+
+ +

+clearEntry

+
+public FunctionInformationMap.Builder clearEntry()
+
+
+
+
+
+
+
+
+
+ +

+removeEntry

+
+public FunctionInformationMap.Builder removeEntry(int index)
+
+
+
+
+
+
+
+
+
+ +

+getEntryBuilder

+
+public FunctionInformationMap.Entry.Builder getEntryBuilder(int index)
+
+
+
+
+
+
+
+
+
+ +

+getEntryOrBuilder

+
+public FunctionInformationMap.EntryOrBuilder getEntryOrBuilder(int index)
+
+
+
Specified by:
getEntryOrBuilder in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+getEntryOrBuilderList

+
+public List<? extends FunctionInformationMap.EntryOrBuilder> getEntryOrBuilderList()
+
+
+
Specified by:
getEntryOrBuilderList in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+addEntryBuilder

+
+public FunctionInformationMap.Entry.Builder addEntryBuilder()
+
+
+
+
+
+
+
+
+
+ +

+addEntryBuilder

+
+public FunctionInformationMap.Entry.Builder addEntryBuilder(int index)
+
+
+
+
+
+
+
+
+
+ +

+getEntryBuilderList

+
+public List<FunctionInformationMap.Entry.Builder> getEntryBuilderList()
+
+
+
+
+
+
+
+
+
+ +

+getModuleList

+
+public List<FunctionInformationMap.Module> getModuleList()
+
+
+
Specified by:
getModuleList in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+getModuleCount

+
+public int getModuleCount()
+
+
+
Specified by:
getModuleCount in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+getModule

+
+public FunctionInformationMap.Module getModule(int index)
+
+
+
Specified by:
getModule in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+setModule

+
+public FunctionInformationMap.Builder setModule(int index,
+                                                FunctionInformationMap.Module value)
+
+
+
+
+
+
+
+
+
+ +

+setModule

+
+public FunctionInformationMap.Builder setModule(int index,
+                                                FunctionInformationMap.Module.Builder builderForValue)
+
+
+
+
+
+
+
+
+
+ +

+addModule

+
+public FunctionInformationMap.Builder addModule(FunctionInformationMap.Module value)
+
+
+
+
+
+
+
+
+
+ +

+addModule

+
+public FunctionInformationMap.Builder addModule(int index,
+                                                FunctionInformationMap.Module value)
+
+
+
+
+
+
+
+
+
+ +

+addModule

+
+public FunctionInformationMap.Builder addModule(FunctionInformationMap.Module.Builder builderForValue)
+
+
+
+
+
+
+
+
+
+ +

+addModule

+
+public FunctionInformationMap.Builder addModule(int index,
+                                                FunctionInformationMap.Module.Builder builderForValue)
+
+
+
+
+
+
+
+
+
+ +

+addAllModule

+
+public FunctionInformationMap.Builder addAllModule(Iterable<? extends FunctionInformationMap.Module> values)
+
+
+
+
+
+
+
+
+
+ +

+clearModule

+
+public FunctionInformationMap.Builder clearModule()
+
+
+
+
+
+
+
+
+
+ +

+removeModule

+
+public FunctionInformationMap.Builder removeModule(int index)
+
+
+
+
+
+
+
+
+
+ +

+getModuleBuilder

+
+public FunctionInformationMap.Module.Builder getModuleBuilder(int index)
+
+
+
+
+
+
+
+
+
+ +

+getModuleOrBuilder

+
+public FunctionInformationMap.ModuleOrBuilder getModuleOrBuilder(int index)
+
+
+
Specified by:
getModuleOrBuilder in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+getModuleOrBuilderList

+
+public List<? extends FunctionInformationMap.ModuleOrBuilder> getModuleOrBuilderList()
+
+
+
Specified by:
getModuleOrBuilderList in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+addModuleBuilder

+
+public FunctionInformationMap.Module.Builder addModuleBuilder()
+
+
+
+
+
+
+
+
+
+ +

+addModuleBuilder

+
+public FunctionInformationMap.Module.Builder addModuleBuilder(int index)
+
+
+
+
+
+
+
+
+
+ +

+getModuleBuilderList

+
+public List<FunctionInformationMap.Module.Builder> getModuleBuilderList()
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.Entry.Builder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.Entry.Builder.html new file mode 100644 index 0000000..34a30b8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.Entry.Builder.html @@ -0,0 +1,1142 @@ + + + + + +FunctionInformationMap.Entry.Builder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class FunctionInformationMap.Entry.Builder

+
+java.lang.Object
+  extended by com.google.protobuf.AbstractMessageLite.Builder<BuilderType>
+      extended by com.google.protobuf.AbstractMessage.Builder<BuilderType>
+          extended by com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Entry.Builder>
+              extended by com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder
+
+
+
All Implemented Interfaces:
FunctionInformationMap.EntryOrBuilder, com.google.protobuf.Message.Builder, com.google.protobuf.MessageLite.Builder, com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder, Cloneable
+
+
+
Enclosing class:
FunctionInformationMap.Entry
+
+
+
+
public static final class FunctionInformationMap.Entry.Builder
extends com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Entry.Builder>
implements FunctionInformationMap.EntryOrBuilder
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ FunctionInformationMap.Entrybuild() + +
+           
+ FunctionInformationMap.EntrybuildPartial() + +
+           
+ FunctionInformationMap.Entry.Builderclear() + +
+           
+ FunctionInformationMap.Entry.BuilderclearCompiledSource() + +
+           
+ FunctionInformationMap.Entry.BuilderclearId() + +
+           
+ FunctionInformationMap.Entry.BuilderclearLineNumber() + +
+           
+ FunctionInformationMap.Entry.BuilderclearModuleName() + +
+           
+ FunctionInformationMap.Entry.BuilderclearName() + +
+           
+ FunctionInformationMap.Entry.BuilderclearSize() + +
+           
+ FunctionInformationMap.Entry.BuilderclearSourceName() + +
+           
+ FunctionInformationMap.Entry.Builderclone() + +
+           
+ StringgetCompiledSource() + +
+           
+ FunctionInformationMap.EntrygetDefaultInstanceForType() + +
+           
+static com.google.protobuf.Descriptors.DescriptorgetDescriptor() + +
+           
+ com.google.protobuf.Descriptors.DescriptorgetDescriptorForType() + +
+           
+ intgetId() + +
+           
+ intgetLineNumber() + +
+           
+ StringgetModuleName() + +
+           
+ StringgetName() + +
+           
+ intgetSize() + +
+           
+ StringgetSourceName() + +
+           
+ booleanhasCompiledSource() + +
+           
+ booleanhasId() + +
+           
+ booleanhasLineNumber() + +
+           
+ booleanhasModuleName() + +
+           
+ booleanhasName() + +
+           
+ booleanhasSize() + +
+           
+ booleanhasSourceName() + +
+           
+protected  com.google.protobuf.GeneratedMessage.FieldAccessorTableinternalGetFieldAccessorTable() + +
+           
+ booleanisInitialized() + +
+           
+ FunctionInformationMap.Entry.BuildermergeFrom(com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+ FunctionInformationMap.Entry.BuildermergeFrom(FunctionInformationMap.Entry other) + +
+           
+ FunctionInformationMap.Entry.BuildermergeFrom(com.google.protobuf.Message other) + +
+           
+ FunctionInformationMap.Entry.BuildersetCompiledSource(String value) + +
+           
+ FunctionInformationMap.Entry.BuildersetId(int value) + +
+           
+ FunctionInformationMap.Entry.BuildersetLineNumber(int value) + +
+           
+ FunctionInformationMap.Entry.BuildersetModuleName(String value) + +
+           
+ FunctionInformationMap.Entry.BuildersetName(String value) + +
+           
+ FunctionInformationMap.Entry.BuildersetSize(int value) + +
+           
+ FunctionInformationMap.Entry.BuildersetSourceName(String value) + +
+           
+ + + + + + + +
Methods inherited from class com.google.protobuf.GeneratedMessage.Builder
addRepeatedField, clearField, getAllFields, getField, getParentForChildren, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField, isClean, markClean, mergeUnknownFields, newBuilderForField, onBuilt, onChanged, parseUnknownField, setField, setRepeatedField, setUnknownFields
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessage.Builder
mergeDelimitedFrom, mergeDelimitedFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, newUninitializedMessageException
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessageLite.Builder
addAll, newUninitializedMessageException
+ + + + + + + +
Methods inherited from class java.lang.Object
equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+  +

+ + + + + + + + +
+Method Detail
+ +

+getDescriptor

+
+public static final com.google.protobuf.Descriptors.Descriptor getDescriptor()
+
+
+
+
+
+
+
+
+
+ +

+internalGetFieldAccessorTable

+
+protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable()
+
+
+
Specified by:
internalGetFieldAccessorTable in class com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Entry.Builder>
+
+
+
+
+
+
+ +

+clear

+
+public FunctionInformationMap.Entry.Builder clear()
+
+
+
Specified by:
clear in interface com.google.protobuf.Message.Builder
Specified by:
clear in interface com.google.protobuf.MessageLite.Builder
Overrides:
clear in class com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Entry.Builder>
+
+
+
+
+
+
+ +

+clone

+
+public FunctionInformationMap.Entry.Builder clone()
+
+
+
Specified by:
clone in interface com.google.protobuf.Message.Builder
Specified by:
clone in interface com.google.protobuf.MessageLite.Builder
Overrides:
clone in class com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Entry.Builder>
+
+
+
+
+
+
+ +

+getDescriptorForType

+
+public com.google.protobuf.Descriptors.Descriptor getDescriptorForType()
+
+
+
Specified by:
getDescriptorForType in interface com.google.protobuf.Message.Builder
Specified by:
getDescriptorForType in interface com.google.protobuf.MessageOrBuilder
Overrides:
getDescriptorForType in class com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Entry.Builder>
+
+
+
+
+
+
+ +

+getDefaultInstanceForType

+
+public FunctionInformationMap.Entry getDefaultInstanceForType()
+
+
+
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageLiteOrBuilder
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageOrBuilder
+
+
+
+
+
+
+ +

+build

+
+public FunctionInformationMap.Entry build()
+
+
+
Specified by:
build in interface com.google.protobuf.Message.Builder
Specified by:
build in interface com.google.protobuf.MessageLite.Builder
+
+
+
+
+
+
+ +

+buildPartial

+
+public FunctionInformationMap.Entry buildPartial()
+
+
+
Specified by:
buildPartial in interface com.google.protobuf.Message.Builder
Specified by:
buildPartial in interface com.google.protobuf.MessageLite.Builder
+
+
+
+
+
+
+ +

+mergeFrom

+
+public FunctionInformationMap.Entry.Builder mergeFrom(com.google.protobuf.Message other)
+
+
+
Specified by:
mergeFrom in interface com.google.protobuf.Message.Builder
Overrides:
mergeFrom in class com.google.protobuf.AbstractMessage.Builder<FunctionInformationMap.Entry.Builder>
+
+
+
+
+
+
+ +

+mergeFrom

+
+public FunctionInformationMap.Entry.Builder mergeFrom(FunctionInformationMap.Entry other)
+
+
+
+
+
+
+
+
+
+ +

+isInitialized

+
+public final boolean isInitialized()
+
+
+
Specified by:
isInitialized in interface com.google.protobuf.MessageLiteOrBuilder
Overrides:
isInitialized in class com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Entry.Builder>
+
+
+
+
+
+
+ +

+mergeFrom

+
+public FunctionInformationMap.Entry.Builder mergeFrom(com.google.protobuf.CodedInputStream input,
+                                                      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                               throws IOException
+
+
+
Specified by:
mergeFrom in interface com.google.protobuf.Message.Builder
Specified by:
mergeFrom in interface com.google.protobuf.MessageLite.Builder
Overrides:
mergeFrom in class com.google.protobuf.AbstractMessage.Builder<FunctionInformationMap.Entry.Builder>
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+hasId

+
+public boolean hasId()
+
+
+
Specified by:
hasId in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+getId

+
+public int getId()
+
+
+
Specified by:
getId in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+setId

+
+public FunctionInformationMap.Entry.Builder setId(int value)
+
+
+
+
+
+
+
+
+
+ +

+clearId

+
+public FunctionInformationMap.Entry.Builder clearId()
+
+
+
+
+
+
+
+
+
+ +

+hasSourceName

+
+public boolean hasSourceName()
+
+
+
Specified by:
hasSourceName in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+getSourceName

+
+public String getSourceName()
+
+
+
Specified by:
getSourceName in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+setSourceName

+
+public FunctionInformationMap.Entry.Builder setSourceName(String value)
+
+
+
+
+
+
+
+
+
+ +

+clearSourceName

+
+public FunctionInformationMap.Entry.Builder clearSourceName()
+
+
+
+
+
+
+
+
+
+ +

+hasLineNumber

+
+public boolean hasLineNumber()
+
+
+
Specified by:
hasLineNumber in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+getLineNumber

+
+public int getLineNumber()
+
+
+
Specified by:
getLineNumber in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+setLineNumber

+
+public FunctionInformationMap.Entry.Builder setLineNumber(int value)
+
+
+
+
+
+
+
+
+
+ +

+clearLineNumber

+
+public FunctionInformationMap.Entry.Builder clearLineNumber()
+
+
+
+
+
+
+
+
+
+ +

+hasModuleName

+
+public boolean hasModuleName()
+
+
+
Specified by:
hasModuleName in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+getModuleName

+
+public String getModuleName()
+
+
+
Specified by:
getModuleName in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+setModuleName

+
+public FunctionInformationMap.Entry.Builder setModuleName(String value)
+
+
+
+
+
+
+
+
+
+ +

+clearModuleName

+
+public FunctionInformationMap.Entry.Builder clearModuleName()
+
+
+
+
+
+
+
+
+
+ +

+hasSize

+
+public boolean hasSize()
+
+
+
Specified by:
hasSize in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+getSize

+
+public int getSize()
+
+
+
Specified by:
getSize in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+setSize

+
+public FunctionInformationMap.Entry.Builder setSize(int value)
+
+
+
+
+
+
+
+
+
+ +

+clearSize

+
+public FunctionInformationMap.Entry.Builder clearSize()
+
+
+
+
+
+
+
+
+
+ +

+hasName

+
+public boolean hasName()
+
+
+
Specified by:
hasName in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+getName

+
+public String getName()
+
+
+
Specified by:
getName in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+setName

+
+public FunctionInformationMap.Entry.Builder setName(String value)
+
+
+
+
+
+
+
+
+
+ +

+clearName

+
+public FunctionInformationMap.Entry.Builder clearName()
+
+
+
+
+
+
+
+
+
+ +

+hasCompiledSource

+
+public boolean hasCompiledSource()
+
+
+
Specified by:
hasCompiledSource in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+getCompiledSource

+
+public String getCompiledSource()
+
+
+
Specified by:
getCompiledSource in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+setCompiledSource

+
+public FunctionInformationMap.Entry.Builder setCompiledSource(String value)
+
+
+
+
+
+
+
+
+
+ +

+clearCompiledSource

+
+public FunctionInformationMap.Entry.Builder clearCompiledSource()
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.Entry.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.Entry.html new file mode 100644 index 0000000..b9e3981 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.Entry.html @@ -0,0 +1,1308 @@ + + + + + +FunctionInformationMap.Entry (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class FunctionInformationMap.Entry

+
+java.lang.Object
+  extended by com.google.protobuf.AbstractMessageLite
+      extended by com.google.protobuf.AbstractMessage
+          extended by com.google.protobuf.GeneratedMessage
+              extended by com.google.javascript.jscomp.FunctionInformationMap.Entry
+
+
+
All Implemented Interfaces:
FunctionInformationMap.EntryOrBuilder, com.google.protobuf.Message, com.google.protobuf.MessageLite, com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder, Serializable
+
+
+
Enclosing class:
FunctionInformationMap
+
+
+
+
public static final class FunctionInformationMap.Entry
extends com.google.protobuf.GeneratedMessage
implements FunctionInformationMap.EntryOrBuilder
+ + +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classFunctionInformationMap.Entry.Builder + +
+           
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.protobuf.GeneratedMessage
com.google.protobuf.GeneratedMessage.BuilderParent, com.google.protobuf.GeneratedMessage.ExtendableBuilder<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage,BuilderType extends com.google.protobuf.GeneratedMessage.ExtendableBuilder>, com.google.protobuf.GeneratedMessage.ExtendableMessage<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage>, com.google.protobuf.GeneratedMessage.ExtendableMessageOrBuilder<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage>, com.google.protobuf.GeneratedMessage.FieldAccessorTable, com.google.protobuf.GeneratedMessage.GeneratedExtension<ContainingType extends com.google.protobuf.Message,Type>
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Field Summary
+static intCOMPILED_SOURCE_FIELD_NUMBER + +
+           
+static intID_FIELD_NUMBER + +
+           
+static intLINE_NUMBER_FIELD_NUMBER + +
+           
+static intMODULE_NAME_FIELD_NUMBER + +
+           
+static intNAME_FIELD_NUMBER + +
+           
+static intSIZE_FIELD_NUMBER + +
+           
+static intSOURCE_NAME_FIELD_NUMBER + +
+           
+ + + + + + + +
Fields inherited from class com.google.protobuf.GeneratedMessage
alwaysUseFieldBuilders
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StringgetCompiledSource() + +
+           
+static FunctionInformationMap.EntrygetDefaultInstance() + +
+           
+ FunctionInformationMap.EntrygetDefaultInstanceForType() + +
+           
+static com.google.protobuf.Descriptors.DescriptorgetDescriptor() + +
+           
+ intgetId() + +
+           
+ intgetLineNumber() + +
+           
+ StringgetModuleName() + +
+           
+ StringgetName() + +
+           
+ intgetSerializedSize() + +
+           
+ intgetSize() + +
+           
+ StringgetSourceName() + +
+           
+ booleanhasCompiledSource() + +
+           
+ booleanhasId() + +
+           
+ booleanhasLineNumber() + +
+           
+ booleanhasModuleName() + +
+           
+ booleanhasName() + +
+           
+ booleanhasSize() + +
+           
+ booleanhasSourceName() + +
+           
+protected  com.google.protobuf.GeneratedMessage.FieldAccessorTableinternalGetFieldAccessorTable() + +
+           
+ booleanisInitialized() + +
+           
+static FunctionInformationMap.Entry.BuildernewBuilder() + +
+           
+static FunctionInformationMap.Entry.BuildernewBuilder(FunctionInformationMap.Entry prototype) + +
+           
+ FunctionInformationMap.Entry.BuildernewBuilderForType() + +
+           
+protected  FunctionInformationMap.Entry.BuildernewBuilderForType(com.google.protobuf.GeneratedMessage.BuilderParent parent) + +
+           
+static FunctionInformationMap.EntryparseDelimitedFrom(InputStream input) + +
+           
+static FunctionInformationMap.EntryparseDelimitedFrom(InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static FunctionInformationMap.EntryparseFrom(byte[] data) + +
+           
+static FunctionInformationMap.EntryparseFrom(byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static FunctionInformationMap.EntryparseFrom(com.google.protobuf.ByteString data) + +
+           
+static FunctionInformationMap.EntryparseFrom(com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static FunctionInformationMap.EntryparseFrom(com.google.protobuf.CodedInputStream input) + +
+           
+static FunctionInformationMap.EntryparseFrom(com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static FunctionInformationMap.EntryparseFrom(InputStream input) + +
+           
+static FunctionInformationMap.EntryparseFrom(InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+ FunctionInformationMap.Entry.BuildertoBuilder() + +
+           
+protected  ObjectwriteReplace() + +
+           
+ voidwriteTo(com.google.protobuf.CodedOutputStream output) + +
+           
+ + + + + + + +
Methods inherited from class com.google.protobuf.GeneratedMessage
getAllFields, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField, newFileScopedGeneratedExtension, newMessageScopedGeneratedExtension
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessage
equals, hashBoolean, hashCode, hashEnum, hashEnumList, hashFields, hashLong, toString
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessageLite
toByteArray, toByteString, writeDelimitedTo, writeTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageLite
toByteArray, toByteString, writeDelimitedTo, writeTo
+  +

+ + + + + + + + +
+Field Detail
+ +

+ID_FIELD_NUMBER

+
+public static final int ID_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+SOURCE_NAME_FIELD_NUMBER

+
+public static final int SOURCE_NAME_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+LINE_NUMBER_FIELD_NUMBER

+
+public static final int LINE_NUMBER_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+MODULE_NAME_FIELD_NUMBER

+
+public static final int MODULE_NAME_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+SIZE_FIELD_NUMBER

+
+public static final int SIZE_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+NAME_FIELD_NUMBER

+
+public static final int NAME_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+COMPILED_SOURCE_FIELD_NUMBER

+
+public static final int COMPILED_SOURCE_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+ + + + + + + + +
+Method Detail
+ +

+getDefaultInstance

+
+public static FunctionInformationMap.Entry getDefaultInstance()
+
+
+
+
+
+
+
+
+
+ +

+getDefaultInstanceForType

+
+public FunctionInformationMap.Entry getDefaultInstanceForType()
+
+
+
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageLiteOrBuilder
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageOrBuilder
+
+
+
+
+
+
+ +

+getDescriptor

+
+public static final com.google.protobuf.Descriptors.Descriptor getDescriptor()
+
+
+
+
+
+
+
+
+
+ +

+internalGetFieldAccessorTable

+
+protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable()
+
+
+
Specified by:
internalGetFieldAccessorTable in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+
+ +

+hasId

+
+public boolean hasId()
+
+
+
Specified by:
hasId in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+getId

+
+public int getId()
+
+
+
Specified by:
getId in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+hasSourceName

+
+public boolean hasSourceName()
+
+
+
Specified by:
hasSourceName in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+getSourceName

+
+public String getSourceName()
+
+
+
Specified by:
getSourceName in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+hasLineNumber

+
+public boolean hasLineNumber()
+
+
+
Specified by:
hasLineNumber in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+getLineNumber

+
+public int getLineNumber()
+
+
+
Specified by:
getLineNumber in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+hasModuleName

+
+public boolean hasModuleName()
+
+
+
Specified by:
hasModuleName in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+getModuleName

+
+public String getModuleName()
+
+
+
Specified by:
getModuleName in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+hasSize

+
+public boolean hasSize()
+
+
+
Specified by:
hasSize in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+getSize

+
+public int getSize()
+
+
+
Specified by:
getSize in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+hasName

+
+public boolean hasName()
+
+
+
Specified by:
hasName in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+getName

+
+public String getName()
+
+
+
Specified by:
getName in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+hasCompiledSource

+
+public boolean hasCompiledSource()
+
+
+
Specified by:
hasCompiledSource in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+getCompiledSource

+
+public String getCompiledSource()
+
+
+
Specified by:
getCompiledSource in interface FunctionInformationMap.EntryOrBuilder
+
+
+
+
+
+
+ +

+isInitialized

+
+public final boolean isInitialized()
+
+
+
Specified by:
isInitialized in interface com.google.protobuf.MessageLiteOrBuilder
Overrides:
isInitialized in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+
+ +

+writeTo

+
+public void writeTo(com.google.protobuf.CodedOutputStream output)
+             throws IOException
+
+
+
Specified by:
writeTo in interface com.google.protobuf.MessageLite
Overrides:
writeTo in class com.google.protobuf.AbstractMessage
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+getSerializedSize

+
+public int getSerializedSize()
+
+
+
Specified by:
getSerializedSize in interface com.google.protobuf.MessageLite
Overrides:
getSerializedSize in class com.google.protobuf.AbstractMessage
+
+
+
+
+
+
+ +

+writeReplace

+
+protected Object writeReplace()
+                       throws ObjectStreamException
+
+
+
Overrides:
writeReplace in class com.google.protobuf.GeneratedMessage
+
+
+ +
Throws: +
ObjectStreamException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap.Entry parseFrom(com.google.protobuf.ByteString data)
+                                              throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap.Entry parseFrom(com.google.protobuf.ByteString data,
+                                                     com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                              throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap.Entry parseFrom(byte[] data)
+                                              throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap.Entry parseFrom(byte[] data,
+                                                     com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                              throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap.Entry parseFrom(InputStream input)
+                                              throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap.Entry parseFrom(InputStream input,
+                                                     com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                              throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseDelimitedFrom

+
+public static FunctionInformationMap.Entry parseDelimitedFrom(InputStream input)
+                                                       throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseDelimitedFrom

+
+public static FunctionInformationMap.Entry parseDelimitedFrom(InputStream input,
+                                                              com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                                       throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap.Entry parseFrom(com.google.protobuf.CodedInputStream input)
+                                              throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap.Entry parseFrom(com.google.protobuf.CodedInputStream input,
+                                                     com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                              throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+newBuilder

+
+public static FunctionInformationMap.Entry.Builder newBuilder()
+
+
+
+
+
+
+
+
+
+ +

+newBuilderForType

+
+public FunctionInformationMap.Entry.Builder newBuilderForType()
+
+
+
Specified by:
newBuilderForType in interface com.google.protobuf.Message
Specified by:
newBuilderForType in interface com.google.protobuf.MessageLite
+
+
+
+
+
+
+ +

+newBuilder

+
+public static FunctionInformationMap.Entry.Builder newBuilder(FunctionInformationMap.Entry prototype)
+
+
+
+
+
+
+
+
+
+ +

+toBuilder

+
+public FunctionInformationMap.Entry.Builder toBuilder()
+
+
+
Specified by:
toBuilder in interface com.google.protobuf.Message
Specified by:
toBuilder in interface com.google.protobuf.MessageLite
+
+
+
+
+
+
+ +

+newBuilderForType

+
+protected FunctionInformationMap.Entry.Builder newBuilderForType(com.google.protobuf.GeneratedMessage.BuilderParent parent)
+
+
+
Specified by:
newBuilderForType in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.EntryOrBuilder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.EntryOrBuilder.html new file mode 100644 index 0000000..af81dd3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.EntryOrBuilder.html @@ -0,0 +1,534 @@ + + + + + +FunctionInformationMap.EntryOrBuilder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface FunctionInformationMap.EntryOrBuilder

+
+
All Superinterfaces:
com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder
+
+
+
All Known Implementing Classes:
FunctionInformationMap.Entry, FunctionInformationMap.Entry.Builder
+
+
+
Enclosing class:
FunctionInformationMap
+
+
+
+
public static interface FunctionInformationMap.EntryOrBuilder
extends com.google.protobuf.MessageOrBuilder
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StringgetCompiledSource() + +
+           
+ intgetId() + +
+           
+ intgetLineNumber() + +
+           
+ StringgetModuleName() + +
+           
+ StringgetName() + +
+           
+ intgetSize() + +
+           
+ StringgetSourceName() + +
+           
+ booleanhasCompiledSource() + +
+           
+ booleanhasId() + +
+           
+ booleanhasLineNumber() + +
+           
+ booleanhasModuleName() + +
+           
+ booleanhasName() + +
+           
+ booleanhasSize() + +
+           
+ booleanhasSourceName() + +
+           
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getDefaultInstanceForType, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageLiteOrBuilder
isInitialized
+  +

+ + + + + + + + +
+Method Detail
+ +

+hasId

+
+boolean hasId()
+
+
+
+
+
+
+
+
+
+ +

+getId

+
+int getId()
+
+
+
+
+
+
+
+
+
+ +

+hasSourceName

+
+boolean hasSourceName()
+
+
+
+
+
+
+
+
+
+ +

+getSourceName

+
+String getSourceName()
+
+
+
+
+
+
+
+
+
+ +

+hasLineNumber

+
+boolean hasLineNumber()
+
+
+
+
+
+
+
+
+
+ +

+getLineNumber

+
+int getLineNumber()
+
+
+
+
+
+
+
+
+
+ +

+hasModuleName

+
+boolean hasModuleName()
+
+
+
+
+
+
+
+
+
+ +

+getModuleName

+
+String getModuleName()
+
+
+
+
+
+
+
+
+
+ +

+hasSize

+
+boolean hasSize()
+
+
+
+
+
+
+
+
+
+ +

+getSize

+
+int getSize()
+
+
+
+
+
+
+
+
+
+ +

+hasName

+
+boolean hasName()
+
+
+
+
+
+
+
+
+
+ +

+getName

+
+String getName()
+
+
+
+
+
+
+
+
+
+ +

+hasCompiledSource

+
+boolean hasCompiledSource()
+
+
+
+
+
+
+
+
+
+ +

+getCompiledSource

+
+String getCompiledSource()
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.Module.Builder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.Module.Builder.html new file mode 100644 index 0000000..6967693 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.Module.Builder.html @@ -0,0 +1,702 @@ + + + + + +FunctionInformationMap.Module.Builder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class FunctionInformationMap.Module.Builder

+
+java.lang.Object
+  extended by com.google.protobuf.AbstractMessageLite.Builder<BuilderType>
+      extended by com.google.protobuf.AbstractMessage.Builder<BuilderType>
+          extended by com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Module.Builder>
+              extended by com.google.javascript.jscomp.FunctionInformationMap.Module.Builder
+
+
+
All Implemented Interfaces:
FunctionInformationMap.ModuleOrBuilder, com.google.protobuf.Message.Builder, com.google.protobuf.MessageLite.Builder, com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder, Cloneable
+
+
+
Enclosing class:
FunctionInformationMap.Module
+
+
+
+
public static final class FunctionInformationMap.Module.Builder
extends com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Module.Builder>
implements FunctionInformationMap.ModuleOrBuilder
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ FunctionInformationMap.Modulebuild() + +
+           
+ FunctionInformationMap.ModulebuildPartial() + +
+           
+ FunctionInformationMap.Module.Builderclear() + +
+           
+ FunctionInformationMap.Module.BuilderclearCompiledSource() + +
+           
+ FunctionInformationMap.Module.BuilderclearName() + +
+           
+ FunctionInformationMap.Module.Builderclone() + +
+           
+ StringgetCompiledSource() + +
+           
+ FunctionInformationMap.ModulegetDefaultInstanceForType() + +
+           
+static com.google.protobuf.Descriptors.DescriptorgetDescriptor() + +
+           
+ com.google.protobuf.Descriptors.DescriptorgetDescriptorForType() + +
+           
+ StringgetName() + +
+           
+ booleanhasCompiledSource() + +
+           
+ booleanhasName() + +
+           
+protected  com.google.protobuf.GeneratedMessage.FieldAccessorTableinternalGetFieldAccessorTable() + +
+           
+ booleanisInitialized() + +
+           
+ FunctionInformationMap.Module.BuildermergeFrom(com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+ FunctionInformationMap.Module.BuildermergeFrom(FunctionInformationMap.Module other) + +
+           
+ FunctionInformationMap.Module.BuildermergeFrom(com.google.protobuf.Message other) + +
+           
+ FunctionInformationMap.Module.BuildersetCompiledSource(String value) + +
+           
+ FunctionInformationMap.Module.BuildersetName(String value) + +
+           
+ + + + + + + +
Methods inherited from class com.google.protobuf.GeneratedMessage.Builder
addRepeatedField, clearField, getAllFields, getField, getParentForChildren, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField, isClean, markClean, mergeUnknownFields, newBuilderForField, onBuilt, onChanged, parseUnknownField, setField, setRepeatedField, setUnknownFields
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessage.Builder
mergeDelimitedFrom, mergeDelimitedFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, newUninitializedMessageException
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessageLite.Builder
addAll, newUninitializedMessageException
+ + + + + + + +
Methods inherited from class java.lang.Object
equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+  +

+ + + + + + + + +
+Method Detail
+ +

+getDescriptor

+
+public static final com.google.protobuf.Descriptors.Descriptor getDescriptor()
+
+
+
+
+
+
+
+
+
+ +

+internalGetFieldAccessorTable

+
+protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable()
+
+
+
Specified by:
internalGetFieldAccessorTable in class com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Module.Builder>
+
+
+
+
+
+
+ +

+clear

+
+public FunctionInformationMap.Module.Builder clear()
+
+
+
Specified by:
clear in interface com.google.protobuf.Message.Builder
Specified by:
clear in interface com.google.protobuf.MessageLite.Builder
Overrides:
clear in class com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Module.Builder>
+
+
+
+
+
+
+ +

+clone

+
+public FunctionInformationMap.Module.Builder clone()
+
+
+
Specified by:
clone in interface com.google.protobuf.Message.Builder
Specified by:
clone in interface com.google.protobuf.MessageLite.Builder
Overrides:
clone in class com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Module.Builder>
+
+
+
+
+
+
+ +

+getDescriptorForType

+
+public com.google.protobuf.Descriptors.Descriptor getDescriptorForType()
+
+
+
Specified by:
getDescriptorForType in interface com.google.protobuf.Message.Builder
Specified by:
getDescriptorForType in interface com.google.protobuf.MessageOrBuilder
Overrides:
getDescriptorForType in class com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Module.Builder>
+
+
+
+
+
+
+ +

+getDefaultInstanceForType

+
+public FunctionInformationMap.Module getDefaultInstanceForType()
+
+
+
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageLiteOrBuilder
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageOrBuilder
+
+
+
+
+
+
+ +

+build

+
+public FunctionInformationMap.Module build()
+
+
+
Specified by:
build in interface com.google.protobuf.Message.Builder
Specified by:
build in interface com.google.protobuf.MessageLite.Builder
+
+
+
+
+
+
+ +

+buildPartial

+
+public FunctionInformationMap.Module buildPartial()
+
+
+
Specified by:
buildPartial in interface com.google.protobuf.Message.Builder
Specified by:
buildPartial in interface com.google.protobuf.MessageLite.Builder
+
+
+
+
+
+
+ +

+mergeFrom

+
+public FunctionInformationMap.Module.Builder mergeFrom(com.google.protobuf.Message other)
+
+
+
Specified by:
mergeFrom in interface com.google.protobuf.Message.Builder
Overrides:
mergeFrom in class com.google.protobuf.AbstractMessage.Builder<FunctionInformationMap.Module.Builder>
+
+
+
+
+
+
+ +

+mergeFrom

+
+public FunctionInformationMap.Module.Builder mergeFrom(FunctionInformationMap.Module other)
+
+
+
+
+
+
+
+
+
+ +

+isInitialized

+
+public final boolean isInitialized()
+
+
+
Specified by:
isInitialized in interface com.google.protobuf.MessageLiteOrBuilder
Overrides:
isInitialized in class com.google.protobuf.GeneratedMessage.Builder<FunctionInformationMap.Module.Builder>
+
+
+
+
+
+
+ +

+mergeFrom

+
+public FunctionInformationMap.Module.Builder mergeFrom(com.google.protobuf.CodedInputStream input,
+                                                       com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                                throws IOException
+
+
+
Specified by:
mergeFrom in interface com.google.protobuf.Message.Builder
Specified by:
mergeFrom in interface com.google.protobuf.MessageLite.Builder
Overrides:
mergeFrom in class com.google.protobuf.AbstractMessage.Builder<FunctionInformationMap.Module.Builder>
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+hasName

+
+public boolean hasName()
+
+
+
Specified by:
hasName in interface FunctionInformationMap.ModuleOrBuilder
+
+
+
+
+
+
+ +

+getName

+
+public String getName()
+
+
+
Specified by:
getName in interface FunctionInformationMap.ModuleOrBuilder
+
+
+
+
+
+
+ +

+setName

+
+public FunctionInformationMap.Module.Builder setName(String value)
+
+
+
+
+
+
+
+
+
+ +

+clearName

+
+public FunctionInformationMap.Module.Builder clearName()
+
+
+
+
+
+
+
+
+
+ +

+hasCompiledSource

+
+public boolean hasCompiledSource()
+
+
+
Specified by:
hasCompiledSource in interface FunctionInformationMap.ModuleOrBuilder
+
+
+
+
+
+
+ +

+getCompiledSource

+
+public String getCompiledSource()
+
+
+
Specified by:
getCompiledSource in interface FunctionInformationMap.ModuleOrBuilder
+
+
+
+
+
+
+ +

+setCompiledSource

+
+public FunctionInformationMap.Module.Builder setCompiledSource(String value)
+
+
+
+
+
+
+
+
+
+ +

+clearCompiledSource

+
+public FunctionInformationMap.Module.Builder clearCompiledSource()
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.Module.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.Module.html new file mode 100644 index 0000000..5c58e7e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.Module.html @@ -0,0 +1,998 @@ + + + + + +FunctionInformationMap.Module (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class FunctionInformationMap.Module

+
+java.lang.Object
+  extended by com.google.protobuf.AbstractMessageLite
+      extended by com.google.protobuf.AbstractMessage
+          extended by com.google.protobuf.GeneratedMessage
+              extended by com.google.javascript.jscomp.FunctionInformationMap.Module
+
+
+
All Implemented Interfaces:
FunctionInformationMap.ModuleOrBuilder, com.google.protobuf.Message, com.google.protobuf.MessageLite, com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder, Serializable
+
+
+
Enclosing class:
FunctionInformationMap
+
+
+
+
public static final class FunctionInformationMap.Module
extends com.google.protobuf.GeneratedMessage
implements FunctionInformationMap.ModuleOrBuilder
+ + +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classFunctionInformationMap.Module.Builder + +
+           
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.protobuf.GeneratedMessage
com.google.protobuf.GeneratedMessage.BuilderParent, com.google.protobuf.GeneratedMessage.ExtendableBuilder<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage,BuilderType extends com.google.protobuf.GeneratedMessage.ExtendableBuilder>, com.google.protobuf.GeneratedMessage.ExtendableMessage<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage>, com.google.protobuf.GeneratedMessage.ExtendableMessageOrBuilder<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage>, com.google.protobuf.GeneratedMessage.FieldAccessorTable, com.google.protobuf.GeneratedMessage.GeneratedExtension<ContainingType extends com.google.protobuf.Message,Type>
+  + + + + + + + + + + + + + + + +
+Field Summary
+static intCOMPILED_SOURCE_FIELD_NUMBER + +
+           
+static intNAME_FIELD_NUMBER + +
+           
+ + + + + + + +
Fields inherited from class com.google.protobuf.GeneratedMessage
alwaysUseFieldBuilders
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StringgetCompiledSource() + +
+           
+static FunctionInformationMap.ModulegetDefaultInstance() + +
+           
+ FunctionInformationMap.ModulegetDefaultInstanceForType() + +
+           
+static com.google.protobuf.Descriptors.DescriptorgetDescriptor() + +
+           
+ StringgetName() + +
+           
+ intgetSerializedSize() + +
+           
+ booleanhasCompiledSource() + +
+           
+ booleanhasName() + +
+           
+protected  com.google.protobuf.GeneratedMessage.FieldAccessorTableinternalGetFieldAccessorTable() + +
+           
+ booleanisInitialized() + +
+           
+static FunctionInformationMap.Module.BuildernewBuilder() + +
+           
+static FunctionInformationMap.Module.BuildernewBuilder(FunctionInformationMap.Module prototype) + +
+           
+ FunctionInformationMap.Module.BuildernewBuilderForType() + +
+           
+protected  FunctionInformationMap.Module.BuildernewBuilderForType(com.google.protobuf.GeneratedMessage.BuilderParent parent) + +
+           
+static FunctionInformationMap.ModuleparseDelimitedFrom(InputStream input) + +
+           
+static FunctionInformationMap.ModuleparseDelimitedFrom(InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static FunctionInformationMap.ModuleparseFrom(byte[] data) + +
+           
+static FunctionInformationMap.ModuleparseFrom(byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static FunctionInformationMap.ModuleparseFrom(com.google.protobuf.ByteString data) + +
+           
+static FunctionInformationMap.ModuleparseFrom(com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static FunctionInformationMap.ModuleparseFrom(com.google.protobuf.CodedInputStream input) + +
+           
+static FunctionInformationMap.ModuleparseFrom(com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static FunctionInformationMap.ModuleparseFrom(InputStream input) + +
+           
+static FunctionInformationMap.ModuleparseFrom(InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+ FunctionInformationMap.Module.BuildertoBuilder() + +
+           
+protected  ObjectwriteReplace() + +
+           
+ voidwriteTo(com.google.protobuf.CodedOutputStream output) + +
+           
+ + + + + + + +
Methods inherited from class com.google.protobuf.GeneratedMessage
getAllFields, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField, newFileScopedGeneratedExtension, newMessageScopedGeneratedExtension
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessage
equals, hashBoolean, hashCode, hashEnum, hashEnumList, hashFields, hashLong, toString
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessageLite
toByteArray, toByteString, writeDelimitedTo, writeTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageLite
toByteArray, toByteString, writeDelimitedTo, writeTo
+  +

+ + + + + + + + +
+Field Detail
+ +

+NAME_FIELD_NUMBER

+
+public static final int NAME_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+COMPILED_SOURCE_FIELD_NUMBER

+
+public static final int COMPILED_SOURCE_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+ + + + + + + + +
+Method Detail
+ +

+getDefaultInstance

+
+public static FunctionInformationMap.Module getDefaultInstance()
+
+
+
+
+
+
+
+
+
+ +

+getDefaultInstanceForType

+
+public FunctionInformationMap.Module getDefaultInstanceForType()
+
+
+
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageLiteOrBuilder
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageOrBuilder
+
+
+
+
+
+
+ +

+getDescriptor

+
+public static final com.google.protobuf.Descriptors.Descriptor getDescriptor()
+
+
+
+
+
+
+
+
+
+ +

+internalGetFieldAccessorTable

+
+protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable()
+
+
+
Specified by:
internalGetFieldAccessorTable in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+
+ +

+hasName

+
+public boolean hasName()
+
+
+
Specified by:
hasName in interface FunctionInformationMap.ModuleOrBuilder
+
+
+
+
+
+
+ +

+getName

+
+public String getName()
+
+
+
Specified by:
getName in interface FunctionInformationMap.ModuleOrBuilder
+
+
+
+
+
+
+ +

+hasCompiledSource

+
+public boolean hasCompiledSource()
+
+
+
Specified by:
hasCompiledSource in interface FunctionInformationMap.ModuleOrBuilder
+
+
+
+
+
+
+ +

+getCompiledSource

+
+public String getCompiledSource()
+
+
+
Specified by:
getCompiledSource in interface FunctionInformationMap.ModuleOrBuilder
+
+
+
+
+
+
+ +

+isInitialized

+
+public final boolean isInitialized()
+
+
+
Specified by:
isInitialized in interface com.google.protobuf.MessageLiteOrBuilder
Overrides:
isInitialized in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+
+ +

+writeTo

+
+public void writeTo(com.google.protobuf.CodedOutputStream output)
+             throws IOException
+
+
+
Specified by:
writeTo in interface com.google.protobuf.MessageLite
Overrides:
writeTo in class com.google.protobuf.AbstractMessage
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+getSerializedSize

+
+public int getSerializedSize()
+
+
+
Specified by:
getSerializedSize in interface com.google.protobuf.MessageLite
Overrides:
getSerializedSize in class com.google.protobuf.AbstractMessage
+
+
+
+
+
+
+ +

+writeReplace

+
+protected Object writeReplace()
+                       throws ObjectStreamException
+
+
+
Overrides:
writeReplace in class com.google.protobuf.GeneratedMessage
+
+
+ +
Throws: +
ObjectStreamException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap.Module parseFrom(com.google.protobuf.ByteString data)
+                                               throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap.Module parseFrom(com.google.protobuf.ByteString data,
+                                                      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                               throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap.Module parseFrom(byte[] data)
+                                               throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap.Module parseFrom(byte[] data,
+                                                      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                               throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap.Module parseFrom(InputStream input)
+                                               throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap.Module parseFrom(InputStream input,
+                                                      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                               throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseDelimitedFrom

+
+public static FunctionInformationMap.Module parseDelimitedFrom(InputStream input)
+                                                        throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseDelimitedFrom

+
+public static FunctionInformationMap.Module parseDelimitedFrom(InputStream input,
+                                                               com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                                        throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap.Module parseFrom(com.google.protobuf.CodedInputStream input)
+                                               throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap.Module parseFrom(com.google.protobuf.CodedInputStream input,
+                                                      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                               throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+newBuilder

+
+public static FunctionInformationMap.Module.Builder newBuilder()
+
+
+
+
+
+
+
+
+
+ +

+newBuilderForType

+
+public FunctionInformationMap.Module.Builder newBuilderForType()
+
+
+
Specified by:
newBuilderForType in interface com.google.protobuf.Message
Specified by:
newBuilderForType in interface com.google.protobuf.MessageLite
+
+
+
+
+
+
+ +

+newBuilder

+
+public static FunctionInformationMap.Module.Builder newBuilder(FunctionInformationMap.Module prototype)
+
+
+
+
+
+
+
+
+
+ +

+toBuilder

+
+public FunctionInformationMap.Module.Builder toBuilder()
+
+
+
Specified by:
toBuilder in interface com.google.protobuf.Message
Specified by:
toBuilder in interface com.google.protobuf.MessageLite
+
+
+
+
+
+
+ +

+newBuilderForType

+
+protected FunctionInformationMap.Module.Builder newBuilderForType(com.google.protobuf.GeneratedMessage.BuilderParent parent)
+
+
+
Specified by:
newBuilderForType in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.ModuleOrBuilder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.ModuleOrBuilder.html new file mode 100644 index 0000000..1cd9966 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.ModuleOrBuilder.html @@ -0,0 +1,314 @@ + + + + + +FunctionInformationMap.ModuleOrBuilder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface FunctionInformationMap.ModuleOrBuilder

+
+
All Superinterfaces:
com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder
+
+
+
All Known Implementing Classes:
FunctionInformationMap.Module, FunctionInformationMap.Module.Builder
+
+
+
Enclosing class:
FunctionInformationMap
+
+
+
+
public static interface FunctionInformationMap.ModuleOrBuilder
extends com.google.protobuf.MessageOrBuilder
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StringgetCompiledSource() + +
+           
+ StringgetName() + +
+           
+ booleanhasCompiledSource() + +
+           
+ booleanhasName() + +
+           
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getDefaultInstanceForType, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageLiteOrBuilder
isInitialized
+  +

+ + + + + + + + +
+Method Detail
+ +

+hasName

+
+boolean hasName()
+
+
+
+
+
+
+
+
+
+ +

+getName

+
+String getName()
+
+
+
+
+
+
+
+
+
+ +

+hasCompiledSource

+
+boolean hasCompiledSource()
+
+
+
+
+
+
+
+
+
+ +

+getCompiledSource

+
+String getCompiledSource()
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.html new file mode 100644 index 0000000..8bf1f5a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMap.html @@ -0,0 +1,1159 @@ + + + + + +FunctionInformationMap (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class FunctionInformationMap

+
+java.lang.Object
+  extended by com.google.protobuf.AbstractMessageLite
+      extended by com.google.protobuf.AbstractMessage
+          extended by com.google.protobuf.GeneratedMessage
+              extended by com.google.javascript.jscomp.FunctionInformationMap
+
+
+
All Implemented Interfaces:
FunctionInformationMapOrBuilder, com.google.protobuf.Message, com.google.protobuf.MessageLite, com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder, Serializable
+
+
+
+
public final class FunctionInformationMap
extends com.google.protobuf.GeneratedMessage
implements FunctionInformationMapOrBuilder
+ + +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Nested Class Summary
+static classFunctionInformationMap.Builder + +
+           
+static classFunctionInformationMap.Entry + +
+           
+static interfaceFunctionInformationMap.EntryOrBuilder + +
+           
+static classFunctionInformationMap.Module + +
+           
+static interfaceFunctionInformationMap.ModuleOrBuilder + +
+           
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.protobuf.GeneratedMessage
com.google.protobuf.GeneratedMessage.BuilderParent, com.google.protobuf.GeneratedMessage.ExtendableBuilder<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage,BuilderType extends com.google.protobuf.GeneratedMessage.ExtendableBuilder>, com.google.protobuf.GeneratedMessage.ExtendableMessage<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage>, com.google.protobuf.GeneratedMessage.ExtendableMessageOrBuilder<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage>, com.google.protobuf.GeneratedMessage.FieldAccessorTable, com.google.protobuf.GeneratedMessage.GeneratedExtension<ContainingType extends com.google.protobuf.Message,Type>
+  + + + + + + + + + + + + + + + +
+Field Summary
+static intENTRY_FIELD_NUMBER + +
+           
+static intMODULE_FIELD_NUMBER + +
+           
+ + + + + + + +
Fields inherited from class com.google.protobuf.GeneratedMessage
alwaysUseFieldBuilders
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static FunctionInformationMapgetDefaultInstance() + +
+           
+ FunctionInformationMapgetDefaultInstanceForType() + +
+           
+static com.google.protobuf.Descriptors.DescriptorgetDescriptor() + +
+           
+ FunctionInformationMap.EntrygetEntry(int index) + +
+           
+ intgetEntryCount() + +
+           
+ List<FunctionInformationMap.Entry>getEntryList() + +
+           
+ FunctionInformationMap.EntryOrBuildergetEntryOrBuilder(int index) + +
+           
+ List<? extends FunctionInformationMap.EntryOrBuilder>getEntryOrBuilderList() + +
+           
+ FunctionInformationMap.ModulegetModule(int index) + +
+           
+ intgetModuleCount() + +
+           
+ List<FunctionInformationMap.Module>getModuleList() + +
+           
+ FunctionInformationMap.ModuleOrBuildergetModuleOrBuilder(int index) + +
+           
+ List<? extends FunctionInformationMap.ModuleOrBuilder>getModuleOrBuilderList() + +
+           
+ intgetSerializedSize() + +
+           
+protected  com.google.protobuf.GeneratedMessage.FieldAccessorTableinternalGetFieldAccessorTable() + +
+           
+ booleanisInitialized() + +
+           
+static FunctionInformationMap.BuildernewBuilder() + +
+           
+static FunctionInformationMap.BuildernewBuilder(FunctionInformationMap prototype) + +
+           
+ FunctionInformationMap.BuildernewBuilderForType() + +
+           
+protected  FunctionInformationMap.BuildernewBuilderForType(com.google.protobuf.GeneratedMessage.BuilderParent parent) + +
+           
+static FunctionInformationMapparseDelimitedFrom(InputStream input) + +
+           
+static FunctionInformationMapparseDelimitedFrom(InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static FunctionInformationMapparseFrom(byte[] data) + +
+           
+static FunctionInformationMapparseFrom(byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static FunctionInformationMapparseFrom(com.google.protobuf.ByteString data) + +
+           
+static FunctionInformationMapparseFrom(com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static FunctionInformationMapparseFrom(com.google.protobuf.CodedInputStream input) + +
+           
+static FunctionInformationMapparseFrom(com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static FunctionInformationMapparseFrom(InputStream input) + +
+           
+static FunctionInformationMapparseFrom(InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+ FunctionInformationMap.BuildertoBuilder() + +
+           
+protected  ObjectwriteReplace() + +
+           
+ voidwriteTo(com.google.protobuf.CodedOutputStream output) + +
+           
+ + + + + + + +
Methods inherited from class com.google.protobuf.GeneratedMessage
getAllFields, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField, newFileScopedGeneratedExtension, newMessageScopedGeneratedExtension
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessage
equals, hashBoolean, hashCode, hashEnum, hashEnumList, hashFields, hashLong, toString
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessageLite
toByteArray, toByteString, writeDelimitedTo, writeTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageLite
toByteArray, toByteString, writeDelimitedTo, writeTo
+  +

+ + + + + + + + +
+Field Detail
+ +

+ENTRY_FIELD_NUMBER

+
+public static final int ENTRY_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+MODULE_FIELD_NUMBER

+
+public static final int MODULE_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+ + + + + + + + +
+Method Detail
+ +

+getDefaultInstance

+
+public static FunctionInformationMap getDefaultInstance()
+
+
+
+
+
+
+
+
+
+ +

+getDefaultInstanceForType

+
+public FunctionInformationMap getDefaultInstanceForType()
+
+
+
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageLiteOrBuilder
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageOrBuilder
+
+
+
+
+
+
+ +

+getDescriptor

+
+public static final com.google.protobuf.Descriptors.Descriptor getDescriptor()
+
+
+
+
+
+
+
+
+
+ +

+internalGetFieldAccessorTable

+
+protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable()
+
+
+
Specified by:
internalGetFieldAccessorTable in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+
+ +

+getEntryList

+
+public List<FunctionInformationMap.Entry> getEntryList()
+
+
+
Specified by:
getEntryList in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+getEntryOrBuilderList

+
+public List<? extends FunctionInformationMap.EntryOrBuilder> getEntryOrBuilderList()
+
+
+
Specified by:
getEntryOrBuilderList in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+getEntryCount

+
+public int getEntryCount()
+
+
+
Specified by:
getEntryCount in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+getEntry

+
+public FunctionInformationMap.Entry getEntry(int index)
+
+
+
Specified by:
getEntry in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+getEntryOrBuilder

+
+public FunctionInformationMap.EntryOrBuilder getEntryOrBuilder(int index)
+
+
+
Specified by:
getEntryOrBuilder in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+getModuleList

+
+public List<FunctionInformationMap.Module> getModuleList()
+
+
+
Specified by:
getModuleList in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+getModuleOrBuilderList

+
+public List<? extends FunctionInformationMap.ModuleOrBuilder> getModuleOrBuilderList()
+
+
+
Specified by:
getModuleOrBuilderList in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+getModuleCount

+
+public int getModuleCount()
+
+
+
Specified by:
getModuleCount in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+getModule

+
+public FunctionInformationMap.Module getModule(int index)
+
+
+
Specified by:
getModule in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+getModuleOrBuilder

+
+public FunctionInformationMap.ModuleOrBuilder getModuleOrBuilder(int index)
+
+
+
Specified by:
getModuleOrBuilder in interface FunctionInformationMapOrBuilder
+
+
+
+
+
+
+ +

+isInitialized

+
+public final boolean isInitialized()
+
+
+
Specified by:
isInitialized in interface com.google.protobuf.MessageLiteOrBuilder
Overrides:
isInitialized in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+
+ +

+writeTo

+
+public void writeTo(com.google.protobuf.CodedOutputStream output)
+             throws IOException
+
+
+
Specified by:
writeTo in interface com.google.protobuf.MessageLite
Overrides:
writeTo in class com.google.protobuf.AbstractMessage
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+getSerializedSize

+
+public int getSerializedSize()
+
+
+
Specified by:
getSerializedSize in interface com.google.protobuf.MessageLite
Overrides:
getSerializedSize in class com.google.protobuf.AbstractMessage
+
+
+
+
+
+
+ +

+writeReplace

+
+protected Object writeReplace()
+                       throws ObjectStreamException
+
+
+
Overrides:
writeReplace in class com.google.protobuf.GeneratedMessage
+
+
+ +
Throws: +
ObjectStreamException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap parseFrom(com.google.protobuf.ByteString data)
+                                        throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap parseFrom(com.google.protobuf.ByteString data,
+                                               com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                        throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap parseFrom(byte[] data)
+                                        throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap parseFrom(byte[] data,
+                                               com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                        throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap parseFrom(InputStream input)
+                                        throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap parseFrom(InputStream input,
+                                               com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                        throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseDelimitedFrom

+
+public static FunctionInformationMap parseDelimitedFrom(InputStream input)
+                                                 throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseDelimitedFrom

+
+public static FunctionInformationMap parseDelimitedFrom(InputStream input,
+                                                        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                                 throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap parseFrom(com.google.protobuf.CodedInputStream input)
+                                        throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static FunctionInformationMap parseFrom(com.google.protobuf.CodedInputStream input,
+                                               com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                        throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+newBuilder

+
+public static FunctionInformationMap.Builder newBuilder()
+
+
+
+
+
+
+
+
+
+ +

+newBuilderForType

+
+public FunctionInformationMap.Builder newBuilderForType()
+
+
+
Specified by:
newBuilderForType in interface com.google.protobuf.Message
Specified by:
newBuilderForType in interface com.google.protobuf.MessageLite
+
+
+
+
+
+
+ +

+newBuilder

+
+public static FunctionInformationMap.Builder newBuilder(FunctionInformationMap prototype)
+
+
+
+
+
+
+
+
+
+ +

+toBuilder

+
+public FunctionInformationMap.Builder toBuilder()
+
+
+
Specified by:
toBuilder in interface com.google.protobuf.Message
Specified by:
toBuilder in interface com.google.protobuf.MessageLite
+
+
+
+
+
+
+ +

+newBuilderForType

+
+protected FunctionInformationMap.Builder newBuilderForType(com.google.protobuf.GeneratedMessage.BuilderParent parent)
+
+
+
Specified by:
newBuilderForType in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMapOrBuilder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMapOrBuilder.html new file mode 100644 index 0000000..32406b0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/FunctionInformationMapOrBuilder.html @@ -0,0 +1,443 @@ + + + + + +FunctionInformationMapOrBuilder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface FunctionInformationMapOrBuilder

+
+
All Superinterfaces:
com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder
+
+
+
All Known Implementing Classes:
FunctionInformationMap, FunctionInformationMap.Builder
+
+
+
+
public interface FunctionInformationMapOrBuilder
extends com.google.protobuf.MessageOrBuilder
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ FunctionInformationMap.EntrygetEntry(int index) + +
+           
+ intgetEntryCount() + +
+           
+ List<FunctionInformationMap.Entry>getEntryList() + +
+           
+ FunctionInformationMap.EntryOrBuildergetEntryOrBuilder(int index) + +
+           
+ List<? extends FunctionInformationMap.EntryOrBuilder>getEntryOrBuilderList() + +
+           
+ FunctionInformationMap.ModulegetModule(int index) + +
+           
+ intgetModuleCount() + +
+           
+ List<FunctionInformationMap.Module>getModuleList() + +
+           
+ FunctionInformationMap.ModuleOrBuildergetModuleOrBuilder(int index) + +
+           
+ List<? extends FunctionInformationMap.ModuleOrBuilder>getModuleOrBuilderList() + +
+           
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getDefaultInstanceForType, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageLiteOrBuilder
isInitialized
+  +

+ + + + + + + + +
+Method Detail
+ +

+getEntryList

+
+List<FunctionInformationMap.Entry> getEntryList()
+
+
+
+
+
+
+
+
+
+ +

+getEntry

+
+FunctionInformationMap.Entry getEntry(int index)
+
+
+
+
+
+
+
+
+
+ +

+getEntryCount

+
+int getEntryCount()
+
+
+
+
+
+
+
+
+
+ +

+getEntryOrBuilderList

+
+List<? extends FunctionInformationMap.EntryOrBuilder> getEntryOrBuilderList()
+
+
+
+
+
+
+
+
+
+ +

+getEntryOrBuilder

+
+FunctionInformationMap.EntryOrBuilder getEntryOrBuilder(int index)
+
+
+
+
+
+
+
+
+
+ +

+getModuleList

+
+List<FunctionInformationMap.Module> getModuleList()
+
+
+
+
+
+
+
+
+
+ +

+getModule

+
+FunctionInformationMap.Module getModule(int index)
+
+
+
+
+
+
+
+
+
+ +

+getModuleCount

+
+int getModuleCount()
+
+
+
+
+
+
+
+
+
+ +

+getModuleOrBuilderList

+
+List<? extends FunctionInformationMap.ModuleOrBuilder> getModuleOrBuilderList()
+
+
+
+
+
+
+
+
+
+ +

+getModuleOrBuilder

+
+FunctionInformationMap.ModuleOrBuilder getModuleOrBuilder(int index)
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/GoogleCodingConvention.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/GoogleCodingConvention.html new file mode 100644 index 0000000..74b937a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/GoogleCodingConvention.html @@ -0,0 +1,556 @@ + + + + + +GoogleCodingConvention (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class GoogleCodingConvention

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CodingConventions.Proxy
+      extended by com.google.javascript.jscomp.GoogleCodingConvention
+
+
+
All Implemented Interfaces:
CodingConvention, Serializable
+
+
+
+
public class GoogleCodingConvention
extends CodingConventions.Proxy
+ + +

+This describes the Google-specific JavaScript coding conventions. + Within Google, variable names are semantically significant. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from interface com.google.javascript.jscomp.CodingConvention
CodingConvention.AssertionFunctionSpec, CodingConvention.Bind, CodingConvention.DelegateRelationship, CodingConvention.ObjectLiteralCast, CodingConvention.SubclassRelationship, CodingConvention.SubclassType
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.jscomp.CodingConventions.Proxy
nextConvention
+  + + + + + + + + + + + + + +
+Constructor Summary
GoogleCodingConvention() + +
+          By default, decorate the ClosureCodingConvention.
GoogleCodingConvention(CodingConvention convention) + +
+          Decorates a wrapped CodingConvention.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleanisConstant(String name) + +
+          This checks whether a given variable name, such as a name in all-caps + should be treated as if it had the @const annotation.
+ booleanisConstantKey(String name) + +
+          This checks whether a given key of an object literal, such as a + name in all-caps should be treated as if it had the @const + annotation.
+ booleanisExported(String name, + boolean local) + +
+          Checks whether a global variable or function name should be treated as + exported, or externally referenceable.
+ booleanisOptionalParameter(Node parameter) + +
+          This checks whether a given parameter name should be treated as an + optional parameter as far as type checking or function call arg count + checking is concerned.
+ booleanisPrivate(String name) + +
+          Checks whether a name should be considered private.
+ booleanisValidEnumKey(String key) + +
+          This checks that a given key may be used as a key for an enum.
+ booleanisVarArgsParameter(Node parameter) + +
+          This checks whether a given parameter should be treated as a marker + for a variable argument list function.
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.CodingConventions.Proxy
applyDelegateRelationship, applySingletonGetter, applySubclassRelationship, checkForCallingConventionDefiningCalls, defineDelegateProxyPrototypeProperties, describeFunctionBind, extractClassNameIfProvide, extractClassNameIfRequire, getAbstractMethodName, getAssertionFunctions, getClassesDefinedByCall, getDelegateRelationship, getDelegateSuperclassName, getExportPropertyFunction, getExportSymbolFunction, getGlobalObject, getObjectLiteralCast, getSingletonGetterClassName, identifyTypeDeclarationCall, isExported, isPropertyTestFunction, isPrototypeAlias, isSuperClassReference
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+GoogleCodingConvention

+
+public GoogleCodingConvention()
+
+
By default, decorate the ClosureCodingConvention. +

+

+
+ +

+GoogleCodingConvention

+
+public GoogleCodingConvention(CodingConvention convention)
+
+
Decorates a wrapped CodingConvention. +

+

+ + + + + + + + +
+Method Detail
+ +

+isConstant

+
+public boolean isConstant(String name)
+
+
This checks whether a given variable name, such as a name in all-caps + should be treated as if it had the @const annotation. + +

This enforces the Google const name convention, that the first character + after the last $ must be an upper-case letter and all subsequent letters + must be upper case. The name must be at least 2 characters long. + +

Examples: +

+      aaa          Not constant - lower-case letters in the name
+      A            Not constant - too short
+      goog$A       Constant - letters after the $ are upper-case.
+      AA17         Constant - digits can appear after the first letter
+      goog$7A      Not constant - first character after the $ must be
+                   upper case.
+      $A           Constant - doesn't have to be anything in front of the $
+ 
+

+

+
Specified by:
isConstant in interface CodingConvention
Overrides:
isConstant in class CodingConventions.Proxy
+
+
+
Parameters:
name - potentially constant variable name +
Returns:
true if the name should be treated as a constant.
+
+
+
+ +

+isConstantKey

+
+public boolean isConstantKey(String name)
+
+
Description copied from interface: CodingConvention
+
This checks whether a given key of an object literal, such as a + name in all-caps should be treated as if it had the @const + annotation. +

+

+
Specified by:
isConstantKey in interface CodingConvention
Overrides:
isConstantKey in class CodingConventions.Proxy
+
+
+
+
+
+
+ +

+isValidEnumKey

+
+public boolean isValidEnumKey(String key)
+
+
This checks that a given key may be used as a key for an enum. + +

This enforces Google's convention about enum key names. They must match + the regular expression [A-Z0-9][A-Z0-9_]*. + +

Examples: +

    +
  • A
  • +
  • 213
  • +
  • FOO_BAR
  • +
+

+

+
Specified by:
isValidEnumKey in interface CodingConvention
Overrides:
isValidEnumKey in class CodingConventions.Proxy
+
+
+
Parameters:
key - the potential key to an enum +
Returns:
true if the key may be used as an enum key, + false otherwise
+
+
+
+ +

+isOptionalParameter

+
+public boolean isOptionalParameter(Node parameter)
+
+
This checks whether a given parameter name should be treated as an + optional parameter as far as type checking or function call arg count + checking is concerned. Note that an optional function parameter may be + declared as a simple type and is automatically converted to a union of the + declared type and Undefined. + +

In Google code, parameter names beginning with opt_ are + treated as optional arguments. +

+

+
Specified by:
isOptionalParameter in interface CodingConvention
Overrides:
isOptionalParameter in class CodingConventions.Proxy
+
+
+
Parameters:
parameter - The parameter's node. +
Returns:
true if the parameter should be treated as an optional + parameter.
+
+
+
+ +

+isVarArgsParameter

+
+public boolean isVarArgsParameter(Node parameter)
+
+
Description copied from interface: CodingConvention
+
This checks whether a given parameter should be treated as a marker + for a variable argument list function. A VarArgs parameter must be the + last parameter in a function declaration. +

+

+
Specified by:
isVarArgsParameter in interface CodingConvention
Overrides:
isVarArgsParameter in class CodingConventions.Proxy
+
+
+
Parameters:
parameter - The parameter's node. +
Returns:
true if the parameter should be treated as a variable + length parameter.
+
+
+
+ +

+isExported

+
+public boolean isExported(String name,
+                          boolean local)
+
+
Checks whether a global variable or function name should be treated as + exported, or externally referenceable. + +

In Google code, any global name starting with an underscore is + considered exported. +

+

+
Specified by:
isExported in interface CodingConvention
Overrides:
isExported in class CodingConventions.Proxy
+
+
+
Parameters:
name - A global variable or function name.
local - true if the name is a local variable. +
Returns:
true if the name should be considered exported.
+
+
+
+ +

+isPrivate

+
+public boolean isPrivate(String name)
+
+
Checks whether a name should be considered private. Private global + variables and functions can only be referenced within the source file in + which they are declared. Private properties and methods should only be + accessed by the class that defines them. + +

In Google code, private names end with an underscore, and exported + names are never considered private (see isExported(java.lang.String, boolean)). +

+

+
Specified by:
isPrivate in interface CodingConvention
Overrides:
isPrivate in class CodingConventions.Proxy
+
+
+
Parameters:
name - The name of a global variable or function, or a method or + property. +
Returns:
true if the name should be considered private.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.html new file mode 100644 index 0000000..aeda557 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.html @@ -0,0 +1,302 @@ + + + + + +GoogleJsMessageIdGenerator (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class GoogleJsMessageIdGenerator

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.GoogleJsMessageIdGenerator
+
+
+
All Implemented Interfaces:
JsMessage.IdGenerator
+
+
+
+
public class GoogleJsMessageIdGenerator
extends Object
implements JsMessage.IdGenerator
+ + +

+An JsMessage.IdGenerator designed to play nicely with Google's Translation + systems. Each message is scoped to a project id, so that it does + not conflict with other messages at Google. +

+ Just as reminder what key type used in different formats: +

    +
  1. XMB - id. We export using this format. +
  2. XTB - id. Internal, result of translation. +
  3. XLB - name. External, use it if we need to share translation with third + part. +
  4. PROPERTIES - name. +
+

+ +

+

+
See Also:
xmb
+
+ +

+ + + + + + + + + + + +
+Constructor Summary
GoogleJsMessageIdGenerator(String projectId) + +
+          Creates an instance.
+  + + + + + + + + + + + +
+Method Summary
+ StringgenerateId(String meaning, + List<CharSequence> messageParts) + +
+          Generate the ID for the message.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+GoogleJsMessageIdGenerator

+
+public GoogleJsMessageIdGenerator(String projectId)
+
+
Creates an instance. +

+

+
Parameters:
projectId - A TC project name (e.g. "MyProject")
+
+ + + + + + + + +
+Method Detail
+ +

+generateId

+
+public String generateId(String meaning,
+                         List<CharSequence> messageParts)
+
+
Description copied from interface: JsMessage.IdGenerator
+
Generate the ID for the message. Messages with the same messageParts + and meaning will get the same id. Messages with the same id + will get the same translation. +

+

+
Specified by:
generateId in interface JsMessage.IdGenerator
+
+
+
Parameters:
meaning - The programmer-specified meaning. If no @meaning + annotation appears, we will use the name of the variable it's + assigned to. If the variable is unnamed, then we will just + use a fingerprint of the message.
messageParts - The parts of the message, including the main + message text.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/HotSwapCompilerPass.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/HotSwapCompilerPass.html new file mode 100644 index 0000000..fc05c44 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/HotSwapCompilerPass.html @@ -0,0 +1,254 @@ + + + + + +HotSwapCompilerPass (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface HotSwapCompilerPass

+
+
All Superinterfaces:
CompilerPass
+
+
+
All Known Implementing Classes:
FieldCleanupPass
+
+
+
+
public interface HotSwapCompilerPass
extends CompilerPass
+ + +

+Interface for compiler passes that can be used in a hot-swap fashion. +

+ The additional method is hotSwapScript which runs this pass on a + subtree of the AST. Each pass that is intended to support hot-swap style + should implement this interface. +

+ It is assumed that Node argument of hotSwapScript is the root + of a sub-tree in AST that represents a js file and so is of type Token.SCRIPT. +

+ +

+


+ +

+ + + + + + + + + + + + +
+Method Summary
+ voidhotSwapScript(Node scriptRoot, + Node originalRoot) + +
+          Process the JS with root node root.
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.CompilerPass
process
+  +

+ + + + + + + + +
+Method Detail
+ +

+hotSwapScript

+
+void hotSwapScript(Node scriptRoot,
+                   Node originalRoot)
+
+
Process the JS with root node root. This is supposed to be significantly + faster compared to corresponding full-compiler passes. +

+

+
+
+
+
Parameters:
scriptRoot - Root node corresponding to the file that is modified, + should be of type Token.SCRIPT.
originalRoot - Root node corresponding to the original version of the + file that is modified. Should be of type token.SCRIPT.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Instrumentation.Builder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Instrumentation.Builder.html new file mode 100644 index 0000000..11bb5f4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Instrumentation.Builder.html @@ -0,0 +1,1190 @@ + + + + + +Instrumentation.Builder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class Instrumentation.Builder

+
+java.lang.Object
+  extended by com.google.protobuf.AbstractMessageLite.Builder<BuilderType>
+      extended by com.google.protobuf.AbstractMessage.Builder<BuilderType>
+          extended by com.google.protobuf.GeneratedMessage.Builder<Instrumentation.Builder>
+              extended by com.google.javascript.jscomp.Instrumentation.Builder
+
+
+
All Implemented Interfaces:
InstrumentationOrBuilder, com.google.protobuf.Message.Builder, com.google.protobuf.MessageLite.Builder, com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder, Cloneable
+
+
+
Enclosing class:
Instrumentation
+
+
+
+
public static final class Instrumentation.Builder
extends com.google.protobuf.GeneratedMessage.Builder<Instrumentation.Builder>
implements InstrumentationOrBuilder
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ Instrumentation.BuilderaddAllDeclarationToRemove(Iterable<String> values) + +
+           
+ Instrumentation.BuilderaddAllInit(Iterable<String> values) + +
+           
+ Instrumentation.BuilderaddDeclarationToRemove(String value) + +
+           
+ Instrumentation.BuilderaddInit(String value) + +
+           
+ Instrumentationbuild() + +
+           
+ InstrumentationbuildPartial() + +
+           
+ Instrumentation.Builderclear() + +
+           
+ Instrumentation.BuilderclearAppNameSetter() + +
+           
+ Instrumentation.BuilderclearDeclarationToRemove() + +
+           
+ Instrumentation.BuilderclearInit() + +
+           
+ Instrumentation.BuilderclearReportCall() + +
+           
+ Instrumentation.BuilderclearReportDefined() + +
+           
+ Instrumentation.BuilderclearReportExit() + +
+           
+ Instrumentation.Builderclone() + +
+           
+ StringgetAppNameSetter() + +
+           
+ StringgetDeclarationToRemove(int index) + +
+           
+ intgetDeclarationToRemoveCount() + +
+           
+ List<String>getDeclarationToRemoveList() + +
+           
+ InstrumentationgetDefaultInstanceForType() + +
+           
+static com.google.protobuf.Descriptors.DescriptorgetDescriptor() + +
+           
+ com.google.protobuf.Descriptors.DescriptorgetDescriptorForType() + +
+           
+ StringgetInit(int index) + +
+           
+ intgetInitCount() + +
+           
+ List<String>getInitList() + +
+           
+ StringgetReportCall() + +
+           
+ StringgetReportDefined() + +
+           
+ StringgetReportExit() + +
+           
+ booleanhasAppNameSetter() + +
+           
+ booleanhasReportCall() + +
+           
+ booleanhasReportDefined() + +
+           
+ booleanhasReportExit() + +
+           
+protected  com.google.protobuf.GeneratedMessage.FieldAccessorTableinternalGetFieldAccessorTable() + +
+           
+ booleanisInitialized() + +
+           
+ Instrumentation.BuildermergeFrom(com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+ Instrumentation.BuildermergeFrom(Instrumentation other) + +
+           
+ Instrumentation.BuildermergeFrom(com.google.protobuf.Message other) + +
+           
+ Instrumentation.BuildersetAppNameSetter(String value) + +
+           
+ Instrumentation.BuildersetDeclarationToRemove(int index, + String value) + +
+           
+ Instrumentation.BuildersetInit(int index, + String value) + +
+           
+ Instrumentation.BuildersetReportCall(String value) + +
+           
+ Instrumentation.BuildersetReportDefined(String value) + +
+           
+ Instrumentation.BuildersetReportExit(String value) + +
+           
+ + + + + + + +
Methods inherited from class com.google.protobuf.GeneratedMessage.Builder
addRepeatedField, clearField, getAllFields, getField, getParentForChildren, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField, isClean, markClean, mergeUnknownFields, newBuilderForField, onBuilt, onChanged, parseUnknownField, setField, setRepeatedField, setUnknownFields
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessage.Builder
mergeDelimitedFrom, mergeDelimitedFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, mergeFrom, newUninitializedMessageException
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessageLite.Builder
addAll, newUninitializedMessageException
+ + + + + + + +
Methods inherited from class java.lang.Object
equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+  +

+ + + + + + + + +
+Method Detail
+ +

+getDescriptor

+
+public static final com.google.protobuf.Descriptors.Descriptor getDescriptor()
+
+
+
+
+
+
+
+
+
+ +

+internalGetFieldAccessorTable

+
+protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable()
+
+
+
Specified by:
internalGetFieldAccessorTable in class com.google.protobuf.GeneratedMessage.Builder<Instrumentation.Builder>
+
+
+
+
+
+
+ +

+clear

+
+public Instrumentation.Builder clear()
+
+
+
Specified by:
clear in interface com.google.protobuf.Message.Builder
Specified by:
clear in interface com.google.protobuf.MessageLite.Builder
Overrides:
clear in class com.google.protobuf.GeneratedMessage.Builder<Instrumentation.Builder>
+
+
+
+
+
+
+ +

+clone

+
+public Instrumentation.Builder clone()
+
+
+
Specified by:
clone in interface com.google.protobuf.Message.Builder
Specified by:
clone in interface com.google.protobuf.MessageLite.Builder
Overrides:
clone in class com.google.protobuf.GeneratedMessage.Builder<Instrumentation.Builder>
+
+
+
+
+
+
+ +

+getDescriptorForType

+
+public com.google.protobuf.Descriptors.Descriptor getDescriptorForType()
+
+
+
Specified by:
getDescriptorForType in interface com.google.protobuf.Message.Builder
Specified by:
getDescriptorForType in interface com.google.protobuf.MessageOrBuilder
Overrides:
getDescriptorForType in class com.google.protobuf.GeneratedMessage.Builder<Instrumentation.Builder>
+
+
+
+
+
+
+ +

+getDefaultInstanceForType

+
+public Instrumentation getDefaultInstanceForType()
+
+
+
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageLiteOrBuilder
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageOrBuilder
+
+
+
+
+
+
+ +

+build

+
+public Instrumentation build()
+
+
+
Specified by:
build in interface com.google.protobuf.Message.Builder
Specified by:
build in interface com.google.protobuf.MessageLite.Builder
+
+
+
+
+
+
+ +

+buildPartial

+
+public Instrumentation buildPartial()
+
+
+
Specified by:
buildPartial in interface com.google.protobuf.Message.Builder
Specified by:
buildPartial in interface com.google.protobuf.MessageLite.Builder
+
+
+
+
+
+
+ +

+mergeFrom

+
+public Instrumentation.Builder mergeFrom(com.google.protobuf.Message other)
+
+
+
Specified by:
mergeFrom in interface com.google.protobuf.Message.Builder
Overrides:
mergeFrom in class com.google.protobuf.AbstractMessage.Builder<Instrumentation.Builder>
+
+
+
+
+
+
+ +

+mergeFrom

+
+public Instrumentation.Builder mergeFrom(Instrumentation other)
+
+
+
+
+
+
+
+
+
+ +

+isInitialized

+
+public final boolean isInitialized()
+
+
+
Specified by:
isInitialized in interface com.google.protobuf.MessageLiteOrBuilder
Overrides:
isInitialized in class com.google.protobuf.GeneratedMessage.Builder<Instrumentation.Builder>
+
+
+
+
+
+
+ +

+mergeFrom

+
+public Instrumentation.Builder mergeFrom(com.google.protobuf.CodedInputStream input,
+                                         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                  throws IOException
+
+
+
Specified by:
mergeFrom in interface com.google.protobuf.Message.Builder
Specified by:
mergeFrom in interface com.google.protobuf.MessageLite.Builder
Overrides:
mergeFrom in class com.google.protobuf.AbstractMessage.Builder<Instrumentation.Builder>
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+hasReportDefined

+
+public boolean hasReportDefined()
+
+
+
Specified by:
hasReportDefined in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getReportDefined

+
+public String getReportDefined()
+
+
+
Specified by:
getReportDefined in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+setReportDefined

+
+public Instrumentation.Builder setReportDefined(String value)
+
+
+
+
+
+
+
+
+
+ +

+clearReportDefined

+
+public Instrumentation.Builder clearReportDefined()
+
+
+
+
+
+
+
+
+
+ +

+hasReportCall

+
+public boolean hasReportCall()
+
+
+
Specified by:
hasReportCall in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getReportCall

+
+public String getReportCall()
+
+
+
Specified by:
getReportCall in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+setReportCall

+
+public Instrumentation.Builder setReportCall(String value)
+
+
+
+
+
+
+
+
+
+ +

+clearReportCall

+
+public Instrumentation.Builder clearReportCall()
+
+
+
+
+
+
+
+
+
+ +

+hasReportExit

+
+public boolean hasReportExit()
+
+
+
Specified by:
hasReportExit in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getReportExit

+
+public String getReportExit()
+
+
+
Specified by:
getReportExit in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+setReportExit

+
+public Instrumentation.Builder setReportExit(String value)
+
+
+
+
+
+
+
+
+
+ +

+clearReportExit

+
+public Instrumentation.Builder clearReportExit()
+
+
+
+
+
+
+
+
+
+ +

+getDeclarationToRemoveList

+
+public List<String> getDeclarationToRemoveList()
+
+
+
Specified by:
getDeclarationToRemoveList in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getDeclarationToRemoveCount

+
+public int getDeclarationToRemoveCount()
+
+
+
Specified by:
getDeclarationToRemoveCount in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getDeclarationToRemove

+
+public String getDeclarationToRemove(int index)
+
+
+
Specified by:
getDeclarationToRemove in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+setDeclarationToRemove

+
+public Instrumentation.Builder setDeclarationToRemove(int index,
+                                                      String value)
+
+
+
+
+
+
+
+
+
+ +

+addDeclarationToRemove

+
+public Instrumentation.Builder addDeclarationToRemove(String value)
+
+
+
+
+
+
+
+
+
+ +

+addAllDeclarationToRemove

+
+public Instrumentation.Builder addAllDeclarationToRemove(Iterable<String> values)
+
+
+
+
+
+
+
+
+
+ +

+clearDeclarationToRemove

+
+public Instrumentation.Builder clearDeclarationToRemove()
+
+
+
+
+
+
+
+
+
+ +

+getInitList

+
+public List<String> getInitList()
+
+
+
Specified by:
getInitList in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getInitCount

+
+public int getInitCount()
+
+
+
Specified by:
getInitCount in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getInit

+
+public String getInit(int index)
+
+
+
Specified by:
getInit in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+setInit

+
+public Instrumentation.Builder setInit(int index,
+                                       String value)
+
+
+
+
+
+
+
+
+
+ +

+addInit

+
+public Instrumentation.Builder addInit(String value)
+
+
+
+
+
+
+
+
+
+ +

+addAllInit

+
+public Instrumentation.Builder addAllInit(Iterable<String> values)
+
+
+
+
+
+
+
+
+
+ +

+clearInit

+
+public Instrumentation.Builder clearInit()
+
+
+
+
+
+
+
+
+
+ +

+hasAppNameSetter

+
+public boolean hasAppNameSetter()
+
+
+
Specified by:
hasAppNameSetter in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getAppNameSetter

+
+public String getAppNameSetter()
+
+
+
Specified by:
getAppNameSetter in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+setAppNameSetter

+
+public Instrumentation.Builder setAppNameSetter(String value)
+
+
+
+
+
+
+
+
+
+ +

+clearAppNameSetter

+
+public Instrumentation.Builder clearAppNameSetter()
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Instrumentation.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Instrumentation.html new file mode 100644 index 0000000..c556832 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Instrumentation.html @@ -0,0 +1,1287 @@ + + + + + +Instrumentation (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class Instrumentation

+
+java.lang.Object
+  extended by com.google.protobuf.AbstractMessageLite
+      extended by com.google.protobuf.AbstractMessage
+          extended by com.google.protobuf.GeneratedMessage
+              extended by com.google.javascript.jscomp.Instrumentation
+
+
+
All Implemented Interfaces:
InstrumentationOrBuilder, com.google.protobuf.Message, com.google.protobuf.MessageLite, com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder, Serializable
+
+
+
+
public final class Instrumentation
extends com.google.protobuf.GeneratedMessage
implements InstrumentationOrBuilder
+ + +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classInstrumentation.Builder + +
+           
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.protobuf.GeneratedMessage
com.google.protobuf.GeneratedMessage.BuilderParent, com.google.protobuf.GeneratedMessage.ExtendableBuilder<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage,BuilderType extends com.google.protobuf.GeneratedMessage.ExtendableBuilder>, com.google.protobuf.GeneratedMessage.ExtendableMessage<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage>, com.google.protobuf.GeneratedMessage.ExtendableMessageOrBuilder<MessageType extends com.google.protobuf.GeneratedMessage.ExtendableMessage>, com.google.protobuf.GeneratedMessage.FieldAccessorTable, com.google.protobuf.GeneratedMessage.GeneratedExtension<ContainingType extends com.google.protobuf.Message,Type>
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Field Summary
+static intAPP_NAME_SETTER_FIELD_NUMBER + +
+           
+static intDECLARATION_TO_REMOVE_FIELD_NUMBER + +
+           
+static intINIT_FIELD_NUMBER + +
+           
+static intREPORT_CALL_FIELD_NUMBER + +
+           
+static intREPORT_DEFINED_FIELD_NUMBER + +
+           
+static intREPORT_EXIT_FIELD_NUMBER + +
+           
+ + + + + + + +
Fields inherited from class com.google.protobuf.GeneratedMessage
alwaysUseFieldBuilders
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StringgetAppNameSetter() + +
+           
+ StringgetDeclarationToRemove(int index) + +
+           
+ intgetDeclarationToRemoveCount() + +
+           
+ List<String>getDeclarationToRemoveList() + +
+           
+static InstrumentationgetDefaultInstance() + +
+           
+ InstrumentationgetDefaultInstanceForType() + +
+           
+static com.google.protobuf.Descriptors.DescriptorgetDescriptor() + +
+           
+ StringgetInit(int index) + +
+           
+ intgetInitCount() + +
+           
+ List<String>getInitList() + +
+           
+ StringgetReportCall() + +
+           
+ StringgetReportDefined() + +
+           
+ StringgetReportExit() + +
+           
+ intgetSerializedSize() + +
+           
+ booleanhasAppNameSetter() + +
+           
+ booleanhasReportCall() + +
+           
+ booleanhasReportDefined() + +
+           
+ booleanhasReportExit() + +
+           
+protected  com.google.protobuf.GeneratedMessage.FieldAccessorTableinternalGetFieldAccessorTable() + +
+           
+ booleanisInitialized() + +
+           
+static Instrumentation.BuildernewBuilder() + +
+           
+static Instrumentation.BuildernewBuilder(Instrumentation prototype) + +
+           
+ Instrumentation.BuildernewBuilderForType() + +
+           
+protected  Instrumentation.BuildernewBuilderForType(com.google.protobuf.GeneratedMessage.BuilderParent parent) + +
+           
+static InstrumentationparseDelimitedFrom(InputStream input) + +
+           
+static InstrumentationparseDelimitedFrom(InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static InstrumentationparseFrom(byte[] data) + +
+           
+static InstrumentationparseFrom(byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static InstrumentationparseFrom(com.google.protobuf.ByteString data) + +
+           
+static InstrumentationparseFrom(com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static InstrumentationparseFrom(com.google.protobuf.CodedInputStream input) + +
+           
+static InstrumentationparseFrom(com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+static InstrumentationparseFrom(InputStream input) + +
+           
+static InstrumentationparseFrom(InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + +
+           
+ Instrumentation.BuildertoBuilder() + +
+           
+protected  ObjectwriteReplace() + +
+           
+ voidwriteTo(com.google.protobuf.CodedOutputStream output) + +
+           
+ + + + + + + +
Methods inherited from class com.google.protobuf.GeneratedMessage
getAllFields, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField, newFileScopedGeneratedExtension, newMessageScopedGeneratedExtension
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessage
equals, hashBoolean, hashCode, hashEnum, hashEnumList, hashFields, hashLong, toString
+ + + + + + + +
Methods inherited from class com.google.protobuf.AbstractMessageLite
toByteArray, toByteString, writeDelimitedTo, writeTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageLite
toByteArray, toByteString, writeDelimitedTo, writeTo
+  +

+ + + + + + + + +
+Field Detail
+ +

+REPORT_DEFINED_FIELD_NUMBER

+
+public static final int REPORT_DEFINED_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+REPORT_CALL_FIELD_NUMBER

+
+public static final int REPORT_CALL_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+REPORT_EXIT_FIELD_NUMBER

+
+public static final int REPORT_EXIT_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+DECLARATION_TO_REMOVE_FIELD_NUMBER

+
+public static final int DECLARATION_TO_REMOVE_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+INIT_FIELD_NUMBER

+
+public static final int INIT_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+APP_NAME_SETTER_FIELD_NUMBER

+
+public static final int APP_NAME_SETTER_FIELD_NUMBER
+
+
+
See Also:
Constant Field Values
+
+ + + + + + + + +
+Method Detail
+ +

+getDefaultInstance

+
+public static Instrumentation getDefaultInstance()
+
+
+
+
+
+
+
+
+
+ +

+getDefaultInstanceForType

+
+public Instrumentation getDefaultInstanceForType()
+
+
+
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageLiteOrBuilder
Specified by:
getDefaultInstanceForType in interface com.google.protobuf.MessageOrBuilder
+
+
+
+
+
+
+ +

+getDescriptor

+
+public static final com.google.protobuf.Descriptors.Descriptor getDescriptor()
+
+
+
+
+
+
+
+
+
+ +

+internalGetFieldAccessorTable

+
+protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable()
+
+
+
Specified by:
internalGetFieldAccessorTable in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+
+ +

+hasReportDefined

+
+public boolean hasReportDefined()
+
+
+
Specified by:
hasReportDefined in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getReportDefined

+
+public String getReportDefined()
+
+
+
Specified by:
getReportDefined in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+hasReportCall

+
+public boolean hasReportCall()
+
+
+
Specified by:
hasReportCall in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getReportCall

+
+public String getReportCall()
+
+
+
Specified by:
getReportCall in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+hasReportExit

+
+public boolean hasReportExit()
+
+
+
Specified by:
hasReportExit in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getReportExit

+
+public String getReportExit()
+
+
+
Specified by:
getReportExit in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getDeclarationToRemoveList

+
+public List<String> getDeclarationToRemoveList()
+
+
+
Specified by:
getDeclarationToRemoveList in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getDeclarationToRemoveCount

+
+public int getDeclarationToRemoveCount()
+
+
+
Specified by:
getDeclarationToRemoveCount in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getDeclarationToRemove

+
+public String getDeclarationToRemove(int index)
+
+
+
Specified by:
getDeclarationToRemove in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getInitList

+
+public List<String> getInitList()
+
+
+
Specified by:
getInitList in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getInitCount

+
+public int getInitCount()
+
+
+
Specified by:
getInitCount in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getInit

+
+public String getInit(int index)
+
+
+
Specified by:
getInit in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+hasAppNameSetter

+
+public boolean hasAppNameSetter()
+
+
+
Specified by:
hasAppNameSetter in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+getAppNameSetter

+
+public String getAppNameSetter()
+
+
+
Specified by:
getAppNameSetter in interface InstrumentationOrBuilder
+
+
+
+
+
+
+ +

+isInitialized

+
+public final boolean isInitialized()
+
+
+
Specified by:
isInitialized in interface com.google.protobuf.MessageLiteOrBuilder
Overrides:
isInitialized in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+
+ +

+writeTo

+
+public void writeTo(com.google.protobuf.CodedOutputStream output)
+             throws IOException
+
+
+
Specified by:
writeTo in interface com.google.protobuf.MessageLite
Overrides:
writeTo in class com.google.protobuf.AbstractMessage
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+getSerializedSize

+
+public int getSerializedSize()
+
+
+
Specified by:
getSerializedSize in interface com.google.protobuf.MessageLite
Overrides:
getSerializedSize in class com.google.protobuf.AbstractMessage
+
+
+
+
+
+
+ +

+writeReplace

+
+protected Object writeReplace()
+                       throws ObjectStreamException
+
+
+
Overrides:
writeReplace in class com.google.protobuf.GeneratedMessage
+
+
+ +
Throws: +
ObjectStreamException
+
+
+
+ +

+parseFrom

+
+public static Instrumentation parseFrom(com.google.protobuf.ByteString data)
+                                 throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static Instrumentation parseFrom(com.google.protobuf.ByteString data,
+                                        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                 throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static Instrumentation parseFrom(byte[] data)
+                                 throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static Instrumentation parseFrom(byte[] data,
+                                        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                 throws com.google.protobuf.InvalidProtocolBufferException
+
+
+
+
+
+ +
Throws: +
com.google.protobuf.InvalidProtocolBufferException
+
+
+
+ +

+parseFrom

+
+public static Instrumentation parseFrom(InputStream input)
+                                 throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static Instrumentation parseFrom(InputStream input,
+                                        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                 throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseDelimitedFrom

+
+public static Instrumentation parseDelimitedFrom(InputStream input)
+                                          throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseDelimitedFrom

+
+public static Instrumentation parseDelimitedFrom(InputStream input,
+                                                 com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                          throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static Instrumentation parseFrom(com.google.protobuf.CodedInputStream input)
+                                 throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+parseFrom

+
+public static Instrumentation parseFrom(com.google.protobuf.CodedInputStream input,
+                                        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+                                 throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+newBuilder

+
+public static Instrumentation.Builder newBuilder()
+
+
+
+
+
+
+
+
+
+ +

+newBuilderForType

+
+public Instrumentation.Builder newBuilderForType()
+
+
+
Specified by:
newBuilderForType in interface com.google.protobuf.Message
Specified by:
newBuilderForType in interface com.google.protobuf.MessageLite
+
+
+
+
+
+
+ +

+newBuilder

+
+public static Instrumentation.Builder newBuilder(Instrumentation prototype)
+
+
+
+
+
+
+
+
+
+ +

+toBuilder

+
+public Instrumentation.Builder toBuilder()
+
+
+
Specified by:
toBuilder in interface com.google.protobuf.Message
Specified by:
toBuilder in interface com.google.protobuf.MessageLite
+
+
+
+
+
+
+ +

+newBuilderForType

+
+protected Instrumentation.Builder newBuilderForType(com.google.protobuf.GeneratedMessage.BuilderParent parent)
+
+
+
Specified by:
newBuilderForType in class com.google.protobuf.GeneratedMessage
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/InstrumentationOrBuilder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/InstrumentationOrBuilder.html new file mode 100644 index 0000000..17aa6a9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/InstrumentationOrBuilder.html @@ -0,0 +1,531 @@ + + + + + +InstrumentationOrBuilder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface InstrumentationOrBuilder

+
+
All Superinterfaces:
com.google.protobuf.MessageLiteOrBuilder, com.google.protobuf.MessageOrBuilder
+
+
+
All Known Implementing Classes:
Instrumentation, Instrumentation.Builder
+
+
+
+
public interface InstrumentationOrBuilder
extends com.google.protobuf.MessageOrBuilder
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StringgetAppNameSetter() + +
+           
+ StringgetDeclarationToRemove(int index) + +
+           
+ intgetDeclarationToRemoveCount() + +
+           
+ List<String>getDeclarationToRemoveList() + +
+           
+ StringgetInit(int index) + +
+           
+ intgetInitCount() + +
+           
+ List<String>getInitList() + +
+           
+ StringgetReportCall() + +
+           
+ StringgetReportDefined() + +
+           
+ StringgetReportExit() + +
+           
+ booleanhasAppNameSetter() + +
+           
+ booleanhasReportCall() + +
+           
+ booleanhasReportDefined() + +
+           
+ booleanhasReportExit() + +
+           
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageOrBuilder
getAllFields, getDefaultInstanceForType, getDescriptorForType, getField, getRepeatedField, getRepeatedFieldCount, getUnknownFields, hasField
+ + + + + + + +
Methods inherited from interface com.google.protobuf.MessageLiteOrBuilder
isInitialized
+  +

+ + + + + + + + +
+Method Detail
+ +

+hasReportDefined

+
+boolean hasReportDefined()
+
+
+
+
+
+
+
+
+
+ +

+getReportDefined

+
+String getReportDefined()
+
+
+
+
+
+
+
+
+
+ +

+hasReportCall

+
+boolean hasReportCall()
+
+
+
+
+
+
+
+
+
+ +

+getReportCall

+
+String getReportCall()
+
+
+
+
+
+
+
+
+
+ +

+hasReportExit

+
+boolean hasReportExit()
+
+
+
+
+
+
+
+
+
+ +

+getReportExit

+
+String getReportExit()
+
+
+
+
+
+
+
+
+
+ +

+getDeclarationToRemoveList

+
+List<String> getDeclarationToRemoveList()
+
+
+
+
+
+
+
+
+
+ +

+getDeclarationToRemoveCount

+
+int getDeclarationToRemoveCount()
+
+
+
+
+
+
+
+
+
+ +

+getDeclarationToRemove

+
+String getDeclarationToRemove(int index)
+
+
+
+
+
+
+
+
+
+ +

+getInitList

+
+List<String> getInitList()
+
+
+
+
+
+
+
+
+
+ +

+getInitCount

+
+int getInitCount()
+
+
+
+
+
+
+
+
+
+ +

+getInit

+
+String getInit(int index)
+
+
+
+
+
+
+
+
+
+ +

+hasAppNameSetter

+
+boolean hasAppNameSetter()
+
+
+
+
+
+
+
+
+
+ +

+getAppNameSetter

+
+String getAppNameSetter()
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/InstrumentationTemplate.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/InstrumentationTemplate.html new file mode 100644 index 0000000..94bb44d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/InstrumentationTemplate.html @@ -0,0 +1,250 @@ + + + + + +InstrumentationTemplate (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class InstrumentationTemplate

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.InstrumentationTemplate
+
+
+
+
public final class InstrumentationTemplate
extends Object
+ + +

+


+ +

+ + + + + + + + + + + + + + + + +
+Method Summary
+static com.google.protobuf.Descriptors.FileDescriptorgetDescriptor() + +
+           
+static voidregisterAllExtensions(com.google.protobuf.ExtensionRegistry registry) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+registerAllExtensions

+
+public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry)
+
+
+
+
+
+
+ +

+getDescriptor

+
+public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JSError.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JSError.html new file mode 100644 index 0000000..9ebbd75 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JSError.html @@ -0,0 +1,645 @@ + + + + + +JSError (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class JSError

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.JSError
+
+
+
+
public class JSError
extends Object
+ + +

+Compile error description +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + +
+Field Summary
+ Stringdescription + +
+          Description of the error
+ CheckLevellevel + +
+          Deprecated. Use #getDefaultLevel
+ intlineNumber + +
+          Line number of the source
+ StringsourceName + +
+          Name of the source
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleanequals(Object o) + +
+           
+ Stringformat(CheckLevel level, + MessageFormatter formatter) + +
+          Format a message at the given level.
+ intgetCharno() + +
+          Get the character number.
+ CheckLevelgetDefaultLevel() + +
+          The default level, before any of the WarningsGuards are applied.
+ intgetNodeLength() + +
+           
+ intgetNodeSourceOffset() + +
+           
+ DiagnosticTypegetType() + +
+           
+ inthashCode() + +
+           
+static JSErrormake(DiagnosticType type, + String... arguments) + +
+          Creates a JSError with no source information
+static JSErrormake(String sourceName, + int lineno, + int charno, + CheckLevel level, + DiagnosticType type, + String... arguments) + +
+          Creates a JSError at a given source location
+static JSErrormake(String sourceName, + int lineno, + int charno, + DiagnosticType type, + String... arguments) + +
+          Creates a JSError at a given source location
+static JSErrormake(String sourceName, + Node n, + CheckLevel level, + DiagnosticType type, + String... arguments) + +
+          Creates a JSError from a file and Node position.
+static JSErrormake(String sourceName, + Node n, + DiagnosticType type, + String... arguments) + +
+          Creates a JSError from a file and Node position.
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+description

+
+public final String description
+
+
Description of the error +

+

+
+
+
+ +

+sourceName

+
+public final String sourceName
+
+
Name of the source +

+

+
+
+
+ +

+lineNumber

+
+public final int lineNumber
+
+
Line number of the source +

+

+
+
+
+ +

+level

+
+@Deprecated
+public final CheckLevel level
+
+
Deprecated. Use #getDefaultLevel
+
+
+ + + + + + + + +
+Method Detail
+ +

+make

+
+public static JSError make(DiagnosticType type,
+                           String... arguments)
+
+
Creates a JSError with no source information +

+

+
Parameters:
type - The DiagnosticType
arguments - Arguments to be incorporated into the message
+
+
+
+ +

+make

+
+public static JSError make(String sourceName,
+                           int lineno,
+                           int charno,
+                           DiagnosticType type,
+                           String... arguments)
+
+
Creates a JSError at a given source location +

+

+
Parameters:
sourceName - The source file name
lineno - Line number with source file, or -1 if unknown
charno - Column number within line, or -1 for whole line.
type - The DiagnosticType
arguments - Arguments to be incorporated into the message
+
+
+
+ +

+make

+
+public static JSError make(String sourceName,
+                           int lineno,
+                           int charno,
+                           CheckLevel level,
+                           DiagnosticType type,
+                           String... arguments)
+
+
Creates a JSError at a given source location +

+

+
Parameters:
sourceName - The source file name
lineno - Line number with source file, or -1 if unknown
charno - Column number within line, or -1 for whole line.
type - The DiagnosticType
arguments - Arguments to be incorporated into the message
+
+
+
+ +

+make

+
+public static JSError make(String sourceName,
+                           Node n,
+                           DiagnosticType type,
+                           String... arguments)
+
+
Creates a JSError from a file and Node position. +

+

+
Parameters:
sourceName - The source file name
n - Determines the line and char position within the source file name
type - The DiagnosticType
arguments - Arguments to be incorporated into the message
+
+
+
+ +

+make

+
+public static JSError make(String sourceName,
+                           Node n,
+                           CheckLevel level,
+                           DiagnosticType type,
+                           String... arguments)
+
+
Creates a JSError from a file and Node position. +

+

+
Parameters:
sourceName - The source file name
n - Determines the line and char position within the source file name
type - The DiagnosticType
arguments - Arguments to be incorporated into the message
+
+
+
+ +

+getType

+
+public DiagnosticType getType()
+
+
+
+
+
+
+ +

+format

+
+public String format(CheckLevel level,
+                     MessageFormatter formatter)
+
+
Format a message at the given level. +

+

+ +
Returns:
the formatted message or null
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+
+ +

+getCharno

+
+public int getCharno()
+
+
Get the character number. +

+

+
+
+
+
+ +

+getNodeSourceOffset

+
+public int getNodeSourceOffset()
+
+
+ +
Returns:
the offset of the region the Error applies to, or -1 if the offset + is unknown.
+
+
+
+ +

+getNodeLength

+
+public int getNodeLength()
+
+
+ +
Returns:
the length of the region the Error applies to, or 0 if the length + is unknown.
+
+
+
+ +

+getDefaultLevel

+
+public CheckLevel getDefaultLevel()
+
+
The default level, before any of the WarningsGuards are applied. +

+

+
+
+
+
+ +

+equals

+
+public boolean equals(Object o)
+
+
+
Overrides:
equals in class Object
+
+
+
+
+
+
+ +

+hashCode

+
+public int hashCode()
+
+
+
Overrides:
hashCode in class Object
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JSModule.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JSModule.html new file mode 100644 index 0000000..f7aa7e3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JSModule.html @@ -0,0 +1,850 @@ + + + + + +JSModule (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class JSModule

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.JSModule
+
+
+
All Implemented Interfaces:
DependencyInfo, Serializable
+
+
+
+
public class JSModule
extends Object
implements DependencyInfo, Serializable
+ + +

+A JavaScript module has a unique name, consists of a list of compiler inputs, + and can depend on other modules. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Constructor Summary
JSModule(String name) + +
+          Creates an instance.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidadd(CompilerInput input) + +
+          Adds a source code input to this module.
+ voidadd(SourceFile file) + +
+          Adds a source file input to this module.
+ voidaddAfter(CompilerInput input, + CompilerInput other) + +
+          Adds a source code input to this module directly after other.
+ voidaddDependency(JSModule dep) + +
+          Adds a dependency on another module.
+ voidaddFirst(CompilerInput input) + +
+          Adds a source code input to this module.
+ voidaddFirst(SourceFile file) + +
+          Adds a source file input to this module.
+ voidclearAsts() + +
+          Removes any references to nodes of the AST.
+ Set<JSModule>getAllDependencies() + +
+          Returns the transitive closure of dependencies starting from the + dependencies of this module.
+ CompilerInputgetByName(String name) + +
+          Returns the input with the given name or null if none.
+ List<JSModule>getDependencies() + +
+          Gets the list of modules that this module depends on.
+ intgetDepth() + +
+           
+ List<CompilerInput>getInputs() + +
+          Gets this module's list of source code inputs.
+ StringgetName() + +
+          Gets the module name.
+ StringgetPathRelativeToClosureBase() + +
+          Gets the path of this file relative to Closure's base.js file.
+ List<String>getProvides() + +
+          Gets the symbols provided by this file.
+ List<String>getRequires() + +
+          Gets the symbols required by this file.
+ Set<JSModule>getThisAndAllDependencies() + +
+          Returns this module and all of its dependencies in one list.
+ voidremove(CompilerInput input) + +
+          Removes an input from this module.
+ voidremoveAll() + +
+          Removes all of the inputs from this module.
+ booleanremoveByName(String name) + +
+          Removes any input with the given name.
+ voidsetDepth(int dep) + +
+           
+ voidsortInputsByDeps(Compiler compiler) + +
+          Puts the JS files into a topologically sorted order by their dependencies.
+static JSModule[]sortJsModules(Collection<JSModule> modules) + +
+          Returns the given collection of modules in topological order.
+ StringtoString() + +
+          Returns the module name (primarily for debugging).
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JSModule

+
+public JSModule(String name)
+
+
Creates an instance. +

+

+
Parameters:
name - A unique name for the module
+
+ + + + + + + + +
+Method Detail
+ +

+getName

+
+public String getName()
+
+
Gets the module name. +

+

+
Specified by:
getName in interface DependencyInfo
+
+
+
+
+
+
+ +

+getProvides

+
+public List<String> getProvides()
+
+
Description copied from interface: DependencyInfo
+
Gets the symbols provided by this file. +

+

+
Specified by:
getProvides in interface DependencyInfo
+
+
+
+
+
+
+ +

+getRequires

+
+public List<String> getRequires()
+
+
Description copied from interface: DependencyInfo
+
Gets the symbols required by this file. +

+

+
Specified by:
getRequires in interface DependencyInfo
+
+
+
+
+
+
+ +

+getPathRelativeToClosureBase

+
+public String getPathRelativeToClosureBase()
+
+
Description copied from interface: DependencyInfo
+
Gets the path of this file relative to Closure's base.js file. +

+

+
Specified by:
getPathRelativeToClosureBase in interface DependencyInfo
+
+
+
+
+
+
+ +

+add

+
+public void add(SourceFile file)
+
+
Adds a source file input to this module. +

+

+
+
+
+
+
+
+
+ +

+addFirst

+
+public void addFirst(SourceFile file)
+
+
Adds a source file input to this module. +

+

+
+
+
+
+
+
+
+ +

+add

+
+public void add(CompilerInput input)
+
+
Adds a source code input to this module. +

+

+
+
+
+
+
+
+
+ +

+addFirst

+
+public void addFirst(CompilerInput input)
+
+
Adds a source code input to this module. +

+

+
+
+
+
+
+
+
+ +

+addAfter

+
+public void addAfter(CompilerInput input,
+                     CompilerInput other)
+
+
Adds a source code input to this module directly after other. +

+

+
+
+
+
+
+
+
+ +

+addDependency

+
+public void addDependency(JSModule dep)
+
+
Adds a dependency on another module. +

+

+
+
+
+
+
+
+
+ +

+remove

+
+public void remove(CompilerInput input)
+
+
Removes an input from this module. +

+

+
+
+
+
+
+
+
+ +

+removeAll

+
+public void removeAll()
+
+
Removes all of the inputs from this module. +

+

+
+
+
+
+
+
+
+ +

+getDependencies

+
+public List<JSModule> getDependencies()
+
+
Gets the list of modules that this module depends on. +

+

+
+
+
+ +
Returns:
A list that may be empty but not null
+
+
+
+ +

+getAllDependencies

+
+public Set<JSModule> getAllDependencies()
+
+
Returns the transitive closure of dependencies starting from the + dependencies of this module. +

+

+
+
+
+
+
+
+
+ +

+getThisAndAllDependencies

+
+public Set<JSModule> getThisAndAllDependencies()
+
+
Returns this module and all of its dependencies in one list. +

+

+
+
+
+
+
+
+
+ +

+getInputs

+
+public List<CompilerInput> getInputs()
+
+
Gets this module's list of source code inputs. +

+

+
+
+
+ +
Returns:
A list that may be empty but not null
+
+
+
+ +

+getByName

+
+public CompilerInput getByName(String name)
+
+
Returns the input with the given name or null if none. +

+

+
+
+
+
+
+
+
+ +

+removeByName

+
+public boolean removeByName(String name)
+
+
Removes any input with the given name. Returns whether any were removed. +

+

+
+
+
+
+
+
+
+ +

+toString

+
+public String toString()
+
+
Returns the module name (primarily for debugging). +

+

+
Overrides:
toString in class Object
+
+
+
+
+
+
+ +

+clearAsts

+
+public void clearAsts()
+
+
Removes any references to nodes of the AST. This method is needed to + allow the ASTs to be garbage collected if the modules are kept around. +

+

+
+
+
+
+
+
+
+ +

+sortInputsByDeps

+
+public void sortInputsByDeps(Compiler compiler)
+
+
Puts the JS files into a topologically sorted order by their dependencies. +

+

+
+
+
+
+
+
+
+ +

+sortJsModules

+
+public static JSModule[] sortJsModules(Collection<JSModule> modules)
+                                throws SortedDependencies.CircularDependencyException
+
+
Returns the given collection of modules in topological order. + + Note that this will return the modules in the same order if they are + already sorted, and in general, will only change the order as necessary to + satisfy the ordering dependencies. This can be important for cases where + the modules do not properly specify all dependencies. +

+

+
+
+
+ +
Throws: +
SortedDependencies.CircularDependencyException
+
+
+
+ +

+setDepth

+
+public void setDepth(int dep)
+
+
+
+
+
+
Parameters:
dep - the depth to set
+
+
+
+ +

+getDepth

+
+public int getDepth()
+
+
+
+
+
+ +
Returns:
the depth
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JSModuleGraph.ModuleDependenceException.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JSModuleGraph.ModuleDependenceException.html new file mode 100644 index 0000000..3b9c73a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JSModuleGraph.ModuleDependenceException.html @@ -0,0 +1,310 @@ + + + + + +JSModuleGraph.ModuleDependenceException (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class JSModuleGraph.ModuleDependenceException

+
+java.lang.Object
+  extended by java.lang.Throwable
+      extended by java.lang.Exception
+          extended by java.lang.RuntimeException
+              extended by java.lang.IllegalArgumentException
+                  extended by com.google.javascript.jscomp.JSModuleGraph.ModuleDependenceException
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
Enclosing class:
JSModuleGraph
+
+
+
+
protected static class JSModuleGraph.ModuleDependenceException
extends IllegalArgumentException
+ + +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + + +
+Constructor Summary
+protected JSModuleGraph.ModuleDependenceException(String message, + JSModule module, + JSModule dependentModule) + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+ JSModulegetDependentModule() + +
+           
+ JSModulegetModule() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Throwable
fillInStackTrace, getCause, getLocalizedMessage, getMessage, getStackTrace, initCause, printStackTrace, printStackTrace, printStackTrace, setStackTrace, toString
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JSModuleGraph.ModuleDependenceException

+
+protected JSModuleGraph.ModuleDependenceException(String message,
+                                                  JSModule module,
+                                                  JSModule dependentModule)
+
+
+ + + + + + + + +
+Method Detail
+ +

+getModule

+
+public JSModule getModule()
+
+
+
+
+
+
+ +

+getDependentModule

+
+public JSModule getDependentModule()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JSModuleGraph.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JSModuleGraph.html new file mode 100644 index 0000000..407eee3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JSModuleGraph.html @@ -0,0 +1,459 @@ + + + + + +JSModuleGraph (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class JSModuleGraph

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.JSModuleGraph
+
+
+
+
public class JSModuleGraph
extends Object
+ + +

+A JSModule dependency graph that assigns a depth to each module and + can answer depth-related queries about them. For the purposes of this class, + a module's depth is defined as the number of hops in the longest path from + the module to a module with no dependencies. +

+ +

+


+ +

+ + + + + + + + + + + +
+Nested Class Summary
+protected static classJSModuleGraph.ModuleDependenceException + +
+           
+  + + + + + + + + + + + + + +
+Constructor Summary
JSModuleGraph(JSModule[] modulesInDepOrder) + +
+          Creates a module graph from a list of modules in dependency order.
JSModuleGraph(List<JSModule> modulesInDepOrder) + +
+          Creates a module graph from a list of modules in dependency order.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidcoalesceDuplicateFiles() + +
+          Replaces any files that are found multiple times with a single instance in + the closest parent module that is common to all modules where it appears.
+ booleandependsOn(JSModule src, + JSModule m) + +
+          Determines whether this module depends on a given module.
+ JSModulegetDeepestCommonDependencyInclusive(Collection<JSModule> modules) + +
+          Returns the deepest common dependency of the given modules.
+ JSModulegetDeepestCommonDependencyInclusive(JSModule m1, + JSModule m2) + +
+          Finds the deepest common dependency of two modules, including the + modules themselves.
+ List<CompilerInput>manageDependencies(DependencyOptions depOptions, + List<CompilerInput> inputs) + +
+          Apply the dependency options to the list of sources, returning a new + source list re-ordering and dropping files as necessary.
+ List<CompilerInput>manageDependencies(List<String> entryPoints, + List<CompilerInput> inputs) + +
+          Applies a DependencyOptions in "dependency sorting" and "dependency pruning" + mode to the given list of inputs.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JSModuleGraph

+
+public JSModuleGraph(JSModule[] modulesInDepOrder)
+
+
Creates a module graph from a list of modules in dependency order. +

+

+
+ +

+JSModuleGraph

+
+public JSModuleGraph(List<JSModule> modulesInDepOrder)
+
+
Creates a module graph from a list of modules in dependency order. +

+

+ + + + + + + + +
+Method Detail
+ +

+dependsOn

+
+public boolean dependsOn(JSModule src,
+                         JSModule m)
+
+
Determines whether this module depends on a given module. Note that a + module never depends on itself, as that dependency would be cyclic. +

+

+
+
+
+
+ +

+getDeepestCommonDependencyInclusive

+
+public JSModule getDeepestCommonDependencyInclusive(JSModule m1,
+                                                    JSModule m2)
+
+
Finds the deepest common dependency of two modules, including the + modules themselves. +

+

+
Parameters:
m1 - A module in this graph
m2 - A module in this graph +
Returns:
The deepest common dep of m1 and m2, or null if + they have no common dependencies
+
+
+
+ +

+getDeepestCommonDependencyInclusive

+
+public JSModule getDeepestCommonDependencyInclusive(Collection<JSModule> modules)
+
+
Returns the deepest common dependency of the given modules. +

+

+
+
+
+
+ +

+coalesceDuplicateFiles

+
+public void coalesceDuplicateFiles()
+
+
Replaces any files that are found multiple times with a single instance in + the closest parent module that is common to all modules where it appears. + + JSCompiler normally errors if you attempt to compile modules containing the + same file. This method can be used to remove duplicates before compiling + to avoid such an error. +

+

+
+
+
+
+ +

+manageDependencies

+
+public List<CompilerInput> manageDependencies(List<String> entryPoints,
+                                              List<CompilerInput> inputs)
+                                       throws SortedDependencies.CircularDependencyException,
+                                              SortedDependencies.MissingProvideException
+
+
Applies a DependencyOptions in "dependency sorting" and "dependency pruning" + mode to the given list of inputs. Returns a new list with the files sorted + and removed. This module graph will be updated to reflect the new list. + + If you need more fine-grained dependency management, you should create your + own DependencyOptions and call + manageDependencies(DependencyOptions, List<CompilerInput>). +

+

+
Parameters:
entryPoints - The entry points into the program. + Expressed as JS symbols.
inputs - The original list of sources. Used to ensure that the sort + is stable. +
Throws: +
SortedDependencies.CircularDependencyException - if there is a circular dependency + between the provides and requires. +
SortedDependencies.MissingProvideException - if an entry point was not provided + by any of the inputs.
See Also:
for more info on how this works.
+
+
+
+ +

+manageDependencies

+
+public List<CompilerInput> manageDependencies(DependencyOptions depOptions,
+                                              List<CompilerInput> inputs)
+                                       throws SortedDependencies.CircularDependencyException,
+                                              SortedDependencies.MissingProvideException
+
+
Apply the dependency options to the list of sources, returning a new + source list re-ordering and dropping files as necessary. + This module graph will be updated to reflect the new list. +

+

+
Parameters:
inputs - The original list of sources. Used to ensure that the sort + is stable. +
Throws: +
SortedDependencies.CircularDependencyException - if there is a circular dependency + between the provides and requires. +
SortedDependencies.MissingProvideException - if an entry point was not provided + by any of the inputs.
See Also:
for more info on how this works.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JSSourceFile.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JSSourceFile.html new file mode 100644 index 0000000..1255f24 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JSSourceFile.html @@ -0,0 +1,468 @@ + + + + + +JSSourceFile (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class JSSourceFile

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.SourceFile
+      extended by com.google.javascript.jscomp.JSSourceFile
+
+
+
All Implemented Interfaces:
StaticSourceFile, Serializable
+
+
+
+
public class JSSourceFile
extends SourceFile
implements Serializable
+ + +

+An abstract representation of a JavaScript source file, as input to + JSCompiler. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.jscomp.SourceFile
SourceFile.Builder, SourceFile.Generator
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidclearCachedSource() + +
+           
+static JSSourceFilefromCode(String fileName, + String code) + +
+           
+static JSSourceFilefromFile(File file) + +
+           
+static JSSourceFilefromFile(File file, + Charset charSet) + +
+           
+static JSSourceFilefromFile(String fileName) + +
+           
+static JSSourceFilefromFile(String fileName, + Charset charSet) + +
+           
+static JSSourceFilefromGenerator(String fileName, + SourceFile.Generator generator) + +
+           
+static JSSourceFilefromInputStream(String fileName, + InputStream s) + +
+           
+ StringgetCode() + +
+          Gets all the code in this source file.
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.SourceFile
builder, fromCode, fromInputStream, fromReader, getCodeReader, getLine, getLineOffset, getName, getOriginalPath, getRegion, isExtern, setOriginalPath, toString
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+fromFile

+
+public static JSSourceFile fromFile(String fileName,
+                                    Charset charSet)
+
+
+
+
+
+
+
+
+
+ +

+fromFile

+
+public static JSSourceFile fromFile(String fileName)
+
+
+
+
+
+
+
+
+
+ +

+fromFile

+
+public static JSSourceFile fromFile(File file,
+                                    Charset charSet)
+
+
+
+
+
+
+
+
+
+ +

+fromFile

+
+public static JSSourceFile fromFile(File file)
+
+
+
+
+
+
+
+
+
+ +

+fromCode

+
+public static JSSourceFile fromCode(String fileName,
+                                    String code)
+
+
+
+
+
+
+
+
+
+ +

+fromInputStream

+
+public static JSSourceFile fromInputStream(String fileName,
+                                           InputStream s)
+                                    throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+fromGenerator

+
+public static JSSourceFile fromGenerator(String fileName,
+                                         SourceFile.Generator generator)
+
+
+
+
+
+
+
+
+
+ +

+getCode

+
+public String getCode()
+               throws IOException
+
+
Description copied from class: SourceFile
+
Gets all the code in this source file. +

+

+
Overrides:
getCode in class SourceFile
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+clearCachedSource

+
+public void clearCachedSource()
+
+
+
Overrides:
clearCachedSource in class SourceFile
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JqueryCodingConvention.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JqueryCodingConvention.html new file mode 100644 index 0000000..abb5c0b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JqueryCodingConvention.html @@ -0,0 +1,390 @@ + + + + + +JqueryCodingConvention (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class JqueryCodingConvention

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.CodingConventions.Proxy
+      extended by com.google.javascript.jscomp.JqueryCodingConvention
+
+
+
All Implemented Interfaces:
CodingConvention, Serializable
+
+
+
+
public class JqueryCodingConvention
extends CodingConventions.Proxy
+ + +

+This describes the jQuery specific JavaScript coding conventions. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from interface com.google.javascript.jscomp.CodingConvention
CodingConvention.AssertionFunctionSpec, CodingConvention.Bind, CodingConvention.DelegateRelationship, CodingConvention.ObjectLiteralCast, CodingConvention.SubclassRelationship, CodingConvention.SubclassType
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.jscomp.CodingConventions.Proxy
nextConvention
+  + + + + + + + + + + + + + +
+Constructor Summary
JqueryCodingConvention() + +
+           
JqueryCodingConvention(CodingConvention wrapped) + +
+           
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StringgetGlobalObject() + +
+          Gets the name of the global object.
+ booleanisPropertyTestFunction(Node call) + +
+          Whether this CALL function is testing for the existence of a property.
+ booleanisPrototypeAlias(Node n) + +
+          Whether this GETPROP node is an alias for an object prototype.
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.CodingConventions.Proxy
applyDelegateRelationship, applySingletonGetter, applySubclassRelationship, checkForCallingConventionDefiningCalls, defineDelegateProxyPrototypeProperties, describeFunctionBind, extractClassNameIfProvide, extractClassNameIfRequire, getAbstractMethodName, getAssertionFunctions, getClassesDefinedByCall, getDelegateRelationship, getDelegateSuperclassName, getExportPropertyFunction, getExportSymbolFunction, getObjectLiteralCast, getSingletonGetterClassName, identifyTypeDeclarationCall, isConstant, isConstantKey, isExported, isExported, isOptionalParameter, isPrivate, isSuperClassReference, isValidEnumKey, isVarArgsParameter
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JqueryCodingConvention

+
+public JqueryCodingConvention()
+
+
+
+ +

+JqueryCodingConvention

+
+public JqueryCodingConvention(CodingConvention wrapped)
+
+
+ + + + + + + + +
+Method Detail
+ +

+getGlobalObject

+
+public String getGlobalObject()
+
+
Description copied from interface: CodingConvention
+
Gets the name of the global object. +

+

+
Specified by:
getGlobalObject in interface CodingConvention
Overrides:
getGlobalObject in class CodingConventions.Proxy
+
+
+
+
+
+
+ +

+isPropertyTestFunction

+
+public boolean isPropertyTestFunction(Node call)
+
+
Description copied from interface: CodingConvention
+
Whether this CALL function is testing for the existence of a property. +

+

+
Specified by:
isPropertyTestFunction in interface CodingConvention
Overrides:
isPropertyTestFunction in class CodingConventions.Proxy
+
+
+
+
+
+
+ +

+isPrototypeAlias

+
+public boolean isPrototypeAlias(Node n)
+
+
Description copied from interface: CodingConvention
+
Whether this GETPROP node is an alias for an object prototype. +

+

+
Specified by:
isPrototypeAlias in interface CodingConvention
Overrides:
isPrototypeAlias in class CodingConventions.Proxy
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsAst.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsAst.html new file mode 100644 index 0000000..06ce02a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsAst.html @@ -0,0 +1,383 @@ + + + + + +JsAst (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class JsAst

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.JsAst
+
+
+
All Implemented Interfaces:
SourceAst, Serializable
+
+
+
+
public class JsAst
extends Object
implements SourceAst
+ + +

+Generates an AST for a JavaScript source file. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Constructor Summary
JsAst(SourceFile sourceFile) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidclearAst() + +
+          Removes any references to root node of the AST.
+ NodegetAstRoot(AbstractCompiler compiler) + +
+          Gets the root node of the AST for the source file this represents.
+ InputIdgetInputId() + +
+           
+ SourceFilegetSourceFile() + +
+          Returns the source file the generated AST represents.
+ voidsetSourceFile(SourceFile file) + +
+          Sets the source file the generated AST represents.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JsAst

+
+public JsAst(SourceFile sourceFile)
+
+
+ + + + + + + + +
+Method Detail
+ +

+getAstRoot

+
+public Node getAstRoot(AbstractCompiler compiler)
+
+
Description copied from interface: SourceAst
+
Gets the root node of the AST for the source file this represents. The AST + is lazily instantiated and cached. +

+

+
Specified by:
getAstRoot in interface SourceAst
+
+
+
+
+
+
+ +

+clearAst

+
+public void clearAst()
+
+
Description copied from interface: SourceAst
+
Removes any references to root node of the AST. If it is requested again, + another parse will be performed. This method is needed to allow the ASTs + to be garbage collected if the inputs are still around after compilation. +

+

+
Specified by:
clearAst in interface SourceAst
+
+
+
+
+
+
+ +

+getInputId

+
+public InputId getInputId()
+
+
+
Specified by:
getInputId in interface SourceAst
+
+
+ +
Returns:
The input id associated with this AST
+
+
+
+ +

+getSourceFile

+
+public SourceFile getSourceFile()
+
+
Description copied from interface: SourceAst
+
Returns the source file the generated AST represents. +

+

+
Specified by:
getSourceFile in interface SourceAst
+
+
+
+
+
+
+ +

+setSourceFile

+
+public void setSourceFile(SourceFile file)
+
+
Description copied from interface: SourceAst
+
Sets the source file the generated AST represents. This can be called after + deserializing if access to the source file is needed. If a different file + is provided than that with which this was created, an IllegalStateException + will be thrown. +

+

+
Specified by:
setSourceFile in interface SourceAst
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessage.Builder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessage.Builder.html new file mode 100644 index 0000000..fca93d3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessage.Builder.html @@ -0,0 +1,538 @@ + + + + + +JsMessage.Builder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class JsMessage.Builder

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.JsMessage.Builder
+
+
+
Enclosing class:
JsMessage
+
+
+
+
public static class JsMessage.Builder
extends Object
+ + +

+Contains functionality for creating js messages. Generates authoritative + keys and fingerprints for a message that must stay constant over time. + + This implementation correctly processes unnamed messages and creates a key + for them that looks like MSG_. +

+ +

+


+ +

+ + + + + + + + + + + + + + +
+Constructor Summary
JsMessage.Builder() + +
+           
JsMessage.Builder(String key) + +
+          Creates an instance.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ JsMessage.BuilderappendPlaceholderReference(String name) + +
+          Appends a placeholder reference to the message
+ JsMessage.BuilderappendStringPart(String part) + +
+          Appends a translatable string literal to the message.
+ JsMessagebuild() + +
+           
+ JsMessagebuild(JsMessage.IdGenerator idGenerator) + +
+           
+ StringgetKey() + +
+          Gets the message's key (e.g.
+ List<CharSequence>getParts() + +
+           
+ Set<String>getPlaceholders() + +
+          Returns the message registred placeholders
+ booleanhasParts() + +
+          Gets whether at least one part has been appended.
+ JsMessage.BuildersetDesc(String desc) + +
+          Sets the description of the message, which helps translators.
+ JsMessage.BuildersetIsHidden(boolean hidden) + +
+          Sets whether the message should be hidden from volunteer translators.
+ JsMessage.BuildersetKey(String key) + +
+           
+ JsMessage.BuildersetMeaning(String meaning) + +
+          Sets the programmer-specified meaning of this message, which + forces this message to translate differently.
+ JsMessage.BuildersetSourceName(String sourceName) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JsMessage.Builder

+
+public JsMessage.Builder()
+
+
+
+ +

+JsMessage.Builder

+
+public JsMessage.Builder(String key)
+
+
Creates an instance. +

+

+ + + + + + + + +
+Method Detail
+ +

+getKey

+
+public String getKey()
+
+
Gets the message's key (e.g. "MSG_HELLO"). +

+

+
+
+
+
+ +

+setKey

+
+public JsMessage.Builder setKey(String key)
+
+
+
Parameters:
key - a key that should uniquely identify this message; typically + it is the message's name (e.g. "MSG_HELLO").
+
+
+
+ +

+setSourceName

+
+public JsMessage.Builder setSourceName(String sourceName)
+
+
+
Parameters:
sourceName - The message's sourceName.
+
+
+
+ +

+appendPlaceholderReference

+
+public JsMessage.Builder appendPlaceholderReference(String name)
+
+
Appends a placeholder reference to the message +

+

+
+
+
+
+ +

+appendStringPart

+
+public JsMessage.Builder appendStringPart(String part)
+
+
Appends a translatable string literal to the message. +

+

+
+
+
+
+ +

+getPlaceholders

+
+public Set<String> getPlaceholders()
+
+
Returns the message registred placeholders +

+

+
+
+
+
+ +

+setDesc

+
+public JsMessage.Builder setDesc(String desc)
+
+
Sets the description of the message, which helps translators. +

+

+
+
+
+
+ +

+setMeaning

+
+public JsMessage.Builder setMeaning(String meaning)
+
+
Sets the programmer-specified meaning of this message, which + forces this message to translate differently. +

+

+
+
+
+
+ +

+setIsHidden

+
+public JsMessage.Builder setIsHidden(boolean hidden)
+
+
Sets whether the message should be hidden from volunteer translators. +

+

+
+
+
+
+ +

+hasParts

+
+public boolean hasParts()
+
+
Gets whether at least one part has been appended. +

+

+
+
+
+
+ +

+getParts

+
+public List<CharSequence> getParts()
+
+
+
+
+
+
+ +

+build

+
+public JsMessage build()
+
+
+
+
+
+
+ +

+build

+
+public JsMessage build(JsMessage.IdGenerator idGenerator)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessage.IdGenerator.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessage.IdGenerator.html new file mode 100644 index 0000000..eba8a6d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessage.IdGenerator.html @@ -0,0 +1,234 @@ + + + + + +JsMessage.IdGenerator (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface JsMessage.IdGenerator

+
+
All Known Implementing Classes:
GoogleJsMessageIdGenerator
+
+
+
Enclosing class:
JsMessage
+
+
+
+
public static interface JsMessage.IdGenerator
+ + +

+


+ +

+ + + + + + + + + + + + +
+Method Summary
+ StringgenerateId(String meaning, + List<CharSequence> messageParts) + +
+          Generate the ID for the message.
+  +

+ + + + + + + + +
+Method Detail
+ +

+generateId

+
+String generateId(String meaning,
+                  List<CharSequence> messageParts)
+
+
Generate the ID for the message. Messages with the same messageParts + and meaning will get the same id. Messages with the same id + will get the same translation. +

+

+
Parameters:
meaning - The programmer-specified meaning. If no @meaning + annotation appears, we will use the name of the variable it's + assigned to. If the variable is unnamed, then we will just + use a fingerprint of the message.
messageParts - The parts of the message, including the main + message text.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessage.PlaceholderReference.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessage.PlaceholderReference.html new file mode 100644 index 0000000..e73511c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessage.PlaceholderReference.html @@ -0,0 +1,378 @@ + + + + + +JsMessage.PlaceholderReference (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class JsMessage.PlaceholderReference

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.JsMessage.PlaceholderReference
+
+
+
All Implemented Interfaces:
CharSequence
+
+
+
Enclosing class:
JsMessage
+
+
+
+
public static class JsMessage.PlaceholderReference
extends Object
implements CharSequence
+ + +

+A reference to a placeholder in a translatable message. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ charcharAt(int index) + +
+           
+ booleanequals(Object o) + +
+           
+ StringgetName() + +
+           
+ inthashCode() + +
+           
+ intlength() + +
+           
+ CharSequencesubSequence(int start, + int end) + +
+           
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+length

+
+public int length()
+
+
+
Specified by:
length in interface CharSequence
+
+
+
+
+
+
+ +

+charAt

+
+public char charAt(int index)
+
+
+
Specified by:
charAt in interface CharSequence
+
+
+
+
+
+
+ +

+subSequence

+
+public CharSequence subSequence(int start,
+                                int end)
+
+
+
Specified by:
subSequence in interface CharSequence
+
+
+
+
+
+
+ +

+getName

+
+public String getName()
+
+
+
+
+
+
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Specified by:
toString in interface CharSequence
Overrides:
toString in class Object
+
+
+
+
+
+
+ +

+equals

+
+public boolean equals(Object o)
+
+
+
Overrides:
equals in class Object
+
+
+
+
+
+
+ +

+hashCode

+
+public int hashCode()
+
+
+
Overrides:
hashCode in class Object
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessage.Style.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessage.Style.html new file mode 100644 index 0000000..59dd124 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessage.Style.html @@ -0,0 +1,358 @@ + + + + + +JsMessage.Style (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum JsMessage.Style

+
+java.lang.Object
+  extended by java.lang.Enum<JsMessage.Style>
+      extended by com.google.javascript.jscomp.JsMessage.Style
+
+
+
All Implemented Interfaces:
Serializable, Comparable<JsMessage.Style>
+
+
+
Enclosing class:
JsMessage
+
+
+
+
public static enum JsMessage.Style
extends Enum<JsMessage.Style>
+ + +

+Message style that could be used for JS code parsing. + The enum order is from most relaxed to most restricted. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Enum Constant Summary
CLOSURE + +
+           
LEGACY + +
+           
RELAX + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static JsMessage.StylevalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static JsMessage.Style[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+LEGACY

+
+public static final JsMessage.Style LEGACY
+
+
+
+
+
+ +

+RELAX

+
+public static final JsMessage.Style RELAX
+
+
+
+
+
+ +

+CLOSURE

+
+public static final JsMessage.Style CLOSURE
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static JsMessage.Style[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (JsMessage.Style c : JsMessage.Style.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static JsMessage.Style valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessage.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessage.html new file mode 100644 index 0000000..9658954 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessage.html @@ -0,0 +1,544 @@ + + + + + +JsMessage (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class JsMessage

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.JsMessage
+
+
+
+
public class JsMessage
extends Object
+ + +

+A representation of a translatable message in JavaScript source code. + +

Instances are created using a JsMessage.Builder, + like this: +

+ JsMessage m = new JsMessage.Builder(key)
+     .appendPart("Hi ")
+     .appendPlaceholderReference("firstName")
+     .appendPart("!")
+     .setDesc("A welcome message")
+     .build();
+ 
+

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + +
+Nested Class Summary
+static classJsMessage.Builder + +
+          Contains functionality for creating js messages.
+static interfaceJsMessage.IdGenerator + +
+           
+static classJsMessage.PlaceholderReference + +
+          A reference to a placeholder in a translatable message.
+static classJsMessage.Style + +
+          Message style that could be used for JS code parsing.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleanequals(Object o) + +
+           
+ StringgetDesc() + +
+          Gets the description associated with this message, intended to help + translators, or null if this message has no description.
+ StringgetId() + +
+          Gets the message's id, or name (e.g.
+ StringgetKey() + +
+          Gets the message's key, or name (e.g.
+ StringgetSourceName() + +
+          Gets the message's sourceName.
+ inthashCode() + +
+           
+ booleanisAnonymous() + +
+           
+ booleanisEmpty() + +
+           
+ booleanisExternal() + +
+           
+ booleanisHidden() + +
+          Gets whether this message should be hidden from volunteer translators (to + reduce the chances of a new feature leak).
+ List<CharSequence>parts() + +
+          Gets a read-only list of the parts of this message.
+ Set<String>placeholders() + +
+          Gets a read-only set of the registered placeholders in this message.
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getSourceName

+
+public String getSourceName()
+
+
Gets the message's sourceName. +

+

+
+
+
+
+ +

+getKey

+
+public String getKey()
+
+
Gets the message's key, or name (e.g. "MSG_HELLO"). +

+

+
+
+
+
+ +

+isAnonymous

+
+public boolean isAnonymous()
+
+
+
+
+
+
+ +

+isExternal

+
+public boolean isExternal()
+
+
+
+
+
+
+ +

+getId

+
+public String getId()
+
+
Gets the message's id, or name (e.g. "92430284230902938293"). +

+

+
+
+
+
+ +

+getDesc

+
+public String getDesc()
+
+
Gets the description associated with this message, intended to help + translators, or null if this message has no description. +

+

+
+
+
+
+ +

+isHidden

+
+public boolean isHidden()
+
+
Gets whether this message should be hidden from volunteer translators (to + reduce the chances of a new feature leak). +

+

+
+
+
+
+ +

+parts

+
+public List<CharSequence> parts()
+
+
Gets a read-only list of the parts of this message. Each part is either a + String or a JsMessage.PlaceholderReference. +

+

+
+
+
+
+ +

+placeholders

+
+public Set<String> placeholders()
+
+
Gets a read-only set of the registered placeholders in this message. +

+

+
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+
+ +

+isEmpty

+
+public boolean isEmpty()
+
+
+ +
Returns:
false iff the message is represented by empty string.
+
+
+
+ +

+equals

+
+public boolean equals(Object o)
+
+
+
Overrides:
equals in class Object
+
+
+
+
+
+
+ +

+hashCode

+
+public int hashCode()
+
+
+
Overrides:
hashCode in class Object
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessageExtractor.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessageExtractor.html new file mode 100644 index 0000000..b2bac55 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/JsMessageExtractor.html @@ -0,0 +1,329 @@ + + + + + +JsMessageExtractor (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class JsMessageExtractor

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.JsMessageExtractor
+
+
+
+
public class JsMessageExtractor
extends Object
+ + +

+Extracts messages and message comments from JS code. + +

Uses a special prefix (e.g. MSG_) to determine which variables + are messages. Here are the recognized formats: + + + var MSG_FOO = "foo"; + var MSG_FOO_HELP = "this message is used for foo"; + + + + var MSG_BAR = function(a, b) { + return a + " bar " + b; + } + var MSG_BAR_HELP = "the bar message"; + + +

This class enforces the policy that message variable names must be unique + across all JS files. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
JsMessageExtractor(JsMessage.IdGenerator idGenerator, + JsMessage.Style style) + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+ + + + + +
+<T extends SourceFile> +
+Collection<JsMessage>
+
extractMessages(Iterable<T> inputs) + +
+          Extracts js messages from javascript code.
+ Collection<JsMessage>extractMessages(SourceFile... inputs) + +
+          Extracts js messages from javascript code.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JsMessageExtractor

+
+public JsMessageExtractor(JsMessage.IdGenerator idGenerator,
+                          JsMessage.Style style)
+
+
+ + + + + + + + +
+Method Detail
+ +

+extractMessages

+
+public Collection<JsMessage> extractMessages(SourceFile... inputs)
+                                      throws IOException
+
+
Extracts js messages from javascript code. +

+

+ +
Throws: +
IOException
+
+
+
+ +

+extractMessages

+
+public <T extends SourceFile> Collection<JsMessage> extractMessages(Iterable<T> inputs)
+                                      throws IOException
+
+
Extracts js messages from javascript code. +

+

+
Parameters:
inputs - the javascript source code inputs +
Returns:
the extracted messages collection +
Throws: +
IOException - if there is a problem reading the js code +
RuntimeException - if there are problems parsing the js code or the + js messages, or if two messages have the same key
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/LightweightMessageFormatter.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/LightweightMessageFormatter.html new file mode 100644 index 0000000..e7f8f62 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/LightweightMessageFormatter.html @@ -0,0 +1,323 @@ + + + + + +LightweightMessageFormatter (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class LightweightMessageFormatter

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.AbstractMessageFormatter
+      extended by com.google.javascript.jscomp.LightweightMessageFormatter
+
+
+
All Implemented Interfaces:
MessageFormatter
+
+
+
+
public class LightweightMessageFormatter
extends AbstractMessageFormatter
+ + +

+Lightweight message formatter. The format of messages this formatter + produces is very compact and to the point. +

+ +

+


+ +

+ + + + + + + + + + + + + + +
+Constructor Summary
LightweightMessageFormatter(SourceExcerptProvider source) + +
+           
LightweightMessageFormatter(SourceExcerptProvider source, + SourceExcerptProvider.SourceExcerpt excerpt) + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+ StringformatError(JSError error) + +
+          Format an error.
+ StringformatWarning(JSError warning) + +
+          Format a warning.
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.AbstractMessageFormatter
getSource, setColorize
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+LightweightMessageFormatter

+
+public LightweightMessageFormatter(SourceExcerptProvider source)
+
+
+
+ +

+LightweightMessageFormatter

+
+public LightweightMessageFormatter(SourceExcerptProvider source,
+                                   SourceExcerptProvider.SourceExcerpt excerpt)
+
+
+ + + + + + + + +
+Method Detail
+ +

+formatError

+
+public String formatError(JSError error)
+
+
Description copied from interface: MessageFormatter
+
Format an error. +

+

+
+
+
+
+ +

+formatWarning

+
+public String formatWarning(JSError warning)
+
+
Description copied from interface: MessageFormatter
+
Format a warning. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/LoggerErrorManager.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/LoggerErrorManager.html new file mode 100644 index 0000000..662aea2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/LoggerErrorManager.html @@ -0,0 +1,337 @@ + + + + + +LoggerErrorManager (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class LoggerErrorManager

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.BasicErrorManager
+      extended by com.google.javascript.jscomp.LoggerErrorManager
+
+
+
All Implemented Interfaces:
ErrorHandler, ErrorManager
+
+
+
+
public class LoggerErrorManager
extends BasicErrorManager
+ + +

+An error manager that logs errors and warnings using a logger in addition to + collecting them in memory. Errors are logged at the SEVERE level and warnings + are logged at the WARNING level. +

+ +

+


+ +

+ + + + + + + + + + + + + + +
+Constructor Summary
LoggerErrorManager(Logger logger) + +
+          Creates an instance with a source-less error formatter.
LoggerErrorManager(MessageFormatter formatter, + Logger logger) + +
+          Creates an instance.
+  + + + + + + + + + + + + + + + +
+Method Summary
+ voidprintln(CheckLevel level, + JSError error) + +
+          Print a message with a trailing new line.
+protected  voidprintSummary() + +
+          Print the summary of the compilation - number of errors and warnings.
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.BasicErrorManager
generateReport, getErrorCount, getErrors, getTypedPercent, getWarningCount, getWarnings, report, setTypedPercent
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+LoggerErrorManager

+
+public LoggerErrorManager(MessageFormatter formatter,
+                          Logger logger)
+
+
Creates an instance. +

+

+
+ +

+LoggerErrorManager

+
+public LoggerErrorManager(Logger logger)
+
+
Creates an instance with a source-less error formatter. +

+

+ + + + + + + + +
+Method Detail
+ +

+println

+
+public void println(CheckLevel level,
+                    JSError error)
+
+
Description copied from class: BasicErrorManager
+
Print a message with a trailing new line. This method is called by the + BasicErrorManager.generateReport() method when generating messages. +

+

+
Specified by:
println in class BasicErrorManager
+
+
+
+
+
+
+ +

+printSummary

+
+protected void printSummary()
+
+
Description copied from class: BasicErrorManager
+
Print the summary of the compilation - number of errors and warnings. +

+

+
Specified by:
printSummary in class BasicErrorManager
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/MessageBundle.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/MessageBundle.html new file mode 100644 index 0000000..7bc2755 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/MessageBundle.html @@ -0,0 +1,279 @@ + + + + + +MessageBundle (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface MessageBundle

+
+
All Known Implementing Classes:
EmptyMessageBundle, XtbMessageBundle
+
+
+
+
public interface MessageBundle
+ + +

+An interface for providing alterative values for user-visible messages in + javascript code. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ Iterable<JsMessage>getAllMessages() + +
+          Returns an iterable over the keys that this object has replacements for.
+ JsMessagegetMessage(String id) + +
+          Gets a message replacement.
+ JsMessage.IdGeneratoridGenerator() + +
+          Gets the message ID generator to use to compute message IDs for this + type of bundle.
+  +

+ + + + + + + + +
+Method Detail
+ +

+idGenerator

+
+JsMessage.IdGenerator idGenerator()
+
+
Gets the message ID generator to use to compute message IDs for this + type of bundle. +

+

+ +
Returns:
idGenerator instance or null if we do not want to use any custom + id generation. In case if idGenerator is null caller should decide how + to create id by itself. In the most cases using the message key is + enough.
+
+
+
+ +

+getMessage

+
+JsMessage getMessage(String id)
+
+
Gets a message replacement. +

+

+
Parameters:
id - the id of the message being replaced; the key is message ID + generated by JsMessage.IdGenerator +
Returns:
the message replacement, which may be null.
+
+
+
+ +

+getAllMessages

+
+Iterable<JsMessage> getAllMessages()
+
+
Returns an iterable over the keys that this object has replacements for. +

+

+ +
Returns:
all messages from this bundle.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/MessageFormatter.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/MessageFormatter.html new file mode 100644 index 0000000..3324a01 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/MessageFormatter.html @@ -0,0 +1,249 @@ + + + + + +MessageFormatter (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface MessageFormatter

+
+
All Known Implementing Classes:
AbstractMessageFormatter, LightweightMessageFormatter
+
+
+
+
public interface MessageFormatter
+ + +

+Format warnings and errors. This interface may be used by implementations of + ErrorManager to request message formatting capabilities. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Method Summary
+ StringformatError(JSError error) + +
+          Format an error.
+ StringformatWarning(JSError warning) + +
+          Format a warning.
+  +

+ + + + + + + + +
+Method Detail
+ +

+formatError

+
+String formatError(JSError error)
+
+
Format an error. +

+

+
+
+
+
+ +

+formatWarning

+
+String formatWarning(JSError warning)
+
+
Format a warning. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.AbstractNodeTypePruningCallback.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.AbstractNodeTypePruningCallback.html new file mode 100644 index 0000000..ca1cb52 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.AbstractNodeTypePruningCallback.html @@ -0,0 +1,324 @@ + + + + + +NodeTraversal.AbstractNodeTypePruningCallback (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class NodeTraversal.AbstractNodeTypePruningCallback

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.NodeTraversal.AbstractNodeTypePruningCallback
+
+
+
All Implemented Interfaces:
NodeTraversal.Callback
+
+
+
Enclosing class:
NodeTraversal
+
+
+
+
public abstract static class NodeTraversal.AbstractNodeTypePruningCallback
extends Object
implements NodeTraversal.Callback
+ + +

+Abstract callback to visit a pruned set of nodes. +

+ +

+


+ +

+ + + + + + + + + + + + + + +
+Constructor Summary
NodeTraversal.AbstractNodeTypePruningCallback(Set<Integer> nodeTypes) + +
+          Creates an abstract pruned callback.
NodeTraversal.AbstractNodeTypePruningCallback(Set<Integer> nodeTypes, + boolean include) + +
+          Creates an abstract pruned callback.
+  + + + + + + + + + + + +
+Method Summary
+ booleanshouldTraverse(NodeTraversal nodeTraversal, + Node n, + Node parent) + +
+          Visits a node in pre order (before visiting its children) and decides + whether this node's children should be traversed.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.NodeTraversal.Callback
visit
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+NodeTraversal.AbstractNodeTypePruningCallback

+
+public NodeTraversal.AbstractNodeTypePruningCallback(Set<Integer> nodeTypes)
+
+
Creates an abstract pruned callback. +

+

+
Parameters:
nodeTypes - the nodes to include in the traversal
+
+
+ +

+NodeTraversal.AbstractNodeTypePruningCallback

+
+public NodeTraversal.AbstractNodeTypePruningCallback(Set<Integer> nodeTypes,
+                                                     boolean include)
+
+
Creates an abstract pruned callback. +

+

+
Parameters:
nodeTypes - the nodes to include/exclude in the traversal
include - whether to include or exclude the nodes in the traversal
+
+ + + + + + + + +
+Method Detail
+ +

+shouldTraverse

+
+public boolean shouldTraverse(NodeTraversal nodeTraversal,
+                              Node n,
+                              Node parent)
+
+
Description copied from interface: NodeTraversal.Callback
+

Visits a node in pre order (before visiting its children) and decides + whether this node's children should be traversed. If children are + traversed, they will be visited by + NodeTraversal.Callback.visit(NodeTraversal, Node, Node) in post order.

+

Implementations can have side effects (e.g. modifying the parse + tree).

+

+

+
Specified by:
shouldTraverse in interface NodeTraversal.Callback
+
+
+ +
Returns:
whether the children of this node should be visited
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.AbstractPostOrderCallback.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.AbstractPostOrderCallback.html new file mode 100644 index 0000000..d6c45ed --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.AbstractPostOrderCallback.html @@ -0,0 +1,303 @@ + + + + + +NodeTraversal.AbstractPostOrderCallback (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class NodeTraversal.AbstractPostOrderCallback

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback
+
+
+
All Implemented Interfaces:
NodeTraversal.Callback
+
+
+
Direct Known Subclasses:
FindExportableNodes
+
+
+
Enclosing class:
NodeTraversal
+
+
+
+
public abstract static class NodeTraversal.AbstractPostOrderCallback
extends Object
implements NodeTraversal.Callback
+ + +

+Abstract callback to visit all nodes in post order. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
NodeTraversal.AbstractPostOrderCallback() + +
+           
+  + + + + + + + + + + + +
+Method Summary
+ booleanshouldTraverse(NodeTraversal nodeTraversal, + Node n, + Node parent) + +
+          Visits a node in pre order (before visiting its children) and decides + whether this node's children should be traversed.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.NodeTraversal.Callback
visit
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+NodeTraversal.AbstractPostOrderCallback

+
+public NodeTraversal.AbstractPostOrderCallback()
+
+
+ + + + + + + + +
+Method Detail
+ +

+shouldTraverse

+
+public final boolean shouldTraverse(NodeTraversal nodeTraversal,
+                                    Node n,
+                                    Node parent)
+
+
Description copied from interface: NodeTraversal.Callback
+

Visits a node in pre order (before visiting its children) and decides + whether this node's children should be traversed. If children are + traversed, they will be visited by + NodeTraversal.Callback.visit(NodeTraversal, Node, Node) in post order.

+

Implementations can have side effects (e.g. modifying the parse + tree).

+

+

+
Specified by:
shouldTraverse in interface NodeTraversal.Callback
+
+
+ +
Returns:
whether the children of this node should be visited
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.AbstractScopedCallback.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.AbstractScopedCallback.html new file mode 100644 index 0000000..42fb165 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.AbstractScopedCallback.html @@ -0,0 +1,352 @@ + + + + + +NodeTraversal.AbstractScopedCallback (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class NodeTraversal.AbstractScopedCallback

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.NodeTraversal.AbstractScopedCallback
+
+
+
All Implemented Interfaces:
NodeTraversal.Callback, NodeTraversal.ScopedCallback
+
+
+
Enclosing class:
NodeTraversal
+
+
+
+
public abstract static class NodeTraversal.AbstractScopedCallback
extends Object
implements NodeTraversal.ScopedCallback
+ + +

+Abstract scoped callback to visit all nodes in post order. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
NodeTraversal.AbstractScopedCallback() + +
+           
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidenterScope(NodeTraversal t) + +
+          Called immediately after entering a new scope.
+ voidexitScope(NodeTraversal t) + +
+          Called immediately before exiting a scope.
+ booleanshouldTraverse(NodeTraversal nodeTraversal, + Node n, + Node parent) + +
+          Visits a node in pre order (before visiting its children) and decides + whether this node's children should be traversed.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.NodeTraversal.Callback
visit
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+NodeTraversal.AbstractScopedCallback

+
+public NodeTraversal.AbstractScopedCallback()
+
+
+ + + + + + + + +
+Method Detail
+ +

+shouldTraverse

+
+public final boolean shouldTraverse(NodeTraversal nodeTraversal,
+                                    Node n,
+                                    Node parent)
+
+
Description copied from interface: NodeTraversal.Callback
+

Visits a node in pre order (before visiting its children) and decides + whether this node's children should be traversed. If children are + traversed, they will be visited by + NodeTraversal.Callback.visit(NodeTraversal, Node, Node) in post order.

+

Implementations can have side effects (e.g. modifying the parse + tree).

+

+

+
Specified by:
shouldTraverse in interface NodeTraversal.Callback
+
+
+ +
Returns:
whether the children of this node should be visited
+
+
+
+ +

+enterScope

+
+public void enterScope(NodeTraversal t)
+
+
Description copied from interface: NodeTraversal.ScopedCallback
+
Called immediately after entering a new scope. The new scope can + be accessed through t.getScope() +

+

+
Specified by:
enterScope in interface NodeTraversal.ScopedCallback
+
+
+
+
+
+
+ +

+exitScope

+
+public void exitScope(NodeTraversal t)
+
+
Description copied from interface: NodeTraversal.ScopedCallback
+
Called immediately before exiting a scope. The ending scope can + be accessed through t.getScope() +

+

+
Specified by:
exitScope in interface NodeTraversal.ScopedCallback
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.AbstractShallowCallback.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.AbstractShallowCallback.html new file mode 100644 index 0000000..09b1641 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.AbstractShallowCallback.html @@ -0,0 +1,301 @@ + + + + + +NodeTraversal.AbstractShallowCallback (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class NodeTraversal.AbstractShallowCallback

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback
+
+
+
All Implemented Interfaces:
NodeTraversal.Callback
+
+
+
Enclosing class:
NodeTraversal
+
+
+
+
public abstract static class NodeTraversal.AbstractShallowCallback
extends Object
implements NodeTraversal.Callback
+ + +

+Abstract callback to visit all nodes but not traverse into function + bodies. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
NodeTraversal.AbstractShallowCallback() + +
+           
+  + + + + + + + + + + + +
+Method Summary
+ booleanshouldTraverse(NodeTraversal nodeTraversal, + Node n, + Node parent) + +
+          Visits a node in pre order (before visiting its children) and decides + whether this node's children should be traversed.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.NodeTraversal.Callback
visit
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+NodeTraversal.AbstractShallowCallback

+
+public NodeTraversal.AbstractShallowCallback()
+
+
+ + + + + + + + +
+Method Detail
+ +

+shouldTraverse

+
+public final boolean shouldTraverse(NodeTraversal nodeTraversal,
+                                    Node n,
+                                    Node parent)
+
+
Description copied from interface: NodeTraversal.Callback
+

Visits a node in pre order (before visiting its children) and decides + whether this node's children should be traversed. If children are + traversed, they will be visited by + NodeTraversal.Callback.visit(NodeTraversal, Node, Node) in post order.

+

Implementations can have side effects (e.g. modifying the parse + tree).

+

+

+
Specified by:
shouldTraverse in interface NodeTraversal.Callback
+
+
+ +
Returns:
whether the children of this node should be visited
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.AbstractShallowStatementCallback.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.AbstractShallowStatementCallback.html new file mode 100644 index 0000000..de0efd3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.AbstractShallowStatementCallback.html @@ -0,0 +1,301 @@ + + + + + +NodeTraversal.AbstractShallowStatementCallback (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class NodeTraversal.AbstractShallowStatementCallback

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.NodeTraversal.AbstractShallowStatementCallback
+
+
+
All Implemented Interfaces:
NodeTraversal.Callback
+
+
+
Enclosing class:
NodeTraversal
+
+
+
+
public abstract static class NodeTraversal.AbstractShallowStatementCallback
extends Object
implements NodeTraversal.Callback
+ + +

+Abstract callback to visit all structure and statement nodes but doesn't + traverse into functions or expressions. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
NodeTraversal.AbstractShallowStatementCallback() + +
+           
+  + + + + + + + + + + + +
+Method Summary
+ booleanshouldTraverse(NodeTraversal nodeTraversal, + Node n, + Node parent) + +
+          Visits a node in pre order (before visiting its children) and decides + whether this node's children should be traversed.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.NodeTraversal.Callback
visit
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+NodeTraversal.AbstractShallowStatementCallback

+
+public NodeTraversal.AbstractShallowStatementCallback()
+
+
+ + + + + + + + +
+Method Detail
+ +

+shouldTraverse

+
+public final boolean shouldTraverse(NodeTraversal nodeTraversal,
+                                    Node n,
+                                    Node parent)
+
+
Description copied from interface: NodeTraversal.Callback
+

Visits a node in pre order (before visiting its children) and decides + whether this node's children should be traversed. If children are + traversed, they will be visited by + NodeTraversal.Callback.visit(NodeTraversal, Node, Node) in post order.

+

Implementations can have side effects (e.g. modifying the parse + tree).

+

+

+
Specified by:
shouldTraverse in interface NodeTraversal.Callback
+
+
+ +
Returns:
whether the children of this node should be visited
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.Callback.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.Callback.html new file mode 100644 index 0000000..45845d3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.Callback.html @@ -0,0 +1,273 @@ + + + + + +NodeTraversal.Callback (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface NodeTraversal.Callback

+
+
All Known Subinterfaces:
NodeTraversal.ScopedCallback
+
+
+
All Known Implementing Classes:
FindExportableNodes, NodeTraversal.AbstractNodeTypePruningCallback, NodeTraversal.AbstractPostOrderCallback, NodeTraversal.AbstractScopedCallback, NodeTraversal.AbstractShallowCallback, NodeTraversal.AbstractShallowStatementCallback, TypeCheck
+
+
+
Enclosing class:
NodeTraversal
+
+
+
+
public static interface NodeTraversal.Callback
+ + +

+Callback +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Method Summary
+ booleanshouldTraverse(NodeTraversal nodeTraversal, + Node n, + Node parent) + +
+          Visits a node in pre order (before visiting its children) and decides + whether this node's children should be traversed.
+ voidvisit(NodeTraversal t, + Node n, + Node parent) + +
+          Visits a node in post order (after its children have been visited).
+  +

+ + + + + + + + +
+Method Detail
+ +

+shouldTraverse

+
+boolean shouldTraverse(NodeTraversal nodeTraversal,
+                       Node n,
+                       Node parent)
+
+

Visits a node in pre order (before visiting its children) and decides + whether this node's children should be traversed. If children are + traversed, they will be visited by + visit(NodeTraversal, Node, Node) in post order.

+

Implementations can have side effects (e.g. modifying the parse + tree).

+

+

+ +
Returns:
whether the children of this node should be visited
+
+
+
+ +

+visit

+
+void visit(NodeTraversal t,
+           Node n,
+           Node parent)
+
+

Visits a node in post order (after its children have been visited). + A node is visited only if all its parents should be traversed + (shouldTraverse(NodeTraversal, Node, Node)).

+

Implementations can have side effects (e.g. modifying the parse + tree).

+

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.ScopedCallback.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.ScopedCallback.html new file mode 100644 index 0000000..30c81cc --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.ScopedCallback.html @@ -0,0 +1,271 @@ + + + + + +NodeTraversal.ScopedCallback (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface NodeTraversal.ScopedCallback

+
+
All Superinterfaces:
NodeTraversal.Callback
+
+
+
All Known Implementing Classes:
NodeTraversal.AbstractScopedCallback
+
+
+
Enclosing class:
NodeTraversal
+
+
+
+
public static interface NodeTraversal.ScopedCallback
extends NodeTraversal.Callback
+ + +

+Callback that also knows about scope changes +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Method Summary
+ voidenterScope(NodeTraversal t) + +
+          Called immediately after entering a new scope.
+ voidexitScope(NodeTraversal t) + +
+          Called immediately before exiting a scope.
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.NodeTraversal.Callback
shouldTraverse, visit
+  +

+ + + + + + + + +
+Method Detail
+ +

+enterScope

+
+void enterScope(NodeTraversal t)
+
+
Called immediately after entering a new scope. The new scope can + be accessed through t.getScope() +

+

+
+
+
+
+
+
+
+ +

+exitScope

+
+void exitScope(NodeTraversal t)
+
+
Called immediately before exiting a scope. The ending scope can + be accessed through t.getScope() +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.html new file mode 100644 index 0000000..cc9ee5e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeTraversal.html @@ -0,0 +1,847 @@ + + + + + +NodeTraversal (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class NodeTraversal

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.NodeTraversal
+
+
+
+
public class NodeTraversal
extends Object
+ + +

+Nodetraversal allows an iteration through the nodes in the parse tree, + and facilitates the optimizations on the parse tree. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Nested Class Summary
+static classNodeTraversal.AbstractNodeTypePruningCallback + +
+          Abstract callback to visit a pruned set of nodes.
+static classNodeTraversal.AbstractPostOrderCallback + +
+          Abstract callback to visit all nodes in post order.
+static classNodeTraversal.AbstractScopedCallback + +
+          Abstract scoped callback to visit all nodes in post order.
+static classNodeTraversal.AbstractShallowCallback + +
+          Abstract callback to visit all nodes but not traverse into function + bodies.
+static classNodeTraversal.AbstractShallowStatementCallback + +
+          Abstract callback to visit all structure and statement nodes but doesn't + traverse into functions or expressions.
+static interfaceNodeTraversal.Callback + +
+          Callback
+static interfaceNodeTraversal.ScopedCallback + +
+          Callback that also knows about scope changes
+ + + + + + + + + + +
+Field Summary
+static DiagnosticTypeNODE_TRAVERSAL_ERROR + +
+           
+  + + + + + + + + + + + + + +
+Constructor Summary
NodeTraversal(AbstractCompiler compiler, + NodeTraversal.Callback cb) + +
+          Creates a node traversal using the specified callback interface.
NodeTraversal(AbstractCompiler compiler, + NodeTraversal.Callback cb, + com.google.javascript.jscomp.ScopeCreator scopeCreator) + +
+          Creates a node traversal using the specified callback interface + and the scope creator.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ CompilergetCompiler() + +
+          Gets the compiler.
+ com.google.javascript.jscomp.ControlFlowGraph<Node>getControlFlowGraph() + +
+          Gets the control flow graph for the current JS scope.
+ NodegetCurrentNode() + +
+          Returns the node currently being traversed.
+ NodegetEnclosingFunction() + +
+          Examines the functions stack for the last instance of a function node.
+ CompilerInputgetInput() + +
+          Gets the current input source.
+ intgetLineNumber() + +
+          Gets the current line number, or zero if it cannot be determined.
+ JSModulegetModule() + +
+          Gets the current input module.
+ ScopegetScope() + +
+          Gets the current scope.
+ NodegetScopeRoot() + +
+          Returns the current scope's root.
+ StringgetSourceName() + +
+          Gets the current input source name.
+ booleanhasScope() + +
+           
+ JSErrormakeError(Node n, + CheckLevel level, + DiagnosticType type, + String... arguments) + +
+          Creates a JSError during NodeTraversal.
+ JSErrormakeError(Node n, + DiagnosticType type, + String... arguments) + +
+          Creates a JSError during NodeTraversal.
+ voidreport(Node n, + DiagnosticType diagnosticType, + String... arguments) + +
+          Reports a diagnostic (error or warning)
+static voidtraverse(AbstractCompiler compiler, + Node root, + NodeTraversal.Callback cb) + +
+          Traverses a node recursively.
+ voidtraverse(Node root) + +
+          Traverses a parse tree recursively.
+protected  voidtraverseInnerNode(Node node, + Node parent, + Scope refinedScope) + +
+          Traverses an inner node recursively with a refined scope.
+static voidtraverseRoots(AbstractCompiler compiler, + List<Node> roots, + NodeTraversal.Callback cb) + +
+          Traverses a list of node trees.
+static voidtraverseRoots(AbstractCompiler compiler, + NodeTraversal.Callback cb, + Node... roots) + +
+           
+ voidtraverseRoots(List<Node> roots) + +
+           
+ voidtraverseRoots(Node... roots) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+NODE_TRAVERSAL_ERROR

+
+public static final DiagnosticType NODE_TRAVERSAL_ERROR
+
+
+
+
+ + + + + + + + +
+Constructor Detail
+ +

+NodeTraversal

+
+public NodeTraversal(AbstractCompiler compiler,
+                     NodeTraversal.Callback cb)
+
+
Creates a node traversal using the specified callback interface. +

+

+
+ +

+NodeTraversal

+
+public NodeTraversal(AbstractCompiler compiler,
+                     NodeTraversal.Callback cb,
+                     com.google.javascript.jscomp.ScopeCreator scopeCreator)
+
+
Creates a node traversal using the specified callback interface + and the scope creator. +

+

+ + + + + + + + +
+Method Detail
+ +

+traverse

+
+public void traverse(Node root)
+
+
Traverses a parse tree recursively. +

+

+
+
+
+
+ +

+traverseRoots

+
+public void traverseRoots(Node... roots)
+
+
+
+
+
+
+ +

+traverseRoots

+
+public void traverseRoots(List<Node> roots)
+
+
+
+
+
+
+ +

+traverseInnerNode

+
+protected void traverseInnerNode(Node node,
+                                 Node parent,
+                                 Scope refinedScope)
+
+
Traverses an inner node recursively with a refined scope. An inner node may + be any node with a non null parent (i.e. all nodes except the + root). +

+

+
Parameters:
node - the node to traverse
parent - the node's parent, it may be not be null
refinedScope - the refined scope of the scope currently at the top of + the scope stack or in trivial cases that very scope or null
+
+
+
+ +

+getCompiler

+
+public Compiler getCompiler()
+
+
Gets the compiler. +

+

+
+
+
+
+ +

+getLineNumber

+
+public int getLineNumber()
+
+
Gets the current line number, or zero if it cannot be determined. The line + number is retrieved lazily as a running time optimization. +

+

+
+
+
+
+ +

+getSourceName

+
+public String getSourceName()
+
+
Gets the current input source name. +

+

+ +
Returns:
A string that may be empty, but not null
+
+
+
+ +

+getInput

+
+public CompilerInput getInput()
+
+
Gets the current input source. +

+

+
+
+
+
+ +

+getModule

+
+public JSModule getModule()
+
+
Gets the current input module. +

+

+
+
+
+
+ +

+getCurrentNode

+
+public Node getCurrentNode()
+
+
Returns the node currently being traversed. +

+

+
+
+
+
+ +

+traverse

+
+public static void traverse(AbstractCompiler compiler,
+                            Node root,
+                            NodeTraversal.Callback cb)
+
+
Traverses a node recursively. +

+

+
+
+
+
+ +

+traverseRoots

+
+public static void traverseRoots(AbstractCompiler compiler,
+                                 List<Node> roots,
+                                 NodeTraversal.Callback cb)
+
+
Traverses a list of node trees. +

+

+
+
+
+
+ +

+traverseRoots

+
+public static void traverseRoots(AbstractCompiler compiler,
+                                 NodeTraversal.Callback cb,
+                                 Node... roots)
+
+
+
+
+
+
+ +

+getEnclosingFunction

+
+public Node getEnclosingFunction()
+
+
Examines the functions stack for the last instance of a function node. +

+

+
+
+
+
+ +

+getScope

+
+public Scope getScope()
+
+
Gets the current scope. +

+

+
+
+
+
+ +

+getControlFlowGraph

+
+public com.google.javascript.jscomp.ControlFlowGraph<Node> getControlFlowGraph()
+
+
Gets the control flow graph for the current JS scope. +

+

+
+
+
+
+ +

+getScopeRoot

+
+public Node getScopeRoot()
+
+
Returns the current scope's root. +

+

+
+
+
+
+ +

+hasScope

+
+public boolean hasScope()
+
+
+
+
+
+
+ +

+report

+
+public void report(Node n,
+                   DiagnosticType diagnosticType,
+                   String... arguments)
+
+
Reports a diagnostic (error or warning) +

+

+
+
+
+
+ +

+makeError

+
+public JSError makeError(Node n,
+                         CheckLevel level,
+                         DiagnosticType type,
+                         String... arguments)
+
+
Creates a JSError during NodeTraversal. +

+

+
Parameters:
n - Determines the line and char position within the source file name
type - The DiagnosticType
arguments - Arguments to be incorporated into the message
+
+
+
+ +

+makeError

+
+public JSError makeError(Node n,
+                         DiagnosticType type,
+                         String... arguments)
+
+
Creates a JSError during NodeTraversal. +

+

+
Parameters:
n - Determines the line and char position within the source file name
type - The DiagnosticType
arguments - Arguments to be incorporated into the message
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeUtil.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeUtil.html new file mode 100644 index 0000000..bbd7a97 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/NodeUtil.html @@ -0,0 +1,543 @@ + + + + + +NodeUtil (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class NodeUtil

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.NodeUtil
+
+
+
+
public final class NodeUtil
extends Object
+ + +

+NodeUtil contains utilities that get properties from the Node object. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static JSDocInfogetFunctionJSDocInfo(Node n) + +
+          Get the JSDocInfo for a function.
+static NodegetFunctionParameters(Node fnNode) + +
+           
+static InputIdgetInputId(Node n) + +
+           
+static intgetInverseOperator(int type) + +
+          Returns the inverse of an operator if it is invertible.
+static StringgetNearestFunctionName(Node n) + +
+          Gets the function's name.
+static NodegetRootOfQualifiedName(Node qName) + +
+          Gets the root node of a qualified name.
+static StaticSourceFilegetSourceFile(Node n) + +
+           
+static StringgetSourceName(Node n) + +
+           
+static Collection<Node>getVarsDeclaredInBranch(Node root) + +
+          Retrieves vars declared in the current node tree, excluding descent scopes.
+static booleanisRelationalOperation(Node n) + +
+          Returns true if the operator on this node is relational.
+static booleanisSymmetricOperation(Node n) + +
+          Returns true if the operator on this node is symmetric
+static booleanisValidQualifiedName(String name) + +
+          Determines whether the given name is a valid qualified name.
+static booleanisValidSimpleName(String name) + +
+          Determines whether the given name is a valid variable name.
+static NodenewExpr(Node child) + +
+          Creates an EXPR_RESULT.
+static NodenewQualifiedNameNode(CodingConvention convention, + String name) + +
+          Creates a node representing a qualified name.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getNearestFunctionName

+
+public static String getNearestFunctionName(Node n)
+
+
Gets the function's name. This method recognizes the forms: +
    +
  • &#123;'name': function() ...&#125;
  • +
  • &#123;name: function() ...&#125;
  • +
  • function name() ...
  • +
  • var name = function() ...
  • +
  • qualified.name = function() ...
  • +
  • var name2 = function name1() ...
  • +
  • qualified.name2 = function name1() ...
  • +
+

+

+
Parameters:
n - a node whose type is Token.FUNCTION +
Returns:
the function's name, or null if it has no name
+
+
+
+ +

+isSymmetricOperation

+
+public static boolean isSymmetricOperation(Node n)
+
+
Returns true if the operator on this node is symmetric +

+

+
+
+
+
+ +

+isRelationalOperation

+
+public static boolean isRelationalOperation(Node n)
+
+
Returns true if the operator on this node is relational. + the returned set does not include the equalities. +

+

+
+
+
+
+ +

+getInverseOperator

+
+public static int getInverseOperator(int type)
+
+
Returns the inverse of an operator if it is invertible. + ex. '>' ==> '<' +

+

+
+
+
+
+ +

+newExpr

+
+public static Node newExpr(Node child)
+
+
Creates an EXPR_RESULT. +

+

+
Parameters:
child - The expression itself. +
Returns:
Newly created EXPR node with the child as subexpression.
+
+
+
+ +

+newQualifiedNameNode

+
+public static Node newQualifiedNameNode(CodingConvention convention,
+                                        String name)
+
+
Creates a node representing a qualified name. +

+

+
Parameters:
name - A qualified name (e.g. "foo" or "foo.bar.baz") +
Returns:
A NAME or GETPROP node
+
+
+
+ +

+getRootOfQualifiedName

+
+public static Node getRootOfQualifiedName(Node qName)
+
+
Gets the root node of a qualified name. Must be either NAME or THIS. +

+

+
+
+
+
+ +

+isValidSimpleName

+
+public static boolean isValidSimpleName(String name)
+
+
Determines whether the given name is a valid variable name. +

+

+
+
+
+
+ +

+isValidQualifiedName

+
+public static boolean isValidQualifiedName(String name)
+
+
Determines whether the given name is a valid qualified name. +

+

+
+
+
+
+ +

+getVarsDeclaredInBranch

+
+public static Collection<Node> getVarsDeclaredInBranch(Node root)
+
+
Retrieves vars declared in the current node tree, excluding descent scopes. +

+

+
+
+
+
+ +

+getFunctionParameters

+
+public static Node getFunctionParameters(Node fnNode)
+
+
+
Parameters:
fnNode - The function. +
Returns:
The Node containing the Function parameters.
+
+
+
+ +

+getFunctionJSDocInfo

+
+public static JSDocInfo getFunctionJSDocInfo(Node n)
+
+
Get the JSDocInfo for a function. +

+

+
+
+
+
+ +

+getSourceName

+
+public static String getSourceName(Node n)
+
+
+
Parameters:
n - The node. +
Returns:
The source name property on the node or its ancestors.
+
+
+
+ +

+getSourceFile

+
+public static StaticSourceFile getSourceFile(Node n)
+
+
+
Parameters:
n - The node. +
Returns:
The source name property on the node or its ancestors.
+
+
+
+ +

+getInputId

+
+public static InputId getInputId(Node n)
+
+
+
Parameters:
n - The node. +
Returns:
The InputId property on the node or its ancestors.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ObjectPropertyStringPreprocess.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ObjectPropertyStringPreprocess.html new file mode 100644 index 0000000..f2bfdae --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ObjectPropertyStringPreprocess.html @@ -0,0 +1,293 @@ + + + + + +ObjectPropertyStringPreprocess (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class ObjectPropertyStringPreprocess

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.ObjectPropertyStringPreprocess
+
+
+
All Implemented Interfaces:
CompilerPass
+
+
+
+
public class ObjectPropertyStringPreprocess
extends Object
implements CompilerPass
+ + +

+Rewrites new goog.testing.ObjectPropertyString(foo, 'bar') to + new JSCompiler_ObjectPropertyString(window, foo.bar). + + These two passes are for use with goog.testing.PropertyReplacer. + + + var ops = new goog.testing.ObjectPropertyString(foo.prototype, 'bar'); + propertyReplacer.set(ops,object, ops.propertyString, baz); + +

+ +

+

+
See Also:
ObjectPropertyStringPostprocess
+
+ +

+ + + + + + + + + + + +
+Field Summary
+static StringEXTERN_OBJECT_PROPERTY_STRING + +
+           
+  + + + + + + + + + + + +
+Method Summary
+ voidprocess(Node externs, + Node root) + +
+          Process the JS with root node root.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+EXTERN_OBJECT_PROPERTY_STRING

+
+public static final String EXTERN_OBJECT_PROPERTY_STRING
+
+
+
See Also:
Constant Field Values
+
+ + + + + + + + +
+Method Detail
+ +

+process

+
+public void process(Node externs,
+                    Node root)
+
+
Description copied from interface: CompilerPass
+
Process the JS with root node root. + Can modify the contents of each Node tree +

+

+
Specified by:
process in interface CompilerPass
+
+
+
Parameters:
externs - Top of external JS tree
root - Top of JS tree
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PassConfig.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PassConfig.html new file mode 100644 index 0000000..bbc8763 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PassConfig.html @@ -0,0 +1,304 @@ + + + + + +PassConfig (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class PassConfig

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.PassConfig
+
+
+
Direct Known Subclasses:
DefaultPassConfig
+
+
+
+
public abstract class PassConfig
extends Object
+ + +

+Pass factories and meta-data for native Compiler passes. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
PassConfig(CompilerOptions options) + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+protected abstract  List<PassFactory>getChecks() + +
+          Gets the checking passes to run.
+protected abstract  List<PassFactory>getOptimizations() + +
+          Gets the optimization passes to run.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+PassConfig

+
+public PassConfig(CompilerOptions options)
+
+
+ + + + + + + + +
+Method Detail
+ +

+getChecks

+
+protected abstract List<PassFactory> getChecks()
+
+
Gets the checking passes to run. + + Checking passes revolve around emitting warnings and errors. + They also may include pre-processor passes needed to do + error analysis more effectively. + + Clients that only want to analyze code (like IDEs) and not emit + code will only run checks and not optimizations. +

+

+
+
+
+
+ +

+getOptimizations

+
+protected abstract List<PassFactory> getOptimizations()
+
+
Gets the optimization passes to run. + + Optimization passes revolve around producing smaller and faster code. + They should always run after checking passes. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PassFactory.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PassFactory.html new file mode 100644 index 0000000..bced727 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PassFactory.html @@ -0,0 +1,280 @@ + + + + + +PassFactory (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class PassFactory

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.PassFactory
+
+
+
+
public abstract class PassFactory
extends Object
+ + +

+A factory for creating JSCompiler passes based on the Options + injected. Contains all meta-data about compiler passes (like + whether it can be run multiple times, a human-readable name for + logging, etc.). +

+ +

+


+ +

+ + + + + + + + + + + + +
+Constructor Summary
+protected PassFactory(String name, + boolean isOneTimePass) + +
+           
+  + + + + + + + + + + + +
+Method Summary
+protected abstract  CompilerPasscreateInternal(AbstractCompiler compiler) + +
+          Creates a new compiler pass to be run.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+PassFactory

+
+protected PassFactory(String name,
+                      boolean isOneTimePass)
+
+
+
Parameters:
name - The name of the pass that this factory creates.
isOneTimePass - If true, the pass produced by this factory can + only be run once.
+
+ + + + + + + + +
+Method Detail
+ +

+createInternal

+
+protected abstract CompilerPass createInternal(AbstractCompiler compiler)
+
+
Creates a new compiler pass to be run. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PeepholeCollectPropertyAssignments.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PeepholeCollectPropertyAssignments.html new file mode 100644 index 0000000..69969bc --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PeepholeCollectPropertyAssignments.html @@ -0,0 +1,347 @@ + + + + + +PeepholeCollectPropertyAssignments (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class PeepholeCollectPropertyAssignments

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.PeepholeCollectPropertyAssignments
+
+
+
+
public class PeepholeCollectPropertyAssignments
extends Object
+ + +

+A pass that looks for assignments to properties of an object or array + immediately following its creation using the abbreviated syntax. +

+ E.g. var a = [];a[0] = 0 is optimized to var a = [0] and + similarly for the object constructor. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
PeepholeCollectPropertyAssignments() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+protected  booleanareNodesEqualForInlining(Node n1, + Node n2) + +
+          Are the nodes equal for the purpose of inlining? + If type aware optimizations are on, type equality is checked.
+protected  voiderror(DiagnosticType diagnostic, + Node n) + +
+          Helper method for reporting an error to the compiler when applying a + peephole optimization.
+protected  booleanisASTNormalized() + +
+          Is the current AST normalized? (e.g.
+protected  voidreportCodeChange() + +
+          Helper method for telling the compiler that something has changed.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+PeepholeCollectPropertyAssignments

+
+public PeepholeCollectPropertyAssignments()
+
+
+ + + + + + + + +
+Method Detail
+ +

+error

+
+protected void error(DiagnosticType diagnostic,
+                     Node n)
+
+
Helper method for reporting an error to the compiler when applying a + peephole optimization. +

+

+
Parameters:
diagnostic - The error type
n - The node for which the error should be reported
+
+
+
+ +

+reportCodeChange

+
+protected void reportCodeChange()
+
+
Helper method for telling the compiler that something has changed. + Subclasses must call these if they have changed the AST. +

+

+
+
+
+
+ +

+areNodesEqualForInlining

+
+protected boolean areNodesEqualForInlining(Node n1,
+                                           Node n2)
+
+
Are the nodes equal for the purpose of inlining? + If type aware optimizations are on, type equality is checked. +

+

+
+
+
+
+ +

+isASTNormalized

+
+protected boolean isASTNormalized()
+
+
Is the current AST normalized? (e.g. has the Normalize pass been run + and has the Denormalize pass not yet been run?) +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PerformanceTracker.Stats.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PerformanceTracker.Stats.html new file mode 100644 index 0000000..6652a21 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PerformanceTracker.Stats.html @@ -0,0 +1,401 @@ + + + + + +PerformanceTracker.Stats (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class PerformanceTracker.Stats

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.PerformanceTracker.Stats
+
+
+
Enclosing class:
PerformanceTracker
+
+
+
+
public static class PerformanceTracker.Stats
extends Object
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Field Summary
+ intchanges + +
+           
+ intdiff + +
+           
+ intgzDiff + +
+           
+ intgzSize + +
+           
+ Stringpass + +
+           
+ intruns + +
+           
+ longruntime + +
+           
+ intsize + +
+           
+  + + + + + + + + + + +
+Constructor Summary
PerformanceTracker.Stats(String pass) + +
+           
+  + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+pass

+
+public final String pass
+
+
+
+
+
+ +

+runtime

+
+public long runtime
+
+
+
+
+
+ +

+runs

+
+public int runs
+
+
+
+
+
+ +

+changes

+
+public int changes
+
+
+
+
+
+ +

+diff

+
+public int diff
+
+
+
+
+
+ +

+gzDiff

+
+public int gzDiff
+
+
+
+
+
+ +

+size

+
+public int size
+
+
+
+
+
+ +

+gzSize

+
+public int gzSize
+
+
+
+
+ + + + + + + + +
+Constructor Detail
+ +

+PerformanceTracker.Stats

+
+public PerformanceTracker.Stats(String pass)
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PerformanceTracker.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PerformanceTracker.html new file mode 100644 index 0000000..670e15b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PerformanceTracker.html @@ -0,0 +1,324 @@ + + + + + +PerformanceTracker (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class PerformanceTracker

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.PerformanceTracker
+
+
+
+
public class PerformanceTracker
extends Object
+ + +

+


+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classPerformanceTracker.Stats + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ com.google.common.collect.ImmutableMap<String,Integer>getCodeSizeRecord() + +
+           
+ com.google.common.collect.ImmutableList<PerformanceTracker.Stats>getLog() + +
+           
+ com.google.common.collect.ImmutableMap<String,Long>getRuntimeRecord() + +
+           
+ com.google.common.collect.ImmutableMap<String,PerformanceTracker.Stats>getStats() + +
+           
+ com.google.common.collect.ImmutableMap<String,Integer>getZippedCodeSizeRecord() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getRuntimeRecord

+
+public com.google.common.collect.ImmutableMap<String,Long> getRuntimeRecord()
+
+
+
+
+
+
+ +

+getStats

+
+public com.google.common.collect.ImmutableMap<String,PerformanceTracker.Stats> getStats()
+
+
+
+
+
+
+ +

+getLog

+
+public com.google.common.collect.ImmutableList<PerformanceTracker.Stats> getLog()
+
+
+
+
+
+
+ +

+getCodeSizeRecord

+
+public com.google.common.collect.ImmutableMap<String,Integer> getCodeSizeRecord()
+
+
+
+
+
+
+ +

+getZippedCodeSizeRecord

+
+public com.google.common.collect.ImmutableMap<String,Integer> getZippedCodeSizeRecord()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PrintStreamErrorManager.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PrintStreamErrorManager.html new file mode 100644 index 0000000..2475f46 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PrintStreamErrorManager.html @@ -0,0 +1,362 @@ + + + + + +PrintStreamErrorManager (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class PrintStreamErrorManager

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.BasicErrorManager
+      extended by com.google.javascript.jscomp.PrintStreamErrorManager
+
+
+
All Implemented Interfaces:
ErrorHandler, ErrorManager
+
+
+
+
public class PrintStreamErrorManager
extends BasicErrorManager
+ + +

+

An error manager that prints errors and warnings to the print stream + provided in addition to the functionality of the + BasicErrorManager.

+ +

It collaborates with a SourceExcerptProvider via a + MessageFormatter to display error messages with source context.

+

+ +

+


+ +

+ + + + + + + + + + + + + + +
+Constructor Summary
PrintStreamErrorManager(MessageFormatter formatter, + PrintStream stream) + +
+          Creates an error manager.
PrintStreamErrorManager(PrintStream stream) + +
+          Creates an instance with a source-less error formatter.
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidprintln(CheckLevel level, + JSError error) + +
+          Print a message with a trailing new line.
+ voidprintSummary() + +
+          Print the summary of the compilation - number of errors and warnings.
+ voidsetSummaryDetailLevel(int summaryDetailLevel) + +
+           
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.BasicErrorManager
generateReport, getErrorCount, getErrors, getTypedPercent, getWarningCount, getWarnings, report, setTypedPercent
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+PrintStreamErrorManager

+
+public PrintStreamErrorManager(MessageFormatter formatter,
+                               PrintStream stream)
+
+
Creates an error manager. +

+

+
Parameters:
formatter - the message formatter used to format the messages
stream - the stream on which the errors and warnings should be + printed. This class does not close the stream
+
+
+ +

+PrintStreamErrorManager

+
+public PrintStreamErrorManager(PrintStream stream)
+
+
Creates an instance with a source-less error formatter. +

+

+ + + + + + + + +
+Method Detail
+ +

+println

+
+public void println(CheckLevel level,
+                    JSError error)
+
+
Description copied from class: BasicErrorManager
+
Print a message with a trailing new line. This method is called by the + BasicErrorManager.generateReport() method when generating messages. +

+

+
Specified by:
println in class BasicErrorManager
+
+
+
+
+
+
+ +

+setSummaryDetailLevel

+
+public void setSummaryDetailLevel(int summaryDetailLevel)
+
+
+
+
+
+
+ +

+printSummary

+
+public void printSummary()
+
+
Description copied from class: BasicErrorManager
+
Print the summary of the compilation - number of errors and warnings. +

+

+
Specified by:
printSummary in class BasicErrorManager
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ProcessCommonJSModules.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ProcessCommonJSModules.html new file mode 100644 index 0000000..9f433ef --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ProcessCommonJSModules.html @@ -0,0 +1,344 @@ + + + + + +ProcessCommonJSModules (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class ProcessCommonJSModules

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.ProcessCommonJSModules
+
+
+
All Implemented Interfaces:
CompilerPass
+
+
+
+
public class ProcessCommonJSModules
extends Object
implements CompilerPass
+ + +

+Rewrites a Common JS module http://wiki.commonjs.org/wiki/Modules/1.1.1 + into a form that can be safely concatenated. + Does not add a function around the module body but instead adds suffixes + to global variables to avoid conflicts. + Calls to require are changed to reference the required module directly. + goog.provide and goog.require are emitted for closure compiler automatic + ordering. +

+ +

+


+ +

+ + + + + + + + + + + +
+Field Summary
+static StringDEFAULT_FILENAME_PREFIX + +
+           
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidprocess(Node externs, + Node root) + +
+          Process the JS with root node root.
+static StringtoModuleName(String filename) + +
+          Turns a filename into a JS identifier that is used for moduleNames in + rewritten code.
+static StringtoModuleName(String requiredFilename, + String currentFilename) + +
+          Turn a filename into a moduleName with support for relative addressing + with ./ and ../ based on currentFilename;
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+DEFAULT_FILENAME_PREFIX

+
+public static final String DEFAULT_FILENAME_PREFIX
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+process

+
+public void process(Node externs,
+                    Node root)
+
+
Description copied from interface: CompilerPass
+
Process the JS with root node root. + Can modify the contents of each Node tree +

+

+
Specified by:
process in interface CompilerPass
+
+
+
Parameters:
externs - Top of external JS tree
root - Top of JS tree
+
+
+
+ +

+toModuleName

+
+public static String toModuleName(String filename)
+
+
Turns a filename into a JS identifier that is used for moduleNames in + rewritten code. Removes leading ./, replaces / with $, removes trailing .js + and replaces - with _. All moduleNames get a "module$" prefix. +

+

+
+
+
+
+
+
+
+ +

+toModuleName

+
+public static String toModuleName(String requiredFilename,
+                                  String currentFilename)
+
+
Turn a filename into a moduleName with support for relative addressing + with ./ and ../ based on currentFilename; +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PropertyRenamingPolicy.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PropertyRenamingPolicy.html new file mode 100644 index 0000000..666994e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/PropertyRenamingPolicy.html @@ -0,0 +1,397 @@ + + + + + +PropertyRenamingPolicy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum PropertyRenamingPolicy

+
+java.lang.Object
+  extended by java.lang.Enum<PropertyRenamingPolicy>
+      extended by com.google.javascript.jscomp.PropertyRenamingPolicy
+
+
+
All Implemented Interfaces:
Serializable, Comparable<PropertyRenamingPolicy>
+
+
+
+
public enum PropertyRenamingPolicy
extends Enum<PropertyRenamingPolicy>
+ + +

+Policies to determine how properties should be renamed. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + +
+Enum Constant Summary
AGGRESSIVE_HEURISTIC + +
+          Rename properties more heuristically.
ALL_UNQUOTED + +
+          Rename all properties that aren't explicitly quoted and aren't + externally defined (i.e.
HEURISTIC + +
+          Rename properties heuristically.
OFF + +
+          Rename no properties.
UNSPECIFIED + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static PropertyRenamingPolicyvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static PropertyRenamingPolicy[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+OFF

+
+public static final PropertyRenamingPolicy OFF
+
+
Rename no properties. +

+

+
+
+
+ +

+HEURISTIC

+
+public static final PropertyRenamingPolicy HEURISTIC
+
+
Rename properties heuristically. +

+

+
See Also:
RenamePrototypes
+
+
+ +

+AGGRESSIVE_HEURISTIC

+
+public static final PropertyRenamingPolicy AGGRESSIVE_HEURISTIC
+
+
Rename properties more heuristically. +

+

+
See Also:
RenamePrototypes
+
+
+ +

+ALL_UNQUOTED

+
+public static final PropertyRenamingPolicy ALL_UNQUOTED
+
+
Rename all properties that aren't explicitly quoted and aren't + externally defined (i.e. declared in an externs file). This policy + achieves better compaction than the others. +

+

+
See Also:
RenameProperties
+
+
+ +

+UNSPECIFIED

+
+public static final PropertyRenamingPolicy UNSPECIFIED
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static PropertyRenamingPolicy[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (PropertyRenamingPolicy c : PropertyRenamingPolicy.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static PropertyRenamingPolicy valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Region.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Region.html new file mode 100644 index 0000000..949a945 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Region.html @@ -0,0 +1,269 @@ + + + + + +Region (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface Region

+
+
All Known Implementing Classes:
SimpleRegion
+
+
+
+
public interface Region
+ + +

+Source code region. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ intgetBeginningLineNumber() + +
+          Get the beginning line number.
+ intgetEndingLineNumber() + +
+          Get the ending line number.
+ StringgetSourceExcerpt() + +
+          Get the source region.
+  +

+ + + + + + + + +
+Method Detail
+ +

+getSourceExcerpt

+
+String getSourceExcerpt()
+
+
Get the source region. +

+

+
+
+
+
+ +

+getBeginningLineNumber

+
+int getBeginningLineNumber()
+
+
Get the beginning line number. +

+

+
+
+
+
+ +

+getEndingLineNumber

+
+int getEndingLineNumber()
+
+
Get the ending line number. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Result.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Result.html new file mode 100644 index 0000000..b257460 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Result.html @@ -0,0 +1,508 @@ + + + + + +Result (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class Result

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.Result
+
+
+
+
public class Result
extends Object
+ + +

+Compilation results +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Field Summary
+ Map<String,Integer>cssNames + +
+           
+ StringdebugLog + +
+           
+ JSError[]errors + +
+           
+ StringexternExport + +
+           
+ FunctionInformationMapfunctionInformationMap + +
+           
+ StringidGeneratorMap + +
+           
+ VariableMapnamedAnonFunctionMap + +
+           
+ VariableMappropertyMap + +
+           
+ SourceMapsourceMap + +
+           
+ VariableMapstringMap + +
+           
+ booleansuccess + +
+           
+ VariableMapvariableMap + +
+           
+ JSError[]warnings + +
+           
+  + + + + + + + + + + +
+Constructor Summary
Result(JSError[] errors, + JSError[] warnings, + String debugLog, + VariableMap variableMap, + VariableMap propertyMap, + VariableMap namedAnonFunctionMap, + FunctionInformationMap functionInformationMap, + SourceMap sourceMap, + String externExport) + +
+           
+  + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+success

+
+public final boolean success
+
+
+
+
+
+ +

+errors

+
+public final JSError[] errors
+
+
+
+
+
+ +

+warnings

+
+public final JSError[] warnings
+
+
+
+
+
+ +

+debugLog

+
+public final String debugLog
+
+
+
+
+
+ +

+variableMap

+
+public final VariableMap variableMap
+
+
+
+
+
+ +

+propertyMap

+
+public final VariableMap propertyMap
+
+
+
+
+
+ +

+namedAnonFunctionMap

+
+public final VariableMap namedAnonFunctionMap
+
+
+
+
+
+ +

+stringMap

+
+public final VariableMap stringMap
+
+
+
+
+
+ +

+functionInformationMap

+
+public final FunctionInformationMap functionInformationMap
+
+
+
+
+
+ +

+sourceMap

+
+public final SourceMap sourceMap
+
+
+
+
+
+ +

+cssNames

+
+public final Map<String,Integer> cssNames
+
+
+
+
+
+ +

+externExport

+
+public final String externExport
+
+
+
+
+
+ +

+idGeneratorMap

+
+public final String idGeneratorMap
+
+
+
+
+ + + + + + + + +
+Constructor Detail
+ +

+Result

+
+public Result(JSError[] errors,
+              JSError[] warnings,
+              String debugLog,
+              VariableMap variableMap,
+              VariableMap propertyMap,
+              VariableMap namedAnonFunctionMap,
+              FunctionInformationMap functionInformationMap,
+              SourceMap sourceMap,
+              String externExport)
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Scope.Arguments.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Scope.Arguments.html new file mode 100644 index 0000000..e248d75 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Scope.Arguments.html @@ -0,0 +1,277 @@ + + + + + +Scope.Arguments (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class Scope.Arguments

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.Scope.Var
+      extended by com.google.javascript.jscomp.Scope.Arguments
+
+
+
All Implemented Interfaces:
StaticReference<JSType>, StaticSlot<JSType>
+
+
+
Enclosing class:
Scope
+
+
+
+
public static class Scope.Arguments
extends Scope.Var
+ + +

+A special subclass of Var used to distinguish "arguments" in the current + scope. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Method Summary
+ booleanequals(Object other) + +
+           
+ inthashCode() + +
+           
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.Scope.Var
getDeclaration, getInitialValue, getInputName, getJSDocInfo, getName, getNameNode, getNode, getParentNode, getSourceFile, getSymbol, getType, isBleedingFunction, isConst, isDefine, isGlobal, isLocal, isNoShadow, isTypeInferred, toString
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+equals

+
+public boolean equals(Object other)
+
+
+
Overrides:
equals in class Scope.Var
+
+
+
+
+
+
+ +

+hashCode

+
+public int hashCode()
+
+
+
Overrides:
hashCode in class Scope.Var
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Scope.Var.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Scope.Var.html new file mode 100644 index 0000000..416502a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Scope.Var.html @@ -0,0 +1,728 @@ + + + + + +Scope.Var (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class Scope.Var

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.Scope.Var
+
+
+
All Implemented Interfaces:
StaticReference<JSType>, StaticSlot<JSType>
+
+
+
Direct Known Subclasses:
Scope.Arguments
+
+
+
Enclosing class:
Scope
+
+
+
+
public static class Scope.Var
extends Object
implements StaticSlot<JSType>, StaticReference<JSType>
+ + +

+Stores info about a variable +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleanequals(Object other) + +
+           
+ Scope.VargetDeclaration() + +
+          Gets the declaration of this symbol.
+ NodegetInitialValue() + +
+           
+ StringgetInputName() + +
+           
+ JSDocInfogetJSDocInfo() + +
+          Gets the JSDocInfo for the variable.
+ StringgetName() + +
+          Gets the name of the variable.
+ NodegetNameNode() + +
+          Returns the name node that produced this variable.
+ NodegetNode() + +
+          Gets the node for the name of the variable.
+ NodegetParentNode() + +
+          Gets the parent of the name node.
+ StaticSourceFilegetSourceFile() + +
+          The source file where the reference lives.
+ Scope.VargetSymbol() + +
+          The variable that this reference points to.
+ JSTypegetType() + +
+          Gets this variable's type.
+ inthashCode() + +
+           
+ booleanisBleedingFunction() + +
+          Whether this is a bleeding function (an anonymous named function + that bleeds into the inner scope.
+ booleanisConst() + +
+          Returns true if the variable is declared as a constant, + based on the value reported by NodeUtil.
+ booleanisDefine() + +
+          Returns true if the variable is declared as a define.
+ booleanisGlobal() + +
+          Returns whether this is a global variable.
+ booleanisLocal() + +
+          Returns whether this is a local variable.
+ booleanisNoShadow() + +
+           
+ booleanisTypeInferred() + +
+          Returns whether this variable's type is inferred.
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getName

+
+public String getName()
+
+
Gets the name of the variable. +

+

+
Specified by:
getName in interface StaticSlot<JSType>
+
+
+
+
+
+
+ +

+getNode

+
+public Node getNode()
+
+
Gets the node for the name of the variable. +

+

+
Specified by:
getNode in interface StaticReference<JSType>
+
+
+
+
+
+
+ +

+getSourceFile

+
+public StaticSourceFile getSourceFile()
+
+
Description copied from interface: StaticReference
+
The source file where the reference lives. +

+

+
Specified by:
getSourceFile in interface StaticReference<JSType>
+
+
+
+
+
+
+ +

+getSymbol

+
+public Scope.Var getSymbol()
+
+
Description copied from interface: StaticReference
+
The variable that this reference points to. +

+

+
Specified by:
getSymbol in interface StaticReference<JSType>
+
+
+
+
+
+
+ +

+getDeclaration

+
+public Scope.Var getDeclaration()
+
+
Description copied from interface: StaticSlot
+
Gets the declaration of this symbol. May not exist. +

+

+
Specified by:
getDeclaration in interface StaticSlot<JSType>
+
+
+
+
+
+
+ +

+getParentNode

+
+public Node getParentNode()
+
+
Gets the parent of the name node. +

+

+
+
+
+
+
+
+
+ +

+isBleedingFunction

+
+public boolean isBleedingFunction()
+
+
Whether this is a bleeding function (an anonymous named function + that bleeds into the inner scope. +

+

+
+
+
+
+
+
+
+ +

+isGlobal

+
+public boolean isGlobal()
+
+
Returns whether this is a global variable. +

+

+
+
+
+
+
+
+
+ +

+isLocal

+
+public boolean isLocal()
+
+
Returns whether this is a local variable. +

+

+
+
+
+
+
+
+
+ +

+isConst

+
+public boolean isConst()
+
+
Returns true if the variable is declared as a constant, + based on the value reported by NodeUtil. +

+

+
+
+
+
+
+
+
+ +

+isDefine

+
+public boolean isDefine()
+
+
Returns true if the variable is declared as a define. + A variable is a define if it is annotaed by @define. +

+

+
+
+
+
+
+
+
+ +

+getInitialValue

+
+public Node getInitialValue()
+
+
+
+
+
+
+
+
+
+ +

+getType

+
+public JSType getType()
+
+
Gets this variable's type. To know whether this type has been inferred, + see #isTypeInferred(). +

+

+
Specified by:
getType in interface StaticSlot<JSType>
+
+
+ +
Returns:
The type or null if no type is declared for it.
+
+
+
+ +

+getNameNode

+
+public Node getNameNode()
+
+
Returns the name node that produced this variable. +

+

+
+
+
+
+
+
+
+ +

+getJSDocInfo

+
+public JSDocInfo getJSDocInfo()
+
+
Gets the JSDocInfo for the variable. +

+

+
Specified by:
getJSDocInfo in interface StaticSlot<JSType>
+
+
+
+
+
+
+ +

+isTypeInferred

+
+public boolean isTypeInferred()
+
+
Returns whether this variable's type is inferred. To get the variable's + type, see getType(). +

+

+
Specified by:
isTypeInferred in interface StaticSlot<JSType>
+
+
+
+
+
+
+ +

+getInputName

+
+public String getInputName()
+
+
+
+
+
+
+
+
+
+ +

+isNoShadow

+
+public boolean isNoShadow()
+
+
+
+
+
+
+
+
+
+ +

+equals

+
+public boolean equals(Object other)
+
+
+
Overrides:
equals in class Object
+
+
+
+
+
+
+ +

+hashCode

+
+public int hashCode()
+
+
+
Overrides:
hashCode in class Object
+
+
+
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Scope.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Scope.html new file mode 100644 index 0000000..c47ad5f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/Scope.html @@ -0,0 +1,675 @@ + + + + + +Scope (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class Scope

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.Scope
+
+
+
All Implemented Interfaces:
StaticScope<JSType>, StaticSymbolTable<Scope.Var,Scope.Var>
+
+
+
+
public class Scope
extends Object
implements StaticScope<JSType>, StaticSymbolTable<Scope.Var,Scope.Var>
+ + +

+Scope contains information about a variable scope in javascript. + Scopes can be nested, a scope points back to its parent scope. + A Scope contains information about variables defined in that scope. +

+ A Scope is also used as a lattice element for flow-sensitive type inference. + As a lattice element, a Scope is viewed as a map from names to types. A name + not in the map is considered to have the bottom type. The join of two maps m1 + and m2 is the map of the union of names with JSType.getLeastSupertype(com.google.javascript.rhino.jstype.JSType) + to meet the m1 type and m2 type. +

+ +

+

+
See Also:
NodeTraversal, +DataFlowAnalysis
+
+ +

+ + + + + + + + + + + + + + + +
+Nested Class Summary
+static classScope.Arguments + +
+          A special subclass of Var used to distinguish "arguments" in the current + scope.
+static classScope.Var + +
+          Stores info about a variable
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ Iterable<Scope.Var>getAllSymbols() + +
+          Returns all variables in this symbol table.
+ Scope.VargetArgumentsVar() + +
+          Get a unique VAR object to represents "arguments" within this scope
+ Iterator<Scope.Var>getDeclarativelyUnboundVarsWithoutTypes() + +
+          Gets all variables declared with "var" but without declared types attached.
+ StaticSlot<JSType>getOwnSlot(String name) + +
+          Like getSlot but does not recurse into parent scopes.
+ ScopegetParent() + +
+           
+ StaticScope<JSType>getParentScope() + +
+          Returns the scope enclosing this one or null if none.
+ Iterable<Scope.Var>getReferences(Scope.Var var) + +
+          Returns the references that point to the given symbol.
+ NodegetRootNode() + +
+          Gets the container node of the scope.
+ StaticScope<JSType>getScope(Scope.Var var) + +
+          Returns the scope for a given symbol.
+ StaticSlot<JSType>getSlot(String name) + +
+          Returns any defined slot within this scope for this name.
+ ObjectTypegetTypeOfThis() + +
+          Gets the type of this in the current scope.
+ Scope.VargetVar(String name) + +
+          Returns the variable, may be null
+ intgetVarCount() + +
+          Returns number of variables in this scope
+ Iterator<Scope.Var>getVars() + +
+          Return an iterator over all of the variables declared in this scope.
+ booleanisDeclared(String name, + boolean recurse) + +
+          Returns true if a variable is declared.
+ booleanisGlobal() + +
+          Returns whether this is the global scope.
+ booleanisLocal() + +
+          Returns whether this is a local scope (i.e.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getRootNode

+
+public Node getRootNode()
+
+
Gets the container node of the scope. This is typically the FUNCTION + node or the global BLOCK/SCRIPT node. +

+

+
Specified by:
getRootNode in interface StaticScope<JSType>
+
+
+
+
+
+
+ +

+getParent

+
+public Scope getParent()
+
+
+
+
+
+
+
+
+
+ +

+getParentScope

+
+public StaticScope<JSType> getParentScope()
+
+
Description copied from interface: StaticScope
+
Returns the scope enclosing this one or null if none. +

+

+
Specified by:
getParentScope in interface StaticScope<JSType>
+
+
+
+
+
+
+ +

+getTypeOfThis

+
+public ObjectType getTypeOfThis()
+
+
Gets the type of this in the current scope. +

+

+
Specified by:
getTypeOfThis in interface StaticScope<JSType>
+
+
+
+
+
+
+ +

+getSlot

+
+public StaticSlot<JSType> getSlot(String name)
+
+
Description copied from interface: StaticScope
+
Returns any defined slot within this scope for this name. This call + continues searching through parent scopes if a slot with this name is not + found in the current scope. +

+

+
Specified by:
getSlot in interface StaticScope<JSType>
+
+
+
Parameters:
name - The name of the variable slot to look up. +
Returns:
The defined slot for the variable, or null if no + definition exists.
+
+
+
+ +

+getOwnSlot

+
+public StaticSlot<JSType> getOwnSlot(String name)
+
+
Description copied from interface: StaticScope
+
Like getSlot but does not recurse into parent scopes. +

+

+
Specified by:
getOwnSlot in interface StaticScope<JSType>
+
+
+
+
+
+
+ +

+getVar

+
+public Scope.Var getVar(String name)
+
+
Returns the variable, may be null +

+

+
+
+
+
+
+
+
+ +

+getArgumentsVar

+
+public Scope.Var getArgumentsVar()
+
+
Get a unique VAR object to represents "arguments" within this scope +

+

+
+
+
+
+
+
+
+ +

+isDeclared

+
+public boolean isDeclared(String name,
+                          boolean recurse)
+
+
Returns true if a variable is declared. +

+

+
+
+
+
+
+
+
+ +

+getVars

+
+public Iterator<Scope.Var> getVars()
+
+
Return an iterator over all of the variables declared in this scope. +

+

+
+
+
+
+
+
+
+ +

+getReferences

+
+public Iterable<Scope.Var> getReferences(Scope.Var var)
+
+
Description copied from interface: StaticSymbolTable
+
Returns the references that point to the given symbol. +

+

+
Specified by:
getReferences in interface StaticSymbolTable<Scope.Var,Scope.Var>
+
+
+
+
+
+
+ +

+getScope

+
+public StaticScope<JSType> getScope(Scope.Var var)
+
+
Description copied from interface: StaticSymbolTable
+
Returns the scope for a given symbol. +

+

+
Specified by:
getScope in interface StaticSymbolTable<Scope.Var,Scope.Var>
+
+
+
+
+
+
+ +

+getAllSymbols

+
+public Iterable<Scope.Var> getAllSymbols()
+
+
Description copied from interface: StaticSymbolTable
+
Returns all variables in this symbol table. +

+

+
Specified by:
getAllSymbols in interface StaticSymbolTable<Scope.Var,Scope.Var>
+
+
+
+
+
+
+ +

+getVarCount

+
+public int getVarCount()
+
+
Returns number of variables in this scope +

+

+
+
+
+
+
+
+
+ +

+isGlobal

+
+public boolean isGlobal()
+
+
Returns whether this is the global scope. +

+

+
+
+
+
+
+
+
+ +

+isLocal

+
+public boolean isLocal()
+
+
Returns whether this is a local scope (i.e. not the global scope). +

+

+
+
+
+
+
+
+
+ +

+getDeclarativelyUnboundVarsWithoutTypes

+
+public Iterator<Scope.Var> getDeclarativelyUnboundVarsWithoutTypes()
+
+
Gets all variables declared with "var" but without declared types attached. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ShowByPathWarningsGuard.ShowType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ShowByPathWarningsGuard.ShowType.html new file mode 100644 index 0000000..ca33d61 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ShowByPathWarningsGuard.ShowType.html @@ -0,0 +1,342 @@ + + + + + +ShowByPathWarningsGuard.ShowType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum ShowByPathWarningsGuard.ShowType

+
+java.lang.Object
+  extended by java.lang.Enum<ShowByPathWarningsGuard.ShowType>
+      extended by com.google.javascript.jscomp.ShowByPathWarningsGuard.ShowType
+
+
+
All Implemented Interfaces:
Serializable, Comparable<ShowByPathWarningsGuard.ShowType>
+
+
+
Enclosing class:
ShowByPathWarningsGuard
+
+
+
+
public static enum ShowByPathWarningsGuard.ShowType
extends Enum<ShowByPathWarningsGuard.ShowType>
+ + +

+Controls whether warnings should be restricted to a specified path or + suppressed within the specified path. +

+ +

+


+ +

+ + + + + + + + + + + + + +
+Enum Constant Summary
EXCLUDE + +
+           
INCLUDE + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static ShowByPathWarningsGuard.ShowTypevalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static ShowByPathWarningsGuard.ShowType[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+INCLUDE

+
+public static final ShowByPathWarningsGuard.ShowType INCLUDE
+
+
+
+
+
+ +

+EXCLUDE

+
+public static final ShowByPathWarningsGuard.ShowType EXCLUDE
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static ShowByPathWarningsGuard.ShowType[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (ShowByPathWarningsGuard.ShowType c : ShowByPathWarningsGuard.ShowType.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static ShowByPathWarningsGuard.ShowType valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ShowByPathWarningsGuard.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ShowByPathWarningsGuard.html new file mode 100644 index 0000000..fc75661 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ShowByPathWarningsGuard.html @@ -0,0 +1,395 @@ + + + + + +ShowByPathWarningsGuard (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class ShowByPathWarningsGuard

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.WarningsGuard
+      extended by com.google.javascript.jscomp.ShowByPathWarningsGuard
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class ShowByPathWarningsGuard
extends WarningsGuard
+ + +

+Control whether warnings should be restricted or suppressed for specified + paths. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classShowByPathWarningsGuard.ShowType + +
+          Controls whether warnings should be restricted to a specified path or + suppressed within the specified path.
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.jscomp.WarningsGuard
WarningsGuard.Priority
+  + + + + + + + + + + + + + + + + + + + + +
+Constructor Summary
ShowByPathWarningsGuard(String checkWarningsOnlyForPath) + +
+           
ShowByPathWarningsGuard(String[] checkWarningsOnlyForPath) + +
+           
ShowByPathWarningsGuard(String[] paths, + ShowByPathWarningsGuard.ShowType showType) + +
+           
ShowByPathWarningsGuard(String path, + ShowByPathWarningsGuard.ShowType showType) + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+protected  intgetPriority() + +
+          The priority in which warnings guards are applied.
+ CheckLevellevel(JSError error) + +
+          Returns a new check level for a given error.
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.WarningsGuard
disables, enables
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+ShowByPathWarningsGuard

+
+public ShowByPathWarningsGuard(String checkWarningsOnlyForPath)
+
+
+
+ +

+ShowByPathWarningsGuard

+
+public ShowByPathWarningsGuard(String[] checkWarningsOnlyForPath)
+
+
+
+ +

+ShowByPathWarningsGuard

+
+public ShowByPathWarningsGuard(String path,
+                               ShowByPathWarningsGuard.ShowType showType)
+
+
+
+ +

+ShowByPathWarningsGuard

+
+public ShowByPathWarningsGuard(String[] paths,
+                               ShowByPathWarningsGuard.ShowType showType)
+
+
+ + + + + + + + +
+Method Detail
+ +

+level

+
+public CheckLevel level(JSError error)
+
+
Description copied from class: WarningsGuard
+
Returns a new check level for a given error. OFF - suppress it, ERROR - + report as error. null means that this guard does not know what to do + with the error. Null is extremely helpful when you have a chain of + guards. If current guard returns null, then the next in the chain should + process it. +

+

+
Specified by:
level in class WarningsGuard
+
+
+
Parameters:
error - a reported error. +
Returns:
what level given error should have.
+
+
+
+ +

+getPriority

+
+protected int getPriority()
+
+
Description copied from class: WarningsGuard
+
The priority in which warnings guards are applied. Lower means the + guard will be applied sooner. Expressed on a scale of 1 to 100. +

+

+
Overrides:
getPriority in class WarningsGuard
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SimpleRegion.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SimpleRegion.html new file mode 100644 index 0000000..efd08d4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SimpleRegion.html @@ -0,0 +1,331 @@ + + + + + +SimpleRegion (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class SimpleRegion

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.SimpleRegion
+
+
+
All Implemented Interfaces:
Region
+
+
+
+
public class SimpleRegion
extends Object
implements Region
+ + +

+Simple region. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
SimpleRegion(int beginningLineNumber, + int endingLineNumber, + String source) + +
+           
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ intgetBeginningLineNumber() + +
+          Get the beginning line number.
+ intgetEndingLineNumber() + +
+          Get the ending line number.
+ StringgetSourceExcerpt() + +
+          Get the source region.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SimpleRegion

+
+public SimpleRegion(int beginningLineNumber,
+                    int endingLineNumber,
+                    String source)
+
+
+ + + + + + + + +
+Method Detail
+ +

+getBeginningLineNumber

+
+public int getBeginningLineNumber()
+
+
Description copied from interface: Region
+
Get the beginning line number. +

+

+
Specified by:
getBeginningLineNumber in interface Region
+
+
+
+
+
+
+ +

+getEndingLineNumber

+
+public int getEndingLineNumber()
+
+
Description copied from interface: Region
+
Get the ending line number. +

+

+
Specified by:
getEndingLineNumber in interface Region
+
+
+
+
+
+
+ +

+getSourceExcerpt

+
+public String getSourceExcerpt()
+
+
Description copied from interface: Region
+
Get the source region. +

+

+
Specified by:
getSourceExcerpt in interface Region
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceAst.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceAst.html new file mode 100644 index 0000000..205a0a6 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceAst.html @@ -0,0 +1,334 @@ + + + + + +SourceAst (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface SourceAst

+
+
All Superinterfaces:
Serializable
+
+
+
All Known Implementing Classes:
CompilerInput, JsAst, JsonMLAst, SyntheticAst
+
+
+
+
public interface SourceAst
extends Serializable
+ + +

+An interface for accessing the AST root of an input. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidclearAst() + +
+          Removes any references to root node of the AST.
+ NodegetAstRoot(AbstractCompiler compiler) + +
+          Gets the root node of the AST for the source file this represents.
+ InputIdgetInputId() + +
+           
+ SourceFilegetSourceFile() + +
+          Returns the source file the generated AST represents.
+ voidsetSourceFile(SourceFile file) + +
+          Sets the source file the generated AST represents.
+  +

+ + + + + + + + +
+Method Detail
+ +

+getAstRoot

+
+Node getAstRoot(AbstractCompiler compiler)
+
+
Gets the root node of the AST for the source file this represents. The AST + is lazily instantiated and cached. +

+

+
+
+
+
+
+
+
+ +

+clearAst

+
+void clearAst()
+
+
Removes any references to root node of the AST. If it is requested again, + another parse will be performed. This method is needed to allow the ASTs + to be garbage collected if the inputs are still around after compilation. +

+

+
+
+
+
+
+
+
+ +

+getInputId

+
+InputId getInputId()
+
+
+
+
+
+ +
Returns:
The input id associated with this AST
+
+
+
+ +

+getSourceFile

+
+SourceFile getSourceFile()
+
+
Returns the source file the generated AST represents. +

+

+
+
+
+
+
+
+
+ +

+setSourceFile

+
+void setSourceFile(SourceFile file)
+
+
Sets the source file the generated AST represents. This can be called after + deserializing if access to the source file is needed. If a different file + is provided than that with which this was created, an IllegalStateException + will be thrown. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceExcerptProvider.ExcerptFormatter.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceExcerptProvider.ExcerptFormatter.html new file mode 100644 index 0000000..2da678f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceExcerptProvider.ExcerptFormatter.html @@ -0,0 +1,250 @@ + + + + + +SourceExcerptProvider.ExcerptFormatter (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface SourceExcerptProvider.ExcerptFormatter

+
+
Enclosing interface:
SourceExcerptProvider
+
+
+
+
public static interface SourceExcerptProvider.ExcerptFormatter
+ + +

+A excerpt formatter is responsible of formatting source excerpts. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Method Summary
+ StringformatLine(String line, + int lineNumber) + +
+          Format a line excerpt.
+ StringformatRegion(Region region) + +
+          Format a region excerpt.
+  +

+ + + + + + + + +
+Method Detail
+ +

+formatLine

+
+String formatLine(String line,
+                  int lineNumber)
+
+
Format a line excerpt. +

+

+
+
+
+
+ +

+formatRegion

+
+String formatRegion(Region region)
+
+
Format a region excerpt. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceExcerptProvider.SourceExcerpt.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceExcerptProvider.SourceExcerpt.html new file mode 100644 index 0000000..63d55cb --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceExcerptProvider.SourceExcerpt.html @@ -0,0 +1,372 @@ + + + + + +SourceExcerptProvider.SourceExcerpt (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum SourceExcerptProvider.SourceExcerpt

+
+java.lang.Object
+  extended by java.lang.Enum<SourceExcerptProvider.SourceExcerpt>
+      extended by com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt
+
+
+
All Implemented Interfaces:
Serializable, Comparable<SourceExcerptProvider.SourceExcerpt>
+
+
+
Enclosing interface:
SourceExcerptProvider
+
+
+
+
public static enum SourceExcerptProvider.SourceExcerpt
extends Enum<SourceExcerptProvider.SourceExcerpt>
+ + +

+Source excerpt variety. +

+ +

+


+ +

+ + + + + + + + + + + + + +
+Enum Constant Summary
LINE + +
+          Line excerpt.
REGION + +
+          Region excerpt.
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+abstract  Stringget(SourceExcerptProvider source, + String sourceName, + int lineNumber, + SourceExcerptProvider.ExcerptFormatter formatter) + +
+          Get a source excerpt string based on the type of the source excerpt.
+static SourceExcerptProvider.SourceExcerptvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static SourceExcerptProvider.SourceExcerpt[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+LINE

+
+public static final SourceExcerptProvider.SourceExcerpt LINE
+
+
Line excerpt. +

+

+
+
+
+ +

+REGION

+
+public static final SourceExcerptProvider.SourceExcerpt REGION
+
+
Region excerpt. +

+

+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static SourceExcerptProvider.SourceExcerpt[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (SourceExcerptProvider.SourceExcerpt c : SourceExcerptProvider.SourceExcerpt.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static SourceExcerptProvider.SourceExcerpt valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+
+ +

+get

+
+public abstract String get(SourceExcerptProvider source,
+                           String sourceName,
+                           int lineNumber,
+                           SourceExcerptProvider.ExcerptFormatter formatter)
+
+
Get a source excerpt string based on the type of the source excerpt. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceExcerptProvider.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceExcerptProvider.html new file mode 100644 index 0000000..6a409e2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceExcerptProvider.html @@ -0,0 +1,285 @@ + + + + + +SourceExcerptProvider (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface SourceExcerptProvider

+
+
All Known Implementing Classes:
AbstractCompiler, Compiler, SimpleSourceExcerptProvider
+
+
+
+
public interface SourceExcerptProvider
+ + +

+A source excerpt provider is responsible for building source code excerpt + of specific locations, such as a specific line or a region around a + given line number. +

+ +

+


+ +

+ + + + + + + + + + + + + + + +
+Nested Class Summary
+static interfaceSourceExcerptProvider.ExcerptFormatter + +
+          A excerpt formatter is responsible of formatting source excerpts.
+static classSourceExcerptProvider.SourceExcerpt + +
+          Source excerpt variety.
+  + + + + + + + + + + + + + + + +
+Method Summary
+ StringgetSourceLine(String sourceName, + int lineNumber) + +
+          Get the line indicated by the line number.
+ RegiongetSourceRegion(String sourceName, + int lineNumber) + +
+          Get a region around the indicated line number.
+  +

+ + + + + + + + +
+Method Detail
+ +

+getSourceLine

+
+String getSourceLine(String sourceName,
+                     int lineNumber)
+
+
Get the line indicated by the line number. This call will return only the + specific line. +

+

+
Parameters:
lineNumber - the line number, 1 being the first line of the file +
Returns:
the line indicated, or null if it does not exist
+
+
+
+ +

+getSourceRegion

+
+Region getSourceRegion(String sourceName,
+                       int lineNumber)
+
+
Get a region around the indicated line number. The exact definition of a + region is implementation specific, but it must contain the line indicated + by the line number. A region must not start or end by a carriage return. +

+

+
Parameters:
lineNumber - the line number, 1 being the first line of the file +
Returns:
the region around the line number indicated, or null + if it does not exist
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceFile.Builder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceFile.Builder.html new file mode 100644 index 0000000..355dbab --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceFile.Builder.html @@ -0,0 +1,425 @@ + + + + + +SourceFile.Builder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class SourceFile.Builder

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.SourceFile.Builder
+
+
+
Enclosing class:
SourceFile
+
+
+
+
public static class SourceFile.Builder
extends Object
+ + +

+A builder interface for source files. + + Allows users to customize the Charset, and the original path of + the source file (if it differs from the path on disk). +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
SourceFile.Builder() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ SourceFilebuildFromCode(String fileName, + String code) + +
+           
+ SourceFilebuildFromFile(File file) + +
+           
+ SourceFilebuildFromFile(String fileName) + +
+           
+ SourceFilebuildFromGenerator(String fileName, + SourceFile.Generator generator) + +
+           
+ SourceFilebuildFromInputStream(String fileName, + InputStream s) + +
+           
+ SourceFilebuildFromReader(String fileName, + Reader r) + +
+           
+ SourceFile.BuilderwithCharset(Charset charset) + +
+          Set the charset to use when reading from an input stream or file.
+ SourceFile.BuilderwithOriginalPath(String originalPath) + +
+          Set the original path to use.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SourceFile.Builder

+
+public SourceFile.Builder()
+
+
+ + + + + + + + +
+Method Detail
+ +

+withCharset

+
+public SourceFile.Builder withCharset(Charset charset)
+
+
Set the charset to use when reading from an input stream or file. +

+

+
+
+
+
+ +

+withOriginalPath

+
+public SourceFile.Builder withOriginalPath(String originalPath)
+
+
Set the original path to use. +

+

+
+
+
+
+ +

+buildFromFile

+
+public SourceFile buildFromFile(String fileName)
+
+
+
+
+
+
+ +

+buildFromFile

+
+public SourceFile buildFromFile(File file)
+
+
+
+
+
+
+ +

+buildFromCode

+
+public SourceFile buildFromCode(String fileName,
+                                String code)
+
+
+
+
+
+
+ +

+buildFromInputStream

+
+public SourceFile buildFromInputStream(String fileName,
+                                       InputStream s)
+                                throws IOException
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+buildFromReader

+
+public SourceFile buildFromReader(String fileName,
+                                  Reader r)
+                           throws IOException
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+buildFromGenerator

+
+public SourceFile buildFromGenerator(String fileName,
+                                     SourceFile.Generator generator)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceFile.Generator.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceFile.Generator.html new file mode 100644 index 0000000..5d673cd --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceFile.Generator.html @@ -0,0 +1,227 @@ + + + + + +SourceFile.Generator (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Interface SourceFile.Generator

+
+
Enclosing class:
SourceFile
+
+
+
+
public static interface SourceFile.Generator
+ + +

+A JavaScript source code provider. The value should + be cached so that the source text stays consistent throughout a single + compile. +

+ +

+


+ +

+ + + + + + + + + + + + +
+Method Summary
+ StringgetCode() + +
+           
+  +

+ + + + + + + + +
+Method Detail
+ +

+getCode

+
+String getCode()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceFile.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceFile.html new file mode 100644 index 0000000..75bb821 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceFile.html @@ -0,0 +1,839 @@ + + + + + +SourceFile (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class SourceFile

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.SourceFile
+
+
+
All Implemented Interfaces:
StaticSourceFile, Serializable
+
+
+
Direct Known Subclasses:
JSSourceFile
+
+
+
+
public class SourceFile
extends Object
implements StaticSourceFile, Serializable
+ + +

+An abstract representation of a source file that provides access to + language-neutral features. The source file can be loaded from various + locations, such as from disk or from a preloaded string. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + + + + + +
+Nested Class Summary
+static classSourceFile.Builder + +
+          A builder interface for source files.
+static interfaceSourceFile.Generator + +
+          A JavaScript source code provider.
+  + + + + + + + + + + +
+Constructor Summary
SourceFile(String fileName) + +
+          Construct a new abstract source file.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static SourceFile.Builderbuilder() + +
+          Create a new builder for source files.
+ voidclearCachedSource() + +
+           
+static SourceFilefromCode(String fileName, + String code) + +
+           
+static SourceFilefromCode(String fileName, + String originalPath, + String code) + +
+           
+static SourceFilefromFile(File file) + +
+           
+static SourceFilefromFile(File file, + Charset c) + +
+           
+static SourceFilefromFile(String fileName) + +
+           
+static SourceFilefromFile(String fileName, + Charset c) + +
+           
+static SourceFilefromGenerator(String fileName, + SourceFile.Generator generator) + +
+           
+static SourceFilefromInputStream(String fileName, + InputStream s) + +
+           
+static SourceFilefromInputStream(String fileName, + String originalPath, + InputStream s) + +
+           
+static SourceFilefromReader(String fileName, + Reader r) + +
+           
+ StringgetCode() + +
+          Gets all the code in this source file.
+ ReadergetCodeReader() + +
+          Gets a reader for the code in this source file.
+ StringgetLine(int lineNumber) + +
+          Gets the source line for the indicated line number.
+ intgetLineOffset(int lineno) + +
+          Returns the offset of the given line number relative to the file start.
+ StringgetName() + +
+          Returns a unique name for the source file.
+ StringgetOriginalPath() + +
+           
+ RegiongetRegion(int lineNumber) + +
+          Get a region around the indicated line number.
+ booleanisExtern() + +
+          Returns whether this is an extern.
+ voidsetOriginalPath(String originalPath) + +
+           
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SourceFile

+
+public SourceFile(String fileName)
+
+
Construct a new abstract source file. +

+

+
Parameters:
fileName - The file name of the source file. It does not necessarily + need to correspond to a real path. But it should be unique. Will + appear in warning messages emitted by the compiler.
+
+ + + + + + + + +
+Method Detail
+ +

+getLineOffset

+
+public int getLineOffset(int lineno)
+
+
Description copied from interface: StaticSourceFile
+
Returns the offset of the given line number relative to the file start. + Line number should be 1-based. + + If the source file doesn't have line information, it should return + Integer.MIN_VALUE. The negative offsets will make it more obvious + what happened. +

+

+
Specified by:
getLineOffset in interface StaticSourceFile
+
+
+
Parameters:
lineno - the line of the input to get the absolute offset of. +
Returns:
the absolute offset of the start of the provided line.
+
+
+
+ +

+getCode

+
+public String getCode()
+               throws IOException
+
+
Gets all the code in this source file. +

+

+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+getCodeReader

+
+public Reader getCodeReader()
+                     throws IOException
+
+
Gets a reader for the code in this source file. +

+

+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+getOriginalPath

+
+public String getOriginalPath()
+
+
+
+
+
+
+
+
+
+ +

+setOriginalPath

+
+public void setOriginalPath(String originalPath)
+
+
+
+
+
+
+
+
+
+ +

+clearCachedSource

+
+public void clearCachedSource()
+
+
+
+
+
+
+
+
+
+ +

+getName

+
+public String getName()
+
+
Returns a unique name for the source file. +

+

+
Specified by:
getName in interface StaticSourceFile
+
+
+
+
+
+
+ +

+isExtern

+
+public boolean isExtern()
+
+
Returns whether this is an extern. +

+

+
Specified by:
isExtern in interface StaticSourceFile
+
+
+
+
+
+
+ +

+getLine

+
+public String getLine(int lineNumber)
+
+
Gets the source line for the indicated line number. +

+

+
+
+
+
Parameters:
lineNumber - the line number, 1 being the first line of the file. +
Returns:
The line indicated. Does not include the newline at the end + of the file. Returns null if it does not exist, + or if there was an IO exception.
+
+
+
+ +

+getRegion

+
+public Region getRegion(int lineNumber)
+
+
Get a region around the indicated line number. The exact definition of a + region is implementation specific, but it must contain the line indicated + by the line number. A region must not start or end by a carriage return. +

+

+
+
+
+
Parameters:
lineNumber - the line number, 1 being the first line of the file. +
Returns:
The line indicated. Returns null if it does not exist, + or if there was an IO exception.
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+
+ +

+fromFile

+
+public static SourceFile fromFile(String fileName,
+                                  Charset c)
+
+
+
+
+
+
+
+
+
+ +

+fromFile

+
+public static SourceFile fromFile(String fileName)
+
+
+
+
+
+
+
+
+
+ +

+fromFile

+
+public static SourceFile fromFile(File file,
+                                  Charset c)
+
+
+
+
+
+
+
+
+
+ +

+fromFile

+
+public static SourceFile fromFile(File file)
+
+
+
+
+
+
+
+
+
+ +

+fromCode

+
+public static SourceFile fromCode(String fileName,
+                                  String code)
+
+
+
+
+
+
+
+
+
+ +

+fromCode

+
+public static SourceFile fromCode(String fileName,
+                                  String originalPath,
+                                  String code)
+
+
+
+
+
+
+
+
+
+ +

+fromInputStream

+
+public static SourceFile fromInputStream(String fileName,
+                                         InputStream s)
+                                  throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+fromInputStream

+
+public static SourceFile fromInputStream(String fileName,
+                                         String originalPath,
+                                         InputStream s)
+                                  throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+fromReader

+
+public static SourceFile fromReader(String fileName,
+                                    Reader r)
+                             throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+fromGenerator

+
+public static SourceFile fromGenerator(String fileName,
+                                       SourceFile.Generator generator)
+
+
+
+
+
+
+
+
+
+ +

+builder

+
+public static SourceFile.Builder builder()
+
+
Create a new builder for source files. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceMap.DetailLevel.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceMap.DetailLevel.html new file mode 100644 index 0000000..5fb93ff --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceMap.DetailLevel.html @@ -0,0 +1,356 @@ + + + + + +SourceMap.DetailLevel (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum SourceMap.DetailLevel

+
+java.lang.Object
+  extended by java.lang.Enum<SourceMap.DetailLevel>
+      extended by com.google.javascript.jscomp.SourceMap.DetailLevel
+
+
+
All Implemented Interfaces:
com.google.common.base.Predicate<Node>, Serializable, Comparable<SourceMap.DetailLevel>
+
+
+
Enclosing class:
SourceMap
+
+
+
+
public static enum SourceMap.DetailLevel
extends Enum<SourceMap.DetailLevel>
implements com.google.common.base.Predicate<Node>
+ + +

+Source maps can be very large different levels of detail can be specified. +

+ +

+


+ +

+ + + + + + + + + + + + + +
+Enum Constant Summary
ALL + +
+           
SYMBOLS + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static SourceMap.DetailLevelvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static SourceMap.DetailLevel[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.common.base.Predicate
apply, equals
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+ALL

+
+public static final SourceMap.DetailLevel ALL
+
+
+
+
+
+ +

+SYMBOLS

+
+public static final SourceMap.DetailLevel SYMBOLS
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static SourceMap.DetailLevel[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (SourceMap.DetailLevel c : SourceMap.DetailLevel.values())
+    System.out.println(c);
+
+

+

+
+
+
+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static SourceMap.DetailLevel valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
+
+
+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceMap.Format.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceMap.Format.html new file mode 100644 index 0000000..4e613cf --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceMap.Format.html @@ -0,0 +1,369 @@ + + + + + +SourceMap.Format (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum SourceMap.Format

+
+java.lang.Object
+  extended by java.lang.Enum<SourceMap.Format>
+      extended by com.google.javascript.jscomp.SourceMap.Format
+
+
+
All Implemented Interfaces:
Serializable, Comparable<SourceMap.Format>
+
+
+
Enclosing class:
SourceMap
+
+
+
+
public static enum SourceMap.Format
extends Enum<SourceMap.Format>
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + +
+Enum Constant Summary
DEFAULT + +
+           
V1 + +
+           
V2 + +
+           
V3 + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static SourceMap.FormatvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static SourceMap.Format[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+V1

+
+public static final SourceMap.Format V1
+
+
+
+
+
+ +

+DEFAULT

+
+public static final SourceMap.Format DEFAULT
+
+
+
+
+
+ +

+V2

+
+public static final SourceMap.Format V2
+
+
+
+
+
+ +

+V3

+
+public static final SourceMap.Format V3
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static SourceMap.Format[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (SourceMap.Format c : SourceMap.Format.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static SourceMap.Format valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceMap.LocationMapping.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceMap.LocationMapping.html new file mode 100644 index 0000000..dad9d0b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceMap.LocationMapping.html @@ -0,0 +1,241 @@ + + + + + +SourceMap.LocationMapping (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class SourceMap.LocationMapping

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.SourceMap.LocationMapping
+
+
+
Enclosing class:
SourceMap
+
+
+
+
public static class SourceMap.LocationMapping
extends Object
+ + +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
SourceMap.LocationMapping(String prefix, + String replacement) + +
+           
+  + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SourceMap.LocationMapping

+
+public SourceMap.LocationMapping(String prefix,
+                                 String replacement)
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceMap.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceMap.html new file mode 100644 index 0000000..250e841 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SourceMap.html @@ -0,0 +1,398 @@ + + + + + +SourceMap (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class SourceMap

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.SourceMap
+
+
+
+
public class SourceMap
extends Object
+ + +

+Collects information mapping the generated (compiled) source back to + its original source for debugging purposes. +

+ +

+

+
See Also:
CodeConsumer, +CodeGenerator, +CodePrinter
+
+ +

+ + + + + + + + + + + + + + + + + + + +
+Nested Class Summary
+static classSourceMap.DetailLevel + +
+          Source maps can be very large different levels of detail can be specified.
+static classSourceMap.Format + +
+           
+static classSourceMap.LocationMapping + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidaddMapping(Node node, + FilePosition outputStartPosition, + FilePosition outputEndPosition) + +
+           
+ voidappendTo(Appendable out, + String name) + +
+           
+ voidreset() + +
+           
+ voidsetPrefixMappings(List<SourceMap.LocationMapping> sourceMapLocationMappings) + +
+           
+ voidsetStartingPosition(int offsetLine, + int offsetIndex) + +
+           
+ voidsetWrapperPrefix(String prefix) + +
+           
+ voidvalidate(boolean validate) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+addMapping

+
+public void addMapping(Node node,
+                       FilePosition outputStartPosition,
+                       FilePosition outputEndPosition)
+
+
+
+
+
+
+ +

+appendTo

+
+public void appendTo(Appendable out,
+                     String name)
+              throws IOException
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+reset

+
+public void reset()
+
+
+
+
+
+
+ +

+setStartingPosition

+
+public void setStartingPosition(int offsetLine,
+                                int offsetIndex)
+
+
+
+
+
+
+ +

+setWrapperPrefix

+
+public void setWrapperPrefix(String prefix)
+
+
+
+
+
+
+ +

+validate

+
+public void validate(boolean validate)
+
+
+
+
+
+
+ +

+setPrefixMappings

+
+public void setPrefixMappings(List<SourceMap.LocationMapping> sourceMapLocationMappings)
+
+
+
Parameters:
sourceMapLocationMappings -
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/StatementFusion.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/StatementFusion.html new file mode 100644 index 0000000..885f498 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/StatementFusion.html @@ -0,0 +1,350 @@ + + + + + +StatementFusion (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class StatementFusion

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.StatementFusion
+
+
+
+
public class StatementFusion
extends Object
+ + +

+Tries to fuse all the statements in a block into a one statement by using + COMMAs. + + Because COMMAs has the lowest precedence, we never need to insert + extra () around. Once we have only one statement in a block, we can then + eliminate a pair of {}'s. Further more, we can also fold a single + statement IF into && or create further opportunities for all the other + goodies in PeepholeSubstituteAlternateSyntax. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
StatementFusion() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+protected  booleanareNodesEqualForInlining(Node n1, + Node n2) + +
+          Are the nodes equal for the purpose of inlining? + If type aware optimizations are on, type equality is checked.
+protected  voiderror(DiagnosticType diagnostic, + Node n) + +
+          Helper method for reporting an error to the compiler when applying a + peephole optimization.
+protected  booleanisASTNormalized() + +
+          Is the current AST normalized? (e.g.
+protected  voidreportCodeChange() + +
+          Helper method for telling the compiler that something has changed.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+StatementFusion

+
+public StatementFusion()
+
+
+ + + + + + + + +
+Method Detail
+ +

+error

+
+protected void error(DiagnosticType diagnostic,
+                     Node n)
+
+
Helper method for reporting an error to the compiler when applying a + peephole optimization. +

+

+
Parameters:
diagnostic - The error type
n - The node for which the error should be reported
+
+
+
+ +

+reportCodeChange

+
+protected void reportCodeChange()
+
+
Helper method for telling the compiler that something has changed. + Subclasses must call these if they have changed the AST. +

+

+
+
+
+
+ +

+areNodesEqualForInlining

+
+protected boolean areNodesEqualForInlining(Node n1,
+                                           Node n2)
+
+
Are the nodes equal for the purpose of inlining? + If type aware optimizations are on, type equality is checked. +

+

+
+
+
+
+ +

+isASTNormalized

+
+protected boolean isASTNormalized()
+
+
Is the current AST normalized? (e.g. has the Normalize pass been run + and has the Denormalize pass not yet been run?) +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/StrictWarningsGuard.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/StrictWarningsGuard.html new file mode 100644 index 0000000..959f63a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/StrictWarningsGuard.html @@ -0,0 +1,339 @@ + + + + + +StrictWarningsGuard (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class StrictWarningsGuard

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.WarningsGuard
+      extended by com.google.javascript.jscomp.StrictWarningsGuard
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class StrictWarningsGuard
extends WarningsGuard
+ + +

+All warnings should be reported as errors. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.jscomp.WarningsGuard
WarningsGuard.Priority
+  + + + + + + + + + + + +
+Constructor Summary
StrictWarningsGuard() + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+protected  intgetPriority() + +
+          The priority in which warnings guards are applied.
+ CheckLevellevel(JSError error) + +
+          Returns a new check level for a given error.
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.WarningsGuard
disables, enables
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+StrictWarningsGuard

+
+public StrictWarningsGuard()
+
+
+ + + + + + + + +
+Method Detail
+ +

+level

+
+public CheckLevel level(JSError error)
+
+
Description copied from class: WarningsGuard
+
Returns a new check level for a given error. OFF - suppress it, ERROR - + report as error. null means that this guard does not know what to do + with the error. Null is extremely helpful when you have a chain of + guards. If current guard returns null, then the next in the chain should + process it. +

+

+
Specified by:
level in class WarningsGuard
+
+
+
Parameters:
error - a reported error. +
Returns:
what level given error should have.
+
+
+
+ +

+getPriority

+
+protected int getPriority()
+
+
Description copied from class: WarningsGuard
+
The priority in which warnings guards are applied. Lower means the + guard will be applied sooner. Expressed on a scale of 1 to 100. +

+

+
Overrides:
getPriority in class WarningsGuard
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SymbolTable.Reference.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SymbolTable.Reference.html new file mode 100644 index 0000000..9634cdd --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SymbolTable.Reference.html @@ -0,0 +1,219 @@ + + + + + +SymbolTable.Reference (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class SymbolTable.Reference

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.SimpleReference<SymbolTable.Symbol>
+      extended by com.google.javascript.jscomp.SymbolTable.Reference
+
+
+
All Implemented Interfaces:
StaticReference<JSType>
+
+
+
Enclosing class:
SymbolTable
+
+
+
+
public static final class SymbolTable.Reference
extends SimpleReference<SymbolTable.Symbol>
+ + +

+


+ +

+ + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.SimpleReference
getNode, getSourceFile, getSymbol, toString
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ +


+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SymbolTable.Symbol.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SymbolTable.Symbol.html new file mode 100644 index 0000000..a42c6e8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SymbolTable.Symbol.html @@ -0,0 +1,498 @@ + + + + + +SymbolTable.Symbol (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class SymbolTable.Symbol

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.SimpleSlot
+      extended by com.google.javascript.jscomp.SymbolTable.Symbol
+
+
+
All Implemented Interfaces:
StaticSlot<JSType>, Serializable
+
+
+
Enclosing class:
SymbolTable
+
+
+
+
public static final class SymbolTable.Symbol
extends SimpleSlot
+ + +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ SymbolTable.ReferencedefineReferenceAt(Node n) + +
+           
+ SymbolTable.ReferencegetDeclaration() + +
+          Gets the declaration of this symbol.
+ NodegetDeclarationNode() + +
+           
+ FunctionTypegetFunctionType() + +
+           
+ JSDocInfogetJSDocInfo() + +
+          Gets the JSDoc for this slot.
+ SymbolTable.SymbolScopegetPropertyScope() + +
+           
+ StringgetSourceFileName() + +
+           
+ booleaninExterns() + +
+           
+ booleaninGlobalScope() + +
+           
+ booleanisDocOnlyParameter() + +
+          Whether this is a variable that's only in JSDoc.
+ booleanisLexicalVariable() + +
+          Whether this is a variable in a lexical scope.
+ booleanisProperty() + +
+          Whether this is a property of another variable.
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.SimpleSlot
getName, getType, isTypeInferred
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getDeclaration

+
+public SymbolTable.Reference getDeclaration()
+
+
Description copied from interface: StaticSlot
+
Gets the declaration of this symbol. May not exist. +

+

+
Specified by:
getDeclaration in interface StaticSlot<JSType>
Overrides:
getDeclaration in class SimpleSlot
+
+
+
+
+
+
+ +

+getFunctionType

+
+public FunctionType getFunctionType()
+
+
+
+
+
+
+ +

+defineReferenceAt

+
+public SymbolTable.Reference defineReferenceAt(Node n)
+
+
+
+
+
+
+ +

+inGlobalScope

+
+public boolean inGlobalScope()
+
+
+
+
+
+
+ +

+inExterns

+
+public boolean inExterns()
+
+
+
+
+
+
+ +

+getDeclarationNode

+
+public Node getDeclarationNode()
+
+
+
+
+
+
+ +

+getSourceFileName

+
+public String getSourceFileName()
+
+
+
+
+
+
+ +

+getPropertyScope

+
+public SymbolTable.SymbolScope getPropertyScope()
+
+
+
+
+
+
+ +

+getJSDocInfo

+
+public JSDocInfo getJSDocInfo()
+
+
Description copied from interface: StaticSlot
+
Gets the JSDoc for this slot. +

+

+
Specified by:
getJSDocInfo in interface StaticSlot<JSType>
Overrides:
getJSDocInfo in class SimpleSlot
+
+
+
+
+
+
+ +

+isProperty

+
+public boolean isProperty()
+
+
Whether this is a property of another variable. +

+

+
+
+
+
+ +

+isLexicalVariable

+
+public boolean isLexicalVariable()
+
+
Whether this is a variable in a lexical scope. +

+

+
+
+
+
+ +

+isDocOnlyParameter

+
+public boolean isDocOnlyParameter()
+
+
Whether this is a variable that's only in JSDoc. +

+

+
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SymbolTable.SymbolScope.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SymbolTable.SymbolScope.html new file mode 100644 index 0000000..9896b4f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SymbolTable.SymbolScope.html @@ -0,0 +1,532 @@ + + + + + +SymbolTable.SymbolScope (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class SymbolTable.SymbolScope

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.SymbolTable.SymbolScope
+
+
+
All Implemented Interfaces:
StaticScope<JSType>
+
+
+
Enclosing class:
SymbolTable
+
+
+
+
public static final class SymbolTable.SymbolScope
extends Object
implements StaticScope<JSType>
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ intgetIndexOfSymbol(SymbolTable.Symbol sym) + +
+          Gets a unique index for the symbol in this scope.
+ SymbolTable.SymbolgetOwnSlot(String name) + +
+          Like getSlot but does not recurse into parent scopes.
+ SymbolTable.SymbolScopegetParentScope() + +
+          Returns the scope enclosing this one or null if none.
+ SymbolTable.SymbolgetQualifiedSlot(String name) + +
+          Get the slot for a fully-qualified name (e.g., "a.b.c") by trying + to find property scopes at each part of the path.
+ NodegetRootNode() + +
+          Returns the root node associated with this scope.
+ intgetScopeDepth() + +
+           
+ SymbolTable.SymbolgetSlot(String name) + +
+          Returns any defined slot within this scope for this name.
+ JSTypegetTypeOfThis() + +
+          Returns the expected type of this in the current scope.
+ booleanisDocScope() + +
+          Returns whether this is a doc scope.
+ booleanisGlobalScope() + +
+           
+ booleanisLexicalScope() + +
+           
+ booleanisPropertyScope() + +
+           
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getIndexOfSymbol

+
+public int getIndexOfSymbol(SymbolTable.Symbol sym)
+
+
Gets a unique index for the symbol in this scope. +

+

+
+
+
+
+
+
+
+ +

+getRootNode

+
+public Node getRootNode()
+
+
Description copied from interface: StaticScope
+
Returns the root node associated with this scope. May be null. +

+

+
Specified by:
getRootNode in interface StaticScope<JSType>
+
+
+
+
+
+
+ +

+getParentScope

+
+public SymbolTable.SymbolScope getParentScope()
+
+
Description copied from interface: StaticScope
+
Returns the scope enclosing this one or null if none. +

+

+
Specified by:
getParentScope in interface StaticScope<JSType>
+
+
+
+
+
+
+ +

+getQualifiedSlot

+
+public SymbolTable.Symbol getQualifiedSlot(String name)
+
+
Get the slot for a fully-qualified name (e.g., "a.b.c") by trying + to find property scopes at each part of the path. +

+

+
+
+
+
+
+
+
+ +

+getSlot

+
+public SymbolTable.Symbol getSlot(String name)
+
+
Description copied from interface: StaticScope
+
Returns any defined slot within this scope for this name. This call + continues searching through parent scopes if a slot with this name is not + found in the current scope. +

+

+
Specified by:
getSlot in interface StaticScope<JSType>
+
+
+
Parameters:
name - The name of the variable slot to look up. +
Returns:
The defined slot for the variable, or null if no + definition exists.
+
+
+
+ +

+getOwnSlot

+
+public SymbolTable.Symbol getOwnSlot(String name)
+
+
Description copied from interface: StaticScope
+
Like getSlot but does not recurse into parent scopes. +

+

+
Specified by:
getOwnSlot in interface StaticScope<JSType>
+
+
+
+
+
+
+ +

+getTypeOfThis

+
+public JSType getTypeOfThis()
+
+
Description copied from interface: StaticScope
+
Returns the expected type of this in the current scope. +

+

+
Specified by:
getTypeOfThis in interface StaticScope<JSType>
+
+
+
+
+
+
+ +

+isGlobalScope

+
+public boolean isGlobalScope()
+
+
+
+
+
+
+
+
+
+ +

+isDocScope

+
+public boolean isDocScope()
+
+
Returns whether this is a doc scope. A doc scope is a table for symbols + that are documented solely within a JSDoc comment. +

+

+
+
+
+
+
+
+
+ +

+isPropertyScope

+
+public boolean isPropertyScope()
+
+
+
+
+
+
+
+
+
+ +

+isLexicalScope

+
+public boolean isLexicalScope()
+
+
+
+
+
+
+
+
+
+ +

+getScopeDepth

+
+public int getScopeDepth()
+
+
+
+
+
+
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SymbolTable.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SymbolTable.html new file mode 100644 index 0000000..186af11 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SymbolTable.html @@ -0,0 +1,867 @@ + + + + + +SymbolTable (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class SymbolTable

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.SymbolTable
+
+
+
All Implemented Interfaces:
StaticSymbolTable<SymbolTable.Symbol,SymbolTable.Reference>
+
+
+
+
public final class SymbolTable
extends Object
implements StaticSymbolTable<SymbolTable.Symbol,SymbolTable.Reference>
+ + +

+A symbol table for people that want to use Closure Compiler as an indexer. + + Contains an index of all the symbols in the code within a compilation + job. The API is designed for people who want to visit all the symbols, rather + than people who want to lookup a specific symbol by a certain key. + + We can use this to combine different types of symbol tables. For example, + one class might have a StaticSymbolTable of all variable references, + and another class might have a StaticSymbolTable of all type names + in JSDoc comments. This class allows you to combine them into a unified + index. + + Most passes build their own "partial" symbol table that implements the same + interface (StaticSymbolTable, StaticSlot, and friends). Individual compiler + passes usually need more or less metadata about the certainty of symbol + information. Building a complete symbol table with all the necessary metadata + for all passes would be too slow. However, as long as these "partial" symbol + tables implement the proper interfaces, we should be able to add them to this + symbol table to make it more complete. + + If clients want fast lookup, they should build their own wrapper around + this symbol table that indexes symbols or references by the desired lookup + key. + + By design, when this symbol table creates symbols for types, it tries + to mimic the symbol table you would get in an OO language. For example, + the "type Foo" and "the constructor that creates objects of type Foo" + are the same symbol. The types of "Foo.prototype" and "new Foo()" also + have the same symbol. Although JSCompiler internally treats these as + distinct symbols, we assume that most clients will not care about + the distinction. +

+ +

+

+
See Also:
For more information on how to write plugins for this + symbol table.
+
+ +

+ + + + + + + + + + + + + + + + + + + +
+Nested Class Summary
+static classSymbolTable.Reference + +
+           
+static classSymbolTable.Symbol + +
+           
+static classSymbolTable.SymbolScope + +
+           
+ + + + + + + + + + +
+Field Summary
+static StringGLOBAL_THIS + +
+          The name we use for the JavaScript built-in Global object.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidaddAnonymousFunctions() + +
+          Finds anonymous functions in local scopes, and gives them names + and symbols.
+ SymbolTable.SymboldeclareInferredSymbol(SymbolTable.SymbolScope scope, + String name, + Node declNode) + +
+          Declare a symbol after the main symbol table was constructed.
+ Collection<JSDocInfo>getAllJSDocInfo() + +
+           
+ Collection<SymbolTable.SymbolScope>getAllScopes() + +
+          Gets all the scopes in this symbol table.
+ Iterable<SymbolTable.Symbol>getAllSymbols() + +
+          Returns all variables in this symbol table.
+ List<SymbolTable.Symbol>getAllSymbolsForType(JSType type) + +
+          Gets all symbols associated with the given type.
+ Iterable<SymbolTable.Symbol>getAllSymbolsForTypeOf(SymbolTable.Symbol sym) + +
+          Get all symbols associated with the type of the given symbol.
+ List<SymbolTable.Symbol>getAllSymbolsSorted() + +
+          Get the symbols in their natural ordering.
+ SymbolTable.SymbolScopegetEnclosingScope(Node n) + +
+          Gets the scope that contains the given node.
+ SymbolTable.SymbolScopegetGlobalScope() + +
+          Returns the global scope.
+ com.google.common.collect.Ordering<SymbolTable.Symbol>getNaturalSymbolOrdering() + +
+          Gets the 'natural' ordering of symbols.
+ SymbolTable.SymbolgetParameterInFunction(SymbolTable.Symbol sym, + String paramName) + +
+          If sym is a function, try to find a Symbol for + a parameter with the given name.
+ List<SymbolTable.Reference>getReferenceList(SymbolTable.Symbol symbol) + +
+           
+ Iterable<SymbolTable.Reference>getReferences(SymbolTable.Symbol symbol) + +
+          Returns the references that point to the given symbol.
+ SymbolTable.SymbolScopegetScope(SymbolTable.Symbol slot) + +
+          Returns the scope for a given symbol.
+ SymbolTable.SymbolgetSymbolDeclaredBy(EnumType enumType) + +
+          Gets the symbol for the given enum.
+ SymbolTable.SymbolgetSymbolDeclaredBy(FunctionType fn) + +
+          Gets the symbol for the given constuctor or interface.
+ SymbolTable.SymbolgetSymbolForInstancesOf(FunctionType fn) + +
+          Gets the symbol for the prototype of the given constructor or interface.
+ SymbolTable.SymbolgetSymbolForInstancesOf(SymbolTable.Symbol sym) + +
+          Gets the symbol for the prototype if this is the symbol for a constructor + or interface.
+ SymbolTable.SymbolgetSymbolForScope(SymbolTable.SymbolScope scope) + +
+          All local scopes are associated with a function, and some functions + are associated with a symbol.
+ StringtoDebugString() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+GLOBAL_THIS

+
+public static final String GLOBAL_THIS
+
+
The name we use for the JavaScript built-in Global object. It's + anonymous in JavaScript, so we have to give it an invalid identifier + to avoid conflicts with user-defined property names. +

+

+
See Also:
Constant Field Values
+
+ + + + + + + + +
+Method Detail
+ +

+getReferences

+
+public Iterable<SymbolTable.Reference> getReferences(SymbolTable.Symbol symbol)
+
+
Description copied from interface: StaticSymbolTable
+
Returns the references that point to the given symbol. +

+

+
Specified by:
getReferences in interface StaticSymbolTable<SymbolTable.Symbol,SymbolTable.Reference>
+
+
+
+
+
+
+ +

+getReferenceList

+
+public List<SymbolTable.Reference> getReferenceList(SymbolTable.Symbol symbol)
+
+
+
+
+
+
+
+
+
+ +

+getAllSymbols

+
+public Iterable<SymbolTable.Symbol> getAllSymbols()
+
+
Description copied from interface: StaticSymbolTable
+
Returns all variables in this symbol table. +

+

+
Specified by:
getAllSymbols in interface StaticSymbolTable<SymbolTable.Symbol,SymbolTable.Reference>
+
+
+
+
+
+
+ +

+getAllSymbolsSorted

+
+public List<SymbolTable.Symbol> getAllSymbolsSorted()
+
+
Get the symbols in their natural ordering. + Always returns a mutable list. +

+

+
+
+
+
+
+
+
+ +

+getNaturalSymbolOrdering

+
+public com.google.common.collect.Ordering<SymbolTable.Symbol> getNaturalSymbolOrdering()
+
+
Gets the 'natural' ordering of symbols. + + Right now, we only guarantee that symbols in the global scope will come + before symbols in local scopes. After that, the order is deterministic but + undefined. +

+

+
+
+
+
+
+
+
+ +

+getScope

+
+public SymbolTable.SymbolScope getScope(SymbolTable.Symbol slot)
+
+
Description copied from interface: StaticSymbolTable
+
Returns the scope for a given symbol. +

+

+
Specified by:
getScope in interface StaticSymbolTable<SymbolTable.Symbol,SymbolTable.Reference>
+
+
+
+
+
+
+ +

+getAllJSDocInfo

+
+public Collection<JSDocInfo> getAllJSDocInfo()
+
+
+
+
+
+
+
+
+
+ +

+declareInferredSymbol

+
+public SymbolTable.Symbol declareInferredSymbol(SymbolTable.SymbolScope scope,
+                                                String name,
+                                                Node declNode)
+
+
Declare a symbol after the main symbol table was constructed. + Throws an exception if you try to declare a symbol twice. +

+

+
+
+
+
+
+
+
+ +

+getEnclosingScope

+
+public SymbolTable.SymbolScope getEnclosingScope(Node n)
+
+
Gets the scope that contains the given node. + If n is a function name, we return the scope that contains the + function, not the function itself. +

+

+
+
+
+
+
+
+
+ +

+getParameterInFunction

+
+public SymbolTable.Symbol getParameterInFunction(SymbolTable.Symbol sym,
+                                                 String paramName)
+
+
If sym is a function, try to find a Symbol for + a parameter with the given name. + + Returns null if we couldn't find one. + + Notice that this just makes a best effort, and may not be able + to find parameters for non-conventional function definitions. + For example, we would not be able to find "y" in this code: + + var x = x() ? function(y) {} : function(y) {}; + +

+

+
+
+
+
+
+
+
+ +

+getSymbolForScope

+
+public SymbolTable.Symbol getSymbolForScope(SymbolTable.SymbolScope scope)
+
+
All local scopes are associated with a function, and some functions + are associated with a symbol. Returns the symbol associated with the given + scope. +

+

+
+
+
+
+
+
+
+ +

+getAllSymbolsForTypeOf

+
+public Iterable<SymbolTable.Symbol> getAllSymbolsForTypeOf(SymbolTable.Symbol sym)
+
+
Get all symbols associated with the type of the given symbol. + + For example, given a variable x declared as + /* @type {Array|Date} / + var x = f(); + this will return the constructors for Array and Date. +

+

+
+
+
+
+
+
+
+ +

+getGlobalScope

+
+public SymbolTable.SymbolScope getGlobalScope()
+
+
Returns the global scope. +

+

+
+
+
+
+
+
+
+ +

+getSymbolDeclaredBy

+
+public SymbolTable.Symbol getSymbolDeclaredBy(FunctionType fn)
+
+
Gets the symbol for the given constuctor or interface. +

+

+
+
+
+
+
+
+
+ +

+getSymbolDeclaredBy

+
+public SymbolTable.Symbol getSymbolDeclaredBy(EnumType enumType)
+
+
Gets the symbol for the given enum. +

+

+
+
+
+
+
+
+
+ +

+getSymbolForInstancesOf

+
+public SymbolTable.Symbol getSymbolForInstancesOf(SymbolTable.Symbol sym)
+
+
Gets the symbol for the prototype if this is the symbol for a constructor + or interface. +

+

+
+
+
+
+
+
+
+ +

+getSymbolForInstancesOf

+
+public SymbolTable.Symbol getSymbolForInstancesOf(FunctionType fn)
+
+
Gets the symbol for the prototype of the given constructor or interface. +

+

+
+
+
+
+
+
+
+ +

+getAllSymbolsForType

+
+public List<SymbolTable.Symbol> getAllSymbolsForType(JSType type)
+
+
Gets all symbols associated with the given type. + For union types, this may be multiple symbols. + For instance types, this will return the constructor of + that instance. +

+

+
+
+
+
+
+
+
+ +

+toDebugString

+
+public String toDebugString()
+
+
+
+
+
+
+
+
+
+ +

+getAllScopes

+
+public Collection<SymbolTable.SymbolScope> getAllScopes()
+
+
Gets all the scopes in this symbol table. +

+

+
+
+
+
+
+
+
+ +

+addAnonymousFunctions

+
+public void addAnonymousFunctions()
+
+
Finds anonymous functions in local scopes, and gives them names + and symbols. They will show up as local variables with names + "function%0", "function%1", etc. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SyntheticAst.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SyntheticAst.html new file mode 100644 index 0000000..6459d36 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/SyntheticAst.html @@ -0,0 +1,350 @@ + + + + + +SyntheticAst (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class SyntheticAst

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.SyntheticAst
+
+
+
All Implemented Interfaces:
SourceAst, Serializable
+
+
+
+
public class SyntheticAst
extends Object
implements SourceAst
+ + +

+An AST generated totally by the compiler. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidclearAst() + +
+          Removes any references to root node of the AST.
+ NodegetAstRoot(AbstractCompiler compiler) + +
+          Gets the root node of the AST for the source file this represents.
+ InputIdgetInputId() + +
+           
+ SourceFilegetSourceFile() + +
+          Returns the source file the generated AST represents.
+ voidsetSourceFile(SourceFile file) + +
+          Sets the source file the generated AST represents.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getAstRoot

+
+public Node getAstRoot(AbstractCompiler compiler)
+
+
Description copied from interface: SourceAst
+
Gets the root node of the AST for the source file this represents. The AST + is lazily instantiated and cached. +

+

+
Specified by:
getAstRoot in interface SourceAst
+
+
+
+
+
+
+ +

+clearAst

+
+public void clearAst()
+
+
Description copied from interface: SourceAst
+
Removes any references to root node of the AST. If it is requested again, + another parse will be performed. This method is needed to allow the ASTs + to be garbage collected if the inputs are still around after compilation. +

+

+
Specified by:
clearAst in interface SourceAst
+
+
+
+
+
+
+ +

+getInputId

+
+public InputId getInputId()
+
+
+
Specified by:
getInputId in interface SourceAst
+
+
+ +
Returns:
The input id associated with this AST
+
+
+
+ +

+getSourceFile

+
+public SourceFile getSourceFile()
+
+
Description copied from interface: SourceAst
+
Returns the source file the generated AST represents. +

+

+
Specified by:
getSourceFile in interface SourceAst
+
+
+
+
+
+
+ +

+setSourceFile

+
+public void setSourceFile(SourceFile file)
+
+
Description copied from interface: SourceAst
+
Sets the source file the generated AST represents. This can be called after + deserializing if access to the source file is needed. If a different file + is provided than that with which this was created, an IllegalStateException + will be thrown. +

+

+
Specified by:
setSourceFile in interface SourceAst
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/TypeCheck.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/TypeCheck.html new file mode 100644 index 0000000..d003730 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/TypeCheck.html @@ -0,0 +1,485 @@ + + + + + +TypeCheck (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class TypeCheck

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.TypeCheck
+
+
+
All Implemented Interfaces:
CompilerPass, NodeTraversal.Callback
+
+
+
+
public class TypeCheck
extends Object
implements NodeTraversal.Callback, CompilerPass
+ + +

+

Checks the types of JS expressions against any declared type + information.

+

+ +

+


+ +

+ + + + + + + + + + + + + + + +
+Field Summary
+protected static DiagnosticTypeNOT_A_CONSTRUCTOR + +
+           
+protected static StringOVERRIDING_PROTOTYPE_WITH_NON_OBJECT + +
+           
+  + + + + + + + + + + + + + +
+Constructor Summary
TypeCheck(AbstractCompiler compiler, + com.google.javascript.jscomp.ReverseAbstractInterpreter reverseInterpreter, + JSTypeRegistry typeRegistry, + CheckLevel reportMissingOverride, + CheckLevel reportUnknownTypes) + +
+           
TypeCheck(AbstractCompiler compiler, + com.google.javascript.jscomp.ReverseAbstractInterpreter reverseInterpreter, + JSTypeRegistry typeRegistry, + Scope topScope, + com.google.javascript.jscomp.ScopeCreator scopeCreator, + CheckLevel reportMissingOverride, + CheckLevel reportUnknownTypes) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidcheck(Node node, + boolean externs) + +
+           
+ voidprocess(Node externsRoot, + Node jsRoot) + +
+          Main entry point for this phase of processing.
+ ScopeprocessForTesting(Node externsRoot, + Node jsRoot) + +
+          Main entry point of this phase for testing code.
+ booleanshouldTraverse(NodeTraversal t, + Node n, + Node parent) + +
+          Visits a node in pre order (before visiting its children) and decides + whether this node's children should be traversed.
+ voidvisit(NodeTraversal t, + Node n, + Node parent) + +
+          This is the meat of the type checking.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+OVERRIDING_PROTOTYPE_WITH_NON_OBJECT

+
+protected static final String OVERRIDING_PROTOTYPE_WITH_NON_OBJECT
+
+
+
See Also:
Constant Field Values
+
+
+ +

+NOT_A_CONSTRUCTOR

+
+protected static final DiagnosticType NOT_A_CONSTRUCTOR
+
+
+
+
+ + + + + + + + +
+Constructor Detail
+ +

+TypeCheck

+
+public TypeCheck(AbstractCompiler compiler,
+                 com.google.javascript.jscomp.ReverseAbstractInterpreter reverseInterpreter,
+                 JSTypeRegistry typeRegistry,
+                 Scope topScope,
+                 com.google.javascript.jscomp.ScopeCreator scopeCreator,
+                 CheckLevel reportMissingOverride,
+                 CheckLevel reportUnknownTypes)
+
+
+
+ +

+TypeCheck

+
+public TypeCheck(AbstractCompiler compiler,
+                 com.google.javascript.jscomp.ReverseAbstractInterpreter reverseInterpreter,
+                 JSTypeRegistry typeRegistry,
+                 CheckLevel reportMissingOverride,
+                 CheckLevel reportUnknownTypes)
+
+
+ + + + + + + + +
+Method Detail
+ +

+process

+
+public void process(Node externsRoot,
+                    Node jsRoot)
+
+
Main entry point for this phase of processing. This follows the pattern for + JSCompiler phases. +

+

+
Specified by:
process in interface CompilerPass
+
+
+
Parameters:
externsRoot - The root of the externs parse tree.
jsRoot - The root of the input parse tree to be checked.
+
+
+
+ +

+processForTesting

+
+public Scope processForTesting(Node externsRoot,
+                               Node jsRoot)
+
+
Main entry point of this phase for testing code. +

+

+
+
+
+
+
+
+
+ +

+check

+
+public void check(Node node,
+                  boolean externs)
+
+
+
+
+
+
+
+
+
+ +

+shouldTraverse

+
+public boolean shouldTraverse(NodeTraversal t,
+                              Node n,
+                              Node parent)
+
+
Description copied from interface: NodeTraversal.Callback
+

Visits a node in pre order (before visiting its children) and decides + whether this node's children should be traversed. If children are + traversed, they will be visited by + NodeTraversal.Callback.visit(NodeTraversal, Node, Node) in post order.

+

Implementations can have side effects (e.g. modifying the parse + tree).

+

+

+
Specified by:
shouldTraverse in interface NodeTraversal.Callback
+
+
+ +
Returns:
whether the children of this node should be visited
+
+
+
+ +

+visit

+
+public void visit(NodeTraversal t,
+                  Node n,
+                  Node parent)
+
+
This is the meat of the type checking. It is basically one big switch, + with each case representing one type of parse tree node. The individual + cases are usually pretty straightforward. +

+

+
Specified by:
visit in interface NodeTraversal.Callback
+
+
+
Parameters:
t - The node traversal object that supplies context, such as the + scope chain to use in name lookups as well as error reporting.
n - The node being visited.
parent - The parent of the node n.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/VariableMap.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/VariableMap.html new file mode 100644 index 0000000..812c486 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/VariableMap.html @@ -0,0 +1,424 @@ + + + + + +VariableMap (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class VariableMap

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.VariableMap
+
+
+
+
public class VariableMap
extends Object
+ + +

+Stores the mapping from original variable name to new variable names. +

+ +

+

+
See Also:
RenameVars
+
+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static VariableMapfromBytes(byte[] bytes) + +
+          Deserializes the variable map from a byte array returned by + toBytes().
+static VariableMapfromMap(Map<String,String> map) + +
+          Initializes the variable map from an existing map.
+ Map<String,String>getNewNameToOriginalNameMap() + +
+          Returns an unmodifiable mapping from new names to original names.
+ Map<String,String>getOriginalNameToNewNameMap() + +
+          Returns an unmodifiable mapping from original names to new names.
+static VariableMapload(String filename) + +
+          Reads the variable map from a file written via save(String).
+ StringlookupNewName(String sourceName) + +
+          Given an original variable name, look up new name, may return null + if it's not found.
+ StringlookupSourceName(String newName) + +
+          Given a new variable name, lookup the source name, may return null + if it's not found.
+ voidsave(String filename) + +
+          Saves the variable map to a file.
+ byte[]toBytes() + +
+          Serializes the variable map to a byte array.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+lookupNewName

+
+public String lookupNewName(String sourceName)
+
+
Given an original variable name, look up new name, may return null + if it's not found. +

+

+
+
+
+
+ +

+lookupSourceName

+
+public String lookupSourceName(String newName)
+
+
Given a new variable name, lookup the source name, may return null + if it's not found. +

+

+
+
+
+
+ +

+getOriginalNameToNewNameMap

+
+public Map<String,String> getOriginalNameToNewNameMap()
+
+
Returns an unmodifiable mapping from original names to new names. +

+

+
+
+
+
+ +

+getNewNameToOriginalNameMap

+
+public Map<String,String> getNewNameToOriginalNameMap()
+
+
Returns an unmodifiable mapping from new names to original names. +

+

+
+
+
+
+ +

+save

+
+public void save(String filename)
+          throws IOException
+
+
Saves the variable map to a file. +

+

+ +
Throws: +
IOException
+
+
+
+ +

+load

+
+public static VariableMap load(String filename)
+                        throws IOException
+
+
Reads the variable map from a file written via save(String). +

+

+ +
Throws: +
IOException
+
+
+
+ +

+toBytes

+
+public byte[] toBytes()
+
+
Serializes the variable map to a byte array. +

+

+
+
+
+
+ +

+fromBytes

+
+public static VariableMap fromBytes(byte[] bytes)
+                             throws ParseException
+
+
Deserializes the variable map from a byte array returned by + toBytes(). +

+

+ +
Throws: +
ParseException
+
+
+
+ +

+fromMap

+
+public static VariableMap fromMap(Map<String,String> map)
+
+
Initializes the variable map from an existing map. +

+

+
Parameters:
map - The map to use from original names to generated names. It is + copied and changes to the specified map will not affect the returned + object.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/VariableRenamingPolicy.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/VariableRenamingPolicy.html new file mode 100644 index 0000000..a417e6b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/VariableRenamingPolicy.html @@ -0,0 +1,376 @@ + + + + + +VariableRenamingPolicy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum VariableRenamingPolicy

+
+java.lang.Object
+  extended by java.lang.Enum<VariableRenamingPolicy>
+      extended by com.google.javascript.jscomp.VariableRenamingPolicy
+
+
+
All Implemented Interfaces:
Serializable, Comparable<VariableRenamingPolicy>
+
+
+
+
public enum VariableRenamingPolicy
extends Enum<VariableRenamingPolicy>
+ + +

+Policies to determine which variables should be renamed. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + +
+Enum Constant Summary
ALL + +
+          Rename all variables and functions unless they are exported or externed.
LOCAL + +
+          Rename local variables only.
OFF + +
+          Rename no variables.
UNSPECIFIED + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static VariableRenamingPolicyvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static VariableRenamingPolicy[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+OFF

+
+public static final VariableRenamingPolicy OFF
+
+
Rename no variables. +

+

+
+
+
+ +

+LOCAL

+
+public static final VariableRenamingPolicy LOCAL
+
+
Rename local variables only. +

+

+
+
+
+ +

+ALL

+
+public static final VariableRenamingPolicy ALL
+
+
Rename all variables and functions unless they are exported or externed. +

+

+
+
+
+ +

+UNSPECIFIED

+
+public static final VariableRenamingPolicy UNSPECIFIED
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static VariableRenamingPolicy[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (VariableRenamingPolicy c : VariableRenamingPolicy.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static VariableRenamingPolicy valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/WarningLevel.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/WarningLevel.html new file mode 100644 index 0000000..b14d7e3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/WarningLevel.html @@ -0,0 +1,373 @@ + + + + + +WarningLevel (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum WarningLevel

+
+java.lang.Object
+  extended by java.lang.Enum<WarningLevel>
+      extended by com.google.javascript.jscomp.WarningLevel
+
+
+
All Implemented Interfaces:
Serializable, Comparable<WarningLevel>
+
+
+
+
public enum WarningLevel
extends Enum<WarningLevel>
+ + +

+Convert the warnings level to an Options object. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Enum Constant Summary
DEFAULT + +
+           
QUIET + +
+           
VERBOSE + +
+           
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidsetOptionsForWarningLevel(CompilerOptions options) + +
+           
+static WarningLevelvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static WarningLevel[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+QUIET

+
+public static final WarningLevel QUIET
+
+
+
+
+
+ +

+DEFAULT

+
+public static final WarningLevel DEFAULT
+
+
+
+
+
+ +

+VERBOSE

+
+public static final WarningLevel VERBOSE
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static WarningLevel[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (WarningLevel c : WarningLevel.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static WarningLevel valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+
+ +

+setOptionsForWarningLevel

+
+public void setOptionsForWarningLevel(CompilerOptions options)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/WarningsGuard.Priority.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/WarningsGuard.Priority.html new file mode 100644 index 0000000..e5c18a2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/WarningsGuard.Priority.html @@ -0,0 +1,436 @@ + + + + + +WarningsGuard.Priority (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Enum WarningsGuard.Priority

+
+java.lang.Object
+  extended by java.lang.Enum<WarningsGuard.Priority>
+      extended by com.google.javascript.jscomp.WarningsGuard.Priority
+
+
+
All Implemented Interfaces:
Serializable, Comparable<WarningsGuard.Priority>
+
+
+
Enclosing class:
WarningsGuard
+
+
+
+
public static enum WarningsGuard.Priority
extends Enum<WarningsGuard.Priority>
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Enum Constant Summary
DEFAULT + +
+           
FILTER_BY_PATH + +
+           
MAX + +
+           
MIN + +
+           
STRICT + +
+           
SUPPRESS_BY_WHITELIST + +
+           
SUPPRESS_DOC + +
+           
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ intgetValue() + +
+           
+static WarningsGuard.PriorityvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static WarningsGuard.Priority[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+MAX

+
+public static final WarningsGuard.Priority MAX
+
+
+
+
+
+ +

+MIN

+
+public static final WarningsGuard.Priority MIN
+
+
+
+
+
+ +

+STRICT

+
+public static final WarningsGuard.Priority STRICT
+
+
+
+
+
+ +

+DEFAULT

+
+public static final WarningsGuard.Priority DEFAULT
+
+
+
+
+
+ +

+SUPPRESS_BY_WHITELIST

+
+public static final WarningsGuard.Priority SUPPRESS_BY_WHITELIST
+
+
+
+
+
+ +

+SUPPRESS_DOC

+
+public static final WarningsGuard.Priority SUPPRESS_DOC
+
+
+
+
+
+ +

+FILTER_BY_PATH

+
+public static final WarningsGuard.Priority FILTER_BY_PATH
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static WarningsGuard.Priority[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (WarningsGuard.Priority c : WarningsGuard.Priority.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static WarningsGuard.Priority valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+
+ +

+getValue

+
+public int getValue()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/WarningsGuard.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/WarningsGuard.html new file mode 100644 index 0000000..b44badc --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/WarningsGuard.html @@ -0,0 +1,400 @@ + + + + + +WarningsGuard (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class WarningsGuard

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.WarningsGuard
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
Direct Known Subclasses:
ComposeWarningsGuard, DiagnosticGroupWarningsGuard, ShowByPathWarningsGuard, StrictWarningsGuard, WhitelistWarningsGuard
+
+
+
+
public abstract class WarningsGuard
extends Object
implements Serializable
+ + +

+Class that allows to flexibly manage what to do with a reported + warning/error. + + Guard has several choices: + - return OFF - suppress the warning/error + - return WARNING + - return ERROR report it with high severity + - return null. Does not know what to do with it. Lets the other guard + decide what to do with it. + + Although the interface is very simple it allows you easyly customize what + warnings you are interested in. + + For example there are could be several implementations: + StrictGuard - {return ERROR}. All warnings should be treat as errors. + SilentGuard - {if (WARNING) return OFF}. Suppress all warnings but still + fail if js has errors. + WhitelistGuard (if !whitelistErrors.contains(error) return ERROR) return + error if it does not present in the whitelist. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classWarningsGuard.Priority + +
+           
+  + + + + + + + + + + +
+Constructor Summary
WarningsGuard() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+protected  booleandisables(DiagnosticGroup group) + +
+          Returns whether all warnings in the given diagnostic group will be + filtered out.
+protected  booleanenables(DiagnosticGroup group) + +
+          Returns whether any of the warnings in the given diagnostic group will be + upgraded to a warning or error.
+protected  intgetPriority() + +
+          The priority in which warnings guards are applied.
+abstract  CheckLevellevel(JSError error) + +
+          Returns a new check level for a given error.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+WarningsGuard

+
+public WarningsGuard()
+
+
+ + + + + + + + +
+Method Detail
+ +

+level

+
+public abstract CheckLevel level(JSError error)
+
+
Returns a new check level for a given error. OFF - suppress it, ERROR - + report as error. null means that this guard does not know what to do + with the error. Null is extremely helpful when you have a chain of + guards. If current guard returns null, then the next in the chain should + process it. +

+

+
+
+
+
Parameters:
error - a reported error. +
Returns:
what level given error should have.
+
+
+
+ +

+getPriority

+
+protected int getPriority()
+
+
The priority in which warnings guards are applied. Lower means the + guard will be applied sooner. Expressed on a scale of 1 to 100. +

+

+
+
+
+
+
+
+
+ +

+disables

+
+protected boolean disables(DiagnosticGroup group)
+
+
Returns whether all warnings in the given diagnostic group will be + filtered out. Used to determine which passes to skip. +

+

+
+
+
+
Parameters:
group - A group of DiagnosticTypes. +
Returns:
Whether all warnings of these types are disabled by this guard.
+
+
+
+ +

+enables

+
+protected boolean enables(DiagnosticGroup group)
+
+
Returns whether any of the warnings in the given diagnostic group will be + upgraded to a warning or error. +

+

+
+
+
+
Parameters:
group - A group of DiagnosticTypes. +
Returns:
Whether any warnings of these types are enabled by this guard.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/WhitelistWarningsGuard.WhitelistBuilder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/WhitelistWarningsGuard.WhitelistBuilder.html new file mode 100644 index 0000000..3b3a0a3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/WhitelistWarningsGuard.WhitelistBuilder.html @@ -0,0 +1,404 @@ + + + + + +WhitelistWarningsGuard.WhitelistBuilder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class WhitelistWarningsGuard.WhitelistBuilder

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.WhitelistWarningsGuard.WhitelistBuilder
+
+
+
All Implemented Interfaces:
ErrorHandler
+
+
+
Enclosing class:
WhitelistWarningsGuard
+
+
+
+
public static class WhitelistWarningsGuard.WhitelistBuilder
extends Object
implements ErrorHandler
+ + +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
WhitelistWarningsGuard.WhitelistBuilder() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidappendWhitelist(PrintStream out) + +
+          Writes the warnings collected in a format that the WhitelistWarningsGuard + can read back later.
+ voidreport(CheckLevel level, + JSError error) + +
+           
+ WhitelistWarningsGuard.WhitelistBuildersetGeneratorTarget(String name) + +
+          Fill in instructions on how to generate this whitelist.
+ WhitelistWarningsGuard.WhitelistBuildersetProductName(String name) + +
+          Fill in your product name to get a fun message!
+ WhitelistWarningsGuard.WhitelistBuildersetWithLineNumber(boolean line) + +
+          Sets whether line number are recorded in the whitelist.
+ voidwriteWhitelist(File out) + +
+          Writes the warnings collected in a format that the WhitelistWarningsGuard + can read back later.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+WhitelistWarningsGuard.WhitelistBuilder

+
+public WhitelistWarningsGuard.WhitelistBuilder()
+
+
+ + + + + + + + +
+Method Detail
+ +

+setProductName

+
+public WhitelistWarningsGuard.WhitelistBuilder setProductName(String name)
+
+
Fill in your product name to get a fun message! +

+

+
+
+
+
+
+
+
+ +

+setGeneratorTarget

+
+public WhitelistWarningsGuard.WhitelistBuilder setGeneratorTarget(String name)
+
+
Fill in instructions on how to generate this whitelist. +

+

+
+
+
+
+
+
+
+ +

+setWithLineNumber

+
+public WhitelistWarningsGuard.WhitelistBuilder setWithLineNumber(boolean line)
+
+
Sets whether line number are recorded in the whitelist. + This means that if lines are added below the warning, the warning + will need to be fixed or the whitelist will need to be regenerated. +

+

+
+
+
+
+
+
+
+ +

+report

+
+public void report(CheckLevel level,
+                   JSError error)
+
+
+
Specified by:
report in interface ErrorHandler
+
+
+
Parameters:
level - the reporting level
error - the error to report
+
+
+
+ +

+writeWhitelist

+
+public void writeWhitelist(File out)
+                    throws IOException
+
+
Writes the warnings collected in a format that the WhitelistWarningsGuard + can read back later. +

+

+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+appendWhitelist

+
+public void appendWhitelist(PrintStream out)
+
+
Writes the warnings collected in a format that the WhitelistWarningsGuard + can read back later. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/WhitelistWarningsGuard.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/WhitelistWarningsGuard.html new file mode 100644 index 0000000..6327063 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/WhitelistWarningsGuard.html @@ -0,0 +1,506 @@ + + + + + +WhitelistWarningsGuard (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class WhitelistWarningsGuard

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.WarningsGuard
+      extended by com.google.javascript.jscomp.WhitelistWarningsGuard
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class WhitelistWarningsGuard
extends WarningsGuard
+ + +

+An extension of WarningsGuard that provides functionality to maintain + a list of warnings (white-list). It is subclasses' responsibility to decide + what to do with the white-list by implementing the level function. + Warnings are defined by the name of the js file and the first line of + warnings description. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classWhitelistWarningsGuard.WhitelistBuilder + +
+           
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.jscomp.WarningsGuard
WarningsGuard.Priority
+  + + + + + + + + + + + +
+Constructor Summary
WhitelistWarningsGuard(Set<String> whiteList) + +
+          This class depends on an input set that contains the white-list.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+protected  booleancontainWarning(String formattedWarning) + +
+          Determines whether a given warning is included in the white-list.
+static StringformatWarning(JSError error) + +
+           
+static StringformatWarning(JSError error, + boolean withLineNumber) + +
+           
+static WhitelistWarningsGuardfromFile(File file) + +
+          Creates a warnings guard from a file.
+static StringgetFirstLine(String warning) + +
+           
+ intgetPriority() + +
+          The priority in which warnings guards are applied.
+ CheckLevellevel(JSError error) + +
+          Returns a new check level for a given error.
+static Set<String>loadWhitelistedJsWarnings(File file) + +
+          Loads legacy warnings list from the file.
+protected static Set<String>loadWhitelistedJsWarnings(com.google.common.io.InputSupplier<InputStreamReader> supplier) + +
+          Loads legacy warnings list from the file.
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.WarningsGuard
disables, enables
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+WhitelistWarningsGuard

+
+public WhitelistWarningsGuard(Set<String> whiteList)
+
+
This class depends on an input set that contains the white-list. The format + of each white-list string is: + : +

+

+
Parameters:
whiteList - The set of js-warnings that are white-listed. This is + expected to have similar format as formatWarning(JSError).
+
+ + + + + + + + +
+Method Detail
+ +

+level

+
+public CheckLevel level(JSError error)
+
+
Description copied from class: WarningsGuard
+
Returns a new check level for a given error. OFF - suppress it, ERROR - + report as error. null means that this guard does not know what to do + with the error. Null is extremely helpful when you have a chain of + guards. If current guard returns null, then the next in the chain should + process it. +

+

+
Specified by:
level in class WarningsGuard
+
+
+
Parameters:
error - a reported error. +
Returns:
what level given error should have.
+
+
+
+ +

+containWarning

+
+protected boolean containWarning(String formattedWarning)
+
+
Determines whether a given warning is included in the white-list. +

+

+
Parameters:
formattedWarning - the warning formatted by formatWarning +
Returns:
whether the given warning is white-listed or not.
+
+
+
+ +

+getPriority

+
+public int getPriority()
+
+
Description copied from class: WarningsGuard
+
The priority in which warnings guards are applied. Lower means the + guard will be applied sooner. Expressed on a scale of 1 to 100. +

+

+
Overrides:
getPriority in class WarningsGuard
+
+
+
+
+
+
+ +

+fromFile

+
+public static WhitelistWarningsGuard fromFile(File file)
+
+
Creates a warnings guard from a file. +

+

+
+
+
+
+ +

+loadWhitelistedJsWarnings

+
+public static Set<String> loadWhitelistedJsWarnings(File file)
+
+
Loads legacy warnings list from the file. As during development line + numbers are changed very often - we just cut it and compare without ones. +

+

+ +
Returns:
known legacy warnings without line numbers.
+
+
+
+ +

+loadWhitelistedJsWarnings

+
+protected static Set<String> loadWhitelistedJsWarnings(com.google.common.io.InputSupplier<InputStreamReader> supplier)
+
+
Loads legacy warnings list from the file. As during development line + numbers are changed very often - we just cut it and compare without ones. +

+

+ +
Returns:
known legacy warnings without line numbers.
+
+
+
+ +

+formatWarning

+
+public static String formatWarning(JSError error)
+
+
+
+
+
+
+ +

+formatWarning

+
+public static String formatWarning(JSError error,
+                                   boolean withLineNumber)
+
+
+
+
+
+
+ +

+getFirstLine

+
+public static String getFirstLine(String warning)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/XtbMessageBundle.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/XtbMessageBundle.html new file mode 100644 index 0000000..6ae75bb --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/XtbMessageBundle.html @@ -0,0 +1,363 @@ + + + + + +XtbMessageBundle (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp +
+Class XtbMessageBundle

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.XtbMessageBundle
+
+
+
All Implemented Interfaces:
MessageBundle
+
+
+
+
public class XtbMessageBundle
extends Object
implements MessageBundle
+ + +

+A MessageBundle that parses messages from an XML Translation Bundle (XTB) + file. +

+ +

+


+ +

+ + + + + + + + + + + + + + +
+Constructor Summary
XtbMessageBundle(InputStream xtb, + String projectId) + +
+          Creates an instance and initializes it with the messages in an XTB file.
XtbMessageBundle(InputStream xtb, + String projectId, + boolean unused) + +
+           
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ Iterable<JsMessage>getAllMessages() + +
+          Returns an iterable over the keys that this object has replacements for.
+ JsMessagegetMessage(String id) + +
+          Gets a message replacement.
+ JsMessage.IdGeneratoridGenerator() + +
+          Gets the message ID generator to use to compute message IDs for this + type of bundle.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+XtbMessageBundle

+
+public XtbMessageBundle(InputStream xtb,
+                        @Nullable
+                        String projectId,
+                        boolean unused)
+
+
+
+ +

+XtbMessageBundle

+
+public XtbMessageBundle(InputStream xtb,
+                        @Nullable
+                        String projectId)
+
+
Creates an instance and initializes it with the messages in an XTB file. +

+

+
Parameters:
xtb - the XTB file as a byte stream
projectId - the translation console project id (i.e. name)
+
+ + + + + + + + +
+Method Detail
+ +

+getMessage

+
+public JsMessage getMessage(String id)
+
+
Description copied from interface: MessageBundle
+
Gets a message replacement. +

+

+
Specified by:
getMessage in interface MessageBundle
+
+
+
Parameters:
id - the id of the message being replaced; the key is message ID + generated by JsMessage.IdGenerator +
Returns:
the message replacement, which may be null.
+
+
+
+ +

+idGenerator

+
+public JsMessage.IdGenerator idGenerator()
+
+
Description copied from interface: MessageBundle
+
Gets the message ID generator to use to compute message IDs for this + type of bundle. +

+

+
Specified by:
idGenerator in interface MessageBundle
+
+
+ +
Returns:
idGenerator instance or null if we do not want to use any custom + id generation. In case if idGenerator is null caller should decide how + to create id by itself. In the most cases using the message key is + enough.
+
+
+
+ +

+getAllMessages

+
+public Iterable<JsMessage> getAllMessages()
+
+
Description copied from interface: MessageBundle
+
Returns an iterable over the keys that this object has replacements for. +

+

+
Specified by:
getAllMessages in interface MessageBundle
+
+
+ +
Returns:
all messages from this bundle.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/AntErrorManager.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/AntErrorManager.html new file mode 100644 index 0000000..314ca8d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/AntErrorManager.html @@ -0,0 +1,318 @@ + + + + + +AntErrorManager (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.ant +
+Class AntErrorManager

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.BasicErrorManager
+      extended by com.google.javascript.jscomp.ant.AntErrorManager
+
+
+
All Implemented Interfaces:
ErrorHandler, ErrorManager
+
+
+
+
public final class AntErrorManager
extends BasicErrorManager
+ + +

+An error manager that pipes warnings and errors properly into the Ant + task infrastructure. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
AntErrorManager(MessageFormatter formatter, + org.apache.tools.ant.Task task) + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+ voidprintln(CheckLevel level, + JSError error) + +
+          Print a message with a trailing new line.
+protected  voidprintSummary() + +
+          Print the summary of the compilation - number of errors and warnings.
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.BasicErrorManager
generateReport, getErrorCount, getErrors, getTypedPercent, getWarningCount, getWarnings, report, setTypedPercent
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+AntErrorManager

+
+public AntErrorManager(MessageFormatter formatter,
+                       org.apache.tools.ant.Task task)
+
+
+ + + + + + + + +
+Method Detail
+ +

+println

+
+public void println(CheckLevel level,
+                    JSError error)
+
+
Description copied from class: BasicErrorManager
+
Print a message with a trailing new line. This method is called by the + BasicErrorManager.generateReport() method when generating messages. +

+

+
Specified by:
println in class BasicErrorManager
+
+
+
+
+
+
+ +

+printSummary

+
+protected void printSummary()
+
+
Description copied from class: BasicErrorManager
+
Print the summary of the compilation - number of errors and warnings. +

+

+
Specified by:
printSummary in class BasicErrorManager
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/CompileTask.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/CompileTask.html new file mode 100644 index 0000000..b3188f8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/CompileTask.html @@ -0,0 +1,731 @@ + + + + + +CompileTask (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.ant +
+Class CompileTask

+
+java.lang.Object
+  extended by org.apache.tools.ant.ProjectComponent
+      extended by org.apache.tools.ant.Task
+          extended by com.google.javascript.jscomp.ant.CompileTask
+
+
+
All Implemented Interfaces:
Cloneable
+
+
+
+
public final class CompileTask
extends org.apache.tools.ant.Task
+ + +

+This class implements a simple Ant task to do almost the same as + CommandLineRunner. + + Most of the public methods of this class are entry points for the + Ant code to hook into. +

+ +

+


+ +

+ + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class org.apache.tools.ant.Task
target, taskName, taskType, wrapper
+ + + + + + + +
Fields inherited from class org.apache.tools.ant.ProjectComponent
description, location, project
+  + + + + + + + + + + +
+Constructor Summary
CompileTask() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidaddExterns(org.apache.tools.ant.types.FileList list) + +
+          Sets the externs file.
+ voidaddPath(org.apache.tools.ant.types.Path list) + +
+          Adds a entry.
+ voidaddSources(org.apache.tools.ant.types.FileList list) + +
+          Sets the source files.
+ voidaddWarning(Warning warning) + +
+          Adds a entry + + Each warning entry must have two attributes, group and level.
+ org.apache.tools.ant.types.ParametercreateDefine() + +
+          Creates a new <define/> nested element.
+ voidexecute() + +
+           
+ voidsetCompilationLevel(String value) + +
+          Set the compilation level.
+ voidsetCustomExternsOnly(boolean value) + +
+          Use only custom externs.
+ voidsetDebug(boolean value) + +
+          Enable debugging options.
+ voidsetEncoding(String encoding) + +
+          Set input file encoding
+ voidsetForceRecompile(boolean forceRecompile) + +
+          Set force recompile option
+ voidsetGenerateExports(boolean generateExports) + +
+          Set generateExports option
+ voidsetManageDependencies(boolean value) + +
+           
+ voidsetOutput(File value) + +
+          Set output file.
+ voidsetOutputEncoding(String outputEncoding) + +
+          Set output file encoding
+ voidsetPrettyPrint(boolean pretty) + +
+          Set pretty print formatting option
+ voidsetPrintInputDelimiter(boolean print) + +
+          Set print input delimitter formatting option
+ voidsetReplaceProperties(boolean value) + +
+          Whether to replace @define lines with properties
+ voidsetReplacePropertiesPrefix(String value) + +
+          Set the replacement property prefix.
+ voidsetWarning(String value) + +
+          Set the warning level.
+ + + + + + + +
Methods inherited from class org.apache.tools.ant.Task
bindToOwner, getOwningTarget, getRuntimeConfigurableWrapper, getTaskName, getTaskType, getWrapper, handleErrorFlush, handleErrorOutput, handleFlush, handleInput, handleOutput, init, isInvalid, log, log, log, log, maybeConfigure, perform, reconfigure, setOwningTarget, setRuntimeConfigurableWrapper, setTaskName, setTaskType
+ + + + + + + +
Methods inherited from class org.apache.tools.ant.ProjectComponent
clone, getDescription, getLocation, getProject, setDescription, setLocation, setProject
+ + + + + + + +
Methods inherited from class java.lang.Object
equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+CompileTask

+
+public CompileTask()
+
+
+ + + + + + + + +
+Method Detail
+ +

+setWarning

+
+public void setWarning(String value)
+
+
Set the warning level. +

+

+
Parameters:
value - The warning level by string name. (default, quiet, verbose).
+
+
+
+ +

+setDebug

+
+public void setDebug(boolean value)
+
+
Enable debugging options. +

+

+
Parameters:
value - True if debug mode is enabled.
+
+
+
+ +

+setCompilationLevel

+
+public void setCompilationLevel(String value)
+
+
Set the compilation level. +

+

+
Parameters:
value - The optimization level by string name. + (whitespace, simple, advanced).
+
+
+
+ +

+setManageDependencies

+
+public void setManageDependencies(boolean value)
+
+
+
+
+
+
+ +

+setCustomExternsOnly

+
+public void setCustomExternsOnly(boolean value)
+
+
Use only custom externs. +

+

+
+
+
+
+ +

+setOutput

+
+public void setOutput(File value)
+
+
Set output file. +

+

+
+
+
+
+ +

+setReplacePropertiesPrefix

+
+public void setReplacePropertiesPrefix(String value)
+
+
Set the replacement property prefix. +

+

+
+
+
+
+ +

+setReplaceProperties

+
+public void setReplaceProperties(boolean value)
+
+
Whether to replace @define lines with properties +

+

+
+
+
+
+ +

+setEncoding

+
+public void setEncoding(String encoding)
+
+
Set input file encoding +

+

+
+
+
+
+ +

+setOutputEncoding

+
+public void setOutputEncoding(String outputEncoding)
+
+
Set output file encoding +

+

+
+
+
+
+ +

+setPrettyPrint

+
+public void setPrettyPrint(boolean pretty)
+
+
Set pretty print formatting option +

+

+
+
+
+
+ +

+setPrintInputDelimiter

+
+public void setPrintInputDelimiter(boolean print)
+
+
Set print input delimitter formatting option +

+

+
+
+
+
+ +

+setForceRecompile

+
+public void setForceRecompile(boolean forceRecompile)
+
+
Set force recompile option +

+

+
+
+
+
+ +

+setGenerateExports

+
+public void setGenerateExports(boolean generateExports)
+
+
Set generateExports option +

+

+
+
+
+
+ +

+addExterns

+
+public void addExterns(org.apache.tools.ant.types.FileList list)
+
+
Sets the externs file. +

+

+
+
+
+
+ +

+addWarning

+
+public void addWarning(Warning warning)
+
+
Adds a entry + + Each warning entry must have two attributes, group and level. Group must + contain one of the constants from DiagnosticGroups (e.g., + "ACCESS_CONTROLS"), while level must contain one of the CheckLevel + constants ("ERROR", "WARNING" or "OFF"). +

+

+
+
+
+
+ +

+addSources

+
+public void addSources(org.apache.tools.ant.types.FileList list)
+
+
Sets the source files. +

+

+
+
+
+
+ +

+addPath

+
+public void addPath(org.apache.tools.ant.types.Path list)
+
+
Adds a entry. +

+

+
+
+
+
+ +

+execute

+
+public void execute()
+
+
+
Overrides:
execute in class org.apache.tools.ant.Task
+
+
+
+
+
+
+ +

+createDefine

+
+public org.apache.tools.ant.types.Parameter createDefine()
+
+
Creates a new <define/> nested element. Supports name and value + attribtues. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/Warning.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/Warning.html new file mode 100644 index 0000000..697b3df --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/Warning.html @@ -0,0 +1,325 @@ + + + + + +Warning (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.ant +
+Class Warning

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.ant.Warning
+
+
+
+
public class Warning
extends Object
+ + +

+Simple representation of a warning flag in Ant +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
Warning() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StringgetGroup() + +
+           
+ CheckLevelgetLevel() + +
+           
+ voidsetGroup(String group) + +
+           
+ voidsetLevel(CheckLevel level) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+Warning

+
+public Warning()
+
+
+ + + + + + + + +
+Method Detail
+ +

+getGroup

+
+public String getGroup()
+
+
+
+
+
+
+ +

+setGroup

+
+public void setGroup(String group)
+
+
+
+
+
+
+ +

+getLevel

+
+public CheckLevel getLevel()
+
+
+
+
+
+
+ +

+setLevel

+
+public void setLevel(CheckLevel level)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/package-frame.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/package-frame.html new file mode 100644 index 0000000..cf3b04b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/package-frame.html @@ -0,0 +1,34 @@ + + + + + +com.google.javascript.jscomp.ant (Compiler) + + + + + + + + + + +com.google.javascript.jscomp.ant + + + + +
+Classes  + +
+AntErrorManager +
+CompileTask +
+Warning
+ + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/package-summary.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/package-summary.html new file mode 100644 index 0000000..0fb4821 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/package-summary.html @@ -0,0 +1,179 @@ + + + + + +com.google.javascript.jscomp.ant (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+

+Package com.google.javascript.jscomp.ant +

+ + + + + + + + + + + + + + + + + +
+Class Summary
AntErrorManagerAn error manager that pipes warnings and errors properly into the Ant + task infrastructure.
CompileTaskThis class implements a simple Ant task to do almost the same as + CommandLineRunner.
WarningSimple representation of a warning flag in Ant
+  + +

+

+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/package-tree.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/package-tree.html new file mode 100644 index 0000000..e0c08f1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/ant/package-tree.html @@ -0,0 +1,173 @@ + + + + + +com.google.javascript.jscomp.ant Class Hierarchy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Hierarchy For Package com.google.javascript.jscomp.ant +

+
+
+
Package Hierarchies:
All Packages
+
+

+Class Hierarchy +

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/DependencyInfo.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/DependencyInfo.html new file mode 100644 index 0000000..067040d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/DependencyInfo.html @@ -0,0 +1,290 @@ + + + + + +DependencyInfo (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.deps +
+Interface DependencyInfo

+
+
All Known Implementing Classes:
CompilerInput, JSModule, SimpleDependencyInfo
+
+
+
+
public interface DependencyInfo
+ + +

+A data structure for JS dependency information for a single .js file. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StringgetName() + +
+          Gets the unique name / path of this file.
+ StringgetPathRelativeToClosureBase() + +
+          Gets the path of this file relative to Closure's base.js file.
+ Collection<String>getProvides() + +
+          Gets the symbols provided by this file.
+ Collection<String>getRequires() + +
+          Gets the symbols required by this file.
+  +

+ + + + + + + + +
+Method Detail
+ +

+getName

+
+String getName()
+
+
Gets the unique name / path of this file. +

+

+
+
+
+
+ +

+getPathRelativeToClosureBase

+
+String getPathRelativeToClosureBase()
+
+
Gets the path of this file relative to Closure's base.js file. +

+

+
+
+
+
+ +

+getProvides

+
+Collection<String> getProvides()
+
+
Gets the symbols provided by this file. +

+

+
+
+
+
+ +

+getRequires

+
+Collection<String> getRequires()
+
+
Gets the symbols required by this file. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/DepsFileParser.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/DepsFileParser.html new file mode 100644 index 0000000..bb9bcc0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/DepsFileParser.html @@ -0,0 +1,397 @@ + + + + + +DepsFileParser (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.deps +
+Class DepsFileParser

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.deps.JsFileLineParser
+      extended by com.google.javascript.jscomp.deps.DepsFileParser
+
+
+
+
public class DepsFileParser
extends JsFileLineParser
+ + +

+A parser that can extract dependency information from existing deps.js files. + +

See //javascript/closure/deps.js for an example file.

+

+ +

+


+ +

+ + + + + + + + + + + + + + +
+Constructor Summary
DepsFileParser(ErrorManager errorManager) + +
+          Constructor
DepsFileParser(com.google.common.base.Function<String,String> pathTranslator, + ErrorManager errorManager) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ List<DependencyInfo>parseFile(String filePath) + +
+          Parses the given file and returns a list of dependency information that it + contained.
+ List<DependencyInfo>parseFile(String filePath, + String fileContents) + +
+          Parses the given file and returns a list of dependency information that it + contained.
+ List<DependencyInfo>parseFileReader(String filePath, + Reader reader) + +
+          Parses the file from the given reader and returns a list of + dependency information that it contained.
+protected  booleanparseLine(String line) + +
+          Extracts dependency information from lines that look like + goog.addDependency('pathRelativeToClosure', ['provides'], ['requires']); + Adds the dependencies to depInfos.
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.deps.JsFileLineParser
didParseSucceed, setShortcutMode
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+DepsFileParser

+
+public DepsFileParser(ErrorManager errorManager)
+
+
Constructor +

+

+
Parameters:
errorManager - Handles parse errors.
+
+
+ +

+DepsFileParser

+
+public DepsFileParser(com.google.common.base.Function<String,String> pathTranslator,
+                      ErrorManager errorManager)
+
+
+
Parameters:
pathTranslator - Translates paths in different build systems.
errorManager - Handles parse errors.
+
+ + + + + + + + +
+Method Detail
+ +

+parseFile

+
+public List<DependencyInfo> parseFile(String filePath)
+                               throws IOException
+
+
Parses the given file and returns a list of dependency information that it + contained. +

+

+
Parameters:
filePath - Path to the file to parse. +
Returns:
A list of DependencyInfo objects. +
Throws: +
IOException - Thrown if the file could not be read.
+
+
+
+ +

+parseFile

+
+public List<DependencyInfo> parseFile(String filePath,
+                                      String fileContents)
+
+
Parses the given file and returns a list of dependency information that it + contained. + It uses the passed in fileContents instead of reading the file. +

+

+
Parameters:
filePath - Path to the file to parse.
fileContents - The contents to parse. +
Returns:
A list of DependencyInfo objects.
+
+
+
+ +

+parseFileReader

+
+public List<DependencyInfo> parseFileReader(String filePath,
+                                            Reader reader)
+
+
Parses the file from the given reader and returns a list of + dependency information that it contained. +

+

+
Parameters:
filePath - Path to the file to parse.
reader - A reader for the file. +
Returns:
A list of DependencyInfo objects.
+
+
+
+ +

+parseLine

+
+protected boolean parseLine(String line)
+                     throws com.google.javascript.jscomp.deps.JsFileLineParser.ParseException
+
+
Extracts dependency information from lines that look like + goog.addDependency('pathRelativeToClosure', ['provides'], ['requires']); + Adds the dependencies to depInfos. +

+

+
+
+
+
Parameters:
line - The line to parse. +
Returns:
true to keep going, false otherwise. +
Throws: +
ParseException - Thrown if the given line has a malformed + goog.addDependency(). +
com.google.javascript.jscomp.deps.JsFileLineParser.ParseException
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/DepsGenerator.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/DepsGenerator.html new file mode 100644 index 0000000..14ee847 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/DepsGenerator.html @@ -0,0 +1,372 @@ + + + + + +DepsGenerator (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.deps +
+Class DepsGenerator

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.deps.DepsGenerator
+
+
+
+
public class DepsGenerator
extends Object
+ + +

+Generates deps.js files by scanning javascript files for + calls to goog.provide(), goog.require() and goog.addDependency(). +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
DepsGenerator(Collection<SourceFile> deps, + Collection<SourceFile> srcs, + com.google.javascript.jscomp.deps.DepsGenerator.InclusionStrategy mergeStrategy, + String closurePathAbs, + ErrorManager errorManager) + +
+          Creates a new DepsGenerator.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+protected  voidcleanUpDuplicatedFiles(Map<String,DependencyInfo> depsFiles, + Map<String,DependencyInfo> jsFiles) + +
+          Removes duplicated depsInfo from jsFiles if this info already present in + some of the parsed deps.js
+ StringcomputeDependencyCalls() + +
+          Performs the parsing inputs and writing of outputs.
+protected  DepsFileParsercreateDepsFileParser() + +
+           
+protected  StringformatPathToDepsFile(String path) + +
+          Format the deps file path so that it can be included in the output file.
+protected  booleanshouldSkipDepsFile(SourceFile file) + +
+          Returns whether we should ignore dependency info in the given deps file.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+DepsGenerator

+
+public DepsGenerator(Collection<SourceFile> deps,
+                     Collection<SourceFile> srcs,
+                     com.google.javascript.jscomp.deps.DepsGenerator.InclusionStrategy mergeStrategy,
+                     String closurePathAbs,
+                     ErrorManager errorManager)
+
+
Creates a new DepsGenerator. +

+

+ + + + + + + + +
+Method Detail
+ +

+computeDependencyCalls

+
+public String computeDependencyCalls()
+                              throws IOException
+
+
Performs the parsing inputs and writing of outputs. +

+

+ +
Returns:
Returns a String of goog.addDependency calls that will build + the dependency graph. Returns null if there was an error. +
Throws: +
IOException - Occurs upon an IO error.
+
+
+
+ +

+cleanUpDuplicatedFiles

+
+protected void cleanUpDuplicatedFiles(Map<String,DependencyInfo> depsFiles,
+                                      Map<String,DependencyInfo> jsFiles)
+
+
Removes duplicated depsInfo from jsFiles if this info already present in + some of the parsed deps.js +

+

+
Parameters:
depsFiles - DepsInfo from deps.js dependencies
jsFiles - DepsInfo from some of jsSources
+
+
+
+ +

+createDepsFileParser

+
+protected DepsFileParser createDepsFileParser()
+
+
+
+
+
+
+ +

+shouldSkipDepsFile

+
+protected boolean shouldSkipDepsFile(SourceFile file)
+
+
Returns whether we should ignore dependency info in the given deps file. +

+

+
+
+
+
+ +

+formatPathToDepsFile

+
+protected String formatPathToDepsFile(String path)
+
+
Format the deps file path so that it can be included in the output file. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/JsFileLineParser.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/JsFileLineParser.html new file mode 100644 index 0000000..786faca --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/JsFileLineParser.html @@ -0,0 +1,302 @@ + + + + + +JsFileLineParser (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.deps +
+Class JsFileLineParser

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.deps.JsFileLineParser
+
+
+
Direct Known Subclasses:
DepsFileParser, JsFileParser, JsFunctionParser
+
+
+
+
public abstract class JsFileLineParser
extends Object
+ + +

+Base class for classes that parse Javascript sources on a line-by-line basis. Strips comments + from files and records all parsing errors. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
JsFileLineParser(ErrorManager errorManager) + +
+          Constructor.
+  + + + + + + + + + + + + + + + +
+Method Summary
+ booleandidParseSucceed() + +
+           
+ voidsetShortcutMode(boolean mode) + +
+          In shortcut mode, the file line parser can stop reading early if + it thinks it found enough information.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JsFileLineParser

+
+public JsFileLineParser(ErrorManager errorManager)
+
+
Constructor. +

+

+
Parameters:
errorManager - Parse error handler.
+
+ + + + + + + + +
+Method Detail
+ +

+setShortcutMode

+
+public void setShortcutMode(boolean mode)
+
+
In shortcut mode, the file line parser can stop reading early if + it thinks it found enough information. + + For example, many parsers assume that dependency information never + shows up after "real" code. +

+

+
+
+
+
+ +

+didParseSucceed

+
+public boolean didParseSucceed()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/JsFileParser.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/JsFileParser.html new file mode 100644 index 0000000..b429391 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/JsFileParser.html @@ -0,0 +1,353 @@ + + + + + +JsFileParser (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.deps +
+Class JsFileParser

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.deps.JsFileLineParser
+      extended by com.google.javascript.jscomp.deps.JsFileParser
+
+
+
+
public class JsFileParser
extends JsFileLineParser
+ + +

+A parser that can extract goog.require() and goog.provide() dependency + information from a .js file. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
JsFileParser(ErrorManager errorManager) + +
+          Constructor
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ DependencyInfoparseFile(String filePath, + String closureRelativePath) + +
+          Parses the given file and returns the dependency information that it + contained.
+ DependencyInfoparseFile(String filePath, + String closureRelativePath, + String fileContents) + +
+          Parses the given file and returns the dependency information that it + contained.
+protected  booleanparseLine(String line) + +
+          Parses a line of javascript, extracting goog.provide and goog.require + information.
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.deps.JsFileLineParser
didParseSucceed, setShortcutMode
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JsFileParser

+
+public JsFileParser(ErrorManager errorManager)
+
+
Constructor +

+

+
Parameters:
errorManager - Handles parse errors.
+
+ + + + + + + + +
+Method Detail
+ +

+parseFile

+
+public DependencyInfo parseFile(String filePath,
+                                String closureRelativePath)
+                         throws IOException
+
+
Parses the given file and returns the dependency information that it + contained. +

+

+
Parameters:
filePath - Path to the file to parse.
closureRelativePath - Path of the file relative to closure. +
Returns:
A DependencyInfo containing all provides/requires found in the + file. +
Throws: +
IOException - Thrown if there was an problem reading the given file.
+
+
+
+ +

+parseFile

+
+public DependencyInfo parseFile(String filePath,
+                                String closureRelativePath,
+                                String fileContents)
+
+
Parses the given file and returns the dependency information that it + contained. +

+

+
Parameters:
filePath - Path to the file to parse.
closureRelativePath - Path of the file relative to closure.
fileContents - The contents to parse. +
Returns:
A DependencyInfo containing all provides/requires found in the + file.
+
+
+
+ +

+parseLine

+
+protected boolean parseLine(String line)
+                     throws com.google.javascript.jscomp.deps.JsFileLineParser.ParseException
+
+
Parses a line of javascript, extracting goog.provide and goog.require + information. +

+

+
+
+
+
Parameters:
line - The line to parse. +
Returns:
true to keep going, false otherwise. +
Throws: +
com.google.javascript.jscomp.deps.JsFileLineParser.ParseException
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/JsFunctionParser.SymbolInfo.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/JsFunctionParser.SymbolInfo.html new file mode 100644 index 0000000..7649455 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/JsFunctionParser.SymbolInfo.html @@ -0,0 +1,260 @@ + + + + + +JsFunctionParser.SymbolInfo (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.deps +
+Class JsFunctionParser.SymbolInfo

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.deps.JsFunctionParser.SymbolInfo
+
+
+
Enclosing class:
JsFunctionParser
+
+
+
+
public static class JsFunctionParser.SymbolInfo
extends Object
+ + +

+


+ +

+ + + + + + + + + + + + + + + +
+Field Summary
+ StringfunctionName + +
+           
+ Stringsymbol + +
+           
+  + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+functionName

+
+public final String functionName
+
+
+
+
+
+ +

+symbol

+
+public final String symbol
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/JsFunctionParser.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/JsFunctionParser.html new file mode 100644 index 0000000..8329cfa --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/JsFunctionParser.html @@ -0,0 +1,337 @@ + + + + + +JsFunctionParser (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.deps +
+Class JsFunctionParser

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.deps.JsFileLineParser
+      extended by com.google.javascript.jscomp.deps.JsFunctionParser
+
+
+
+
public class JsFunctionParser
extends JsFileLineParser
+ + +

+A parser that can extract dependency information from a .js file. +

+ +

+


+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classJsFunctionParser.SymbolInfo + +
+           
+  + + + + + + + + + + +
+Constructor Summary
JsFunctionParser(Collection<String> functions, + ErrorManager errorManager) + +
+          Constructor
+  + + + + + + + + + + + + + + + +
+Method Summary
+ Collection<JsFunctionParser.SymbolInfo>parseFile(String filePath, + String fileContents) + +
+          Parses the given file and returns the dependency information that it + contained.
+protected  booleanparseLine(String line) + +
+          Parses a line of javascript, extracting dependency information.
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.deps.JsFileLineParser
didParseSucceed, setShortcutMode
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JsFunctionParser

+
+public JsFunctionParser(Collection<String> functions,
+                        ErrorManager errorManager)
+
+
Constructor +

+

+
Parameters:
functions - Functions to parse.
errorManager - Handles parse errors.
+
+ + + + + + + + +
+Method Detail
+ +

+parseFile

+
+public Collection<JsFunctionParser.SymbolInfo> parseFile(String filePath,
+                                                         String fileContents)
+
+
Parses the given file and returns the dependency information that it + contained. +

+

+
Parameters:
filePath - Path to the file to parse.
fileContents - The contents to parse. +
Returns:
A collection containing all symbols found in the + file.
+
+
+
+ +

+parseLine

+
+protected boolean parseLine(String line)
+                     throws com.google.javascript.jscomp.deps.JsFileLineParser.ParseException
+
+
Parses a line of javascript, extracting dependency information. +

+

+
+
+
+
Parameters:
line - The line to parse. +
Returns:
true to keep going, false otherwise. +
Throws: +
com.google.javascript.jscomp.deps.JsFileLineParser.ParseException
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/PathUtil.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/PathUtil.html new file mode 100644 index 0000000..929ac58 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/PathUtil.html @@ -0,0 +1,342 @@ + + + + + +PathUtil (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.deps +
+Class PathUtil

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.deps.PathUtil
+
+
+
+
public final class PathUtil
extends Object
+ + +

+Utility methods for manipulation of UNIX-like paths. + NOTE: According to kevinb, equivalent methods will be in the standard library once + jsr203 is ready. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static StringcollapseDots(String path) + +
+          Removes all ../ and ./ entries from within the given path.
+static StringmakeAbsolute(String path) + +
+          Converts the given path into an absolute one.
+static StringmakeAbsolute(String path, + String rootPath) + +
+          Converts the given path into an absolute one.
+static StringmakeRelative(String basePath, + String targetPath) + +
+          Returns targetPath relative to basePath.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+collapseDots

+
+public static String collapseDots(String path)
+
+
Removes all ../ and ./ entries from within the given path. If there are extra ..s that move + beyond the first directory given, they are removed. + + Examples: + "a/b/../c" results in "a/c" + "./foo/./../bar" results in "bar" + "a/.." results in "" + "a/../../foo" results in "foo" +

+

+
Parameters:
path - The path to remove dots from. +
Returns:
The path with all dots collapsed.
+
+
+
+ +

+makeAbsolute

+
+public static String makeAbsolute(String path)
+
+
Converts the given path into an absolute one. This prepends the current + working directory and removes all .'s from the path. If an absolute path + is given, it will not be prefixed. + +

Unlike File.getAbsolutePath(), this function does remove .'s from the + path and unlike File.getCanonicalPath(), this function does not resolve + symlinks and does not use filesystem calls.

+

+

+
Parameters:
path - The path to make absolute. +
Returns:
The path made absolute.
+
+
+
+ +

+makeAbsolute

+
+public static String makeAbsolute(String path,
+                                  String rootPath)
+
+
Converts the given path into an absolute one. This prepends the given + rootPath and removes all .'s from the path. If an absolute path is given, + it will not be prefixed. + +

Unlike File.getAbsolutePath(), this function does remove .'s from the + path and unlike File.getCanonicalPath(), this function does not resolve + symlinks and does not use filesystem calls.

+

+

+
Parameters:
rootPath - The path to prefix to path if path is not already absolute.
path - The path to make absolute. +
Returns:
The path made absolute.
+
+
+
+ +

+makeRelative

+
+public static String makeRelative(String basePath,
+                                  String targetPath)
+
+
Returns targetPath relative to basePath. + +

basePath and targetPath must either both be relative, or both be + absolute paths.

+ +

This function is different from makeRelative + in that it is able to add in ../ components and collapse existing ones as well.

+ + Examples: + base="some/relative/path" target="some/relative/path/foo" return="foo" + base="some/relative/path" target="some/relative" return=".." + base="some/relative/path" target="foo/bar" return="../../../foo/bar" + base="/some/abs/path" target="/foo/bar" return="../../../foo/bar" +

+

+
Parameters:
basePath - The path to make targetPath relative to.
targetPath - The path to make relative. +
Returns:
A path relative to targetPath. The returned value will never start + with a slash.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/SimpleDependencyInfo.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/SimpleDependencyInfo.html new file mode 100644 index 0000000..6ec6dbb --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/SimpleDependencyInfo.html @@ -0,0 +1,410 @@ + + + + + +SimpleDependencyInfo (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.deps +
+Class SimpleDependencyInfo

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.deps.SimpleDependencyInfo
+
+
+
All Implemented Interfaces:
DependencyInfo
+
+
+
+
public class SimpleDependencyInfo
extends Object
implements DependencyInfo
+ + +

+A class to hold JS dependency information for a single .js file. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
SimpleDependencyInfo(String srcPathRelativeToClosure, + String pathOfDefiningFile, + List<String> provides, + List<String> requires) + +
+          Constructs a DependencyInfo object with the given list of provides & + requires.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleanequals(Object obj) + +
+           
+ StringgetName() + +
+          Gets the unique name / path of this file.
+ StringgetPathRelativeToClosureBase() + +
+          Gets the path of this file relative to Closure's base.js file.
+ Collection<String>getProvides() + +
+          Gets the symbols provided by this file.
+ Collection<String>getRequires() + +
+          Gets the symbols required by this file.
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SimpleDependencyInfo

+
+public SimpleDependencyInfo(String srcPathRelativeToClosure,
+                            String pathOfDefiningFile,
+                            List<String> provides,
+                            List<String> requires)
+
+
Constructs a DependencyInfo object with the given list of provides & + requires. This does *not* copy the given lists, but uses them directly. +

+

+
Parameters:
srcPathRelativeToClosure - The closure-relative path of the file + associated with this DependencyInfo.
pathOfDefiningFile - The path to the file from which this dependency + information was extracted.
provides - List of provided symbols.
requires - List of required symbols.
+
+ + + + + + + + +
+Method Detail
+ +

+getName

+
+public String getName()
+
+
Description copied from interface: DependencyInfo
+
Gets the unique name / path of this file. +

+

+
Specified by:
getName in interface DependencyInfo
+
+
+
+
+
+
+ +

+getPathRelativeToClosureBase

+
+public String getPathRelativeToClosureBase()
+
+
Description copied from interface: DependencyInfo
+
Gets the path of this file relative to Closure's base.js file. +

+

+
Specified by:
getPathRelativeToClosureBase in interface DependencyInfo
+
+
+
+
+
+
+ +

+getProvides

+
+public Collection<String> getProvides()
+
+
Description copied from interface: DependencyInfo
+
Gets the symbols provided by this file. +

+

+
Specified by:
getProvides in interface DependencyInfo
+
+
+
+
+
+
+ +

+getRequires

+
+public Collection<String> getRequires()
+
+
Description copied from interface: DependencyInfo
+
Gets the symbols required by this file. +

+

+
Specified by:
getRequires in interface DependencyInfo
+
+
+
+
+
+
+ +

+equals

+
+public boolean equals(Object obj)
+
+
+
Overrides:
equals in class Object
+
+
+
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/SortedDependencies.CircularDependencyException.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/SortedDependencies.CircularDependencyException.html new file mode 100644 index 0000000..14d8412 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/SortedDependencies.CircularDependencyException.html @@ -0,0 +1,222 @@ + + + + + +SortedDependencies.CircularDependencyException (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.deps +
+Class SortedDependencies.CircularDependencyException

+
+java.lang.Object
+  extended by java.lang.Throwable
+      extended by java.lang.Exception
+          extended by com.google.javascript.jscomp.deps.SortedDependencies.CircularDependencyException
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
Enclosing class:
SortedDependencies<INPUT extends DependencyInfo>
+
+
+
+
public static class SortedDependencies.CircularDependencyException
extends Exception
+ + +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class java.lang.Throwable
fillInStackTrace, getCause, getLocalizedMessage, getMessage, getStackTrace, initCause, printStackTrace, printStackTrace, printStackTrace, setStackTrace, toString
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ +


+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/SortedDependencies.MissingProvideException.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/SortedDependencies.MissingProvideException.html new file mode 100644 index 0000000..000c7f9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/SortedDependencies.MissingProvideException.html @@ -0,0 +1,222 @@ + + + + + +SortedDependencies.MissingProvideException (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.deps +
+Class SortedDependencies.MissingProvideException

+
+java.lang.Object
+  extended by java.lang.Throwable
+      extended by java.lang.Exception
+          extended by com.google.javascript.jscomp.deps.SortedDependencies.MissingProvideException
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
Enclosing class:
SortedDependencies<INPUT extends DependencyInfo>
+
+
+
+
public static class SortedDependencies.MissingProvideException
extends Exception
+ + +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class java.lang.Throwable
fillInStackTrace, getCause, getLocalizedMessage, getMessage, getStackTrace, initCause, printStackTrace, printStackTrace, printStackTrace, setStackTrace, toString
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ +


+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/SortedDependencies.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/SortedDependencies.html new file mode 100644 index 0000000..ee01722 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/SortedDependencies.html @@ -0,0 +1,398 @@ + + + + + +SortedDependencies (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.deps +
+Class SortedDependencies<INPUT extends DependencyInfo>

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.deps.SortedDependencies<INPUT>
+
+
+
+
public class SortedDependencies<INPUT extends DependencyInfo>
extends Object
+ + +

+A sorted list of inputs with dependency information. Uses a stable + topological sort to make sure that an input always comes after its + dependencies. + + Also exposes other information about the inputs, like which inputs + do not provide symbols. +

+ +

+


+ +

+ + + + + + + + + + + + + + + +
+Nested Class Summary
+static classSortedDependencies.CircularDependencyException + +
+           
+static classSortedDependencies.MissingProvideException + +
+           
+  + + + + + + + + + + +
+Constructor Summary
SortedDependencies(List<INPUT> inputs) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ List<INPUT>getDependenciesOf(List<INPUT> roots, + boolean sorted) + +
+          Gets all the dependencies of the given roots.
+ INPUTgetInputProviding(String symbol) + +
+          Return the input that gives us the given symbol.
+ List<INPUT>getInputsWithoutProvides() + +
+           
+ List<INPUT>getSortedDependenciesOf(List<INPUT> roots) + +
+          Gets all the dependencies of the given roots.
+ List<INPUT>getSortedList() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SortedDependencies

+
+public SortedDependencies(List<INPUT> inputs)
+                   throws SortedDependencies.CircularDependencyException
+
+
+ +
Throws: +
SortedDependencies.CircularDependencyException
+
+ + + + + + + + +
+Method Detail
+ +

+getInputProviding

+
+public INPUT getInputProviding(String symbol)
+                                               throws SortedDependencies.MissingProvideException
+
+
Return the input that gives us the given symbol. +

+

+ +
Throws: +
SortedDependencies.MissingProvideException - An exception if there is no + input for this symbol.
+
+
+
+ +

+getSortedList

+
+public List<INPUT> getSortedList()
+
+
+
+
+
+
+ +

+getSortedDependenciesOf

+
+public List<INPUT> getSortedDependenciesOf(List<INPUT> roots)
+
+
Gets all the dependencies of the given roots. The inputs must be returned + in a stable order. In other words, if A comes before B, and A does not + transitively depend on B, then A must also come before B in the returned + list. +

+

+
+
+
+
+ +

+getDependenciesOf

+
+public List<INPUT> getDependenciesOf(List<INPUT> roots,
+                                     boolean sorted)
+
+
Gets all the dependencies of the given roots. The inputs must be returned + in a stable order. In other words, if A comes before B, and A does not + transitively depend on B, then A must also come before B in the returned + list. +

+

+
Parameters:
sorted - If true, get them in topologically sorted order. If false, + get them in the original order they were passed to the compiler.
+
+
+
+ +

+getInputsWithoutProvides

+
+public List<INPUT> getInputsWithoutProvides()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/package-frame.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/package-frame.html new file mode 100644 index 0000000..8a202e8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/package-frame.html @@ -0,0 +1,70 @@ + + + + + +com.google.javascript.jscomp.deps (Compiler) + + + + + + + + + + +com.google.javascript.jscomp.deps + + + + +
+Interfaces  + +
+DependencyInfo
+ + + + + + +
+Classes  + +
+DepsFileParser +
+DepsGenerator +
+JsFileLineParser +
+JsFileParser +
+JsFunctionParser +
+JsFunctionParser.SymbolInfo +
+PathUtil +
+SimpleDependencyInfo +
+SortedDependencies
+ + + + + + +
+Exceptions  + +
+SortedDependencies.CircularDependencyException +
+SortedDependencies.MissingProvideException
+ + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/package-summary.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/package-summary.html new file mode 100644 index 0000000..705d408 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/package-summary.html @@ -0,0 +1,250 @@ + + + + + +com.google.javascript.jscomp.deps (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+

+Package com.google.javascript.jscomp.deps +

+Analyzes information about dependencies between files. +

+See: +
+          Description +

+ + + + + + + + + +
+Interface Summary
DependencyInfoA data structure for JS dependency information for a single .js file.
+  + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Class Summary
DepsFileParserA parser that can extract dependency information from existing deps.js files.
DepsGeneratorGenerates deps.js files by scanning javascript files for + calls to goog.provide(), goog.require() and goog.addDependency().
JsFileLineParserBase class for classes that parse Javascript sources on a line-by-line basis.
JsFileParserA parser that can extract goog.require() and goog.provide() dependency + information from a .js file.
JsFunctionParserA parser that can extract dependency information from a .js file.
JsFunctionParser.SymbolInfo 
PathUtilUtility methods for manipulation of UNIX-like paths.
SimpleDependencyInfoA class to hold JS dependency information for a single .js file.
SortedDependencies<INPUT extends DependencyInfo>A sorted list of inputs with dependency information.
+  + +

+ + + + + + + + + + + + + +
+Exception Summary
SortedDependencies.CircularDependencyException 
SortedDependencies.MissingProvideException 
+  + +

+

+Package com.google.javascript.jscomp.deps Description +

+ +

+Analyzes information about dependencies between files. +

+ +

+

+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/package-tree.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/package-tree.html new file mode 100644 index 0000000..7c33d4d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/deps/package-tree.html @@ -0,0 +1,178 @@ + + + + + +com.google.javascript.jscomp.deps Class Hierarchy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Hierarchy For Package com.google.javascript.jscomp.deps +

+
+
+
Package Hierarchies:
All Packages
+
+

+Class Hierarchy +

+ +

+Interface Hierarchy +

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/AdjacencyGraph.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/AdjacencyGraph.html new file mode 100644 index 0000000..34a901b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/AdjacencyGraph.html @@ -0,0 +1,322 @@ + + + + + +AdjacencyGraph (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Interface AdjacencyGraph<N,E>

+
+
Type Parameters:
N - Value type that the graph node stores.
E - Value type that the graph edge stores.
+
+
All Known Implementing Classes:
DiGraph, Graph, LinkedDirectedGraph, LinkedUndirectedGraph, UndiGraph
+
+
+
+
public interface AdjacencyGraph<N,E>
+ + +

+A minimal graph interface. Provided is add nodes to the graph, adjacency + calculation between a SubGraph and a GraphNode, and adding node annotations. + +

For a more extensive interface, see Graph. +

+ +

+

+
See Also:
Graph
+
+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidclearNodeAnnotations() + +
+          Makes each node's annotation null.
+ GraphNode<N,E>getNode(N value) + +
+          Gets a node from the graph given a value.
+ Collection<GraphNode<N,E>>getNodes() + +
+          Gets an immutable list of all nodes.
+ intgetWeight(N value) + +
+          Returns a weight for the given value to be used in ordering nodes, e.g.
+ SubGraph<N,E>newSubGraph() + +
+          Returns an empty SubGraph for this Graph.
+  +

+ + + + + + + + +
+Method Detail
+ +

+getNodes

+
+Collection<GraphNode<N,E>> getNodes()
+
+
Gets an immutable list of all nodes. +

+

+
+
+
+
+ +

+getNode

+
+GraphNode<N,E> getNode(N value)
+
+
Gets a node from the graph given a value. Values equality are compared + using Object.equals. +

+

+
Parameters:
value - The node's value. +
Returns:
The corresponding node in the graph, null if there value has no + corresponding node.
+
+
+
+ +

+newSubGraph

+
+SubGraph<N,E> newSubGraph()
+
+
Returns an empty SubGraph for this Graph. +

+

+
+
+
+
+ +

+clearNodeAnnotations

+
+void clearNodeAnnotations()
+
+
Makes each node's annotation null. +

+

+
+
+
+
+ +

+getWeight

+
+int getWeight(N value)
+
+
Returns a weight for the given value to be used in ordering nodes, e.g. + in GraphColoring. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/Annotatable.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/Annotatable.html new file mode 100644 index 0000000..a569611 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/Annotatable.html @@ -0,0 +1,259 @@ + + + + + +Annotatable (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Interface Annotatable

+
+
All Known Subinterfaces:
DiGraph.DiGraphEdge<N,E>, DiGraph.DiGraphNode<N,E>, Graph.GraphEdge<N,E>, GraphNode<N,E>, UndiGraph.UndiGraphEdge<N,E>, UndiGraph.UndiGraphNode<N,E>
+
+
+
+
public interface Annotatable
+ + +

+Object that has an annotation. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Method Summary
+ + + + + +
+<A extends Annotation> +
+A
+
getAnnotation() + +
+          Retrieves a piece of information that has been annotated.
+ voidsetAnnotation(Annotation data) + +
+          Annotates a piece of information to the object.
+  +

+ + + + + + + + +
+Method Detail
+ +

+setAnnotation

+
+void setAnnotation(Annotation data)
+
+
Annotates a piece of information to the object. +

+

+
Parameters:
data - Information to be annotated.
+
+
+
+ +

+getAnnotation

+
+<A extends Annotation> A getAnnotation()
+
+
Retrieves a piece of information that has been annotated. +

+

+ +
Returns:
The annotation or null if the object has not + been annotated.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/Annotation.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/Annotation.html new file mode 100644 index 0000000..f9e3797 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/Annotation.html @@ -0,0 +1,188 @@ + + + + + +Annotation (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Interface Annotation

+
+
All Known Implementing Classes:
GraphColoring.Color
+
+
+
+
public interface Annotation
+ + +

+Information that can be annotated to a GraphNode or + Graph.GraphEdge. +

+ +

+


+ +

+ +

+ +


+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/DiGraph.DiGraphEdge.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/DiGraph.DiGraphEdge.html new file mode 100644 index 0000000..83699c0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/DiGraph.DiGraphEdge.html @@ -0,0 +1,317 @@ + + + + + +DiGraph.DiGraphEdge (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Interface DiGraph.DiGraphEdge<N,E>

+
+
Type Parameters:
N - Value type that the graph node stores.
E - Value type that the graph edge stores.
+
+
All Superinterfaces:
Annotatable, Graph.GraphEdge<N,E>
+
+
+
Enclosing class:
DiGraph<N,E>
+
+
+
+
public static interface DiGraph.DiGraphEdge<N,E>
extends Graph.GraphEdge<N,E>
+ + +

+A generic directed graph edge. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ DiGraph.DiGraphNode<N,E>getDestination() + +
+           
+ DiGraph.DiGraphNode<N,E>getSource() + +
+           
+ voidsetDestination(DiGraph.DiGraphNode<N,E> node) + +
+           
+ voidsetSource(DiGraph.DiGraphNode<N,E> node) + +
+           
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.graph.Graph.GraphEdge
getNodeA, getNodeB, getValue
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.graph.Annotatable
getAnnotation, setAnnotation
+  +

+ + + + + + + + +
+Method Detail
+ +

+getSource

+
+DiGraph.DiGraphNode<N,E> getSource()
+
+
+
+
+
+
+
+
+
+ +

+getDestination

+
+DiGraph.DiGraphNode<N,E> getDestination()
+
+
+
+
+
+
+
+
+
+ +

+setSource

+
+void setSource(DiGraph.DiGraphNode<N,E> node)
+
+
+
+
+
+
+
+
+
+ +

+setDestination

+
+void setDestination(DiGraph.DiGraphNode<N,E> node)
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/DiGraph.DiGraphNode.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/DiGraph.DiGraphNode.html new file mode 100644 index 0000000..8613ab3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/DiGraph.DiGraphNode.html @@ -0,0 +1,273 @@ + + + + + +DiGraph.DiGraphNode (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Interface DiGraph.DiGraphNode<N,E>

+
+
Type Parameters:
N - Value type that the graph node stores.
E - Value type that the graph edge stores.
+
+
All Superinterfaces:
Annotatable, GraphNode<N,E>
+
+
+
Enclosing class:
DiGraph<N,E>
+
+
+
+
public static interface DiGraph.DiGraphNode<N,E>
extends GraphNode<N,E>
+ + +

+A generic directed graph node. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Method Summary
+ List<DiGraph.DiGraphEdge<N,E>>getInEdges() + +
+           
+ List<DiGraph.DiGraphEdge<N,E>>getOutEdges() + +
+           
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.graph.GraphNode
getValue
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.graph.Annotatable
getAnnotation, setAnnotation
+  +

+ + + + + + + + +
+Method Detail
+ +

+getOutEdges

+
+List<DiGraph.DiGraphEdge<N,E>> getOutEdges()
+
+
+
+
+
+
+
+
+
+ +

+getInEdges

+
+List<DiGraph.DiGraphEdge<N,E>> getInEdges()
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/DiGraph.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/DiGraph.html new file mode 100644 index 0000000..e0ff33c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/DiGraph.html @@ -0,0 +1,643 @@ + + + + + +DiGraph (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Class DiGraph<N,E>

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.graph.Graph<N,E>
+      extended by com.google.javascript.jscomp.graph.DiGraph<N,E>
+
+
+
Type Parameters:
N - Value type that the graph node stores.
E - Value type that the graph edge stores.
+
+
All Implemented Interfaces:
AdjacencyGraph<N,E>
+
+
+
Direct Known Subclasses:
LinkedDirectedGraph
+
+
+
+
public abstract class DiGraph<N,E>
extends Graph<N,E>
+ + +

+A generic directed graph. +

+ +

+


+ +

+ + + + + + + + + + + + + + + +
+Nested Class Summary
+static interfaceDiGraph.DiGraphEdge<N,E> + +
+          A generic directed graph edge.
+static interfaceDiGraph.DiGraphNode<N,E> + +
+          A generic directed graph node.
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.jscomp.graph.Graph
Graph.GraphEdge<N,E>
+  + + + + + + + + + + + +
+Constructor Summary
DiGraph() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+abstract  DiGraph.DiGraphNode<N,E>createDirectedGraphNode(N nodeValue) + +
+           
+abstract  voiddisconnectInDirection(N n1, + N n2) + +
+          Disconnects all edges from n1 to n2.
+abstract  List<DiGraph.DiGraphEdge<N,E>>getDirectedGraphEdges(N n1, + N n2) + +
+           
+abstract  DiGraph.DiGraphNode<N,E>getDirectedGraphNode(N nodeValue) + +
+           
+abstract  Iterable<DiGraph.DiGraphNode<N,E>>getDirectedGraphNodes() + +
+          Gets an immutable iterable over all the nodes in the graph.
+abstract  List<DiGraph.DiGraphNode<N,E>>getDirectedPredNodes(DiGraph.DiGraphNode<N,E> n) + +
+           
+abstract  List<DiGraph.DiGraphNode<N,E>>getDirectedPredNodes(N nodeValue) + +
+           
+abstract  List<DiGraph.DiGraphNode<N,E>>getDirectedSuccNodes(DiGraph.DiGraphNode<N,E> n) + +
+           
+abstract  List<DiGraph.DiGraphNode<N,E>>getDirectedSuccNodes(N nodeValue) + +
+           
+abstract  List<DiGraph.DiGraphEdge<N,E>>getInEdges(N nodeValue) + +
+          Gets an immutable list of in edges of the given node.
+abstract  List<DiGraph.DiGraphEdge<N,E>>getOutEdges(N nodeValue) + +
+          Gets an immutable list of out edges of the given node.
+ booleanisConnected(N n1, + E e, + N n2) + +
+          Checks whether two nodes in the graph are connected by the given + edge type.
+ booleanisConnected(N n1, + N n2) + +
+          Checks whether two nodes in the graph are connected.
+abstract  booleanisConnectedInDirection(N n1, + E edgeValue, + N n2) + +
+          Checks whether two nodes in the graph are connected via a directed edge + with the given value.
+abstract  booleanisConnectedInDirection(N n1, + N n2) + +
+          Checks whether two nodes in the graph are connected via a directed edge.
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.graph.Graph
clearEdgeAnnotations, clearNodeAnnotations, connect, connectIfNotFound, createNode, disconnect, getEdges, getEdges, getFirstEdge, getNeighborNodes, getNeighborNodesIterator, getNodeDegree, getNodes, getWeight, hasNode, popEdgeAnnotations, popNodeAnnotations, pushEdgeAnnotations, pushNodeAnnotations
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.graph.AdjacencyGraph
getNode, newSubGraph
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+DiGraph

+
+public DiGraph()
+
+
+ + + + + + + + +
+Method Detail
+ +

+getDirectedGraphNodes

+
+public abstract Iterable<DiGraph.DiGraphNode<N,E>> getDirectedGraphNodes()
+
+
Gets an immutable iterable over all the nodes in the graph. +

+

+
+
+
+
+ +

+getOutEdges

+
+public abstract List<DiGraph.DiGraphEdge<N,E>> getOutEdges(N nodeValue)
+
+
Gets an immutable list of out edges of the given node. +

+

+
+
+
+
+ +

+getInEdges

+
+public abstract List<DiGraph.DiGraphEdge<N,E>> getInEdges(N nodeValue)
+
+
Gets an immutable list of in edges of the given node. +

+

+
+
+
+
+ +

+getDirectedPredNodes

+
+public abstract List<DiGraph.DiGraphNode<N,E>> getDirectedPredNodes(DiGraph.DiGraphNode<N,E> n)
+
+
+
+
+
+
+ +

+getDirectedSuccNodes

+
+public abstract List<DiGraph.DiGraphNode<N,E>> getDirectedSuccNodes(DiGraph.DiGraphNode<N,E> n)
+
+
+
+
+
+
+ +

+getDirectedPredNodes

+
+public abstract List<DiGraph.DiGraphNode<N,E>> getDirectedPredNodes(N nodeValue)
+
+
+
+
+
+
+ +

+getDirectedSuccNodes

+
+public abstract List<DiGraph.DiGraphNode<N,E>> getDirectedSuccNodes(N nodeValue)
+
+
+
+
+
+
+ +

+createDirectedGraphNode

+
+public abstract DiGraph.DiGraphNode<N,E> createDirectedGraphNode(N nodeValue)
+
+
+
+
+
+
+ +

+getDirectedGraphNode

+
+public abstract DiGraph.DiGraphNode<N,E> getDirectedGraphNode(N nodeValue)
+
+
+
+
+
+
+ +

+getDirectedGraphEdges

+
+public abstract List<DiGraph.DiGraphEdge<N,E>> getDirectedGraphEdges(N n1,
+                                                                     N n2)
+
+
+
+
+
+
+ +

+disconnectInDirection

+
+public abstract void disconnectInDirection(N n1,
+                                           N n2)
+
+
Disconnects all edges from n1 to n2. +

+

+
Parameters:
n1 - Source node.
n2 - Destination node.
+
+
+
+ +

+isConnectedInDirection

+
+public abstract boolean isConnectedInDirection(N n1,
+                                               N n2)
+
+
Checks whether two nodes in the graph are connected via a directed edge. +

+

+
Parameters:
n1 - Node 1.
n2 - Node 2. +
Returns:
true if the graph contains edge from n1 to n2.
+
+
+
+ +

+isConnectedInDirection

+
+public abstract boolean isConnectedInDirection(N n1,
+                                               E edgeValue,
+                                               N n2)
+
+
Checks whether two nodes in the graph are connected via a directed edge + with the given value. +

+

+
Parameters:
n1 - Node 1.
edgeValue - edge value tag
n2 - Node 2. +
Returns:
true if the edge exists.
+
+
+
+ +

+isConnected

+
+public boolean isConnected(N n1,
+                           N n2)
+
+
Description copied from class: Graph
+
Checks whether two nodes in the graph are connected. +

+

+
Specified by:
isConnected in class Graph<N,E>
+
+
+
Parameters:
n1 - Node 1.
n2 - Node 2. +
Returns:
true if the two nodes are connected.
+
+
+
+ +

+isConnected

+
+public boolean isConnected(N n1,
+                           E e,
+                           N n2)
+
+
Description copied from class: Graph
+
Checks whether two nodes in the graph are connected by the given + edge type. +

+

+
Specified by:
isConnected in class Graph<N,E>
+
+
+
Parameters:
n1 - Node 1.
e - The edge type.
n2 - Node 2.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/FixedPointGraphTraversal.EdgeCallback.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/FixedPointGraphTraversal.EdgeCallback.html new file mode 100644 index 0000000..3a1b55e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/FixedPointGraphTraversal.EdgeCallback.html @@ -0,0 +1,235 @@ + + + + + +FixedPointGraphTraversal.EdgeCallback (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Interface FixedPointGraphTraversal.EdgeCallback<Node,Edge>

+
+
All Known Implementing Classes:
GraphReachability
+
+
+
Enclosing class:
FixedPointGraphTraversal<N,E>
+
+
+
+
public static interface FixedPointGraphTraversal.EdgeCallback<Node,Edge>
+ + +

+


+ +

+ + + + + + + + + + + + +
+Method Summary
+ booleantraverseEdge(Node source, + Edge e, + Node destination) + +
+          Update the state of the destination node when the given edge + is traversed.
+  +

+ + + + + + + + +
+Method Detail
+ +

+traverseEdge

+
+boolean traverseEdge(Node source,
+                     Edge e,
+                     Node destination)
+
+
Update the state of the destination node when the given edge + is traversed. For the fixed-point computation to work, only the + destination node may be modified. The source node and the edge must + not be modified. +

+

+
Parameters:
source - The start node.
e - The edge.
destination - The end node. +
Returns:
Whether the state of the destination node changed.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/FixedPointGraphTraversal.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/FixedPointGraphTraversal.html new file mode 100644 index 0000000..c5c1f79 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/FixedPointGraphTraversal.html @@ -0,0 +1,408 @@ + + + + + +FixedPointGraphTraversal (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Class FixedPointGraphTraversal<N,E>

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.graph.FixedPointGraphTraversal<N,E>
+
+
+
Type Parameters:
N - Value type that the graph node stores.
E - Value type that the graph edge stores.
+
+
+
public final class FixedPointGraphTraversal<N,E>
extends Object
+ + +

+A utility class for doing fixed-point computations. We traverse + the edges over the given directed graph until the graph reaches + a steady state. +

+ +

+


+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static interfaceFixedPointGraphTraversal.EdgeCallback<Node,Edge> + +
+           
+ + + + + + + + + + +
+Field Summary
+static StringNON_HALTING_ERROR_MSG + +
+           
+  + + + + + + + + + + +
+Constructor Summary
FixedPointGraphTraversal(FixedPointGraphTraversal.EdgeCallback<N,E> callback) + +
+          Create a new traversal.
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidcomputeFixedPoint(DiGraph<N,E> graph) + +
+          Compute a fixed point for the given graph.
+ voidcomputeFixedPoint(DiGraph<N,E> graph, + N entry) + +
+          Compute a fixed point for the given graph, entering from the given node.
+ voidcomputeFixedPoint(DiGraph<N,E> graph, + Set<N> entrySet) + +
+          Compute a fixed point for the given graph, entering from the given nodes.
+static + + + + +
+<NODE,EDGE> +
+FixedPointGraphTraversal<NODE,EDGE>
+
newTraversal(FixedPointGraphTraversal.EdgeCallback<NODE,EDGE> callback) + +
+          Helper method for creating new traversals.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+NON_HALTING_ERROR_MSG

+
+public static final String NON_HALTING_ERROR_MSG
+
+
+
See Also:
Constant Field Values
+
+ + + + + + + + +
+Constructor Detail
+ +

+FixedPointGraphTraversal

+
+public FixedPointGraphTraversal(FixedPointGraphTraversal.EdgeCallback<N,E> callback)
+
+
Create a new traversal. +

+

+
Parameters:
callback - A callback for updating the state of the graph each + time an edge is traversed.
+
+ + + + + + + + +
+Method Detail
+ +

+newTraversal

+
+public static <NODE,EDGE> FixedPointGraphTraversal<NODE,EDGE> newTraversal(FixedPointGraphTraversal.EdgeCallback<NODE,EDGE> callback)
+
+
Helper method for creating new traversals. +

+

+
+
+
+
+ +

+computeFixedPoint

+
+public void computeFixedPoint(DiGraph<N,E> graph)
+
+
Compute a fixed point for the given graph. +

+

+
Parameters:
graph - The graph to traverse.
+
+
+
+ +

+computeFixedPoint

+
+public void computeFixedPoint(DiGraph<N,E> graph,
+                              N entry)
+
+
Compute a fixed point for the given graph, entering from the given node. +

+

+
Parameters:
graph - The graph to traverse.
entry - The node to begin traversing from.
+
+
+
+ +

+computeFixedPoint

+
+public void computeFixedPoint(DiGraph<N,E> graph,
+                              Set<N> entrySet)
+
+
Compute a fixed point for the given graph, entering from the given nodes. +

+

+
Parameters:
graph - The graph to traverse.
entrySet - The nodes to begin traversing from.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/Graph.GraphEdge.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/Graph.GraphEdge.html new file mode 100644 index 0000000..4d662ff --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/Graph.GraphEdge.html @@ -0,0 +1,292 @@ + + + + + +Graph.GraphEdge (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Interface Graph.GraphEdge<N,E>

+
+
Type Parameters:
N - Value type that the graph node stores.
E - Value type that the graph edge stores.
+
+
All Superinterfaces:
Annotatable
+
+
+
All Known Subinterfaces:
DiGraph.DiGraphEdge<N,E>, UndiGraph.UndiGraphEdge<N,E>
+
+
+
Enclosing class:
Graph<N,E>
+
+
+
+
public static interface Graph.GraphEdge<N,E>
extends Annotatable
+ + +

+A generic edge. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ GraphNode<N,E>getNodeA() + +
+           
+ GraphNode<N,E>getNodeB() + +
+           
+ EgetValue() + +
+          Retrieves the edge's value.
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.graph.Annotatable
getAnnotation, setAnnotation
+  +

+ + + + + + + + +
+Method Detail
+ +

+getValue

+
+E getValue()
+
+
Retrieves the edge's value. +

+

+
+
+
+ +
Returns:
The value.
+
+
+
+ +

+getNodeA

+
+GraphNode<N,E> getNodeA()
+
+
+
+
+
+
+
+
+
+ +

+getNodeB

+
+GraphNode<N,E> getNodeB()
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/Graph.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/Graph.html new file mode 100644 index 0000000..59a4348 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/Graph.html @@ -0,0 +1,852 @@ + + + + + +Graph (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Class Graph<N,E>

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.graph.Graph<N,E>
+
+
+
Type Parameters:
N - Value type that the graph node stores.
E - Value type that the graph edge stores.
+
+
All Implemented Interfaces:
AdjacencyGraph<N,E>
+
+
+
Direct Known Subclasses:
DiGraph, UndiGraph
+
+
+
+
public abstract class Graph<N,E>
extends Object
implements AdjacencyGraph<N,E>
+ + +

+The base generic class for graph-like data structure and algorithms in + the compiler. +

+ Nodes and edges in the graph can store a piece of data that this graph is + used to represent. For example, a variable interference graph might store a + variable in the node. This piece of data can be accessed with + GraphNode.getValue() and Graph.GraphEdge.getValue(). +

+ Algorithms and analysis can annotate information on the nodes and edges + using GraphNode.getValue() and Graph.GraphEdge.getValue(). For example, + a graph coloring algorithm can store the color as an annotation. If multiple + analyses are required, it is up to the user of the analysis to save the + annotated solution between passes. +

+ We implemented our own graph data structure (as opposed to using + com.google.common.graph) for two reasons. First, aside from + the node's label value, we would like to annotate information on the nodes + and edges. Using a map to annotate would introduce too much overhead during + fix point analysis. Also, com.google.common.graph does not + support labeling of edges. Secondly, not using another external package would + limit our dependencies. +

+ TODO(user): All functionality for removing nodes and edges. +

+ +

+


+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static interfaceGraph.GraphEdge<N,E> + +
+          A generic edge.
+  + + + + + + + + + + +
+Constructor Summary
Graph() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidclearEdgeAnnotations() + +
+          Makes each edge's annotation null.
+ voidclearNodeAnnotations() + +
+          Makes each node's annotation null.
+abstract  voidconnect(N n1, + E edge, + N n2) + +
+          Connects two nodes in the graph with an edge.
+ voidconnectIfNotFound(N n1, + E edge, + N n2) + +
+          Connects two nodes in the graph with an edge if such edge does not already + exists between the nodes.
+abstract  GraphNode<N,E>createNode(N value) + +
+          Gets a node from the graph given a value.
+abstract  voiddisconnect(N n1, + N n2) + +
+          Disconnects two nodes in the graph by removing all edges between them.
+abstract  List<Graph.GraphEdge<N,E>>getEdges() + +
+          Gets an immutable list of all edges.
+abstract  List<Graph.GraphEdge<N,E>>getEdges(N n1, + N n2) + +
+          Retrieves an edge from the graph.
+abstract  Graph.GraphEdge<N,E>getFirstEdge(N n1, + N n2) + +
+          Retrieves any edge from the graph.
+abstract  List<GraphNode<N,E>>getNeighborNodes(N value) + +
+          Gets the neighboring nodes.
+abstract  Iterator<GraphNode<N,E>>getNeighborNodesIterator(N value) + +
+           
+abstract  intgetNodeDegree(N value) + +
+          Gets the degree of a node.
+abstract  Collection<GraphNode<N,E>>getNodes() + +
+          Gets an immutable list of all nodes.
+ intgetWeight(N value) + +
+          Returns a weight for the given value to be used in ordering nodes, e.g.
+ booleanhasNode(N n) + +
+          Checks whether the node exists in the graph (createNode(Object) + has been called with that value).
+abstract  booleanisConnected(N n1, + E e, + N n2) + +
+          Checks whether two nodes in the graph are connected by the given + edge type.
+abstract  booleanisConnected(N n1, + N n2) + +
+          Checks whether two nodes in the graph are connected.
+ voidpopEdgeAnnotations() + +
+          Restores edges' annotation values to state before last + pushEdgeAnnotations().
+ voidpopNodeAnnotations() + +
+          Restores nodes' annotation values to state before last + pushNodeAnnotations().
+ voidpushEdgeAnnotations() + +
+          Pushes edges' annotation values.
+ voidpushNodeAnnotations() + +
+          Pushes nodes' annotation values.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.graph.AdjacencyGraph
getNode, newSubGraph
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+Graph

+
+public Graph()
+
+
+ + + + + + + + +
+Method Detail
+ +

+connect

+
+public abstract void connect(N n1,
+                             E edge,
+                             N n2)
+
+
Connects two nodes in the graph with an edge. +

+

+
+
+
+
Parameters:
n1 - First node.
edge - The edge.
n2 - Second node.
+
+
+
+ +

+disconnect

+
+public abstract void disconnect(N n1,
+                                N n2)
+
+
Disconnects two nodes in the graph by removing all edges between them. +

+

+
+
+
+
Parameters:
n1 - First node.
n2 - Second node.
+
+
+
+ +

+connectIfNotFound

+
+public final void connectIfNotFound(N n1,
+                                    E edge,
+                                    N n2)
+
+
Connects two nodes in the graph with an edge if such edge does not already + exists between the nodes. +

+

+
+
+
+
Parameters:
n1 - First node.
edge - The edge.
n2 - Second node.
+
+
+
+ +

+createNode

+
+public abstract GraphNode<N,E> createNode(N value)
+
+
Gets a node from the graph given a value. New nodes are created if that + value has not been assigned a graph node. Values equality are compared + using Object.equals. +

+

+
+
+
+
Parameters:
value - The node's value. +
Returns:
The corresponding node in the graph.
+
+
+
+ +

+getNodes

+
+public abstract Collection<GraphNode<N,E>> getNodes()
+
+
Gets an immutable list of all nodes. +

+

+
Specified by:
getNodes in interface AdjacencyGraph<N,E>
+
+
+
+
+
+
+ +

+getEdges

+
+public abstract List<Graph.GraphEdge<N,E>> getEdges()
+
+
Gets an immutable list of all edges. +

+

+
+
+
+
+
+
+
+ +

+getNodeDegree

+
+public abstract int getNodeDegree(N value)
+
+
Gets the degree of a node. +

+

+
+
+
+
Parameters:
value - The node's value. +
Returns:
The degree of the node.
+
+
+
+ +

+getWeight

+
+public int getWeight(N value)
+
+
Description copied from interface: AdjacencyGraph
+
Returns a weight for the given value to be used in ordering nodes, e.g. + in GraphColoring. +

+

+
Specified by:
getWeight in interface AdjacencyGraph<N,E>
+
+
+
+
+
+
+ +

+getNeighborNodes

+
+public abstract List<GraphNode<N,E>> getNeighborNodes(N value)
+
+
Gets the neighboring nodes. +

+

+
+
+
+
Parameters:
value - The node's value. +
Returns:
A list of neighboring nodes.
+
+
+
+ +

+getNeighborNodesIterator

+
+public abstract Iterator<GraphNode<N,E>> getNeighborNodesIterator(N value)
+
+
+
+
+
+
+
+
+
+ +

+getEdges

+
+public abstract List<Graph.GraphEdge<N,E>> getEdges(N n1,
+                                                    N n2)
+
+
Retrieves an edge from the graph. +

+

+
+
+
+
Parameters:
n1 - Node one.
n2 - Node two. +
Returns:
The list of edges between those two values in the graph.
+
+
+
+ +

+getFirstEdge

+
+public abstract Graph.GraphEdge<N,E> getFirstEdge(N n1,
+                                                  N n2)
+
+
Retrieves any edge from the graph. +

+

+
+
+
+
Parameters:
n1 - Node one.
n2 - Node two. +
Returns:
The first edges between those two values in the graph. null if + there are none.
+
+
+
+ +

+hasNode

+
+public final boolean hasNode(N n)
+
+
Checks whether the node exists in the graph (createNode(Object) + has been called with that value). +

+

+
+
+
+
Parameters:
n - Node. +
Returns:
true if it exist.
+
+
+
+ +

+isConnected

+
+public abstract boolean isConnected(N n1,
+                                    N n2)
+
+
Checks whether two nodes in the graph are connected. +

+

+
+
+
+
Parameters:
n1 - Node 1.
n2 - Node 2. +
Returns:
true if the two nodes are connected.
+
+
+
+ +

+isConnected

+
+public abstract boolean isConnected(N n1,
+                                    E e,
+                                    N n2)
+
+
Checks whether two nodes in the graph are connected by the given + edge type. +

+

+
+
+
+
Parameters:
n1 - Node 1.
e - The edge type.
n2 - Node 2.
+
+
+
+ +

+clearNodeAnnotations

+
+public final void clearNodeAnnotations()
+
+
Description copied from interface: AdjacencyGraph
+
Makes each node's annotation null. +

+

+
Specified by:
clearNodeAnnotations in interface AdjacencyGraph<N,E>
+
+
+
+
+
+
+ +

+clearEdgeAnnotations

+
+public final void clearEdgeAnnotations()
+
+
Makes each edge's annotation null. +

+

+
+
+
+
+
+
+
+ +

+pushNodeAnnotations

+
+public final void pushNodeAnnotations()
+
+
Pushes nodes' annotation values. Restored with + popNodeAnnotations(). Nodes' annotation values are cleared. +

+

+
+
+
+
+
+
+
+ +

+popNodeAnnotations

+
+public final void popNodeAnnotations()
+
+
Restores nodes' annotation values to state before last + pushNodeAnnotations(). +

+

+
+
+
+
+
+
+
+ +

+pushEdgeAnnotations

+
+public final void pushEdgeAnnotations()
+
+
Pushes edges' annotation values. Restored with + popEdgeAnnotations(). Edges' annotation values are cleared. +

+

+
+
+
+
+
+
+
+ +

+popEdgeAnnotations

+
+public final void popEdgeAnnotations()
+
+
Restores edges' annotation values to state before last + pushEdgeAnnotations(). +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphColoring.Color.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphColoring.Color.html new file mode 100644 index 0000000..98e8430 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphColoring.Color.html @@ -0,0 +1,262 @@ + + + + + +GraphColoring.Color (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Class GraphColoring.Color

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.graph.GraphColoring.Color
+
+
+
All Implemented Interfaces:
Annotation
+
+
+
Enclosing class:
GraphColoring<N,E>
+
+
+
+
public static class GraphColoring.Color
extends Object
implements Annotation
+ + +

+


+ +

+ + + + + + + + + + + + + + + + +
+Method Summary
+ booleanequals(Object other) + +
+           
+ inthashCode() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+equals

+
+public boolean equals(Object other)
+
+
+
Overrides:
equals in class Object
+
+
+
+
+
+
+ +

+hashCode

+
+public int hashCode()
+
+
+
Overrides:
hashCode in class Object
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphColoring.GreedyGraphColoring.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphColoring.GreedyGraphColoring.html new file mode 100644 index 0000000..ab026f9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphColoring.GreedyGraphColoring.html @@ -0,0 +1,346 @@ + + + + + +GraphColoring.GreedyGraphColoring (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Class GraphColoring.GreedyGraphColoring<N,E>

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.graph.GraphColoring<N,E>
+      extended by com.google.javascript.jscomp.graph.GraphColoring.GreedyGraphColoring<N,E>
+
+
+
Enclosing class:
GraphColoring<N,E>
+
+
+
+
public static class GraphColoring.GreedyGraphColoring<N,E>
extends GraphColoring<N,E>
+ + +

+Greedily assign nodes with high degree unique colors. +

+ +

+


+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.jscomp.graph.GraphColoring
GraphColoring.Color, GraphColoring.GreedyGraphColoring<N,E>
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.jscomp.graph.GraphColoring
colorToNodeMap, graph
+  + + + + + + + + + + + + + +
+Constructor Summary
GraphColoring.GreedyGraphColoring(AdjacencyGraph<N,E> graph) + +
+           
GraphColoring.GreedyGraphColoring(AdjacencyGraph<N,E> graph, + Comparator<N> tieBreaker) + +
+           
+  + + + + + + + + + + + +
+Method Summary
+ intcolor() + +
+          Annotates the graph with GraphColoring.Color objects using + Annotatable.setAnnotation(Annotation).
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.graph.GraphColoring
getGraph, getPartitionSuperNode
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+GraphColoring.GreedyGraphColoring

+
+public GraphColoring.GreedyGraphColoring(AdjacencyGraph<N,E> graph)
+
+
+
+ +

+GraphColoring.GreedyGraphColoring

+
+public GraphColoring.GreedyGraphColoring(AdjacencyGraph<N,E> graph,
+                                         Comparator<N> tieBreaker)
+
+
+
Parameters:
tieBreaker - In case of a tie between two nodes of the same degree, + this comparator will determine which node should be colored first.
+
+ + + + + + + + +
+Method Detail
+ +

+color

+
+public int color()
+
+
Description copied from class: GraphColoring
+
Annotates the graph with GraphColoring.Color objects using + Annotatable.setAnnotation(Annotation). +

+

+
Specified by:
color in class GraphColoring<N,E>
+
+
+ +
Returns:
The number of unique colors need.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphColoring.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphColoring.html new file mode 100644 index 0000000..2ffbbd0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphColoring.html @@ -0,0 +1,410 @@ + + + + + +GraphColoring (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Class GraphColoring<N,E>

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.graph.GraphColoring<N,E>
+
+
+
Type Parameters:
N - Value type that the graph node stores.
E - Value type that the graph edge stores.
+
+
Direct Known Subclasses:
GraphColoring.GreedyGraphColoring
+
+
+
+
public abstract class GraphColoring<N,E>
extends Object
+ + +

+Annotates the graph with a color in a way that no connected node will have + the same color. Nodes of the same color cab then be partitioned together and + be represented by a super node. This class will merely annotate the nodes + with a color using Annotatable.setAnnotation(Annotation) and provide + a node to super node mapping with getPartitionSuperNode(Object). The + give graph itself will not be modified. + +

This algorithm is NOT deterministic by default. Passes that + requires deterministic output should provide a Comparator in the + constructor as a tie-breaker. This tie-break will be used when deciding + which node should be colored first when multiple nodes have the same degree. +

+ +

+


+ +

+ + + + + + + + + + + + + + + +
+Nested Class Summary
+static classGraphColoring.Color + +
+           
+static classGraphColoring.GreedyGraphColoring<N,E> + +
+          Greedily assign nodes with high degree unique colors.
+ + + + + + + + + + + + + + +
+Field Summary
+protected  N[]colorToNodeMap + +
+           
+protected  AdjacencyGraph<N,E>graph + +
+           
+  + + + + + + + + + + +
+Constructor Summary
GraphColoring(AdjacencyGraph<N,E> graph) + +
+           
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+abstract  intcolor() + +
+          Annotates the graph with GraphColoring.Color objects using + Annotatable.setAnnotation(Annotation).
+ AdjacencyGraph<N,E>getGraph() + +
+           
+ NgetPartitionSuperNode(N node) + +
+          Using the coloring as partitions, finds the node that represents that + partition as the super node.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+colorToNodeMap

+
+protected N[] colorToNodeMap
+
+
+
+
+
+ +

+graph

+
+protected final AdjacencyGraph<N,E> graph
+
+
+
+
+ + + + + + + + +
+Constructor Detail
+ +

+GraphColoring

+
+public GraphColoring(AdjacencyGraph<N,E> graph)
+
+
+ + + + + + + + +
+Method Detail
+ +

+color

+
+public abstract int color()
+
+
Annotates the graph with GraphColoring.Color objects using + Annotatable.setAnnotation(Annotation). +

+

+ +
Returns:
The number of unique colors need.
+
+
+
+ +

+getPartitionSuperNode

+
+public N getPartitionSuperNode(N node)
+
+
Using the coloring as partitions, finds the node that represents that + partition as the super node. The first to retrieve its partition will + become the super node. +

+

+
+
+
+
+ +

+getGraph

+
+public AdjacencyGraph<N,E> getGraph()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphNode.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphNode.html new file mode 100644 index 0000000..f5962f6 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphNode.html @@ -0,0 +1,245 @@ + + + + + +GraphNode (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Interface GraphNode<N,E>

+
+
Type Parameters:
N - Value type that the graph node stores.
E - Value type that the graph edge stores.
+
+
All Superinterfaces:
Annotatable
+
+
+
All Known Subinterfaces:
DiGraph.DiGraphNode<N,E>, UndiGraph.UndiGraphNode<N,E>
+
+
+
+
public interface GraphNode<N,E>
extends Annotatable
+ + +

+A generic node. +

+ +

+


+ +

+ + + + + + + + + + + + +
+Method Summary
+ NgetValue() + +
+          Retrieves the node's value.
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.graph.Annotatable
getAnnotation, setAnnotation
+  +

+ + + + + + + + +
+Method Detail
+ +

+getValue

+
+N getValue()
+
+
Retrieves the node's value. +

+

+
+
+
+ +
Returns:
The value.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphPruner.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphPruner.html new file mode 100644 index 0000000..d10aead --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphPruner.html @@ -0,0 +1,276 @@ + + + + + +GraphPruner (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Class GraphPruner<N,E>

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.graph.GraphPruner<N,E>
+
+
+
+
public class GraphPruner<N,E>
extends Object
+ + +

+Prunes a graph, creating a new graph with nodes removed. + + If a node is removed from the graph, any paths through that node + will be replaced with edges. In other words, if A and B are nodes + in the original graph and the pruned graph, then there exists a path + from A -> B in the original graph iff there's a path from A -> B + in the pruned graph. + + We do not make any guarantees about what edges are in the pruned graph. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
GraphPruner(DiGraph<N,E> graph) + +
+           
+  + + + + + + + + + + + +
+Method Summary
+ LinkedDirectedGraph<N,E>prune(com.google.common.base.Predicate<N> keep) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+GraphPruner

+
+public GraphPruner(DiGraph<N,E> graph)
+
+
+ + + + + + + + +
+Method Detail
+ +

+prune

+
+public LinkedDirectedGraph<N,E> prune(com.google.common.base.Predicate<N> keep)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphReachability.EdgeTuple.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphReachability.EdgeTuple.html new file mode 100644 index 0000000..619e8be --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphReachability.EdgeTuple.html @@ -0,0 +1,319 @@ + + + + + +GraphReachability.EdgeTuple (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Class GraphReachability.EdgeTuple<N,E>

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.graph.GraphReachability.EdgeTuple<N,E>
+
+
+
Enclosing class:
GraphReachability<N,E>
+
+
+
+
public static final class GraphReachability.EdgeTuple<N,E>
extends Object
+ + +

+Represents Source Node, Edge and Destination Node. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + +
+Field Summary
+ NdestNode + +
+           
+ Eedge + +
+           
+ NsourceNode + +
+           
+  + + + + + + + + + + +
+Constructor Summary
GraphReachability.EdgeTuple(N sourceNode, + E edge, + N destNode) + +
+           
+  + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+sourceNode

+
+public final N sourceNode
+
+
+
+
+
+ +

+edge

+
+public final E edge
+
+
+
+
+
+ +

+destNode

+
+public final N destNode
+
+
+
+
+ + + + + + + + +
+Constructor Detail
+ +

+GraphReachability.EdgeTuple

+
+public GraphReachability.EdgeTuple(N sourceNode,
+                                   E edge,
+                                   N destNode)
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphReachability.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphReachability.html new file mode 100644 index 0000000..9ebe64b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphReachability.html @@ -0,0 +1,407 @@ + + + + + +GraphReachability (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Class GraphReachability<N,E>

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.graph.GraphReachability<N,E>
+
+
+
All Implemented Interfaces:
FixedPointGraphTraversal.EdgeCallback<N,E>
+
+
+
+
public class GraphReachability<N,E>
extends Object
implements FixedPointGraphTraversal.EdgeCallback<N,E>
+ + +

+Computes all the reachable nodes. Upon execution of compute(Object), + the graph nodes will be annotated with REACHABLE if it is reachable + from the specified entry node. +

+ +

+

+
See Also:
Annotatable.getAnnotation()
+
+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classGraphReachability.EdgeTuple<N,E> + +
+          Represents Source Node, Edge and Destination Node.
+ + + + + + + + + + +
+Field Summary
+static AnnotationREACHABLE + +
+           
+  + + + + + + + + + + + + + +
+Constructor Summary
GraphReachability(DiGraph<N,E> graph) + +
+           
GraphReachability(DiGraph<N,E> graph, + com.google.common.base.Predicate<GraphReachability.EdgeTuple<N,E>> edgePredicate) + +
+           
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidcompute(N entry) + +
+           
+ voidrecompute(N reachableNode) + +
+           
+ booleantraverseEdge(N source, + E e, + N destination) + +
+          Update the state of the destination node when the given edge + is traversed.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+REACHABLE

+
+public static final Annotation REACHABLE
+
+
+
+
+ + + + + + + + +
+Constructor Detail
+ +

+GraphReachability

+
+public GraphReachability(DiGraph<N,E> graph)
+
+
+
+ +

+GraphReachability

+
+public GraphReachability(DiGraph<N,E> graph,
+                         com.google.common.base.Predicate<GraphReachability.EdgeTuple<N,E>> edgePredicate)
+
+
+
Parameters:
graph - The graph.
edgePredicate - Given the predecessor P of the a node S and the edge + coming from P to S, this predicate should return true if S is + reachable from P using the edge.
+
+ + + + + + + + +
+Method Detail
+ +

+compute

+
+public void compute(N entry)
+
+
+
+
+
+
+
+
+
+ +

+recompute

+
+public void recompute(N reachableNode)
+
+
+
+
+
+
+
+
+
+ +

+traverseEdge

+
+public boolean traverseEdge(N source,
+                            E e,
+                            N destination)
+
+
Description copied from interface: FixedPointGraphTraversal.EdgeCallback
+
Update the state of the destination node when the given edge + is traversed. For the fixed-point computation to work, only the + destination node may be modified. The source node and the edge must + not be modified. +

+

+
Specified by:
traverseEdge in interface FixedPointGraphTraversal.EdgeCallback<N,E>
+
+
+
Parameters:
source - The start node.
e - The edge.
destination - The end node. +
Returns:
Whether the state of the destination node changed.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphvizGraph.GraphvizEdge.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphvizGraph.GraphvizEdge.html new file mode 100644 index 0000000..2aef115 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphvizGraph.GraphvizEdge.html @@ -0,0 +1,296 @@ + + + + + +GraphvizGraph.GraphvizEdge (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Interface GraphvizGraph.GraphvizEdge

+
+
Enclosing interface:
GraphvizGraph
+
+
+
+
public static interface GraphvizGraph.GraphvizEdge
+ + +

+A Graphviz edge. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StringgetColor() + +
+          Retrieves color of the edge.
+ StringgetLabel() + +
+          Retrieves the label of the edge.
+ StringgetNode1Id() + +
+          Get the first node in the edge.
+ StringgetNode2Id() + +
+          Get the second node in the edge.
+  +

+ + + + + + + + +
+Method Detail
+ +

+getNode1Id

+
+String getNode1Id()
+
+
Get the first node in the edge. In a directed node, this will be the + source node. +

+

+ +
Returns:
First node in the edge.
+
+
+
+ +

+getNode2Id

+
+String getNode2Id()
+
+
Get the second node in the edge. In a directed node, this will be the + destination node. +

+

+ +
Returns:
First node in the edge.
+
+
+
+ +

+getColor

+
+String getColor()
+
+
Retrieves color of the edge. +

+

+ +
Returns:
The color of the edge.
+
+
+
+ +

+getLabel

+
+String getLabel()
+
+
Retrieves the label of the edge. +

+

+ +
Returns:
Label of the edge.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphvizGraph.GraphvizNode.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphvizGraph.GraphvizNode.html new file mode 100644 index 0000000..77b17e2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphvizGraph.GraphvizNode.html @@ -0,0 +1,272 @@ + + + + + +GraphvizGraph.GraphvizNode (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Interface GraphvizGraph.GraphvizNode

+
+
Enclosing interface:
GraphvizGraph
+
+
+
+
public static interface GraphvizGraph.GraphvizNode
+ + +

+A Graphviz node. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StringgetColor() + +
+          Retrieves color of the node.
+ StringgetId() + +
+          Retrieves the unique ID.
+ StringgetLabel() + +
+          Retrieves the label of the node.
+  +

+ + + + + + + + +
+Method Detail
+ +

+getId

+
+String getId()
+
+
Retrieves the unique ID. +

+

+ +
Returns:
A the unique ID of the node.
+
+
+
+ +

+getColor

+
+String getColor()
+
+
Retrieves color of the node. +

+

+ +
Returns:
The color of the node.
+
+
+
+ +

+getLabel

+
+String getLabel()
+
+
Retrieves the label of the node. +

+

+ +
Returns:
Label of the node.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphvizGraph.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphvizGraph.html new file mode 100644 index 0000000..f49d314 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/GraphvizGraph.html @@ -0,0 +1,323 @@ + + + + + +GraphvizGraph (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Interface GraphvizGraph

+
+
All Known Implementing Classes:
LinkedDirectedGraph, LinkedUndirectedGraph
+
+
+
+
public interface GraphvizGraph
+ + +

+A graph that can be dumped to a Graphviz DOT file. +

+ An object which can be visualized as a graph should implement this interface. + The DotFormatter.toDot function can be used to get a + visualization of the object for debugging purpose. +

+ +

+


+ +

+ + + + + + + + + + + + + + + +
+Nested Class Summary
+static interfaceGraphvizGraph.GraphvizEdge + +
+          A Graphviz edge.
+static interfaceGraphvizGraph.GraphvizNode + +
+          A Graphviz node.
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ List<GraphvizGraph.GraphvizEdge>getGraphvizEdges() + +
+          Retrieve a list of edges in the graph.
+ List<GraphvizGraph.GraphvizNode>getGraphvizNodes() + +
+          Retrieve a list of nodes in the graph.
+ StringgetName() + +
+          Name of the graph.
+ booleanisDirected() + +
+          Graph type.
+  +

+ + + + + + + + +
+Method Detail
+ +

+getName

+
+String getName()
+
+
Name of the graph. +

+

+ +
Returns:
Name of the graph.
+
+
+
+ +

+isDirected

+
+boolean isDirected()
+
+
Graph type. +

+

+ +
Returns:
True if the graph is a directed graph.
+
+
+
+ +

+getGraphvizNodes

+
+List<GraphvizGraph.GraphvizNode> getGraphvizNodes()
+
+
Retrieve a list of nodes in the graph. +

+

+ +
Returns:
A list of nodes in the graph.
+
+
+
+ +

+getGraphvizEdges

+
+List<GraphvizGraph.GraphvizEdge> getGraphvizEdges()
+
+
Retrieve a list of edges in the graph. +

+

+ +
Returns:
A list of edges in the graph.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/LinkedDirectedGraph.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/LinkedDirectedGraph.html new file mode 100644 index 0000000..638670e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/LinkedDirectedGraph.html @@ -0,0 +1,1235 @@ + + + + + +LinkedDirectedGraph (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Class LinkedDirectedGraph<N,E>

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.graph.Graph<N,E>
+      extended by com.google.javascript.jscomp.graph.DiGraph<N,E>
+          extended by com.google.javascript.jscomp.graph.LinkedDirectedGraph<N,E>
+
+
+
Type Parameters:
N - Value type that the graph node stores.
E - Value type that the graph edge stores.
+
+
All Implemented Interfaces:
AdjacencyGraph<N,E>, GraphvizGraph
+
+
+
+
public class LinkedDirectedGraph<N,E>
extends DiGraph<N,E>
implements GraphvizGraph
+ + +

+A directed graph using linked list within nodes to store edge information. +

+ This implementation favors directed graph operations inherited from + DirectedGraph. + Operations from Graph would tends to be slower. +

+ +

+


+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.jscomp.graph.DiGraph
DiGraph.DiGraphEdge<N,E>, DiGraph.DiGraphNode<N,E>
+  + + + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.jscomp.graph.Graph
Graph.GraphEdge<N,E>
+  + + + + + + + + +
Nested classes/interfaces inherited from interface com.google.javascript.jscomp.graph.GraphvizGraph
GraphvizGraph.GraphvizEdge, GraphvizGraph.GraphvizNode
+  + + + + + + + + + + + +
+Field Summary
+protected  Map<N,com.google.javascript.jscomp.graph.LinkedDirectedGraph.LinkedDirectedGraphNode<N,E>>nodes + +
+           
+  + + + + + + + + + + + +
+Constructor Summary
+protected LinkedDirectedGraph(boolean useNodeAnnotations, + boolean useEdgeAnnotations) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidconnect(N srcValue, + E edgeValue, + N destValue) + +
+          Connects two nodes in the graph with an edge.
+static + + + + +
+<N,E> LinkedDirectedGraph<N,E>
+
create() + +
+           
+ DiGraph.DiGraphNode<N,E>createDirectedGraphNode(N nodeValue) + +
+           
+ GraphNode<N,E>createNode(N value) + +
+          Gets a node from the graph given a value.
+static + + + + +
+<N,E> LinkedDirectedGraph<N,E>
+
createWithEdgeAnnotations() + +
+           
+static + + + + +
+<N,E> LinkedDirectedGraph<N,E>
+
createWithNodeAnnotations() + +
+           
+static + + + + +
+<N,E> LinkedDirectedGraph<N,E>
+
createWithoutAnnotations() + +
+           
+ voiddisconnect(N n1, + N n2) + +
+          Disconnects two nodes in the graph by removing all edges between them.
+ voiddisconnectInDirection(N srcValue, + N destValue) + +
+          Disconnects all edges from n1 to n2.
+ List<DiGraph.DiGraphEdge<N,E>>getDirectedGraphEdges(N n1, + N n2) + +
+           
+ DiGraph.DiGraphNode<N,E>getDirectedGraphNode(N nodeValue) + +
+           
+ Iterable<DiGraph.DiGraphNode<N,E>>getDirectedGraphNodes() + +
+          Gets an immutable iterable over all the nodes in the graph.
+ List<DiGraph.DiGraphNode<N,E>>getDirectedPredNodes(DiGraph.DiGraphNode<N,E> dNode) + +
+           
+ List<DiGraph.DiGraphNode<N,E>>getDirectedPredNodes(N nodeValue) + +
+           
+ List<DiGraph.DiGraphNode<N,E>>getDirectedSuccNodes(DiGraph.DiGraphNode<N,E> dNode) + +
+           
+ List<DiGraph.DiGraphNode<N,E>>getDirectedSuccNodes(N nodeValue) + +
+           
+ List<Graph.GraphEdge<N,E>>getEdges() + +
+          Gets an immutable list of all edges.
+ List<Graph.GraphEdge<N,E>>getEdges(N n1, + N n2) + +
+          Retrieves an edge from the graph.
+ Graph.GraphEdge<N,E>getFirstEdge(N n1, + N n2) + +
+          Retrieves any edge from the graph.
+ List<GraphvizGraph.GraphvizEdge>getGraphvizEdges() + +
+          Retrieve a list of edges in the graph.
+ List<GraphvizGraph.GraphvizNode>getGraphvizNodes() + +
+          Retrieve a list of nodes in the graph.
+ List<DiGraph.DiGraphEdge<N,E>>getInEdges(N nodeValue) + +
+          Gets an immutable list of in edges of the given node.
+ StringgetName() + +
+          Name of the graph.
+ List<GraphNode<N,E>>getNeighborNodes(DiGraph.DiGraphNode<N,E> node) + +
+           
+ List<GraphNode<N,E>>getNeighborNodes(N value) + +
+          Gets the neighboring nodes.
+ Iterator<GraphNode<N,E>>getNeighborNodesIterator(N value) + +
+           
+ GraphNode<N,E>getNode(N nodeValue) + +
+          Gets a node from the graph given a value.
+ intgetNodeDegree(N value) + +
+          Gets the degree of a node.
+ Collection<GraphNode<N,E>>getNodes() + +
+          Gets an immutable list of all nodes.
+ List<DiGraph.DiGraphEdge<N,E>>getOutEdges(N nodeValue) + +
+          Gets an immutable list of out edges of the given node.
+ booleanisConnectedInDirection(N n1, + E edgeValue, + N n2) + +
+          Checks whether two nodes in the graph are connected via a directed edge + with the given value.
+ booleanisConnectedInDirection(N n1, + N n2) + +
+          Checks whether two nodes in the graph are connected via a directed edge.
+ booleanisDirected() + +
+          Graph type.
+ SubGraph<N,E>newSubGraph() + +
+          Returns an empty SubGraph for this Graph.
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.graph.DiGraph
isConnected, isConnected
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.graph.Graph
clearEdgeAnnotations, clearNodeAnnotations, connectIfNotFound, getWeight, hasNode, popEdgeAnnotations, popNodeAnnotations, pushEdgeAnnotations, pushNodeAnnotations
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+nodes

+
+protected final Map<N,com.google.javascript.jscomp.graph.LinkedDirectedGraph.LinkedDirectedGraphNode<N,E>> nodes
+
+
+
+
+ + + + + + + + +
+Constructor Detail
+ +

+LinkedDirectedGraph

+
+protected LinkedDirectedGraph(boolean useNodeAnnotations,
+                              boolean useEdgeAnnotations)
+
+
+ + + + + + + + +
+Method Detail
+ +

+newSubGraph

+
+public SubGraph<N,E> newSubGraph()
+
+
Description copied from interface: AdjacencyGraph
+
Returns an empty SubGraph for this Graph. +

+

+
Specified by:
newSubGraph in interface AdjacencyGraph<N,E>
+
+
+
+
+
+
+ +

+createWithoutAnnotations

+
+public static <N,E> LinkedDirectedGraph<N,E> createWithoutAnnotations()
+
+
+
+
+
+
+
+
+
+ +

+createWithNodeAnnotations

+
+public static <N,E> LinkedDirectedGraph<N,E> createWithNodeAnnotations()
+
+
+
+
+
+
+
+
+
+ +

+createWithEdgeAnnotations

+
+public static <N,E> LinkedDirectedGraph<N,E> createWithEdgeAnnotations()
+
+
+
+
+
+
+
+
+
+ +

+create

+
+public static <N,E> LinkedDirectedGraph<N,E> create()
+
+
+
+
+
+
+
+
+
+ +

+connect

+
+public void connect(N srcValue,
+                    E edgeValue,
+                    N destValue)
+
+
Description copied from class: Graph
+
Connects two nodes in the graph with an edge. +

+

+
Specified by:
connect in class Graph<N,E>
+
+
+
Parameters:
srcValue - First node.
edgeValue - The edge.
destValue - Second node.
+
+
+
+ +

+disconnect

+
+public void disconnect(N n1,
+                       N n2)
+
+
Description copied from class: Graph
+
Disconnects two nodes in the graph by removing all edges between them. +

+

+
Specified by:
disconnect in class Graph<N,E>
+
+
+
Parameters:
n1 - First node.
n2 - Second node.
+
+
+
+ +

+disconnectInDirection

+
+public void disconnectInDirection(N srcValue,
+                                  N destValue)
+
+
Description copied from class: DiGraph
+
Disconnects all edges from n1 to n2. +

+

+
Specified by:
disconnectInDirection in class DiGraph<N,E>
+
+
+
Parameters:
srcValue - Source node.
destValue - Destination node.
+
+
+
+ +

+getDirectedGraphNodes

+
+public Iterable<DiGraph.DiGraphNode<N,E>> getDirectedGraphNodes()
+
+
Description copied from class: DiGraph
+
Gets an immutable iterable over all the nodes in the graph. +

+

+
Specified by:
getDirectedGraphNodes in class DiGraph<N,E>
+
+
+
+
+
+
+ +

+getDirectedGraphNode

+
+public DiGraph.DiGraphNode<N,E> getDirectedGraphNode(N nodeValue)
+
+
+
Specified by:
getDirectedGraphNode in class DiGraph<N,E>
+
+
+
+
+
+
+ +

+getNode

+
+public GraphNode<N,E> getNode(N nodeValue)
+
+
Description copied from interface: AdjacencyGraph
+
Gets a node from the graph given a value. Values equality are compared + using Object.equals. +

+

+
Specified by:
getNode in interface AdjacencyGraph<N,E>
+
+
+
Parameters:
nodeValue - The node's value. +
Returns:
The corresponding node in the graph, null if there value has no + corresponding node.
+
+
+
+ +

+getInEdges

+
+public List<DiGraph.DiGraphEdge<N,E>> getInEdges(N nodeValue)
+
+
Description copied from class: DiGraph
+
Gets an immutable list of in edges of the given node. +

+

+
Specified by:
getInEdges in class DiGraph<N,E>
+
+
+
+
+
+
+ +

+getOutEdges

+
+public List<DiGraph.DiGraphEdge<N,E>> getOutEdges(N nodeValue)
+
+
Description copied from class: DiGraph
+
Gets an immutable list of out edges of the given node. +

+

+
Specified by:
getOutEdges in class DiGraph<N,E>
+
+
+
+
+
+
+ +

+createDirectedGraphNode

+
+public DiGraph.DiGraphNode<N,E> createDirectedGraphNode(N nodeValue)
+
+
+
Specified by:
createDirectedGraphNode in class DiGraph<N,E>
+
+
+
+
+
+
+ +

+getEdges

+
+public List<Graph.GraphEdge<N,E>> getEdges(N n1,
+                                           N n2)
+
+
Description copied from class: Graph
+
Retrieves an edge from the graph. +

+

+
Specified by:
getEdges in class Graph<N,E>
+
+
+
Parameters:
n1 - Node one.
n2 - Node two. +
Returns:
The list of edges between those two values in the graph.
+
+
+
+ +

+getFirstEdge

+
+public Graph.GraphEdge<N,E> getFirstEdge(N n1,
+                                         N n2)
+
+
Description copied from class: Graph
+
Retrieves any edge from the graph. +

+

+
Specified by:
getFirstEdge in class Graph<N,E>
+
+
+
Parameters:
n1 - Node one.
n2 - Node two. +
Returns:
The first edges between those two values in the graph. null if + there are none.
+
+
+
+ +

+createNode

+
+public GraphNode<N,E> createNode(N value)
+
+
Description copied from class: Graph
+
Gets a node from the graph given a value. New nodes are created if that + value has not been assigned a graph node. Values equality are compared + using Object.equals. +

+

+
Specified by:
createNode in class Graph<N,E>
+
+
+
Parameters:
value - The node's value. +
Returns:
The corresponding node in the graph.
+
+
+
+ +

+getDirectedGraphEdges

+
+public List<DiGraph.DiGraphEdge<N,E>> getDirectedGraphEdges(N n1,
+                                                            N n2)
+
+
+
Specified by:
getDirectedGraphEdges in class DiGraph<N,E>
+
+
+
+
+
+
+ +

+isConnectedInDirection

+
+public boolean isConnectedInDirection(N n1,
+                                      N n2)
+
+
Description copied from class: DiGraph
+
Checks whether two nodes in the graph are connected via a directed edge. +

+

+
Specified by:
isConnectedInDirection in class DiGraph<N,E>
+
+
+
Parameters:
n1 - Node 1.
n2 - Node 2. +
Returns:
true if the graph contains edge from n1 to n2.
+
+
+
+ +

+isConnectedInDirection

+
+public boolean isConnectedInDirection(N n1,
+                                      E edgeValue,
+                                      N n2)
+
+
Description copied from class: DiGraph
+
Checks whether two nodes in the graph are connected via a directed edge + with the given value. +

+

+
Specified by:
isConnectedInDirection in class DiGraph<N,E>
+
+
+
Parameters:
n1 - Node 1.
edgeValue - edge value tag
n2 - Node 2. +
Returns:
true if the edge exists.
+
+
+
+ +

+getDirectedPredNodes

+
+public List<DiGraph.DiGraphNode<N,E>> getDirectedPredNodes(N nodeValue)
+
+
+
Specified by:
getDirectedPredNodes in class DiGraph<N,E>
+
+
+
+
+
+
+ +

+getDirectedSuccNodes

+
+public List<DiGraph.DiGraphNode<N,E>> getDirectedSuccNodes(N nodeValue)
+
+
+
Specified by:
getDirectedSuccNodes in class DiGraph<N,E>
+
+
+
+
+
+
+ +

+getDirectedPredNodes

+
+public List<DiGraph.DiGraphNode<N,E>> getDirectedPredNodes(DiGraph.DiGraphNode<N,E> dNode)
+
+
+
Specified by:
getDirectedPredNodes in class DiGraph<N,E>
+
+
+
+
+
+
+ +

+getDirectedSuccNodes

+
+public List<DiGraph.DiGraphNode<N,E>> getDirectedSuccNodes(DiGraph.DiGraphNode<N,E> dNode)
+
+
+
Specified by:
getDirectedSuccNodes in class DiGraph<N,E>
+
+
+
+
+
+
+ +

+getGraphvizEdges

+
+public List<GraphvizGraph.GraphvizEdge> getGraphvizEdges()
+
+
Description copied from interface: GraphvizGraph
+
Retrieve a list of edges in the graph. +

+

+
Specified by:
getGraphvizEdges in interface GraphvizGraph
+
+
+ +
Returns:
A list of edges in the graph.
+
+
+
+ +

+getGraphvizNodes

+
+public List<GraphvizGraph.GraphvizNode> getGraphvizNodes()
+
+
Description copied from interface: GraphvizGraph
+
Retrieve a list of nodes in the graph. +

+

+
Specified by:
getGraphvizNodes in interface GraphvizGraph
+
+
+ +
Returns:
A list of nodes in the graph.
+
+
+
+ +

+getName

+
+public String getName()
+
+
Description copied from interface: GraphvizGraph
+
Name of the graph. +

+

+
Specified by:
getName in interface GraphvizGraph
+
+
+ +
Returns:
Name of the graph.
+
+
+
+ +

+isDirected

+
+public boolean isDirected()
+
+
Description copied from interface: GraphvizGraph
+
Graph type. +

+

+
Specified by:
isDirected in interface GraphvizGraph
+
+
+ +
Returns:
True if the graph is a directed graph.
+
+
+
+ +

+getNodes

+
+public Collection<GraphNode<N,E>> getNodes()
+
+
Description copied from class: Graph
+
Gets an immutable list of all nodes. +

+

+
Specified by:
getNodes in interface AdjacencyGraph<N,E>
Specified by:
getNodes in class Graph<N,E>
+
+
+
+
+
+
+ +

+getNeighborNodes

+
+public List<GraphNode<N,E>> getNeighborNodes(N value)
+
+
Description copied from class: Graph
+
Gets the neighboring nodes. +

+

+
Specified by:
getNeighborNodes in class Graph<N,E>
+
+
+
Parameters:
value - The node's value. +
Returns:
A list of neighboring nodes.
+
+
+
+ +

+getNeighborNodes

+
+public List<GraphNode<N,E>> getNeighborNodes(DiGraph.DiGraphNode<N,E> node)
+
+
+
+
+
+
+
+
+
+ +

+getNeighborNodesIterator

+
+public Iterator<GraphNode<N,E>> getNeighborNodesIterator(N value)
+
+
+
Specified by:
getNeighborNodesIterator in class Graph<N,E>
+
+
+
+
+
+
+ +

+getEdges

+
+public List<Graph.GraphEdge<N,E>> getEdges()
+
+
Description copied from class: Graph
+
Gets an immutable list of all edges. +

+

+
Specified by:
getEdges in class Graph<N,E>
+
+
+
+
+
+
+ +

+getNodeDegree

+
+public int getNodeDegree(N value)
+
+
Description copied from class: Graph
+
Gets the degree of a node. +

+

+
Specified by:
getNodeDegree in class Graph<N,E>
+
+
+
Parameters:
value - The node's value. +
Returns:
The degree of the node.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/LinkedUndirectedGraph.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/LinkedUndirectedGraph.html new file mode 100644 index 0000000..f24f95e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/LinkedUndirectedGraph.html @@ -0,0 +1,1035 @@ + + + + + +LinkedUndirectedGraph (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Class LinkedUndirectedGraph<N,E>

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.graph.Graph<N,E>
+      extended by com.google.javascript.jscomp.graph.UndiGraph<N,E>
+          extended by com.google.javascript.jscomp.graph.LinkedUndirectedGraph<N,E>
+
+
+
Type Parameters:
N - Value type that the graph node stores.
E - Value type that the graph edge stores.
+
+
All Implemented Interfaces:
AdjacencyGraph<N,E>, GraphvizGraph
+
+
+
+
public class LinkedUndirectedGraph<N,E>
extends UndiGraph<N,E>
implements GraphvizGraph
+ + +

+An undirected graph using linked list within nodes to store edge + information. +

+ +

+


+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.jscomp.graph.UndiGraph
UndiGraph.UndiGraphEdge<N,E>, UndiGraph.UndiGraphNode<N,E>
+  + + + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.jscomp.graph.Graph
Graph.GraphEdge<N,E>
+  + + + + + + + + +
Nested classes/interfaces inherited from interface com.google.javascript.jscomp.graph.GraphvizGraph
GraphvizGraph.GraphvizEdge, GraphvizGraph.GraphvizNode
+  + + + + + + + + + + + +
+Field Summary
+protected  Map<N,com.google.javascript.jscomp.graph.LinkedUndirectedGraph.LinkedUndirectedGraphNode<N,E>>nodes + +
+           
+  + + + + + + + + + + + +
+Constructor Summary
+protected LinkedUndirectedGraph(boolean useNodeAnnotations, + boolean useEdgeAnnotations) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidconnect(N srcValue, + E edgeValue, + N destValue) + +
+          Connects two nodes in the graph with an edge.
+static + + + + +
+<N,E> LinkedUndirectedGraph<N,E>
+
create() + +
+           
+ GraphNode<N,E>createNode(N value) + +
+          Gets a node from the graph given a value.
+ UndiGraph.UndiGraphNode<N,E>createUndirectedGraphNode(N nodeValue) + +
+           
+static + + + + +
+<N,E> LinkedUndirectedGraph<N,E>
+
createWithEdgeAnnotations() + +
+           
+static + + + + +
+<N,E> LinkedUndirectedGraph<N,E>
+
createWithNodeAnnotations() + +
+           
+static + + + + +
+<N,E> LinkedUndirectedGraph<N,E>
+
createWithoutAnnotations() + +
+           
+ voiddisconnect(N srcValue, + N destValue) + +
+          Disconnects two nodes in the graph by removing all edges between them.
+ List<Graph.GraphEdge<N,E>>getEdges() + +
+          Gets an immutable list of all edges.
+ List<Graph.GraphEdge<N,E>>getEdges(N n1, + N n2) + +
+          Retrieves an edge from the graph.
+ Graph.GraphEdge<N,E>getFirstEdge(N n1, + N n2) + +
+          Retrieves any edge from the graph.
+ List<GraphvizGraph.GraphvizEdge>getGraphvizEdges() + +
+          Retrieve a list of edges in the graph.
+ List<GraphvizGraph.GraphvizNode>getGraphvizNodes() + +
+          Retrieve a list of nodes in the graph.
+ StringgetName() + +
+          Name of the graph.
+ List<GraphNode<N,E>>getNeighborNodes(N value) + +
+          Gets the neighboring nodes.
+ Iterator<GraphNode<N,E>>getNeighborNodesIterator(N value) + +
+           
+ GraphNode<N,E>getNode(N value) + +
+          Gets a node from the graph given a value.
+ intgetNodeDegree(N value) + +
+          Gets the degree of a node.
+ Collection<GraphNode<N,E>>getNodes() + +
+          Gets an immutable list of all nodes.
+ List<UndiGraph.UndiGraphEdge<N,E>>getUndirectedGraphEdges(N n1, + N n2) + +
+           
+ UndiGraph.UndiGraphNode<N,E>getUndirectedGraphNode(N nodeValue) + +
+           
+ Collection<UndiGraph.UndiGraphNode<N,E>>getUndirectedGraphNodes() + +
+          Gets an immutable collection of all the nodes in this graph.
+ booleanisConnected(N n1, + E e, + N n2) + +
+          Checks whether two nodes in the graph are connected by the given + edge type.
+ booleanisConnected(N n1, + N n2) + +
+          Checks whether two nodes in the graph are connected.
+ booleanisDirected() + +
+          Graph type.
+ SubGraph<N,E>newSubGraph() + +
+          Returns an empty SubGraph for this Graph.
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.graph.Graph
clearEdgeAnnotations, clearNodeAnnotations, connectIfNotFound, getWeight, hasNode, popEdgeAnnotations, popNodeAnnotations, pushEdgeAnnotations, pushNodeAnnotations
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+nodes

+
+protected final Map<N,com.google.javascript.jscomp.graph.LinkedUndirectedGraph.LinkedUndirectedGraphNode<N,E>> nodes
+
+
+
+
+ + + + + + + + +
+Constructor Detail
+ +

+LinkedUndirectedGraph

+
+protected LinkedUndirectedGraph(boolean useNodeAnnotations,
+                                boolean useEdgeAnnotations)
+
+
+ + + + + + + + +
+Method Detail
+ +

+newSubGraph

+
+public SubGraph<N,E> newSubGraph()
+
+
Description copied from interface: AdjacencyGraph
+
Returns an empty SubGraph for this Graph. +

+

+
Specified by:
newSubGraph in interface AdjacencyGraph<N,E>
+
+
+
+
+
+
+ +

+createWithoutAnnotations

+
+public static <N,E> LinkedUndirectedGraph<N,E> createWithoutAnnotations()
+
+
+
+
+
+
+
+
+
+ +

+createWithNodeAnnotations

+
+public static <N,E> LinkedUndirectedGraph<N,E> createWithNodeAnnotations()
+
+
+
+
+
+
+
+
+
+ +

+createWithEdgeAnnotations

+
+public static <N,E> LinkedUndirectedGraph<N,E> createWithEdgeAnnotations()
+
+
+
+
+
+
+
+
+
+ +

+create

+
+public static <N,E> LinkedUndirectedGraph<N,E> create()
+
+
+
+
+
+
+
+
+
+ +

+connect

+
+public void connect(N srcValue,
+                    E edgeValue,
+                    N destValue)
+
+
Description copied from class: Graph
+
Connects two nodes in the graph with an edge. +

+

+
Specified by:
connect in class Graph<N,E>
+
+
+
Parameters:
srcValue - First node.
edgeValue - The edge.
destValue - Second node.
+
+
+
+ +

+disconnect

+
+public void disconnect(N srcValue,
+                       N destValue)
+
+
Description copied from class: Graph
+
Disconnects two nodes in the graph by removing all edges between them. +

+

+
Specified by:
disconnect in class Graph<N,E>
+
+
+
Parameters:
srcValue - First node.
destValue - Second node.
+
+
+
+ +

+createUndirectedGraphNode

+
+public UndiGraph.UndiGraphNode<N,E> createUndirectedGraphNode(N nodeValue)
+
+
+
+
+
+
+
+
+
+ +

+getNeighborNodes

+
+public List<GraphNode<N,E>> getNeighborNodes(N value)
+
+
Description copied from class: Graph
+
Gets the neighboring nodes. +

+

+
Specified by:
getNeighborNodes in class Graph<N,E>
+
+
+
Parameters:
value - The node's value. +
Returns:
A list of neighboring nodes.
+
+
+
+ +

+getNeighborNodesIterator

+
+public Iterator<GraphNode<N,E>> getNeighborNodesIterator(N value)
+
+
+
Specified by:
getNeighborNodesIterator in class Graph<N,E>
+
+
+
+
+
+
+ +

+getUndirectedGraphEdges

+
+public List<UndiGraph.UndiGraphEdge<N,E>> getUndirectedGraphEdges(N n1,
+                                                                  N n2)
+
+
+
+
+
+
+
+
+
+ +

+getUndirectedGraphNode

+
+public UndiGraph.UndiGraphNode<N,E> getUndirectedGraphNode(N nodeValue)
+
+
+
Specified by:
getUndirectedGraphNode in class UndiGraph<N,E>
+
+
+
+
+
+
+ +

+getUndirectedGraphNodes

+
+public Collection<UndiGraph.UndiGraphNode<N,E>> getUndirectedGraphNodes()
+
+
Description copied from class: UndiGraph
+
Gets an immutable collection of all the nodes in this graph. +

+

+
+
+
+
+
+
+
+ +

+createNode

+
+public GraphNode<N,E> createNode(N value)
+
+
Description copied from class: Graph
+
Gets a node from the graph given a value. New nodes are created if that + value has not been assigned a graph node. Values equality are compared + using Object.equals. +

+

+
Specified by:
createNode in class Graph<N,E>
+
+
+
Parameters:
value - The node's value. +
Returns:
The corresponding node in the graph.
+
+
+
+ +

+getEdges

+
+public List<Graph.GraphEdge<N,E>> getEdges(N n1,
+                                           N n2)
+
+
Description copied from class: Graph
+
Retrieves an edge from the graph. +

+

+
Specified by:
getEdges in class Graph<N,E>
+
+
+
Parameters:
n1 - Node one.
n2 - Node two. +
Returns:
The list of edges between those two values in the graph.
+
+
+
+ +

+getFirstEdge

+
+public Graph.GraphEdge<N,E> getFirstEdge(N n1,
+                                         N n2)
+
+
Description copied from class: Graph
+
Retrieves any edge from the graph. +

+

+
Specified by:
getFirstEdge in class Graph<N,E>
+
+
+
Parameters:
n1 - Node one.
n2 - Node two. +
Returns:
The first edges between those two values in the graph. null if + there are none.
+
+
+
+ +

+getNode

+
+public GraphNode<N,E> getNode(N value)
+
+
Description copied from interface: AdjacencyGraph
+
Gets a node from the graph given a value. Values equality are compared + using Object.equals. +

+

+
Specified by:
getNode in interface AdjacencyGraph<N,E>
+
+
+
Parameters:
value - The node's value. +
Returns:
The corresponding node in the graph, null if there value has no + corresponding node.
+
+
+
+ +

+isConnected

+
+public boolean isConnected(N n1,
+                           N n2)
+
+
Description copied from class: Graph
+
Checks whether two nodes in the graph are connected. +

+

+
Specified by:
isConnected in class Graph<N,E>
+
+
+
Parameters:
n1 - Node 1.
n2 - Node 2. +
Returns:
true if the two nodes are connected.
+
+
+
+ +

+isConnected

+
+public boolean isConnected(N n1,
+                           E e,
+                           N n2)
+
+
Description copied from class: Graph
+
Checks whether two nodes in the graph are connected by the given + edge type. +

+

+
Specified by:
isConnected in class Graph<N,E>
+
+
+
Parameters:
n1 - Node 1.
e - The edge type.
n2 - Node 2.
+
+
+
+ +

+getGraphvizEdges

+
+public List<GraphvizGraph.GraphvizEdge> getGraphvizEdges()
+
+
Description copied from interface: GraphvizGraph
+
Retrieve a list of edges in the graph. +

+

+
Specified by:
getGraphvizEdges in interface GraphvizGraph
+
+
+ +
Returns:
A list of edges in the graph.
+
+
+
+ +

+getName

+
+public String getName()
+
+
Description copied from interface: GraphvizGraph
+
Name of the graph. +

+

+
Specified by:
getName in interface GraphvizGraph
+
+
+ +
Returns:
Name of the graph.
+
+
+
+ +

+getGraphvizNodes

+
+public List<GraphvizGraph.GraphvizNode> getGraphvizNodes()
+
+
Description copied from interface: GraphvizGraph
+
Retrieve a list of nodes in the graph. +

+

+
Specified by:
getGraphvizNodes in interface GraphvizGraph
+
+
+ +
Returns:
A list of nodes in the graph.
+
+
+
+ +

+isDirected

+
+public boolean isDirected()
+
+
Description copied from interface: GraphvizGraph
+
Graph type. +

+

+
Specified by:
isDirected in interface GraphvizGraph
+
+
+ +
Returns:
True if the graph is a directed graph.
+
+
+
+ +

+getNodes

+
+public Collection<GraphNode<N,E>> getNodes()
+
+
Description copied from class: Graph
+
Gets an immutable list of all nodes. +

+

+
Specified by:
getNodes in interface AdjacencyGraph<N,E>
Specified by:
getNodes in class Graph<N,E>
+
+
+
+
+
+
+ +

+getEdges

+
+public List<Graph.GraphEdge<N,E>> getEdges()
+
+
Description copied from class: Graph
+
Gets an immutable list of all edges. +

+

+
Specified by:
getEdges in class Graph<N,E>
+
+
+
+
+
+
+ +

+getNodeDegree

+
+public int getNodeDegree(N value)
+
+
Description copied from class: Graph
+
Gets the degree of a node. +

+

+
Specified by:
getNodeDegree in class Graph<N,E>
+
+
+
Parameters:
value - The node's value. +
Returns:
The degree of the node.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/StandardUnionFind.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/StandardUnionFind.html new file mode 100644 index 0000000..cb99dd1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/StandardUnionFind.html @@ -0,0 +1,473 @@ + + + + + +StandardUnionFind (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Class StandardUnionFind<E>

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.graph.StandardUnionFind<E>
+
+
+
Type Parameters:
E - element type
+
+
All Implemented Interfaces:
UnionFind<E>, Serializable
+
+
+
+
public class StandardUnionFind<E>
extends Object
implements Serializable, UnionFind<E>
+ + +

+A Union-Find implementation. + +

This class implements Union-Find algorithm with rank and path + compression. + +

See + algorithmist for more detail. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + + + + +
+Constructor Summary
StandardUnionFind() + +
+          Creates an empty UnionFind structure.
StandardUnionFind(UnionFind<E> other) + +
+          Creates an UnionFind structure being a copy of other structure.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidadd(E e) + +
+          Adds the given element to a new set if it is not already in a set.
+ Collection<Set<E>>allEquivalenceClasses() + +
+          Returns an immutable collection containing all equivalence classes.
+ booleanareEquivalent(E a, + E b) + +
+          Returns true if a and b belong to the same equivalence + class.
+ Set<E>elements() + +
+          Returns an unmodifiable set of all elements added to the UnionFind.
+ Efind(E e) + +
+          Returns the representative of the equivalence class of e.
+ Set<E>findAll(E value) + +
+          Returns the elements in the same equivalence class as value.
+ Eunion(E a, + E b) + +
+          Unions the equivalence classes of a and b and returns the + representative of the resulting equivalence class.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+StandardUnionFind

+
+public StandardUnionFind()
+
+
Creates an empty UnionFind structure. +

+

+
+ +

+StandardUnionFind

+
+public StandardUnionFind(UnionFind<E> other)
+
+
Creates an UnionFind structure being a copy of other structure. + The created structure is optimal in a sense that the paths to + the root from any element will have a length of at most 1. +

+

+
Parameters:
other - structure to be copied
+
+ + + + + + + + +
+Method Detail
+ +

+add

+
+public void add(E e)
+
+
Description copied from interface: UnionFind
+
Adds the given element to a new set if it is not already in a set. +

+

+
Specified by:
add in interface UnionFind<E>
+
+
+
+
+
+
+ +

+union

+
+public E union(E a,
+               E b)
+
+
Description copied from interface: UnionFind
+
Unions the equivalence classes of a and b and returns the + representative of the resulting equivalence class. The elements will be + added if they are not already present. +

+

+
Specified by:
union in interface UnionFind<E>
+
+
+
+
+
+
+ +

+find

+
+public E find(E e)
+
+
Description copied from interface: UnionFind
+
Returns the representative of the equivalence class of e. +

+

+
Specified by:
find in interface UnionFind<E>
+
+
+
+
+
+
+ +

+areEquivalent

+
+public boolean areEquivalent(E a,
+                             E b)
+
+
Description copied from interface: UnionFind
+
Returns true if a and b belong to the same equivalence + class. +

+

+
Specified by:
areEquivalent in interface UnionFind<E>
+
+
+
+
+
+
+ +

+elements

+
+public Set<E> elements()
+
+
Description copied from interface: UnionFind
+
Returns an unmodifiable set of all elements added to the UnionFind. +

+

+
Specified by:
elements in interface UnionFind<E>
+
+
+
+
+
+
+ +

+allEquivalenceClasses

+
+public Collection<Set<E>> allEquivalenceClasses()
+
+
Description copied from interface: UnionFind
+
Returns an immutable collection containing all equivalence classes. The + returned collection represents a snapshot of the current state and will not + reflect changes made to the data structure. +

+

+
Specified by:
allEquivalenceClasses in interface UnionFind<E>
+
+
+
+
+
+
+ +

+findAll

+
+public Set<E> findAll(E value)
+
+
Description copied from interface: UnionFind
+
Returns the elements in the same equivalence class as value. +

+

+
Specified by:
findAll in interface UnionFind<E>
+
+
+ +
Returns:
an unmodifiable view. As equivalence classes are merged, this set + will reflect those changes.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/SubGraph.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/SubGraph.html new file mode 100644 index 0000000..451c9bb --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/SubGraph.html @@ -0,0 +1,248 @@ + + + + + +SubGraph (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Interface SubGraph<N,E>

+
+
Type Parameters:
N - Value type that the graph node stores.
E - Value type that the graph edge stores.
+
+
+
public interface SubGraph<N,E>
+ + +

+An interface representing a subgraph that provides adjacency calculation to + a node. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Method Summary
+ voidaddNode(N value) + +
+          Adds the node into this subgraph.
+ booleanisIndependentOf(N node) + +
+          Returns true if the node is a neighbor of any node in this SubGraph.
+  +

+ + + + + + + + +
+Method Detail
+ +

+isIndependentOf

+
+boolean isIndependentOf(N node)
+
+
Returns true if the node is a neighbor of any node in this SubGraph. +

+

+
+
+
+
+ +

+addNode

+
+void addNode(N value)
+
+
Adds the node into this subgraph. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/UndiGraph.UndiGraphEdge.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/UndiGraph.UndiGraphEdge.html new file mode 100644 index 0000000..8e568bb --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/UndiGraph.UndiGraphEdge.html @@ -0,0 +1,220 @@ + + + + + +UndiGraph.UndiGraphEdge (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Interface UndiGraph.UndiGraphEdge<N,E>

+
+
Type Parameters:
N - Value type that the graph node stores.
E - Value type that the graph edge stores.
+
+
All Superinterfaces:
Annotatable, Graph.GraphEdge<N,E>
+
+
+
Enclosing class:
UndiGraph<N,E>
+
+
+
+
public static interface UndiGraph.UndiGraphEdge<N,E>
extends Graph.GraphEdge<N,E>
+ + +

+A generic undirected graph edge. +

+ +

+


+ +

+ + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.graph.Graph.GraphEdge
getNodeA, getNodeB, getValue
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.graph.Annotatable
getAnnotation, setAnnotation
+  +

+ +


+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/UndiGraph.UndiGraphNode.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/UndiGraph.UndiGraphNode.html new file mode 100644 index 0000000..8aabc9c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/UndiGraph.UndiGraphNode.html @@ -0,0 +1,273 @@ + + + + + +UndiGraph.UndiGraphNode (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Interface UndiGraph.UndiGraphNode<N,E>

+
+
Type Parameters:
N - Value type that the graph node stores.
E - Value type that the graph edge stores.
+
+
All Superinterfaces:
Annotatable, GraphNode<N,E>
+
+
+
Enclosing class:
UndiGraph<N,E>
+
+
+
+
public static interface UndiGraph.UndiGraphNode<N,E>
extends GraphNode<N,E>
+ + +

+A generic undirected graph node. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Method Summary
+ List<UndiGraph.UndiGraphEdge<N,E>>getNeighborEdges() + +
+           
+ Iterator<UndiGraph.UndiGraphEdge<N,E>>getNeighborEdgesIterator() + +
+           
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.graph.GraphNode
getValue
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.graph.Annotatable
getAnnotation, setAnnotation
+  +

+ + + + + + + + +
+Method Detail
+ +

+getNeighborEdges

+
+List<UndiGraph.UndiGraphEdge<N,E>> getNeighborEdges()
+
+
+
+
+
+
+
+
+
+ +

+getNeighborEdgesIterator

+
+Iterator<UndiGraph.UndiGraphEdge<N,E>> getNeighborEdgesIterator()
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/UndiGraph.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/UndiGraph.html new file mode 100644 index 0000000..56799e4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/UndiGraph.html @@ -0,0 +1,330 @@ + + + + + +UndiGraph (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Class UndiGraph<N,E>

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.graph.Graph<N,E>
+      extended by com.google.javascript.jscomp.graph.UndiGraph<N,E>
+
+
+
Type Parameters:
N - Value type that the graph node stores.
E - Value type that the graph edge stores.
+
+
All Implemented Interfaces:
AdjacencyGraph<N,E>
+
+
+
Direct Known Subclasses:
LinkedUndirectedGraph
+
+
+
+
public abstract class UndiGraph<N,E>
extends Graph<N,E>
+ + +

+A generic undirected graph. +

+ +

+


+ +

+ + + + + + + + + + + + + + + +
+Nested Class Summary
+static interfaceUndiGraph.UndiGraphEdge<N,E> + +
+          A generic undirected graph edge.
+static interfaceUndiGraph.UndiGraphNode<N,E> + +
+          A generic undirected graph node.
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.jscomp.graph.Graph
Graph.GraphEdge<N,E>
+  + + + + + + + + + + + +
+Constructor Summary
UndiGraph() + +
+           
+  + + + + + + + + + + + +
+Method Summary
+abstract  UndiGraph.UndiGraphNode<N,E>getUndirectedGraphNode(N nodeValue) + +
+           
+ + + + + + + +
Methods inherited from class com.google.javascript.jscomp.graph.Graph
clearEdgeAnnotations, clearNodeAnnotations, connect, connectIfNotFound, createNode, disconnect, getEdges, getEdges, getFirstEdge, getNeighborNodes, getNeighborNodesIterator, getNodeDegree, getNodes, getWeight, hasNode, isConnected, isConnected, popEdgeAnnotations, popNodeAnnotations, pushEdgeAnnotations, pushNodeAnnotations
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+ + + + + + + +
Methods inherited from interface com.google.javascript.jscomp.graph.AdjacencyGraph
getNode, newSubGraph
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+UndiGraph

+
+public UndiGraph()
+
+
+ + + + + + + + +
+Method Detail
+ +

+getUndirectedGraphNode

+
+public abstract UndiGraph.UndiGraphNode<N,E> getUndirectedGraphNode(N nodeValue)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/UnionFind.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/UnionFind.html new file mode 100644 index 0000000..c29329e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/UnionFind.html @@ -0,0 +1,389 @@ + + + + + +UnionFind (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.graph +
+Interface UnionFind<E>

+
+
Type Parameters:
E - element type
+
+
All Known Implementing Classes:
StandardUnionFind
+
+
+
+
public interface UnionFind<E>
+ + +

+Union-Find is a classical algorithm used to find connected components in + graph theory. + +

Each equivalence class has a representative element that is chosen + arbitrarily and is used to determine if two elements are members of the same + class. + +

See + algorithmist for more detail. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidadd(E e) + +
+          Adds the given element to a new set if it is not already in a set.
+ Collection<Set<E>>allEquivalenceClasses() + +
+          Returns an immutable collection containing all equivalence classes.
+ booleanareEquivalent(E a, + E b) + +
+          Returns true if a and b belong to the same equivalence + class.
+ Set<E>elements() + +
+          Returns an unmodifiable set of all elements added to the UnionFind.
+ Efind(E e) + +
+          Returns the representative of the equivalence class of e.
+ Set<E>findAll(E value) + +
+          Returns the elements in the same equivalence class as value.
+ Eunion(E a, + E b) + +
+          Unions the equivalence classes of a and b and returns the + representative of the resulting equivalence class.
+  +

+ + + + + + + + +
+Method Detail
+ +

+add

+
+void add(E e)
+
+
Adds the given element to a new set if it is not already in a set. +

+

+ +
Throws: +
UnsupportedOperationException - if the add operation is not + supported by this union-find.
+
+
+
+ +

+union

+
+E union(E a,
+        E b)
+
+
Unions the equivalence classes of a and b and returns the + representative of the resulting equivalence class. The elements will be + added if they are not already present. +

+

+ +
Throws: +
UnsupportedOperationException - if the add operation is not + supported by this union-find.
+
+
+
+ +

+find

+
+E find(E e)
+
+
Returns the representative of the equivalence class of e. +

+

+
+
+
+
+ +

+areEquivalent

+
+boolean areEquivalent(E a,
+                      E b)
+
+
Returns true if a and b belong to the same equivalence + class. +

+

+ +
Throws: +
IllegalArgumentException - if any argument is not an element of this + structure.
+
+
+
+ +

+elements

+
+Set<E> elements()
+
+
Returns an unmodifiable set of all elements added to the UnionFind. +

+

+
+
+
+
+ +

+allEquivalenceClasses

+
+Collection<Set<E>> allEquivalenceClasses()
+
+
Returns an immutable collection containing all equivalence classes. The + returned collection represents a snapshot of the current state and will not + reflect changes made to the data structure. +

+

+
+
+
+
+ +

+findAll

+
+Set<E> findAll(E value)
+
+
Returns the elements in the same equivalence class as value. +

+

+ +
Returns:
an unmodifiable view. As equivalence classes are merged, this set + will reflect those changes. +
Throws: +
IllegalArgumentException - if a requested element does not belong + to the structure.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/package-frame.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/package-frame.html new file mode 100644 index 0000000..58bb4be --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/package-frame.html @@ -0,0 +1,93 @@ + + + + + +com.google.javascript.jscomp.graph (Compiler) + + + + + + + + + + +com.google.javascript.jscomp.graph + + + + +
+Interfaces  + +
+AdjacencyGraph +
+Annotatable +
+Annotation +
+DiGraph.DiGraphEdge +
+DiGraph.DiGraphNode +
+FixedPointGraphTraversal.EdgeCallback +
+Graph.GraphEdge +
+GraphNode +
+GraphvizGraph +
+GraphvizGraph.GraphvizEdge +
+GraphvizGraph.GraphvizNode +
+SubGraph +
+UndiGraph.UndiGraphEdge +
+UndiGraph.UndiGraphNode +
+UnionFind
+ + + + + + +
+Classes  + +
+DiGraph +
+FixedPointGraphTraversal +
+Graph +
+GraphColoring +
+GraphColoring.Color +
+GraphColoring.GreedyGraphColoring +
+GraphPruner +
+GraphReachability +
+GraphReachability.EdgeTuple +
+LinkedDirectedGraph +
+LinkedUndirectedGraph +
+StandardUnionFind +
+UndiGraph
+ + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/package-summary.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/package-summary.html new file mode 100644 index 0000000..c617126 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/package-summary.html @@ -0,0 +1,310 @@ + + + + + +com.google.javascript.jscomp.graph (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+

+Package com.google.javascript.jscomp.graph +

+Provides graph data structures and algorithms for coloring and fixed-point +computations. +

+See: +
+          Description +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Interface Summary
AdjacencyGraph<N,E>A minimal graph interface.
AnnotatableObject that has an annotation.
AnnotationInformation that can be annotated to a GraphNode or + Graph.GraphEdge.
DiGraph.DiGraphEdge<N,E>A generic directed graph edge.
DiGraph.DiGraphNode<N,E>A generic directed graph node.
FixedPointGraphTraversal.EdgeCallback<Node,Edge> 
Graph.GraphEdge<N,E>A generic edge.
GraphNode<N,E>A generic node.
GraphvizGraphA graph that can be dumped to a Graphviz DOT file.
GraphvizGraph.GraphvizEdgeA Graphviz edge.
GraphvizGraph.GraphvizNodeA Graphviz node.
SubGraph<N,E>An interface representing a subgraph that provides adjacency calculation to + a node.
UndiGraph.UndiGraphEdge<N,E>A generic undirected graph edge.
UndiGraph.UndiGraphNode<N,E>A generic undirected graph node.
UnionFind<E>Union-Find is a classical algorithm used to find connected components in + graph theory.
+  + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Class Summary
DiGraph<N,E>A generic directed graph.
FixedPointGraphTraversal<N,E>A utility class for doing fixed-point computations.
Graph<N,E>The base generic class for graph-like data structure and algorithms in + the compiler.
GraphColoring<N,E>Annotates the graph with a color in a way that no connected node will have + the same color.
GraphColoring.Color 
GraphColoring.GreedyGraphColoring<N,E>Greedily assign nodes with high degree unique colors.
GraphPruner<N,E>Prunes a graph, creating a new graph with nodes removed.
GraphReachability<N,E>Computes all the reachable nodes.
GraphReachability.EdgeTuple<N,E>Represents Source Node, Edge and Destination Node.
LinkedDirectedGraph<N,E>A directed graph using linked list within nodes to store edge information.
LinkedUndirectedGraph<N,E>An undirected graph using linked list within nodes to store edge + information.
StandardUnionFind<E>A Union-Find implementation.
UndiGraph<N,E>A generic undirected graph.
+  + +

+

+Package com.google.javascript.jscomp.graph Description +

+ +

+Provides graph data structures and algorithms for coloring and fixed-point +computations. +

+ +

+

+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/package-tree.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/package-tree.html new file mode 100644 index 0000000..f0dca0b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/graph/package-tree.html @@ -0,0 +1,190 @@ + + + + + +com.google.javascript.jscomp.graph Class Hierarchy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Hierarchy For Package com.google.javascript.jscomp.graph +

+
+
+
Package Hierarchies:
All Packages
+
+

+Class Hierarchy +

+ +

+Interface Hierarchy +

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/ErrorLevel.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/ErrorLevel.html new file mode 100644 index 0000000..67612d3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/ErrorLevel.html @@ -0,0 +1,354 @@ + + + + + +ErrorLevel (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.jsonml +
+Enum ErrorLevel

+
+java.lang.Object
+  extended by java.lang.Enum<ErrorLevel>
+      extended by com.google.javascript.jscomp.jsonml.ErrorLevel
+
+
+
All Implemented Interfaces:
Serializable, Comparable<ErrorLevel>
+
+
+
+
public enum ErrorLevel
extends Enum<ErrorLevel>
+ + +

+Represents possible error levels for JsonML errors. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Enum Constant Summary
COMPILATION_ERROR + +
+           
COMPILATION_WARNING + +
+           
SYNTAX_ERROR + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static ErrorLevelvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static ErrorLevel[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+COMPILATION_ERROR

+
+public static final ErrorLevel COMPILATION_ERROR
+
+
+
+
+
+ +

+COMPILATION_WARNING

+
+public static final ErrorLevel COMPILATION_WARNING
+
+
+
+
+
+ +

+SYNTAX_ERROR

+
+public static final ErrorLevel SYNTAX_ERROR
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static ErrorLevel[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (ErrorLevel c : ErrorLevel.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static ErrorLevel valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/JsonML.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/JsonML.html new file mode 100644 index 0000000..2fd6b77 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/JsonML.html @@ -0,0 +1,743 @@ + + + + + +JsonML (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.jsonml +
+Class JsonML

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.jsonml.JsonML
+
+
+
+
public class JsonML
extends Object
+ + +

+Class which represents JsonML element according to the specification at + "http://code.google.com/p/es-lab/wiki/JsonMLASTFormat" +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + +
+Constructor Summary
JsonML(TagType type) + +
+          Creates a new element with a given type.
JsonML(TagType type, + JsonML... children) + +
+          Creates a new element.
JsonML(TagType type, + List<? extends JsonML> children) + +
+           
JsonML(TagType type, + Map<? extends TagAttr,?> attributes) + +
+           
JsonML(TagType type, + Map<? extends TagAttr,?> attributes, + List<? extends JsonML> children) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidaddChild(int index, + JsonML element) + +
+          Inserts the given JsonML element at the given position in the + list of children.
+ voidappendChild(JsonML element) + +
+          Appends a given child element to the list of children.
+ voidappendChildren(Collection<? extends JsonML> elements) + +
+          Appends a collection of children to the back of the list of children.
+ intchildrenSize() + +
+          Returns number of the children.
+ voidclearChildren() + +
+          Removes all elements from the list of children.
+ ObjectgetAttribute(TagAttr name) + +
+          Returns value associated with a given attribute.
+ Map<TagAttr,Object>getAttributes() + +
+          Returns a map with attributes and respective values.
+ JsonMLgetChild(int index) + +
+          Returns child at a given position.
+ List<JsonML>getChildren() + +
+          Returns a list of all children.
+ List<JsonML>getChildren(int fromIndex, + int toIndex) + +
+          Returns the portion of children list between the specified + fromIndex, inclusive, and toIndex, exclusive.
+ TagTypegetType() + +
+          Returns type of the JsonML element.
+ booleanhasChildren() + +
+          Returns true if the JsonML element has at least one child.
+ voidsetAttribute(TagAttr name, + Object value) + +
+          Sets value for a given attribute.
+ voidsetAttributes(Map<TagAttr,Object> attributes) + +
+          Sets attributes of the JsonML element.
+ voidsetChild(int index, + JsonML element) + +
+          Replaces the element at the given position in the list of children wit + the given JsonML element.
+ voidsetChildren(JsonML... children) + +
+          Replaces all elements in the list of children with the given + JsonML elements.
+ voidsetChildren(List<JsonML> children) + +
+          Replaces all elements in the list of children with the given + list of JsonML elements..
+ StringtoString() + +
+           
+ StringtoStringTree() + +
+          Prints a JsonML tree in a human readable format.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JsonML

+
+public JsonML(TagType type)
+
+
Creates a new element with a given type. +

+

+
Parameters:
type -
+
+
+ +

+JsonML

+
+public JsonML(TagType type,
+              JsonML... children)
+
+
Creates a new element. +

+

+
Parameters:
type - type of the element
children - children to append to the element
+
+
+ +

+JsonML

+
+public JsonML(TagType type,
+              List<? extends JsonML> children)
+
+
+
+ +

+JsonML

+
+public JsonML(TagType type,
+              Map<? extends TagAttr,?> attributes)
+
+
+
+ +

+JsonML

+
+public JsonML(TagType type,
+              Map<? extends TagAttr,?> attributes,
+              List<? extends JsonML> children)
+
+
+ + + + + + + + +
+Method Detail
+ +

+addChild

+
+public void addChild(int index,
+                     JsonML element)
+
+
Inserts the given JsonML element at the given position in the + list of children. +

+

+
Parameters:
index - index at which the given element is to be inserted
element - JsonML element to be inserted
+
+
+
+ +

+appendChild

+
+public void appendChild(JsonML element)
+
+
Appends a given child element to the list of children. +

+

+
Parameters:
element - JsonML element to append
+
+
+
+ +

+appendChildren

+
+public void appendChildren(Collection<? extends JsonML> elements)
+
+
Appends a collection of children to the back of the list of children. +

+

+
Parameters:
elements - collection of JsonML elements to append
+
+
+
+ +

+childrenSize

+
+public int childrenSize()
+
+
Returns number of the children. +

+

+
+
+
+
+ +

+clearChildren

+
+public void clearChildren()
+
+
Removes all elements from the list of children. +

+

+
+
+
+
+ +

+getAttribute

+
+public Object getAttribute(TagAttr name)
+
+
Returns value associated with a given attribute. +

+

+
Parameters:
name - name of the attribute +
Returns:
associated value or null if the attribute is not present
+
+
+
+ +

+getAttributes

+
+public Map<TagAttr,Object> getAttributes()
+
+
Returns a map with attributes and respective values. +

+

+
+
+
+
+ +

+getChild

+
+public JsonML getChild(int index)
+
+
Returns child at a given position. +

+

+
+
+
+
+ +

+getChildren

+
+public List<JsonML> getChildren()
+
+
Returns a list of all children. +

+

+
+
+
+
+ +

+getChildren

+
+public List<JsonML> getChildren(int fromIndex,
+                                int toIndex)
+
+
Returns the portion of children list between the specified + fromIndex, inclusive, and toIndex, exclusive. +

+

+
Parameters:
fromIndex - low endpoint (inclusive)
toIndex - high endpoint (exclusive)
+
+
+
+ +

+getType

+
+public TagType getType()
+
+
Returns type of the JsonML element. +

+

+
+
+
+
+ +

+hasChildren

+
+public boolean hasChildren()
+
+
Returns true if the JsonML element has at least one child. +

+

+
+
+
+
+ +

+setAttribute

+
+public void setAttribute(TagAttr name,
+                         Object value)
+
+
Sets value for a given attribute. +

+

+
Parameters:
name - name of the attribute
value - value to associate with the attribute
+
+
+
+ +

+setAttributes

+
+public void setAttributes(Map<TagAttr,Object> attributes)
+
+
Sets attributes of the JsonML element. +

+

+
Parameters:
attributes - map with attributes and their values
+
+
+
+ +

+setChild

+
+public void setChild(int index,
+                     JsonML element)
+
+
Replaces the element at the given position in the list of children wit + the given JsonML element. +

+

+
Parameters:
index - index of element to replace
element - JsonML element to append
+
+
+
+ +

+setChildren

+
+public void setChildren(JsonML... children)
+
+
Replaces all elements in the list of children with the given + JsonML elements. +

+

+
Parameters:
children - a comma separated list of JsonML elements
+
+
+
+ +

+setChildren

+
+public void setChildren(List<JsonML> children)
+
+
Replaces all elements in the list of children with the given + list of JsonML elements.. +

+

+
Parameters:
children - a list of JsonML elements.
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+
+ +

+toStringTree

+
+public String toStringTree()
+
+
Prints a JsonML tree in a human readable format. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/JsonMLAst.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/JsonMLAst.html new file mode 100644 index 0000000..845a77a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/JsonMLAst.html @@ -0,0 +1,455 @@ + + + + + +JsonMLAst (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.jsonml +
+Class JsonMLAst

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.jsonml.JsonMLAst
+
+
+
All Implemented Interfaces:
SourceAst, Serializable
+
+
+
+
public class JsonMLAst
extends Object
implements SourceAst
+ + +

+Generates an AST from a JsonML source file. + + JsonML format for representation of JavaScript is specified + here. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Constructor Summary
JsonMLAst(JsonML jsonml) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidclearAst() + +
+          Removes any references to root node of the AST.
+ JsonMLconvertToJsonML() + +
+           
+ NodegetAstRoot(AbstractCompiler compiler) + +
+          Generates AST based on AST representation
+ JsonMLgetElementPreOrder(int n) + +
+          Returns a JsonML element with the specified number from the tree in + pre-order walk.
+ InputIdgetInputId() + +
+           
+ SourceFilegetSourceFile() + +
+          Returns the source file the generated AST represents.
+ StringgetSourceName() + +
+           
+ voidsetSourceFile(SourceFile file) + +
+          Sets the source file the generated AST represents.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JsonMLAst

+
+public JsonMLAst(JsonML jsonml)
+
+
+ + + + + + + + +
+Method Detail
+ +

+clearAst

+
+public void clearAst()
+
+
Description copied from interface: SourceAst
+
Removes any references to root node of the AST. If it is requested again, + another parse will be performed. This method is needed to allow the ASTs + to be garbage collected if the inputs are still around after compilation. +

+

+
Specified by:
clearAst in interface SourceAst
+
+
+
+
+
+
+ +

+getAstRoot

+
+public Node getAstRoot(AbstractCompiler compiler)
+
+
Generates AST based on AST representation +

+

+
Specified by:
getAstRoot in interface SourceAst
+
+
+
See Also:
SourceAst.getAstRoot(AbstractCompiler)
+
+
+
+ +

+getSourceFile

+
+public SourceFile getSourceFile()
+
+
Description copied from interface: SourceAst
+
Returns the source file the generated AST represents. +

+

+
Specified by:
getSourceFile in interface SourceAst
+
+
+
+
+
+
+ +

+setSourceFile

+
+public void setSourceFile(SourceFile file)
+
+
Description copied from interface: SourceAst
+
Sets the source file the generated AST represents. This can be called after + deserializing if access to the source file is needed. If a different file + is provided than that with which this was created, an IllegalStateException + will be thrown. +

+

+
Specified by:
setSourceFile in interface SourceAst
+
+
+
+
+
+
+ +

+getSourceName

+
+public String getSourceName()
+
+
+
+
+
+
+
+
+
+ +

+convertToJsonML

+
+public JsonML convertToJsonML()
+
+
+
+
+
+
+
+
+
+ +

+getElementPreOrder

+
+public JsonML getElementPreOrder(int n)
+
+
Returns a JsonML element with the specified number from the tree in + pre-order walk. +

+

+
+
+
+ +
Returns:
n-th node or null if the node does not exists
+
+
+
+ +

+getInputId

+
+public InputId getInputId()
+
+
+
Specified by:
getInputId in interface SourceAst
+
+
+ +
Returns:
The input id associated with this AST
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/JsonMLError.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/JsonMLError.html new file mode 100644 index 0000000..9ceff1e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/JsonMLError.html @@ -0,0 +1,384 @@ + + + + + +JsonMLError (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.jsonml +
+Class JsonMLError

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.jsonml.JsonMLError
+
+
+
+
public class JsonMLError
extends Object
+ + +

+Class used to represent errors which correspond to JsonML elements. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Field Summary
+ Stringdescription + +
+          Description of the error
+ JsonMLelement + +
+          Node where the warning occurred.
+ ErrorLevellevel + +
+          Level
+ intlineNumber + +
+          Line number of the source
+ StringsourceName + +
+          Name of the source
+  + + + + + + + + + + + + + + + +
+Method Summary
+static JsonMLErrormake(DiagnosticType type, + String sourceName, + JsonML element, + int lineNumber, + ErrorLevel level, + String... arguments) + +
+           
+static JsonMLErrormake(JSError error, + JsonMLAst ast) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+description

+
+public final String description
+
+
Description of the error +

+

+
+
+
+ +

+sourceName

+
+public final String sourceName
+
+
Name of the source +

+

+
+
+
+ +

+element

+
+public final JsonML element
+
+
Node where the warning occurred. +

+

+
+
+
+ +

+lineNumber

+
+public final int lineNumber
+
+
Line number of the source +

+

+
+
+
+ +

+level

+
+public final ErrorLevel level
+
+
Level +

+

+
+
+ + + + + + + + +
+Method Detail
+ +

+make

+
+public static JsonMLError make(DiagnosticType type,
+                               String sourceName,
+                               JsonML element,
+                               int lineNumber,
+                               ErrorLevel level,
+                               String... arguments)
+
+
+
+
+
+
+ +

+make

+
+public static JsonMLError make(JSError error,
+                               JsonMLAst ast)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/JsonMLUtil.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/JsonMLUtil.html new file mode 100644 index 0000000..afb07ae --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/JsonMLUtil.html @@ -0,0 +1,352 @@ + + + + + +JsonMLUtil (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.jsonml +
+Class JsonMLUtil

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.jsonml.JsonMLUtil
+
+
+
+
public class JsonMLUtil
extends Object
+ + +

+JsonMLUtil contains utilities for the JsonML object. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
JsonMLUtil() + +
+           
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static Stringcompare(JsonML tree1, + JsonML tree2) + +
+          Compares two specified JsonML trees.
+static booleanisExpression(JsonML element) + +
+          Checks if the specified JsonML element represents an expression.
+static JsonMLparseString(String jsonml) + +
+          Parses JSON string which contains serialized JsonML content.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JsonMLUtil

+
+public JsonMLUtil()
+
+
+ + + + + + + + +
+Method Detail
+ +

+isExpression

+
+public static boolean isExpression(JsonML element)
+
+
Checks if the specified JsonML element represents an expression. +

+

+
+
+
+
+ +

+parseString

+
+public static JsonML parseString(String jsonml)
+                          throws Exception
+
+
Parses JSON string which contains serialized JsonML content. +

+

+
Parameters:
jsonml - string representation of JsonML +
Returns:
root element of a JsonML tree +
Throws: +
Exception
+
+
+
+ +

+compare

+
+public static String compare(JsonML tree1,
+                             JsonML tree2)
+
+
Compares two specified JsonML trees. + + Two JsonML nodes are considered to be equal when the following conditions + are met: + + - have the same type + - have the same attributes from the list of attributes to compare + - have the same number of children + - nodes in each pair of corresponding children are equal + + Two JsonML trees are equal, if their roots are equal. + + When two nodes are compared, only the following attributes are taken + into account: + TagAttr.BODY, TagAttr.FLAGS, TagAttr.IS_PREFIX, TagAttr.LABEL, + TagAttr.NAME, TagAttr.OP, TagAttr.TYPE, TagAttr.VALUE + Generally, the comparator does not care about debugging attributes. +

+

+ +
Returns:
Returns string describing the inequality in the following format: + + The trees are not equal: + + Tree1: + -- string representation of Tree1 + + Tree2: + -- string representation of Tree2 + + Subtree1: + -- string representation of the subtree of the Tree1 which is not + -- equal to the corresponding subtree of the Tree2 + + Subtree2: + -- see Subtree1 + + If the trees are equal, null is returned.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/Reader.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/Reader.html new file mode 100644 index 0000000..f5c7f4e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/Reader.html @@ -0,0 +1,293 @@ + + + + + +Reader (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.jsonml +
+Class Reader

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.jsonml.Reader
+
+
+
+
public class Reader
extends Object
+ + +

+Traverse JsonML source tree and generates AST. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
Reader() + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+ Nodeparse(AbstractCompiler compiler) + +
+          Generates AST for a specified JsonML source file.
+ voidsetRootElement(JsonML rootElement) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+Reader

+
+public Reader()
+
+
+ + + + + + + + +
+Method Detail
+ +

+setRootElement

+
+public void setRootElement(JsonML rootElement)
+
+
+
+
+
+
+ +

+parse

+
+public Node parse(AbstractCompiler compiler)
+           throws com.google.javascript.jscomp.jsonml.JsonMLException
+
+
Generates AST for a specified JsonML source file. +

+

+ +
Returns:
root node of the generated AST +
Throws: +
JsonMLException - if an error occurs
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/SecureCompiler.Report.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/SecureCompiler.Report.html new file mode 100644 index 0000000..d2704e0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/SecureCompiler.Report.html @@ -0,0 +1,272 @@ + + + + + +SecureCompiler.Report (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.jsonml +
+Class SecureCompiler.Report

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.jsonml.SecureCompiler.Report
+
+
+
Enclosing class:
SecureCompiler
+
+
+
+
public class SecureCompiler.Report
extends Object
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ JsonMLError[]getErrors() + +
+           
+ JsonMLError[]getWarnings() + +
+           
+ booleanisSuccessful() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+isSuccessful

+
+public boolean isSuccessful()
+
+
+
+
+
+
+ +

+getErrors

+
+public JsonMLError[] getErrors()
+
+
+
+
+
+
+ +

+getWarnings

+
+public JsonMLError[] getWarnings()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/SecureCompiler.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/SecureCompiler.html new file mode 100644 index 0000000..6763ba6 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/SecureCompiler.html @@ -0,0 +1,380 @@ + + + + + +SecureCompiler (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.jsonml +
+Class SecureCompiler

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.jsonml.SecureCompiler
+
+
+
+
public class SecureCompiler
extends Object
+ + +

+Compilation of JavaScript code which guarantees that all security + capabilities are preserved after the process. In particular, it can be + safely applied to cajoled source. + + JS Compiler is used for code analysis and optimization. It runs a series + of passes which try to improve the code. + + For safety reasons, only a subset of local passes, which are provided by + JS Compiler, are processed. Currently it includes: + - elimination of temporary variables + + Using SecureCompiler is quite straightforward. A user just needs to create + a new instance and call compile() method. Currently the only input which + is supported is JsonML. +

+ +

+


+ +

+ + + + + + + + + + + +
+Nested Class Summary
+ classSecureCompiler.Report + +
+           
+  + + + + + + + + + + +
+Constructor Summary
SecureCompiler() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidcompile(JsonML source) + +
+           
+ voidenableFoldConstant() + +
+           
+ JsonMLgetJsonML() + +
+          Returns compiled source in JsonML format.
+ SecureCompiler.ReportgetReport() + +
+          Returns report from the last compilation.
+ StringgetString() + +
+          Returns compiled source as a JavaScript.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SecureCompiler

+
+public SecureCompiler()
+
+
+ + + + + + + + +
+Method Detail
+ +

+getJsonML

+
+public JsonML getJsonML()
+
+
Returns compiled source in JsonML format. +

+

+
+
+
+
+ +

+getString

+
+public String getString()
+
+
Returns compiled source as a JavaScript. +

+

+
+
+
+
+ +

+getReport

+
+public SecureCompiler.Report getReport()
+
+
Returns report from the last compilation. +

+

+
+
+
+
+ +

+compile

+
+public void compile(JsonML source)
+
+
+
+
+
+
+ +

+enableFoldConstant

+
+public void enableFoldConstant()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/TagAttr.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/TagAttr.html new file mode 100644 index 0000000..2de2103 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/TagAttr.html @@ -0,0 +1,587 @@ + + + + + +TagAttr (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.jsonml +
+Enum TagAttr

+
+java.lang.Object
+  extended by java.lang.Enum<TagAttr>
+      extended by com.google.javascript.jscomp.jsonml.TagAttr
+
+
+
All Implemented Interfaces:
Serializable, Comparable<TagAttr>
+
+
+
+
public enum TagAttr
extends Enum<TagAttr>
+ + +

+List of attributes that a JsonML element may have. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Enum Constant Summary
BODY + +
+           
DIRECTIVE + +
+           
END_COLUMN + +
+           
END_LINE + +
+           
FLAGS + +
+           
IS_PREFIX + +
+           
LABEL + +
+           
NAME + +
+           
OP + +
+           
OPAQUE_POSITION + +
+           
SOURCE + +
+           
START_COLUMN + +
+           
START_LINE + +
+           
TYPE + +
+           
VALUE + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static TagAttrget(String name) + +
+           
+ StringtoString() + +
+           
+static TagAttrvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static TagAttr[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+BODY

+
+public static final TagAttr BODY
+
+
+
+
+
+ +

+DIRECTIVE

+
+public static final TagAttr DIRECTIVE
+
+
+
+
+
+ +

+END_COLUMN

+
+public static final TagAttr END_COLUMN
+
+
+
+
+
+ +

+END_LINE

+
+public static final TagAttr END_LINE
+
+
+
+
+
+ +

+FLAGS

+
+public static final TagAttr FLAGS
+
+
+
+
+
+ +

+IS_PREFIX

+
+public static final TagAttr IS_PREFIX
+
+
+
+
+
+ +

+LABEL

+
+public static final TagAttr LABEL
+
+
+
+
+
+ +

+NAME

+
+public static final TagAttr NAME
+
+
+
+
+
+ +

+OP

+
+public static final TagAttr OP
+
+
+
+
+
+ +

+OPAQUE_POSITION

+
+public static final TagAttr OPAQUE_POSITION
+
+
+
+
+
+ +

+SOURCE

+
+public static final TagAttr SOURCE
+
+
+
+
+
+ +

+START_COLUMN

+
+public static final TagAttr START_COLUMN
+
+
+
+
+
+ +

+START_LINE

+
+public static final TagAttr START_LINE
+
+
+
+
+
+ +

+TYPE

+
+public static final TagAttr TYPE
+
+
+
+
+
+ +

+VALUE

+
+public static final TagAttr VALUE
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static TagAttr[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (TagAttr c : TagAttr.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static TagAttr valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+
+ +

+get

+
+public static TagAttr get(String name)
+
+
+
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Enum<TagAttr>
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/TagType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/TagType.html new file mode 100644 index 0000000..44f850f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/TagType.html @@ -0,0 +1,1122 @@ + + + + + +TagType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.jsonml +
+Enum TagType

+
+java.lang.Object
+  extended by java.lang.Enum<TagType>
+      extended by com.google.javascript.jscomp.jsonml.TagType
+
+
+
All Implemented Interfaces:
Serializable, Comparable<TagType>
+
+
+
+
public enum TagType
extends Enum<TagType>
+ + +

+List of types allowed for JsonML elements. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Enum Constant Summary
ArrayExpr + +
+           
AssignExpr + +
+           
BinaryExpr + +
+           
BlockStmt + +
+           
BreakStmt + +
+           
CallExpr + +
+           
Case + +
+           
CatchClause + +
+           
ConditionalExpr + +
+           
ContinueStmt + +
+           
CountExpr + +
+           
DataProp + +
+           
DebuggerStmt + +
+           
DefaultCase + +
+           
DeleteExpr + +
+           
DoWhileStmt + +
+           
Empty + +
+           
EmptyStmt + +
+           
EvalExpr + +
+           
ForInStmt + +
+           
ForStmt + +
+           
FunctionDecl + +
+           
FunctionExpr + +
+           
GetterProp + +
+           
IdExpr + +
+           
IdPatt + +
+           
IfStmt + +
+           
InitPatt + +
+           
InvokeExpr + +
+           
LabelledStmt + +
+           
LiteralExpr + +
+           
LogicalAndExpr + +
+           
LogicalOrExpr + +
+           
MemberExpr + +
+           
NewExpr + +
+           
ObjectExpr + +
+           
ParamDecl + +
+           
Program + +
+           
PrologueDecl + +
+           
RegExpExpr + +
+           
ReturnStmt + +
+           
SetterProp + +
+           
SwitchStmt + +
+           
ThisExpr + +
+           
ThrowStmt + +
+           
TryStmt + +
+           
TypeofExpr + +
+           
UnaryExpr + +
+           
VarDecl + +
+           
WhileStmt + +
+           
WithStmt + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static TagTypevalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static TagType[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+ArrayExpr

+
+public static final TagType ArrayExpr
+
+
+
+
+
+ +

+AssignExpr

+
+public static final TagType AssignExpr
+
+
+
+
+
+ +

+BinaryExpr

+
+public static final TagType BinaryExpr
+
+
+
+
+
+ +

+CallExpr

+
+public static final TagType CallExpr
+
+
+
+
+
+ +

+ConditionalExpr

+
+public static final TagType ConditionalExpr
+
+
+
+
+
+ +

+CountExpr

+
+public static final TagType CountExpr
+
+
+
+
+
+ +

+DeleteExpr

+
+public static final TagType DeleteExpr
+
+
+
+
+
+ +

+EvalExpr

+
+public static final TagType EvalExpr
+
+
+
+
+
+ +

+FunctionExpr

+
+public static final TagType FunctionExpr
+
+
+
+
+
+ +

+IdExpr

+
+public static final TagType IdExpr
+
+
+
+
+
+ +

+InvokeExpr

+
+public static final TagType InvokeExpr
+
+
+
+
+
+ +

+LiteralExpr

+
+public static final TagType LiteralExpr
+
+
+
+
+
+ +

+LogicalAndExpr

+
+public static final TagType LogicalAndExpr
+
+
+
+
+
+ +

+LogicalOrExpr

+
+public static final TagType LogicalOrExpr
+
+
+
+
+
+ +

+MemberExpr

+
+public static final TagType MemberExpr
+
+
+
+
+
+ +

+NewExpr

+
+public static final TagType NewExpr
+
+
+
+
+
+ +

+ObjectExpr

+
+public static final TagType ObjectExpr
+
+
+
+
+
+ +

+RegExpExpr

+
+public static final TagType RegExpExpr
+
+
+
+
+
+ +

+ThisExpr

+
+public static final TagType ThisExpr
+
+
+
+
+
+ +

+TypeofExpr

+
+public static final TagType TypeofExpr
+
+
+
+
+
+ +

+UnaryExpr

+
+public static final TagType UnaryExpr
+
+
+
+
+
+ +

+BlockStmt

+
+public static final TagType BlockStmt
+
+
+
+
+
+ +

+BreakStmt

+
+public static final TagType BreakStmt
+
+
+
+
+
+ +

+ContinueStmt

+
+public static final TagType ContinueStmt
+
+
+
+
+
+ +

+DebuggerStmt

+
+public static final TagType DebuggerStmt
+
+
+
+
+
+ +

+DoWhileStmt

+
+public static final TagType DoWhileStmt
+
+
+
+
+
+ +

+EmptyStmt

+
+public static final TagType EmptyStmt
+
+
+
+
+
+ +

+ForInStmt

+
+public static final TagType ForInStmt
+
+
+
+
+
+ +

+ForStmt

+
+public static final TagType ForStmt
+
+
+
+
+
+ +

+IfStmt

+
+public static final TagType IfStmt
+
+
+
+
+
+ +

+LabelledStmt

+
+public static final TagType LabelledStmt
+
+
+
+
+
+ +

+ReturnStmt

+
+public static final TagType ReturnStmt
+
+
+
+
+
+ +

+SwitchStmt

+
+public static final TagType SwitchStmt
+
+
+
+
+
+ +

+ThrowStmt

+
+public static final TagType ThrowStmt
+
+
+
+
+
+ +

+TryStmt

+
+public static final TagType TryStmt
+
+
+
+
+
+ +

+WhileStmt

+
+public static final TagType WhileStmt
+
+
+
+
+
+ +

+WithStmt

+
+public static final TagType WithStmt
+
+
+
+
+
+ +

+FunctionDecl

+
+public static final TagType FunctionDecl
+
+
+
+
+
+ +

+ParamDecl

+
+public static final TagType ParamDecl
+
+
+
+
+
+ +

+PrologueDecl

+
+public static final TagType PrologueDecl
+
+
+
+
+
+ +

+VarDecl

+
+public static final TagType VarDecl
+
+
+
+
+
+ +

+DataProp

+
+public static final TagType DataProp
+
+
+
+
+
+ +

+GetterProp

+
+public static final TagType GetterProp
+
+
+
+
+
+ +

+SetterProp

+
+public static final TagType SetterProp
+
+
+
+
+
+ +

+IdPatt

+
+public static final TagType IdPatt
+
+
+
+
+
+ +

+InitPatt

+
+public static final TagType InitPatt
+
+
+
+
+
+ +

+Case

+
+public static final TagType Case
+
+
+
+
+
+ +

+DefaultCase

+
+public static final TagType DefaultCase
+
+
+
+
+
+ +

+CatchClause

+
+public static final TagType CatchClause
+
+
+
+
+
+ +

+Empty

+
+public static final TagType Empty
+
+
+
+
+
+ +

+Program

+
+public static final TagType Program
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static TagType[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (TagType c : TagType.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static TagType valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/Validator.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/Validator.html new file mode 100644 index 0000000..0cb5b67 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/Validator.html @@ -0,0 +1,374 @@ + + + + + +Validator (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.jsonml +
+Class Validator

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.jsonml.Validator
+
+
+
+
public class Validator
extends Object
+ + +

+Statically validates JsonML elements. + + It is done in constant time: no subtree is traversed, but the element + is validated based only on its properties. Sometimes, also its children + are taken into account. + + Usually it checks if the specified element has a correct number of children, + and if all require attributes exist. It does not enforce all restrictions + which are implied by ES3 or ES5 specification. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Field Summary
+static TagType[]exprTypes + +
+           
+static StringMISSING_ARGUMENT + +
+           
+static StringNOT_ENOUGH_CHILDREN_FMT + +
+           
+static StringTOO_MANY_CHILDREN_FMT + +
+           
+static StringWRONG_CHILD_TYPE_FMT + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static StringprintList(Object[] list) + +
+           
+static Stringvalidate(JsonML element) + +
+          Validates the specified JsonML element.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+MISSING_ARGUMENT

+
+public static final String MISSING_ARGUMENT
+
+
+
See Also:
Constant Field Values
+
+
+ +

+NOT_ENOUGH_CHILDREN_FMT

+
+public static final String NOT_ENOUGH_CHILDREN_FMT
+
+
+
See Also:
Constant Field Values
+
+
+ +

+TOO_MANY_CHILDREN_FMT

+
+public static final String TOO_MANY_CHILDREN_FMT
+
+
+
See Also:
Constant Field Values
+
+
+ +

+WRONG_CHILD_TYPE_FMT

+
+public static final String WRONG_CHILD_TYPE_FMT
+
+
+
See Also:
Constant Field Values
+
+
+ +

+exprTypes

+
+public static TagType[] exprTypes
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+validate

+
+public static String validate(JsonML element)
+
+
Validates the specified JsonML element. +

+

+
Parameters:
element - JsonML element to validate +
Returns:
error message if the element could not be + validated, an empty string otherwise
+
+
+
+ +

+printList

+
+public static String printList(Object[] list)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/Writer.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/Writer.html new file mode 100644 index 0000000..ae70135 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/Writer.html @@ -0,0 +1,271 @@ + + + + + +Writer (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.jsonml +
+Class Writer

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.jsonml.Writer
+
+
+
+
public class Writer
extends Object
+ + +

+Converts internal AST into JsonML tree. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
Writer() + +
+           
+  + + + + + + + + + + + +
+Method Summary
+ JsonMLprocessAst(Node root) + +
+          Creates JsonML tree based on a specified AST.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+Writer

+
+public Writer()
+
+
+ + + + + + + + +
+Method Detail
+ +

+processAst

+
+public JsonML processAst(Node root)
+
+
Creates JsonML tree based on a specified AST. +

+

+
Parameters:
root - AST node +
Returns:
root of a created JsonML tree
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/package-frame.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/package-frame.html new file mode 100644 index 0000000..5600e42 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/package-frame.html @@ -0,0 +1,59 @@ + + + + + +com.google.javascript.jscomp.jsonml (Compiler) + + + + + + + + + + +com.google.javascript.jscomp.jsonml + + + + +
+Classes  + +
+JsonML +
+JsonMLAst +
+JsonMLError +
+JsonMLUtil +
+Reader +
+SecureCompiler +
+Validator +
+Writer
+ + + + + + +
+Enums  + +
+ErrorLevel +
+TagAttr +
+TagType
+ + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/package-summary.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/package-summary.html new file mode 100644 index 0000000..be038d9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/package-summary.html @@ -0,0 +1,236 @@ + + + + + +com.google.javascript.jscomp.jsonml (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+

+Package com.google.javascript.jscomp.jsonml +

+Provides the classes to support JsonML format and secure compiler. +

+See: +
+          Description +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Class Summary
JsonMLClass which represents JsonML element according to the specification at + "http://code.google.com/p/es-lab/wiki/JsonMLASTFormat"
JsonMLAstGenerates an AST from a JsonML source file.
JsonMLErrorClass used to represent errors which correspond to JsonML elements.
JsonMLUtilJsonMLUtil contains utilities for the JsonML object.
ReaderTraverse JsonML source tree and generates AST.
SecureCompilerCompilation of JavaScript code which guarantees that all security + capabilities are preserved after the process.
ValidatorStatically validates JsonML elements.
WriterConverts internal AST into JsonML tree.
+  + +

+ + + + + + + + + + + + + + + + + +
+Enum Summary
ErrorLevelRepresents possible error levels for JsonML errors.
TagAttrList of attributes that a JsonML element may have.
TagTypeList of types allowed for JsonML elements.
+  + +

+

+Package com.google.javascript.jscomp.jsonml Description +

+ +

+Provides the classes to support JsonML format and secure compiler. +

+ +

+

+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/package-tree.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/package-tree.html new file mode 100644 index 0000000..e2a07dc --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/jsonml/package-tree.html @@ -0,0 +1,176 @@ + + + + + +com.google.javascript.jscomp.jsonml Class Hierarchy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Hierarchy For Package com.google.javascript.jscomp.jsonml +

+
+
+
Package Hierarchies:
All Packages
+
+

+Class Hierarchy +

+ +

+Enum Hierarchy +

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/package-frame.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/package-frame.html new file mode 100644 index 0000000..e93d8a1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/package-frame.html @@ -0,0 +1,335 @@ + + + + + +com.google.javascript.jscomp (Compiler) + + + + + + + + + + +com.google.javascript.jscomp + + + + +
+Interfaces  + +
+AstValidator.ViolationHandler +
+CodingConvention +
+CompilerOptions.AliasTransformation +
+CompilerOptions.AliasTransformationHandler +
+CompilerPass +
+CssRenamingMap +
+ErrorHandler +
+ErrorManager +
+FunctionInformationMap.EntryOrBuilder +
+FunctionInformationMap.ModuleOrBuilder +
+FunctionInformationMapOrBuilder +
+HotSwapCompilerPass +
+InstrumentationOrBuilder +
+JsMessage.IdGenerator +
+MessageBundle +
+MessageFormatter +
+NodeTraversal.Callback +
+NodeTraversal.ScopedCallback +
+Region +
+SourceAst +
+SourceExcerptProvider +
+SourceExcerptProvider.ExcerptFormatter +
+SourceFile.Generator
+ + + + + + +
+Classes  + +
+AbstractCompiler +
+AbstractMessageFormatter +
+AstValidator +
+BasicErrorManager +
+CallGraph +
+ClosureCodingConvention +
+CodingConvention.AssertionFunctionSpec +
+CodingConvention.Bind +
+CodingConvention.DelegateRelationship +
+CodingConvention.ObjectLiteralCast +
+CodingConvention.SubclassRelationship +
+CodingConventions +
+CodingConventions.Proxy +
+CommandLineRunner +
+Compiler +
+Compiler.CodeBuilder +
+Compiler.IntermediateState +
+CompilerInput +
+CompilerOptions +
+ComposeWarningsGuard +
+CssRenamingMap.ByPart +
+CssRenamingMap.ByWhole +
+DefaultPassConfig +
+DependencyOptions +
+DiagnosticGroup +
+DiagnosticGroups +
+DiagnosticGroupWarningsGuard +
+DiagnosticType +
+DotFormatter +
+EmptyMessageBundle +
+FieldCleanupPass +
+FindExportableNodes +
+FindExportableNodes.GenerateNodeContext +
+FunctionInfo +
+FunctionInformationMap +
+FunctionInformationMap.Builder +
+FunctionInformationMap.Entry +
+FunctionInformationMap.Entry.Builder +
+FunctionInformationMap.Module +
+FunctionInformationMap.Module.Builder +
+GoogleCodingConvention +
+GoogleJsMessageIdGenerator +
+Instrumentation +
+Instrumentation.Builder +
+InstrumentationTemplate +
+JqueryCodingConvention +
+JsAst +
+JSError +
+JsMessage +
+JsMessage.Builder +
+JsMessage.PlaceholderReference +
+JsMessageExtractor +
+JSModule +
+JSModuleGraph +
+JSSourceFile +
+LightweightMessageFormatter +
+LoggerErrorManager +
+NodeTraversal +
+NodeTraversal.AbstractNodeTypePruningCallback +
+NodeTraversal.AbstractPostOrderCallback +
+NodeTraversal.AbstractScopedCallback +
+NodeTraversal.AbstractShallowCallback +
+NodeTraversal.AbstractShallowStatementCallback +
+NodeUtil +
+ObjectPropertyStringPreprocess +
+PassConfig +
+PassFactory +
+PeepholeCollectPropertyAssignments +
+PerformanceTracker +
+PerformanceTracker.Stats +
+PrintStreamErrorManager +
+ProcessCommonJSModules +
+Result +
+Scope +
+Scope.Arguments +
+Scope.Var +
+ShowByPathWarningsGuard +
+SimpleRegion +
+SourceFile +
+SourceFile.Builder +
+SourceMap +
+SourceMap.LocationMapping +
+StatementFusion +
+StrictWarningsGuard +
+SymbolTable +
+SymbolTable.Reference +
+SymbolTable.Symbol +
+SymbolTable.SymbolScope +
+SyntheticAst +
+TypeCheck +
+VariableMap +
+WarningsGuard +
+WhitelistWarningsGuard +
+WhitelistWarningsGuard.WhitelistBuilder +
+XtbMessageBundle
+ + + + + + +
+Enums  + +
+AnonymousFunctionNamingPolicy +
+CheckLevel +
+CheckLevelLegacy +
+CodingConvention.SubclassType +
+CompilationLevel +
+CompilerOptions.LanguageMode +
+CompilerOptions.Reach +
+CompilerOptions.TracerMode +
+CompilerOptions.TweakProcessing +
+CssRenamingMap.Style +
+CustomPassExecutionTime +
+ErrorFormat +
+JsMessage.Style +
+PropertyRenamingPolicy +
+ShowByPathWarningsGuard.ShowType +
+SourceExcerptProvider.SourceExcerpt +
+SourceMap.DetailLevel +
+SourceMap.Format +
+VariableRenamingPolicy +
+WarningLevel +
+WarningsGuard.Priority
+ + + + + + +
+Exceptions  + +
+JSModuleGraph.ModuleDependenceException
+ + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/package-summary.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/package-summary.html new file mode 100644 index 0000000..3d85e41 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/package-summary.html @@ -0,0 +1,825 @@ + + + + + +com.google.javascript.jscomp (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+

+Package com.google.javascript.jscomp +

+Provides the core compiler and its public API. +

+See: +
+          Description +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Interface Summary
AstValidator.ViolationHandler 
CodingConventionCodingConvention defines a set of hooks to customize the behavior of the + Compiler for a specific team/company.
CompilerOptions.AliasTransformationA Role Specific Interface for the JsCompiler to report aliases used to + change the code during a compile.
CompilerOptions.AliasTransformationHandlerA Role Specific Interface for JsCompiler that represents a data holder + object which is used to store goog.scope alias code changes to code made + during a compile.
CompilerPassInterface for classes that can compile JS.
CssRenamingMapInterface used by ReplaceCssNames to substitute CSS class names.
ErrorHandlerThe error handler is any generic sink for warnings and errors, + after they've passed through any filtering WarningsGuards.
ErrorManagerThe error manager is in charge of storing, organizing and displaying + errors and warnings generated by the compiler.
FunctionInformationMap.EntryOrBuilder 
FunctionInformationMap.ModuleOrBuilder 
FunctionInformationMapOrBuilder 
HotSwapCompilerPassInterface for compiler passes that can be used in a hot-swap fashion.
InstrumentationOrBuilder 
JsMessage.IdGenerator 
MessageBundleAn interface for providing alterative values for user-visible messages in + javascript code.
MessageFormatterFormat warnings and errors.
NodeTraversal.CallbackCallback
NodeTraversal.ScopedCallbackCallback that also knows about scope changes
RegionSource code region.
SourceAstAn interface for accessing the AST root of an input.
SourceExcerptProviderA source excerpt provider is responsible for building source code excerpt + of specific locations, such as a specific line or a region around a + given line number.
SourceExcerptProvider.ExcerptFormatterA excerpt formatter is responsible of formatting source excerpts.
SourceFile.GeneratorA JavaScript source code provider.
+  + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Class Summary
AbstractCompilerAn abstract compiler, to help remove the circular dependency of + passes on JSCompiler.
AbstractMessageFormatterAbstract message formatter providing default behavior for implementations + of MessageFormatter needing a SourceExcerptProvider.
AstValidatorThis class walks the AST and validates that the structure is correct.
BasicErrorManagerA basic error manager that sorts all errors and warnings reported to it to + generate a sorted report when the BasicErrorManager.generateReport() method + is called.
CallGraphA pass the uses a DefinitionProvider to compute a call graph for an + AST.
ClosureCodingConventionThis describes the Closure-specific JavaScript coding conventions.
CodingConvention.AssertionFunctionSpecA function that will throw an exception when either: + -One or more of its parameters evaluate to false.
CodingConvention.Bind 
CodingConvention.DelegateRelationshipDelegates provides a mechanism and structure for identifying where classes + can call out to optional code to augment their functionality.
CodingConvention.ObjectLiteralCastAn object literal cast provides a mechanism to cast object literals to + other types without a warning.
CodingConvention.SubclassRelationship 
CodingConventionsHelper classes for dealing with coding conventions.
CodingConventions.ProxyA convention that wraps another.
CommandLineRunnerCommandLineRunner translates flags into Java API calls on the Compiler.
CompilerCompiler (and the other classes in this package) does the following: + + parses JS code + checks for undefined variables + performs optimizations such as constant folding and constants inlining + renames variables (to short names) + outputs compact javascript code + + + External variables are declared in 'externs' files.
Compiler.CodeBuilderStores a buffer of text to which more can be appended.
Compiler.IntermediateStateStores the internal compiler state just before optimization is performed.
CompilerInputA class for the internal representation of an input to the compiler.
CompilerOptionsCompiler options
ComposeWarningsGuardWarningsGuard that represents just a chain of other guards.
CssRenamingMap.ByPart 
CssRenamingMap.ByWhole 
DefaultPassConfigPass factories and meta-data for native JSCompiler passes.
DependencyOptionsOptions for how to manage dependencies between input files.
DiagnosticGroupGroup a set of related diagnostic types together, so that they can + be toggled on and off as one unit.
DiagnosticGroupsNamed groups of DiagnosticTypes exposed by Compiler.
DiagnosticGroupWarningsGuardSets the level for a particular DiagnosticGroup.
DiagnosticTypeThe type of a compile or analysis error.
DotFormatterDotFormatter prints out a dot file of the Abstract Syntax Tree.
EmptyMessageBundleAn implementation of MessageBundle that has no translations.
FieldCleanupPassA CleanupPass implementation that will remove all field declarations on + JSTypes contributed by the original file.
FindExportableNodesRecords all of the symbols and properties that should be exported.
FindExportableNodes.GenerateNodeContextContext holding the node references required for generating the export + calls.
FunctionInfo 
FunctionInformationMap 
FunctionInformationMap.Builder 
FunctionInformationMap.Entry 
FunctionInformationMap.Entry.Builder 
FunctionInformationMap.Module 
FunctionInformationMap.Module.Builder 
GoogleCodingConventionThis describes the Google-specific JavaScript coding conventions.
GoogleJsMessageIdGeneratorAn JsMessage.IdGenerator designed to play nicely with Google's Translation + systems.
Instrumentation 
Instrumentation.Builder 
InstrumentationTemplate 
JqueryCodingConventionThis describes the jQuery specific JavaScript coding conventions.
JsAstGenerates an AST for a JavaScript source file.
JSErrorCompile error description
JsMessageA representation of a translatable message in JavaScript source code.
JsMessage.BuilderContains functionality for creating js messages.
JsMessage.PlaceholderReferenceA reference to a placeholder in a translatable message.
JsMessageExtractorExtracts messages and message comments from JS code.
JSModuleA JavaScript module has a unique name, consists of a list of compiler inputs, + and can depend on other modules.
JSModuleGraphA JSModule dependency graph that assigns a depth to each module and + can answer depth-related queries about them.
JSSourceFileAn abstract representation of a JavaScript source file, as input to + JSCompiler.
LightweightMessageFormatterLightweight message formatter.
LoggerErrorManagerAn error manager that logs errors and warnings using a logger in addition to + collecting them in memory.
NodeTraversalNodetraversal allows an iteration through the nodes in the parse tree, + and facilitates the optimizations on the parse tree.
NodeTraversal.AbstractNodeTypePruningCallbackAbstract callback to visit a pruned set of nodes.
NodeTraversal.AbstractPostOrderCallbackAbstract callback to visit all nodes in post order.
NodeTraversal.AbstractScopedCallbackAbstract scoped callback to visit all nodes in post order.
NodeTraversal.AbstractShallowCallbackAbstract callback to visit all nodes but not traverse into function + bodies.
NodeTraversal.AbstractShallowStatementCallbackAbstract callback to visit all structure and statement nodes but doesn't + traverse into functions or expressions.
NodeUtilNodeUtil contains utilities that get properties from the Node object.
ObjectPropertyStringPreprocessRewrites new goog.testing.ObjectPropertyString(foo, 'bar') to + new JSCompiler_ObjectPropertyString(window, foo.bar).
PassConfigPass factories and meta-data for native Compiler passes.
PassFactoryA factory for creating JSCompiler passes based on the Options + injected.
PeepholeCollectPropertyAssignmentsA pass that looks for assignments to properties of an object or array + immediately following its creation using the abbreviated syntax.
PerformanceTracker 
PerformanceTracker.Stats 
PrintStreamErrorManagerAn error manager that prints errors and warnings to the print stream + provided in addition to the functionality of the + BasicErrorManager.
ProcessCommonJSModulesRewrites a Common JS module http://wiki.commonjs.org/wiki/Modules/1.1.1 + into a form that can be safely concatenated.
ResultCompilation results
ScopeScope contains information about a variable scope in javascript.
Scope.ArgumentsA special subclass of Var used to distinguish "arguments" in the current + scope.
Scope.VarStores info about a variable
ShowByPathWarningsGuardControl whether warnings should be restricted or suppressed for specified + paths.
SimpleRegionSimple region.
SourceFileAn abstract representation of a source file that provides access to + language-neutral features.
SourceFile.BuilderA builder interface for source files.
SourceMapCollects information mapping the generated (compiled) source back to + its original source for debugging purposes.
SourceMap.LocationMapping 
StatementFusionTries to fuse all the statements in a block into a one statement by using + COMMAs.
StrictWarningsGuardAll warnings should be reported as errors.
SymbolTableA symbol table for people that want to use Closure Compiler as an indexer.
SymbolTable.Reference 
SymbolTable.Symbol 
SymbolTable.SymbolScope 
SyntheticAstAn AST generated totally by the compiler.
TypeCheckChecks the types of JS expressions against any declared type + information.
VariableMapStores the mapping from original variable name to new variable names.
WarningsGuardClass that allows to flexibly manage what to do with a reported + warning/error.
WhitelistWarningsGuardAn extension of WarningsGuard that provides functionality to maintain + a list of warnings (white-list).
WhitelistWarningsGuard.WhitelistBuilder 
XtbMessageBundleA MessageBundle that parses messages from an XML Translation Bundle (XTB) + file.
+  + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Enum Summary
AnonymousFunctionNamingPolicyStrategies for how to do naming of anonymous functions that occur as + r-values in assignments and variable declarations.
CheckLevelControls checking levels of certain options.
CheckLevelLegacyEnum used in flags to control the behavior of JS compiler checks.
CodingConvention.SubclassType 
CompilationLevelA CompilationLevel represents the level of optimization that should be + applied when compiling JavaScript code.
CompilerOptions.LanguageModeWhen to do the extra sanity checks
CompilerOptions.Reach 
CompilerOptions.TracerMode 
CompilerOptions.TweakProcessing 
CssRenamingMap.Style 
CustomPassExecutionTimeCustom pass type.
ErrorFormatError formats available.
JsMessage.StyleMessage style that could be used for JS code parsing.
PropertyRenamingPolicyPolicies to determine how properties should be renamed.
ShowByPathWarningsGuard.ShowTypeControls whether warnings should be restricted to a specified path or + suppressed within the specified path.
SourceExcerptProvider.SourceExcerptSource excerpt variety.
SourceMap.DetailLevelSource maps can be very large different levels of detail can be specified.
SourceMap.Format 
VariableRenamingPolicyPolicies to determine which variables should be renamed.
WarningLevelConvert the warnings level to an Options object.
WarningsGuard.Priority 
+  + +

+ + + + + + + + + +
+Exception Summary
JSModuleGraph.ModuleDependenceException 
+  + +

+

+Package com.google.javascript.jscomp Description +

+ +

+Provides the core compiler and its public API. +

+ +

+

+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/package-tree.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/package-tree.html new file mode 100644 index 0000000..864cf93 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/package-tree.html @@ -0,0 +1,290 @@ + + + + + +com.google.javascript.jscomp Class Hierarchy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Hierarchy For Package com.google.javascript.jscomp +

+
+
+
Package Hierarchies:
All Packages
+
+

+Class Hierarchy +

+ +

+Interface Hierarchy +

+ +

+Enum Hierarchy +

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/Config.LanguageMode.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/Config.LanguageMode.html new file mode 100644 index 0000000..8992e0c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/Config.LanguageMode.html @@ -0,0 +1,353 @@ + + + + + +Config.LanguageMode (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.parsing +
+Enum Config.LanguageMode

+
+java.lang.Object
+  extended by java.lang.Enum<Config.LanguageMode>
+      extended by com.google.javascript.jscomp.parsing.Config.LanguageMode
+
+
+
All Implemented Interfaces:
Serializable, Comparable<Config.LanguageMode>
+
+
+
Enclosing class:
Config
+
+
+
+
public static enum Config.LanguageMode
extends Enum<Config.LanguageMode>
+ + +

+


+ +

+ + + + + + + + + + + + + + + + +
+Enum Constant Summary
ECMASCRIPT3 + +
+           
ECMASCRIPT5 + +
+           
ECMASCRIPT5_STRICT + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static Config.LanguageModevalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static Config.LanguageMode[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+ECMASCRIPT3

+
+public static final Config.LanguageMode ECMASCRIPT3
+
+
+
+
+
+ +

+ECMASCRIPT5

+
+public static final Config.LanguageMode ECMASCRIPT5
+
+
+
+
+
+ +

+ECMASCRIPT5_STRICT

+
+public static final Config.LanguageMode ECMASCRIPT5_STRICT
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static Config.LanguageMode[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (Config.LanguageMode c : Config.LanguageMode.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static Config.LanguageMode valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/Config.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/Config.html new file mode 100644 index 0000000..0ae6796 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/Config.html @@ -0,0 +1,225 @@ + + + + + +Config (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.parsing +
+Class Config

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.parsing.Config
+
+
+
+
public class Config
extends Object
+ + +

+Configuration for the AST factory. Should be shared across AST creation + for all files of a compilation process. +

+ +

+


+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classConfig.LanguageMode + +
+           
+  + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ +


+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/JsDocInfoParser.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/JsDocInfoParser.html new file mode 100644 index 0000000..0a4f838 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/JsDocInfoParser.html @@ -0,0 +1,239 @@ + + + + + +JsDocInfoParser (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.parsing +
+Class JsDocInfoParser

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.parsing.JsDocInfoParser
+
+
+
+
public final class JsDocInfoParser
extends Object
+ + +

+A parser for JSDoc comments. +

+ +

+


+ +

+ + + + + + + + + + + + +
+Method Summary
+static NodeparseTypeString(String typeString) + +
+          Parses a string containing a JsDoc type declaration, returning the + type if the parsing succeeded or null if it failed.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+parseTypeString

+
+public static Node parseTypeString(String typeString)
+
+
Parses a string containing a JsDoc type declaration, returning the + type if the parsing succeeded or null if it failed. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/NullErrorReporter.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/NullErrorReporter.html new file mode 100644 index 0000000..95b6479 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/NullErrorReporter.html @@ -0,0 +1,304 @@ + + + + + +NullErrorReporter (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.parsing +
+Class NullErrorReporter

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.parsing.NullErrorReporter
+
+
+
+
public abstract class NullErrorReporter
extends Object
+ + +

+An error reporter which consumes all calls and performs no actions. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voiderror(String message, + String sourceName, + int line, + int lineOffset) + +
+           
+static com.google.javascript.rhino.head.ErrorReporterforNewRhino() + +
+           
+static ErrorReporterforOldRhino() + +
+           
+ voidwarning(String message, + String sourceName, + int line, + int lineOffset) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+error

+
+public void error(String message,
+                  String sourceName,
+                  int line,
+                  int lineOffset)
+
+
+
+
+
+
+ +

+warning

+
+public void warning(String message,
+                    String sourceName,
+                    int line,
+                    int lineOffset)
+
+
+
+
+
+
+ +

+forOldRhino

+
+public static ErrorReporter forOldRhino()
+
+
+
+
+
+
+ +

+forNewRhino

+
+public static com.google.javascript.rhino.head.ErrorReporter forNewRhino()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/ParserRunner.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/ParserRunner.html new file mode 100644 index 0000000..a04bf97 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/ParserRunner.html @@ -0,0 +1,315 @@ + + + + + +ParserRunner (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.parsing +
+Class ParserRunner

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.parsing.ParserRunner
+
+
+
+
public class ParserRunner
extends Object
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static ConfigcreateConfig(boolean isIdeMode) + +
+          Deprecated. 
+static ConfigcreateConfig(boolean isIdeMode, + Config.LanguageMode languageMode, + boolean acceptConstKeyword) + +
+           
+static ConfigcreateConfig(boolean isIdeMode, + Config.LanguageMode languageMode, + boolean acceptConstKeyword, + Set<String> extraAnnotationNames) + +
+           
+static Nodeparse(StaticSourceFile sourceFile, + String sourceString, + Config config, + com.google.javascript.rhino.head.ErrorReporter errorReporter, + Logger logger) + +
+          Parses the JavaScript text given by a reader.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+createConfig

+
+@Deprecated
+public static Config createConfig(boolean isIdeMode)
+
+
Deprecated.  +

+

+
+
+
+
+ +

+createConfig

+
+public static Config createConfig(boolean isIdeMode,
+                                  Config.LanguageMode languageMode,
+                                  boolean acceptConstKeyword)
+
+
+
+
+
+
+ +

+createConfig

+
+public static Config createConfig(boolean isIdeMode,
+                                  Config.LanguageMode languageMode,
+                                  boolean acceptConstKeyword,
+                                  Set<String> extraAnnotationNames)
+
+
+
+
+
+
+ +

+parse

+
+public static Node parse(StaticSourceFile sourceFile,
+                         String sourceString,
+                         Config config,
+                         com.google.javascript.rhino.head.ErrorReporter errorReporter,
+                         Logger logger)
+                  throws IOException
+
+
Parses the JavaScript text given by a reader. +

+

+
Parameters:
sourceString - Source code from the file.
errorReporter - An error.
logger - A logger. +
Returns:
The AST of the given text. +
Throws: +
IOException
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/package-frame.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/package-frame.html new file mode 100644 index 0000000..5970d1e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/package-frame.html @@ -0,0 +1,47 @@ + + + + + +com.google.javascript.jscomp.parsing (Compiler) + + + + + + + + + + +com.google.javascript.jscomp.parsing + + + + +
+Classes  + +
+Config +
+JsDocInfoParser +
+NullErrorReporter +
+ParserRunner
+ + + + + + +
+Enums  + +
+Config.LanguageMode
+ + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/package-summary.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/package-summary.html new file mode 100644 index 0000000..ae8420f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/package-summary.html @@ -0,0 +1,212 @@ + + + + + +com.google.javascript.jscomp.parsing (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+

+Package com.google.javascript.jscomp.parsing +

+Provides utilities to help with parsing JSDoc annotations and performing AST +transformations. +

+See: +
+          Description +

+ + + + + + + + + + + + + + + + + + + + + +
+Class Summary
ConfigConfiguration for the AST factory.
JsDocInfoParserA parser for JSDoc comments.
NullErrorReporterAn error reporter which consumes all calls and performs no actions.
ParserRunner 
+  + +

+ + + + + + + + + +
+Enum Summary
Config.LanguageMode 
+  + +

+

+Package com.google.javascript.jscomp.parsing Description +

+ +

+Provides utilities to help with parsing JSDoc annotations and performing AST +transformations. +

+ +

+

+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/package-tree.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/package-tree.html new file mode 100644 index 0000000..963cb34 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/parsing/package-tree.html @@ -0,0 +1,175 @@ + + + + + +com.google.javascript.jscomp.parsing Class Hierarchy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Hierarchy For Package com.google.javascript.jscomp.parsing +

+
+
+
Package Hierarchies:
All Packages
+
+

+Class Hierarchy +

+ +

+Enum Hierarchy +

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/regex/CaseCanonicalize.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/regex/CaseCanonicalize.html new file mode 100644 index 0000000..009407e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/regex/CaseCanonicalize.html @@ -0,0 +1,406 @@ + + + + + +CaseCanonicalize (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.regex +
+Class CaseCanonicalize

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.regex.CaseCanonicalize
+
+
+
+
public final class CaseCanonicalize
extends Object
+ + +

+Implements the EcmaScript 5 + Canonicalize operation + used to specify how case-insensitive regular expressions match. + +

+ From section 15.10.2.9, +

+ The abstract operation Canonicalize takes a character parameter ch and + performs the following steps: +
    +
  • If IgnoreCase is false, return ch. +
  • Let u be ch converted to upper case as if by calling the standard + built-in method String.prototype.toUpperCase on the one-character + String ch. +
  • If u does not consist of a single character, return ch. +
  • Let cu be u's character. +
  • If ch's code unit value is greater than or equal to decimal 128 and + cu's code unit value is less than decimal 128, then return ch. +
  • Return cu. +
+

+ +

+


+ +

+ + + + + + + + + + + +
+Field Summary
+static com.google.javascript.jscomp.regex.CharRangesCASE_SENSITIVE + +
+          Set of code units that are case-insensitively equivalent to some other + code unit according to the EcmaScript + Canonicalize operation + described in section 15.10.2.8.
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static charcaseCanonicalize(char ch) + +
+          Returns the case canonical version of the given code-unit.
+static StringcaseCanonicalize(String s) + +
+          Returns the case canonical version of the given string.
+static com.google.javascript.jscomp.regex.CharRangesexpandToAllMatched(com.google.javascript.jscomp.regex.CharRanges ranges) + +
+          Given a character range that may include case sensitive code-units, + such as [0-9B-M], returns the character range that includes all + the code-units in the input and those that are case-insensitively + equivalent to a code-unit in the input.
+static com.google.javascript.jscomp.regex.CharRangesreduceToMinimum(com.google.javascript.jscomp.regex.CharRanges ranges) + +
+          Given a character range that may include case sensitive code-units, + such as [0-9B-M], returns the character range that includes + the minimal set of code units such that for every code unit in the + input there is a case-sensitively equivalent canonical code unit in the + output.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+CASE_SENSITIVE

+
+public static final com.google.javascript.jscomp.regex.CharRanges CASE_SENSITIVE
+
+
Set of code units that are case-insensitively equivalent to some other + code unit according to the EcmaScript + Canonicalize operation + described in section 15.10.2.8. + The case sensitive characters are the ones that canonicalize to a character + other than themselves or have a character that canonicalizes to them. + Canonicalize is based on the definition of + String.prototype.toUpperCase which is itself based on Unicode 3.0.0 + as specified at + + UnicodeData-3.0.0 + + and + SpecialCasings-2.txt + . + +

+ This table was generated by running the below on Chrome: +

+
+ for (var cc = 0; cc < 0x10000; ++cc) {
+   var ch = String.fromCharCode(cc);
+   var u = ch.toUpperCase();
+   if (ch != u && u.length === 1) {
+     var cu = u.charCodeAt(0);
+     if (cc <= 128 || u.charCodeAt(0) > 128) {
+       print('0x' + cc.toString(16) + ', 0x' + cu.toString(16) + ',');
+     }
+   }
+ }
+ 
+

+

+
+
+ + + + + + + + +
+Method Detail
+ +

+caseCanonicalize

+
+public static String caseCanonicalize(String s)
+
+
Returns the case canonical version of the given string. +

+

+
+
+
+
+ +

+caseCanonicalize

+
+public static char caseCanonicalize(char ch)
+
+
Returns the case canonical version of the given code-unit. EcmaScript 5 + explicitly says that code-units are to be treated as their code-point + equivalent, even surrogates. +

+

+
+
+
+
+ +

+expandToAllMatched

+
+public static com.google.javascript.jscomp.regex.CharRanges expandToAllMatched(com.google.javascript.jscomp.regex.CharRanges ranges)
+
+
Given a character range that may include case sensitive code-units, + such as [0-9B-M], returns the character range that includes all + the code-units in the input and those that are case-insensitively + equivalent to a code-unit in the input. +

+

+
+
+
+
+ +

+reduceToMinimum

+
+public static com.google.javascript.jscomp.regex.CharRanges reduceToMinimum(com.google.javascript.jscomp.regex.CharRanges ranges)
+
+
Given a character range that may include case sensitive code-units, + such as [0-9B-M], returns the character range that includes + the minimal set of code units such that for every code unit in the + input there is a case-sensitively equivalent canonical code unit in the + output. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/regex/RegExpTree.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/regex/RegExpTree.html new file mode 100644 index 0000000..d93d097 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/regex/RegExpTree.html @@ -0,0 +1,552 @@ + + + + + +RegExpTree (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.regex +
+Class RegExpTree

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.regex.RegExpTree
+
+
+
+
public abstract class RegExpTree
extends Object
+ + +

+An AST for JavaScript regular expressions. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
RegExpTree() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+protected abstract  voidappendDebugInfo(StringBuilder sb) + +
+           
+protected abstract  voidappendSourceCode(StringBuilder sb) + +
+          Appends this regular expression source to the given buffer.
+abstract  List<? extends RegExpTree>children() + +
+          The children of this node.
+abstract  booleancontainsAnchor() + +
+          True if the regular expression contains an anchor : ^ or $.
+abstract  booleanequals(Object o) + +
+           
+ booleanhasCapturingGroup() + +
+          True if the regular expression contains capturing groups.
+abstract  inthashCode() + +
+           
+abstract  booleanisCaseSensitive() + +
+          True if the presence or absence of an "i" flag would change the + meaning of this regular expression.
+static booleanmatchesWholeInput(RegExpTree t, + String flags) + +
+          True if, but not necessarily always when the, given regular expression + must match the whole input or none of it.
+abstract  intnumCapturingGroups() + +
+          The number of capturing groups.
+static RegExpTreeparseRegExp(String pattern, + String flags) + +
+          Parses a regular expression to an AST.
+abstract  RegExpTreesimplify(String flags) + +
+          Returns a simpler regular expression that is semantically the same assuming + the given flags.
+ StringtoDebugString() + +
+           
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+RegExpTree

+
+public RegExpTree()
+
+
+ + + + + + + + +
+Method Detail
+ +

+simplify

+
+public abstract RegExpTree simplify(String flags)
+
+
Returns a simpler regular expression that is semantically the same assuming + the given flags. +

+

+
Parameters:
flags - Regular expression flags, e.g. "igm".
+
+
+
+ +

+isCaseSensitive

+
+public abstract boolean isCaseSensitive()
+
+
True if the presence or absence of an "i" flag would change the + meaning of this regular expression. +

+

+
+
+
+
+ +

+containsAnchor

+
+public abstract boolean containsAnchor()
+
+
True if the regular expression contains an anchor : ^ or $. +

+

+
+
+
+
+ +

+hasCapturingGroup

+
+public final boolean hasCapturingGroup()
+
+
True if the regular expression contains capturing groups. +

+

+
+
+
+
+ +

+numCapturingGroups

+
+public abstract int numCapturingGroups()
+
+
The number of capturing groups. +

+

+
+
+
+
+ +

+children

+
+public abstract List<? extends RegExpTree> children()
+
+
The children of this node. +

+

+
+
+
+
+ +

+appendSourceCode

+
+protected abstract void appendSourceCode(StringBuilder sb)
+
+
Appends this regular expression source to the given buffer. +

+

+
+
+
+
+ +

+appendDebugInfo

+
+protected abstract void appendDebugInfo(StringBuilder sb)
+
+
+
+
+
+
+ +

+toString

+
+public final String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+
+ +

+toDebugString

+
+public final String toDebugString()
+
+
+
+
+
+
+ +

+equals

+
+public abstract boolean equals(Object o)
+
+
+
Overrides:
equals in class Object
+
+
+
+
+
+
+ +

+hashCode

+
+public abstract int hashCode()
+
+
+
Overrides:
hashCode in class Object
+
+
+
+
+
+
+ +

+parseRegExp

+
+public static RegExpTree parseRegExp(String pattern,
+                                     String flags)
+
+
Parses a regular expression to an AST. +

+

+
Parameters:
pattern - The foo From /foo/i.
flags - The i From /foo/i.
+
+
+
+ +

+matchesWholeInput

+
+public static boolean matchesWholeInput(RegExpTree t,
+                                        String flags)
+
+
True if, but not necessarily always when the, given regular expression + must match the whole input or none of it. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/regex/package-frame.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/regex/package-frame.html new file mode 100644 index 0000000..7d72415 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/regex/package-frame.html @@ -0,0 +1,32 @@ + + + + + +com.google.javascript.jscomp.regex (Compiler) + + + + + + + + + + +com.google.javascript.jscomp.regex + + + + +
+Classes  + +
+CaseCanonicalize +
+RegExpTree
+ + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/regex/package-summary.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/regex/package-summary.html new file mode 100644 index 0000000..35f2a58 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/regex/package-summary.html @@ -0,0 +1,175 @@ + + + + + +com.google.javascript.jscomp.regex (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+

+Package com.google.javascript.jscomp.regex +

+ + + + + + + + + + + + + +
+Class Summary
CaseCanonicalizeImplements the EcmaScript 5 + Canonicalize operation + used to specify how case-insensitive regular expressions match.
RegExpTreeAn AST for JavaScript regular expressions.
+  + +

+

+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/regex/package-tree.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/regex/package-tree.html new file mode 100644 index 0000000..1c5f3cf --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/regex/package-tree.html @@ -0,0 +1,165 @@ + + + + + +com.google.javascript.jscomp.regex Class Hierarchy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Hierarchy For Package com.google.javascript.jscomp.regex +

+
+
+
Package Hierarchies:
All Packages
+
+

+Class Hierarchy +

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/testing/SimpleSourceExcerptProvider.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/testing/SimpleSourceExcerptProvider.html new file mode 100644 index 0000000..28d6c27 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/testing/SimpleSourceExcerptProvider.html @@ -0,0 +1,331 @@ + + + + + +SimpleSourceExcerptProvider (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.testing +
+Class SimpleSourceExcerptProvider

+
+java.lang.Object
+  extended by com.google.javascript.jscomp.testing.SimpleSourceExcerptProvider
+
+
+
All Implemented Interfaces:
SourceExcerptProvider
+
+
+
+
public class SimpleSourceExcerptProvider
extends Object
implements SourceExcerptProvider
+ + +

+A simple source excerpt provider for testing. +

+ +

+


+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from interface com.google.javascript.jscomp.SourceExcerptProvider
SourceExcerptProvider.ExcerptFormatter, SourceExcerptProvider.SourceExcerpt
+  + + + + + + + + + + + +
+Constructor Summary
SimpleSourceExcerptProvider(String source) + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+ StringgetSourceLine(String sourceName, + int lineNumber) + +
+          Get the line indicated by the line number.
+ RegiongetSourceRegion(String sourceName, + int lineNumber) + +
+          Get a region around the indicated line number.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SimpleSourceExcerptProvider

+
+public SimpleSourceExcerptProvider(String source)
+
+
+ + + + + + + + +
+Method Detail
+ +

+getSourceLine

+
+public String getSourceLine(String sourceName,
+                            int lineNumber)
+
+
Description copied from interface: SourceExcerptProvider
+
Get the line indicated by the line number. This call will return only the + specific line. +

+

+
Specified by:
getSourceLine in interface SourceExcerptProvider
+
+
+
lineNumber - the line number, 1 being the first line of the file +
Returns:
the line indicated, or null if it does not exist
+
+
+
+ +

+getSourceRegion

+
+public Region getSourceRegion(String sourceName,
+                              int lineNumber)
+
+
Description copied from interface: SourceExcerptProvider
+
Get a region around the indicated line number. The exact definition of a + region is implementation specific, but it must contain the line indicated + by the line number. A region must not start or end by a carriage return. +

+

+
Specified by:
getSourceRegion in interface SourceExcerptProvider
+
+
+
lineNumber - the line number, 1 being the first line of the file +
Returns:
the region around the line number indicated, or null + if it does not exist
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/testing/TestErrorReporter.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/testing/TestErrorReporter.html new file mode 100644 index 0000000..8b02216 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/testing/TestErrorReporter.html @@ -0,0 +1,411 @@ + + + + + +TestErrorReporter (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.jscomp.testing +
+Class TestErrorReporter

+
+java.lang.Object
+  extended by junit.framework.Assert
+      extended by com.google.javascript.jscomp.testing.TestErrorReporter
+
+
+
All Implemented Interfaces:
com.google.javascript.rhino.head.ErrorReporter
+
+
+
+
public final class TestErrorReporter
extends junit.framework.Assert
implements com.google.javascript.rhino.head.ErrorReporter
+ + +

+

An error reporter for testing that verifies that messages reported to the + reporter are expected.

+ +

Sample use

+
+ TestErrorReporter e =
+   new TestErrorReporter(null, new String[] { "first warning" });
+ ...
+ assertTrue(e.hasEncounteredAllWarnings());
+ 
+

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
TestErrorReporter(String[] errors, + String[] warnings) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voiderror(String message, + String sourceName, + int line, + String lineSource, + int lineOffset) + +
+           
+ booleanhasEncounteredAllErrors() + +
+          Returns whether all errors were reported to this reporter.
+ booleanhasEncounteredAllWarnings() + +
+          Returns whether all warnings were reported to this reporter.
+ com.google.javascript.rhino.head.EvaluatorExceptionruntimeError(String message, + String sourceName, + int line, + String lineSource, + int lineOffset) + +
+           
+ voidwarning(String message, + String sourceName, + int line, + String lineSource, + int lineOffset) + +
+           
+ + + + + + + +
Methods inherited from class junit.framework.Assert
assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, fail, fail, failNotEquals, failNotSame, failSame, format
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+TestErrorReporter

+
+public TestErrorReporter(String[] errors,
+                         String[] warnings)
+
+
+ + + + + + + + +
+Method Detail
+ +

+error

+
+public void error(String message,
+                  String sourceName,
+                  int line,
+                  String lineSource,
+                  int lineOffset)
+
+
+
Specified by:
error in interface com.google.javascript.rhino.head.ErrorReporter
+
+
+
+
+
+
+ +

+warning

+
+public void warning(String message,
+                    String sourceName,
+                    int line,
+                    String lineSource,
+                    int lineOffset)
+
+
+
Specified by:
warning in interface com.google.javascript.rhino.head.ErrorReporter
+
+
+
+
+
+
+ +

+runtimeError

+
+public com.google.javascript.rhino.head.EvaluatorException runtimeError(String message,
+                                                                        String sourceName,
+                                                                        int line,
+                                                                        String lineSource,
+                                                                        int lineOffset)
+
+
+
Specified by:
runtimeError in interface com.google.javascript.rhino.head.ErrorReporter
+
+
+
+
+
+
+ +

+hasEncounteredAllWarnings

+
+public boolean hasEncounteredAllWarnings()
+
+
Returns whether all warnings were reported to this reporter. +

+

+
+
+
+
+
+
+
+ +

+hasEncounteredAllErrors

+
+public boolean hasEncounteredAllErrors()
+
+
Returns whether all errors were reported to this reporter. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/testing/package-frame.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/testing/package-frame.html new file mode 100644 index 0000000..70b6bdc --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/testing/package-frame.html @@ -0,0 +1,32 @@ + + + + + +com.google.javascript.jscomp.testing (Compiler) + + + + + + + + + + +com.google.javascript.jscomp.testing + + + + +
+Classes  + +
+SimpleSourceExcerptProvider +
+TestErrorReporter
+ + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/testing/package-summary.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/testing/package-summary.html new file mode 100644 index 0000000..008828a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/testing/package-summary.html @@ -0,0 +1,174 @@ + + + + + +com.google.javascript.jscomp.testing (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+

+Package com.google.javascript.jscomp.testing +

+ + + + + + + + + + + + + +
+Class Summary
SimpleSourceExcerptProviderA simple source excerpt provider for testing.
TestErrorReporterAn error reporter for testing that verifies that messages reported to the + reporter are expected.
+  + +

+

+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/testing/package-tree.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/testing/package-tree.html new file mode 100644 index 0000000..4419797 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/jscomp/testing/package-tree.html @@ -0,0 +1,169 @@ + + + + + +com.google.javascript.jscomp.testing Class Hierarchy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Hierarchy For Package com.google.javascript.jscomp.testing +

+
+
+
Package Hierarchies:
All Packages
+
+

+Class Hierarchy +

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/ErrorReporter.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/ErrorReporter.html new file mode 100644 index 0000000..feb7e49 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/ErrorReporter.html @@ -0,0 +1,274 @@ + + + + + +ErrorReporter (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Interface ErrorReporter

+
+
All Known Implementing Classes:
SimpleErrorReporter, TestErrorReporter
+
+
+
+
public interface ErrorReporter
+ + +

+This is interface defines a protocol for the reporting of + errors during JavaScript translation or execution. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Method Summary
+ voiderror(String message, + String sourceName, + int line, + int lineOffset) + +
+          Report an error.
+ voidwarning(String message, + String sourceName, + int line, + int lineOffset) + +
+          Report a warning.
+  +

+ + + + + + + + +
+Method Detail
+ +

+warning

+
+void warning(String message,
+             String sourceName,
+             int line,
+             int lineOffset)
+
+
Report a warning. + + The implementing class may choose to ignore the warning + if it desires. +

+

+
Parameters:
message - a String describing the warning
sourceName - a String describing the JavaScript source + where the warning occured; typically a filename or URL
line - the line number associated with the warning
lineOffset - the offset into lineSource where problem was detected
+
+
+
+ +

+error

+
+void error(String message,
+           String sourceName,
+           int line,
+           int lineOffset)
+
+
Report an error. + + The implementing class is free to throw an exception if + it desires. + + If execution has not yet begun, the JavaScript engine is + free to find additional errors rather than terminating + the translation. It will not execute a script that had + errors, however. +

+

+
Parameters:
message - a String describing the error
sourceName - a String describing the JavaScript source + where the error occured; typically a filename or URL
line - the line number associated with the error
lineOffset - the offset into lineSource where problem was detected
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/IR.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/IR.html new file mode 100644 index 0000000..dcc3c47 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/IR.html @@ -0,0 +1,1489 @@ + + + + + +IR (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class IR

+
+java.lang.Object
+  extended by com.google.javascript.rhino.IR
+
+
+
+
public class IR
extends Object
+ + +

+An AST construction helper class +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static Nodeadd(Node expr1, + Node expr2) + +
+           
+static Nodeand(Node expr1, + Node expr2) + +
+           
+static Nodearraylit(Node... exprs) + +
+           
+static Nodeassign(Node target, + Node expr) + +
+           
+static Nodeblock() + +
+           
+static Nodeblock(Node... stmts) + +
+           
+static Nodeblock(Node stmt) + +
+           
+static NodebreakNode() + +
+           
+static NodebreakNode(Node name) + +
+           
+static Nodecall(Node target, + Node... args) + +
+           
+static NodecaseNode(Node expr, + Node body) + +
+           
+static NodecatchNode(Node expr, + Node body) + +
+           
+static Nodecomma(Node expr1, + Node expr2) + +
+           
+static NodecontinueNode() + +
+           
+static NodecontinueNode(Node name) + +
+           
+static NodedefaultCase(Node body) + +
+           
+static NodedoNode(Node body, + Node cond) + +
+           
+static Nodeempty() + +
+           
+static Nodeeq(Node expr1, + Node expr2) + +
+          "=="
+static NodeexprResult(Node expr) + +
+           
+static NodefalseNode() + +
+           
+static NodeforIn(Node target, + Node cond, + Node body) + +
+           
+static NodeforNode(Node init, + Node cond, + Node incr, + Node body) + +
+           
+static Nodefunction(Node name, + Node params, + Node body) + +
+           
+static Nodegetelem(Node target, + Node elem) + +
+           
+static Nodegetprop(Node target, + Node prop) + +
+           
+static Nodehook(Node cond, + Node trueval, + Node falseval) + +
+           
+static NodeifNode(Node cond, + Node then) + +
+           
+static NodeifNode(Node cond, + Node then, + Node elseNode) + +
+           
+static Nodelabel(Node name, + Node stmt) + +
+           
+static NodelabelName(String name) + +
+           
+static Nodename(String name) + +
+           
+static Nodeneg(Node expr1) + +
+           
+static NodenewNode(Node target, + Node... args) + +
+           
+static Nodenot(Node expr1) + +
+           
+static NodenullNode() + +
+           
+static Nodenumber(double d) + +
+           
+static Nodeobjectlit(Node... propdefs) + +
+           
+static Nodeor(Node expr1, + Node expr2) + +
+           
+static NodeparamList() + +
+           
+static NodeparamList(List<Node> params) + +
+           
+static NodeparamList(Node... params) + +
+           
+static NodeparamList(Node param) + +
+           
+static Nodepos(Node expr1) + +
+           
+static Nodepropdef(Node string, + Node value) + +
+           
+static Noderegexp(Node expr) + +
+           
+static Noderegexp(Node expr, + Node flags) + +
+           
+static NodereturnNode() + +
+           
+static NodereturnNode(Node expr) + +
+           
+static Nodescript(Node... stmts) + +
+           
+static Nodesheq(Node expr1, + Node expr2) + +
+          "==="
+static Nodestring(String s) + +
+           
+static Nodesub(Node expr1, + Node expr2) + +
+           
+static NodeswitchNode(Node cond, + Node... cases) + +
+           
+static NodethisNode() + +
+           
+static NodethrowNode(Node expr) + +
+           
+static NodetrueNode() + +
+           
+static NodetryCatch(Node tryBody, + Node catchNode) + +
+           
+static NodetryCatchFinally(Node tryBody, + Node catchNode, + Node finallyBody) + +
+           
+static NodetryFinally(Node tryBody, + Node finallyBody) + +
+           
+static Nodevar(Node name) + +
+           
+static Nodevar(Node name, + Node value) + +
+           
+static NodevoidNode(Node expr1) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+empty

+
+public static Node empty()
+
+
+
+
+
+
+ +

+function

+
+public static Node function(Node name,
+                            Node params,
+                            Node body)
+
+
+
+
+
+
+ +

+paramList

+
+public static Node paramList()
+
+
+
+
+
+
+ +

+paramList

+
+public static Node paramList(Node param)
+
+
+
+
+
+
+ +

+paramList

+
+public static Node paramList(Node... params)
+
+
+
+
+
+
+ +

+paramList

+
+public static Node paramList(List<Node> params)
+
+
+
+
+
+
+ +

+block

+
+public static Node block()
+
+
+
+
+
+
+ +

+block

+
+public static Node block(Node stmt)
+
+
+
+
+
+
+ +

+block

+
+public static Node block(Node... stmts)
+
+
+
+
+
+
+ +

+script

+
+public static Node script(Node... stmts)
+
+
+
+
+
+
+ +

+var

+
+public static Node var(Node name,
+                       Node value)
+
+
+
+
+
+
+ +

+var

+
+public static Node var(Node name)
+
+
+
+
+
+
+ +

+returnNode

+
+public static Node returnNode()
+
+
+
+
+
+
+ +

+returnNode

+
+public static Node returnNode(Node expr)
+
+
+
+
+
+
+ +

+throwNode

+
+public static Node throwNode(Node expr)
+
+
+
+
+
+
+ +

+exprResult

+
+public static Node exprResult(Node expr)
+
+
+
+
+
+
+ +

+ifNode

+
+public static Node ifNode(Node cond,
+                          Node then)
+
+
+
+
+
+
+ +

+ifNode

+
+public static Node ifNode(Node cond,
+                          Node then,
+                          Node elseNode)
+
+
+
+
+
+
+ +

+doNode

+
+public static Node doNode(Node body,
+                          Node cond)
+
+
+
+
+
+
+ +

+forIn

+
+public static Node forIn(Node target,
+                         Node cond,
+                         Node body)
+
+
+
+
+
+
+ +

+forNode

+
+public static Node forNode(Node init,
+                           Node cond,
+                           Node incr,
+                           Node body)
+
+
+
+
+
+
+ +

+switchNode

+
+public static Node switchNode(Node cond,
+                              Node... cases)
+
+
+
+
+
+
+ +

+caseNode

+
+public static Node caseNode(Node expr,
+                            Node body)
+
+
+
+
+
+
+ +

+defaultCase

+
+public static Node defaultCase(Node body)
+
+
+
+
+
+
+ +

+label

+
+public static Node label(Node name,
+                         Node stmt)
+
+
+
+
+
+
+ +

+labelName

+
+public static Node labelName(String name)
+
+
+
+
+
+
+ +

+tryFinally

+
+public static Node tryFinally(Node tryBody,
+                              Node finallyBody)
+
+
+
+
+
+
+ +

+tryCatch

+
+public static Node tryCatch(Node tryBody,
+                            Node catchNode)
+
+
+
+
+
+
+ +

+tryCatchFinally

+
+public static Node tryCatchFinally(Node tryBody,
+                                   Node catchNode,
+                                   Node finallyBody)
+
+
+
+
+
+
+ +

+catchNode

+
+public static Node catchNode(Node expr,
+                             Node body)
+
+
+
+
+
+
+ +

+breakNode

+
+public static Node breakNode()
+
+
+
+
+
+
+ +

+breakNode

+
+public static Node breakNode(Node name)
+
+
+
+
+
+
+ +

+continueNode

+
+public static Node continueNode()
+
+
+
+
+
+
+ +

+continueNode

+
+public static Node continueNode(Node name)
+
+
+
+
+
+
+ +

+call

+
+public static Node call(Node target,
+                        Node... args)
+
+
+
+
+
+
+ +

+newNode

+
+public static Node newNode(Node target,
+                           Node... args)
+
+
+
+
+
+
+ +

+name

+
+public static Node name(String name)
+
+
+
+
+
+
+ +

+getprop

+
+public static Node getprop(Node target,
+                           Node prop)
+
+
+
+
+
+
+ +

+getelem

+
+public static Node getelem(Node target,
+                           Node elem)
+
+
+
+
+
+
+ +

+assign

+
+public static Node assign(Node target,
+                          Node expr)
+
+
+
+
+
+
+ +

+hook

+
+public static Node hook(Node cond,
+                        Node trueval,
+                        Node falseval)
+
+
+
+
+
+
+ +

+comma

+
+public static Node comma(Node expr1,
+                         Node expr2)
+
+
+
+
+
+
+ +

+and

+
+public static Node and(Node expr1,
+                       Node expr2)
+
+
+
+
+
+
+ +

+or

+
+public static Node or(Node expr1,
+                      Node expr2)
+
+
+
+
+
+
+ +

+not

+
+public static Node not(Node expr1)
+
+
+
+
+
+
+ +

+eq

+
+public static Node eq(Node expr1,
+                      Node expr2)
+
+
"==" +

+

+
+
+
+
+ +

+sheq

+
+public static Node sheq(Node expr1,
+                        Node expr2)
+
+
"===" +

+

+
+
+
+
+ +

+voidNode

+
+public static Node voidNode(Node expr1)
+
+
+
+
+
+
+ +

+neg

+
+public static Node neg(Node expr1)
+
+
+
+
+
+
+ +

+pos

+
+public static Node pos(Node expr1)
+
+
+
+
+
+
+ +

+add

+
+public static Node add(Node expr1,
+                       Node expr2)
+
+
+
+
+
+
+ +

+sub

+
+public static Node sub(Node expr1,
+                       Node expr2)
+
+
+
+
+
+
+ +

+objectlit

+
+public static Node objectlit(Node... propdefs)
+
+
+
+
+
+
+ +

+propdef

+
+public static Node propdef(Node string,
+                           Node value)
+
+
+
+
+
+
+ +

+arraylit

+
+public static Node arraylit(Node... exprs)
+
+
+
+
+
+
+ +

+regexp

+
+public static Node regexp(Node expr)
+
+
+
+
+
+
+ +

+regexp

+
+public static Node regexp(Node expr,
+                          Node flags)
+
+
+
+
+
+
+ +

+string

+
+public static Node string(String s)
+
+
+
+
+
+
+ +

+number

+
+public static Node number(double d)
+
+
+
+
+
+
+ +

+thisNode

+
+public static Node thisNode()
+
+
+
+
+
+
+ +

+trueNode

+
+public static Node trueNode()
+
+
+
+
+
+
+ +

+falseNode

+
+public static Node falseNode()
+
+
+
+
+
+
+ +

+nullNode

+
+public static Node nullNode()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/InputId.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/InputId.html new file mode 100644 index 0000000..502ffc3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/InputId.html @@ -0,0 +1,378 @@ + + + + + +InputId (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class InputId

+
+java.lang.Object
+  extended by com.google.javascript.rhino.InputId
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class InputId
extends Object
implements Serializable
+ + +

+An id used uniquely identify a CompilerInput +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Field Summary
+static longserialVersionUID + +
+           
+  + + + + + + + + + + +
+Constructor Summary
InputId(String id) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleanequals(Object obj) + +
+           
+ StringgetIdName() + +
+           
+ inthashCode() + +
+           
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+serialVersionUID

+
+public static final long serialVersionUID
+
+
+
See Also:
Constant Field Values
+
+ + + + + + + + +
+Constructor Detail
+ +

+InputId

+
+public InputId(String id)
+
+
+ + + + + + + + +
+Method Detail
+ +

+getIdName

+
+public String getIdName()
+
+
+
+
+
+
+
+
+
+ +

+hashCode

+
+public int hashCode()
+
+
+
Overrides:
hashCode in class Object
+
+
+
+
+
+
+ +

+equals

+
+public boolean equals(Object obj)
+
+
+
Overrides:
equals in class Object
+
+
+
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.Marker.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.Marker.html new file mode 100644 index 0000000..c413a8c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.Marker.html @@ -0,0 +1,374 @@ + + + + + +JSDocInfo.Marker (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class JSDocInfo.Marker

+
+java.lang.Object
+  extended by com.google.javascript.rhino.JSDocInfo.Marker
+
+
+
Enclosing class:
JSDocInfo
+
+
+
+
public static final class JSDocInfo.Marker
extends Object
+ + +

+Defines a class for containing the parsing information + for this JSDocInfo. For each annotation found in the + JsDoc, a marker will be created indicating the annotation + itself, the name of the annotation (if any; for example, + a @param has a name, but a @return does not), the + textual description found on that annotation and, if applicable, + the type declaration. All this information is only collected + if documentation collection is turned on. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
JSDocInfo.Marker() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ JSDocInfo.StringPositiongetAnnotation() + +
+          Gets the position information for the annotation name.
+ JSDocInfo.StringPositiongetDescription() + +
+          Gets the position information for the description found + in a block tag.
+ JSDocInfo.StringPositiongetName() + +
+          Deprecated. Use #getNameNode
+ SourcePosition<Node>getNameNode() + +
+          Gets the position information for the name found + in an @param tag.
+ JSDocInfo.TypePositiongetType() + +
+          Gets the position information for the type expression found + in some block tags, like "@param" and "@return".
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JSDocInfo.Marker

+
+public JSDocInfo.Marker()
+
+
+ + + + + + + + +
+Method Detail
+ +

+getAnnotation

+
+public JSDocInfo.StringPosition getAnnotation()
+
+
Gets the position information for the annotation name. (e.g., "param") +

+

+
+
+
+
+ +

+getName

+
+@Deprecated
+public JSDocInfo.StringPosition getName()
+
+
Deprecated. Use #getNameNode +

+

Gets the position information for the name found + in a @param tag. +

+

+
+
+
+
+ +

+getNameNode

+
+public SourcePosition<Node> getNameNode()
+
+
Gets the position information for the name found + in an @param tag. +

+

+
+
+
+
+ +

+getDescription

+
+public JSDocInfo.StringPosition getDescription()
+
+
Gets the position information for the description found + in a block tag. +

+

+
+
+
+
+ +

+getType

+
+public JSDocInfo.TypePosition getType()
+
+
Gets the position information for the type expression found + in some block tags, like "@param" and "@return". +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.NamePosition.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.NamePosition.html new file mode 100644 index 0000000..1a96ff2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.NamePosition.html @@ -0,0 +1,254 @@ + + + + + +JSDocInfo.NamePosition (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class JSDocInfo.NamePosition

+
+java.lang.Object
+  extended by com.google.javascript.rhino.SourcePosition<Node>
+      extended by com.google.javascript.rhino.JSDocInfo.NamePosition
+
+
+
Enclosing class:
JSDocInfo
+
+
+
+
public static class JSDocInfo.NamePosition
extends SourcePosition<Node>
+ + +

+A piece of information (found in a marker) which contains a position + with a name node. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
JSDocInfo.NamePosition() + +
+           
+  + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.SourcePosition
getEndLine, getItem, getPositionOnEndLine, getPositionOnStartLine, getStartLine, setItem, setPositionInformation
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JSDocInfo.NamePosition

+
+public JSDocInfo.NamePosition()
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.StringPosition.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.StringPosition.html new file mode 100644 index 0000000..a364f5d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.StringPosition.html @@ -0,0 +1,254 @@ + + + + + +JSDocInfo.StringPosition (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class JSDocInfo.StringPosition

+
+java.lang.Object
+  extended by com.google.javascript.rhino.SourcePosition<String>
+      extended by com.google.javascript.rhino.JSDocInfo.StringPosition
+
+
+
Enclosing class:
JSDocInfo
+
+
+
+
public static class JSDocInfo.StringPosition
extends SourcePosition<String>
+ + +

+A piece of information (found in a marker) which contains a position + with a string. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
JSDocInfo.StringPosition() + +
+           
+  + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.SourcePosition
getEndLine, getItem, getPositionOnEndLine, getPositionOnStartLine, getStartLine, setItem, setPositionInformation
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JSDocInfo.StringPosition

+
+public JSDocInfo.StringPosition()
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.TypePosition.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.TypePosition.html new file mode 100644 index 0000000..245b764 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.TypePosition.html @@ -0,0 +1,284 @@ + + + + + +JSDocInfo.TypePosition (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class JSDocInfo.TypePosition

+
+java.lang.Object
+  extended by com.google.javascript.rhino.SourcePosition<Node>
+      extended by com.google.javascript.rhino.JSDocInfo.TypePosition
+
+
+
Enclosing class:
JSDocInfo
+
+
+
+
public static class JSDocInfo.TypePosition
extends SourcePosition<Node>
+ + +

+A piece of information (found in a marker) which contains a position + with a type expression syntax tree. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
JSDocInfo.TypePosition() + +
+           
+  + + + + + + + + + + + +
+Method Summary
+ booleanhasBrackets() + +
+          Returns whether the type has curly braces around it.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.SourcePosition
getEndLine, getItem, getPositionOnEndLine, getPositionOnStartLine, getStartLine, setItem, setPositionInformation
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JSDocInfo.TypePosition

+
+public JSDocInfo.TypePosition()
+
+
+ + + + + + + + +
+Method Detail
+ +

+hasBrackets

+
+public boolean hasBrackets()
+
+
Returns whether the type has curly braces around it. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.Visibility.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.Visibility.html new file mode 100644 index 0000000..5f16832 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.Visibility.html @@ -0,0 +1,376 @@ + + + + + +JSDocInfo.Visibility (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Enum JSDocInfo.Visibility

+
+java.lang.Object
+  extended by java.lang.Enum<JSDocInfo.Visibility>
+      extended by com.google.javascript.rhino.JSDocInfo.Visibility
+
+
+
All Implemented Interfaces:
Serializable, Comparable<JSDocInfo.Visibility>
+
+
+
Enclosing class:
JSDocInfo
+
+
+
+
public static enum JSDocInfo.Visibility
extends Enum<JSDocInfo.Visibility>
+ + +

+Visibility categories. The Enum.ordinal() can be used as a + numerical indicator of privacy, where 0 is the most private. This means + that the Enum.compareTo(E) method can be used to + determine if a visibility is more permissive than another. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + +
+Enum Constant Summary
INHERITED + +
+           
PRIVATE + +
+           
PROTECTED + +
+           
PUBLIC + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static JSDocInfo.VisibilityvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static JSDocInfo.Visibility[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+PRIVATE

+
+public static final JSDocInfo.Visibility PRIVATE
+
+
+
+
+
+ +

+PROTECTED

+
+public static final JSDocInfo.Visibility PROTECTED
+
+
+
+
+
+ +

+PUBLIC

+
+public static final JSDocInfo.Visibility PUBLIC
+
+
+
+
+
+ +

+INHERITED

+
+public static final JSDocInfo.Visibility INHERITED
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static JSDocInfo.Visibility[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (JSDocInfo.Visibility c : JSDocInfo.Visibility.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static JSDocInfo.Visibility valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.html new file mode 100644 index 0000000..443c1a9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfo.html @@ -0,0 +1,2151 @@ + + + + + +JSDocInfo (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class JSDocInfo

+
+java.lang.Object
+  extended by com.google.javascript.rhino.JSDocInfo
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class JSDocInfo
extends Object
implements Serializable
+ + +

+

JSDoc information describing JavaScript code. JSDoc is represented as a + unified object with fields for each JSDoc annotation, even though some + combinations are incorrect. For instance, if a JSDoc describes an enum, + it cannot have information about a return type. This implementation + takes advantage of such incompatibilities to reuse fields for multiple + purposes, reducing memory consumption.

+ +

Constructing JSDocInfo objects is simplified by + JSDocInfoBuilder which provides early incompatibility detection.

+

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Nested Class Summary
+static classJSDocInfo.Marker + +
+          Defines a class for containing the parsing information + for this JSDocInfo.
+static classJSDocInfo.NamePosition + +
+          A piece of information (found in a marker) which contains a position + with a name node.
+static classJSDocInfo.StringPosition + +
+          A piece of information (found in a marker) which contains a position + with a string.
+static classJSDocInfo.TypePosition + +
+          A piece of information (found in a marker) which contains a position + with a type expression syntax tree.
+static classJSDocInfo.Visibility + +
+          Visibility categories.
+  + + + + + + + + + + +
+Constructor Summary
JSDocInfo() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidaddSuppression(String suppression) + +
+          Add a suppressed warning.
+ booleancontainsDeclaration() + +
+           
+ NodegetAssociatedNode() + +
+           
+ Collection<String>getAuthors() + +
+          Returns the list of authors or null if none.
+ JSTypeExpressiongetBaseType() + +
+          Gets the base type specified by the @extends annotation.
+ StringgetBlockDescription() + +
+          Returns the block-level description or null if none specified.
+ StringgetDeprecationReason() + +
+          Returns the deprecation reason or null if none specified.
+ StringgetDescription() + +
+          Gets the description specified by the @desc annotation.
+ StringgetDescriptionForParameter(String name) + +
+          Returns the description for the parameter with the given name, if its + exists.
+ JSTypeExpressiongetEnumParameterType() + +
+          Gets the enum parameter type specified by the @enum annotation.
+ List<JSTypeExpression>getExtendedInterfaces() + +
+          Returns the interfaces extended by an interface
+ intgetExtendedInterfacesCount() + +
+          Gets the number of extended interfaces specified
+ StringgetFileOverview() + +
+          Returns the file overview or null if none specified.
+ intgetImplementedInterfaceCount() + +
+          Gets the number of interfaces specified by the @implements + annotation.
+ List<JSTypeExpression>getImplementedInterfaces() + +
+          Returns the types specified by the @implements annotation.
+ StringgetLendsName() + +
+          Gets the name we're lending to in a @lends annotation.
+ StringgetLicense() + +
+          Gets the description specified by the @license annotation.
+ Collection<JSDocInfo.Marker>getMarkers() + +
+          Gets the list of all markers for the documentation in this JSDoc.
+ StringgetMeaning() + +
+          Gets the meaning specified by the @meaning annotation.
+ Set<String>getModifies() + +
+          Returns the set of sideeffect notations.
+ StringgetOriginalCommentString() + +
+          Returns the original JSDoc comment string.
+ intgetParameterCount() + +
+          Gets the number of parameters defined.
+ Set<String>getParameterNames() + +
+          Returns the set of names of the defined parameters.
+ JSTypeExpressiongetParameterType(String parameter) + +
+          Gets the parameter type.
+ Collection<String>getReferences() + +
+          Returns the list of references or null if none.
+ StringgetReturnDescription() + +
+          Returns the description of the returned object or null if none specified.
+ JSTypeExpressiongetReturnType() + +
+          Gets the return type specified by the @return annotation.
+ StringgetSourceName() + +
+          Gets the name of the source file that contains this JSDoc.
+ Set<String>getSuppressions() + +
+          Returns the set of suppressed warnings.
+ StringgetTemplateTypeName() + +
+          Gets the template type name.
+ JSTypeExpressiongetThisType() + +
+          Gets the type specified by the @this annotation.
+ List<JSTypeExpression>getThrownTypes() + +
+          Returns the list of thrown types.
+ JSTypeExpressiongetType() + +
+          Gets the type specified by the @type annotation.
+ JSTypeExpressiongetTypedefType() + +
+          Gets the typedef type specified by the @type annotation.
+ Collection<Node>getTypeNodes() + +
+          Returns a collection of all type nodes that are a part of this JSDocInfo.
+ StringgetVersion() + +
+          Returns the version or null if none.
+ JSDocInfo.VisibilitygetVisibility() + +
+          Gets the visibility specified by @private, @protected or + @public annotation.
+ booleanhasBaseType() + +
+          Returns whether this JSDocInfo contains a type for @extends + annotation.
+ booleanhasDescriptionForParameter(String name) + +
+          Returns whether a description exists for the parameter with the specified + name.
+ booleanhasEnumParameterType() + +
+          Returns whether an enum parameter type, specified using the @enum + annotation, is present on this JSDoc.
+ booleanhasFileOverview() + +
+          Returns whether this has a fileoverview flag.
+ booleanhasModifies() + +
+           
+ booleanhasParameter(String parameter) + +
+          Returns whether the parameter is defined.
+ booleanhasParameterType(String parameter) + +
+          Returns whether the parameter has an attached type.
+ booleanhasReturnType() + +
+          Returns whether this JSDocInfo contains a type for @return + annotation.
+ booleanhasThisType() + +
+          Returns whether this JSDocInfo contains a type for @this + annotation.
+ booleanhasType() + +
+          Returns whether a type, specified using the @type annotation, is + present on this JSDoc.
+ booleanhasTypedefType() + +
+          Returns whether a typedef parameter type, specified using the + @typedef annotation, is present on this JSDoc.
+ booleanisConsistentIdGenerator() + +
+           
+ booleanisConstant() + +
+          Returns whether the @const annotation is present on this + JSDocInfo.
+ booleanisConstructor() + +
+          Returns whether the @constructor annotation is present on this + JSDocInfo.
+ booleanisDefine() + +
+          Returns whether the @define annotation is present on this + JSDocInfo.
+ booleanisDeprecated() + +
+          Returns whether the @deprecated annotation is present on this + JSDocInfo.
+ booleanisExport() + +
+          Returns whether the @export annotation is present on this + JSDocInfo.
+ booleanisExpose() + +
+          Returns whether the @expose annotation is present on this + JSDocInfo.
+ booleanisExterns() + +
+          Returns whether the @externs annotation is present on this + JSDocInfo.
+ booleanisHidden() + +
+          Returns whether the @hidden annotation is present on this + JSDocInfo.
+ booleanisIdGenerator() + +
+           
+ booleanisImplicitCast() + +
+          Returns whether the @implicitCast annotation is present on this + JSDocInfo.
+ booleanisInterface() + +
+          Returns whether the @interface annotation is present on this + JSDocInfo.
+ booleanisJavaDispatch() + +
+          Returns whether the @javadispath annotation is present on this + JSDocInfo.
+ booleanisNoAlias() + +
+          Returns whether the @noalias annotation is present on this + JSDocInfo.
+ booleanisNoCompile() + +
+          Returns whether the @nocompile annotation is present on this + JSDocInfo.
+ booleanisNoShadow() + +
+          Returns whether the @noshadow annotation is present on this + JSDocInfo.
+ booleanisNoSideEffects() + +
+          Returns whether the @nosideeffects annotation is present on this + JSDocInfo.
+ booleanisNoTypeCheck() + +
+          Returns whether the @nocheck annotation is present on this + JSDocInfo.
+ booleanisOverride() + +
+          Returns whether the @override annotation is present on this + JSDocInfo.
+ voidsetAssociatedNode(Node node) + +
+          Sets the node associated with this JSDoc.
+ voidsetDeprecated(boolean value) + +
+           
+ voidsetLicense(String license) + +
+          License directives can appear in multiple comments, and always + apply to the entire file.
+ voidsetVisibility(JSDocInfo.Visibility visibility) + +
+           
+ booleanshouldPreserveTry() + +
+          Returns whether the @preserveTry annotation is present on this + JSDocInfo.
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JSDocInfo

+
+public JSDocInfo()
+
+
+ + + + + + + + +
+Method Detail
+ +

+setDeprecated

+
+public void setDeprecated(boolean value)
+
+
+
+
+
+
+
+
+
+ +

+isConsistentIdGenerator

+
+public boolean isConsistentIdGenerator()
+
+
+
+
+
+ +
Returns:
whether the @consistentIdGenerator is present on + this JSDocInfo
+
+
+
+ +

+isConstant

+
+public boolean isConstant()
+
+
Returns whether the @const annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+isConstructor

+
+public boolean isConstructor()
+
+
Returns whether the @constructor annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+isDefine

+
+public boolean isDefine()
+
+
Returns whether the @define annotation is present on this + JSDocInfo. If this annotation is present, then the + getType() method will retrieve the define type. +

+

+
+
+
+
+
+
+
+ +

+isHidden

+
+public boolean isHidden()
+
+
Returns whether the @hidden annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+isNoTypeCheck

+
+public boolean isNoTypeCheck()
+
+
Returns whether the @nocheck annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+shouldPreserveTry

+
+public boolean shouldPreserveTry()
+
+
Returns whether the @preserveTry annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+isOverride

+
+public boolean isOverride()
+
+
Returns whether the @override annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+isNoAlias

+
+public boolean isNoAlias()
+
+
Returns whether the @noalias annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+isDeprecated

+
+public boolean isDeprecated()
+
+
Returns whether the @deprecated annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+isInterface

+
+public boolean isInterface()
+
+
Returns whether the @interface annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+isExport

+
+public boolean isExport()
+
+
Returns whether the @export annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+isExpose

+
+public boolean isExpose()
+
+
Returns whether the @expose annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+isNoShadow

+
+public boolean isNoShadow()
+
+
Returns whether the @noshadow annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+isIdGenerator

+
+public boolean isIdGenerator()
+
+
+
+
+
+ +
Returns:
whether the @idGenerator is present on + this JSDocInfo
+
+
+
+ +

+isImplicitCast

+
+public boolean isImplicitCast()
+
+
Returns whether the @implicitCast annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+isNoSideEffects

+
+public boolean isNoSideEffects()
+
+
Returns whether the @nosideeffects annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+isExterns

+
+public boolean isExterns()
+
+
Returns whether the @externs annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+isJavaDispatch

+
+public boolean isJavaDispatch()
+
+
Returns whether the @javadispath annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+isNoCompile

+
+public boolean isNoCompile()
+
+
Returns whether the @nocompile annotation is present on this + JSDocInfo. +

+

+
+
+
+
+
+
+
+ +

+containsDeclaration

+
+public boolean containsDeclaration()
+
+
+
+
+
+ +
Returns:
Whether there is declaration present on this JSDocInfo.
+
+
+
+ +

+setVisibility

+
+public void setVisibility(JSDocInfo.Visibility visibility)
+
+
+
+
+
+
+
+
+
+ +

+addSuppression

+
+public void addSuppression(String suppression)
+
+
Add a suppressed warning. +

+

+
+
+
+
+
+
+
+ +

+getVisibility

+
+public JSDocInfo.Visibility getVisibility()
+
+
Gets the visibility specified by @private, @protected or + @public annotation. If no visibility is specified, visibility + is inherited from the base class. +

+

+
+
+
+
+
+
+
+ +

+getParameterType

+
+public JSTypeExpression getParameterType(String parameter)
+
+
Gets the parameter type. +

+

+
+
+
+
Parameters:
parameter - the parameter's name +
Returns:
the parameter's type or null if this parameter is not + defined or has a null type
+
+
+
+ +

+hasParameter

+
+public boolean hasParameter(String parameter)
+
+
Returns whether the parameter is defined. +

+

+
+
+
+
+
+
+
+ +

+hasParameterType

+
+public boolean hasParameterType(String parameter)
+
+
Returns whether the parameter has an attached type. +

+

+
+
+
+ +
Returns:
true if the parameter has an attached type, false + if the parameter has no attached type or does not exist.
+
+
+
+ +

+getParameterNames

+
+public Set<String> getParameterNames()
+
+
Returns the set of names of the defined parameters. The iteration order + of the returned set is not the order in which parameters are defined. +

+

+
+
+
+ +
Returns:
the set of names of the defined parameters. The returned set is + immutable.
+
+
+
+ +

+getParameterCount

+
+public int getParameterCount()
+
+
Gets the number of parameters defined. +

+

+
+
+
+
+
+
+
+ +

+getThrownTypes

+
+public List<JSTypeExpression> getThrownTypes()
+
+
Returns the list of thrown types. +

+

+
+
+
+
+
+
+
+ +

+hasType

+
+public boolean hasType()
+
+
Returns whether a type, specified using the @type annotation, is + present on this JSDoc. +

+

+
+
+
+
+
+
+
+ +

+hasEnumParameterType

+
+public boolean hasEnumParameterType()
+
+
Returns whether an enum parameter type, specified using the @enum + annotation, is present on this JSDoc. +

+

+
+
+
+
+
+
+
+ +

+hasTypedefType

+
+public boolean hasTypedefType()
+
+
Returns whether a typedef parameter type, specified using the + @typedef annotation, is present on this JSDoc. +

+

+
+
+
+
+
+
+
+ +

+hasReturnType

+
+public boolean hasReturnType()
+
+
Returns whether this JSDocInfo contains a type for @return + annotation. +

+

+
+
+
+
+
+
+
+ +

+getType

+
+public JSTypeExpression getType()
+
+
Gets the type specified by the @type annotation. +

+

+
+
+
+
+
+
+
+ +

+getReturnType

+
+public JSTypeExpression getReturnType()
+
+
Gets the return type specified by the @return annotation. +

+

+
+
+
+
+
+
+
+ +

+getEnumParameterType

+
+public JSTypeExpression getEnumParameterType()
+
+
Gets the enum parameter type specified by the @enum annotation. +

+

+
+
+
+
+
+
+
+ +

+getTypedefType

+
+public JSTypeExpression getTypedefType()
+
+
Gets the typedef type specified by the @type annotation. +

+

+
+
+
+
+
+
+
+ +

+getThisType

+
+public JSTypeExpression getThisType()
+
+
Gets the type specified by the @this annotation. +

+

+
+
+
+
+
+
+
+ +

+hasThisType

+
+public boolean hasThisType()
+
+
Returns whether this JSDocInfo contains a type for @this + annotation. +

+

+
+
+
+
+
+
+
+ +

+getBaseType

+
+public JSTypeExpression getBaseType()
+
+
Gets the base type specified by the @extends annotation. +

+

+
+
+
+
+
+
+
+ +

+getDescription

+
+public String getDescription()
+
+
Gets the description specified by the @desc annotation. +

+

+
+
+
+
+
+
+
+ +

+getMeaning

+
+public String getMeaning()
+
+
Gets the meaning specified by the @meaning annotation. + + In localization systems, two messages with the same content but + different "meanings" may be translated differently. By default, we + use the name of the variable that the message is initialized to as + the "meaning" of the message. + + But some code generators (like Closure Templates) inject their own + meaning with the jsdoc @meaning annotation. +

+

+
+
+
+
+
+
+
+ +

+getLendsName

+
+public String getLendsName()
+
+
Gets the name we're lending to in a @lends annotation. + + In many reflection APIs, you pass an anonymous object to a function, + and that function mixes the anonymous object into another object. + The @lends annotation allows the type system to track + those property assignments. +

+

+
+
+
+
+
+
+
+ +

+getLicense

+
+public String getLicense()
+
+
Gets the description specified by the @license annotation. +

+

+
+
+
+
+
+
+
+ +

+setLicense

+
+public void setLicense(String license)
+
+
License directives can appear in multiple comments, and always + apply to the entire file. Break protection and allow outsiders to + update the license string so that we can attach the license text even + when the JSDocInfo has been created and tagged with other information. +

+

+
+
+
+
Parameters:
license - String containing new license text.
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+
+ +

+hasBaseType

+
+public boolean hasBaseType()
+
+
Returns whether this JSDocInfo contains a type for @extends + annotation. +

+

+
+
+
+
+
+
+
+ +

+getImplementedInterfaces

+
+public List<JSTypeExpression> getImplementedInterfaces()
+
+
Returns the types specified by the @implements annotation. +

+

+
+
+
+ +
Returns:
An immutable list of JSTypeExpression objects that can + be resolved to types.
+
+
+
+ +

+getImplementedInterfaceCount

+
+public int getImplementedInterfaceCount()
+
+
Gets the number of interfaces specified by the @implements + annotation. +

+

+
+
+
+
+
+
+
+ +

+getExtendedInterfaces

+
+public List<JSTypeExpression> getExtendedInterfaces()
+
+
Returns the interfaces extended by an interface +

+

+
+
+
+ +
Returns:
An immutable list of JSTypeExpression objects that can + be resolved to types.
+
+
+
+ +

+getExtendedInterfacesCount

+
+public int getExtendedInterfacesCount()
+
+
Gets the number of extended interfaces specified +

+

+
+
+
+
+
+
+
+ +

+getDeprecationReason

+
+public String getDeprecationReason()
+
+
Returns the deprecation reason or null if none specified. +

+

+
+
+
+
+
+
+
+ +

+getSuppressions

+
+public Set<String> getSuppressions()
+
+
Returns the set of suppressed warnings. +

+

+
+
+
+
+
+
+
+ +

+getModifies

+
+public Set<String> getModifies()
+
+
Returns the set of sideeffect notations. +

+

+
+
+
+
+
+
+
+ +

+hasDescriptionForParameter

+
+public boolean hasDescriptionForParameter(String name)
+
+
Returns whether a description exists for the parameter with the specified + name. +

+

+
+
+
+
+
+
+
+ +

+getDescriptionForParameter

+
+public String getDescriptionForParameter(String name)
+
+
Returns the description for the parameter with the given name, if its + exists. +

+

+
+
+
+
+
+
+
+ +

+getAuthors

+
+public Collection<String> getAuthors()
+
+
Returns the list of authors or null if none. +

+

+
+
+
+
+
+
+
+ +

+getReferences

+
+public Collection<String> getReferences()
+
+
Returns the list of references or null if none. +

+

+
+
+
+
+
+
+
+ +

+getVersion

+
+public String getVersion()
+
+
Returns the version or null if none. +

+

+
+
+
+
+
+
+
+ +

+getReturnDescription

+
+public String getReturnDescription()
+
+
Returns the description of the returned object or null if none specified. +

+

+
+
+
+
+
+
+
+ +

+getBlockDescription

+
+public String getBlockDescription()
+
+
Returns the block-level description or null if none specified. +

+

+
+
+
+
+
+
+
+ +

+hasFileOverview

+
+public boolean hasFileOverview()
+
+
Returns whether this has a fileoverview flag. +

+

+
+
+
+
+
+
+
+ +

+getFileOverview

+
+public String getFileOverview()
+
+
Returns the file overview or null if none specified. +

+

+
+
+
+
+
+
+
+ +

+getAssociatedNode

+
+public Node getAssociatedNode()
+
+
+
+
+
+
+
+
+
+ +

+setAssociatedNode

+
+public void setAssociatedNode(Node node)
+
+
Sets the node associated with this JSDoc. + Notice that many nodes may have pointer to the same JSDocInfo + object (because we propagate it across the type graph). But there + is only one canonical "owner" node of the JSDocInfo, which corresponds + to its original place in the syntax tree. +

+

+
+
+
+
+
+
+
+ +

+getSourceName

+
+public String getSourceName()
+
+
Gets the name of the source file that contains this JSDoc. +

+

+
+
+
+
+
+
+
+ +

+getMarkers

+
+public Collection<JSDocInfo.Marker> getMarkers()
+
+
Gets the list of all markers for the documentation in this JSDoc. +

+

+
+
+
+
+
+
+
+ +

+getTemplateTypeName

+
+public String getTemplateTypeName()
+
+
Gets the template type name. +

+

+
+
+
+
+
+
+
+ +

+getTypeNodes

+
+public Collection<Node> getTypeNodes()
+
+
Returns a collection of all type nodes that are a part of this JSDocInfo. + This includes @type, @this, @extends, @implements, @param, @throws, + and @return. Any future type specific JSDoc should make sure to add the + appropriate nodes here. +

+

+
+
+
+ +
Returns:
collection of all type nodes
+
+
+
+ +

+hasModifies

+
+public boolean hasModifies()
+
+
+
+
+
+
+
+
+
+ +

+getOriginalCommentString

+
+public String getOriginalCommentString()
+
+
Returns the original JSDoc comment string. Returns null unless + parseJsDocDocumentation is enabled via the ParserConfig. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfoBuilder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfoBuilder.html new file mode 100644 index 0000000..c1467d6 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSDocInfoBuilder.html @@ -0,0 +1,1692 @@ + + + + + +JSDocInfoBuilder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class JSDocInfoBuilder

+
+java.lang.Object
+  extended by com.google.javascript.rhino.JSDocInfoBuilder
+
+
+
+
public final class JSDocInfoBuilder
extends Object
+ + +

+A builder for JSDocInfo objects. This builder abstracts the + construction process of JSDocInfo objects whilst minimizing the + number of instances of JSDocInfo objects. It provides early + incompatibility detection among properties stored on the JSDocInfo + object being created. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
JSDocInfoBuilder(boolean parseDocumentation) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleanaddAuthor(String author) + +
+          Adds an author to the current information.
+ booleanaddReference(String reference) + +
+          Adds a reference ("@see") to the current information.
+ JSDocInfobuild(Node associatedNode) + +
+          Builds a JSDocInfo object based on the populated information and + returns it.
+ booleanhasParameter(String name) + +
+           
+ booleanisConstructorRecorded() + +
+          Whether the JSDocInfo being built will have its + JSDocInfo.isConstructor() flag set to true.
+ booleanisDescriptionRecorded() + +
+          Returns whether this builder recorded a description.
+ booleanisInterfaceRecorded() + +
+          Whether the JSDocInfo being built will have its + JSDocInfo.isInterface() flag set to true.
+ booleanisJavaDispatch() + +
+          Whether the JSDocInfo being built will have its + JSDocInfo.isJavaDispatch() flag set to true.
+ booleanisPopulated() + +
+          Returns whether this builder is populated with information that can be + used to build(com.google.javascript.rhino.Node) a JSDocInfo object.
+ booleanisPopulatedWithFileOverview() + +
+          Returns whether this builder is populated with information that can be + used to build(com.google.javascript.rhino.Node) a JSDocInfo object that has a + fileoverview tag.
+ voidmarkAnnotation(String annotation, + int lineno, + int charno) + +
+          Adds a marker to the current JSDocInfo and populates the marker with the + annotation information.
+ voidmarkName(String name, + int lineno, + int charno) + +
+          Deprecated. Use #markName(String, StaticSourceFile, int, int)
+ voidmarkName(String name, + StaticSourceFile file, + int lineno, + int charno) + +
+          Adds a name declaration to the current marker.
+ voidmarkText(String text, + int startLineno, + int startCharno, + int endLineno, + int endCharno) + +
+          Adds a textual block to the current marker.
+ voidmarkTypeNode(Node typeNode, + int lineno, + int startCharno, + int endLineno, + int endCharno, + boolean hasLC) + +
+          Adds a type declaration to the current marker.
+ booleanrecordBaseType(JSTypeExpression jsType) + +
+          Records a base type.
+ booleanrecordBlockDescription(String description) + +
+          Records a block-level description.
+ booleanrecordConsistentIdGenerator() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isConsistentIdGenerator() flag set to + true.
+ booleanrecordConstancy() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isConstant() flag set to true.
+ booleanrecordConstructor() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isConstructor() flag set to true.
+ booleanrecordDefineType(JSTypeExpression type) + +
+          Records the type of a define.
+ booleanrecordDeprecated() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isDeprecated() flag set to true.
+ booleanrecordDeprecationReason(String reason) + +
+          Records the deprecation reason.
+ booleanrecordDescription(String description) + +
+          Records a description giving context for translation (i18n).
+ booleanrecordEnumParameterType(JSTypeExpression type) + +
+          Records a parameter type to an enum.
+ booleanrecordExport() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isExport() flag set to true.
+ booleanrecordExpose() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isExpose() flag set to true.
+ booleanrecordExtendedInterface(JSTypeExpression interfaceType) + +
+          Records an extended interface type.
+ booleanrecordExterns() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isExterns() flag set to true.
+ booleanrecordFileOverview(String description) + +
+          Records a fileoverview description.
+ booleanrecordHiddenness() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isHidden() flag set to true.
+ booleanrecordIdGenerator() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isIdGenerator() flag set to + true.
+ booleanrecordImplementedInterface(JSTypeExpression interfaceName) + +
+          Records an implemented interface.
+ booleanrecordImplicitCast() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isImplicitCast() flag set to true.
+ booleanrecordInterface() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isInterface() flag set to true.
+ booleanrecordJavaDispatch() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isJavaDispatch() flag set to true.
+ booleanrecordLends(String name) + +
+          Records that we're lending to another name.
+ booleanrecordMeaning(String meaning) + +
+          Records a meaning giving context for translation (i18n).
+ booleanrecordModifies(Set<String> modifies) + +
+          Records the list of modifies warnings.
+ booleanrecordNoAlias() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isNoAlias() flag set to true.
+ booleanrecordNoCompile() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isNoCompile() flag set to true.
+ booleanrecordNoShadow() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isNoShadow() flag set to true.
+ booleanrecordNoSideEffects() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isNoSideEffects() flag set to true.
+ booleanrecordNoTypeCheck() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isNoTypeCheck() flag set to true.
+ voidrecordOriginalCommentString(String sourceComment) + +
+          Sets the original JSDoc comment string.
+ booleanrecordOverride() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.isOverride() flag set to true.
+ booleanrecordParameter(String parameterName, + JSTypeExpression type) + +
+          Records a typed parameter.
+ booleanrecordParameterDescription(String parameterName, + String description) + +
+          Records a parameter's description.
+ booleanrecordPreserveTry() + +
+          Records that the JSDocInfo being built should have its + JSDocInfo.shouldPreserveTry() flag set to true.
+ booleanrecordReturnDescription(String description) + +
+          Records a return description
+ booleanrecordReturnType(JSTypeExpression jsType) + +
+          Records a return type.
+ booleanrecordSuppressions(Set<String> suppressions) + +
+          Records the list of suppressed warnings.
+ booleanrecordTemplateTypeName(String name) + +
+          Records a template type name.
+ booleanrecordThisType(JSTypeExpression type) + +
+          Records a type for @this annotation.
+ booleanrecordThrowDescription(JSTypeExpression type, + String description) + +
+          Records a throw type's description.
+ booleanrecordThrowType(JSTypeExpression type) + +
+          Records a thrown type.
+ booleanrecordType(JSTypeExpression type) + +
+          Records a type.
+ booleanrecordTypedef(JSTypeExpression type) + +
+          Records that the JSDocInfo being built should be populated + with a typedef'd type.
+ booleanrecordVersion(String version) + +
+          Records the version.
+ booleanrecordVisibility(JSDocInfo.Visibility visibility) + +
+          Records a visibility.
+ booleanshouldParseDocumentation() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JSDocInfoBuilder

+
+public JSDocInfoBuilder(boolean parseDocumentation)
+
+
+ + + + + + + + +
+Method Detail
+ +

+recordOriginalCommentString

+
+public void recordOriginalCommentString(String sourceComment)
+
+
Sets the original JSDoc comment string. This is a no-op if the builder + isn't configured to record documentation. +

+

+
+
+
+
+ +

+shouldParseDocumentation

+
+public boolean shouldParseDocumentation()
+
+
+
+
+
+
+ +

+isPopulated

+
+public boolean isPopulated()
+
+
Returns whether this builder is populated with information that can be + used to build(com.google.javascript.rhino.Node) a JSDocInfo object. +

+

+
+
+
+
+ +

+isPopulatedWithFileOverview

+
+public boolean isPopulatedWithFileOverview()
+
+
Returns whether this builder is populated with information that can be + used to build(com.google.javascript.rhino.Node) a JSDocInfo object that has a + fileoverview tag. +

+

+
+
+
+
+ +

+isDescriptionRecorded

+
+public boolean isDescriptionRecorded()
+
+
Returns whether this builder recorded a description. +

+

+
+
+
+
+ +

+build

+
+public JSDocInfo build(Node associatedNode)
+
+
Builds a JSDocInfo object based on the populated information and + returns it. Once this method is called, the builder can be reused to build + another JSDocInfo object. +

+

+
Parameters:
associatedNode - The source node containing the JSDoc. +
Returns:
a JSDocInfo object populated with the values given to this + builder. If no value was populated, this method simply returns + null
+
+
+
+ +

+markAnnotation

+
+public void markAnnotation(String annotation,
+                           int lineno,
+                           int charno)
+
+
Adds a marker to the current JSDocInfo and populates the marker with the + annotation information. +

+

+
+
+
+
+ +

+markText

+
+public void markText(String text,
+                     int startLineno,
+                     int startCharno,
+                     int endLineno,
+                     int endCharno)
+
+
Adds a textual block to the current marker. +

+

+
+
+
+
+ +

+markTypeNode

+
+public void markTypeNode(Node typeNode,
+                         int lineno,
+                         int startCharno,
+                         int endLineno,
+                         int endCharno,
+                         boolean hasLC)
+
+
Adds a type declaration to the current marker. +

+

+
+
+
+
+ +

+markName

+
+@Deprecated
+public void markName(String name,
+                                int lineno,
+                                int charno)
+
+
Deprecated. Use #markName(String, StaticSourceFile, int, int) +

+

Adds a name declaration to the current marker. +

+

+
+
+
+
+ +

+markName

+
+public void markName(String name,
+                     StaticSourceFile file,
+                     int lineno,
+                     int charno)
+
+
Adds a name declaration to the current marker. +

+

+
+
+
+
+ +

+recordBlockDescription

+
+public boolean recordBlockDescription(String description)
+
+
Records a block-level description. +

+

+ +
Returns:
true if the description was recorded.
+
+
+
+ +

+recordVisibility

+
+public boolean recordVisibility(JSDocInfo.Visibility visibility)
+
+
Records a visibility. +

+

+ +
Returns:
true if the visibility was recorded and false + if it was already defined
+
+
+
+ +

+recordParameter

+
+public boolean recordParameter(String parameterName,
+                               JSTypeExpression type)
+
+
Records a typed parameter. +

+

+ +
Returns:
true if the typed parameter was recorded and + false if a parameter with the same name was already defined
+
+
+
+ +

+recordParameterDescription

+
+public boolean recordParameterDescription(String parameterName,
+                                          String description)
+
+
Records a parameter's description. +

+

+ +
Returns:
true if the parameter's description was recorded and + false if a parameter with the same name was already defined
+
+
+
+ +

+recordTemplateTypeName

+
+public boolean recordTemplateTypeName(String name)
+
+
Records a template type name. +

+

+ +
Returns:
true if the template type name was recorded and + false if a template type name was already defined.
+
+
+
+ +

+recordThrowType

+
+public boolean recordThrowType(JSTypeExpression type)
+
+
Records a thrown type. +

+

+
+
+
+
+ +

+recordThrowDescription

+
+public boolean recordThrowDescription(JSTypeExpression type,
+                                      String description)
+
+
Records a throw type's description. +

+

+ +
Returns:
true if the type's description was recorded and + false if a description with the same type was already defined
+
+
+
+ +

+addAuthor

+
+public boolean addAuthor(String author)
+
+
Adds an author to the current information. +

+

+
+
+
+
+ +

+addReference

+
+public boolean addReference(String reference)
+
+
Adds a reference ("@see") to the current information. +

+

+
+
+
+
+ +

+recordConsistentIdGenerator

+
+public boolean recordConsistentIdGenerator()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isConsistentIdGenerator() flag set to + true. +

+

+ +
Returns:
true if the consistentIdGenerator flag was recorded and + false if it was already recorded
+
+
+
+ +

+recordVersion

+
+public boolean recordVersion(String version)
+
+
Records the version. +

+

+
+
+
+
+ +

+recordDeprecationReason

+
+public boolean recordDeprecationReason(String reason)
+
+
Records the deprecation reason. +

+

+
+
+
+
+ +

+recordSuppressions

+
+public boolean recordSuppressions(Set<String> suppressions)
+
+
Records the list of suppressed warnings. +

+

+
+
+
+
+ +

+recordModifies

+
+public boolean recordModifies(Set<String> modifies)
+
+
Records the list of modifies warnings. +

+

+
+
+
+
+ +

+recordType

+
+public boolean recordType(JSTypeExpression type)
+
+
Records a type. +

+

+ +
Returns:
true if the type was recorded and false if + it is invalid or was already defined
+
+
+
+ +

+recordTypedef

+
+public boolean recordTypedef(JSTypeExpression type)
+
+
Records that the JSDocInfo being built should be populated + with a typedef'd type. +

+

+
+
+
+
+ +

+recordIdGenerator

+
+public boolean recordIdGenerator()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isIdGenerator() flag set to + true. +

+

+ +
Returns:
true if the idGenerator flag was recorded and false + if it was already recorded
+
+
+
+ +

+recordReturnType

+
+public boolean recordReturnType(JSTypeExpression jsType)
+
+
Records a return type. +

+

+ +
Returns:
true if the return type was recorded and false if + it is invalid or was already defined
+
+
+
+ +

+recordReturnDescription

+
+public boolean recordReturnDescription(String description)
+
+
Records a return description +

+

+ +
Returns:
true if the return description was recorded and + false if it is invalid or was already defined
+
+
+
+ +

+recordDefineType

+
+public boolean recordDefineType(JSTypeExpression type)
+
+
Records the type of a define. + + 'Define' values are special constants that may be manipulated by + the compiler. They are designed to mimic the #define command in + the C preprocessor. +

+

+
+
+
+
+ +

+recordEnumParameterType

+
+public boolean recordEnumParameterType(JSTypeExpression type)
+
+
Records a parameter type to an enum. +

+

+ +
Returns:
true if the enum's parameter type was recorded and + false if it was invalid or already defined
+
+
+
+ +

+recordThisType

+
+public boolean recordThisType(JSTypeExpression type)
+
+
Records a type for @this annotation. +

+

+ +
Returns:
true if the type was recorded and + false if it is invalid or if it collided with @enum or + @type annotations
+
+
+
+ +

+recordBaseType

+
+public boolean recordBaseType(JSTypeExpression jsType)
+
+
Records a base type. +

+

+ +
Returns:
true if the base type was recorded and false + if it was already defined
+
+
+
+ +

+recordConstancy

+
+public boolean recordConstancy()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isConstant() flag set to true. +

+

+ +
Returns:
true if the constancy was recorded and false + if it was already defined
+
+
+
+ +

+recordDescription

+
+public boolean recordDescription(String description)
+
+
Records a description giving context for translation (i18n). +

+

+ +
Returns:
true if the description was recorded and false + if the description was invalid or was already defined
+
+
+
+ +

+recordMeaning

+
+public boolean recordMeaning(String meaning)
+
+
Records a meaning giving context for translation (i18n). Different + meanings will result in different translations. +

+

+ +
Returns:
true If the meaning was successfully updated.
+
+
+
+ +

+recordFileOverview

+
+public boolean recordFileOverview(String description)
+
+
Records a fileoverview description. +

+

+ +
Returns:
true if the description was recorded and false + if the description was invalid or was already defined.
+
+
+
+ +

+recordHiddenness

+
+public boolean recordHiddenness()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isHidden() flag set to true. +

+

+ +
Returns:
true if the hiddenness was recorded and false + if it was already defined
+
+
+
+ +

+recordNoCompile

+
+public boolean recordNoCompile()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isNoCompile() flag set to true. +

+

+ +
Returns:
true if the no compile flag was recorded and false + if it was already recorded
+
+
+
+ +

+recordNoTypeCheck

+
+public boolean recordNoTypeCheck()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isNoTypeCheck() flag set to true. +

+

+ +
Returns:
true if the no check flag was recorded and false + if it was already recorded
+
+
+
+ +

+recordConstructor

+
+public boolean recordConstructor()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isConstructor() flag set to true. +

+

+ +
Returns:
true if the constructor was recorded and false + if it was already defined or it was incompatible with the existing + flags
+
+
+
+ +

+isConstructorRecorded

+
+public boolean isConstructorRecorded()
+
+
Whether the JSDocInfo being built will have its + JSDocInfo.isConstructor() flag set to true. +

+

+
+
+
+
+ +

+recordJavaDispatch

+
+public boolean recordJavaDispatch()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isJavaDispatch() flag set to true. +

+

+ +
Returns:
true if the javadispatch was recorded and false + if it was already defined or it was incompatible with the existing + flags
+
+
+
+ +

+isJavaDispatch

+
+public boolean isJavaDispatch()
+
+
Whether the JSDocInfo being built will have its + JSDocInfo.isJavaDispatch() flag set to true. +

+

+
+
+
+
+ +

+recordPreserveTry

+
+public boolean recordPreserveTry()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.shouldPreserveTry() flag set to true. +

+

+
+
+
+
+ +

+recordOverride

+
+public boolean recordOverride()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isOverride() flag set to true. +

+

+
+
+
+
+ +

+recordNoAlias

+
+public boolean recordNoAlias()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isNoAlias() flag set to true. +

+

+
+
+
+
+ +

+recordDeprecated

+
+public boolean recordDeprecated()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isDeprecated() flag set to true. +

+

+
+
+
+
+ +

+recordInterface

+
+public boolean recordInterface()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isInterface() flag set to true. +

+

+ +
Returns:
true if the flag was recorded and false + if it was already defined or it was incompatible with the existing + flags
+
+
+
+ +

+recordExport

+
+public boolean recordExport()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isExport() flag set to true. +

+

+
+
+
+
+ +

+recordExpose

+
+public boolean recordExpose()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isExpose() flag set to true. +

+

+
+
+
+
+ +

+recordNoShadow

+
+public boolean recordNoShadow()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isNoShadow() flag set to true. +

+

+
+
+
+
+ +

+recordImplicitCast

+
+public boolean recordImplicitCast()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isImplicitCast() flag set to true. +

+

+
+
+
+
+ +

+recordNoSideEffects

+
+public boolean recordNoSideEffects()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isNoSideEffects() flag set to true. +

+

+
+
+
+
+ +

+recordExterns

+
+public boolean recordExterns()
+
+
Records that the JSDocInfo being built should have its + JSDocInfo.isExterns() flag set to true. +

+

+
+
+
+
+ +

+isInterfaceRecorded

+
+public boolean isInterfaceRecorded()
+
+
Whether the JSDocInfo being built will have its + JSDocInfo.isInterface() flag set to true. +

+

+
+
+
+
+ +

+hasParameter

+
+public boolean hasParameter(String name)
+
+
+ +
Returns:
Whether a parameter of the given name has already been recorded.
+
+
+
+ +

+recordImplementedInterface

+
+public boolean recordImplementedInterface(JSTypeExpression interfaceName)
+
+
Records an implemented interface. +

+

+
+
+
+
+ +

+recordExtendedInterface

+
+public boolean recordExtendedInterface(JSTypeExpression interfaceType)
+
+
Records an extended interface type. +

+

+
+
+
+
+ +

+recordLends

+
+public boolean recordLends(String name)
+
+
Records that we're lending to another name. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSTypeExpression.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSTypeExpression.html new file mode 100644 index 0000000..c3dffe0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/JSTypeExpression.html @@ -0,0 +1,423 @@ + + + + + +JSTypeExpression (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class JSTypeExpression

+
+java.lang.Object
+  extended by com.google.javascript.rhino.JSTypeExpression
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public final class JSTypeExpression
extends Object
implements Serializable
+ + +

+Represents a type expression as a miniture Rhino AST, so that the + type expression can be evaluated later. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Constructor Summary
JSTypeExpression(Node root, + String sourceName) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleanequals(Object other) + +
+           
+ JSTypeevaluate(StaticScope<JSType> scope, + JSTypeRegistry registry) + +
+          Evaluates the type expression into a JSType object.
+ NodegetRoot() + +
+           
+ inthashCode() + +
+           
+ booleanisOptionalArg() + +
+           
+ booleanisVarArgs() + +
+           
+static JSTypeExpressionmakeOptionalArg(JSTypeExpression expr) + +
+          Make the given type expression into an optional type expression, + if possible.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JSTypeExpression

+
+public JSTypeExpression(Node root,
+                        String sourceName)
+
+
+ + + + + + + + +
+Method Detail
+ +

+makeOptionalArg

+
+public static JSTypeExpression makeOptionalArg(JSTypeExpression expr)
+
+
Make the given type expression into an optional type expression, + if possible. +

+

+
+
+
+
+
+
+
+ +

+isOptionalArg

+
+public boolean isOptionalArg()
+
+
+
+
+
+ +
Returns:
Whether this expression denotes an optional @param.
+
+
+
+ +

+isVarArgs

+
+public boolean isVarArgs()
+
+
+
+
+
+ +
Returns:
Whether this expression denotes a rest args @param.
+
+
+
+ +

+evaluate

+
+public JSType evaluate(StaticScope<JSType> scope,
+                       JSTypeRegistry registry)
+
+
Evaluates the type expression into a JSType object. +

+

+
+
+
+
+
+
+
+ +

+equals

+
+public boolean equals(Object other)
+
+
+
Overrides:
equals in class Object
+
+
+
+
+
+
+ +

+hashCode

+
+public int hashCode()
+
+
+
Overrides:
hashCode in class Object
+
+
+
+
+
+
+ +

+getRoot

+
+public Node getRoot()
+
+
+
+
+
+ +
Returns:
The source for this type expression. Note that it will not + contain an expression if there's an @override tag.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/Node.AncestorIterable.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/Node.AncestorIterable.html new file mode 100644 index 0000000..016e4a9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/Node.AncestorIterable.html @@ -0,0 +1,244 @@ + + + + + +Node.AncestorIterable (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class Node.AncestorIterable

+
+java.lang.Object
+  extended by com.google.javascript.rhino.Node.AncestorIterable
+
+
+
All Implemented Interfaces:
Iterable<Node>
+
+
+
Enclosing class:
Node
+
+
+
+
public static class Node.AncestorIterable
extends Object
implements Iterable<Node>
+ + +

+Iterator to go up the ancestor tree. +

+ +

+


+ +

+ + + + + + + + + + + + +
+Method Summary
+ Iterator<Node>iterator() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+iterator

+
+public Iterator<Node> iterator()
+
+
+
Specified by:
iterator in interface Iterable<Node>
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/Node.FileLevelJsDocBuilder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/Node.FileLevelJsDocBuilder.html new file mode 100644 index 0000000..547f3ac --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/Node.FileLevelJsDocBuilder.html @@ -0,0 +1,276 @@ + + + + + +Node.FileLevelJsDocBuilder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class Node.FileLevelJsDocBuilder

+
+java.lang.Object
+  extended by com.google.javascript.rhino.Node.FileLevelJsDocBuilder
+
+
+
Enclosing class:
Node
+
+
+
+
public class Node.FileLevelJsDocBuilder
extends Object
+ + +

+An inner class that provides back-door access to the license + property of the JSDocInfo property for this node. This is only + meant to be used for top level script nodes where the + JsDocInfoParser needs to + be able to append directly to the top level node, not just the + current node. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
Node.FileLevelJsDocBuilder() + +
+           
+  + + + + + + + + + + + +
+Method Summary
+ voidappend(String fileLevelComment) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+Node.FileLevelJsDocBuilder

+
+public Node.FileLevelJsDocBuilder()
+
+
+ + + + + + + + +
+Method Detail
+ +

+append

+
+public void append(String fileLevelComment)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/Node.SideEffectFlags.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/Node.SideEffectFlags.html new file mode 100644 index 0000000..24ab1c3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/Node.SideEffectFlags.html @@ -0,0 +1,464 @@ + + + + + +Node.SideEffectFlags (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class Node.SideEffectFlags

+
+java.lang.Object
+  extended by com.google.javascript.rhino.Node.SideEffectFlags
+
+
+
Enclosing class:
Node
+
+
+
+
public static class Node.SideEffectFlags
extends Object
+ + +

+A helper class for getting and setting the side-effect flags. +

+ +

+


+ +

+ + + + + + + + + + + + + + +
+Constructor Summary
Node.SideEffectFlags() + +
+           
Node.SideEffectFlags(int value) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleanareAllFlagsSet() + +
+           
+ voidclearAllFlags() + +
+          No side-effects occur and the returned results are local.
+ voidclearSideEffectFlags() + +
+          Preserve the return result flag, but clear the others: + no global state change, no throws, no this change, no arguments change
+ voidsetAllFlags() + +
+          All side-effect occur and the returned results are non-local.
+ voidsetMutatesArguments() + +
+           
+ voidsetMutatesGlobalState() + +
+           
+ voidsetMutatesThis() + +
+           
+ voidsetReturnsTainted() + +
+           
+ voidsetThrows() + +
+           
+ intvalueOf() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+Node.SideEffectFlags

+
+public Node.SideEffectFlags()
+
+
+
+ +

+Node.SideEffectFlags

+
+public Node.SideEffectFlags(int value)
+
+
+ + + + + + + + +
+Method Detail
+ +

+valueOf

+
+public int valueOf()
+
+
+
+
+
+
+ +

+setAllFlags

+
+public void setAllFlags()
+
+
All side-effect occur and the returned results are non-local. +

+

+
+
+
+
+ +

+clearAllFlags

+
+public void clearAllFlags()
+
+
No side-effects occur and the returned results are local. +

+

+
+
+
+
+ +

+areAllFlagsSet

+
+public boolean areAllFlagsSet()
+
+
+
+
+
+
+ +

+clearSideEffectFlags

+
+public void clearSideEffectFlags()
+
+
Preserve the return result flag, but clear the others: + no global state change, no throws, no this change, no arguments change +

+

+
+
+
+
+ +

+setMutatesGlobalState

+
+public void setMutatesGlobalState()
+
+
+
+
+
+
+ +

+setThrows

+
+public void setThrows()
+
+
+
+
+
+
+ +

+setMutatesThis

+
+public void setMutatesThis()
+
+
+
+
+
+
+ +

+setMutatesArguments

+
+public void setMutatesArguments()
+
+
+
+
+
+
+ +

+setReturnsTainted

+
+public void setReturnsTainted()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/Node.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/Node.html new file mode 100644 index 0000000..e0def05 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/Node.html @@ -0,0 +1,5300 @@ + + + + + +Node (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class Node

+
+java.lang.Object
+  extended by com.google.javascript.rhino.Node
+
+
+
All Implemented Interfaces:
Serializable, Cloneable
+
+
+
+
public class Node
extends Object
implements Cloneable, Serializable
+ + +

+This class implements the root of the intermediate representation. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + + + + + + + + + +
+Nested Class Summary
+static classNode.AncestorIterable + +
+          Iterator to go up the ancestor tree.
+ classNode.FileLevelJsDocBuilder + +
+          An inner class that provides back-door access to the license + property of the JSDocInfo property for this node.
+static classNode.SideEffectFlags + +
+          A helper class for getting and setting the side-effect flags.
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Field Summary
+static intBRACELESS_TYPE + +
+           
+static intCOLUMN_BITS + +
+          COLUMN_BITS represents how many of the lower-order bits of + sourcePosition are reserved for storing the column number.
+static intCOLUMN_MASK + +
+          COLUMN_MASK stores a value where bits storing the column number + are set, and bits storing the line are not set.
+static intDECR_FLAG + +
+           
+static intDIRECT_EVAL + +
+           
+static intDIRECTIVES + +
+           
+static intEMPTY_BLOCK + +
+           
+static intFLAG_ARGUMENTS_UNMODIFIED + +
+           
+static intFLAG_GLOBAL_STATE_UNMODIFIED + +
+           
+static intFLAG_LOCAL_RESULTS + +
+           
+static intFLAG_NO_THROWS + +
+           
+static intFLAG_THIS_UNMODIFIED + +
+           
+static intFREE_CALL + +
+           
+static intINCRDECR_PROP + +
+           
+static intINPUT_ID + +
+           
+static intIS_CONSTANT_NAME + +
+           
+static intIS_DISPATCHER + +
+           
+static intIS_NAMESPACE + +
+           
+static intIS_OPTIONAL_PARAM + +
+           
+static intIS_VAR_ARGS_PARAM + +
+           
+static intJSDOC_INFO_PROP + +
+           
+static intLAST_PROP + +
+           
+static intLENGTH + +
+           
+static intMAX_COLUMN_NUMBER + +
+          MAX_COLUMN_NUMBER represents the maximum column number that can + be represented.
+static intNO_SIDE_EFFECTS + +
+           
+static intOPT_ARG_NAME + +
+           
+static intORIGINALNAME_PROP + +
+           
+static intPARENTHESIZED_PROP + +
+           
+static intPOST_FLAG + +
+           
+static intQUOTED_PROP + +
+           
+static intSIDE_EFFECT_FLAGS + +
+           
+static intSIDE_EFFECTS_ALL + +
+           
+static intSIDE_EFFECTS_FLAGS_MASK + +
+           
+static intSLASH_V + +
+           
+static intSOURCENAME_PROP + +
+           
+static intSTATIC_SOURCE_FILE + +
+           
+static intSYNTHETIC_BLOCK_PROP + +
+           
+static intVAR_ARGS_NAME + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Constructor Summary
Node(int nodeType) + +
+           
Node(int nodeType, + int lineno, + int charno) + +
+           
Node(int nodeType, + Node child) + +
+           
Node(int nodeType, + Node[] children) + +
+           
Node(int nodeType, + Node[] children, + int lineno, + int charno) + +
+           
Node(int nodeType, + Node child, + int lineno, + int charno) + +
+           
Node(int nodeType, + Node left, + Node right) + +
+           
Node(int nodeType, + Node left, + Node right, + int lineno, + int charno) + +
+           
Node(int nodeType, + Node left, + Node mid, + Node right) + +
+           
Node(int nodeType, + Node left, + Node mid, + Node right, + int lineno, + int charno) + +
+           
Node(int nodeType, + Node left, + Node mid, + Node mid2, + Node right) + +
+           
Node(int nodeType, + Node left, + Node mid, + Node mid2, + Node right, + int lineno, + int charno) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voidaddChildAfter(Node newChild, + Node node) + +
+          Add 'child' after 'node'.
+ voidaddChildBefore(Node newChild, + Node node) + +
+          Add 'child' before 'node'.
+ voidaddChildrenAfter(Node children, + Node node) + +
+          Add all children after 'node'.
+ voidaddChildrenToBack(Node children) + +
+           
+ voidaddChildrenToFront(Node children) + +
+           
+ voidaddChildToBack(Node child) + +
+           
+ voidaddChildToFront(Node child) + +
+           
+ voidaddSuppression(String warning) + +
+          Adds a warning to be suppressed.
+ voidappendStringTree(Appendable appendable) + +
+           
+ StringcheckTreeEquals(Node node2) + +
+          Checks if the subtree under this node is the same as another subtree.
+ Iterable<Node>children() + +
+          Return an iterable object that iterates over this nodes's children.
+ NodecloneNode() + +
+           
+ NodeclonePropsFrom(Node other) + +
+          Clone the properties from the provided node without copying + the property object.
+ NodecloneTree() + +
+           
+ NodecopyInformationFrom(Node other) + +
+          Copies source file and name information from the other + node given to the current node.
+ NodecopyInformationFromForTree(Node other) + +
+          Copies source file and name information from the other node to the + entire tree rooted at this node.
+ voiddetachChildren() + +
+          Removes all children from this node and isolates the children from each + other.
+ NodedetachFromParent() + +
+          Removes this node from its parent.
+protected static intextractCharno(int lineCharNo) + +
+          Extracts the character number and character number from a merged line + char number (see mergeLineCharNo(int, int)).
+protected static intextractLineno(int lineCharNo) + +
+          Extracts the line number and character number from a merged line char + number (see mergeLineCharNo(int, int)).
+ NodegetAncestor(int level) + +
+          Gets the ancestor node relative to this.
+ Node.AncestorIterablegetAncestors() + +
+          Iterates all of the node's ancestors excluding itself.
+ booleangetBooleanProp(int propType) + +
+           
+ intgetCharno() + +
+           
+ NodegetChildAtIndex(int i) + +
+           
+ NodegetChildBefore(Node child) + +
+           
+ intgetChildCount() + +
+           
+ Set<String>getDirectives() + +
+          Returns the set of ES5 directives for this node.
+ doublegetDouble() + +
+          Can only be called when getType() == TokenStream.NUMBER
+ intgetExistingIntProp(int propType) + +
+           
+ NodegetFirstChild() + +
+           
+ intgetIndexOfChild(Node child) + +
+           
+ InputIdgetInputId() + +
+           
+ intgetIntProp(int propType) + +
+          Returns the integer value for the property, or 0 if the property + is not defined.
+ Node.FileLevelJsDocBuildergetJsDocBuilderForNode() + +
+           
+ JSDocInfogetJSDocInfo() + +
+          Get the JSDocInfo attached to this node.
+ JSTypegetJSType() + +
+           
+ NodegetLastChild() + +
+           
+ NodegetLastSibling() + +
+           
+ intgetLength() + +
+           
+ intgetLineno() + +
+           
+ NodegetNext() + +
+           
+ NodegetParent() + +
+           
+ ObjectgetProp(int propType) + +
+           
+ StringgetQualifiedName() + +
+          This function takes a set of GETPROP nodes and produces a string that is + each property separated by dots.
+ intgetSideEffectFlags() + +
+          Returns the side effects flags for this node.
+ StringgetSourceFileName() + +
+           
+ intgetSourceOffset() + +
+           
+ intgetSourcePosition() + +
+           
+ StaticSourceFilegetStaticSourceFile() + +
+          Returns the source file associated with this input.
+ StringgetString() + +
+          Can only be called when node has String context.
+ intgetType() + +
+           
+ booleanhasChild(Node child) + +
+           
+ booleanhasChildren() + +
+           
+ booleanhasMoreThanOneChild() + +
+          Check for more than one child more efficiently than by iterating over all + the children as is done with Node.getChildCount().
+ booleanhasOneChild() + +
+          Check for one child more efficiently than by iterating over all the + children as is done with Node.getChildCount().
+ booleanisAdd() + +
+          AST type check methods
+ booleanisAnd() + +
+           
+ booleanisArrayLit() + +
+           
+ booleanisAssign() + +
+           
+ booleanisAssignAdd() + +
+           
+ booleanisBlock() + +
+           
+ booleanisBreak() + +
+           
+ booleanisCall() + +
+           
+ booleanisCase() + +
+           
+ booleanisCatch() + +
+           
+ booleanisComma() + +
+           
+ booleanisContinue() + +
+           
+ booleanisDebugger() + +
+           
+ booleanisDec() + +
+           
+ booleanisDefaultCase() + +
+           
+ booleanisDelProp() + +
+           
+ booleanisDo() + +
+           
+ booleanisEmpty() + +
+           
+ booleanisEquivalentTo(Node node) + +
+          Returns true if this node is equivalent semantically to another
+ booleanisEquivalentToTyped(Node node) + +
+          Returns true if this node is equivalent semantically to another and + the types are equivalent.
+ booleanisExprResult() + +
+           
+ booleanisFalse() + +
+           
+ booleanisFor() + +
+           
+ booleanisFromExterns() + +
+           
+ booleanisFunction() + +
+           
+ booleanisGetElem() + +
+           
+ booleanisGetProp() + +
+           
+ booleanisGetterDef() + +
+           
+ booleanisHook() + +
+           
+ booleanisIf() + +
+           
+ booleanisIn() + +
+           
+ booleanisInc() + +
+           
+ booleanisInstanceOf() + +
+           
+ booleanisLabel() + +
+           
+ booleanisLabelName() + +
+           
+ booleanisLocalResultCall() + +
+          Returns true if this node is a function or constructor call that + returns a primitive or a local object (an object that has no other + references).
+ booleanisName() + +
+           
+ booleanisNE() + +
+           
+ booleanisNew() + +
+           
+ booleanisNoSideEffectsCall() + +
+          Returns true if this node is a function or constructor call that + has no side effects.
+ booleanisNot() + +
+           
+ booleanisNull() + +
+           
+ booleanisNumber() + +
+           
+ booleanisObjectLit() + +
+           
+ booleanisOnlyModifiesThisCall() + +
+           
+ booleanisOptionalArg() + +
+          Returns whether this node is an optional argument node.
+ booleanisOr() + +
+           
+ booleanisParamList() + +
+           
+ booleanisQualifiedName() + +
+          Returns whether a node corresponds to a simple or a qualified name, such as + x or a.b.c or this.a.
+ booleanisQuotedString() + +
+          This should only be called for STRING nodes children of OBJECTLIT.
+ booleanisRegExp() + +
+           
+ booleanisReturn() + +
+           
+ booleanisScript() + +
+           
+ booleanisSetterDef() + +
+           
+ booleanisString() + +
+           
+ booleanisSwitch() + +
+           
+ booleanisSyntheticBlock() + +
+          Returns whether this is a synthetic block that should not be considered + a real source block.
+ booleanisThis() + +
+           
+ booleanisThrow() + +
+           
+ booleanisTrue() + +
+           
+ booleanisTry() + +
+           
+ booleanisTypeOf() + +
+           
+ booleanisUnscopedQualifiedName() + +
+          Returns whether a node corresponds to a simple or a qualified name without + a "this" reference, such as a.b.c, but not this.a + .
+ booleanisVar() + +
+           
+ booleanisVarArgs() + +
+          Returns whether this node is a variable length argument node.
+ booleanisVoid() + +
+           
+ booleanisWhile() + +
+           
+ booleanisWith() + +
+           
+protected static intmergeLineCharNo(int lineno, + int charno) + +
+          Merges the line number and character number in one integer.
+static NodenewNumber(double number) + +
+           
+static NodenewNumber(double number, + int lineno, + int charno) + +
+           
+static NodenewString(int type, + String str) + +
+           
+static NodenewString(int type, + String str, + int lineno, + int charno) + +
+           
+static NodenewString(String str) + +
+           
+static NodenewString(String str, + int lineno, + int charno) + +
+           
+ voidputBooleanProp(int propType, + boolean value) + +
+           
+ voidputIntProp(int propType, + int value) + +
+           
+ voidputProp(int propType, + Object value) + +
+           
+ voidremoveChild(Node child) + +
+          Detach a child from its parent and siblings.
+ NoderemoveChildAfter(Node prev) + +
+           
+ NoderemoveChildren() + +
+           
+ NoderemoveFirstChild() + +
+          Removes the first child of Node.
+ voidremoveProp(int propType) + +
+           
+ voidreplaceChild(Node child, + Node newChild) + +
+          Detaches child from Node and replaces it with newChild.
+ voidreplaceChildAfter(Node prevChild, + Node newChild) + +
+           
+ voidsetCharno(int charno) + +
+           
+ voidsetDirectives(Set<String> val) + +
+          Sets the ES5 directives on this node.
+ voidsetDouble(double s) + +
+          Can only be called when getType() == TokenStream.NUMBER
+ voidsetInputId(InputId inputId) + +
+           
+ voidsetIsSyntheticBlock(boolean val) + +
+          Sets whether this is a synthetic block that should not be considered + a real source block.
+ voidsetJSDocInfo(JSDocInfo info) + +
+          Sets the JSDocInfo attached to this node.
+ voidsetJSType(JSType jsType) + +
+           
+ voidsetLength(int length) + +
+           
+ voidsetLineno(int lineno) + +
+           
+ voidsetOptionalArg(boolean optionalArg) + +
+          Sets whether this node is an optional argument node.
+ voidsetQuotedString() + +
+          This should only be called for STRING nodes children of OBJECTLIT.
+ voidsetSideEffectFlags(int flags) + +
+          Marks this function or constructor call's side effect flags.
+ voidsetSideEffectFlags(Node.SideEffectFlags flags) + +
+           
+ voidsetSourceEncodedPosition(int sourcePosition) + +
+           
+ voidsetSourceEncodedPositionForTree(int sourcePosition) + +
+           
+ voidsetSourceFileForTesting(String name) + +
+          Sets the source file to a non-extern file of the given name.
+ voidsetStaticSourceFile(StaticSourceFile file) + +
+           
+ voidsetString(String s) + +
+          Can only be called when node has String context.
+ voidsetType(int type) + +
+           
+ voidsetVarArgs(boolean varArgs) + +
+          Sets whether this node is a variable length argument node.
+ voidsetWasEmptyNode(boolean val) + +
+          Sets whether this is a synthetic block that should not be considered + a real source block.
+ Iterable<Node>siblings() + +
+          Return an iterable object that iterates over this nodes's siblings.
+ Nodesrcref(Node other) + +
+           
+ NodesrcrefTree(Node other) + +
+           
+ StringtoString() + +
+           
+ StringtoString(boolean printSource, + boolean printAnnotations, + boolean printType) + +
+           
+ StringtoStringTree() + +
+           
+ NodeuseSourceInfoFrom(Node other) + +
+          Overwrite all the source information in this node with + that of other.
+ NodeuseSourceInfoFromForTree(Node other) + +
+          Overwrite all the source information in this node and its subtree with + that of other.
+ NodeuseSourceInfoIfMissingFrom(Node other) + +
+          Overwrite all the source information in this node with + that of other iff the source info is missing.
+ NodeuseSourceInfoIfMissingFromForTree(Node other) + +
+          Overwrite all the source information in this node and its subtree with + that of other iff the source info is missing.
+ booleanwasEmptyNode() + +
+          Returns whether this is a synthetic block that should not be considered + a real source block.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+SOURCENAME_PROP

+
+public static final int SOURCENAME_PROP
+
+
+
See Also:
Constant Field Values
+
+
+ +

+JSDOC_INFO_PROP

+
+public static final int JSDOC_INFO_PROP
+
+
+
See Also:
Constant Field Values
+
+
+ +

+VAR_ARGS_NAME

+
+public static final int VAR_ARGS_NAME
+
+
+
See Also:
Constant Field Values
+
+
+ +

+INCRDECR_PROP

+
+public static final int INCRDECR_PROP
+
+
+
See Also:
Constant Field Values
+
+
+ +

+PARENTHESIZED_PROP

+
+public static final int PARENTHESIZED_PROP
+
+
+
See Also:
Constant Field Values
+
+
+ +

+QUOTED_PROP

+
+public static final int QUOTED_PROP
+
+
+
See Also:
Constant Field Values
+
+
+ +

+OPT_ARG_NAME

+
+public static final int OPT_ARG_NAME
+
+
+
See Also:
Constant Field Values
+
+
+ +

+SYNTHETIC_BLOCK_PROP

+
+public static final int SYNTHETIC_BLOCK_PROP
+
+
+
See Also:
Constant Field Values
+
+
+ +

+EMPTY_BLOCK

+
+public static final int EMPTY_BLOCK
+
+
+
See Also:
Constant Field Values
+
+
+ +

+ORIGINALNAME_PROP

+
+public static final int ORIGINALNAME_PROP
+
+
+
See Also:
Constant Field Values
+
+
+ +

+BRACELESS_TYPE

+
+public static final int BRACELESS_TYPE
+
+
+
See Also:
Constant Field Values
+
+
+ +

+SIDE_EFFECT_FLAGS

+
+public static final int SIDE_EFFECT_FLAGS
+
+
+
See Also:
Constant Field Values
+
+
+ +

+IS_CONSTANT_NAME

+
+public static final int IS_CONSTANT_NAME
+
+
+
See Also:
Constant Field Values
+
+
+ +

+IS_OPTIONAL_PARAM

+
+public static final int IS_OPTIONAL_PARAM
+
+
+
See Also:
Constant Field Values
+
+
+ +

+IS_VAR_ARGS_PARAM

+
+public static final int IS_VAR_ARGS_PARAM
+
+
+
See Also:
Constant Field Values
+
+
+ +

+IS_NAMESPACE

+
+public static final int IS_NAMESPACE
+
+
+
See Also:
Constant Field Values
+
+
+ +

+IS_DISPATCHER

+
+public static final int IS_DISPATCHER
+
+
+
See Also:
Constant Field Values
+
+
+ +

+DIRECTIVES

+
+public static final int DIRECTIVES
+
+
+
See Also:
Constant Field Values
+
+
+ +

+DIRECT_EVAL

+
+public static final int DIRECT_EVAL
+
+
+
See Also:
Constant Field Values
+
+
+ +

+FREE_CALL

+
+public static final int FREE_CALL
+
+
+
See Also:
Constant Field Values
+
+
+ +

+STATIC_SOURCE_FILE

+
+public static final int STATIC_SOURCE_FILE
+
+
+
See Also:
Constant Field Values
+
+
+ +

+LENGTH

+
+public static final int LENGTH
+
+
+
See Also:
Constant Field Values
+
+
+ +

+INPUT_ID

+
+public static final int INPUT_ID
+
+
+
See Also:
Constant Field Values
+
+
+ +

+SLASH_V

+
+public static final int SLASH_V
+
+
+
See Also:
Constant Field Values
+
+
+ +

+LAST_PROP

+
+public static final int LAST_PROP
+
+
+
See Also:
Constant Field Values
+
+
+ +

+DECR_FLAG

+
+public static final int DECR_FLAG
+
+
+
See Also:
Constant Field Values
+
+
+ +

+POST_FLAG

+
+public static final int POST_FLAG
+
+
+
See Also:
Constant Field Values
+
+
+ +

+COLUMN_BITS

+
+public static final int COLUMN_BITS
+
+
COLUMN_BITS represents how many of the lower-order bits of + sourcePosition are reserved for storing the column number. + Bits above these store the line number. + This gives us decent position information for everything except + files already passed through a minimizer, where lines might + be longer than 4096 characters. +

+

+
See Also:
Constant Field Values
+
+
+ +

+MAX_COLUMN_NUMBER

+
+public static final int MAX_COLUMN_NUMBER
+
+
MAX_COLUMN_NUMBER represents the maximum column number that can + be represented. JSCompiler's modifications to Rhino cause all + tokens located beyond the maximum column to MAX_COLUMN_NUMBER. +

+

+
See Also:
Constant Field Values
+
+
+ +

+COLUMN_MASK

+
+public static final int COLUMN_MASK
+
+
COLUMN_MASK stores a value where bits storing the column number + are set, and bits storing the line are not set. It's handy for + separating column number from line number. +

+

+
See Also:
Constant Field Values
+
+
+ +

+FLAG_GLOBAL_STATE_UNMODIFIED

+
+public static final int FLAG_GLOBAL_STATE_UNMODIFIED
+
+
+
See Also:
Constant Field Values
+
+
+ +

+FLAG_THIS_UNMODIFIED

+
+public static final int FLAG_THIS_UNMODIFIED
+
+
+
See Also:
Constant Field Values
+
+
+ +

+FLAG_ARGUMENTS_UNMODIFIED

+
+public static final int FLAG_ARGUMENTS_UNMODIFIED
+
+
+
See Also:
Constant Field Values
+
+
+ +

+FLAG_NO_THROWS

+
+public static final int FLAG_NO_THROWS
+
+
+
See Also:
Constant Field Values
+
+
+ +

+FLAG_LOCAL_RESULTS

+
+public static final int FLAG_LOCAL_RESULTS
+
+
+
See Also:
Constant Field Values
+
+
+ +

+SIDE_EFFECTS_FLAGS_MASK

+
+public static final int SIDE_EFFECTS_FLAGS_MASK
+
+
+
See Also:
Constant Field Values
+
+
+ +

+SIDE_EFFECTS_ALL

+
+public static final int SIDE_EFFECTS_ALL
+
+
+
See Also:
Constant Field Values
+
+
+ +

+NO_SIDE_EFFECTS

+
+public static final int NO_SIDE_EFFECTS
+
+
+
See Also:
Constant Field Values
+
+ + + + + + + + +
+Constructor Detail
+ +

+Node

+
+public Node(int nodeType)
+
+
+
+ +

+Node

+
+public Node(int nodeType,
+            Node child)
+
+
+
+ +

+Node

+
+public Node(int nodeType,
+            Node left,
+            Node right)
+
+
+
+ +

+Node

+
+public Node(int nodeType,
+            Node left,
+            Node mid,
+            Node right)
+
+
+
+ +

+Node

+
+public Node(int nodeType,
+            Node left,
+            Node mid,
+            Node mid2,
+            Node right)
+
+
+
+ +

+Node

+
+public Node(int nodeType,
+            int lineno,
+            int charno)
+
+
+
+ +

+Node

+
+public Node(int nodeType,
+            Node child,
+            int lineno,
+            int charno)
+
+
+
+ +

+Node

+
+public Node(int nodeType,
+            Node left,
+            Node right,
+            int lineno,
+            int charno)
+
+
+
+ +

+Node

+
+public Node(int nodeType,
+            Node left,
+            Node mid,
+            Node right,
+            int lineno,
+            int charno)
+
+
+
+ +

+Node

+
+public Node(int nodeType,
+            Node left,
+            Node mid,
+            Node mid2,
+            Node right,
+            int lineno,
+            int charno)
+
+
+
+ +

+Node

+
+public Node(int nodeType,
+            Node[] children,
+            int lineno,
+            int charno)
+
+
+
+ +

+Node

+
+public Node(int nodeType,
+            Node[] children)
+
+
+ + + + + + + + +
+Method Detail
+ +

+newNumber

+
+public static Node newNumber(double number)
+
+
+
+
+
+
+
+
+
+ +

+newNumber

+
+public static Node newNumber(double number,
+                             int lineno,
+                             int charno)
+
+
+
+
+
+
+
+
+
+ +

+newString

+
+public static Node newString(String str)
+
+
+
+
+
+
+
+
+
+ +

+newString

+
+public static Node newString(int type,
+                             String str)
+
+
+
+
+
+
+
+
+
+ +

+newString

+
+public static Node newString(String str,
+                             int lineno,
+                             int charno)
+
+
+
+
+
+
+
+
+
+ +

+newString

+
+public static Node newString(int type,
+                             String str,
+                             int lineno,
+                             int charno)
+
+
+
+
+
+
+
+
+
+ +

+getType

+
+public int getType()
+
+
+
+
+
+
+
+
+
+ +

+setType

+
+public void setType(int type)
+
+
+
+
+
+
+
+
+
+ +

+hasChildren

+
+public boolean hasChildren()
+
+
+
+
+
+
+
+
+
+ +

+getFirstChild

+
+public Node getFirstChild()
+
+
+
+
+
+
+
+
+
+ +

+getLastChild

+
+public Node getLastChild()
+
+
+
+
+
+
+
+
+
+ +

+getNext

+
+public Node getNext()
+
+
+
+
+
+
+
+
+
+ +

+getChildBefore

+
+public Node getChildBefore(Node child)
+
+
+
+
+
+
+
+
+
+ +

+getChildAtIndex

+
+public Node getChildAtIndex(int i)
+
+
+
+
+
+
+
+
+
+ +

+getIndexOfChild

+
+public int getIndexOfChild(Node child)
+
+
+
+
+
+
+
+
+
+ +

+getLastSibling

+
+public Node getLastSibling()
+
+
+
+
+
+
+
+
+
+ +

+addChildToFront

+
+public void addChildToFront(Node child)
+
+
+
+
+
+
+
+
+
+ +

+addChildToBack

+
+public void addChildToBack(Node child)
+
+
+
+
+
+
+
+
+
+ +

+addChildrenToFront

+
+public void addChildrenToFront(Node children)
+
+
+
+
+
+
+
+
+
+ +

+addChildrenToBack

+
+public void addChildrenToBack(Node children)
+
+
+
+
+
+
+
+
+
+ +

+addChildBefore

+
+public void addChildBefore(Node newChild,
+                           Node node)
+
+
Add 'child' before 'node'. +

+

+
+
+
+
+
+
+
+ +

+addChildAfter

+
+public void addChildAfter(Node newChild,
+                          Node node)
+
+
Add 'child' after 'node'. +

+

+
+
+
+
+
+
+
+ +

+addChildrenAfter

+
+public void addChildrenAfter(Node children,
+                             Node node)
+
+
Add all children after 'node'. +

+

+
+
+
+
+
+
+
+ +

+removeChild

+
+public void removeChild(Node child)
+
+
Detach a child from its parent and siblings. +

+

+
+
+
+
+
+
+
+ +

+replaceChild

+
+public void replaceChild(Node child,
+                         Node newChild)
+
+
Detaches child from Node and replaces it with newChild. +

+

+
+
+
+
+
+
+
+ +

+replaceChildAfter

+
+public void replaceChildAfter(Node prevChild,
+                              Node newChild)
+
+
+
+
+
+
+
+
+
+ +

+clonePropsFrom

+
+public Node clonePropsFrom(Node other)
+
+
Clone the properties from the provided node without copying + the property object. The recieving node may not have any + existing properties. +

+

+
+
+
+
Parameters:
other - The node to clone properties from. +
Returns:
this node.
+
+
+
+ +

+removeProp

+
+public void removeProp(int propType)
+
+
+
+
+
+
+
+
+
+ +

+getProp

+
+public Object getProp(int propType)
+
+
+
+
+
+
+
+
+
+ +

+getBooleanProp

+
+public boolean getBooleanProp(int propType)
+
+
+
+
+
+
+
+
+
+ +

+getIntProp

+
+public int getIntProp(int propType)
+
+
Returns the integer value for the property, or 0 if the property + is not defined. +

+

+
+
+
+
+
+
+
+ +

+getExistingIntProp

+
+public int getExistingIntProp(int propType)
+
+
+
+
+
+
+
+
+
+ +

+putProp

+
+public void putProp(int propType,
+                    Object value)
+
+
+
+
+
+
+
+
+
+ +

+putBooleanProp

+
+public void putBooleanProp(int propType,
+                           boolean value)
+
+
+
+
+
+
+
+
+
+ +

+putIntProp

+
+public void putIntProp(int propType,
+                       int value)
+
+
+
+
+
+
+
+
+
+ +

+getDouble

+
+public double getDouble()
+                 throws UnsupportedOperationException
+
+
Can only be called when getType() == TokenStream.NUMBER +

+

+
+
+
+ +
Throws: +
UnsupportedOperationException
+
+
+
+ +

+setDouble

+
+public void setDouble(double s)
+               throws UnsupportedOperationException
+
+
Can only be called when getType() == TokenStream.NUMBER +

+

+
+
+
+ +
Throws: +
UnsupportedOperationException
+
+
+
+ +

+getString

+
+public String getString()
+                 throws UnsupportedOperationException
+
+
Can only be called when node has String context. +

+

+
+
+
+ +
Throws: +
UnsupportedOperationException
+
+
+
+ +

+setString

+
+public void setString(String s)
+               throws UnsupportedOperationException
+
+
Can only be called when node has String context. +

+

+
+
+
+ +
Throws: +
UnsupportedOperationException
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+
+ +

+toString

+
+public String toString(boolean printSource,
+                       boolean printAnnotations,
+                       boolean printType)
+
+
+
+
+
+
+
+
+
+ +

+toStringTree

+
+public String toStringTree()
+
+
+
+
+
+
+
+
+
+ +

+appendStringTree

+
+public void appendStringTree(Appendable appendable)
+                      throws IOException
+
+
+
+
+
+ +
Throws: +
IOException
+
+
+
+ +

+setStaticSourceFile

+
+public void setStaticSourceFile(StaticSourceFile file)
+
+
+
+
+
+
+
+
+
+ +

+setSourceFileForTesting

+
+public void setSourceFileForTesting(String name)
+
+
Sets the source file to a non-extern file of the given name. +

+

+
+
+
+
+
+
+
+ +

+getSourceFileName

+
+public String getSourceFileName()
+
+
+
+
+
+
+
+
+
+ +

+getStaticSourceFile

+
+public StaticSourceFile getStaticSourceFile()
+
+
Returns the source file associated with this input. May be null +

+

+
+
+
+
+
+
+
+ +

+setInputId

+
+public void setInputId(InputId inputId)
+
+
+
+
+
+
Parameters:
inputId -
+
+
+
+ +

+getInputId

+
+public InputId getInputId()
+
+
+
+
+
+ +
Returns:
The Id of the CompilerInput associated with this Node.
+
+
+
+ +

+isFromExterns

+
+public boolean isFromExterns()
+
+
+
+
+
+
+
+
+
+ +

+getLength

+
+public int getLength()
+
+
+
+
+
+
+
+
+
+ +

+setLength

+
+public void setLength(int length)
+
+
+
+
+
+
+
+
+
+ +

+getLineno

+
+public int getLineno()
+
+
+
+
+
+
+
+
+
+ +

+getCharno

+
+public int getCharno()
+
+
+
+
+
+
+
+
+
+ +

+getSourceOffset

+
+public int getSourceOffset()
+
+
+
+
+
+
+
+
+
+ +

+getSourcePosition

+
+public int getSourcePosition()
+
+
+
+
+
+
+
+
+
+ +

+setLineno

+
+public void setLineno(int lineno)
+
+
+
+
+
+
+
+
+
+ +

+setCharno

+
+public void setCharno(int charno)
+
+
+
+
+
+
+
+
+
+ +

+setSourceEncodedPosition

+
+public void setSourceEncodedPosition(int sourcePosition)
+
+
+
+
+
+
+
+
+
+ +

+setSourceEncodedPositionForTree

+
+public void setSourceEncodedPositionForTree(int sourcePosition)
+
+
+
+
+
+
+
+
+
+ +

+mergeLineCharNo

+
+protected static int mergeLineCharNo(int lineno,
+                                     int charno)
+
+
Merges the line number and character number in one integer. The Character + number takes the first 12 bits and the line number takes the rest. If + the character number is greater than 212-1 it is + adjusted to 212-1. +

+

+
+
+
+
+
+
+
+ +

+extractLineno

+
+protected static int extractLineno(int lineCharNo)
+
+
Extracts the line number and character number from a merged line char + number (see mergeLineCharNo(int, int)). +

+

+
+
+
+
+
+
+
+ +

+extractCharno

+
+protected static int extractCharno(int lineCharNo)
+
+
Extracts the character number and character number from a merged line + char number (see mergeLineCharNo(int, int)). +

+

+
+
+
+
+
+
+
+ +

+children

+
+public Iterable<Node> children()
+
+

Return an iterable object that iterates over this nodes's children. + The iterator does not support the optional operation + Iterator.remove().

+ +

To iterate over a node's siblings, one can write

+
Node n = ...;
+ for (Node child : n.children()) { ...
+

+

+
+
+
+
+
+
+
+ +

+siblings

+
+public Iterable<Node> siblings()
+
+

Return an iterable object that iterates over this nodes's siblings. + The iterator does not support the optional operation + Iterator.remove().

+ +

To iterate over a node's siblings, one can write

+
Node n = ...;
+ for (Node sibling : n.siblings()) { ...
+

+

+
+
+
+
+
+
+
+ +

+getParent

+
+public Node getParent()
+
+
+
+
+
+
+
+
+
+ +

+getAncestor

+
+public Node getAncestor(int level)
+
+
Gets the ancestor node relative to this. +

+

+
+
+
+
Parameters:
level - 0 = this, 1 = the parent, etc.
+
+
+
+ +

+getAncestors

+
+public Node.AncestorIterable getAncestors()
+
+
Iterates all of the node's ancestors excluding itself. +

+

+
+
+
+
+
+
+
+ +

+hasOneChild

+
+public boolean hasOneChild()
+
+
Check for one child more efficiently than by iterating over all the + children as is done with Node.getChildCount(). +

+

+
+
+
+ +
Returns:
Whether the node has exactly one child.
+
+
+
+ +

+hasMoreThanOneChild

+
+public boolean hasMoreThanOneChild()
+
+
Check for more than one child more efficiently than by iterating over all + the children as is done with Node.getChildCount(). +

+

+
+
+
+ +
Returns:
Whether the node more than one child.
+
+
+
+ +

+getChildCount

+
+public int getChildCount()
+
+
+
+
+
+
+
+
+
+ +

+hasChild

+
+public boolean hasChild(Node child)
+
+
+
+
+
+
+
+
+
+ +

+checkTreeEquals

+
+public String checkTreeEquals(Node node2)
+
+
Checks if the subtree under this node is the same as another subtree. + Returns null if it's equal, or a message describing the differences. +

+

+
+
+
+
+
+
+
+ +

+isEquivalentTo

+
+public boolean isEquivalentTo(Node node)
+
+
Returns true if this node is equivalent semantically to another +

+

+
+
+
+
+
+
+
+ +

+isEquivalentToTyped

+
+public boolean isEquivalentToTyped(Node node)
+
+
Returns true if this node is equivalent semantically to another and + the types are equivalent. +

+

+
+
+
+
+
+
+
+ +

+getQualifiedName

+
+public String getQualifiedName()
+
+
This function takes a set of GETPROP nodes and produces a string that is + each property separated by dots. If the node ultimately under the left + sub-tree is not a simple name, this is not a valid qualified name. +

+

+
+
+
+ +
Returns:
a null if this is not a qualified name, or a dot-separated string + of the name and properties.
+
+
+
+ +

+isQualifiedName

+
+public boolean isQualifiedName()
+
+
Returns whether a node corresponds to a simple or a qualified name, such as + x or a.b.c or this.a. +

+

+
+
+
+
+
+
+
+ +

+isUnscopedQualifiedName

+
+public boolean isUnscopedQualifiedName()
+
+
Returns whether a node corresponds to a simple or a qualified name without + a "this" reference, such as a.b.c, but not this.a + . +

+

+
+
+
+
+
+
+
+ +

+detachFromParent

+
+public Node detachFromParent()
+
+
Removes this node from its parent. Equivalent to: + node.getParent().removeChild(); +

+

+
+
+
+
+
+
+
+ +

+removeFirstChild

+
+public Node removeFirstChild()
+
+
Removes the first child of Node. Equivalent to: + node.removeChild(node.getFirstChild()); +

+

+
+
+
+ +
Returns:
The removed Node.
+
+
+
+ +

+removeChildren

+
+public Node removeChildren()
+
+
+
+
+
+ +
Returns:
A Node that is the head of the list of children.
+
+
+
+ +

+detachChildren

+
+public void detachChildren()
+
+
Removes all children from this node and isolates the children from each + other. +

+

+
+
+
+
+
+
+
+ +

+removeChildAfter

+
+public Node removeChildAfter(Node prev)
+
+
+
+
+
+
+
+
+
+ +

+cloneNode

+
+public Node cloneNode()
+
+
+
+
+
+ +
Returns:
A detached clone of the Node, specifically excluding its children.
+
+
+
+ +

+cloneTree

+
+public Node cloneTree()
+
+
+
+
+
+ +
Returns:
A detached clone of the Node and all its children.
+
+
+
+ +

+copyInformationFrom

+
+public Node copyInformationFrom(Node other)
+
+
Copies source file and name information from the other + node given to the current node. Used for maintaining + debug information across node append and remove operations. +

+

+
+
+
+ +
Returns:
this
+
+
+
+ +

+copyInformationFromForTree

+
+public Node copyInformationFromForTree(Node other)
+
+
Copies source file and name information from the other node to the + entire tree rooted at this node. +

+

+
+
+
+ +
Returns:
this
+
+
+
+ +

+useSourceInfoFrom

+
+public Node useSourceInfoFrom(Node other)
+
+
Overwrite all the source information in this node with + that of other. +

+

+
+
+
+
+
+
+
+ +

+srcref

+
+public Node srcref(Node other)
+
+
+
+
+
+
+
+
+
+ +

+useSourceInfoFromForTree

+
+public Node useSourceInfoFromForTree(Node other)
+
+
Overwrite all the source information in this node and its subtree with + that of other. +

+

+
+
+
+
+
+
+
+ +

+srcrefTree

+
+public Node srcrefTree(Node other)
+
+
+
+
+
+
+
+
+
+ +

+useSourceInfoIfMissingFrom

+
+public Node useSourceInfoIfMissingFrom(Node other)
+
+
Overwrite all the source information in this node with + that of other iff the source info is missing. +

+

+
+
+
+
+
+
+
+ +

+useSourceInfoIfMissingFromForTree

+
+public Node useSourceInfoIfMissingFromForTree(Node other)
+
+
Overwrite all the source information in this node and its subtree with + that of other iff the source info is missing. +

+

+
+
+
+
+
+
+
+ +

+getJSType

+
+public JSType getJSType()
+
+
+
+
+
+
+
+
+
+ +

+setJSType

+
+public void setJSType(JSType jsType)
+
+
+
+
+
+
+
+
+
+ +

+getJsDocBuilderForNode

+
+public Node.FileLevelJsDocBuilder getJsDocBuilderForNode()
+
+
+
+
+
+
+
+
+
+ +

+getJSDocInfo

+
+public JSDocInfo getJSDocInfo()
+
+
Get the JSDocInfo attached to this node. +

+

+
+
+
+ +
Returns:
the information or null if no JSDoc is attached to this + node
+
+
+
+ +

+setJSDocInfo

+
+public void setJSDocInfo(JSDocInfo info)
+
+
Sets the JSDocInfo attached to this node. +

+

+
+
+
+
+
+
+
+ +

+setVarArgs

+
+public void setVarArgs(boolean varArgs)
+
+
Sets whether this node is a variable length argument node. This + method is meaningful only on Token.NAME nodes + used to define a Token.FUNCTION's argument list. +

+

+
+
+
+
+
+
+
+ +

+isVarArgs

+
+public boolean isVarArgs()
+
+
Returns whether this node is a variable length argument node. This + method's return value is meaningful only on Token.NAME nodes + used to define a Token.FUNCTION's argument list. +

+

+
+
+
+
+
+
+
+ +

+setOptionalArg

+
+public void setOptionalArg(boolean optionalArg)
+
+
Sets whether this node is an optional argument node. This + method is meaningful only on Token.NAME nodes + used to define a Token.FUNCTION's argument list. +

+

+
+
+
+
+
+
+
+ +

+isOptionalArg

+
+public boolean isOptionalArg()
+
+
Returns whether this node is an optional argument node. This + method's return value is meaningful only on Token.NAME nodes + used to define a Token.FUNCTION's argument list. +

+

+
+
+
+
+
+
+
+ +

+setIsSyntheticBlock

+
+public void setIsSyntheticBlock(boolean val)
+
+
Sets whether this is a synthetic block that should not be considered + a real source block. +

+

+
+
+
+
+
+
+
+ +

+isSyntheticBlock

+
+public boolean isSyntheticBlock()
+
+
Returns whether this is a synthetic block that should not be considered + a real source block. +

+

+
+
+
+
+
+
+
+ +

+setDirectives

+
+public void setDirectives(Set<String> val)
+
+
Sets the ES5 directives on this node. +

+

+
+
+
+
+
+
+
+ +

+getDirectives

+
+public Set<String> getDirectives()
+
+
Returns the set of ES5 directives for this node. +

+

+
+
+
+
+
+
+
+ +

+addSuppression

+
+public void addSuppression(String warning)
+
+
Adds a warning to be suppressed. This is indistinguishable + from having a @suppress tag in the code. +

+

+
+
+
+
+
+
+
+ +

+setWasEmptyNode

+
+public void setWasEmptyNode(boolean val)
+
+
Sets whether this is a synthetic block that should not be considered + a real source block. +

+

+
+
+
+
+
+
+
+ +

+wasEmptyNode

+
+public boolean wasEmptyNode()
+
+
Returns whether this is a synthetic block that should not be considered + a real source block. +

+

+
+
+
+
+
+
+
+ +

+setSideEffectFlags

+
+public void setSideEffectFlags(int flags)
+
+
Marks this function or constructor call's side effect flags. + This property is only meaningful for Token.CALL and + Token.NEW nodes. +

+

+
+
+
+
+
+
+
+ +

+setSideEffectFlags

+
+public void setSideEffectFlags(Node.SideEffectFlags flags)
+
+
+
+
+
+
+
+
+
+ +

+getSideEffectFlags

+
+public int getSideEffectFlags()
+
+
Returns the side effects flags for this node. +

+

+
+
+
+
+
+
+
+ +

+isOnlyModifiesThisCall

+
+public boolean isOnlyModifiesThisCall()
+
+
+
+
+
+ +
Returns:
Whether the only side-effect is "modifies this"
+
+
+
+ +

+isNoSideEffectsCall

+
+public boolean isNoSideEffectsCall()
+
+
Returns true if this node is a function or constructor call that + has no side effects. +

+

+
+
+
+
+
+
+
+ +

+isLocalResultCall

+
+public boolean isLocalResultCall()
+
+
Returns true if this node is a function or constructor call that + returns a primitive or a local object (an object that has no other + references). +

+

+
+
+
+
+
+
+
+ +

+isQuotedString

+
+public boolean isQuotedString()
+
+
This should only be called for STRING nodes children of OBJECTLIT. +

+

+
+
+
+
+
+
+
+ +

+setQuotedString

+
+public void setQuotedString()
+
+
This should only be called for STRING nodes children of OBJECTLIT. +

+

+
+
+
+
+
+
+
+ +

+isAdd

+
+public boolean isAdd()
+
+
AST type check methods +

+

+
+
+
+
+
+
+
+ +

+isAnd

+
+public boolean isAnd()
+
+
+
+
+
+
+
+
+
+ +

+isArrayLit

+
+public boolean isArrayLit()
+
+
+
+
+
+
+
+
+
+ +

+isAssign

+
+public boolean isAssign()
+
+
+
+
+
+
+
+
+
+ +

+isAssignAdd

+
+public boolean isAssignAdd()
+
+
+
+
+
+
+
+
+
+ +

+isBlock

+
+public boolean isBlock()
+
+
+
+
+
+
+
+
+
+ +

+isBreak

+
+public boolean isBreak()
+
+
+
+
+
+
+
+
+
+ +

+isCall

+
+public boolean isCall()
+
+
+
+
+
+
+
+
+
+ +

+isCase

+
+public boolean isCase()
+
+
+
+
+
+
+
+
+
+ +

+isCatch

+
+public boolean isCatch()
+
+
+
+
+
+
+
+
+
+ +

+isComma

+
+public boolean isComma()
+
+
+
+
+
+
+
+
+
+ +

+isContinue

+
+public boolean isContinue()
+
+
+
+
+
+
+
+
+
+ +

+isDebugger

+
+public boolean isDebugger()
+
+
+
+
+
+
+
+
+
+ +

+isDec

+
+public boolean isDec()
+
+
+
+
+
+
+
+
+
+ +

+isDefaultCase

+
+public boolean isDefaultCase()
+
+
+
+
+
+
+
+
+
+ +

+isDelProp

+
+public boolean isDelProp()
+
+
+
+
+
+
+
+
+
+ +

+isDo

+
+public boolean isDo()
+
+
+
+
+
+
+
+
+
+ +

+isEmpty

+
+public boolean isEmpty()
+
+
+
+
+
+
+
+
+
+ +

+isExprResult

+
+public boolean isExprResult()
+
+
+
+
+
+
+
+
+
+ +

+isFalse

+
+public boolean isFalse()
+
+
+
+
+
+
+
+
+
+ +

+isFor

+
+public boolean isFor()
+
+
+
+
+
+
+
+
+
+ +

+isFunction

+
+public boolean isFunction()
+
+
+
+
+
+
+
+
+
+ +

+isGetterDef

+
+public boolean isGetterDef()
+
+
+
+
+
+
+
+
+
+ +

+isGetElem

+
+public boolean isGetElem()
+
+
+
+
+
+
+
+
+
+ +

+isGetProp

+
+public boolean isGetProp()
+
+
+
+
+
+
+
+
+
+ +

+isHook

+
+public boolean isHook()
+
+
+
+
+
+
+
+
+
+ +

+isIf

+
+public boolean isIf()
+
+
+
+
+
+
+
+
+
+ +

+isIn

+
+public boolean isIn()
+
+
+
+
+
+
+
+
+
+ +

+isInc

+
+public boolean isInc()
+
+
+
+
+
+
+
+
+
+ +

+isInstanceOf

+
+public boolean isInstanceOf()
+
+
+
+
+
+
+
+
+
+ +

+isLabel

+
+public boolean isLabel()
+
+
+
+
+
+
+
+
+
+ +

+isLabelName

+
+public boolean isLabelName()
+
+
+
+
+
+
+
+
+
+ +

+isName

+
+public boolean isName()
+
+
+
+
+
+
+
+
+
+ +

+isNE

+
+public boolean isNE()
+
+
+
+
+
+
+
+
+
+ +

+isNew

+
+public boolean isNew()
+
+
+
+
+
+
+
+
+
+ +

+isNot

+
+public boolean isNot()
+
+
+
+
+
+
+
+
+
+ +

+isNull

+
+public boolean isNull()
+
+
+
+
+
+
+
+
+
+ +

+isNumber

+
+public boolean isNumber()
+
+
+
+
+
+
+
+
+
+ +

+isObjectLit

+
+public boolean isObjectLit()
+
+
+
+
+
+
+
+
+
+ +

+isOr

+
+public boolean isOr()
+
+
+
+
+
+
+
+
+
+ +

+isParamList

+
+public boolean isParamList()
+
+
+
+
+
+
+
+
+
+ +

+isRegExp

+
+public boolean isRegExp()
+
+
+
+
+
+
+
+
+
+ +

+isReturn

+
+public boolean isReturn()
+
+
+
+
+
+
+
+
+
+ +

+isScript

+
+public boolean isScript()
+
+
+
+
+
+
+
+
+
+ +

+isSetterDef

+
+public boolean isSetterDef()
+
+
+
+
+
+
+
+
+
+ +

+isString

+
+public boolean isString()
+
+
+
+
+
+
+
+
+
+ +

+isSwitch

+
+public boolean isSwitch()
+
+
+
+
+
+
+
+
+
+ +

+isThis

+
+public boolean isThis()
+
+
+
+
+
+
+
+
+
+ +

+isThrow

+
+public boolean isThrow()
+
+
+
+
+
+
+
+
+
+ +

+isTrue

+
+public boolean isTrue()
+
+
+
+
+
+
+
+
+
+ +

+isTry

+
+public boolean isTry()
+
+
+
+
+
+
+
+
+
+ +

+isTypeOf

+
+public boolean isTypeOf()
+
+
+
+
+
+
+
+
+
+ +

+isVar

+
+public boolean isVar()
+
+
+
+
+
+
+
+
+
+ +

+isVoid

+
+public boolean isVoid()
+
+
+
+
+
+
+
+
+
+ +

+isWhile

+
+public boolean isWhile()
+
+
+
+
+
+
+
+
+
+ +

+isWith

+
+public boolean isWith()
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/ScriptRuntime.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/ScriptRuntime.html new file mode 100644 index 0000000..9f14a0c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/ScriptRuntime.html @@ -0,0 +1,453 @@ + + + + + +ScriptRuntime (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class ScriptRuntime

+
+java.lang.Object
+  extended by com.google.javascript.rhino.ScriptRuntime
+
+
+
+
public class ScriptRuntime
extends Object
+ + +

+This is the class that implements the runtime. +

+ +

+


+ +

+ + + + + + + + + + + + + + + +
+Field Summary
+static doubleNaN + +
+           
+static doublenegativeZero + +
+           
+  + + + + + + + + + + + +
+Constructor Summary
+protected ScriptRuntime() + +
+          No instances should be created.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static StringescapeString(String s) + +
+           
+static StringescapeString(String s, + char escapeQuote) + +
+          For escaping strings printed by object and array literals; not quite + the same as 'escape.'
+static StringgetMessage(String messageId, + Object[] arguments) + +
+           
+static StringgetMessage0(String messageId) + +
+           
+static StringgetMessage1(String messageId, + Object arg1) + +
+           
+static booleanisJSLineTerminator(int c) + +
+           
+static longtestUint32String(String str) + +
+          If str is a decimal presentation of Uint32 value, return it as long.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+NaN

+
+public static final double NaN
+
+
+
+
+
+ +

+negativeZero

+
+public static final double negativeZero
+
+
+
+
+ + + + + + + + +
+Constructor Detail
+ +

+ScriptRuntime

+
+protected ScriptRuntime()
+
+
No instances should be created. +

+

+ + + + + + + + +
+Method Detail
+ +

+isJSLineTerminator

+
+public static boolean isJSLineTerminator(int c)
+
+
+
+
+
+
+ +

+escapeString

+
+public static String escapeString(String s)
+
+
+
+
+
+
+ +

+escapeString

+
+public static String escapeString(String s,
+                                  char escapeQuote)
+
+
For escaping strings printed by object and array literals; not quite + the same as 'escape.' +

+

+
+
+
+
+ +

+testUint32String

+
+public static long testUint32String(String str)
+
+
If str is a decimal presentation of Uint32 value, return it as long. + Othewise return -1L; +

+

+
+
+
+
+ +

+getMessage0

+
+public static String getMessage0(String messageId)
+
+
+
+
+
+
+ +

+getMessage1

+
+public static String getMessage1(String messageId,
+                                 Object arg1)
+
+
+
+
+
+
+ +

+getMessage

+
+public static String getMessage(String messageId,
+                                Object[] arguments)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/SimpleErrorReporter.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/SimpleErrorReporter.html new file mode 100644 index 0000000..14e9f16 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/SimpleErrorReporter.html @@ -0,0 +1,376 @@ + + + + + +SimpleErrorReporter (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class SimpleErrorReporter

+
+java.lang.Object
+  extended by com.google.javascript.rhino.SimpleErrorReporter
+
+
+
All Implemented Interfaces:
ErrorReporter
+
+
+
+
public class SimpleErrorReporter
extends Object
implements ErrorReporter
+ + +

+A simple ErrorReporter that collects warnings and errors and makes + them accessible via errors() and warnings(). +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
SimpleErrorReporter() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voiderror(String message, + String sourceName, + int line, + int lineOffset) + +
+          Report an error.
+ List<String>errors() + +
+          Returns the list of errors, or null if there were none.
+ voidwarning(String message, + String sourceName, + int line, + int lineOffset) + +
+          Report a warning.
+ List<String>warnings() + +
+          Returns the list of warnings, or null if there were none.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SimpleErrorReporter

+
+public SimpleErrorReporter()
+
+
+ + + + + + + + +
+Method Detail
+ +

+warning

+
+public void warning(String message,
+                    String sourceName,
+                    int line,
+                    int lineOffset)
+
+
Description copied from interface: ErrorReporter
+
Report a warning. + + The implementing class may choose to ignore the warning + if it desires. +

+

+
Specified by:
warning in interface ErrorReporter
+
+
+
Parameters:
message - a String describing the warning
sourceName - a String describing the JavaScript source + where the warning occured; typically a filename or URL
line - the line number associated with the warning
lineOffset - the offset into lineSource where problem was detected
+
+
+
+ +

+error

+
+public void error(String message,
+                  String sourceName,
+                  int line,
+                  int lineOffset)
+
+
Description copied from interface: ErrorReporter
+
Report an error. + + The implementing class is free to throw an exception if + it desires. + + If execution has not yet begun, the JavaScript engine is + free to find additional errors rather than terminating + the translation. It will not execute a script that had + errors, however. +

+

+
Specified by:
error in interface ErrorReporter
+
+
+
Parameters:
message - a String describing the error
sourceName - a String describing the JavaScript source + where the error occured; typically a filename or URL
line - the line number associated with the error
lineOffset - the offset into lineSource where problem was detected
+
+
+
+ +

+errors

+
+public List<String> errors()
+
+
Returns the list of errors, or null if there were none. +

+

+
+
+
+
+
+
+
+ +

+warnings

+
+public List<String> warnings()
+
+
Returns the list of warnings, or null if there were none. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/SourcePosition.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/SourcePosition.html new file mode 100644 index 0000000..42a1123 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/SourcePosition.html @@ -0,0 +1,406 @@ + + + + + +SourcePosition (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class SourcePosition<T>

+
+java.lang.Object
+  extended by com.google.javascript.rhino.SourcePosition<T>
+
+
+
Direct Known Subclasses:
JSDocInfo.NamePosition, JSDocInfo.StringPosition, JSDocInfo.TypePosition
+
+
+
+
public abstract class SourcePosition<T>
extends Object
+ + +

+Represents a position in some piece of source code, with an associated + item of type T found at that position. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
SourcePosition() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ intgetEndLine() + +
+          Returns the ending line number of this position.
+ TgetItem() + +
+          Returns the item found at this source position.
+ intgetPositionOnEndLine() + +
+          Returns the character position on the ending line.
+ intgetPositionOnStartLine() + +
+          Returns the character position on the starting line.
+ intgetStartLine() + +
+          Returns the starting line number of this position.
+ voidsetItem(T item) + +
+          Sets the item that this source position references.
+ voidsetPositionInformation(int startLineno, + int startCharno, + int endLineno, + int endCharno) + +
+          Sets the position information contained in this source position.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SourcePosition

+
+public SourcePosition()
+
+
+ + + + + + + + +
+Method Detail
+ +

+setItem

+
+public void setItem(T item)
+
+
Sets the item that this source position references. +

+

+
+
+
+
+ +

+setPositionInformation

+
+public void setPositionInformation(int startLineno,
+                                   int startCharno,
+                                   int endLineno,
+                                   int endCharno)
+
+
Sets the position information contained in this source position. +

+

+
+
+
+
+ +

+getItem

+
+public T getItem()
+
+
Returns the item found at this source position. +

+

+
+
+
+
+ +

+getStartLine

+
+public int getStartLine()
+
+
Returns the starting line number of this position. +

+

+
+
+
+
+ +

+getPositionOnStartLine

+
+public int getPositionOnStartLine()
+
+
Returns the character position on the starting line. +

+

+
+
+
+
+ +

+getEndLine

+
+public int getEndLine()
+
+
Returns the ending line number of this position. +

+

+
+
+
+
+ +

+getPositionOnEndLine

+
+public int getPositionOnEndLine()
+
+
Returns the character position on the ending line. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/Token.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/Token.html new file mode 100644 index 0000000..2c26700 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/Token.html @@ -0,0 +1,2440 @@ + + + + + +Token (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class Token

+
+java.lang.Object
+  extended by com.google.javascript.rhino.Token
+
+
+
+
public class Token
extends Object
+ + +

+This class implements the JavaScript scanner. + + It is based on the C source files jsscan.c and jsscan.h + in the jsref package. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Field Summary
+static intADD + +
+          Token types.
+static intAND + +
+          Token types.
+static intANNOTATION + +
+          Token types.
+static intARRAYLIT + +
+          Token types.
+static intASSIGN + +
+          Token types.
+static intASSIGN_ADD + +
+          Token types.
+static intASSIGN_BITAND + +
+          Token types.
+static intASSIGN_BITOR + +
+          Token types.
+static intASSIGN_BITXOR + +
+          Token types.
+static intASSIGN_DIV + +
+          Token types.
+static intASSIGN_LSH + +
+          Token types.
+static intASSIGN_MOD + +
+          Token types.
+static intASSIGN_MUL + +
+          Token types.
+static intASSIGN_RSH + +
+          Token types.
+static intASSIGN_SUB + +
+          Token types.
+static intASSIGN_URSH + +
+          Token types.
+static intBANG + +
+          Token types.
+static intBITAND + +
+          Token types.
+static intBITNOT + +
+          Token types.
+static intBITOR + +
+          Token types.
+static intBITXOR + +
+          Token types.
+static intBLOCK + +
+          Token types.
+static intBREAK + +
+          Token types.
+static intCALL + +
+          Token types.
+static intCASE + +
+          Token types.
+static intCATCH + +
+          Token types.
+static intCOLON + +
+          Token types.
+static intCOMMA + +
+          Token types.
+static intCONST + +
+          Token types.
+static intCONTINUE + +
+          Token types.
+static intDEBUGGER + +
+          Token types.
+static intDEC + +
+          Token types.
+static intDEFAULT + +
+           
+static intDEFAULT_CASE + +
+          Token types.
+static intDELPROP + +
+          Token types.
+static intDIV + +
+          Token types.
+static intDO + +
+          Token types.
+static intELLIPSIS + +
+          Token types.
+static intEMPTY + +
+          Token types.
+static intEOC + +
+          Token types.
+static intEQ + +
+          Token types.
+static intEQUALS + +
+          Token types.
+static intERROR + +
+          Token types.
+static intEXPR_RESULT + +
+          Token types.
+static intFALSE + +
+          Token types.
+static intFOR + +
+          Token types.
+static intFUNCTION + +
+          Token types.
+static intGE + +
+          Token types.
+static intGET + +
+           
+static intGETELEM + +
+          Token types.
+static intGETPROP + +
+          Token types.
+static intGETTER_DEF + +
+          Token types.
+static intGT + +
+          Token types.
+static intHOOK + +
+          Token types.
+static intIF + +
+          Token types.
+static intIN + +
+          Token types.
+static intINC + +
+          Token types.
+static intINSTANCEOF + +
+          Token types.
+static intLABEL + +
+          Token types.
+static intLABEL_NAME + +
+          Token types.
+static intLB + +
+          Token types.
+static intLC + +
+          Token types.
+static intLE + +
+          Token types.
+static intLP + +
+           
+static intLSH + +
+          Token types.
+static intLT + +
+          Token types.
+static intMOD + +
+          Token types.
+static intMUL + +
+          Token types.
+static intNAME + +
+          Token types.
+static intNE + +
+          Token types.
+static intNEG + +
+          Token types.
+static intNEW + +
+          Token types.
+static intNOT + +
+          Token types.
+static intNULL + +
+          Token types.
+static intNUMBER + +
+          Token types.
+static intOBJECTLIT + +
+          Token types.
+static intOR + +
+          Token types.
+static intPARAM_LIST + +
+          Token types.
+static intPIPE + +
+          Token types.
+static intPOS + +
+          Token types.
+static intQMARK + +
+          Token types.
+static intREGEXP + +
+          Token types.
+static intRETURN + +
+          Token types.
+static intRSH + +
+          Token types.
+static intSCRIPT + +
+          Token types.
+static intSET + +
+           
+static intSETTER_DEF + +
+          Token types.
+static intSHEQ + +
+          Token types.
+static intSHNE + +
+          Token types.
+static intSTAR + +
+          Token types.
+static intSTRING + +
+          Token types.
+static intSUB + +
+          Token types.
+static intSWITCH + +
+          Token types.
+static intTHIS + +
+          Token types.
+static intTHROW + +
+          Token types.
+static intTRUE + +
+          Token types.
+static intTRY + +
+          Token types.
+static intTYPEOF + +
+          Token types.
+static intURSH + +
+          Token types.
+static intVAR + +
+          Token types.
+static intVOID + +
+          Token types.
+static intWHILE + +
+          Token types.
+static intWITH + +
+          Token types.
+  + + + + + + + + + + +
+Constructor Summary
Token() + +
+           
+  + + + + + + + + + + + +
+Method Summary
+static Stringname(int token) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+ERROR

+
+public static final int ERROR
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+RETURN

+
+public static final int RETURN
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+BITOR

+
+public static final int BITOR
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+BITXOR

+
+public static final int BITXOR
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+BITAND

+
+public static final int BITAND
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+EQ

+
+public static final int EQ
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+NE

+
+public static final int NE
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+LT

+
+public static final int LT
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+LE

+
+public static final int LE
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+GT

+
+public static final int GT
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+GE

+
+public static final int GE
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+LSH

+
+public static final int LSH
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+RSH

+
+public static final int RSH
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+URSH

+
+public static final int URSH
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+ADD

+
+public static final int ADD
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+SUB

+
+public static final int SUB
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+MUL

+
+public static final int MUL
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+DIV

+
+public static final int DIV
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+MOD

+
+public static final int MOD
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+NOT

+
+public static final int NOT
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+BITNOT

+
+public static final int BITNOT
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+POS

+
+public static final int POS
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+NEG

+
+public static final int NEG
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+NEW

+
+public static final int NEW
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+DELPROP

+
+public static final int DELPROP
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+TYPEOF

+
+public static final int TYPEOF
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+GETPROP

+
+public static final int GETPROP
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+GETELEM

+
+public static final int GETELEM
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+CALL

+
+public static final int CALL
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+NAME

+
+public static final int NAME
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+NUMBER

+
+public static final int NUMBER
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+STRING

+
+public static final int STRING
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+NULL

+
+public static final int NULL
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+THIS

+
+public static final int THIS
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+FALSE

+
+public static final int FALSE
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+TRUE

+
+public static final int TRUE
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+SHEQ

+
+public static final int SHEQ
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+SHNE

+
+public static final int SHNE
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+REGEXP

+
+public static final int REGEXP
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+THROW

+
+public static final int THROW
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+IN

+
+public static final int IN
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+INSTANCEOF

+
+public static final int INSTANCEOF
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+ARRAYLIT

+
+public static final int ARRAYLIT
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+OBJECTLIT

+
+public static final int OBJECTLIT
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+TRY

+
+public static final int TRY
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+PARAM_LIST

+
+public static final int PARAM_LIST
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+COMMA

+
+public static final int COMMA
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+ASSIGN

+
+public static final int ASSIGN
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+ASSIGN_BITOR

+
+public static final int ASSIGN_BITOR
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+ASSIGN_BITXOR

+
+public static final int ASSIGN_BITXOR
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+ASSIGN_BITAND

+
+public static final int ASSIGN_BITAND
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+ASSIGN_LSH

+
+public static final int ASSIGN_LSH
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+ASSIGN_RSH

+
+public static final int ASSIGN_RSH
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+ASSIGN_URSH

+
+public static final int ASSIGN_URSH
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+ASSIGN_ADD

+
+public static final int ASSIGN_ADD
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+ASSIGN_SUB

+
+public static final int ASSIGN_SUB
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+ASSIGN_MUL

+
+public static final int ASSIGN_MUL
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+ASSIGN_DIV

+
+public static final int ASSIGN_DIV
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+ASSIGN_MOD

+
+public static final int ASSIGN_MOD
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+HOOK

+
+public static final int HOOK
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+OR

+
+public static final int OR
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+AND

+
+public static final int AND
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+INC

+
+public static final int INC
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+DEC

+
+public static final int DEC
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+FUNCTION

+
+public static final int FUNCTION
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+IF

+
+public static final int IF
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+SWITCH

+
+public static final int SWITCH
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+CASE

+
+public static final int CASE
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+DEFAULT_CASE

+
+public static final int DEFAULT_CASE
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+WHILE

+
+public static final int WHILE
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+DO

+
+public static final int DO
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+FOR

+
+public static final int FOR
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+BREAK

+
+public static final int BREAK
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+CONTINUE

+
+public static final int CONTINUE
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+VAR

+
+public static final int VAR
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+WITH

+
+public static final int WITH
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+CATCH

+
+public static final int CATCH
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+VOID

+
+public static final int VOID
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+EMPTY

+
+public static final int EMPTY
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+BLOCK

+
+public static final int BLOCK
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+LABEL

+
+public static final int LABEL
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+EXPR_RESULT

+
+public static final int EXPR_RESULT
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+SCRIPT

+
+public static final int SCRIPT
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+GETTER_DEF

+
+public static final int GETTER_DEF
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+SETTER_DEF

+
+public static final int SETTER_DEF
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+CONST

+
+public static final int CONST
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+DEBUGGER

+
+public static final int DEBUGGER
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+LABEL_NAME

+
+public static final int LABEL_NAME
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+ANNOTATION

+
+public static final int ANNOTATION
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+PIPE

+
+public static final int PIPE
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+STAR

+
+public static final int STAR
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+EOC

+
+public static final int EOC
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+QMARK

+
+public static final int QMARK
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+ELLIPSIS

+
+public static final int ELLIPSIS
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+BANG

+
+public static final int BANG
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+EQUALS

+
+public static final int EQUALS
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+LB

+
+public static final int LB
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+LC

+
+public static final int LC
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+COLON

+
+public static final int COLON
+
+
Token types. These values correspond to JSTokenType values in + jsscan.c. +

+

+
See Also:
Constant Field Values
+
+
+ +

+DEFAULT

+
+public static final int DEFAULT
+
+
+
See Also:
Constant Field Values
+
+
+ +

+GET

+
+public static final int GET
+
+
+
See Also:
Constant Field Values
+
+
+ +

+LP

+
+public static final int LP
+
+
+
See Also:
Constant Field Values
+
+
+ +

+SET

+
+public static final int SET
+
+
+
See Also:
Constant Field Values
+
+ + + + + + + + +
+Constructor Detail
+ +

+Token

+
+public Token()
+
+
+ + + + + + + + +
+Method Detail
+ +

+name

+
+public static String name(int token)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/TokenStream.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/TokenStream.html new file mode 100644 index 0000000..1e510fa --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/TokenStream.html @@ -0,0 +1,290 @@ + + + + + +TokenStream (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino +
+Class TokenStream

+
+java.lang.Object
+  extended by com.google.javascript.rhino.TokenStream
+
+
+
+
public class TokenStream
extends Object
+ + +

+This class implements the JavaScript scanner. + + It is based on the C source files jsscan.c and jsscan.h + in the jsref package. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
TokenStream() + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static booleanisJSIdentifier(String s) + +
+           
+static booleanisKeyword(String name) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+TokenStream

+
+public TokenStream()
+
+
+ + + + + + + + +
+Method Detail
+ +

+isKeyword

+
+public static boolean isKeyword(String name)
+
+
+
+
+
+
+ +

+isJSIdentifier

+
+public static boolean isJSIdentifier(String s)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/AllType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/AllType.html new file mode 100644 index 0000000..704d674 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/AllType.html @@ -0,0 +1,540 @@ + + + + + +AllType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class AllType

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType
+      extended by com.google.javascript.rhino.jstype.AllType
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public final class AllType
extends JSType
+ + +

+All type, representing all values. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.JSType
JSType.TypePair
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.rhino.jstype.JSType
EMPTY_TYPE_COMPONENT, ENUMDECL, NOT_A_CLASS, NOT_A_TYPE, NOT_ENUMDECL, UNKNOWN_NAME
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleancanBeCalled() + +
+          This predicate is used to test whether a given type can be used as the + 'function' in a function call.
+ StringgetDisplayName() + +
+          Returns a user meaningful label for the JSType instance.
+ BooleanLiteralSetgetPossibleToBooleanOutcomes() + +
+          Computes the set of possible outcomes of the ToBoolean predicate + for this type.
+ booleanhasDisplayName() + +
+           
+ booleanisAllType() + +
+           
+ booleanmatchesObjectContext() + +
+          This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement.
+ booleanmatchesStringContext() + +
+          This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator.
+ TernaryValuetestForEquality(JSType that) + +
+          Compares this and that.
+ + + + + +
+<T> T
+
visit(Visitor<T> visitor) + +
+          Visit this type with the given visitor.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.JSType
autobox, autoboxesTo, canAssignTo, canTestForEqualityWith, canTestForShallowEqualityWith, clearResolved, collapseUnion, dereference, differsFrom, equals, findPropertyType, forceResolve, getGreatestSubtype, getJSDocInfo, getLeastSupertype, getRestrictedTypeGivenToBooleanOutcome, getTypesUnderEquality, getTypesUnderInequality, getTypesUnderShallowEquality, getTypesUnderShallowInequality, hashCode, isArrayType, isBooleanObjectType, isBooleanValueType, isCheckedUnknownType, isConstructor, isDateType, isEmptyType, isEnumElementType, isEnumType, isEquivalent, isEquivalentTo, isFunctionPrototypeType, isFunctionType, isGlobalThisType, isInstanceType, isInterface, isNominalConstructor, isNominalType, isNoObjectType, isNoResolvedType, isNoType, isNullable, isNullType, isNumber, isNumberObjectType, isNumberValueType, isObject, isOrdinaryFunction, isRecordType, isRegexpType, isResolved, isString, isStringObjectType, isStringValueType, isSubtype, isTemplateType, isUnionType, isUnknownType, isVoidType, matchConstraint, matchesInt32Context, matchesNumberContext, matchesUint32Context, resolve, restrictByNotNullOrUndefined, setValidator, toAnnotationString, toDebugHashCodeString, toMaybeEnumElementType, toMaybeEnumType, toMaybeFunctionType, toMaybeFunctionType, toMaybeUnionType, toObjectType, toString, unboxesTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+isAllType

+
+public boolean isAllType()
+
+
+
Overrides:
isAllType in class JSType
+
+
+
+
+
+
+ +

+matchesStringContext

+
+public boolean matchesStringContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator. + + All types have at least the potential for converting to String. + When we add externally defined types, such as a browser OM, we may choose + to add types that do not automatically convert to String. +

+

+
Overrides:
matchesStringContext in class JSType
+
+
+
+
+
+
+ +

+matchesObjectContext

+
+public boolean matchesObjectContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement. + + Most types we will encounter, except notably null, have at least + the potential for converting to Object. Host defined objects can + get peculiar. +

+

+
Overrides:
matchesObjectContext in class JSType
+
+
+
+
+
+
+ +

+canBeCalled

+
+public boolean canBeCalled()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can be used as the + 'function' in a function call. +

+

+
Overrides:
canBeCalled in class JSType
+
+
+ +
Returns:
true if this type might be callable.
+
+
+
+ +

+testForEquality

+
+public TernaryValue testForEquality(JSType that)
+
+
Description copied from class: JSType
+
Compares this and that. +

+

+
Overrides:
testForEquality in class JSType
+
+
+ +
Returns:
    +
  • TernaryValue.TRUE if the comparison of values of + this type and that always succeed (such as + undefined compared to null)
  • +
  • TernaryValue.FALSE if the comparison of values of + this type and that always fails (such as + undefined compared to number)
  • +
  • TernaryValue.UNKNOWN if the comparison can succeed or + fail depending on the concrete values
  • +
+
+
+
+ +

+getDisplayName

+
+public String getDisplayName()
+
+
Description copied from class: JSType
+
Returns a user meaningful label for the JSType instance. For example, + Functions and Enums will return their declaration name (if they have one). + Some types will not have a meaningful display name. Calls to + hasDisplayName() will return true IFF getDisplayName() will return null + or a zero length string. +

+

+
Overrides:
getDisplayName in class JSType
+
+
+ +
Returns:
the display name of the type, or null if one is not available
+
+
+
+ +

+hasDisplayName

+
+public boolean hasDisplayName()
+
+
+
Overrides:
hasDisplayName in class JSType
+
+
+ +
Returns:
true if the JSType has a user meaningful label.
+
+
+
+ +

+visit

+
+public <T> T visit(Visitor<T> visitor)
+
+
Description copied from class: JSType
+
Visit this type with the given visitor. +

+

+
Specified by:
visit in class JSType
+
+
+ +
Returns:
the value returned by the visitor
See Also:
Visitor
+
+
+
+ +

+getPossibleToBooleanOutcomes

+
+public BooleanLiteralSet getPossibleToBooleanOutcomes()
+
+
Description copied from class: JSType
+
Computes the set of possible outcomes of the ToBoolean predicate + for this type. The ToBoolean predicate is defined by the ECMA-262 + standard, 3rd edition. Its behavior for simple types can be + summarized by the following table: + + + + + + + + +
typeresult
undefined{false}
null{false}
boolean{true, false}
number{true, false}
string{true, false}
Object{true}
+

+

+
Specified by:
getPossibleToBooleanOutcomes in class JSType
+
+
+ +
Returns:
the set of boolean literals for this type
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/BooleanLiteralSet.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/BooleanLiteralSet.html new file mode 100644 index 0000000..0f6213f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/BooleanLiteralSet.html @@ -0,0 +1,455 @@ + + + + + +BooleanLiteralSet (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Enum BooleanLiteralSet

+
+java.lang.Object
+  extended by java.lang.Enum<BooleanLiteralSet>
+      extended by com.google.javascript.rhino.jstype.BooleanLiteralSet
+
+
+
All Implemented Interfaces:
Serializable, Comparable<BooleanLiteralSet>
+
+
+
+
public enum BooleanLiteralSet
extends Enum<BooleanLiteralSet>
+ + +

+A set in the domain {true,false}. + There are four possible sets: {}, {true}, {false}, {true,false}. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + +
+Enum Constant Summary
BOTH + +
+           
EMPTY + +
+           
FALSE + +
+           
TRUE + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleancontains(boolean literalValue) + +
+          Returns whether this contains the given literal value.
+static BooleanLiteralSetget(boolean literalValue) + +
+          Returns the singleton set {literalValue}.
+ BooleanLiteralSetintersection(BooleanLiteralSet that) + +
+          Computes the intersection of this set and that.
+ BooleanLiteralSetunion(BooleanLiteralSet that) + +
+          Computes the union of this set and that.
+static BooleanLiteralSetvalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static BooleanLiteralSet[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+EMPTY

+
+public static final BooleanLiteralSet EMPTY
+
+
+
+
+
+ +

+TRUE

+
+public static final BooleanLiteralSet TRUE
+
+
+
+
+
+ +

+FALSE

+
+public static final BooleanLiteralSet FALSE
+
+
+
+
+
+ +

+BOTH

+
+public static final BooleanLiteralSet BOTH
+
+
+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static BooleanLiteralSet[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (BooleanLiteralSet c : BooleanLiteralSet.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static BooleanLiteralSet valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+
+ +

+intersection

+
+public BooleanLiteralSet intersection(BooleanLiteralSet that)
+
+
Computes the intersection of this set and that. +

+

+
+
+
+
+ +

+union

+
+public BooleanLiteralSet union(BooleanLiteralSet that)
+
+
Computes the union of this set and that. +

+

+
+
+
+
+ +

+contains

+
+public boolean contains(boolean literalValue)
+
+
Returns whether this contains the given literal value. +

+

+
+
+
+
+ +

+get

+
+public static BooleanLiteralSet get(boolean literalValue)
+
+
Returns the singleton set {literalValue}. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/BooleanType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/BooleanType.html new file mode 100644 index 0000000..23e4a5e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/BooleanType.html @@ -0,0 +1,590 @@ + + + + + +BooleanType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class BooleanType

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType
+      extended by com.google.javascript.rhino.jstype.BooleanType
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class BooleanType
extends JSType
+ + +

+Boolean type. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.JSType
JSType.TypePair
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.rhino.jstype.JSType
EMPTY_TYPE_COMPONENT, ENUMDECL, NOT_A_CLASS, NOT_A_TYPE, NOT_ENUMDECL, UNKNOWN_NAME
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ JSTypeautoboxesTo() + +
+          Gets the type to which this type auto-boxes.
+ StringgetDisplayName() + +
+          Returns a user meaningful label for the JSType instance.
+ BooleanLiteralSetgetPossibleToBooleanOutcomes() + +
+          Computes the set of possible outcomes of the ToBoolean predicate + for this type.
+ booleanhasDisplayName() + +
+           
+ booleanisBooleanValueType() + +
+           
+ booleanisNullable() + +
+          Tests whether this type is nullable.
+ booleanmatchesNumberContext() + +
+          This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator.
+ booleanmatchesObjectContext() + +
+          This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement.
+ booleanmatchesStringContext() + +
+          This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator.
+ TernaryValuetestForEquality(JSType that) + +
+          Compares this and that.
+ + + + + +
+<T> T
+
visit(Visitor<T> visitor) + +
+          Visit this type with the given visitor.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.JSType
autobox, canAssignTo, canBeCalled, canTestForEqualityWith, canTestForShallowEqualityWith, clearResolved, collapseUnion, dereference, differsFrom, equals, findPropertyType, forceResolve, getGreatestSubtype, getJSDocInfo, getLeastSupertype, getRestrictedTypeGivenToBooleanOutcome, getTypesUnderEquality, getTypesUnderInequality, getTypesUnderShallowEquality, getTypesUnderShallowInequality, hashCode, isAllType, isArrayType, isBooleanObjectType, isCheckedUnknownType, isConstructor, isDateType, isEmptyType, isEnumElementType, isEnumType, isEquivalent, isEquivalentTo, isFunctionPrototypeType, isFunctionType, isGlobalThisType, isInstanceType, isInterface, isNominalConstructor, isNominalType, isNoObjectType, isNoResolvedType, isNoType, isNullType, isNumber, isNumberObjectType, isNumberValueType, isObject, isOrdinaryFunction, isRecordType, isRegexpType, isResolved, isString, isStringObjectType, isStringValueType, isSubtype, isTemplateType, isUnionType, isUnknownType, isVoidType, matchConstraint, matchesInt32Context, matchesUint32Context, resolve, restrictByNotNullOrUndefined, setValidator, toAnnotationString, toDebugHashCodeString, toMaybeEnumElementType, toMaybeEnumType, toMaybeFunctionType, toMaybeFunctionType, toMaybeUnionType, toObjectType, toString, unboxesTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+isNullable

+
+public boolean isNullable()
+
+
Description copied from class: JSType
+
Tests whether this type is nullable. +

+

+
Overrides:
isNullable in class JSType
+
+
+
+
+
+
+ +

+testForEquality

+
+public TernaryValue testForEquality(JSType that)
+
+
Description copied from class: JSType
+
Compares this and that. +

+

+
Overrides:
testForEquality in class JSType
+
+
+ +
Returns:
    +
  • TernaryValue.TRUE if the comparison of values of + this type and that always succeed (such as + undefined compared to null)
  • +
  • TernaryValue.FALSE if the comparison of values of + this type and that always fails (such as + undefined compared to number)
  • +
  • TernaryValue.UNKNOWN if the comparison can succeed or + fail depending on the concrete values
  • +
+
+
+
+ +

+isBooleanValueType

+
+public boolean isBooleanValueType()
+
+
+
Overrides:
isBooleanValueType in class JSType
+
+
+
+
+
+
+ +

+matchesNumberContext

+
+public boolean matchesNumberContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator. +

+

+
Overrides:
matchesNumberContext in class JSType
+
+
+
+
+
+
+ +

+matchesStringContext

+
+public boolean matchesStringContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator. + + All types have at least the potential for converting to String. + When we add externally defined types, such as a browser OM, we may choose + to add types that do not automatically convert to String. +

+

+
Overrides:
matchesStringContext in class JSType
+
+
+
+
+
+
+ +

+matchesObjectContext

+
+public boolean matchesObjectContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement. + + Most types we will encounter, except notably null, have at least + the potential for converting to Object. Host defined objects can + get peculiar. +

+

+
Overrides:
matchesObjectContext in class JSType
+
+
+
+
+
+
+ +

+autoboxesTo

+
+public JSType autoboxesTo()
+
+
Description copied from class: JSType
+
Gets the type to which this type auto-boxes. +

+

+
Overrides:
autoboxesTo in class JSType
+
+
+ +
Returns:
the auto-boxed type or null if this type does not auto-box
+
+
+
+ +

+getDisplayName

+
+public String getDisplayName()
+
+
Description copied from class: JSType
+
Returns a user meaningful label for the JSType instance. For example, + Functions and Enums will return their declaration name (if they have one). + Some types will not have a meaningful display name. Calls to + hasDisplayName() will return true IFF getDisplayName() will return null + or a zero length string. +

+

+
Overrides:
getDisplayName in class JSType
+
+
+ +
Returns:
the display name of the type, or null if one is not available
+
+
+
+ +

+getPossibleToBooleanOutcomes

+
+public BooleanLiteralSet getPossibleToBooleanOutcomes()
+
+
Description copied from class: JSType
+
Computes the set of possible outcomes of the ToBoolean predicate + for this type. The ToBoolean predicate is defined by the ECMA-262 + standard, 3rd edition. Its behavior for simple types can be + summarized by the following table: + + + + + + + + +
typeresult
undefined{false}
null{false}
boolean{true, false}
number{true, false}
string{true, false}
Object{true}
+

+

+
Specified by:
getPossibleToBooleanOutcomes in class JSType
+
+
+ +
Returns:
the set of boolean literals for this type
+
+
+
+ +

+visit

+
+public <T> T visit(Visitor<T> visitor)
+
+
Description copied from class: JSType
+
Visit this type with the given visitor. +

+

+
Specified by:
visit in class JSType
+
+
+ +
Returns:
the value returned by the visitor
See Also:
Visitor
+
+
+
+ +

+hasDisplayName

+
+public boolean hasDisplayName()
+
+
+
Overrides:
hasDisplayName in class JSType
+
+
+ +
Returns:
true if the JSType has a user meaningful label.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/EnumElementType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/EnumElementType.html new file mode 100644 index 0000000..55de25f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/EnumElementType.html @@ -0,0 +1,1023 @@ + + + + + +EnumElementType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class EnumElementType

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType
+      extended by com.google.javascript.rhino.jstype.ObjectType
+          extended by com.google.javascript.rhino.jstype.EnumElementType
+
+
+
All Implemented Interfaces:
StaticScope<JSType>, Serializable
+
+
+
+
public class EnumElementType
extends ObjectType
+ + +

+The type of individual elements of an enum type + (see EnumType). +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.ObjectType
ObjectType.Property
+  + + + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.JSType
JSType.TypePair
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.rhino.jstype.JSType
EMPTY_TYPE_COMPONENT, ENUMDECL, NOT_A_CLASS, NOT_A_TYPE, NOT_ENUMDECL, UNKNOWN_NAME
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ JSTypeautoboxesTo() + +
+          Gets the type to which this type auto-boxes.
+ booleancanBeCalled() + +
+          This predicate is used to test whether a given type can be used as the + 'function' in a function call.
+ JSTypefindPropertyType(String propertyName) + +
+          Coerces this type to an Object type, then gets the type of the property + whose name is given.
+ FunctionTypegetConstructor() + +
+          Gets this object's constructor.
+ ObjectTypegetImplicitPrototype() + +
+          Gets the implicit prototype (a.k.a.
+ JSTypegetPrimitiveType() + +
+          Gets the primitive type of this enum element.
+ intgetPropertiesCount() + +
+          Gets the number of properties of this object.
+ JSTypegetPropertyType(String propertyName) + +
+          Gets the property type of the property whose name is given.
+ StringgetReferenceName() + +
+          Gets the reference name for this object.
+ ObjectType.PropertygetSlot(String name) + +
+          Returns any defined slot within this scope for this name.
+ inthashCode() + +
+          If this is equal to a NamedType object, its hashCode must be equal + to the hashCode of the NamedType object.
+ booleanhasProperty(String propertyName) + +
+          Checks whether the property whose name is given is present on the + object.
+ booleanhasReferenceName() + +
+          Returns true if the object is named.
+ booleanisEquivalentTo(JSType that) + +
+          Checks if two types are equivalent.
+ booleanisNominalType() + +
+          Whether this type is a nominal type (a named instance object or + a named enum).
+ booleanisNullable() + +
+          This predicate determines whether objects of this type can have the null + value, and therefore can appear in contexts where null is expected.
+ booleanisObject() + +
+          Tests whether this type is an Object, or any subtype thereof.
+ booleanisPropertyTypeDeclared(String propertyName) + +
+          Checks whether the property's type is declared.
+ booleanisPropertyTypeInferred(String propertyName) + +
+          Checks whether the property's type is inferred.
+ booleanisSubtype(JSType that) + +
+          Checks whether this is a subtype of that.
+ booleanmatchesNumberContext() + +
+          This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator.
+ booleanmatchesObjectContext() + +
+          This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement.
+ booleanmatchesStringContext() + +
+          This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator.
+ TernaryValuetestForEquality(JSType that) + +
+          Compares this and that.
+ EnumElementTypetoMaybeEnumElementType() + +
+          Downcasts this to an EnumElementType, or returns null if this is not an EnumElementType.
+ + + + + +
+<T> T
+
visit(Visitor<T> visitor) + +
+          Visit this type with the given visitor.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.ObjectType
cast, clearCachedValues, createDelegateSuffix, defineDeclaredProperty, defineInferredProperty, getCtorExtendedInterfaces, getCtorImplementedInterfaces, getDisplayName, getIndexType, getJSDocInfo, getNormalizedReferenceName, getOwnerFunction, getOwnPropertyJSDocInfo, getOwnPropertyNames, getOwnSlot, getParameterType, getParentScope, getPossibleToBooleanOutcomes, getPropertyNames, getPropertyNode, getRootNode, getTypeOfThis, hasCachedValues, hasOwnProperty, isFunctionPrototypeType, isNativeObjectType, isPropertyInExterns, isUnknownType, removeProperty, setJSDocInfo, setPropertyJSDocInfo
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.JSType
autobox, canAssignTo, canTestForEqualityWith, canTestForShallowEqualityWith, clearResolved, collapseUnion, dereference, differsFrom, equals, forceResolve, getGreatestSubtype, getLeastSupertype, getRestrictedTypeGivenToBooleanOutcome, getTypesUnderEquality, getTypesUnderInequality, getTypesUnderShallowEquality, getTypesUnderShallowInequality, hasDisplayName, isAllType, isArrayType, isBooleanObjectType, isBooleanValueType, isCheckedUnknownType, isConstructor, isDateType, isEmptyType, isEnumElementType, isEnumType, isEquivalent, isFunctionType, isGlobalThisType, isInstanceType, isInterface, isNominalConstructor, isNoObjectType, isNoResolvedType, isNoType, isNullType, isNumber, isNumberObjectType, isNumberValueType, isOrdinaryFunction, isRecordType, isRegexpType, isResolved, isString, isStringObjectType, isStringValueType, isTemplateType, isUnionType, isVoidType, matchConstraint, matchesInt32Context, matchesUint32Context, resolve, restrictByNotNullOrUndefined, setValidator, toAnnotationString, toDebugHashCodeString, toMaybeEnumType, toMaybeFunctionType, toMaybeFunctionType, toMaybeUnionType, toObjectType, toString, unboxesTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getSlot

+
+public ObjectType.Property getSlot(String name)
+
+
Description copied from interface: StaticScope
+
Returns any defined slot within this scope for this name. This call + continues searching through parent scopes if a slot with this name is not + found in the current scope. +

+

+
Specified by:
getSlot in interface StaticScope<JSType>
Specified by:
getSlot in class ObjectType
+
+
+
Parameters:
name - The name of the variable slot to look up. +
Returns:
The defined slot for the variable, or null if no + definition exists.
+
+
+
+ +

+toMaybeEnumElementType

+
+public EnumElementType toMaybeEnumElementType()
+
+
Description copied from class: JSType
+
Downcasts this to an EnumElementType, or returns null if this is not an EnumElementType. +

+

+
Overrides:
toMaybeEnumElementType in class JSType
+
+
+
+
+
+
+ +

+matchesNumberContext

+
+public boolean matchesNumberContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator. +

+

+
Overrides:
matchesNumberContext in class JSType
+
+
+
+
+
+
+ +

+matchesStringContext

+
+public boolean matchesStringContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator. + + All types have at least the potential for converting to String. + When we add externally defined types, such as a browser OM, we may choose + to add types that do not automatically convert to String. +

+

+
Overrides:
matchesStringContext in class JSType
+
+
+
+
+
+
+ +

+matchesObjectContext

+
+public boolean matchesObjectContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement. + + Most types we will encounter, except notably null, have at least + the potential for converting to Object. Host defined objects can + get peculiar. +

+

+
Overrides:
matchesObjectContext in class JSType
+
+
+
+
+
+
+ +

+canBeCalled

+
+public boolean canBeCalled()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can be used as the + 'function' in a function call. +

+

+
Overrides:
canBeCalled in class JSType
+
+
+ +
Returns:
true if this type might be callable.
+
+
+
+ +

+isObject

+
+public boolean isObject()
+
+
Description copied from class: JSType
+
Tests whether this type is an Object, or any subtype thereof. +

+

+
Overrides:
isObject in class ObjectType
+
+
+ +
Returns:
this &lt;: Object
+
+
+
+ +

+testForEquality

+
+public TernaryValue testForEquality(JSType that)
+
+
Description copied from class: JSType
+
Compares this and that. +

+

+
Overrides:
testForEquality in class ObjectType
+
+
+ +
Returns:
    +
  • TernaryValue.TRUE if the comparison of values of + this type and that always succeed (such as + undefined compared to null)
  • +
  • TernaryValue.FALSE if the comparison of values of + this type and that always fails (such as + undefined compared to number)
  • +
  • TernaryValue.UNKNOWN if the comparison can succeed or + fail depending on the concrete values
  • +
+
+
+
+ +

+isNullable

+
+public boolean isNullable()
+
+
This predicate determines whether objects of this type can have the null + value, and therefore can appear in contexts where null is expected. +

+

+
Overrides:
isNullable in class JSType
+
+
+ +
Returns:
true for everything but Number and Boolean types.
+
+
+
+ +

+isNominalType

+
+public boolean isNominalType()
+
+
Description copied from class: JSType
+
Whether this type is a nominal type (a named instance object or + a named enum). +

+

+
Overrides:
isNominalType in class JSType
+
+
+
+
+
+
+ +

+isEquivalentTo

+
+public boolean isEquivalentTo(JSType that)
+
+
Description copied from class: JSType
+
Checks if two types are equivalent. +

+

+
Overrides:
isEquivalentTo in class JSType
+
+
+
+
+
+
+ +

+hashCode

+
+public int hashCode()
+
+
If this is equal to a NamedType object, its hashCode must be equal + to the hashCode of the NamedType object. +

+

+
Overrides:
hashCode in class JSType
+
+
+
+
+
+
+ +

+getReferenceName

+
+public String getReferenceName()
+
+
Description copied from class: ObjectType
+
Gets the reference name for this object. This includes named types + like constructors, prototypes, and enums. It notably does not include + literal types like strings and booleans and structural types. +

+

+
Specified by:
getReferenceName in class ObjectType
+
+
+ +
Returns:
the object's name or null if this is an anonymous + object
+
+
+
+ +

+hasReferenceName

+
+public boolean hasReferenceName()
+
+
Description copied from class: ObjectType
+
Returns true if the object is named. +

+

+
Overrides:
hasReferenceName in class ObjectType
+
+
+ +
Returns:
true if the object is named, false if it is anonymous
+
+
+
+ +

+isSubtype

+
+public boolean isSubtype(JSType that)
+
+
Description copied from class: JSType
+
Checks whether this is a subtype of that.

+ + Subtyping rules: +

    +
  • (unknown) — every type is a subtype of the Unknown type.
  • +
  • (no) — the No type is a subtype of every type.
  • +
  • (no-object) — the NoObject type is a subtype of every object + type (i.e. subtypes of the Object type).
  • +
  • (ref) — a type is a subtype of itself.
  • +
  • (union-l) — A union type is a subtype of a type U if all the + union type's constituents are a subtype of U. Formally
    + (T<sub>1</sub>, &hellip;, T<sub>n</sub>) &lt;: U if and only + T<sub>k</sub> &lt;: U for all k &isin; 1..n.
  • +
  • (union-r) — A type U is a subtype of a union type if it is a + subtype of one of the union type's constituents. Formally
    + U &lt;: (T<sub>1</sub>, &hellip;, T<sub>n</sub>) if and only + if U &lt;: T<sub>k</sub> for some index k.
  • +
  • (objects) — an Object O<sub>1</sub> is a subtype + of an object O<sub>2</sub> if it has more properties + than O<sub>2</sub> and all common properties are + pairwise subtypes.
  • +
+

+

+
Overrides:
isSubtype in class JSType
+
+
+ +
Returns:
this &lt;: that
+
+
+
+ +

+visit

+
+public <T> T visit(Visitor<T> visitor)
+
+
Description copied from class: JSType
+
Visit this type with the given visitor. +

+

+
Overrides:
visit in class ObjectType
+
+
+ +
Returns:
the value returned by the visitor
See Also:
Visitor
+
+
+
+ +

+isPropertyTypeDeclared

+
+public boolean isPropertyTypeDeclared(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property's type is declared. +

+

+
Specified by:
isPropertyTypeDeclared in class ObjectType
+
+
+
+
+
+
+ +

+isPropertyTypeInferred

+
+public boolean isPropertyTypeInferred(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property's type is inferred. +

+

+
Specified by:
isPropertyTypeInferred in class ObjectType
+
+
+
+
+
+
+ +

+getImplicitPrototype

+
+public ObjectType getImplicitPrototype()
+
+
Description copied from class: ObjectType
+
Gets the implicit prototype (a.k.a. the [[Prototype]] property). +

+

+
Specified by:
getImplicitPrototype in class ObjectType
+
+
+
+
+
+
+ +

+getPropertiesCount

+
+public int getPropertiesCount()
+
+
Description copied from class: ObjectType
+
Gets the number of properties of this object. +

+

+
Specified by:
getPropertiesCount in class ObjectType
+
+
+
+
+
+
+ +

+findPropertyType

+
+public JSType findPropertyType(String propertyName)
+
+
Description copied from class: JSType
+
Coerces this type to an Object type, then gets the type of the property + whose name is given. + + Unlike ObjectType.getPropertyType(java.lang.String), returns null if the property + is not found. +

+

+
Overrides:
findPropertyType in class ObjectType
+
+
+ +
Returns:
The property's type. null if the current type cannot + have properties, or if the type is not found.
+
+
+
+ +

+getPropertyType

+
+public JSType getPropertyType(String propertyName)
+
+
Description copied from class: ObjectType
+
Gets the property type of the property whose name is given. If the + underlying object does not have this property, the Unknown type is + returned to indicate that no information is available on this property. +

+

+
Specified by:
getPropertyType in class ObjectType
+
+
+ +
Returns:
the property's type or UnknownType. This method never + returns null.
+
+
+
+ +

+hasProperty

+
+public boolean hasProperty(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property whose name is given is present on the + object. +

+

+
Specified by:
hasProperty in class ObjectType
+
+
+
+
+
+
+ +

+getConstructor

+
+public FunctionType getConstructor()
+
+
Description copied from class: ObjectType
+
Gets this object's constructor. +

+

+
Specified by:
getConstructor in class ObjectType
+
+
+ +
Returns:
this object's constructor or null if it is a native + object (constructed natively v.s. by instantiation of a function)
+
+
+
+ +

+autoboxesTo

+
+public JSType autoboxesTo()
+
+
Description copied from class: JSType
+
Gets the type to which this type auto-boxes. +

+

+
Overrides:
autoboxesTo in class JSType
+
+
+ +
Returns:
the auto-boxed type or null if this type does not auto-box
+
+
+
+ +

+getPrimitiveType

+
+public JSType getPrimitiveType()
+
+
Gets the primitive type of this enum element. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/EnumType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/EnumType.html new file mode 100644 index 0000000..5dfc393 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/EnumType.html @@ -0,0 +1,1307 @@ + + + + + +EnumType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class EnumType

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType
+      extended by com.google.javascript.rhino.jstype.ObjectType
+          extended by com.google.javascript.rhino.jstype.EnumType
+
+
+
All Implemented Interfaces:
StaticScope<JSType>, Serializable
+
+
+
+
public class EnumType
extends ObjectType
+ + +

+An enum type representing a branded collection of elements. Each element + is referenced by its name, and has an EnumElementType type. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.ObjectType
ObjectType.Property
+  + + + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.JSType
JSType.TypePair
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.rhino.jstype.JSType
EMPTY_TYPE_COMPONENT, ENUMDECL, NOT_A_CLASS, NOT_A_TYPE, NOT_ENUMDECL, UNKNOWN_NAME
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleancanBeCalled() + +
+          This predicate is used to test whether a given type can be used as the + 'function' in a function call.
+ booleandefineElement(String name, + Node definingNode) + +
+          Defines a new element on this enum.
+ FunctionTypegetConstructor() + +
+          Gets this object's constructor.
+ Iterable<ObjectType>getCtorExtendedInterfaces() + +
+          Gets the interfaces extended by the interface associated with this type.
+ Iterable<ObjectType>getCtorImplementedInterfaces() + +
+          Gets the interfaces implemented by the ctor associated with this type.
+ StringgetDisplayName() + +
+          Returns a user meaningful label for the JSType instance.
+ Set<String>getElements() + +
+          Gets the elements defined on this enum.
+ EnumElementTypegetElementsType() + +
+          Gets the elements' type.
+ ObjectTypegetImplicitPrototype() + +
+          Gets the implicit prototype (a.k.a.
+ FunctionTypegetOwnerFunction() + +
+          Gets the owner of this if it's a function prototype.
+ JSDocInfogetOwnPropertyJSDocInfo(String propertyName) + +
+          Gets the docInfo on the specified property on this type.
+ Set<String>getOwnPropertyNames() + +
+          Returns the names of all the properties directly on this type.
+ intgetPropertiesCount() + +
+          Gets the number of properties of this object.
+ NodegetPropertyNode(String propertyName) + +
+          Gets the node corresponding to the definition of the specified property.
+ JSTypegetPropertyType(String property) + +
+          Gets the property type of the property whose name is given.
+ StringgetReferenceName() + +
+          Gets the reference name for this object.
+ ObjectType.PropertygetSlot(String name) + +
+          Returns any defined slot within this scope for this name.
+ NodegetSource() + +
+          Gets the source node or null if this is an unknown enum.
+ booleanhasCachedValues() + +
+          Returns true if any cached values have been set for this type.
+ booleanhasOwnProperty(String propertyName) + +
+          Checks whether the property whose name is given is present directly on + the object.
+ booleanhasProperty(String propertyName) + +
+          Checks whether the property whose name is given is present on the + object.
+ booleanhasReferenceName() + +
+          Returns true if the object is named.
+ booleanisNativeObjectType() + +
+          Whether this is a built-in object.
+ booleanisPropertyInExterns(String propertyName) + +
+          Checks whether the property was defined in the externs.
+ booleanisPropertyTypeDeclared(String property) + +
+          Checks whether the property's type is declared.
+ booleanisPropertyTypeInferred(String property) + +
+          Checks whether the property's type is inferred.
+ booleanisSubtype(JSType that) + +
+          Checks whether this is a subtype of that.
+ voidmatchConstraint(ObjectType constraintObj) + +
+          Modify this type so that it matches the specified type.
+ booleanmatchesNumberContext() + +
+          This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator.
+ booleanmatchesObjectContext() + +
+          This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement.
+ booleanmatchesStringContext() + +
+          This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator.
+ booleanremoveProperty(String name) + +
+          Removes the declared or inferred property from this ObjectType.
+ voidsetPropertyJSDocInfo(String propertyName, + JSDocInfo info) + +
+          Sets the docInfo for the specified property from the + JSDocInfo on its definition.
+ TernaryValuetestForEquality(JSType that) + +
+          Compares this and that.
+ EnumTypetoMaybeEnumType() + +
+          Downcasts this to an EnumType, or returns null if this is not an EnumType.
+ JSTypeunboxesTo() + +
+          Gets the type to which this type unboxes.
+ + + + + +
+<T> T
+
visit(Visitor<T> visitor) + +
+          Visit this type with the given visitor.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.ObjectType
cast, clearCachedValues, createDelegateSuffix, defineDeclaredProperty, defineInferredProperty, findPropertyType, getIndexType, getJSDocInfo, getNormalizedReferenceName, getOwnSlot, getParameterType, getParentScope, getPossibleToBooleanOutcomes, getPropertyNames, getRootNode, getTypeOfThis, isFunctionPrototypeType, isObject, isUnknownType, setJSDocInfo
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.JSType
autobox, autoboxesTo, canAssignTo, canTestForEqualityWith, canTestForShallowEqualityWith, clearResolved, collapseUnion, dereference, differsFrom, equals, forceResolve, getGreatestSubtype, getLeastSupertype, getRestrictedTypeGivenToBooleanOutcome, getTypesUnderEquality, getTypesUnderInequality, getTypesUnderShallowEquality, getTypesUnderShallowInequality, hasDisplayName, hashCode, isAllType, isArrayType, isBooleanObjectType, isBooleanValueType, isCheckedUnknownType, isConstructor, isDateType, isEmptyType, isEnumElementType, isEnumType, isEquivalent, isEquivalentTo, isFunctionType, isGlobalThisType, isInstanceType, isInterface, isNominalConstructor, isNominalType, isNoObjectType, isNoResolvedType, isNoType, isNullable, isNullType, isNumber, isNumberObjectType, isNumberValueType, isOrdinaryFunction, isRecordType, isRegexpType, isResolved, isString, isStringObjectType, isStringValueType, isTemplateType, isUnionType, isVoidType, matchesInt32Context, matchesUint32Context, resolve, restrictByNotNullOrUndefined, setValidator, toAnnotationString, toDebugHashCodeString, toMaybeEnumElementType, toMaybeFunctionType, toMaybeFunctionType, toMaybeUnionType, toObjectType, toString
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getSource

+
+public Node getSource()
+
+
Gets the source node or null if this is an unknown enum. +

+

+
+
+
+
+ +

+toMaybeEnumType

+
+public EnumType toMaybeEnumType()
+
+
Description copied from class: JSType
+
Downcasts this to an EnumType, or returns null if this is not an EnumType. +

+

+
Overrides:
toMaybeEnumType in class JSType
+
+
+
+
+
+
+ +

+getImplicitPrototype

+
+public ObjectType getImplicitPrototype()
+
+
Description copied from class: ObjectType
+
Gets the implicit prototype (a.k.a. the [[Prototype]] property). +

+

+
+
+
+
+
+
+
+ +

+getElements

+
+public Set<String> getElements()
+
+
Gets the elements defined on this enum. +

+

+ +
Returns:
the elements' names defined on this enum. The returned set is + immutable.
+
+
+
+ +

+defineElement

+
+public boolean defineElement(String name,
+                             Node definingNode)
+
+
Defines a new element on this enum. +

+

+
Parameters:
name - the name of the new element
definingNode - the Node that defines this new element +
Returns:
true iff the new element is added successfully
+
+
+
+ +

+getElementsType

+
+public EnumElementType getElementsType()
+
+
Gets the elements' type. +

+

+
+
+
+
+ +

+testForEquality

+
+public TernaryValue testForEquality(JSType that)
+
+
Description copied from class: JSType
+
Compares this and that. +

+

+
Overrides:
testForEquality in class ObjectType
+
+
+ +
Returns:
    +
  • TernaryValue.TRUE if the comparison of values of + this type and that always succeed (such as + undefined compared to null)
  • +
  • TernaryValue.FALSE if the comparison of values of + this type and that always fails (such as + undefined compared to number)
  • +
  • TernaryValue.UNKNOWN if the comparison can succeed or + fail depending on the concrete values
  • +
+
+
+
+ +

+isSubtype

+
+public boolean isSubtype(JSType that)
+
+
Description copied from class: JSType
+
Checks whether this is a subtype of that.

+ + Subtyping rules: +

    +
  • (unknown) — every type is a subtype of the Unknown type.
  • +
  • (no) — the No type is a subtype of every type.
  • +
  • (no-object) — the NoObject type is a subtype of every object + type (i.e. subtypes of the Object type).
  • +
  • (ref) — a type is a subtype of itself.
  • +
  • (union-l) — A union type is a subtype of a type U if all the + union type's constituents are a subtype of U. Formally
    + (T<sub>1</sub>, &hellip;, T<sub>n</sub>) &lt;: U if and only + T<sub>k</sub> &lt;: U for all k &isin; 1..n.
  • +
  • (union-r) — A type U is a subtype of a union type if it is a + subtype of one of the union type's constituents. Formally
    + U &lt;: (T<sub>1</sub>, &hellip;, T<sub>n</sub>) if and only + if U &lt;: T<sub>k</sub> for some index k.
  • +
  • (objects) — an Object O<sub>1</sub> is a subtype + of an object O<sub>2</sub> if it has more properties + than O<sub>2</sub> and all common properties are + pairwise subtypes.
  • +
+

+

+
+
+
+ +
Returns:
this &lt;: that
+
+
+
+ +

+getDisplayName

+
+public String getDisplayName()
+
+
Description copied from class: JSType
+
Returns a user meaningful label for the JSType instance. For example, + Functions and Enums will return their declaration name (if they have one). + Some types will not have a meaningful display name. Calls to + hasDisplayName() will return true IFF getDisplayName() will return null + or a zero length string. +

+

+
Overrides:
getDisplayName in class ObjectType
+
+
+ +
Returns:
the display name of the type, or null if one is not available
+
+
+
+ +

+visit

+
+public <T> T visit(Visitor<T> visitor)
+
+
Description copied from class: JSType
+
Visit this type with the given visitor. +

+

+
Overrides:
visit in class ObjectType
+
+
+ +
Returns:
the value returned by the visitor
See Also:
Visitor
+
+
+
+ +

+getConstructor

+
+public FunctionType getConstructor()
+
+
Description copied from class: ObjectType
+
Gets this object's constructor. +

+

+
+
+
+ +
Returns:
this object's constructor or null if it is a native + object (constructed natively v.s. by instantiation of a function)
+
+
+
+ +

+matchesNumberContext

+
+public boolean matchesNumberContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator. +

+

+
+
+
+
+
+
+
+ +

+matchesStringContext

+
+public boolean matchesStringContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator. + + All types have at least the potential for converting to String. + When we add externally defined types, such as a browser OM, we may choose + to add types that do not automatically convert to String. +

+

+
+
+
+
+
+
+
+ +

+matchesObjectContext

+
+public boolean matchesObjectContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement. + + Most types we will encounter, except notably null, have at least + the potential for converting to Object. Host defined objects can + get peculiar. +

+

+
+
+
+
+
+
+
+ +

+getSlot

+
+public ObjectType.Property getSlot(String name)
+
+
Description copied from interface: StaticScope
+
Returns any defined slot within this scope for this name. This call + continues searching through parent scopes if a slot with this name is not + found in the current scope. +

+

+
Specified by:
getSlot in interface StaticScope<JSType>
Specified by:
getSlot in class ObjectType
+
+
+
Parameters:
name - The name of the variable slot to look up. +
Returns:
The defined slot for the variable, or null if no + definition exists.
+
+
+
+ +

+getPropertiesCount

+
+public int getPropertiesCount()
+
+
Gets the number of properties of this object. +

+

+
Specified by:
getPropertiesCount in class ObjectType
+
+
+
+
+
+
+ +

+hasProperty

+
+public boolean hasProperty(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property whose name is given is present on the + object. +

+

+
Specified by:
hasProperty in class ObjectType
+
+
+
+
+
+
+ +

+hasOwnProperty

+
+public boolean hasOwnProperty(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property whose name is given is present directly on + the object. Returns false even if it is declared on a supertype. +

+

+
Overrides:
hasOwnProperty in class ObjectType
+
+
+
+
+
+
+ +

+getOwnPropertyNames

+
+public Set<String> getOwnPropertyNames()
+
+
Description copied from class: ObjectType
+
Returns the names of all the properties directly on this type. +

+

+
Overrides:
getOwnPropertyNames in class ObjectType
+
+
+
+
+
+
+ +

+isPropertyTypeDeclared

+
+public boolean isPropertyTypeDeclared(String property)
+
+
Description copied from class: ObjectType
+
Checks whether the property's type is declared. +

+

+
Specified by:
isPropertyTypeDeclared in class ObjectType
+
+
+
+
+
+
+ +

+isPropertyTypeInferred

+
+public boolean isPropertyTypeInferred(String property)
+
+
Description copied from class: ObjectType
+
Checks whether the property's type is inferred. +

+

+
Specified by:
isPropertyTypeInferred in class ObjectType
+
+
+
+
+
+
+ +

+getPropertyType

+
+public JSType getPropertyType(String property)
+
+
Description copied from class: ObjectType
+
Gets the property type of the property whose name is given. If the + underlying object does not have this property, the Unknown type is + returned to indicate that no information is available on this property. +

+

+
Specified by:
getPropertyType in class ObjectType
+
+
+ +
Returns:
the property's type or UnknownType. This method never + returns null.
+
+
+
+ +

+isPropertyInExterns

+
+public boolean isPropertyInExterns(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property was defined in the externs. +

+

+
Overrides:
isPropertyInExterns in class ObjectType
+
+
+
+
+
+
+ +

+removeProperty

+
+public boolean removeProperty(String name)
+
+
Description copied from class: ObjectType
+
Removes the declared or inferred property from this ObjectType. +

+

+
Overrides:
removeProperty in class ObjectType
+
+
+
Parameters:
name - the property's name +
Returns:
true if the property was removed successfully. False if the + property did not exist, or could not be removed.
+
+
+
+ +

+getPropertyNode

+
+public Node getPropertyNode(String propertyName)
+
+
Description copied from class: ObjectType
+
Gets the node corresponding to the definition of the specified property. + This could be the node corresponding to declaration of the property or the + node corresponding to the first reference to this property, e.g., + "this.propertyName" in a constructor. Note this is mainly intended to be + an estimate of where in the source code a property is defined. Sometime + the returned node is not even part of the global AST but in the AST of the + JsDoc that defines a type. +

+

+
Overrides:
getPropertyNode in class ObjectType
+
+
+
Parameters:
propertyName - the name of the property +
Returns:
the Node corresponding to the property or null.
+
+
+
+ +

+getOwnPropertyJSDocInfo

+
+public JSDocInfo getOwnPropertyJSDocInfo(String propertyName)
+
+
Description copied from class: ObjectType
+
Gets the docInfo on the specified property on this type. This should not + be done implemented recursively, as you generally need to know exactly on + which type in the prototype chain the JSDocInfo exists. +

+

+
Overrides:
getOwnPropertyJSDocInfo in class ObjectType
+
+
+
+
+
+
+ +

+setPropertyJSDocInfo

+
+public void setPropertyJSDocInfo(String propertyName,
+                                 JSDocInfo info)
+
+
Description copied from class: ObjectType
+
Sets the docInfo for the specified property from the + JSDocInfo on its definition. +

+

+
Overrides:
setPropertyJSDocInfo in class ObjectType
+
+
+
info - JSDocInfo for the property definition. May be + null.
+
+
+
+ +

+unboxesTo

+
+public JSType unboxesTo()
+
+
Description copied from class: JSType
+
Gets the type to which this type unboxes. +

+

+
Overrides:
unboxesTo in class JSType
+
+
+ +
Returns:
the unboxed type or null if this type does not unbox.
+
+
+
+ +

+canBeCalled

+
+public boolean canBeCalled()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can be used as the + 'function' in a function call. +

+

+
Overrides:
canBeCalled in class JSType
+
+
+ +
Returns:
true if this type might be callable.
+
+
+
+ +

+getReferenceName

+
+public String getReferenceName()
+
+
Description copied from class: ObjectType
+
Gets the reference name for this object. This includes named types + like constructors, prototypes, and enums. It notably does not include + literal types like strings and booleans and structural types. +

+

+
Specified by:
getReferenceName in class ObjectType
+
+
+ +
Returns:
the object's name or null if this is an anonymous + object
+
+
+
+ +

+hasReferenceName

+
+public boolean hasReferenceName()
+
+
Description copied from class: ObjectType
+
Returns true if the object is named. +

+

+
Overrides:
hasReferenceName in class ObjectType
+
+
+ +
Returns:
true if the object is named, false if it is anonymous
+
+
+
+ +

+hasCachedValues

+
+public boolean hasCachedValues()
+
+
Description copied from class: ObjectType
+
Returns true if any cached values have been set for this type. If true, + then the prototype chain should not be changed, as it might invalidate the + cached values. +

+

+
Overrides:
hasCachedValues in class ObjectType
+
+
+
+
+
+
+ +

+isNativeObjectType

+
+public boolean isNativeObjectType()
+
+
Whether this is a built-in object. +

+

+
Overrides:
isNativeObjectType in class ObjectType
+
+
+
+
+
+
+ +

+getOwnerFunction

+
+public FunctionType getOwnerFunction()
+
+
Description copied from class: ObjectType
+
Gets the owner of this if it's a function prototype. +

+

+
Overrides:
getOwnerFunction in class ObjectType
+
+
+
+
+
+
+ +

+getCtorImplementedInterfaces

+
+public Iterable<ObjectType> getCtorImplementedInterfaces()
+
+
Description copied from class: ObjectType
+
Gets the interfaces implemented by the ctor associated with this type. + Intended to be overridden by subclasses. +

+

+
Overrides:
getCtorImplementedInterfaces in class ObjectType
+
+
+
+
+
+
+ +

+getCtorExtendedInterfaces

+
+public Iterable<ObjectType> getCtorExtendedInterfaces()
+
+
Description copied from class: ObjectType
+
Gets the interfaces extended by the interface associated with this type. + Intended to be overriden by subclasses. +

+

+
Overrides:
getCtorExtendedInterfaces in class ObjectType
+
+
+
+
+
+
+ +

+matchConstraint

+
+public void matchConstraint(ObjectType constraintObj)
+
+
Description copied from class: JSType
+
Modify this type so that it matches the specified type. + + This is useful for reverse type-inference, where we want to + infer that an object literal matches its contraint (much like + how the java compiler does reverse-inference to figure out generics). +

+

+
Overrides:
matchConstraint in class JSType
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/FunctionBuilder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/FunctionBuilder.html new file mode 100644 index 0000000..1f8db78 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/FunctionBuilder.html @@ -0,0 +1,527 @@ + + + + + +FunctionBuilder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class FunctionBuilder

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.FunctionBuilder
+
+
+
+
public final class FunctionBuilder
extends Object
+ + +

+A builder class for function and arrow types. + + If you need to build an interface constructor, + use JSTypeRegistry.createInterfaceType(java.lang.String, com.google.javascript.rhino.Node). +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
FunctionBuilder(JSTypeRegistry registry) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ FunctionTypebuild() + +
+          Construct a new function type.
+ FunctionBuildercopyFromOtherFunction(FunctionType otherType) + +
+          Copies all the information from another function type.
+ FunctionBuilderforConstructor() + +
+          Make this a constructor.
+ FunctionBuildersetIsConstructor(boolean isConstructor) + +
+          Set whether this is a constructor.
+ FunctionBuilderwithInferredReturnType(JSType returnType) + +
+          Sets an inferred return type.
+ FunctionBuilderwithName(String name) + +
+          Set the name of the function type.
+ FunctionBuilderwithParams(FunctionParamBuilder params) + +
+          Set the parameters of the function type from a FunctionParamBuilder.
+ FunctionBuilderwithParamsNode(Node parametersNode) + +
+          Set the parameters of the function type with a specially-formatted node.
+ FunctionBuilderwithReturnType(JSType returnType) + +
+          Set the return type.
+ FunctionBuilderwithReturnType(JSType returnType, + boolean inferred) + +
+          Set the return type and whether it's inferred.
+ FunctionBuilderwithSourceNode(Node sourceNode) + +
+          Set the source node of the function type.
+ FunctionBuilderwithTemplateName(String templateTypeName) + +
+          Set the template name.
+ FunctionBuilderwithTypeOfThis(ObjectType typeOfThis) + +
+          Set the "this" type.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+FunctionBuilder

+
+public FunctionBuilder(JSTypeRegistry registry)
+
+
+ + + + + + + + +
+Method Detail
+ +

+withName

+
+public FunctionBuilder withName(String name)
+
+
Set the name of the function type. +

+

+
+
+
+
+ +

+withSourceNode

+
+public FunctionBuilder withSourceNode(Node sourceNode)
+
+
Set the source node of the function type. +

+

+
+
+
+
+ +

+withParams

+
+public FunctionBuilder withParams(FunctionParamBuilder params)
+
+
Set the parameters of the function type from a FunctionParamBuilder. +

+

+
+
+
+
+ +

+withParamsNode

+
+public FunctionBuilder withParamsNode(Node parametersNode)
+
+
Set the parameters of the function type with a specially-formatted node. +

+

+
+
+
+
+ +

+withReturnType

+
+public FunctionBuilder withReturnType(JSType returnType)
+
+
Set the return type. +

+

+
+
+
+
+ +

+withReturnType

+
+public FunctionBuilder withReturnType(JSType returnType,
+                                      boolean inferred)
+
+
Set the return type and whether it's inferred. +

+

+
+
+
+
+ +

+withInferredReturnType

+
+public FunctionBuilder withInferredReturnType(JSType returnType)
+
+
Sets an inferred return type. +

+

+
+
+
+
+ +

+withTypeOfThis

+
+public FunctionBuilder withTypeOfThis(ObjectType typeOfThis)
+
+
Set the "this" type. +

+

+
+
+
+
+ +

+withTemplateName

+
+public FunctionBuilder withTemplateName(String templateTypeName)
+
+
Set the template name. +

+

+
+
+
+
+ +

+forConstructor

+
+public FunctionBuilder forConstructor()
+
+
Make this a constructor. +

+

+
+
+
+
+ +

+setIsConstructor

+
+public FunctionBuilder setIsConstructor(boolean isConstructor)
+
+
Set whether this is a constructor. +

+

+
+
+
+
+ +

+copyFromOtherFunction

+
+public FunctionBuilder copyFromOtherFunction(FunctionType otherType)
+
+
Copies all the information from another function type. +

+

+
+
+
+
+ +

+build

+
+public FunctionType build()
+
+
Construct a new function type. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/FunctionParamBuilder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/FunctionParamBuilder.html new file mode 100644 index 0000000..8df1b53 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/FunctionParamBuilder.html @@ -0,0 +1,398 @@ + + + + + +FunctionParamBuilder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class FunctionParamBuilder

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.FunctionParamBuilder
+
+
+
+
public class FunctionParamBuilder
extends Object
+ + +

+A builder for the Rhino Node representing Function parameters. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
FunctionParamBuilder(JSTypeRegistry registry) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleanaddOptionalParams(JSType... types) + +
+          Add optional parameters of the given type to the end of the param list.
+ booleanaddRequiredParams(JSType... types) + +
+          Add parameters of the given type to the end of the param list.
+ booleanaddVarArgs(JSType type) + +
+          Add variable arguments to the end of the parameter list.
+ Nodebuild() + +
+           
+ booleanhasVarArgs() + +
+           
+ NodenewOptionalParameterFromNode(Node n) + +
+          Copies the parameter specification from the given node, + but makes sure it's optional.
+ NodenewParameterFromNode(Node n) + +
+          Copies the parameter specification from the given node.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+FunctionParamBuilder

+
+public FunctionParamBuilder(JSTypeRegistry registry)
+
+
+ + + + + + + + +
+Method Detail
+ +

+addRequiredParams

+
+public boolean addRequiredParams(JSType... types)
+
+
Add parameters of the given type to the end of the param list. +

+

+ +
Returns:
False if this is called after optional params are added.
+
+
+
+ +

+addOptionalParams

+
+public boolean addOptionalParams(JSType... types)
+
+
Add optional parameters of the given type to the end of the param list. +

+

+
Parameters:
types - Types for each optional parameter. The builder will make them + undefineable. +
Returns:
False if this is called after var args are added.
+
+
+
+ +

+addVarArgs

+
+public boolean addVarArgs(JSType type)
+
+
Add variable arguments to the end of the parameter list. +

+

+ +
Returns:
False if this is called after var args are added.
+
+
+
+ +

+newParameterFromNode

+
+public Node newParameterFromNode(Node n)
+
+
Copies the parameter specification from the given node. +

+

+
+
+
+
+ +

+newOptionalParameterFromNode

+
+public Node newOptionalParameterFromNode(Node n)
+
+
Copies the parameter specification from the given node, + but makes sure it's optional. +

+

+
+
+
+
+ +

+build

+
+public Node build()
+
+
+
+
+
+
+ +

+hasVarArgs

+
+public boolean hasVarArgs()
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/FunctionType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/FunctionType.html new file mode 100644 index 0000000..2ff80a7 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/FunctionType.html @@ -0,0 +1,1991 @@ + + + + + +FunctionType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class FunctionType

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType
+      extended by com.google.javascript.rhino.jstype.ObjectType
+          extended by com.google.javascript.rhino.jstype.FunctionType
+
+
+
All Implemented Interfaces:
StaticScope<JSType>, Serializable
+
+
+
Direct Known Subclasses:
NoObjectType
+
+
+
+
public class FunctionType
extends ObjectType
+ + +

+This derived type provides extended information about a function, including + its return type and argument types.

+ + Note: the parameters list is the LP node that is the parent of the + actual NAME node containing the parsed argument list (annotated with + JSDOC_TYPE_PROP's for the compile-time type of each argument. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.ObjectType
ObjectType.Property
+  + + + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.JSType
JSType.TypePair
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.rhino.jstype.JSType
EMPTY_TYPE_COMPONENT, ENUMDECL, NOT_A_CLASS, NOT_A_TYPE, NOT_ENUMDECL, UNKNOWN_NAME
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleancanBeCalled() + +
+          This predicate is used to test whether a given type can be used as the + 'function' in a function call.
+ voidclearCachedValues() + +
+          Clear cached values.
+ Iterable<ObjectType>getAllExtendedInterfaces() + +
+          Returns all extended interfaces declared by an interfaces or its super- + interfaces.
+ Iterable<ObjectType>getAllImplementedInterfaces() + +
+          Returns all interfaces implemented by a class or its superclass and any + superclasses for any of those interfaces.
+ FunctionTypegetBindReturnType(int argsToBind) + +
+          Get the return value of calling "bind" on this function + with the specified number of arguments.
+ FunctionTypegetConstructor() + +
+          Gets this object's constructor.
+ Iterable<ObjectType>getCtorExtendedInterfaces() + +
+          Gets the interfaces extended by the interface associated with this type.
+ Iterable<ObjectType>getCtorImplementedInterfaces() + +
+          Gets the interfaces implemented by the ctor associated with this type.
+ Iterable<ObjectType>getExtendedInterfaces() + +
+          Returns interfaces directly extended by an interface
+ intgetExtendedInterfacesCount() + +
+          Returns the number of interfaces directly extended by an interface
+ Iterable<ObjectType>getImplementedInterfaces() + +
+          Returns interfaces implemented directly by a class or its superclass.
+ ObjectTypegetImplicitPrototype() + +
+          Gets the implicit prototype (a.k.a.
+ ObjectTypegetInstanceType() + +
+          Gets the type of instance of this function.
+ intgetMaxArguments() + +
+          Gets the maximum number of arguments that this function requires, + or Integer.MAX_VALUE if this is a variable argument function.
+ intgetMinArguments() + +
+          Gets the minimum number of arguments that this function requires.
+ FunctionTypegetOwnerFunction() + +
+          Gets the owner of this if it's a function prototype.
+ Iterable<ObjectType>getOwnImplementedInterfaces() + +
+          Returns interfaces directly implemented by the class.
+ JSDocInfogetOwnPropertyJSDocInfo(String propertyName) + +
+          Gets the docInfo on the specified property on this type.
+ Set<String>getOwnPropertyNames() + +
+          Includes the prototype iff someone has created it.
+ Iterable<Node>getParameters() + +
+           
+ NodegetParametersNode() + +
+          Gets an LP node that contains all params.
+ intgetPropertiesCount() + +
+          Gets the number of properties of this object.
+ NodegetPropertyNode(String propertyName) + +
+          Gets the node corresponding to the definition of the specified property.
+ JSTypegetPropertyType(String name) + +
+          Gets the property type of the property whose name is given.
+ ObjectTypegetPrototype() + +
+          Gets the prototype property of this function type.
+ StringgetReferenceName() + +
+          Gets the reference name for this object.
+ JSTypegetReturnType() + +
+           
+ ObjectType.PropertygetSlot(String name) + +
+          Returns any defined slot within this scope for this name.
+ NodegetSource() + +
+          Gets the source node or null if this is an unknown function.
+ List<FunctionType>getSubTypes() + +
+          Returns a list of types that are subtypes of this type.
+ FunctionTypegetSuperClassConstructor() + +
+          Given a constructor or an interface type, get its superclass constructor + or null if none exists.
+ StringgetTemplateTypeName() + +
+          Gets the template type name.
+static ObjectTypegetTopDefiningInterface(ObjectType type, + String propertyName) + +
+          Given an interface and a property, finds the top-most super interface + that has the property defined (including this interface).
+ ObjectTypegetTopMostDefiningType(String propertyName) + +
+          Given a constructor or an interface type and a property, finds the + top-most superclass that has the property defined (including this + constructor).
+ ObjectTypegetTypeOfThis() + +
+          Gets the type of this in this function.
+ booleanhasCachedValues() + +
+          Returns true if any cached values have been set for this type.
+ booleanhasEqualCallType(FunctionType otherType) + +
+           
+ inthashCode() + +
+           
+ booleanhasImplementedInterfaces() + +
+           
+ booleanhasInstanceType() + +
+          Returns whether this function type has an instance type.
+ booleanhasOwnProperty(String propertyName) + +
+          Checks whether the property whose name is given is present directly on + the object.
+ booleanhasProperty(String propertyName) + +
+          Checks whether the property whose name is given is present on the + object.
+ booleanhasReferenceName() + +
+          Returns true if the object is named.
+ booleanisConstructor() + +
+          Whether this type is a FunctionType that is a constructor or a + named type that points to such a type.
+ booleanisEquivalentTo(JSType otherType) + +
+          Two function types are equal if their signatures match.
+ booleanisInstanceType() + +
+          Whether this type is an Instance object of some constructor.
+ booleanisInterface() + +
+          Whether this type is a FunctionType that is an interface or a named + type that points to such a type.
+ booleanisNativeObjectType() + +
+          Whether this is a built-in object.
+ booleanisOrdinaryFunction() + +
+          Whether this type is a FunctionType that is an ordinary function or + a named type that points to such a type.
+ booleanisPropertyInExterns(String propertyName) + +
+          Checks whether the property was defined in the externs.
+ booleanisPropertyTypeDeclared(String property) + +
+          Checks whether the property's type is declared.
+ booleanisPropertyTypeInferred(String property) + +
+          Checks whether the property's type is inferred.
+ booleanisReturnTypeInferred() + +
+           
+ booleanisSubtype(JSType that) + +
+          A function is a subtype of another if their call methods are related via + subtyping and this is a subtype of that with regard to + the prototype chain.
+ voidmatchConstraint(ObjectType constraintObj) + +
+          Modify this type so that it matches the specified type.
+ booleanmatchesNumberContext() + +
+          This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator.
+ booleanmatchesObjectContext() + +
+          This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement.
+ booleanmatchesStringContext() + +
+          This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator.
+ booleanremoveProperty(String name) + +
+          Removes the declared or inferred property from this ObjectType.
+ voidsetExtendedInterfaces(List<ObjectType> extendedInterfaces) + +
+           
+ voidsetImplementedInterfaces(List<ObjectType> implementedInterfaces) + +
+           
+ voidsetPropertyJSDocInfo(String propertyName, + JSDocInfo info) + +
+          Sets the docInfo for the specified property from the + JSDocInfo on its definition.
+ voidsetPrototypeBasedOn(ObjectType baseType) + +
+          Sets the prototype, creating the prototype object from the given + base type.
+ voidsetSource(Node source) + +
+          Sets the source node.
+ StringtoDebugHashCodeString() + +
+          A hash code function for diagnosing complicated issues + around type-identity.
+ FunctionTypetoMaybeFunctionType() + +
+          Downcasts this to a FunctionType, or returns null if this is not + a function.
+ JSTypeunboxesTo() + +
+          Gets the type to which this type unboxes.
+ + + + + +
+<T> T
+
visit(Visitor<T> visitor) + +
+          Visit this type with the given visitor.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.ObjectType
cast, createDelegateSuffix, defineDeclaredProperty, defineInferredProperty, findPropertyType, getDisplayName, getIndexType, getJSDocInfo, getNormalizedReferenceName, getOwnSlot, getParameterType, getParentScope, getPossibleToBooleanOutcomes, getPropertyNames, getRootNode, isFunctionPrototypeType, isObject, isUnknownType, setJSDocInfo, testForEquality
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.JSType
autobox, autoboxesTo, canAssignTo, canTestForEqualityWith, canTestForShallowEqualityWith, clearResolved, collapseUnion, dereference, differsFrom, equals, forceResolve, getGreatestSubtype, getLeastSupertype, getRestrictedTypeGivenToBooleanOutcome, getTypesUnderEquality, getTypesUnderInequality, getTypesUnderShallowEquality, getTypesUnderShallowInequality, hasDisplayName, isAllType, isArrayType, isBooleanObjectType, isBooleanValueType, isCheckedUnknownType, isDateType, isEmptyType, isEnumElementType, isEnumType, isEquivalent, isFunctionType, isGlobalThisType, isNominalConstructor, isNominalType, isNoObjectType, isNoResolvedType, isNoType, isNullable, isNullType, isNumber, isNumberObjectType, isNumberValueType, isRecordType, isRegexpType, isResolved, isString, isStringObjectType, isStringValueType, isTemplateType, isUnionType, isVoidType, matchesInt32Context, matchesUint32Context, resolve, restrictByNotNullOrUndefined, setValidator, toAnnotationString, toMaybeEnumElementType, toMaybeEnumType, toMaybeFunctionType, toMaybeUnionType, toObjectType, toString
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+isInstanceType

+
+public boolean isInstanceType()
+
+
Description copied from class: JSType
+
Whether this type is an Instance object of some constructor. + Does not necessarily mean this is an InstanceObjectType. +

+

+
Overrides:
isInstanceType in class JSType
+
+
+
+
+
+
+ +

+isConstructor

+
+public boolean isConstructor()
+
+
Description copied from class: JSType
+
Whether this type is a FunctionType that is a constructor or a + named type that points to such a type. +

+

+
Overrides:
isConstructor in class JSType
+
+
+
+
+
+
+ +

+isInterface

+
+public boolean isInterface()
+
+
Description copied from class: JSType
+
Whether this type is a FunctionType that is an interface or a named + type that points to such a type. +

+

+
Overrides:
isInterface in class JSType
+
+
+
+
+
+
+ +

+isOrdinaryFunction

+
+public boolean isOrdinaryFunction()
+
+
Description copied from class: JSType
+
Whether this type is a FunctionType that is an ordinary function or + a named type that points to such a type. +

+

+
Overrides:
isOrdinaryFunction in class JSType
+
+
+
+
+
+
+ +

+toMaybeFunctionType

+
+public FunctionType toMaybeFunctionType()
+
+
Description copied from class: JSType
+
Downcasts this to a FunctionType, or returns null if this is not + a function. + + For the purposes of this function, we define a MaybeFunctionType as any + type in the sub-lattice + { x | LEAST_FUNCTION_TYPE <= x <= GREATEST_FUNCTION_TYPE } + This definition excludes bottom types like NoType and NoObjectType. + + This definition is somewhat arbitrary and axiomatic, but this is the + definition that makes the most sense for the most callers. +

+

+
Overrides:
toMaybeFunctionType in class JSType
+
+
+
+
+
+
+ +

+canBeCalled

+
+public boolean canBeCalled()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can be used as the + 'function' in a function call. +

+

+
+
+
+ +
Returns:
true if this type might be callable.
+
+
+
+ +

+hasImplementedInterfaces

+
+public boolean hasImplementedInterfaces()
+
+
+
+
+
+
+ +

+getParameters

+
+public Iterable<Node> getParameters()
+
+
+
+
+
+
+ +

+getParametersNode

+
+public Node getParametersNode()
+
+
Gets an LP node that contains all params. May be null. +

+

+
+
+
+
+ +

+getMinArguments

+
+public int getMinArguments()
+
+
Gets the minimum number of arguments that this function requires. +

+

+
+
+
+
+ +

+getMaxArguments

+
+public int getMaxArguments()
+
+
Gets the maximum number of arguments that this function requires, + or Integer.MAX_VALUE if this is a variable argument function. +

+

+
+
+
+
+ +

+getReturnType

+
+public JSType getReturnType()
+
+
+
+
+
+
+ +

+isReturnTypeInferred

+
+public boolean isReturnTypeInferred()
+
+
+
+
+
+
+ +

+getSlot

+
+public ObjectType.Property getSlot(String name)
+
+
Description copied from interface: StaticScope
+
Returns any defined slot within this scope for this name. This call + continues searching through parent scopes if a slot with this name is not + found in the current scope. +

+

+
Specified by:
getSlot in interface StaticScope<JSType>
+
+
+
Parameters:
name - The name of the variable slot to look up. +
Returns:
The defined slot for the variable, or null if no + definition exists.
+
+
+
+ +

+getOwnPropertyNames

+
+public Set<String> getOwnPropertyNames()
+
+
Includes the prototype iff someone has created it. We do not want + to expose the prototype for ordinary functions. +

+

+
+
+
+
+
+
+
+ +

+getPrototype

+
+public ObjectType getPrototype()
+
+
Gets the prototype property of this function type. This is + equivalent to (ObjectType) getPropertyType("prototype"). +

+

+
+
+
+
+ +

+setPrototypeBasedOn

+
+public void setPrototypeBasedOn(ObjectType baseType)
+
+
Sets the prototype, creating the prototype object from the given + base type. +

+

+
Parameters:
baseType - The base type.
+
+
+
+ +

+getAllImplementedInterfaces

+
+public Iterable<ObjectType> getAllImplementedInterfaces()
+
+
Returns all interfaces implemented by a class or its superclass and any + superclasses for any of those interfaces. If this is called before all + types are resolved, it may return an incomplete set. +

+

+
+
+
+
+ +

+getImplementedInterfaces

+
+public Iterable<ObjectType> getImplementedInterfaces()
+
+
Returns interfaces implemented directly by a class or its superclass. +

+

+
+
+
+
+ +

+getOwnImplementedInterfaces

+
+public Iterable<ObjectType> getOwnImplementedInterfaces()
+
+
Returns interfaces directly implemented by the class. +

+

+
+
+
+
+ +

+setImplementedInterfaces

+
+public void setImplementedInterfaces(List<ObjectType> implementedInterfaces)
+
+
+
+
+
+
+ +

+getAllExtendedInterfaces

+
+public Iterable<ObjectType> getAllExtendedInterfaces()
+
+
Returns all extended interfaces declared by an interfaces or its super- + interfaces. If this is called before all types are resolved, it may return + an incomplete set. +

+

+
+
+
+
+ +

+getExtendedInterfaces

+
+public Iterable<ObjectType> getExtendedInterfaces()
+
+
Returns interfaces directly extended by an interface +

+

+
+
+
+
+ +

+getExtendedInterfacesCount

+
+public int getExtendedInterfacesCount()
+
+
Returns the number of interfaces directly extended by an interface +

+

+
+
+
+
+ +

+setExtendedInterfaces

+
+public void setExtendedInterfaces(List<ObjectType> extendedInterfaces)
+                           throws UnsupportedOperationException
+
+
+ +
Throws: +
UnsupportedOperationException
+
+
+
+ +

+getPropertyType

+
+public JSType getPropertyType(String name)
+
+
Description copied from class: ObjectType
+
Gets the property type of the property whose name is given. If the + underlying object does not have this property, the Unknown type is + returned to indicate that no information is available on this property. +

+

+
+
+
+ +
Returns:
the property's type or UnknownType. This method never + returns null.
+
+
+
+ +

+getBindReturnType

+
+public FunctionType getBindReturnType(int argsToBind)
+
+
Get the return value of calling "bind" on this function + with the specified number of arguments. + + If -1 is passed, then we will return a result that accepts + any parameters. +

+

+
+
+
+
+ +

+getSuperClassConstructor

+
+public FunctionType getSuperClassConstructor()
+
+
Given a constructor or an interface type, get its superclass constructor + or null if none exists. +

+

+
+
+
+
+ +

+getTopDefiningInterface

+
+public static ObjectType getTopDefiningInterface(ObjectType type,
+                                                 String propertyName)
+
+
Given an interface and a property, finds the top-most super interface + that has the property defined (including this interface). +

+

+
+
+
+
+ +

+getTopMostDefiningType

+
+public ObjectType getTopMostDefiningType(String propertyName)
+
+
Given a constructor or an interface type and a property, finds the + top-most superclass that has the property defined (including this + constructor). +

+

+
+
+
+
+ +

+isEquivalentTo

+
+public boolean isEquivalentTo(JSType otherType)
+
+
Two function types are equal if their signatures match. Since they don't + have signatures, two interfaces are equal if their names match. +

+

+
Overrides:
isEquivalentTo in class JSType
+
+
+
+
+
+
+ +

+hashCode

+
+public int hashCode()
+
+
+
Overrides:
hashCode in class JSType
+
+
+
+
+
+
+ +

+hasEqualCallType

+
+public boolean hasEqualCallType(FunctionType otherType)
+
+
+
+
+
+
+ +

+isSubtype

+
+public boolean isSubtype(JSType that)
+
+
A function is a subtype of another if their call methods are related via + subtyping and this is a subtype of that with regard to + the prototype chain. +

+

+
+
+
+ +
Returns:
this &lt;: that
+
+
+
+ +

+visit

+
+public <T> T visit(Visitor<T> visitor)
+
+
Description copied from class: JSType
+
Visit this type with the given visitor. +

+

+
Overrides:
visit in class ObjectType
+
+
+ +
Returns:
the value returned by the visitor
See Also:
Visitor
+
+
+
+ +

+getInstanceType

+
+public ObjectType getInstanceType()
+
+
Gets the type of instance of this function. +

+

+ +
Throws: +
IllegalStateException - if this function is not a constructor + (see isConstructor()).
+
+
+
+ +

+hasInstanceType

+
+public boolean hasInstanceType()
+
+
Returns whether this function type has an instance type. +

+

+
+
+
+
+ +

+getTypeOfThis

+
+public ObjectType getTypeOfThis()
+
+
Gets the type of this in this function. +

+

+
Specified by:
getTypeOfThis in interface StaticScope<JSType>
Overrides:
getTypeOfThis in class ObjectType
+
+
+
+
+
+
+ +

+getSource

+
+public Node getSource()
+
+
Gets the source node or null if this is an unknown function. +

+

+
+
+
+
+ +

+setSource

+
+public void setSource(Node source)
+
+
Sets the source node. +

+

+
+
+
+
+ +

+clearCachedValues

+
+public void clearCachedValues()
+
+
Description copied from class: ObjectType
+
Clear cached values. Should be called before making changes to a prototype + that may have been changed since creation. +

+

+
Overrides:
clearCachedValues in class ObjectType
+
+
+
+
+
+
+ +

+getSubTypes

+
+public List<FunctionType> getSubTypes()
+
+
Returns a list of types that are subtypes of this type. This is only valid + for constructor functions, and may be null. This allows a downward + traversal of the subtype graph. +

+

+
+
+
+
+ +

+hasCachedValues

+
+public boolean hasCachedValues()
+
+
Description copied from class: ObjectType
+
Returns true if any cached values have been set for this type. If true, + then the prototype chain should not be changed, as it might invalidate the + cached values. +

+

+
+
+
+
+
+
+
+ +

+getTemplateTypeName

+
+public String getTemplateTypeName()
+
+
Gets the template type name. +

+

+
+
+
+
+ +

+toDebugHashCodeString

+
+public String toDebugHashCodeString()
+
+
Description copied from class: JSType
+
A hash code function for diagnosing complicated issues + around type-identity. +

+

+
Overrides:
toDebugHashCodeString in class JSType
+
+
+
+
+
+
+ +

+getPropertiesCount

+
+public int getPropertiesCount()
+
+
Gets the number of properties of this object. +

+

+
Specified by:
getPropertiesCount in class ObjectType
+
+
+
+
+
+
+ +

+hasProperty

+
+public boolean hasProperty(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property whose name is given is present on the + object. +

+

+
Specified by:
hasProperty in class ObjectType
+
+
+
+
+
+
+ +

+hasOwnProperty

+
+public boolean hasOwnProperty(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property whose name is given is present directly on + the object. Returns false even if it is declared on a supertype. +

+

+
Overrides:
hasOwnProperty in class ObjectType
+
+
+
+
+
+
+ +

+isPropertyTypeDeclared

+
+public boolean isPropertyTypeDeclared(String property)
+
+
Description copied from class: ObjectType
+
Checks whether the property's type is declared. +

+

+
Specified by:
isPropertyTypeDeclared in class ObjectType
+
+
+
+
+
+
+ +

+isPropertyTypeInferred

+
+public boolean isPropertyTypeInferred(String property)
+
+
Description copied from class: ObjectType
+
Checks whether the property's type is inferred. +

+

+
Specified by:
isPropertyTypeInferred in class ObjectType
+
+
+
+
+
+
+ +

+isPropertyInExterns

+
+public boolean isPropertyInExterns(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property was defined in the externs. +

+

+
Overrides:
isPropertyInExterns in class ObjectType
+
+
+
+
+
+
+ +

+removeProperty

+
+public boolean removeProperty(String name)
+
+
Description copied from class: ObjectType
+
Removes the declared or inferred property from this ObjectType. +

+

+
Overrides:
removeProperty in class ObjectType
+
+
+
Parameters:
name - the property's name +
Returns:
true if the property was removed successfully. False if the + property did not exist, or could not be removed.
+
+
+
+ +

+getPropertyNode

+
+public Node getPropertyNode(String propertyName)
+
+
Description copied from class: ObjectType
+
Gets the node corresponding to the definition of the specified property. + This could be the node corresponding to declaration of the property or the + node corresponding to the first reference to this property, e.g., + "this.propertyName" in a constructor. Note this is mainly intended to be + an estimate of where in the source code a property is defined. Sometime + the returned node is not even part of the global AST but in the AST of the + JsDoc that defines a type. +

+

+
Overrides:
getPropertyNode in class ObjectType
+
+
+
Parameters:
propertyName - the name of the property +
Returns:
the Node corresponding to the property or null.
+
+
+
+ +

+getOwnPropertyJSDocInfo

+
+public JSDocInfo getOwnPropertyJSDocInfo(String propertyName)
+
+
Description copied from class: ObjectType
+
Gets the docInfo on the specified property on this type. This should not + be done implemented recursively, as you generally need to know exactly on + which type in the prototype chain the JSDocInfo exists. +

+

+
Overrides:
getOwnPropertyJSDocInfo in class ObjectType
+
+
+
+
+
+
+ +

+setPropertyJSDocInfo

+
+public void setPropertyJSDocInfo(String propertyName,
+                                 JSDocInfo info)
+
+
Description copied from class: ObjectType
+
Sets the docInfo for the specified property from the + JSDocInfo on its definition. +

+

+
Overrides:
setPropertyJSDocInfo in class ObjectType
+
+
+
info - JSDocInfo for the property definition. May be + null.
+
+
+
+ +

+matchesNumberContext

+
+public boolean matchesNumberContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator. +

+

+
Overrides:
matchesNumberContext in class JSType
+
+
+
+
+
+
+ +

+matchesStringContext

+
+public boolean matchesStringContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator. + + All types have at least the potential for converting to String. + When we add externally defined types, such as a browser OM, we may choose + to add types that do not automatically convert to String. +

+

+
Overrides:
matchesStringContext in class JSType
+
+
+
+
+
+
+ +

+unboxesTo

+
+public JSType unboxesTo()
+
+
Description copied from class: JSType
+
Gets the type to which this type unboxes. +

+

+
Overrides:
unboxesTo in class JSType
+
+
+ +
Returns:
the unboxed type or null if this type does not unbox.
+
+
+
+ +

+matchesObjectContext

+
+public boolean matchesObjectContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement. + + Most types we will encounter, except notably null, have at least + the potential for converting to Object. Host defined objects can + get peculiar. +

+

+
Overrides:
matchesObjectContext in class JSType
+
+
+
+
+
+
+ +

+getConstructor

+
+public FunctionType getConstructor()
+
+
Description copied from class: ObjectType
+
Gets this object's constructor. +

+

+
Specified by:
getConstructor in class ObjectType
+
+
+ +
Returns:
this object's constructor or null if it is a native + object (constructed natively v.s. by instantiation of a function)
+
+
+
+ +

+getImplicitPrototype

+
+public ObjectType getImplicitPrototype()
+
+
Description copied from class: ObjectType
+
Gets the implicit prototype (a.k.a. the [[Prototype]] property). +

+

+
Specified by:
getImplicitPrototype in class ObjectType
+
+
+
+
+
+
+ +

+getReferenceName

+
+public String getReferenceName()
+
+
Description copied from class: ObjectType
+
Gets the reference name for this object. This includes named types + like constructors, prototypes, and enums. It notably does not include + literal types like strings and booleans and structural types. +

+

+
Specified by:
getReferenceName in class ObjectType
+
+
+ +
Returns:
the object's name or null if this is an anonymous + object
+
+
+
+ +

+hasReferenceName

+
+public boolean hasReferenceName()
+
+
Description copied from class: ObjectType
+
Returns true if the object is named. +

+

+
Overrides:
hasReferenceName in class ObjectType
+
+
+ +
Returns:
true if the object is named, false if it is anonymous
+
+
+
+ +

+isNativeObjectType

+
+public boolean isNativeObjectType()
+
+
Whether this is a built-in object. +

+

+
Overrides:
isNativeObjectType in class ObjectType
+
+
+
+
+
+
+ +

+getOwnerFunction

+
+public FunctionType getOwnerFunction()
+
+
Description copied from class: ObjectType
+
Gets the owner of this if it's a function prototype. +

+

+
Overrides:
getOwnerFunction in class ObjectType
+
+
+
+
+
+
+ +

+getCtorImplementedInterfaces

+
+public Iterable<ObjectType> getCtorImplementedInterfaces()
+
+
Description copied from class: ObjectType
+
Gets the interfaces implemented by the ctor associated with this type. + Intended to be overridden by subclasses. +

+

+
Overrides:
getCtorImplementedInterfaces in class ObjectType
+
+
+
+
+
+
+ +

+getCtorExtendedInterfaces

+
+public Iterable<ObjectType> getCtorExtendedInterfaces()
+
+
Description copied from class: ObjectType
+
Gets the interfaces extended by the interface associated with this type. + Intended to be overriden by subclasses. +

+

+
Overrides:
getCtorExtendedInterfaces in class ObjectType
+
+
+
+
+
+
+ +

+matchConstraint

+
+public void matchConstraint(ObjectType constraintObj)
+
+
Description copied from class: JSType
+
Modify this type so that it matches the specified type. + + This is useful for reverse type-inference, where we want to + infer that an object literal matches its contraint (much like + how the java compiler does reverse-inference to figure out generics). +

+

+
Overrides:
matchConstraint in class JSType
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/JSType.TypePair.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/JSType.TypePair.html new file mode 100644 index 0000000..c581220 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/JSType.TypePair.html @@ -0,0 +1,295 @@ + + + + + +JSType.TypePair (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class JSType.TypePair

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType.TypePair
+
+
+
Enclosing class:
JSType
+
+
+
+
public static class JSType.TypePair
extends Object
+ + +

+


+ +

+ + + + + + + + + + + + + + + +
+Field Summary
+ JSTypetypeA + +
+           
+ JSTypetypeB + +
+           
+  + + + + + + + + + + +
+Constructor Summary
JSType.TypePair(JSType typeA, + JSType typeB) + +
+           
+  + + + + + + + +
+Method Summary
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+typeA

+
+public final JSType typeA
+
+
+
+
+
+ +

+typeB

+
+public final JSType typeB
+
+
+
+
+ + + + + + + + +
+Constructor Detail
+ +

+JSType.TypePair

+
+public JSType.TypePair(JSType typeA,
+                       JSType typeB)
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/JSType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/JSType.html new file mode 100644 index 0000000..bf404a0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/JSType.html @@ -0,0 +1,2624 @@ + + + + + +JSType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class JSType

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
Direct Known Subclasses:
AllType, BooleanType, NullType, NumberType, ObjectType, StringType, UnionType, VoidType
+
+
+
+
public abstract class JSType
extends Object
implements Serializable
+ + +

+Represents JavaScript value types.

+ + Types are split into two separate families: value types and object types. + + A special UnknownType exists to represent a wildcard type on which + no information can be gathered. In particular, it can assign to everyone, + is a subtype of everyone (and everyone is a subtype of it).

+ + If you remove the UnknownType, the set of types in the type system + forms a lattice with the isSubtype(com.google.javascript.rhino.jstype.JSType) relation defining the partial + order of types. All types are united at the top of the lattice by the + AllType and at the bottom by the NoType.

+

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classJSType.TypePair + +
+           
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Field Summary
+static StringEMPTY_TYPE_COMPONENT + +
+           
+static intENUMDECL + +
+           
+static StringNOT_A_CLASS + +
+           
+static StringNOT_A_TYPE + +
+           
+static intNOT_ENUMDECL + +
+           
+static StringUNKNOWN_NAME + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ JSTypeautobox() + +
+          Dereference a type for property access.
+ JSTypeautoboxesTo() + +
+          Gets the type to which this type auto-boxes.
+ booleancanAssignTo(JSType that) + +
+          Tests whether values of this type can be safely assigned + to values of that type.
+ booleancanBeCalled() + +
+          This predicate is used to test whether a given type can be used as the + 'function' in a function call.
+ booleancanTestForEqualityWith(JSType that) + +
+          Tests whether this and that are meaningfully + comparable.
+ booleancanTestForShallowEqualityWith(JSType that) + +
+          Tests whether this and that are meaningfully + comparable using shallow comparison.
+ voidclearResolved() + +
+          Clears the resolved field.
+ JSTypecollapseUnion() + +
+          Gets the least supertype of this that's not a union.
+ ObjectTypedereference() + +
+          Dereference a type for property access.
+ booleandiffersFrom(JSType that) + +
+          Whether this type is meaningfully different from that type.
+ booleanequals(Object jsType) + +
+           
+ JSTypefindPropertyType(String propertyName) + +
+          Coerces this type to an Object type, then gets the type of the property + whose name is given.
+ JSTypeforceResolve(ErrorReporter t, + StaticScope<JSType> scope) + +
+          Force this type to resolve, even if the registry is in a lazy + resolving mode.
+ StringgetDisplayName() + +
+          Returns a user meaningful label for the JSType instance.
+ JSTypegetGreatestSubtype(JSType that) + +
+          Gets the greatest subtype of this and that.
+ JSDocInfogetJSDocInfo() + +
+          Gets the docInfo for this type.
+ JSTypegetLeastSupertype(JSType that) + +
+          Gets the least supertype of this and that.
+abstract  BooleanLiteralSetgetPossibleToBooleanOutcomes() + +
+          Computes the set of possible outcomes of the ToBoolean predicate + for this type.
+ JSTypegetRestrictedTypeGivenToBooleanOutcome(boolean outcome) + +
+          Computes the restricted type of this type knowing that the + ToBoolean predicate has a specific value.
+ JSType.TypePairgetTypesUnderEquality(JSType that) + +
+          Computes the subset of this and that types if equality + is observed.
+ JSType.TypePairgetTypesUnderInequality(JSType that) + +
+          Computes the subset of this and that types if inequality + is observed.
+ JSType.TypePairgetTypesUnderShallowEquality(JSType that) + +
+          Computes the subset of this and that types under shallow + equality.
+ JSType.TypePairgetTypesUnderShallowInequality(JSType that) + +
+          Computes the subset of this and that types under + shallow inequality.
+ booleanhasDisplayName() + +
+           
+ inthashCode() + +
+           
+ booleanisAllType() + +
+           
+ booleanisArrayType() + +
+           
+ booleanisBooleanObjectType() + +
+           
+ booleanisBooleanValueType() + +
+           
+ booleanisCheckedUnknownType() + +
+           
+ booleanisConstructor() + +
+          Whether this type is a FunctionType that is a constructor or a + named type that points to such a type.
+ booleanisDateType() + +
+           
+ booleanisEmptyType() + +
+           
+ booleanisEnumElementType() + +
+           
+ booleanisEnumType() + +
+           
+static booleanisEquivalent(JSType typeA, + JSType typeB) + +
+           
+ booleanisEquivalentTo(JSType jsType) + +
+          Checks if two types are equivalent.
+ booleanisFunctionPrototypeType() + +
+          Whether this is the prototype of a function.
+ booleanisFunctionType() + +
+          Returns true if toMaybeFunctionType returns a non-null FunctionType.
+ booleanisGlobalThisType() + +
+          Returns true if this is a global this type.
+ booleanisInstanceType() + +
+          Whether this type is an Instance object of some constructor.
+ booleanisInterface() + +
+          Whether this type is a FunctionType that is an interface or a named + type that points to such a type.
+ booleanisNominalConstructor() + +
+          Whether this type is the original constructor of a nominal type.
+ booleanisNominalType() + +
+          Whether this type is a nominal type (a named instance object or + a named enum).
+ booleanisNoObjectType() + +
+           
+ booleanisNoResolvedType() + +
+           
+ booleanisNoType() + +
+           
+ booleanisNullable() + +
+          Tests whether this type is nullable.
+ booleanisNullType() + +
+           
+ booleanisNumber() + +
+          Tests whether the type is a number (value or Object).
+ booleanisNumberObjectType() + +
+           
+ booleanisNumberValueType() + +
+           
+ booleanisObject() + +
+          Tests whether this type is an Object, or any subtype thereof.
+ booleanisOrdinaryFunction() + +
+          Whether this type is a FunctionType that is an ordinary function or + a named type that points to such a type.
+ booleanisRecordType() + +
+           
+ booleanisRegexpType() + +
+           
+ booleanisResolved() + +
+          Whether the type has been resolved.
+ booleanisString() + +
+          Tests whether the type is a string (value or Object).
+ booleanisStringObjectType() + +
+           
+ booleanisStringValueType() + +
+           
+ booleanisSubtype(JSType that) + +
+          Checks whether this is a subtype of that.
+ booleanisTemplateType() + +
+           
+ booleanisUnionType() + +
+           
+ booleanisUnknownType() + +
+           
+ booleanisVoidType() + +
+           
+ voidmatchConstraint(ObjectType contraint) + +
+          Modify this type so that it matches the specified type.
+ booleanmatchesInt32Context() + +
+          This predicate is used to test whether a given type can appear in a + 'Int32' context.
+ booleanmatchesNumberContext() + +
+          This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator.
+ booleanmatchesObjectContext() + +
+          This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement.
+ booleanmatchesStringContext() + +
+          This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator.
+ booleanmatchesUint32Context() + +
+          This predicate is used to test whether a given type can appear in a + 'Uint32' context.
+ JSTyperesolve(ErrorReporter t, + StaticScope<JSType> scope) + +
+          Resolve this type in the given scope.
+ JSTyperestrictByNotNullOrUndefined() + +
+          If this is a union type, returns a union type that does not include + the null or undefined type.
+ booleansetValidator(com.google.common.base.Predicate<JSType> validator) + +
+          Certain types have constraints on them at resolution-time.
+ TernaryValuetestForEquality(JSType that) + +
+          Compares this and that.
+ StringtoAnnotationString() + +
+          A string representation of this type, suitable for printing + in type annotations at code generation time.
+ StringtoDebugHashCodeString() + +
+          A hash code function for diagnosing complicated issues + around type-identity.
+ EnumElementTypetoMaybeEnumElementType() + +
+          Downcasts this to an EnumElementType, or returns null if this is not an EnumElementType.
+ EnumTypetoMaybeEnumType() + +
+          Downcasts this to an EnumType, or returns null if this is not an EnumType.
+ FunctionTypetoMaybeFunctionType() + +
+          Downcasts this to a FunctionType, or returns null if this is not + a function.
+static FunctionTypetoMaybeFunctionType(JSType type) + +
+          Null-safe version of toMaybeFunctionType().
+ UnionTypetoMaybeUnionType() + +
+          Downcasts this to a UnionType, or returns null if this is not a UnionType.
+ ObjectTypetoObjectType() + +
+          Casts this to an ObjectType, or returns null if this is not an ObjectType.
+ StringtoString() + +
+          A string representation of this type, suitable for printing + in warnings.
+ JSTypeunboxesTo() + +
+          Gets the type to which this type unboxes.
+abstract + + + + +
+<T> T
+
visit(Visitor<T> visitor) + +
+          Visit this type with the given visitor.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+UNKNOWN_NAME

+
+public static final String UNKNOWN_NAME
+
+
+
See Also:
Constant Field Values
+
+
+ +

+NOT_A_CLASS

+
+public static final String NOT_A_CLASS
+
+
+
See Also:
Constant Field Values
+
+
+ +

+NOT_A_TYPE

+
+public static final String NOT_A_TYPE
+
+
+
See Also:
Constant Field Values
+
+
+ +

+EMPTY_TYPE_COMPONENT

+
+public static final String EMPTY_TYPE_COMPONENT
+
+
+
See Also:
Constant Field Values
+
+
+ +

+ENUMDECL

+
+public static final int ENUMDECL
+
+
+
See Also:
Constant Field Values
+
+
+ +

+NOT_ENUMDECL

+
+public static final int NOT_ENUMDECL
+
+
+
See Also:
Constant Field Values
+
+ + + + + + + + +
+Method Detail
+ +

+getJSDocInfo

+
+public JSDocInfo getJSDocInfo()
+
+
Gets the docInfo for this type. By default, documentation cannot be + attached to arbitrary types. This must be overridden for + programmer-defined types. +

+

+
+
+
+
+
+
+
+ +

+getDisplayName

+
+public String getDisplayName()
+
+
Returns a user meaningful label for the JSType instance. For example, + Functions and Enums will return their declaration name (if they have one). + Some types will not have a meaningful display name. Calls to + hasDisplayName() will return true IFF getDisplayName() will return null + or a zero length string. +

+

+
+
+
+ +
Returns:
the display name of the type, or null if one is not available
+
+
+
+ +

+hasDisplayName

+
+public boolean hasDisplayName()
+
+
+
+
+
+ +
Returns:
true if the JSType has a user meaningful label.
+
+
+
+ +

+isNoType

+
+public boolean isNoType()
+
+
+
+
+
+
+
+
+
+ +

+isNoResolvedType

+
+public boolean isNoResolvedType()
+
+
+
+
+
+
+
+
+
+ +

+isNoObjectType

+
+public boolean isNoObjectType()
+
+
+
+
+
+
+
+
+
+ +

+isEmptyType

+
+public final boolean isEmptyType()
+
+
+
+
+
+
+
+
+
+ +

+isNumberObjectType

+
+public boolean isNumberObjectType()
+
+
+
+
+
+
+
+
+
+ +

+isNumberValueType

+
+public boolean isNumberValueType()
+
+
+
+
+
+
+
+
+
+ +

+isFunctionPrototypeType

+
+public boolean isFunctionPrototypeType()
+
+
Whether this is the prototype of a function. +

+

+
+
+
+
+
+
+
+ +

+isStringObjectType

+
+public boolean isStringObjectType()
+
+
+
+
+
+
+
+
+
+ +

+isStringValueType

+
+public boolean isStringValueType()
+
+
+
+
+
+
+
+
+
+ +

+isString

+
+public final boolean isString()
+
+
Tests whether the type is a string (value or Object). +

+

+
+
+
+ +
Returns:
this &lt;: (String, string)
+
+
+
+ +

+isNumber

+
+public final boolean isNumber()
+
+
Tests whether the type is a number (value or Object). +

+

+
+
+
+ +
Returns:
this &lt;: (Number, number)
+
+
+
+ +

+isArrayType

+
+public boolean isArrayType()
+
+
+
+
+
+
+
+
+
+ +

+isBooleanObjectType

+
+public boolean isBooleanObjectType()
+
+
+
+
+
+
+
+
+
+ +

+isBooleanValueType

+
+public boolean isBooleanValueType()
+
+
+
+
+
+
+
+
+
+ +

+isRegexpType

+
+public boolean isRegexpType()
+
+
+
+
+
+
+
+
+
+ +

+isDateType

+
+public boolean isDateType()
+
+
+
+
+
+
+
+
+
+ +

+isNullType

+
+public boolean isNullType()
+
+
+
+
+
+
+
+
+
+ +

+isVoidType

+
+public boolean isVoidType()
+
+
+
+
+
+
+
+
+
+ +

+isAllType

+
+public boolean isAllType()
+
+
+
+
+
+
+
+
+
+ +

+isUnknownType

+
+public boolean isUnknownType()
+
+
+
+
+
+
+
+
+
+ +

+isCheckedUnknownType

+
+public boolean isCheckedUnknownType()
+
+
+
+
+
+
+
+
+
+ +

+isUnionType

+
+public final boolean isUnionType()
+
+
+
+
+
+
+
+
+
+ +

+toMaybeUnionType

+
+public UnionType toMaybeUnionType()
+
+
Downcasts this to a UnionType, or returns null if this is not a UnionType. + + Named in honor of Haskell's Maybe type constructor. +

+

+
+
+
+
+
+
+
+ +

+isGlobalThisType

+
+public final boolean isGlobalThisType()
+
+
Returns true if this is a global this type. +

+

+
+
+
+
+
+
+
+ +

+isFunctionType

+
+public final boolean isFunctionType()
+
+
Returns true if toMaybeFunctionType returns a non-null FunctionType. +

+

+
+
+
+
+
+
+
+ +

+toMaybeFunctionType

+
+public FunctionType toMaybeFunctionType()
+
+
Downcasts this to a FunctionType, or returns null if this is not + a function. + + For the purposes of this function, we define a MaybeFunctionType as any + type in the sub-lattice + { x | LEAST_FUNCTION_TYPE <= x <= GREATEST_FUNCTION_TYPE } + This definition excludes bottom types like NoType and NoObjectType. + + This definition is somewhat arbitrary and axiomatic, but this is the + definition that makes the most sense for the most callers. +

+

+
+
+
+
+
+
+
+ +

+toMaybeFunctionType

+
+public static FunctionType toMaybeFunctionType(JSType type)
+
+
Null-safe version of toMaybeFunctionType(). +

+

+
+
+
+
+
+
+
+ +

+isEnumElementType

+
+public final boolean isEnumElementType()
+
+
+
+
+
+
+
+
+
+ +

+toMaybeEnumElementType

+
+public EnumElementType toMaybeEnumElementType()
+
+
Downcasts this to an EnumElementType, or returns null if this is not an EnumElementType. +

+

+
+
+
+
+
+
+
+ +

+isEnumType

+
+public boolean isEnumType()
+
+
+
+
+
+
+
+
+
+ +

+toMaybeEnumType

+
+public EnumType toMaybeEnumType()
+
+
Downcasts this to an EnumType, or returns null if this is not an EnumType. +

+

+
+
+
+
+
+
+
+ +

+isRecordType

+
+public boolean isRecordType()
+
+
+
+
+
+
+
+
+
+ +

+isTemplateType

+
+public boolean isTemplateType()
+
+
+
+
+
+
+
+
+
+ +

+isObject

+
+public boolean isObject()
+
+
Tests whether this type is an Object, or any subtype thereof. +

+

+
+
+
+ +
Returns:
this &lt;: Object
+
+
+
+ +

+isConstructor

+
+public boolean isConstructor()
+
+
Whether this type is a FunctionType that is a constructor or a + named type that points to such a type. +

+

+
+
+
+
+
+
+
+ +

+isNominalType

+
+public boolean isNominalType()
+
+
Whether this type is a nominal type (a named instance object or + a named enum). +

+

+
+
+
+
+
+
+
+ +

+isNominalConstructor

+
+public final boolean isNominalConstructor()
+
+
Whether this type is the original constructor of a nominal type. + Does not include structural constructors. +

+

+
+
+
+
+
+
+
+ +

+isInstanceType

+
+public boolean isInstanceType()
+
+
Whether this type is an Instance object of some constructor. + Does not necessarily mean this is an InstanceObjectType. +

+

+
+
+
+
+
+
+
+ +

+isInterface

+
+public boolean isInterface()
+
+
Whether this type is a FunctionType that is an interface or a named + type that points to such a type. +

+

+
+
+
+
+
+
+
+ +

+isOrdinaryFunction

+
+public boolean isOrdinaryFunction()
+
+
Whether this type is a FunctionType that is an ordinary function or + a named type that points to such a type. +

+

+
+
+
+
+
+
+
+ +

+isEquivalentTo

+
+public boolean isEquivalentTo(JSType jsType)
+
+
Checks if two types are equivalent. +

+

+
+
+
+
+
+
+
+ +

+isEquivalent

+
+public static boolean isEquivalent(JSType typeA,
+                                   JSType typeB)
+
+
+
+
+
+
+
+
+
+ +

+equals

+
+public boolean equals(Object jsType)
+
+
+
Overrides:
equals in class Object
+
+
+
+
+
+
+ +

+hashCode

+
+public int hashCode()
+
+
+
Overrides:
hashCode in class Object
+
+
+
+
+
+
+ +

+matchesInt32Context

+
+public final boolean matchesInt32Context()
+
+
This predicate is used to test whether a given type can appear in a + 'Int32' context. This context includes, for example, the operands of a + bitwise or operator. Since we do not currently support integer types, + this is a synonym for Number. +

+

+
+
+
+
+
+
+
+ +

+matchesUint32Context

+
+public final boolean matchesUint32Context()
+
+
This predicate is used to test whether a given type can appear in a + 'Uint32' context. This context includes the right-hand operand of a shift + operator. +

+

+
+
+
+
+
+
+
+ +

+matchesNumberContext

+
+public boolean matchesNumberContext()
+
+
This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator. +

+

+
+
+
+
+
+
+
+ +

+matchesStringContext

+
+public boolean matchesStringContext()
+
+
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator. + + All types have at least the potential for converting to String. + When we add externally defined types, such as a browser OM, we may choose + to add types that do not automatically convert to String. +

+

+
+
+
+
+
+
+
+ +

+matchesObjectContext

+
+public boolean matchesObjectContext()
+
+
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement. + + Most types we will encounter, except notably null, have at least + the potential for converting to Object. Host defined objects can + get peculiar. +

+

+
+
+
+
+
+
+
+ +

+findPropertyType

+
+public JSType findPropertyType(String propertyName)
+
+
Coerces this type to an Object type, then gets the type of the property + whose name is given. + + Unlike ObjectType.getPropertyType(java.lang.String), returns null if the property + is not found. +

+

+
+
+
+ +
Returns:
The property's type. null if the current type cannot + have properties, or if the type is not found.
+
+
+
+ +

+canBeCalled

+
+public boolean canBeCalled()
+
+
This predicate is used to test whether a given type can be used as the + 'function' in a function call. +

+

+
+
+
+ +
Returns:
true if this type might be callable.
+
+
+
+ +

+canAssignTo

+
+public boolean canAssignTo(JSType that)
+
+
Tests whether values of this type can be safely assigned + to values of that type.

+ + The default implementation verifies that this is a subtype + of that.

+

+

+
+
+
+
+
+
+
+ +

+autoboxesTo

+
+public JSType autoboxesTo()
+
+
Gets the type to which this type auto-boxes. +

+

+
+
+
+ +
Returns:
the auto-boxed type or null if this type does not auto-box
+
+
+
+ +

+unboxesTo

+
+public JSType unboxesTo()
+
+
Gets the type to which this type unboxes. +

+

+
+
+
+ +
Returns:
the unboxed type or null if this type does not unbox.
+
+
+
+ +

+toObjectType

+
+public ObjectType toObjectType()
+
+
Casts this to an ObjectType, or returns null if this is not an ObjectType. + + Does not change the underlying JS type. If you want to simulate JS + autoboxing or dereferencing, you should use autoboxesTo() or dereference(). + Those methods may change the underlying JS type. +

+

+
+
+
+
+
+
+
+ +

+autobox

+
+public JSType autobox()
+
+
Dereference a type for property access. + + Autoboxes the type, and filters null/undefined, and returns the result. +

+

+
+
+
+
+
+
+
+ +

+dereference

+
+public final ObjectType dereference()
+
+
Dereference a type for property access. + + Autoboxes the type, filters null/undefined, and returns the result + iff it's an object. +

+

+
+
+
+
+
+
+
+ +

+canTestForEqualityWith

+
+public final boolean canTestForEqualityWith(JSType that)
+
+
Tests whether this and that are meaningfully + comparable. By meaningfully, we mean compatible types that do not lead + to step 22 of the definition of the Abstract Equality Comparison + Algorithm (11.9.3, page 55–56) of the ECMA-262 specification.

+

+

+
+
+
+
+
+
+
+ +

+testForEquality

+
+public TernaryValue testForEquality(JSType that)
+
+
Compares this and that. +

+

+
+
+
+ +
Returns:
    +
  • TernaryValue.TRUE if the comparison of values of + this type and that always succeed (such as + undefined compared to null)
  • +
  • TernaryValue.FALSE if the comparison of values of + this type and that always fails (such as + undefined compared to number)
  • +
  • TernaryValue.UNKNOWN if the comparison can succeed or + fail depending on the concrete values
  • +
+
+
+
+ +

+canTestForShallowEqualityWith

+
+public final boolean canTestForShallowEqualityWith(JSType that)
+
+
Tests whether this and that are meaningfully + comparable using shallow comparison. By meaningfully, we mean compatible + types that are not rejected by step 1 of the definition of the Strict + Equality Comparison Algorithm (11.9.6, page 56–57) of the + ECMA-262 specification.

+

+

+
+
+
+
+
+
+
+ +

+isNullable

+
+public boolean isNullable()
+
+
Tests whether this type is nullable. +

+

+
+
+
+
+
+
+
+ +

+collapseUnion

+
+public JSType collapseUnion()
+
+
Gets the least supertype of this that's not a union. +

+

+
+
+
+
+
+
+
+ +

+getLeastSupertype

+
+public JSType getLeastSupertype(JSType that)
+
+
Gets the least supertype of this and that. + The least supertype is the join (∨) or supremum of both types in the + type lattice.

+ Examples: +

    +
  • number &#8744; * = *
  • +
  • number &#8744; Object = (number, Object)
  • +
  • Number &#8744; Object = Object
  • +
+

+

+
+
+
+ +
Returns:
this &#8744; that
+
+
+
+ +

+getGreatestSubtype

+
+public JSType getGreatestSubtype(JSType that)
+
+
Gets the greatest subtype of this and that. + The greatest subtype is the meet (∧) or infimum of both types in the + type lattice.

+ Examples +

    +
  • Number &#8743; Any = Any
  • +
  • number &#8743; Object = Any
  • +
  • Number &#8743; Object = Number
  • +
+

+

+
+
+
+ +
Returns:
this &#8744; that
+
+
+
+ +

+getRestrictedTypeGivenToBooleanOutcome

+
+public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome)
+
+
Computes the restricted type of this type knowing that the + ToBoolean predicate has a specific value. For more information + about the ToBoolean predicate, see + getPossibleToBooleanOutcomes(). +

+

+
+
+
+
Parameters:
outcome - the value of the ToBoolean predicate +
Returns:
the restricted type, or the Any Type if the underlying type could + not have yielded this ToBoolean value + + TODO(user): Move this method to the SemanticRAI and use the visit + method of types to get the restricted type.
+
+
+
+ +

+getPossibleToBooleanOutcomes

+
+public abstract BooleanLiteralSet getPossibleToBooleanOutcomes()
+
+
Computes the set of possible outcomes of the ToBoolean predicate + for this type. The ToBoolean predicate is defined by the ECMA-262 + standard, 3rd edition. Its behavior for simple types can be + summarized by the following table: + + + + + + + + +
typeresult
undefined{false}
null{false}
boolean{true, false}
number{true, false}
string{true, false}
Object{true}
+

+

+
+
+
+ +
Returns:
the set of boolean literals for this type
+
+
+
+ +

+getTypesUnderEquality

+
+public JSType.TypePair getTypesUnderEquality(JSType that)
+
+
Computes the subset of this and that types if equality + is observed. If a value v1 of type null is equal to a value + v2 of type (undefined,number), we can infer that the + type of v1 is null and the type of v2 is + undefined. +

+

+
+
+
+ +
Returns:
a pair containing the restricted type of this as the first + component and the restricted type of that as the second + element. The returned pair is never null even though its + components may be null
+
+
+
+ +

+getTypesUnderInequality

+
+public JSType.TypePair getTypesUnderInequality(JSType that)
+
+
Computes the subset of this and that types if inequality + is observed. If a value v1 of type number is not equal to a + value v2 of type (undefined,number), we can infer that the + type of v1 is number and the type of v2 is + number as well. +

+

+
+
+
+ +
Returns:
a pair containing the restricted type of this as the first + component and the restricted type of that as the second + element. The returned pair is never null even though its + components may be null
+
+
+
+ +

+getTypesUnderShallowEquality

+
+public JSType.TypePair getTypesUnderShallowEquality(JSType that)
+
+
Computes the subset of this and that types under shallow + equality. +

+

+
+
+
+ +
Returns:
a pair containing the restricted type of this as the first + component and the restricted type of that as the second + element. The returned pair is never null even though its + components may be null.
+
+
+
+ +

+getTypesUnderShallowInequality

+
+public JSType.TypePair getTypesUnderShallowInequality(JSType that)
+
+
Computes the subset of this and that types under + shallow inequality. +

+

+
+
+
+ +
Returns:
A pair containing the restricted type of this as the first + component and the restricted type of that as the second + element. The returned pair is never null even though its + components may be null
+
+
+
+ +

+restrictByNotNullOrUndefined

+
+public JSType restrictByNotNullOrUndefined()
+
+
If this is a union type, returns a union type that does not include + the null or undefined type. +

+

+
+
+
+
+
+
+
+ +

+isSubtype

+
+public boolean isSubtype(JSType that)
+
+
Checks whether this is a subtype of that.

+ + Subtyping rules: +

    +
  • (unknown) — every type is a subtype of the Unknown type.
  • +
  • (no) — the No type is a subtype of every type.
  • +
  • (no-object) — the NoObject type is a subtype of every object + type (i.e. subtypes of the Object type).
  • +
  • (ref) — a type is a subtype of itself.
  • +
  • (union-l) — A union type is a subtype of a type U if all the + union type's constituents are a subtype of U. Formally
    + (T<sub>1</sub>, &hellip;, T<sub>n</sub>) &lt;: U if and only + T<sub>k</sub> &lt;: U for all k &isin; 1..n.
  • +
  • (union-r) — A type U is a subtype of a union type if it is a + subtype of one of the union type's constituents. Formally
    + U &lt;: (T<sub>1</sub>, &hellip;, T<sub>n</sub>) if and only + if U &lt;: T<sub>k</sub> for some index k.
  • +
  • (objects) — an Object O<sub>1</sub> is a subtype + of an object O<sub>2</sub> if it has more properties + than O<sub>2</sub> and all common properties are + pairwise subtypes.
  • +
+

+

+
+
+
+ +
Returns:
this &lt;: that
+
+
+
+ +

+differsFrom

+
+public boolean differsFrom(JSType that)
+
+
Whether this type is meaningfully different from that type. + This is a trickier check than pure equality, because it has to properly + handle unknown types. +

+

+
+
+
+
See Also:
Unknown + unknowns
+
+
+
+ +

+visit

+
+public abstract <T> T visit(Visitor<T> visitor)
+
+
Visit this type with the given visitor. +

+

+
+
+
+ +
Returns:
the value returned by the visitor
See Also:
Visitor
+
+
+
+ +

+forceResolve

+
+public final JSType forceResolve(ErrorReporter t,
+                                 StaticScope<JSType> scope)
+
+
Force this type to resolve, even if the registry is in a lazy + resolving mode. +

+

+
+
+
+
See Also:
resolve(com.google.javascript.rhino.ErrorReporter, com.google.javascript.rhino.jstype.StaticScope)
+
+
+
+ +

+resolve

+
+public final JSType resolve(ErrorReporter t,
+                            StaticScope<JSType> scope)
+
+
Resolve this type in the given scope. + + The returned value must be equal to this, as defined by + isEquivalentTo(com.google.javascript.rhino.jstype.JSType). It may or may not be the same object. This method + may modify the internal state of this, as long as it does + so in a way that preserves Object equality. + + For efficiency, we should only resolve a type once per compilation job. + For incremental compilations, one compilation job may need the + artifacts from a previous generation, so we will eventually need + a generational flag instead of a boolean one. +

+

+
+
+
+
+
+
+
+ +

+isResolved

+
+public final boolean isResolved()
+
+
Whether the type has been resolved. +

+

+
+
+
+
+
+
+
+ +

+clearResolved

+
+public final void clearResolved()
+
+
Clears the resolved field. +

+

+
+
+
+
+
+
+
+ +

+setValidator

+
+public boolean setValidator(com.google.common.base.Predicate<JSType> validator)
+
+
Certain types have constraints on them at resolution-time. + For example, a type in an @extends annotation must be an + object. Clients should inject a validator that emits a warning + if the type does not validate, and return false. +

+

+
+
+
+
+
+
+
+ +

+toString

+
+public String toString()
+
+
A string representation of this type, suitable for printing + in warnings. +

+

+
Overrides:
toString in class Object
+
+
+
+
+
+
+ +

+toDebugHashCodeString

+
+public String toDebugHashCodeString()
+
+
A hash code function for diagnosing complicated issues + around type-identity. +

+

+
+
+
+
+
+
+
+ +

+toAnnotationString

+
+public final String toAnnotationString()
+
+
A string representation of this type, suitable for printing + in type annotations at code generation time. +

+

+
+
+
+
+
+
+
+ +

+matchConstraint

+
+public void matchConstraint(ObjectType contraint)
+
+
Modify this type so that it matches the specified type. + + This is useful for reverse type-inference, where we want to + infer that an object literal matches its contraint (much like + how the java compiler does reverse-inference to figure out generics). +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/JSTypeNative.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/JSTypeNative.html new file mode 100644 index 0000000..746754f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/JSTypeNative.html @@ -0,0 +1,1218 @@ + + + + + +JSTypeNative (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Enum JSTypeNative

+
+java.lang.Object
+  extended by java.lang.Enum<JSTypeNative>
+      extended by com.google.javascript.rhino.jstype.JSTypeNative
+
+
+
All Implemented Interfaces:
Serializable, Comparable<JSTypeNative>
+
+
+
+
public enum JSTypeNative
extends Enum<JSTypeNative>
+ + +

+Constants corresponding to types that are built into a JavaScript engine + and other types that occur very often in the type system. See + JSTypeRegistry.getNativeType(JSTypeNative). +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Enum Constant Summary
ALL_TYPE + +
+           
ARRAY_FUNCTION_TYPE + +
+           
ARRAY_TYPE + +
+           
BOOLEAN_OBJECT_FUNCTION_TYPE + +
+           
BOOLEAN_OBJECT_TYPE + +
+           
BOOLEAN_TYPE + +
+           
CHECKED_UNKNOWN_TYPE + +
+          A checked unknown type is a type that we know something about, + but we're not really sure what we know about it.
DATE_FUNCTION_TYPE + +
+           
DATE_TYPE + +
+           
ERROR_FUNCTION_TYPE + +
+           
ERROR_TYPE + +
+           
EVAL_ERROR_FUNCTION_TYPE + +
+           
EVAL_ERROR_TYPE + +
+           
FUNCTION_FUNCTION_TYPE + +
+           
FUNCTION_INSTANCE_TYPE + +
+           
FUNCTION_PROTOTYPE + +
+           
GLOBAL_THIS + +
+           
GREATEST_FUNCTION_TYPE + +
+           
LEAST_FUNCTION_TYPE + +
+           
NO_OBJECT_TYPE + +
+           
NO_RESOLVED_TYPE + +
+           
NO_TYPE + +
+           
NULL_TYPE + +
+           
NUMBER_OBJECT_FUNCTION_TYPE + +
+           
NUMBER_OBJECT_TYPE + +
+           
NUMBER_STRING + +
+          (number,string)
NUMBER_STRING_BOOLEAN + +
+          (number,string,boolean)
NUMBER_TYPE + +
+           
NUMBER_VALUE_OR_OBJECT_TYPE + +
+           
OBJECT_FUNCTION_TYPE + +
+           
OBJECT_NUMBER_STRING + +
+          (Object,number,string)
OBJECT_NUMBER_STRING_BOOLEAN + +
+          (Object,number,string,boolean)
OBJECT_PROTOTYPE + +
+           
OBJECT_TYPE + +
+           
RANGE_ERROR_FUNCTION_TYPE + +
+           
RANGE_ERROR_TYPE + +
+           
REFERENCE_ERROR_FUNCTION_TYPE + +
+           
REFERENCE_ERROR_TYPE + +
+           
REGEXP_FUNCTION_TYPE + +
+           
REGEXP_TYPE + +
+           
STRING_OBJECT_FUNCTION_TYPE + +
+           
STRING_OBJECT_TYPE + +
+           
STRING_TYPE + +
+           
STRING_VALUE_OR_OBJECT_TYPE + +
+           
SYNTAX_ERROR_FUNCTION_TYPE + +
+           
SYNTAX_ERROR_TYPE + +
+           
TOP_LEVEL_PROTOTYPE + +
+           
TYPE_ERROR_FUNCTION_TYPE + +
+           
TYPE_ERROR_TYPE + +
+           
U2U_CONSTRUCTOR_TYPE + +
+           
U2U_FUNCTION_TYPE + +
+           
UNKNOWN_TYPE + +
+           
URI_ERROR_FUNCTION_TYPE + +
+           
URI_ERROR_TYPE + +
+           
VOID_TYPE + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static JSTypeNativevalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static JSTypeNative[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+ARRAY_TYPE

+
+public static final JSTypeNative ARRAY_TYPE
+
+
+
+
+
+ +

+ARRAY_FUNCTION_TYPE

+
+public static final JSTypeNative ARRAY_FUNCTION_TYPE
+
+
+
+
+
+ +

+BOOLEAN_TYPE

+
+public static final JSTypeNative BOOLEAN_TYPE
+
+
+
+
+
+ +

+BOOLEAN_OBJECT_TYPE

+
+public static final JSTypeNative BOOLEAN_OBJECT_TYPE
+
+
+
+
+
+ +

+BOOLEAN_OBJECT_FUNCTION_TYPE

+
+public static final JSTypeNative BOOLEAN_OBJECT_FUNCTION_TYPE
+
+
+
+
+
+ +

+CHECKED_UNKNOWN_TYPE

+
+public static final JSTypeNative CHECKED_UNKNOWN_TYPE
+
+
A checked unknown type is a type that we know something about, + but we're not really sure what we know about it. + + Examples of checked unknown types include: + + if (x) { // x is unknown + alert(x); // x is checked unknown + } + + + + /* @param {SomeForwardDeclaredType} x / + function f(x) { + // x is checked unknown. We know it's some type, but the type + // has not been included in this binary. + } + + + This is useful for missing property warnings, where we don't + want to emit warnings on things that have been checked. +

+

+
+
+
+ +

+DATE_TYPE

+
+public static final JSTypeNative DATE_TYPE
+
+
+
+
+
+ +

+DATE_FUNCTION_TYPE

+
+public static final JSTypeNative DATE_FUNCTION_TYPE
+
+
+
+
+
+ +

+ERROR_FUNCTION_TYPE

+
+public static final JSTypeNative ERROR_FUNCTION_TYPE
+
+
+
+
+
+ +

+ERROR_TYPE

+
+public static final JSTypeNative ERROR_TYPE
+
+
+
+
+
+ +

+EVAL_ERROR_FUNCTION_TYPE

+
+public static final JSTypeNative EVAL_ERROR_FUNCTION_TYPE
+
+
+
+
+
+ +

+EVAL_ERROR_TYPE

+
+public static final JSTypeNative EVAL_ERROR_TYPE
+
+
+
+
+
+ +

+FUNCTION_FUNCTION_TYPE

+
+public static final JSTypeNative FUNCTION_FUNCTION_TYPE
+
+
+
+
+
+ +

+FUNCTION_INSTANCE_TYPE

+
+public static final JSTypeNative FUNCTION_INSTANCE_TYPE
+
+
+
+
+
+ +

+FUNCTION_PROTOTYPE

+
+public static final JSTypeNative FUNCTION_PROTOTYPE
+
+
+
+
+
+ +

+NULL_TYPE

+
+public static final JSTypeNative NULL_TYPE
+
+
+
+
+
+ +

+NUMBER_TYPE

+
+public static final JSTypeNative NUMBER_TYPE
+
+
+
+
+
+ +

+NUMBER_OBJECT_TYPE

+
+public static final JSTypeNative NUMBER_OBJECT_TYPE
+
+
+
+
+
+ +

+NUMBER_OBJECT_FUNCTION_TYPE

+
+public static final JSTypeNative NUMBER_OBJECT_FUNCTION_TYPE
+
+
+
+
+
+ +

+OBJECT_TYPE

+
+public static final JSTypeNative OBJECT_TYPE
+
+
+
+
+
+ +

+OBJECT_FUNCTION_TYPE

+
+public static final JSTypeNative OBJECT_FUNCTION_TYPE
+
+
+
+
+
+ +

+OBJECT_PROTOTYPE

+
+public static final JSTypeNative OBJECT_PROTOTYPE
+
+
+
+
+
+ +

+RANGE_ERROR_FUNCTION_TYPE

+
+public static final JSTypeNative RANGE_ERROR_FUNCTION_TYPE
+
+
+
+
+
+ +

+RANGE_ERROR_TYPE

+
+public static final JSTypeNative RANGE_ERROR_TYPE
+
+
+
+
+
+ +

+REFERENCE_ERROR_FUNCTION_TYPE

+
+public static final JSTypeNative REFERENCE_ERROR_FUNCTION_TYPE
+
+
+
+
+
+ +

+REFERENCE_ERROR_TYPE

+
+public static final JSTypeNative REFERENCE_ERROR_TYPE
+
+
+
+
+
+ +

+REGEXP_TYPE

+
+public static final JSTypeNative REGEXP_TYPE
+
+
+
+
+
+ +

+REGEXP_FUNCTION_TYPE

+
+public static final JSTypeNative REGEXP_FUNCTION_TYPE
+
+
+
+
+
+ +

+STRING_OBJECT_TYPE

+
+public static final JSTypeNative STRING_OBJECT_TYPE
+
+
+
+
+
+ +

+STRING_OBJECT_FUNCTION_TYPE

+
+public static final JSTypeNative STRING_OBJECT_FUNCTION_TYPE
+
+
+
+
+
+ +

+STRING_TYPE

+
+public static final JSTypeNative STRING_TYPE
+
+
+
+
+
+ +

+SYNTAX_ERROR_FUNCTION_TYPE

+
+public static final JSTypeNative SYNTAX_ERROR_FUNCTION_TYPE
+
+
+
+
+
+ +

+SYNTAX_ERROR_TYPE

+
+public static final JSTypeNative SYNTAX_ERROR_TYPE
+
+
+
+
+
+ +

+TYPE_ERROR_FUNCTION_TYPE

+
+public static final JSTypeNative TYPE_ERROR_FUNCTION_TYPE
+
+
+
+
+
+ +

+TYPE_ERROR_TYPE

+
+public static final JSTypeNative TYPE_ERROR_TYPE
+
+
+
+
+
+ +

+UNKNOWN_TYPE

+
+public static final JSTypeNative UNKNOWN_TYPE
+
+
+
+
+
+ +

+URI_ERROR_FUNCTION_TYPE

+
+public static final JSTypeNative URI_ERROR_FUNCTION_TYPE
+
+
+
+
+
+ +

+URI_ERROR_TYPE

+
+public static final JSTypeNative URI_ERROR_TYPE
+
+
+
+
+
+ +

+VOID_TYPE

+
+public static final JSTypeNative VOID_TYPE
+
+
+
+
+
+ +

+TOP_LEVEL_PROTOTYPE

+
+public static final JSTypeNative TOP_LEVEL_PROTOTYPE
+
+
+
+
+
+ +

+STRING_VALUE_OR_OBJECT_TYPE

+
+public static final JSTypeNative STRING_VALUE_OR_OBJECT_TYPE
+
+
+
+
+
+ +

+NUMBER_VALUE_OR_OBJECT_TYPE

+
+public static final JSTypeNative NUMBER_VALUE_OR_OBJECT_TYPE
+
+
+
+
+
+ +

+ALL_TYPE

+
+public static final JSTypeNative ALL_TYPE
+
+
+
+
+
+ +

+NO_TYPE

+
+public static final JSTypeNative NO_TYPE
+
+
+
+
+
+ +

+NO_OBJECT_TYPE

+
+public static final JSTypeNative NO_OBJECT_TYPE
+
+
+
+
+
+ +

+NO_RESOLVED_TYPE

+
+public static final JSTypeNative NO_RESOLVED_TYPE
+
+
+
+
+
+ +

+GLOBAL_THIS

+
+public static final JSTypeNative GLOBAL_THIS
+
+
+
+
+
+ +

+U2U_CONSTRUCTOR_TYPE

+
+public static final JSTypeNative U2U_CONSTRUCTOR_TYPE
+
+
+
+
+
+ +

+U2U_FUNCTION_TYPE

+
+public static final JSTypeNative U2U_FUNCTION_TYPE
+
+
+
+
+
+ +

+LEAST_FUNCTION_TYPE

+
+public static final JSTypeNative LEAST_FUNCTION_TYPE
+
+
+
+
+
+ +

+GREATEST_FUNCTION_TYPE

+
+public static final JSTypeNative GREATEST_FUNCTION_TYPE
+
+
+
+
+
+ +

+OBJECT_NUMBER_STRING

+
+public static final JSTypeNative OBJECT_NUMBER_STRING
+
+
(Object,number,string) +

+

+
+
+
+ +

+OBJECT_NUMBER_STRING_BOOLEAN

+
+public static final JSTypeNative OBJECT_NUMBER_STRING_BOOLEAN
+
+
(Object,number,string,boolean) +

+

+
+
+
+ +

+NUMBER_STRING_BOOLEAN

+
+public static final JSTypeNative NUMBER_STRING_BOOLEAN
+
+
(number,string,boolean) +

+

+
+
+
+ +

+NUMBER_STRING

+
+public static final JSTypeNative NUMBER_STRING
+
+
(number,string) +

+

+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static JSTypeNative[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (JSTypeNative c : JSTypeNative.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static JSTypeNative valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/JSTypeRegistry.ResolveMode.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/JSTypeRegistry.ResolveMode.html new file mode 100644 index 0000000..f406099 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/JSTypeRegistry.ResolveMode.html @@ -0,0 +1,372 @@ + + + + + +JSTypeRegistry.ResolveMode (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Enum JSTypeRegistry.ResolveMode

+
+java.lang.Object
+  extended by java.lang.Enum<JSTypeRegistry.ResolveMode>
+      extended by com.google.javascript.rhino.jstype.JSTypeRegistry.ResolveMode
+
+
+
All Implemented Interfaces:
Serializable, Comparable<JSTypeRegistry.ResolveMode>
+
+
+
Enclosing class:
JSTypeRegistry
+
+
+
+
public static enum JSTypeRegistry.ResolveMode
extends Enum<JSTypeRegistry.ResolveMode>
+ + +

+The type registry has three modes, which control how type ASTs are + converted to types in JSTypeRegistry.createFromTypeNodes(com.google.javascript.rhino.Node, java.lang.String, com.google.javascript.rhino.jstype.StaticScope). +

+ +

+


+ +

+ + + + + + + + + + + + + + + + +
+Enum Constant Summary
IMMEDIATE + +
+          Expressions and type names are evaluated aggressively.
LAZY_EXPRESSIONS + +
+          Expressions are converted into Unknown blobs that can be + resolved into complex types.
LAZY_NAMES + +
+          Expressions are evaluated.
+  + + + + + + + + + + + + + + + +
+Method Summary
+static JSTypeRegistry.ResolveModevalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static JSTypeRegistry.ResolveMode[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+LAZY_EXPRESSIONS

+
+public static final JSTypeRegistry.ResolveMode LAZY_EXPRESSIONS
+
+
Expressions are converted into Unknown blobs that can be + resolved into complex types. +

+

+
+
+
+ +

+LAZY_NAMES

+
+public static final JSTypeRegistry.ResolveMode LAZY_NAMES
+
+
Expressions are evaluated. If any names in the expression point to + unknown types, then we create a proxy NamedType structure + until the type can be resolved. + + This is the legacy way of resolving ways, and may not exist in the + future. +

+

+
+
+
+ +

+IMMEDIATE

+
+public static final JSTypeRegistry.ResolveMode IMMEDIATE
+
+
Expressions and type names are evaluated aggressively. A warning + will be emitted if a type name fails to resolve to a real type. +

+

+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static JSTypeRegistry.ResolveMode[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (JSTypeRegistry.ResolveMode c : JSTypeRegistry.ResolveMode.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static JSTypeRegistry.ResolveMode valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/JSTypeRegistry.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/JSTypeRegistry.html new file mode 100644 index 0000000..7630354 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/JSTypeRegistry.html @@ -0,0 +1,1962 @@ + + + + + +JSTypeRegistry (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class JSTypeRegistry

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSTypeRegistry
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class JSTypeRegistry
extends Object
implements Serializable
+ + +

+The type registry is used to resolve named types. + +

This class is not thread-safe. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classJSTypeRegistry.ResolveMode + +
+          The type registry has three modes, which control how type ASTs are + converted to types in createFromTypeNodes(com.google.javascript.rhino.Node, java.lang.String, com.google.javascript.rhino.jstype.StaticScope).
+  + + + + + + + + + + + + + +
+Constructor Summary
JSTypeRegistry(ErrorReporter reporter) + +
+          Constructs a new type registry populated with the built-in types.
JSTypeRegistry(ErrorReporter reporter, + boolean tolerateUndefinedValues) + +
+          Constructs a new type registry populated with the built-in types.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleancanPropertyBeDefined(JSType type, + String propertyName) + +
+          Returns whether the given property can possibly be set on the given type.
+ voidclearNamedTypes() + +
+          Flushes out the current resolved and unresovled Named Types from + the type registry.
+ voidclearTemplateTypeName() + +
+          Clears the template type name.
+ ObjectTypecreateAnonymousObjectType() + +
+          Create an anonymous object type.
+ FunctionTypecreateConstructorType(JSType returnType, + boolean lastVarArgs, + JSType... parameterTypes) + +
+          Creates a function type which can act as a constructor.
+ FunctionTypecreateConstructorType(JSType returnType, + JSType... parameterTypes) + +
+          Creates a function type which can act as a constructor.
+ FunctionTypecreateConstructorType(String name, + Node source, + Node parameters, + JSType returnType) + +
+          Creates a constructor function type.
+ FunctionTypecreateConstructorTypeWithVarArgs(JSType returnType, + JSType... parameterTypes) + +
+          Creates a function type which can act as a constructor.
+ JSTypecreateDefaultObjectUnion(JSType type) + +
+          Creates a type representing nullable values of the given type.
+ EnumTypecreateEnumType(String name, + Node source, + JSType elementsType) + +
+          Creates an enum type.
+ JSTypecreateFromTypeNodes(Node n, + String sourceName, + StaticScope<JSType> scope) + +
+          Creates a JSType from the nodes representing a type.
+ FunctionTypecreateFunctionType(JSType returnType, + boolean lastVarArgs, + JSType... parameterTypes) + +
+          Creates a function type.
+ FunctionTypecreateFunctionType(JSType returnType, + JSType... parameterTypes) + +
+          Creates a function type.
+ FunctionTypecreateFunctionType(JSType returnType, + List<JSType> parameterTypes) + +
+          Creates a function type.
+ FunctionTypecreateFunctionType(JSType returnType, + Node parameters) + +
+           
+ JSTypecreateFunctionType(ObjectType instanceType, + JSType returnType, + List<JSType> parameterTypes) + +
+          Creates a function type in which this refers to an object instance.
+ FunctionTypecreateFunctionTypeWithNewReturnType(FunctionType existingFunctionType, + JSType returnType) + +
+          Creates a new function type based on an existing function type but + with a new return type.
+ FunctionTypecreateFunctionTypeWithNewThisType(FunctionType existingFunctionType, + ObjectType thisType) + +
+          Creates a new function type based on an existing function type but + with a new this type.
+ FunctionTypecreateFunctionTypeWithVarArgs(JSType returnType, + JSType... parameterTypes) + +
+          Creates a function type.
+ FunctionTypecreateFunctionTypeWithVarArgs(JSType returnType, + List<JSType> parameterTypes) + +
+          Creates a function type.
+ JSTypecreateFunctionTypeWithVarArgs(ObjectType instanceType, + JSType returnType, + List<JSType> parameterTypes) + +
+          Creates a function type in which this refers to an object instance.
+ FunctionTypecreateInterfaceType(String name, + Node source) + +
+          Creates an interface function type.
+ JSTypecreateNamedType(String reference, + String sourceName, + int lineno, + int charno) + +
+          Creates a named type.
+ JSTypecreateNullableType(JSType type) + +
+          Creates a type representing nullable values of the given type.
+ ObjectTypecreateObjectType(ObjectType implicitPrototype) + +
+          Create an object type.
+ ObjectTypecreateObjectType(String name, + Node n, + ObjectType implicitPrototype) + +
+          Create an object type.
+ JSTypecreateOptionalNullableType(JSType type) + +
+          Creates a nullabel and undefine-able value of the given type.
+ NodecreateOptionalParameters(JSType... parameterTypes) + +
+          Creates a tree hierarchy representing a typed parameter list in which + every parameter is optional.
+ JSTypecreateOptionalType(JSType type) + +
+          Creates a type representing optional values of the given type.
+ com.google.javascript.rhino.jstype.ParameterizedTypecreateParameterizedType(ObjectType objectType, + JSType parameterType) + +
+          Creates a parameterized type.
+ NodecreateParameters(JSType... parameterTypes) + +
+          Creates a tree hierarchy representing a typed argument list.
+ NodecreateParameters(List<JSType> parameterTypes) + +
+          Creates a tree hierarchy representing a typed argument list.
+ NodecreateParametersWithVarArgs(JSType... parameterTypes) + +
+          Creates a tree hierarchy representing a typed argument list.
+ NodecreateParametersWithVarArgs(List<JSType> parameterTypes) + +
+          Creates a tree hierarchy representing a typed argument list.
+ com.google.javascript.rhino.jstype.RecordTypecreateRecordType(Map<String,com.google.javascript.rhino.jstype.RecordTypeBuilder.RecordProperty> properties) + +
+          Creates a record type.
+ JSTypecreateUnionType(JSType... variants) + +
+          Creates a union type whose variants are the arguments.
+ JSTypecreateUnionType(JSTypeNative... variants) + +
+          Creates a union type whose variants are the builtin types specified + by the arguments.
+ booleandeclareType(String name, + JSType t) + +
+          Records declared global type names.
+ voidforwardDeclareType(String name) + +
+          Records a forward-declared type name.
+ Collection<FunctionType>getDirectImplementors(ObjectType interfaceInstance) + +
+          Returns a collection of types that directly implement interfaceInstance.
+ Iterable<ObjectType>getEachReferenceTypeWithProperty(String propertyName) + +
+          Returns each reference type that has a property propertyName + defined on it.
+ ErrorReportergetErrorReporter() + +
+           
+ JSTypegetGreatestSubtypeWithProperty(JSType type, + String propertyName) + +
+          Gets the greatest subtype of the type that has a property + propertyName defined on it.
+ FunctionTypegetNativeFunctionType(JSTypeNative typeId) + +
+           
+ ObjectTypegetNativeObjectType(JSTypeNative typeId) + +
+           
+ JSTypegetNativeType(JSTypeNative typeId) + +
+           
+ JSTypegetType(StaticScope<JSType> scope, + String jsTypeName, + String sourceName, + int lineno, + int charno) + +
+          Looks up a type by name.
+ JSTypegetType(String jsTypeName) + +
+          Looks up a type by name.
+ Iterable<JSType>getTypesWithProperty(String propertyName) + +
+          Returns each type that has a property propertyName defined on it.
+ booleanhasNamespace(String name) + +
+          Determines whether the given JS package exists.
+ voididentifyNonNullableName(String name) + +
+          Identifies the name of a typedef or enum before we actually declare it.
+ voidincrementGeneration() + +
+          Increments the current generation.
+ booleanisForwardDeclaredType(String name) + +
+          Whether this is a forward-declared type name.
+ voidoverwriteDeclaredType(String name, + JSType t) + +
+          Overrides a declared global type name.
+ voidregisterPropertyOnType(String propertyName, + JSType type) + +
+          Tells the type system that owner may have a property named + propertyName.
+ voidresetForTypeCheck() + +
+          Reset to run the TypeCheck pass.
+ booleanresetImplicitPrototype(JSType type, + ObjectType newImplicitProto) + +
+          Set the implicit prototype if it's possible to do so.
+ voidresolveTypesInScope(StaticScope<JSType> scope) + +
+          Resolve all the unresolved types in the given scope.
+ voidsetLastGeneration(boolean lastGeneration) + +
+          Sets whether this is the last generation.
+ voidsetResolveMode(JSTypeRegistry.ResolveMode mode) + +
+          Set the current resolving mode of the type registry.
+ voidsetTemplateTypeName(String name) + +
+          Sets the template type name.
+ booleanshouldTolerateUndefinedValues() + +
+           
+ voidunregisterPropertyOnType(String propertyName, + JSType type) + +
+          Removes the index's reference to a property on the given type (if it is + currently registered).
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+JSTypeRegistry

+
+public JSTypeRegistry(ErrorReporter reporter)
+
+
Constructs a new type registry populated with the built-in types. +

+

+
+ +

+JSTypeRegistry

+
+public JSTypeRegistry(ErrorReporter reporter,
+                      boolean tolerateUndefinedValues)
+
+
Constructs a new type registry populated with the built-in types. +

+

+ + + + + + + + +
+Method Detail
+ +

+setResolveMode

+
+public void setResolveMode(JSTypeRegistry.ResolveMode mode)
+
+
Set the current resolving mode of the type registry. +

+

+
+
+
+
See Also:
JSTypeRegistry.ResolveMode
+
+
+
+ +

+getErrorReporter

+
+public ErrorReporter getErrorReporter()
+
+
+
+
+
+
+
+
+
+ +

+shouldTolerateUndefinedValues

+
+public boolean shouldTolerateUndefinedValues()
+
+
+
+
+
+
+
+
+
+ +

+resetForTypeCheck

+
+public void resetForTypeCheck()
+
+
Reset to run the TypeCheck pass. +

+

+
+
+
+
+
+
+
+ +

+registerPropertyOnType

+
+public void registerPropertyOnType(String propertyName,
+                                   JSType type)
+
+
Tells the type system that owner may have a property named + propertyName. This allows the registry to keep track of what + types a property is defined upon. + + This is NOT the same as saying that owner must have a property + named type. ObjectType#hasProperty attempts to minimize false positives + ("if we're not sure, then don't type check this property"). The type + registry, on the other hand, should attempt to minimize false negatives + ("if this property is assigned anywhere in the program, it must + show up in the type registry"). +

+

+
+
+
+
+
+
+
+ +

+unregisterPropertyOnType

+
+public void unregisterPropertyOnType(String propertyName,
+                                     JSType type)
+
+
Removes the index's reference to a property on the given type (if it is + currently registered). If the property is not registered on the type yet, + this method will not change internal state. +

+

+
+
+
+
Parameters:
propertyName - the name of the property to unregister
type - the type to unregister the property on.
+
+
+
+ +

+getGreatestSubtypeWithProperty

+
+public JSType getGreatestSubtypeWithProperty(JSType type,
+                                             String propertyName)
+
+
Gets the greatest subtype of the type that has a property + propertyName defined on it. +

+

+
+
+
+
+
+
+
+ +

+canPropertyBeDefined

+
+public boolean canPropertyBeDefined(JSType type,
+                                    String propertyName)
+
+
Returns whether the given property can possibly be set on the given type. +

+

+
+
+
+
+
+
+
+ +

+getTypesWithProperty

+
+public Iterable<JSType> getTypesWithProperty(String propertyName)
+
+
Returns each type that has a property propertyName defined on it. + + Like most types in our type system, the collection of types returned + will be collapsed. This means that if a type is defined on + Object and on Array, it would be reasonable for this + method to return either [Object, Array] or just [Object]. +

+

+
+
+
+
+
+
+
+ +

+getEachReferenceTypeWithProperty

+
+public Iterable<ObjectType> getEachReferenceTypeWithProperty(String propertyName)
+
+
Returns each reference type that has a property propertyName + defined on it. + + Unlike most types in our type system, the collection of types returned + will not be collapsed. This means that if a type is defined on + Object and on Array, this method must return + [Object, Array]. It would not be correct to collapse them to + [Object]. +

+

+
+
+
+
+
+
+
+ +

+incrementGeneration

+
+public void incrementGeneration()
+
+
Increments the current generation. Clients must call this in order to + move to the next generation of type resolution, allowing types to attempt + resolution again. +

+

+
+
+
+
+
+
+
+ +

+setLastGeneration

+
+public void setLastGeneration(boolean lastGeneration)
+
+
Sets whether this is the last generation. In the last generation, + NamedType warns about unresolved types. +

+

+
+
+
+
+
+
+
+ +

+getDirectImplementors

+
+public Collection<FunctionType> getDirectImplementors(ObjectType interfaceInstance)
+
+
Returns a collection of types that directly implement interfaceInstance. Subtypes of implementing types are not guaranteed to + be returned. interfaceInstance must be an ObjectType for the + instance of the interface. +

+

+
+
+
+
+
+
+
+ +

+declareType

+
+public boolean declareType(String name,
+                           JSType t)
+
+
Records declared global type names. This makes resolution faster + and more robust in the common case. +

+

+
+
+
+
Parameters:
name - The name of the type to be recorded.
t - The actual type being associated with the name. +
Returns:
True if this name is not already defined, false otherwise.
+
+
+
+ +

+overwriteDeclaredType

+
+public void overwriteDeclaredType(String name,
+                                  JSType t)
+
+
Overrides a declared global type name. Throws an exception if this + type name hasn't been declared yet. +

+

+
+
+
+
+
+
+
+ +

+forwardDeclareType

+
+public void forwardDeclareType(String name)
+
+
Records a forward-declared type name. We will not emit errors if this + type name never resolves to anything. +

+

+
+
+
+
+
+
+
+ +

+isForwardDeclaredType

+
+public boolean isForwardDeclaredType(String name)
+
+
Whether this is a forward-declared type name. +

+

+
+
+
+
+
+
+
+ +

+hasNamespace

+
+public boolean hasNamespace(String name)
+
+
Determines whether the given JS package exists. +

+

+
+
+
+
+
+
+
+ +

+getType

+
+public JSType getType(String jsTypeName)
+
+
Looks up a type by name. +

+

+
+
+
+
Parameters:
jsTypeName - The name string. +
Returns:
the corresponding JSType object or null it cannot be found
+
+
+
+ +

+getNativeType

+
+public JSType getNativeType(JSTypeNative typeId)
+
+
+
+
+
+
+
+
+
+ +

+getNativeObjectType

+
+public ObjectType getNativeObjectType(JSTypeNative typeId)
+
+
+
+
+
+
+
+
+
+ +

+getNativeFunctionType

+
+public FunctionType getNativeFunctionType(JSTypeNative typeId)
+
+
+
+
+
+
+
+
+
+ +

+getType

+
+public JSType getType(StaticScope<JSType> scope,
+                      String jsTypeName,
+                      String sourceName,
+                      int lineno,
+                      int charno)
+
+
Looks up a type by name. To allow for forward references to types, an + unrecognized string has to be bound to a NamedType object that will be + resolved later. +

+

+
+
+
+
Parameters:
scope - A scope for doing type name resolution.
jsTypeName - The name string.
sourceName - The name of the source file where this reference appears.
lineno - The line number of the reference. +
Returns:
a NamedType if the string argument is not one of the known types, + otherwise the corresponding JSType object.
+
+
+
+ +

+clearNamedTypes

+
+public void clearNamedTypes()
+
+
Flushes out the current resolved and unresovled Named Types from + the type registry. This is intended to be used ONLY before a + compile is run. +

+

+
+
+
+
+
+
+
+ +

+resolveTypesInScope

+
+public void resolveTypesInScope(StaticScope<JSType> scope)
+
+
Resolve all the unresolved types in the given scope. +

+

+
+
+
+
+
+
+
+ +

+createOptionalType

+
+public JSType createOptionalType(JSType type)
+
+
Creates a type representing optional values of the given type. +

+

+
+
+
+ +
Returns:
the union of the type and the void type
+
+
+
+ +

+createDefaultObjectUnion

+
+public JSType createDefaultObjectUnion(JSType type)
+
+
Creates a type representing nullable values of the given type. +

+

+
+
+
+ +
Returns:
the union of the type and the Null type
+
+
+
+ +

+createNullableType

+
+public JSType createNullableType(JSType type)
+
+
Creates a type representing nullable values of the given type. +

+

+
+
+
+ +
Returns:
the union of the type and the Null type
+
+
+
+ +

+createOptionalNullableType

+
+public JSType createOptionalNullableType(JSType type)
+
+
Creates a nullabel and undefine-able value of the given type. +

+

+
+
+
+ +
Returns:
The union of the type and null and undefined.
+
+
+
+ +

+createUnionType

+
+public JSType createUnionType(JSType... variants)
+
+
Creates a union type whose variants are the arguments. +

+

+
+
+
+
+
+
+
+ +

+createUnionType

+
+public JSType createUnionType(JSTypeNative... variants)
+
+
Creates a union type whose variants are the builtin types specified + by the arguments. +

+

+
+
+
+
+
+
+
+ +

+createEnumType

+
+public EnumType createEnumType(String name,
+                               Node source,
+                               JSType elementsType)
+
+
Creates an enum type. +

+

+
+
+
+
+
+
+
+ +

+createFunctionType

+
+public FunctionType createFunctionType(JSType returnType,
+                                       JSType... parameterTypes)
+
+
Creates a function type. +

+

+
+
+
+
Parameters:
returnType - the function's return type
parameterTypes - the parameters' types
+
+
+
+ +

+createFunctionTypeWithVarArgs

+
+public FunctionType createFunctionTypeWithVarArgs(JSType returnType,
+                                                  List<JSType> parameterTypes)
+
+
Creates a function type. The last parameter type of the function is + considered a variable length argument. +

+

+
+
+
+
Parameters:
returnType - the function's return type
parameterTypes - the parameters' types
+
+
+
+ +

+createFunctionType

+
+public FunctionType createFunctionType(JSType returnType,
+                                       List<JSType> parameterTypes)
+
+
Creates a function type. +

+

+
+
+
+
Parameters:
returnType - the function's return type
parameterTypes - the parameters' types
+
+
+
+ +

+createFunctionTypeWithVarArgs

+
+public FunctionType createFunctionTypeWithVarArgs(JSType returnType,
+                                                  JSType... parameterTypes)
+
+
Creates a function type. The last parameter type of the function is + considered a variable length argument. +

+

+
+
+
+
Parameters:
returnType - the function's return type
parameterTypes - the parameters' types
+
+
+
+ +

+createConstructorType

+
+public FunctionType createConstructorType(JSType returnType,
+                                          JSType... parameterTypes)
+
+
Creates a function type which can act as a constructor. +

+

+
+
+
+
Parameters:
returnType - the function's return type
parameterTypes - the parameters' types
+
+
+
+ +

+createConstructorTypeWithVarArgs

+
+public FunctionType createConstructorTypeWithVarArgs(JSType returnType,
+                                                     JSType... parameterTypes)
+
+
Creates a function type which can act as a constructor. The last + parameter type of the constructor is considered a variable length argument. +

+

+
+
+
+
Parameters:
returnType - the function's return type
parameterTypes - the parameters' types
+
+
+
+ +

+createFunctionType

+
+public JSType createFunctionType(ObjectType instanceType,
+                                 JSType returnType,
+                                 List<JSType> parameterTypes)
+
+
Creates a function type in which this refers to an object instance. +

+

+
+
+
+
Parameters:
instanceType - the type of this
returnType - the function's return type
parameterTypes - the parameters' types
+
+
+
+ +

+createFunctionTypeWithVarArgs

+
+public JSType createFunctionTypeWithVarArgs(ObjectType instanceType,
+                                            JSType returnType,
+                                            List<JSType> parameterTypes)
+
+
Creates a function type in which this refers to an object instance. + The last parameter type of the function is considered a variable length + argument. +

+

+
+
+
+
Parameters:
instanceType - the type of this
returnType - the function's return type
parameterTypes - the parameters' types
+
+
+
+ +

+createParameters

+
+public Node createParameters(List<JSType> parameterTypes)
+
+
Creates a tree hierarchy representing a typed argument list. +

+

+
+
+
+
Parameters:
parameterTypes - the parameter types. +
Returns:
a tree hierarchy representing a typed argument list.
+
+
+
+ +

+createParametersWithVarArgs

+
+public Node createParametersWithVarArgs(List<JSType> parameterTypes)
+
+
Creates a tree hierarchy representing a typed argument list. The last + parameter type is considered a variable length argument. +

+

+
+
+
+
Parameters:
parameterTypes - the parameter types. The last element of this array + is considered a variable length argument. +
Returns:
a tree hierarchy representing a typed argument list.
+
+
+
+ +

+createParameters

+
+public Node createParameters(JSType... parameterTypes)
+
+
Creates a tree hierarchy representing a typed argument list. +

+

+
+
+
+
Parameters:
parameterTypes - the parameter types. +
Returns:
a tree hierarchy representing a typed argument list.
+
+
+
+ +

+createParametersWithVarArgs

+
+public Node createParametersWithVarArgs(JSType... parameterTypes)
+
+
Creates a tree hierarchy representing a typed argument list. The last + parameter type is considered a variable length argument. +

+

+
+
+
+
Parameters:
parameterTypes - the parameter types. The last element of this array + is considered a variable length argument. +
Returns:
a tree hierarchy representing a typed argument list.
+
+
+
+ +

+createOptionalParameters

+
+public Node createOptionalParameters(JSType... parameterTypes)
+
+
Creates a tree hierarchy representing a typed parameter list in which + every parameter is optional. +

+

+
+
+
+
+
+
+
+ +

+createFunctionType

+
+public FunctionType createFunctionType(JSType returnType,
+                                       boolean lastVarArgs,
+                                       JSType... parameterTypes)
+
+
Creates a function type. +

+

+
+
+
+
Parameters:
returnType - the function's return type
lastVarArgs - whether the last parameter type should be considered as + an extensible var_args parameter
parameterTypes - the parameters' types
+
+
+
+ +

+createFunctionTypeWithNewReturnType

+
+public FunctionType createFunctionTypeWithNewReturnType(FunctionType existingFunctionType,
+                                                        JSType returnType)
+
+
Creates a new function type based on an existing function type but + with a new return type. +

+

+
+
+
+
Parameters:
existingFunctionType - the existing function type.
returnType - the new return type.
+
+
+
+ +

+createFunctionTypeWithNewThisType

+
+public FunctionType createFunctionTypeWithNewThisType(FunctionType existingFunctionType,
+                                                      ObjectType thisType)
+
+
Creates a new function type based on an existing function type but + with a new this type. +

+

+
+
+
+
Parameters:
existingFunctionType - the existing function type.
thisType - the new this type.
+
+
+
+ +

+createFunctionType

+
+public FunctionType createFunctionType(JSType returnType,
+                                       Node parameters)
+
+
+
+
+
+
Parameters:
parameters - the function's parameters or null + to indicate that the parameter types are unknown.
returnType - the function's return type or null to indicate + that the return type is unknown.
+
+
+
+ +

+createConstructorType

+
+public FunctionType createConstructorType(JSType returnType,
+                                          boolean lastVarArgs,
+                                          JSType... parameterTypes)
+
+
Creates a function type which can act as a constructor. +

+

+
+
+
+
Parameters:
returnType - the function's return type
lastVarArgs - whether the last parameter type should be considered as + an extensible var_args parameter
parameterTypes - the parameters' types
+
+
+
+ +

+createObjectType

+
+public ObjectType createObjectType(ObjectType implicitPrototype)
+
+
Create an object type. +

+

+
+
+
+
+
+
+
+ +

+createRecordType

+
+public com.google.javascript.rhino.jstype.RecordType createRecordType(Map<String,com.google.javascript.rhino.jstype.RecordTypeBuilder.RecordProperty> properties)
+
+
Creates a record type. +

+

+
+
+
+
+
+
+
+ +

+createObjectType

+
+public ObjectType createObjectType(String name,
+                                   Node n,
+                                   ObjectType implicitPrototype)
+
+
Create an object type. +

+

+
+
+
+
+
+
+
+ +

+createAnonymousObjectType

+
+public ObjectType createAnonymousObjectType()
+
+
Create an anonymous object type. +

+

+
+
+
+
+
+
+
+ +

+resetImplicitPrototype

+
+public boolean resetImplicitPrototype(JSType type,
+                                      ObjectType newImplicitProto)
+
+
Set the implicit prototype if it's possible to do so. +

+

+
+
+
+ +
Returns:
True if we were able to set the implicit prototype successfully, + false if it was not possible to do so for some reason. There are + a few different reasons why this could fail: for example, numbers + can't be implicit prototypes, and we don't want to change the implicit + prototype if other classes have already subclassed this one.
+
+
+
+ +

+createConstructorType

+
+public FunctionType createConstructorType(String name,
+                                          Node source,
+                                          Node parameters,
+                                          JSType returnType)
+
+
Creates a constructor function type. +

+

+
+
+
+
Parameters:
name - the function's name or null to indicate that the + function is anonymous.
source - the node defining this function. Its type + (Node.getType()) must be Token.FUNCTION.
parameters - the function's parameters or null + to indicate that the parameter types are unknown.
returnType - the function's return type or null to indicate + that the return type is unknown.
+
+
+
+ +

+createInterfaceType

+
+public FunctionType createInterfaceType(String name,
+                                        Node source)
+
+
Creates an interface function type. +

+

+
+
+
+
Parameters:
name - the function's name
source - the node defining this function. Its type + (Node.getType()) must be Token.FUNCTION.
+
+
+
+ +

+createParameterizedType

+
+public com.google.javascript.rhino.jstype.ParameterizedType createParameterizedType(ObjectType objectType,
+                                                                                    JSType parameterType)
+
+
Creates a parameterized type. +

+

+
+
+
+
+
+
+
+ +

+createNamedType

+
+public JSType createNamedType(String reference,
+                              String sourceName,
+                              int lineno,
+                              int charno)
+
+
Creates a named type. +

+

+
+
+
+
+
+
+
+ +

+identifyNonNullableName

+
+public void identifyNonNullableName(String name)
+
+
Identifies the name of a typedef or enum before we actually declare it. +

+

+
+
+
+
+
+
+
+ +

+createFromTypeNodes

+
+public JSType createFromTypeNodes(Node n,
+                                  String sourceName,
+                                  StaticScope<JSType> scope)
+
+
Creates a JSType from the nodes representing a type. +

+

+
+
+
+
Parameters:
n - The node with type info.
sourceName - The source file name.
scope - A scope for doing type name lookups.
+
+
+
+ +

+setTemplateTypeName

+
+public void setTemplateTypeName(String name)
+
+
Sets the template type name. +

+

+
+
+
+
+
+
+
+ +

+clearTemplateTypeName

+
+public void clearTemplateTypeName()
+
+
Clears the template type name. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/NoObjectType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/NoObjectType.html new file mode 100644 index 0000000..bdd0277 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/NoObjectType.html @@ -0,0 +1,1132 @@ + + + + + +NoObjectType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class NoObjectType

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType
+      extended by com.google.javascript.rhino.jstype.ObjectType
+          extended by com.google.javascript.rhino.jstype.FunctionType
+              extended by com.google.javascript.rhino.jstype.NoObjectType
+
+
+
All Implemented Interfaces:
StaticScope<JSType>, Serializable
+
+
+
Direct Known Subclasses:
NoType
+
+
+
+
public class NoObjectType
extends FunctionType
+ + +

+The bottom Object type, representing the subclass of all objects. + + Although JavaScript programmers can't explicitly denote the bottom + Object type, it comes up in static analysis. For example, if we have: + + var x = function() {}; + if (x instanceof Array) { + f(x); + } + + We need to be able to assign x a type within the f(x) + call. It has no possible type, but x would not be legal if f + expected a string. So we assign it the NoObjectType. +

+ +

+

+
See Also:
Bottom types, +Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.ObjectType
ObjectType.Property
+  + + + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.JSType
JSType.TypePair
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.rhino.jstype.JSType
EMPTY_TYPE_COMPONENT, ENUMDECL, NOT_A_CLASS, NOT_A_TYPE, NOT_ENUMDECL, UNKNOWN_NAME
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ FunctionTypegetConstructor() + +
+          Gets this object's constructor.
+ Iterable<ObjectType>getCtorExtendedInterfaces() + +
+          Gets the interfaces extended by the interface associated with this type.
+ Iterable<ObjectType>getCtorImplementedInterfaces() + +
+          Gets the interfaces implemented by the ctor associated with this type.
+ ObjectTypegetImplicitPrototype() + +
+          Gets the implicit prototype (a.k.a.
+ FunctionTypegetOwnerFunction() + +
+          Gets the owner of this if it's a function prototype.
+ JSDocInfogetOwnPropertyJSDocInfo(String propertyName) + +
+          Gets the docInfo on the specified property on this type.
+ intgetPropertiesCount() + +
+          Gets the number of properties of this object.
+ NodegetPropertyNode(String propertyName) + +
+          Gets the node corresponding to the definition of the specified property.
+ JSTypegetPropertyType(String propertyName) + +
+          Gets the property type of the property whose name is given.
+ StringgetReferenceName() + +
+          Gets the reference name for this object.
+ inthashCode() + +
+           
+ booleanhasOwnProperty(String propertyName) + +
+          Checks whether the property whose name is given is present directly on + the object.
+ booleanhasProperty(String propertyName) + +
+          Checks whether the property whose name is given is present on the + object.
+ booleanhasReferenceName() + +
+          Returns true if the object is named.
+ booleanisEquivalentTo(JSType that) + +
+          Two function types are equal if their signatures match.
+ booleanisNativeObjectType() + +
+          Whether this is a built-in object.
+ booleanisNoObjectType() + +
+           
+ booleanisPropertyInExterns(String propertyName) + +
+          Checks whether the property was defined in the externs.
+ booleanisPropertyTypeDeclared(String property) + +
+          Checks whether the property's type is declared.
+ booleanisPropertyTypeInferred(String propertyName) + +
+          Checks whether the property's type is inferred.
+ booleanisSubtype(JSType that) + +
+          A function is a subtype of another if their call methods are related via + subtyping and this is a subtype of that with regard to + the prototype chain.
+ voidmatchConstraint(ObjectType constraintObj) + +
+          Modify this type so that it matches the specified type.
+ booleanmatchesNumberContext() + +
+          This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator.
+ booleanmatchesObjectContext() + +
+          This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement.
+ booleanmatchesStringContext() + +
+          This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator.
+ booleanremoveProperty(String name) + +
+          Removes the declared or inferred property from this ObjectType.
+ voidsetPropertyJSDocInfo(String propertyName, + JSDocInfo info) + +
+          Sets the docInfo for the specified property from the + JSDocInfo on its definition.
+ FunctionTypetoMaybeFunctionType() + +
+          Downcasts this to a FunctionType, or returns null if this is not + a function.
+ JSTypeunboxesTo() + +
+          Gets the type to which this type unboxes.
+ + + + + +
+<T> T
+
visit(Visitor<T> visitor) + +
+          Visit this type with the given visitor.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.FunctionType
canBeCalled, clearCachedValues, getAllExtendedInterfaces, getAllImplementedInterfaces, getBindReturnType, getExtendedInterfaces, getExtendedInterfacesCount, getImplementedInterfaces, getInstanceType, getMaxArguments, getMinArguments, getOwnImplementedInterfaces, getOwnPropertyNames, getParameters, getParametersNode, getPrototype, getReturnType, getSlot, getSource, getSubTypes, getSuperClassConstructor, getTemplateTypeName, getTopDefiningInterface, getTopMostDefiningType, getTypeOfThis, hasCachedValues, hasEqualCallType, hasImplementedInterfaces, hasInstanceType, isConstructor, isInstanceType, isInterface, isOrdinaryFunction, isReturnTypeInferred, setExtendedInterfaces, setImplementedInterfaces, setPrototypeBasedOn, setSource, toDebugHashCodeString
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.ObjectType
cast, createDelegateSuffix, defineDeclaredProperty, defineInferredProperty, findPropertyType, getDisplayName, getIndexType, getJSDocInfo, getNormalizedReferenceName, getOwnSlot, getParameterType, getParentScope, getPossibleToBooleanOutcomes, getPropertyNames, getRootNode, isFunctionPrototypeType, isObject, isUnknownType, setJSDocInfo, testForEquality
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.JSType
autobox, autoboxesTo, canAssignTo, canTestForEqualityWith, canTestForShallowEqualityWith, clearResolved, collapseUnion, dereference, differsFrom, equals, forceResolve, getGreatestSubtype, getLeastSupertype, getRestrictedTypeGivenToBooleanOutcome, getTypesUnderEquality, getTypesUnderInequality, getTypesUnderShallowEquality, getTypesUnderShallowInequality, hasDisplayName, isAllType, isArrayType, isBooleanObjectType, isBooleanValueType, isCheckedUnknownType, isDateType, isEmptyType, isEnumElementType, isEnumType, isEquivalent, isFunctionType, isGlobalThisType, isNominalConstructor, isNominalType, isNoResolvedType, isNoType, isNullable, isNullType, isNumber, isNumberObjectType, isNumberValueType, isRecordType, isRegexpType, isResolved, isString, isStringObjectType, isStringValueType, isTemplateType, isUnionType, isVoidType, matchesInt32Context, matchesUint32Context, resolve, restrictByNotNullOrUndefined, setValidator, toAnnotationString, toMaybeEnumElementType, toMaybeEnumType, toMaybeFunctionType, toMaybeUnionType, toObjectType, toString
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+isSubtype

+
+public boolean isSubtype(JSType that)
+
+
Description copied from class: FunctionType
+
A function is a subtype of another if their call methods are related via + subtyping and this is a subtype of that with regard to + the prototype chain. +

+

+
Overrides:
isSubtype in class FunctionType
+
+
+ +
Returns:
this &lt;: that
+
+
+
+ +

+toMaybeFunctionType

+
+public FunctionType toMaybeFunctionType()
+
+
Description copied from class: JSType
+
Downcasts this to a FunctionType, or returns null if this is not + a function. + + For the purposes of this function, we define a MaybeFunctionType as any + type in the sub-lattice + { x | LEAST_FUNCTION_TYPE <= x <= GREATEST_FUNCTION_TYPE } + This definition excludes bottom types like NoType and NoObjectType. + + This definition is somewhat arbitrary and axiomatic, but this is the + definition that makes the most sense for the most callers. +

+

+
Overrides:
toMaybeFunctionType in class FunctionType
+
+
+
+
+
+
+ +

+isNoObjectType

+
+public boolean isNoObjectType()
+
+
+
Overrides:
isNoObjectType in class JSType
+
+
+
+
+
+
+ +

+getImplicitPrototype

+
+public ObjectType getImplicitPrototype()
+
+
Description copied from class: ObjectType
+
Gets the implicit prototype (a.k.a. the [[Prototype]] property). +

+

+
+
+
+
+
+
+
+ +

+getReferenceName

+
+public String getReferenceName()
+
+
Description copied from class: ObjectType
+
Gets the reference name for this object. This includes named types + like constructors, prototypes, and enums. It notably does not include + literal types like strings and booleans and structural types. +

+

+
+
+
+ +
Returns:
the object's name or null if this is an anonymous + object
+
+
+
+ +

+matchesNumberContext

+
+public boolean matchesNumberContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator. +

+

+
+
+
+
+
+
+
+ +

+matchesObjectContext

+
+public boolean matchesObjectContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement. + + Most types we will encounter, except notably null, have at least + the potential for converting to Object. Host defined objects can + get peculiar. +

+

+
+
+
+
+
+
+
+ +

+matchesStringContext

+
+public boolean matchesStringContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator. + + All types have at least the potential for converting to String. + When we add externally defined types, such as a browser OM, we may choose + to add types that do not automatically convert to String. +

+

+
+
+
+
+
+
+
+ +

+isEquivalentTo

+
+public boolean isEquivalentTo(JSType that)
+
+
Description copied from class: FunctionType
+
Two function types are equal if their signatures match. Since they don't + have signatures, two interfaces are equal if their names match. +

+

+
Overrides:
isEquivalentTo in class FunctionType
+
+
+
+
+
+
+ +

+hashCode

+
+public int hashCode()
+
+
+
Overrides:
hashCode in class FunctionType
+
+
+
+
+
+
+ +

+getPropertiesCount

+
+public int getPropertiesCount()
+
+
Gets the number of properties of this object. +

+

+
+
+
+
+
+
+
+ +

+getPropertyType

+
+public JSType getPropertyType(String propertyName)
+
+
Description copied from class: ObjectType
+
Gets the property type of the property whose name is given. If the + underlying object does not have this property, the Unknown type is + returned to indicate that no information is available on this property. +

+

+
Overrides:
getPropertyType in class FunctionType
+
+
+ +
Returns:
the property's type or UnknownType. This method never + returns null.
+
+
+
+ +

+hasProperty

+
+public boolean hasProperty(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property whose name is given is present on the + object. +

+

+
+
+
+
+
+
+
+ +

+removeProperty

+
+public boolean removeProperty(String name)
+
+
Description copied from class: ObjectType
+
Removes the declared or inferred property from this ObjectType. +

+

+
+
+
+
Parameters:
name - the property's name +
Returns:
true if the property was removed successfully. False if the + property did not exist, or could not be removed.
+
+
+
+ +

+getOwnPropertyJSDocInfo

+
+public JSDocInfo getOwnPropertyJSDocInfo(String propertyName)
+
+
Description copied from class: ObjectType
+
Gets the docInfo on the specified property on this type. This should not + be done implemented recursively, as you generally need to know exactly on + which type in the prototype chain the JSDocInfo exists. +

+

+
+
+
+
+
+
+
+ +

+setPropertyJSDocInfo

+
+public void setPropertyJSDocInfo(String propertyName,
+                                 JSDocInfo info)
+
+
Description copied from class: ObjectType
+
Sets the docInfo for the specified property from the + JSDocInfo on its definition. +

+

+
+
+
+
info - JSDocInfo for the property definition. May be + null.
+
+
+
+ +

+isPropertyTypeInferred

+
+public boolean isPropertyTypeInferred(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property's type is inferred. +

+

+
+
+
+
+
+
+
+ +

+visit

+
+public <T> T visit(Visitor<T> visitor)
+
+
Description copied from class: JSType
+
Visit this type with the given visitor. +

+

+
Overrides:
visit in class FunctionType
+
+
+ +
Returns:
the value returned by the visitor
See Also:
Visitor
+
+
+
+ +

+getConstructor

+
+public FunctionType getConstructor()
+
+
Description copied from class: ObjectType
+
Gets this object's constructor. +

+

+
+
+
+ +
Returns:
this object's constructor or null if it is a native + object (constructed natively v.s. by instantiation of a function)
+
+
+
+ +

+hasOwnProperty

+
+public boolean hasOwnProperty(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property whose name is given is present directly on + the object. Returns false even if it is declared on a supertype. +

+

+
Overrides:
hasOwnProperty in class ObjectType
+
+
+
+
+
+
+ +

+isPropertyTypeDeclared

+
+public boolean isPropertyTypeDeclared(String property)
+
+
Description copied from class: ObjectType
+
Checks whether the property's type is declared. +

+

+
Specified by:
isPropertyTypeDeclared in class ObjectType
+
+
+
+
+
+
+ +

+isPropertyInExterns

+
+public boolean isPropertyInExterns(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property was defined in the externs. +

+

+
Overrides:
isPropertyInExterns in class ObjectType
+
+
+
+
+
+
+ +

+getPropertyNode

+
+public Node getPropertyNode(String propertyName)
+
+
Description copied from class: ObjectType
+
Gets the node corresponding to the definition of the specified property. + This could be the node corresponding to declaration of the property or the + node corresponding to the first reference to this property, e.g., + "this.propertyName" in a constructor. Note this is mainly intended to be + an estimate of where in the source code a property is defined. Sometime + the returned node is not even part of the global AST but in the AST of the + JsDoc that defines a type. +

+

+
Overrides:
getPropertyNode in class ObjectType
+
+
+
Parameters:
propertyName - the name of the property +
Returns:
the Node corresponding to the property or null.
+
+
+
+ +

+unboxesTo

+
+public JSType unboxesTo()
+
+
Description copied from class: JSType
+
Gets the type to which this type unboxes. +

+

+
Overrides:
unboxesTo in class JSType
+
+
+ +
Returns:
the unboxed type or null if this type does not unbox.
+
+
+
+ +

+hasReferenceName

+
+public boolean hasReferenceName()
+
+
Description copied from class: ObjectType
+
Returns true if the object is named. +

+

+
Overrides:
hasReferenceName in class ObjectType
+
+
+ +
Returns:
true if the object is named, false if it is anonymous
+
+
+
+ +

+isNativeObjectType

+
+public boolean isNativeObjectType()
+
+
Whether this is a built-in object. +

+

+
Overrides:
isNativeObjectType in class ObjectType
+
+
+
+
+
+
+ +

+getOwnerFunction

+
+public FunctionType getOwnerFunction()
+
+
Description copied from class: ObjectType
+
Gets the owner of this if it's a function prototype. +

+

+
Overrides:
getOwnerFunction in class ObjectType
+
+
+
+
+
+
+ +

+getCtorImplementedInterfaces

+
+public Iterable<ObjectType> getCtorImplementedInterfaces()
+
+
Description copied from class: ObjectType
+
Gets the interfaces implemented by the ctor associated with this type. + Intended to be overridden by subclasses. +

+

+
Overrides:
getCtorImplementedInterfaces in class ObjectType
+
+
+
+
+
+
+ +

+getCtorExtendedInterfaces

+
+public Iterable<ObjectType> getCtorExtendedInterfaces()
+
+
Description copied from class: ObjectType
+
Gets the interfaces extended by the interface associated with this type. + Intended to be overriden by subclasses. +

+

+
Overrides:
getCtorExtendedInterfaces in class ObjectType
+
+
+
+
+
+
+ +

+matchConstraint

+
+public void matchConstraint(ObjectType constraintObj)
+
+
Description copied from class: JSType
+
Modify this type so that it matches the specified type. + + This is useful for reverse type-inference, where we want to + infer that an object literal matches its contraint (much like + how the java compiler does reverse-inference to figure out generics). +

+

+
Overrides:
matchConstraint in class JSType
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/NoType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/NoType.html new file mode 100644 index 0000000..f5f17db --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/NoType.html @@ -0,0 +1,873 @@ + + + + + +NoType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class NoType

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType
+      extended by com.google.javascript.rhino.jstype.ObjectType
+          extended by com.google.javascript.rhino.jstype.FunctionType
+              extended by com.google.javascript.rhino.jstype.NoObjectType
+                  extended by com.google.javascript.rhino.jstype.NoType
+
+
+
All Implemented Interfaces:
StaticScope<JSType>, Serializable
+
+
+
+
public class NoType
extends NoObjectType
+ + +

+Bottom type, representing the subclass of any value or object. + + Although JavaScript programmers can't explicitly denote the bottom type, + it comes up in static analysis. For example, if we have: + + var x = null; + if (x) { + f(x); + } + + We need to be able to assign x a type within the f(x) + call. Since it has no possible type, we assign x the NoType, + so that f(x) is legal no matter what the type of f's + first argument is. +

+ +

+

+
See Also:
Bottom types, +Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.ObjectType
ObjectType.Property
+  + + + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.JSType
JSType.TypePair
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.rhino.jstype.JSType
EMPTY_TYPE_COMPONENT, ENUMDECL, NOT_A_CLASS, NOT_A_TYPE, NOT_ENUMDECL, UNKNOWN_NAME
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ Iterable<ObjectType>getCtorExtendedInterfaces() + +
+          Gets the interfaces extended by the interface associated with this type.
+ Iterable<ObjectType>getCtorImplementedInterfaces() + +
+          Gets the interfaces implemented by the ctor associated with this type.
+ FunctionTypegetOwnerFunction() + +
+          Gets the owner of this if it's a function prototype.
+ BooleanLiteralSetgetPossibleToBooleanOutcomes() + +
+          Computes the set of possible outcomes of the ToBoolean predicate + for this type.
+ NodegetPropertyNode(String propertyName) + +
+          Gets the node corresponding to the definition of the specified property.
+ booleanhasOwnProperty(String propertyName) + +
+          Checks whether the property whose name is given is present directly on + the object.
+ booleanhasReferenceName() + +
+          Returns true if the object is named.
+ booleanisNativeObjectType() + +
+          Whether this is a built-in object.
+ booleanisNoObjectType() + +
+           
+ booleanisNoType() + +
+           
+ booleanisNullable() + +
+          Tests whether this type is nullable.
+ booleanisPropertyInExterns(String propertyName) + +
+          Checks whether the property was defined in the externs.
+ booleanisPropertyTypeDeclared(String property) + +
+          Checks whether the property's type is declared.
+ booleanisSubtype(JSType that) + +
+          A function is a subtype of another if their call methods are related via + subtyping and this is a subtype of that with regard to + the prototype chain.
+ voidmatchConstraint(ObjectType constraintObj) + +
+          Modify this type so that it matches the specified type.
+ booleanmatchesNumberContext() + +
+          This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator.
+ booleanmatchesObjectContext() + +
+          This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement.
+ booleanmatchesStringContext() + +
+          This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator.
+ JSTypeunboxesTo() + +
+          Gets the type to which this type unboxes.
+ + + + + +
+<T> T
+
visit(Visitor<T> visitor) + +
+          Visit this type with the given visitor.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.NoObjectType
getConstructor, getImplicitPrototype, getOwnPropertyJSDocInfo, getPropertiesCount, getPropertyType, getReferenceName, hashCode, hasProperty, isEquivalentTo, isPropertyTypeInferred, removeProperty, setPropertyJSDocInfo, toMaybeFunctionType
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.FunctionType
canBeCalled, clearCachedValues, getAllExtendedInterfaces, getAllImplementedInterfaces, getBindReturnType, getExtendedInterfaces, getExtendedInterfacesCount, getImplementedInterfaces, getInstanceType, getMaxArguments, getMinArguments, getOwnImplementedInterfaces, getOwnPropertyNames, getParameters, getParametersNode, getPrototype, getReturnType, getSlot, getSource, getSubTypes, getSuperClassConstructor, getTemplateTypeName, getTopDefiningInterface, getTopMostDefiningType, getTypeOfThis, hasCachedValues, hasEqualCallType, hasImplementedInterfaces, hasInstanceType, isConstructor, isInstanceType, isInterface, isOrdinaryFunction, isReturnTypeInferred, setExtendedInterfaces, setImplementedInterfaces, setPrototypeBasedOn, setSource, toDebugHashCodeString
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.ObjectType
cast, createDelegateSuffix, defineDeclaredProperty, defineInferredProperty, findPropertyType, getDisplayName, getIndexType, getJSDocInfo, getNormalizedReferenceName, getOwnSlot, getParameterType, getParentScope, getPropertyNames, getRootNode, isFunctionPrototypeType, isObject, isUnknownType, setJSDocInfo, testForEquality
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.JSType
autobox, autoboxesTo, canAssignTo, canTestForEqualityWith, canTestForShallowEqualityWith, clearResolved, collapseUnion, dereference, differsFrom, equals, forceResolve, getGreatestSubtype, getLeastSupertype, getRestrictedTypeGivenToBooleanOutcome, getTypesUnderEquality, getTypesUnderInequality, getTypesUnderShallowEquality, getTypesUnderShallowInequality, hasDisplayName, isAllType, isArrayType, isBooleanObjectType, isBooleanValueType, isCheckedUnknownType, isDateType, isEmptyType, isEnumElementType, isEnumType, isEquivalent, isFunctionType, isGlobalThisType, isNominalConstructor, isNominalType, isNoResolvedType, isNullType, isNumber, isNumberObjectType, isNumberValueType, isRecordType, isRegexpType, isResolved, isString, isStringObjectType, isStringValueType, isTemplateType, isUnionType, isVoidType, matchesInt32Context, matchesUint32Context, resolve, restrictByNotNullOrUndefined, setValidator, toAnnotationString, toMaybeEnumElementType, toMaybeEnumType, toMaybeFunctionType, toMaybeUnionType, toObjectType, toString
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+isNoObjectType

+
+public boolean isNoObjectType()
+
+
+
Overrides:
isNoObjectType in class NoObjectType
+
+
+
+
+
+
+ +

+isNoType

+
+public boolean isNoType()
+
+
+
Overrides:
isNoType in class JSType
+
+
+
+
+
+
+ +

+isNullable

+
+public boolean isNullable()
+
+
Description copied from class: JSType
+
Tests whether this type is nullable. +

+

+
Overrides:
isNullable in class JSType
+
+
+
+
+
+
+ +

+isSubtype

+
+public boolean isSubtype(JSType that)
+
+
Description copied from class: FunctionType
+
A function is a subtype of another if their call methods are related via + subtyping and this is a subtype of that with regard to + the prototype chain. +

+

+
Overrides:
isSubtype in class NoObjectType
+
+
+ +
Returns:
this &lt;: that
+
+
+
+ +

+getPossibleToBooleanOutcomes

+
+public BooleanLiteralSet getPossibleToBooleanOutcomes()
+
+
Description copied from class: JSType
+
Computes the set of possible outcomes of the ToBoolean predicate + for this type. The ToBoolean predicate is defined by the ECMA-262 + standard, 3rd edition. Its behavior for simple types can be + summarized by the following table: + + + + + + + + +
typeresult
undefined{false}
null{false}
boolean{true, false}
number{true, false}
string{true, false}
Object{true}
+

+

+
Overrides:
getPossibleToBooleanOutcomes in class ObjectType
+
+
+ +
Returns:
the set of boolean literals for this type
+
+
+
+ +

+matchesNumberContext

+
+public boolean matchesNumberContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator. +

+

+
Overrides:
matchesNumberContext in class NoObjectType
+
+
+
+
+
+
+ +

+matchesObjectContext

+
+public boolean matchesObjectContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement. + + Most types we will encounter, except notably null, have at least + the potential for converting to Object. Host defined objects can + get peculiar. +

+

+
Overrides:
matchesObjectContext in class NoObjectType
+
+
+
+
+
+
+ +

+matchesStringContext

+
+public boolean matchesStringContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator. + + All types have at least the potential for converting to String. + When we add externally defined types, such as a browser OM, we may choose + to add types that do not automatically convert to String. +

+

+
Overrides:
matchesStringContext in class NoObjectType
+
+
+
+
+
+
+ +

+visit

+
+public <T> T visit(Visitor<T> visitor)
+
+
Description copied from class: JSType
+
Visit this type with the given visitor. +

+

+
Overrides:
visit in class NoObjectType
+
+
+ +
Returns:
the value returned by the visitor
See Also:
Visitor
+
+
+
+ +

+hasOwnProperty

+
+public boolean hasOwnProperty(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property whose name is given is present directly on + the object. Returns false even if it is declared on a supertype. +

+

+
Overrides:
hasOwnProperty in class ObjectType
+
+
+
+
+
+
+ +

+isPropertyTypeDeclared

+
+public boolean isPropertyTypeDeclared(String property)
+
+
Description copied from class: ObjectType
+
Checks whether the property's type is declared. +

+

+
Specified by:
isPropertyTypeDeclared in class ObjectType
+
+
+
+
+
+
+ +

+isPropertyInExterns

+
+public boolean isPropertyInExterns(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property was defined in the externs. +

+

+
Overrides:
isPropertyInExterns in class ObjectType
+
+
+
+
+
+
+ +

+getPropertyNode

+
+public Node getPropertyNode(String propertyName)
+
+
Description copied from class: ObjectType
+
Gets the node corresponding to the definition of the specified property. + This could be the node corresponding to declaration of the property or the + node corresponding to the first reference to this property, e.g., + "this.propertyName" in a constructor. Note this is mainly intended to be + an estimate of where in the source code a property is defined. Sometime + the returned node is not even part of the global AST but in the AST of the + JsDoc that defines a type. +

+

+
Overrides:
getPropertyNode in class ObjectType
+
+
+
Parameters:
propertyName - the name of the property +
Returns:
the Node corresponding to the property or null.
+
+
+
+ +

+unboxesTo

+
+public JSType unboxesTo()
+
+
Description copied from class: JSType
+
Gets the type to which this type unboxes. +

+

+
Overrides:
unboxesTo in class JSType
+
+
+ +
Returns:
the unboxed type or null if this type does not unbox.
+
+
+
+ +

+hasReferenceName

+
+public boolean hasReferenceName()
+
+
Description copied from class: ObjectType
+
Returns true if the object is named. +

+

+
Overrides:
hasReferenceName in class ObjectType
+
+
+ +
Returns:
true if the object is named, false if it is anonymous
+
+
+
+ +

+isNativeObjectType

+
+public boolean isNativeObjectType()
+
+
Whether this is a built-in object. +

+

+
Overrides:
isNativeObjectType in class ObjectType
+
+
+
+
+
+
+ +

+getOwnerFunction

+
+public FunctionType getOwnerFunction()
+
+
Description copied from class: ObjectType
+
Gets the owner of this if it's a function prototype. +

+

+
Overrides:
getOwnerFunction in class ObjectType
+
+
+
+
+
+
+ +

+getCtorImplementedInterfaces

+
+public Iterable<ObjectType> getCtorImplementedInterfaces()
+
+
Description copied from class: ObjectType
+
Gets the interfaces implemented by the ctor associated with this type. + Intended to be overridden by subclasses. +

+

+
Overrides:
getCtorImplementedInterfaces in class ObjectType
+
+
+
+
+
+
+ +

+getCtorExtendedInterfaces

+
+public Iterable<ObjectType> getCtorExtendedInterfaces()
+
+
Description copied from class: ObjectType
+
Gets the interfaces extended by the interface associated with this type. + Intended to be overriden by subclasses. +

+

+
Overrides:
getCtorExtendedInterfaces in class ObjectType
+
+
+
+
+
+
+ +

+matchConstraint

+
+public void matchConstraint(ObjectType constraintObj)
+
+
Description copied from class: JSType
+
Modify this type so that it matches the specified type. + + This is useful for reverse type-inference, where we want to + infer that an object literal matches its contraint (much like + how the java compiler does reverse-inference to figure out generics). +

+

+
Overrides:
matchConstraint in class JSType
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/NullType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/NullType.html new file mode 100644 index 0000000..7367969 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/NullType.html @@ -0,0 +1,591 @@ + + + + + +NullType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class NullType

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType
+      extended by com.google.javascript.rhino.jstype.NullType
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public final class NullType
extends JSType
+ + +

+Null type. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.JSType
JSType.TypePair
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.rhino.jstype.JSType
EMPTY_TYPE_COMPONENT, ENUMDECL, NOT_A_CLASS, NOT_A_TYPE, NOT_ENUMDECL, UNKNOWN_NAME
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StringgetDisplayName() + +
+          Returns a user meaningful label for the JSType instance.
+ BooleanLiteralSetgetPossibleToBooleanOutcomes() + +
+          Computes the set of possible outcomes of the ToBoolean predicate + for this type.
+ booleanhasDisplayName() + +
+           
+ booleanisNullable() + +
+          Tests whether this type is nullable.
+ booleanisNullType() + +
+           
+ booleanmatchesNumberContext() + +
+          This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator.
+ booleanmatchesObjectContext() + +
+          This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement.
+ booleanmatchesStringContext() + +
+          This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator.
+ JSTyperestrictByNotNullOrUndefined() + +
+          If this is a union type, returns a union type that does not include + the null or undefined type.
+ TernaryValuetestForEquality(JSType that) + +
+          Compares this and that.
+ + + + + +
+<T> T
+
visit(Visitor<T> visitor) + +
+          Visit this type with the given visitor.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.JSType
autobox, autoboxesTo, canAssignTo, canBeCalled, canTestForEqualityWith, canTestForShallowEqualityWith, clearResolved, collapseUnion, dereference, differsFrom, equals, findPropertyType, forceResolve, getGreatestSubtype, getJSDocInfo, getLeastSupertype, getRestrictedTypeGivenToBooleanOutcome, getTypesUnderEquality, getTypesUnderInequality, getTypesUnderShallowEquality, getTypesUnderShallowInequality, hashCode, isAllType, isArrayType, isBooleanObjectType, isBooleanValueType, isCheckedUnknownType, isConstructor, isDateType, isEmptyType, isEnumElementType, isEnumType, isEquivalent, isEquivalentTo, isFunctionPrototypeType, isFunctionType, isGlobalThisType, isInstanceType, isInterface, isNominalConstructor, isNominalType, isNoObjectType, isNoResolvedType, isNoType, isNumber, isNumberObjectType, isNumberValueType, isObject, isOrdinaryFunction, isRecordType, isRegexpType, isResolved, isString, isStringObjectType, isStringValueType, isSubtype, isTemplateType, isUnionType, isUnknownType, isVoidType, matchConstraint, matchesInt32Context, matchesUint32Context, resolve, setValidator, toAnnotationString, toDebugHashCodeString, toMaybeEnumElementType, toMaybeEnumType, toMaybeFunctionType, toMaybeFunctionType, toMaybeUnionType, toObjectType, toString, unboxesTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+isNullType

+
+public boolean isNullType()
+
+
+
Overrides:
isNullType in class JSType
+
+
+
+
+
+
+ +

+isNullable

+
+public boolean isNullable()
+
+
Description copied from class: JSType
+
Tests whether this type is nullable. +

+

+
Overrides:
isNullable in class JSType
+
+
+
+
+
+
+ +

+matchesNumberContext

+
+public boolean matchesNumberContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator. +

+

+
Overrides:
matchesNumberContext in class JSType
+
+
+
+
+
+
+ +

+matchesObjectContext

+
+public boolean matchesObjectContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement. + + Most types we will encounter, except notably null, have at least + the potential for converting to Object. Host defined objects can + get peculiar. +

+

+
Overrides:
matchesObjectContext in class JSType
+
+
+
+
+
+
+ +

+matchesStringContext

+
+public boolean matchesStringContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator. + + All types have at least the potential for converting to String. + When we add externally defined types, such as a browser OM, we may choose + to add types that do not automatically convert to String. +

+

+
Overrides:
matchesStringContext in class JSType
+
+
+
+
+
+
+ +

+restrictByNotNullOrUndefined

+
+public JSType restrictByNotNullOrUndefined()
+
+
Description copied from class: JSType
+
If this is a union type, returns a union type that does not include + the null or undefined type. +

+

+
Overrides:
restrictByNotNullOrUndefined in class JSType
+
+
+
+
+
+
+ +

+testForEquality

+
+public TernaryValue testForEquality(JSType that)
+
+
Description copied from class: JSType
+
Compares this and that. +

+

+
Overrides:
testForEquality in class JSType
+
+
+ +
Returns:
    +
  • TernaryValue.TRUE if the comparison of values of + this type and that always succeed (such as + undefined compared to null)
  • +
  • TernaryValue.FALSE if the comparison of values of + this type and that always fails (such as + undefined compared to number)
  • +
  • TernaryValue.UNKNOWN if the comparison can succeed or + fail depending on the concrete values
  • +
+
+
+
+ +

+getDisplayName

+
+public String getDisplayName()
+
+
Description copied from class: JSType
+
Returns a user meaningful label for the JSType instance. For example, + Functions and Enums will return their declaration name (if they have one). + Some types will not have a meaningful display name. Calls to + hasDisplayName() will return true IFF getDisplayName() will return null + or a zero length string. +

+

+
Overrides:
getDisplayName in class JSType
+
+
+ +
Returns:
the display name of the type, or null if one is not available
+
+
+
+ +

+getPossibleToBooleanOutcomes

+
+public BooleanLiteralSet getPossibleToBooleanOutcomes()
+
+
Description copied from class: JSType
+
Computes the set of possible outcomes of the ToBoolean predicate + for this type. The ToBoolean predicate is defined by the ECMA-262 + standard, 3rd edition. Its behavior for simple types can be + summarized by the following table: + + + + + + + + +
typeresult
undefined{false}
null{false}
boolean{true, false}
number{true, false}
string{true, false}
Object{true}
+

+

+
Specified by:
getPossibleToBooleanOutcomes in class JSType
+
+
+ +
Returns:
the set of boolean literals for this type
+
+
+
+ +

+visit

+
+public <T> T visit(Visitor<T> visitor)
+
+
Description copied from class: JSType
+
Visit this type with the given visitor. +

+

+
Specified by:
visit in class JSType
+
+
+ +
Returns:
the value returned by the visitor
See Also:
Visitor
+
+
+
+ +

+hasDisplayName

+
+public boolean hasDisplayName()
+
+
+
Overrides:
hasDisplayName in class JSType
+
+
+ +
Returns:
true if the JSType has a user meaningful label.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/NumberType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/NumberType.html new file mode 100644 index 0000000..9be56a4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/NumberType.html @@ -0,0 +1,590 @@ + + + + + +NumberType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class NumberType

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType
+      extended by com.google.javascript.rhino.jstype.NumberType
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class NumberType
extends JSType
+ + +

+Number type. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.JSType
JSType.TypePair
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.rhino.jstype.JSType
EMPTY_TYPE_COMPONENT, ENUMDECL, NOT_A_CLASS, NOT_A_TYPE, NOT_ENUMDECL, UNKNOWN_NAME
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ JSTypeautoboxesTo() + +
+          Gets the type to which this type auto-boxes.
+ StringgetDisplayName() + +
+          Returns a user meaningful label for the JSType instance.
+ BooleanLiteralSetgetPossibleToBooleanOutcomes() + +
+          Computes the set of possible outcomes of the ToBoolean predicate + for this type.
+ booleanhasDisplayName() + +
+           
+ booleanisNullable() + +
+          Tests whether this type is nullable.
+ booleanisNumberValueType() + +
+           
+ booleanmatchesNumberContext() + +
+          This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator.
+ booleanmatchesObjectContext() + +
+          This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement.
+ booleanmatchesStringContext() + +
+          This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator.
+ TernaryValuetestForEquality(JSType that) + +
+          Compares this and that.
+ + + + + +
+<T> T
+
visit(Visitor<T> visitor) + +
+          Visit this type with the given visitor.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.JSType
autobox, canAssignTo, canBeCalled, canTestForEqualityWith, canTestForShallowEqualityWith, clearResolved, collapseUnion, dereference, differsFrom, equals, findPropertyType, forceResolve, getGreatestSubtype, getJSDocInfo, getLeastSupertype, getRestrictedTypeGivenToBooleanOutcome, getTypesUnderEquality, getTypesUnderInequality, getTypesUnderShallowEquality, getTypesUnderShallowInequality, hashCode, isAllType, isArrayType, isBooleanObjectType, isBooleanValueType, isCheckedUnknownType, isConstructor, isDateType, isEmptyType, isEnumElementType, isEnumType, isEquivalent, isEquivalentTo, isFunctionPrototypeType, isFunctionType, isGlobalThisType, isInstanceType, isInterface, isNominalConstructor, isNominalType, isNoObjectType, isNoResolvedType, isNoType, isNullType, isNumber, isNumberObjectType, isObject, isOrdinaryFunction, isRecordType, isRegexpType, isResolved, isString, isStringObjectType, isStringValueType, isSubtype, isTemplateType, isUnionType, isUnknownType, isVoidType, matchConstraint, matchesInt32Context, matchesUint32Context, resolve, restrictByNotNullOrUndefined, setValidator, toAnnotationString, toDebugHashCodeString, toMaybeEnumElementType, toMaybeEnumType, toMaybeFunctionType, toMaybeFunctionType, toMaybeUnionType, toObjectType, toString, unboxesTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+isNullable

+
+public boolean isNullable()
+
+
Description copied from class: JSType
+
Tests whether this type is nullable. +

+

+
Overrides:
isNullable in class JSType
+
+
+
+
+
+
+ +

+testForEquality

+
+public TernaryValue testForEquality(JSType that)
+
+
Description copied from class: JSType
+
Compares this and that. +

+

+
Overrides:
testForEquality in class JSType
+
+
+ +
Returns:
    +
  • TernaryValue.TRUE if the comparison of values of + this type and that always succeed (such as + undefined compared to null)
  • +
  • TernaryValue.FALSE if the comparison of values of + this type and that always fails (such as + undefined compared to number)
  • +
  • TernaryValue.UNKNOWN if the comparison can succeed or + fail depending on the concrete values
  • +
+
+
+
+ +

+isNumberValueType

+
+public boolean isNumberValueType()
+
+
+
Overrides:
isNumberValueType in class JSType
+
+
+
+
+
+
+ +

+matchesNumberContext

+
+public boolean matchesNumberContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator. +

+

+
Overrides:
matchesNumberContext in class JSType
+
+
+
+
+
+
+ +

+matchesStringContext

+
+public boolean matchesStringContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator. + + All types have at least the potential for converting to String. + When we add externally defined types, such as a browser OM, we may choose + to add types that do not automatically convert to String. +

+

+
Overrides:
matchesStringContext in class JSType
+
+
+
+
+
+
+ +

+matchesObjectContext

+
+public boolean matchesObjectContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement. + + Most types we will encounter, except notably null, have at least + the potential for converting to Object. Host defined objects can + get peculiar. +

+

+
Overrides:
matchesObjectContext in class JSType
+
+
+
+
+
+
+ +

+getDisplayName

+
+public String getDisplayName()
+
+
Description copied from class: JSType
+
Returns a user meaningful label for the JSType instance. For example, + Functions and Enums will return their declaration name (if they have one). + Some types will not have a meaningful display name. Calls to + hasDisplayName() will return true IFF getDisplayName() will return null + or a zero length string. +

+

+
Overrides:
getDisplayName in class JSType
+
+
+ +
Returns:
the display name of the type, or null if one is not available
+
+
+
+ +

+getPossibleToBooleanOutcomes

+
+public BooleanLiteralSet getPossibleToBooleanOutcomes()
+
+
Description copied from class: JSType
+
Computes the set of possible outcomes of the ToBoolean predicate + for this type. The ToBoolean predicate is defined by the ECMA-262 + standard, 3rd edition. Its behavior for simple types can be + summarized by the following table: + + + + + + + + +
typeresult
undefined{false}
null{false}
boolean{true, false}
number{true, false}
string{true, false}
Object{true}
+

+

+
Specified by:
getPossibleToBooleanOutcomes in class JSType
+
+
+ +
Returns:
the set of boolean literals for this type
+
+
+
+ +

+visit

+
+public <T> T visit(Visitor<T> visitor)
+
+
Description copied from class: JSType
+
Visit this type with the given visitor. +

+

+
Specified by:
visit in class JSType
+
+
+ +
Returns:
the value returned by the visitor
See Also:
Visitor
+
+
+
+ +

+autoboxesTo

+
+public JSType autoboxesTo()
+
+
Description copied from class: JSType
+
Gets the type to which this type auto-boxes. +

+

+
Overrides:
autoboxesTo in class JSType
+
+
+ +
Returns:
the auto-boxed type or null if this type does not auto-box
+
+
+
+ +

+hasDisplayName

+
+public boolean hasDisplayName()
+
+
+
Overrides:
hasDisplayName in class JSType
+
+
+ +
Returns:
true if the JSType has a user meaningful label.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/ObjectType.Property.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/ObjectType.Property.html new file mode 100644 index 0000000..0ad2412 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/ObjectType.Property.html @@ -0,0 +1,443 @@ + + + + + +ObjectType.Property (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class ObjectType.Property

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.ObjectType.Property
+
+
+
All Implemented Interfaces:
StaticReference<JSType>, StaticSlot<JSType>, Serializable
+
+
+
Enclosing class:
ObjectType
+
+
+
+
public static final class ObjectType.Property
extends Object
implements Serializable, StaticSlot<JSType>, StaticReference<JSType>
+ + +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ ObjectType.PropertygetDeclaration() + +
+          Gets the declaration of this symbol.
+ JSDocInfogetJSDocInfo() + +
+          Gets the JSDoc for this slot.
+ StringgetName() + +
+          Gets the name of the slot.
+ NodegetNode() + +
+          The node where the reference lives.
+ StaticSourceFilegetSourceFile() + +
+          The source file where the reference lives.
+ ObjectType.PropertygetSymbol() + +
+          The variable that this reference points to.
+ JSTypegetType() + +
+          Returns the type information, if any, for this slot.
+ booleanisTypeInferred() + +
+          Returns whether the type has been inferred (as opposed to declared).
+ voidsetNode(Node n) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getName

+
+public String getName()
+
+
Description copied from interface: StaticSlot
+
Gets the name of the slot. +

+

+
Specified by:
getName in interface StaticSlot<JSType>
+
+
+
+
+
+
+ +

+getNode

+
+public Node getNode()
+
+
Description copied from interface: StaticReference
+
The node where the reference lives. +

+

+
Specified by:
getNode in interface StaticReference<JSType>
+
+
+
+
+
+
+ +

+getSourceFile

+
+public StaticSourceFile getSourceFile()
+
+
Description copied from interface: StaticReference
+
The source file where the reference lives. +

+

+
Specified by:
getSourceFile in interface StaticReference<JSType>
+
+
+
+
+
+
+ +

+getSymbol

+
+public ObjectType.Property getSymbol()
+
+
Description copied from interface: StaticReference
+
The variable that this reference points to. +

+

+
Specified by:
getSymbol in interface StaticReference<JSType>
+
+
+
+
+
+
+ +

+getDeclaration

+
+public ObjectType.Property getDeclaration()
+
+
Description copied from interface: StaticSlot
+
Gets the declaration of this symbol. May not exist. +

+

+
Specified by:
getDeclaration in interface StaticSlot<JSType>
+
+
+
+
+
+
+ +

+getType

+
+public JSType getType()
+
+
Description copied from interface: StaticSlot
+
Returns the type information, if any, for this slot. +

+

+
Specified by:
getType in interface StaticSlot<JSType>
+
+
+ +
Returns:
The type or null if no type is declared for it.
+
+
+
+ +

+isTypeInferred

+
+public boolean isTypeInferred()
+
+
Description copied from interface: StaticSlot
+
Returns whether the type has been inferred (as opposed to declared). +

+

+
Specified by:
isTypeInferred in interface StaticSlot<JSType>
+
+
+
+
+
+
+ +

+getJSDocInfo

+
+public JSDocInfo getJSDocInfo()
+
+
Description copied from interface: StaticSlot
+
Gets the JSDoc for this slot. +

+

+
Specified by:
getJSDocInfo in interface StaticSlot<JSType>
+
+
+
+
+
+
+ +

+setNode

+
+public void setNode(Node n)
+
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/ObjectType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/ObjectType.html new file mode 100644 index 0000000..5d76fff --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/ObjectType.html @@ -0,0 +1,1503 @@ + + + + + +ObjectType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class ObjectType

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType
+      extended by com.google.javascript.rhino.jstype.ObjectType
+
+
+
All Implemented Interfaces:
StaticScope<JSType>, Serializable
+
+
+
Direct Known Subclasses:
EnumElementType, EnumType, FunctionType, TemplateType, UnknownType
+
+
+
+
public abstract class ObjectType
extends JSType
implements StaticScope<JSType>
+ + +

+Object type. + + In JavaScript, all object types have properties, and each of those + properties has a type. Property types may be DECLARED, INFERRED, or + UNKNOWN. + + DECLARED properties have an explicit type annotation, as in: + + /xx @type {number} x/ + Foo.prototype.bar = 1; + + This property may only hold number values, and an assignment to any + other type of value is an error. + + INFERRED properties do not have an explicit type annotation. Rather, + we try to find all the possible types that this property can hold. + + Foo.prototype.bar = 1; + + If the programmer assigns other types of values to this property, + the property will take on the union of all these types. + + UNKNOWN properties are properties on the UNKNOWN type. The UNKNOWN + type has all properties, but we do not know whether they are + declared or inferred. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Nested Class Summary
+static classObjectType.Property + +
+           
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.JSType
JSType.TypePair
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.rhino.jstype.JSType
EMPTY_TYPE_COMPONENT, ENUMDECL, NOT_A_CLASS, NOT_A_TYPE, NOT_ENUMDECL, UNKNOWN_NAME
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static ObjectTypecast(JSType type) + +
+          A null-safe version of JSType#toObjectType.
+ voidclearCachedValues() + +
+          Clear cached values.
+static StringcreateDelegateSuffix(String suffix) + +
+          Creates a suffix for a proxy delegate.
+ booleandefineDeclaredProperty(String propertyName, + JSType type, + Node propertyNode) + +
+          Defines a property whose type is synthesized (i.e.
+ booleandefineInferredProperty(String propertyName, + JSType type, + Node propertyNode) + +
+          Defines a property whose type is inferred.
+ JSTypefindPropertyType(String propertyName) + +
+          Coerces this type to an Object type, then gets the type of the property + whose name is given.
+abstract  FunctionTypegetConstructor() + +
+          Gets this object's constructor.
+ Iterable<ObjectType>getCtorExtendedInterfaces() + +
+          Gets the interfaces extended by the interface associated with this type.
+ Iterable<ObjectType>getCtorImplementedInterfaces() + +
+          Gets the interfaces implemented by the ctor associated with this type.
+ StringgetDisplayName() + +
+          Returns a user meaningful label for the JSType instance.
+abstract  ObjectTypegetImplicitPrototype() + +
+          Gets the implicit prototype (a.k.a.
+ JSTypegetIndexType() + +
+          Gets the declared default index type.
+ JSDocInfogetJSDocInfo() + +
+          Gets the docInfo for this type.
+ StringgetNormalizedReferenceName() + +
+          Due to the complexity of some of our internal type systems, sometimes + we have different types constructed by the same constructor.
+ FunctionTypegetOwnerFunction() + +
+          Gets the owner of this if it's a function prototype.
+ JSDocInfogetOwnPropertyJSDocInfo(String propertyName) + +
+          Gets the docInfo on the specified property on this type.
+ Set<String>getOwnPropertyNames() + +
+          Returns the names of all the properties directly on this type.
+ ObjectType.PropertygetOwnSlot(String name) + +
+          Like getSlot but does not recurse into parent scopes.
+ JSTypegetParameterType() + +
+          Gets the declared default element type.
+ ObjectTypegetParentScope() + +
+          Returns the scope enclosing this one or null if none.
+ BooleanLiteralSetgetPossibleToBooleanOutcomes() + +
+          Computes the set of possible outcomes of the ToBoolean predicate + for this type.
+abstract  intgetPropertiesCount() + +
+          Gets the number of properties of this object.
+ Set<String>getPropertyNames() + +
+          Returns a list of properties defined or inferred on this type and any of + its supertypes.
+ NodegetPropertyNode(String propertyName) + +
+          Gets the node corresponding to the definition of the specified property.
+abstract  JSTypegetPropertyType(String propertyName) + +
+          Gets the property type of the property whose name is given.
+abstract  StringgetReferenceName() + +
+          Gets the reference name for this object.
+ NodegetRootNode() + +
+          Returns the root node associated with this scope.
+abstract  ObjectType.PropertygetSlot(String name) + +
+          Returns any defined slot within this scope for this name.
+ ObjectTypegetTypeOfThis() + +
+          Returns the expected type of this in the current scope.
+ booleanhasCachedValues() + +
+          Returns true if any cached values have been set for this type.
+ booleanhasOwnProperty(String propertyName) + +
+          Checks whether the property whose name is given is present directly on + the object.
+abstract  booleanhasProperty(String propertyName) + +
+          Checks whether the property whose name is given is present on the + object.
+ booleanhasReferenceName() + +
+          Returns true if the object is named.
+ booleanisFunctionPrototypeType() + +
+          Whether this is the prototype of a function.
+ booleanisNativeObjectType() + +
+          Whether this is a built-in object.
+ booleanisObject() + +
+          Tests whether this type is an Object, or any subtype thereof.
+ booleanisPropertyInExterns(String propertyName) + +
+          Checks whether the property was defined in the externs.
+abstract  booleanisPropertyTypeDeclared(String propertyName) + +
+          Checks whether the property's type is declared.
+abstract  booleanisPropertyTypeInferred(String propertyName) + +
+          Checks whether the property's type is inferred.
+ booleanisUnknownType() + +
+          We treat this as the unknown type if any of its implicit prototype + properties is unknown.
+ booleanremoveProperty(String propertyName) + +
+          Removes the declared or inferred property from this ObjectType.
+ voidsetJSDocInfo(JSDocInfo info) + +
+          Sets the docInfo for this type from the given + JSDocInfo.
+ voidsetPropertyJSDocInfo(String propertyName, + JSDocInfo info) + +
+          Sets the docInfo for the specified property from the + JSDocInfo on its definition.
+ TernaryValuetestForEquality(JSType that) + +
+          Compares this and that.
+ + + + + +
+<T> T
+
visit(Visitor<T> visitor) + +
+          Visit this type with the given visitor.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.JSType
autobox, autoboxesTo, canAssignTo, canBeCalled, canTestForEqualityWith, canTestForShallowEqualityWith, clearResolved, collapseUnion, dereference, differsFrom, equals, forceResolve, getGreatestSubtype, getLeastSupertype, getRestrictedTypeGivenToBooleanOutcome, getTypesUnderEquality, getTypesUnderInequality, getTypesUnderShallowEquality, getTypesUnderShallowInequality, hasDisplayName, hashCode, isAllType, isArrayType, isBooleanObjectType, isBooleanValueType, isCheckedUnknownType, isConstructor, isDateType, isEmptyType, isEnumElementType, isEnumType, isEquivalent, isEquivalentTo, isFunctionType, isGlobalThisType, isInstanceType, isInterface, isNominalConstructor, isNominalType, isNoObjectType, isNoResolvedType, isNoType, isNullable, isNullType, isNumber, isNumberObjectType, isNumberValueType, isOrdinaryFunction, isRecordType, isRegexpType, isResolved, isString, isStringObjectType, isStringValueType, isSubtype, isTemplateType, isUnionType, isVoidType, matchConstraint, matchesInt32Context, matchesNumberContext, matchesObjectContext, matchesStringContext, matchesUint32Context, resolve, restrictByNotNullOrUndefined, setValidator, toAnnotationString, toDebugHashCodeString, toMaybeEnumElementType, toMaybeEnumType, toMaybeFunctionType, toMaybeFunctionType, toMaybeUnionType, toObjectType, toString, unboxesTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getRootNode

+
+public Node getRootNode()
+
+
Description copied from interface: StaticScope
+
Returns the root node associated with this scope. May be null. +

+

+
Specified by:
getRootNode in interface StaticScope<JSType>
+
+
+
+
+
+
+ +

+getParentScope

+
+public ObjectType getParentScope()
+
+
Description copied from interface: StaticScope
+
Returns the scope enclosing this one or null if none. +

+

+
Specified by:
getParentScope in interface StaticScope<JSType>
+
+
+
+
+
+
+ +

+getSlot

+
+public abstract ObjectType.Property getSlot(String name)
+
+
Description copied from interface: StaticScope
+
Returns any defined slot within this scope for this name. This call + continues searching through parent scopes if a slot with this name is not + found in the current scope. +

+

+
Specified by:
getSlot in interface StaticScope<JSType>
+
+
+
Parameters:
name - The name of the variable slot to look up. +
Returns:
The defined slot for the variable, or null if no + definition exists.
+
+
+
+ +

+getOwnSlot

+
+public ObjectType.Property getOwnSlot(String name)
+
+
Description copied from interface: StaticScope
+
Like getSlot but does not recurse into parent scopes. +

+

+
Specified by:
getOwnSlot in interface StaticScope<JSType>
+
+
+
+
+
+
+ +

+getTypeOfThis

+
+public ObjectType getTypeOfThis()
+
+
Description copied from interface: StaticScope
+
Returns the expected type of this in the current scope. +

+

+
Specified by:
getTypeOfThis in interface StaticScope<JSType>
+
+
+
+
+
+
+ +

+getParameterType

+
+public JSType getParameterType()
+
+
Gets the declared default element type. +

+

+
+
+
+
See Also:
ParameterizedType
+
+
+
+ +

+getIndexType

+
+public JSType getIndexType()
+
+
Gets the declared default index type. +

+

+
+
+
+
See Also:
IndexedType
+
+
+
+ +

+getJSDocInfo

+
+public JSDocInfo getJSDocInfo()
+
+
Gets the docInfo for this type. +

+

+
Overrides:
getJSDocInfo in class JSType
+
+
+
+
+
+
+ +

+setJSDocInfo

+
+public void setJSDocInfo(JSDocInfo info)
+
+
Sets the docInfo for this type from the given + JSDocInfo. The JSDocInfo may be null. +

+

+
+
+
+
+
+
+
+ +

+getReferenceName

+
+public abstract String getReferenceName()
+
+
Gets the reference name for this object. This includes named types + like constructors, prototypes, and enums. It notably does not include + literal types like strings and booleans and structural types. +

+

+
+
+
+ +
Returns:
the object's name or null if this is an anonymous + object
+
+
+
+ +

+getNormalizedReferenceName

+
+public String getNormalizedReferenceName()
+
+
Due to the complexity of some of our internal type systems, sometimes + we have different types constructed by the same constructor. + In other parts of the type system, these are called delegates. + We construct these types by appending suffixes to the constructor name. + + The normalized reference name does not have these suffixes, and as such, + recollapses these implicit types back to their real type. +

+

+
+
+
+
+
+
+
+ +

+getDisplayName

+
+public String getDisplayName()
+
+
Description copied from class: JSType
+
Returns a user meaningful label for the JSType instance. For example, + Functions and Enums will return their declaration name (if they have one). + Some types will not have a meaningful display name. Calls to + hasDisplayName() will return true IFF getDisplayName() will return null + or a zero length string. +

+

+
Overrides:
getDisplayName in class JSType
+
+
+ +
Returns:
the display name of the type, or null if one is not available
+
+
+
+ +

+createDelegateSuffix

+
+public static String createDelegateSuffix(String suffix)
+
+
Creates a suffix for a proxy delegate. +

+

+
+
+
+
See Also:
getNormalizedReferenceName()
+
+
+
+ +

+hasReferenceName

+
+public boolean hasReferenceName()
+
+
Returns true if the object is named. +

+

+
+
+
+ +
Returns:
true if the object is named, false if it is anonymous
+
+
+
+ +

+testForEquality

+
+public TernaryValue testForEquality(JSType that)
+
+
Description copied from class: JSType
+
Compares this and that. +

+

+
Overrides:
testForEquality in class JSType
+
+
+ +
Returns:
    +
  • TernaryValue.TRUE if the comparison of values of + this type and that always succeed (such as + undefined compared to null)
  • +
  • TernaryValue.FALSE if the comparison of values of + this type and that always fails (such as + undefined compared to number)
  • +
  • TernaryValue.UNKNOWN if the comparison can succeed or + fail depending on the concrete values
  • +
+
+
+
+ +

+getConstructor

+
+public abstract FunctionType getConstructor()
+
+
Gets this object's constructor. +

+

+
+
+
+ +
Returns:
this object's constructor or null if it is a native + object (constructed natively v.s. by instantiation of a function)
+
+
+
+ +

+getImplicitPrototype

+
+public abstract ObjectType getImplicitPrototype()
+
+
Gets the implicit prototype (a.k.a. the [[Prototype]] property). +

+

+
+
+
+
+
+
+
+ +

+defineDeclaredProperty

+
+public final boolean defineDeclaredProperty(String propertyName,
+                                            JSType type,
+                                            Node propertyNode)
+
+
Defines a property whose type is synthesized (i.e. not inferred). +

+

+
+
+
+
Parameters:
propertyName - the property's name
type - the type
propertyNode - the node corresponding to the declaration of property + which might later be accessed using getPropertyNode.
+
+
+
+ +

+defineInferredProperty

+
+public final boolean defineInferredProperty(String propertyName,
+                                            JSType type,
+                                            Node propertyNode)
+
+
Defines a property whose type is inferred. +

+

+
+
+
+
Parameters:
propertyName - the property's name
type - the type
propertyNode - the node corresponding to the inferred definition of + property that might later be accessed using getPropertyNode.
+
+
+
+ +

+removeProperty

+
+public boolean removeProperty(String propertyName)
+
+
Removes the declared or inferred property from this ObjectType. +

+

+
+
+
+
Parameters:
propertyName - the property's name +
Returns:
true if the property was removed successfully. False if the + property did not exist, or could not be removed.
+
+
+
+ +

+getPropertyNode

+
+public Node getPropertyNode(String propertyName)
+
+
Gets the node corresponding to the definition of the specified property. + This could be the node corresponding to declaration of the property or the + node corresponding to the first reference to this property, e.g., + "this.propertyName" in a constructor. Note this is mainly intended to be + an estimate of where in the source code a property is defined. Sometime + the returned node is not even part of the global AST but in the AST of the + JsDoc that defines a type. +

+

+
+
+
+
Parameters:
propertyName - the name of the property +
Returns:
the Node corresponding to the property or null.
+
+
+
+ +

+getOwnPropertyJSDocInfo

+
+public JSDocInfo getOwnPropertyJSDocInfo(String propertyName)
+
+
Gets the docInfo on the specified property on this type. This should not + be done implemented recursively, as you generally need to know exactly on + which type in the prototype chain the JSDocInfo exists. +

+

+
+
+
+
+
+
+
+ +

+setPropertyJSDocInfo

+
+public void setPropertyJSDocInfo(String propertyName,
+                                 JSDocInfo info)
+
+
Sets the docInfo for the specified property from the + JSDocInfo on its definition. +

+

+
+
+
+
Parameters:
info - JSDocInfo for the property definition. May be + null.
+
+
+
+ +

+findPropertyType

+
+public JSType findPropertyType(String propertyName)
+
+
Description copied from class: JSType
+
Coerces this type to an Object type, then gets the type of the property + whose name is given. + + Unlike getPropertyType(java.lang.String), returns null if the property + is not found. +

+

+
Overrides:
findPropertyType in class JSType
+
+
+ +
Returns:
The property's type. null if the current type cannot + have properties, or if the type is not found.
+
+
+
+ +

+getPropertyType

+
+public abstract JSType getPropertyType(String propertyName)
+
+
Gets the property type of the property whose name is given. If the + underlying object does not have this property, the Unknown type is + returned to indicate that no information is available on this property. +

+

+
+
+
+ +
Returns:
the property's type or UnknownType. This method never + returns null.
+
+
+
+ +

+hasProperty

+
+public abstract boolean hasProperty(String propertyName)
+
+
Checks whether the property whose name is given is present on the + object. +

+

+
+
+
+
+
+
+
+ +

+hasOwnProperty

+
+public boolean hasOwnProperty(String propertyName)
+
+
Checks whether the property whose name is given is present directly on + the object. Returns false even if it is declared on a supertype. +

+

+
+
+
+
+
+
+
+ +

+getOwnPropertyNames

+
+public Set<String> getOwnPropertyNames()
+
+
Returns the names of all the properties directly on this type. +

+

+
+
+
+
+
+
+
+ +

+isPropertyTypeInferred

+
+public abstract boolean isPropertyTypeInferred(String propertyName)
+
+
Checks whether the property's type is inferred. +

+

+
+
+
+
+
+
+
+ +

+isPropertyTypeDeclared

+
+public abstract boolean isPropertyTypeDeclared(String propertyName)
+
+
Checks whether the property's type is declared. +

+

+
+
+
+
+
+
+
+ +

+isPropertyInExterns

+
+public boolean isPropertyInExterns(String propertyName)
+
+
Checks whether the property was defined in the externs. +

+

+
+
+
+
+
+
+
+ +

+getPropertiesCount

+
+public abstract int getPropertiesCount()
+
+
Gets the number of properties of this object. +

+

+
+
+
+
+
+
+
+ +

+getPropertyNames

+
+public Set<String> getPropertyNames()
+
+
Returns a list of properties defined or inferred on this type and any of + its supertypes. +

+

+
+
+
+
+
+
+
+ +

+visit

+
+public <T> T visit(Visitor<T> visitor)
+
+
Description copied from class: JSType
+
Visit this type with the given visitor. +

+

+
Specified by:
visit in class JSType
+
+
+ +
Returns:
the value returned by the visitor
See Also:
Visitor
+
+
+
+ +

+getPossibleToBooleanOutcomes

+
+public BooleanLiteralSet getPossibleToBooleanOutcomes()
+
+
Description copied from class: JSType
+
Computes the set of possible outcomes of the ToBoolean predicate + for this type. The ToBoolean predicate is defined by the ECMA-262 + standard, 3rd edition. Its behavior for simple types can be + summarized by the following table: + + + + + + + + +
typeresult
undefined{false}
null{false}
boolean{true, false}
number{true, false}
string{true, false}
Object{true}
+

+

+
Specified by:
getPossibleToBooleanOutcomes in class JSType
+
+
+ +
Returns:
the set of boolean literals for this type
+
+
+
+ +

+isUnknownType

+
+public boolean isUnknownType()
+
+
We treat this as the unknown type if any of its implicit prototype + properties is unknown. +

+

+
Overrides:
isUnknownType in class JSType
+
+
+
+
+
+
+ +

+isObject

+
+public boolean isObject()
+
+
Description copied from class: JSType
+
Tests whether this type is an Object, or any subtype thereof. +

+

+
Overrides:
isObject in class JSType
+
+
+ +
Returns:
this &lt;: Object
+
+
+
+ +

+hasCachedValues

+
+public boolean hasCachedValues()
+
+
Returns true if any cached values have been set for this type. If true, + then the prototype chain should not be changed, as it might invalidate the + cached values. +

+

+
+
+
+
+
+
+
+ +

+clearCachedValues

+
+public void clearCachedValues()
+
+
Clear cached values. Should be called before making changes to a prototype + that may have been changed since creation. +

+

+
+
+
+
+
+
+
+ +

+isNativeObjectType

+
+public boolean isNativeObjectType()
+
+
Whether this is a built-in object. +

+

+
+
+
+
+
+
+
+ +

+cast

+
+public static ObjectType cast(JSType type)
+
+
A null-safe version of JSType#toObjectType. +

+

+
+
+
+
+
+
+
+ +

+isFunctionPrototypeType

+
+public final boolean isFunctionPrototypeType()
+
+
Description copied from class: JSType
+
Whether this is the prototype of a function. +

+

+
Overrides:
isFunctionPrototypeType in class JSType
+
+
+
+
+
+
+ +

+getOwnerFunction

+
+public FunctionType getOwnerFunction()
+
+
Gets the owner of this if it's a function prototype. +

+

+
+
+
+
+
+
+
+ +

+getCtorImplementedInterfaces

+
+public Iterable<ObjectType> getCtorImplementedInterfaces()
+
+
Gets the interfaces implemented by the ctor associated with this type. + Intended to be overridden by subclasses. +

+

+
+
+
+
+
+
+
+ +

+getCtorExtendedInterfaces

+
+public Iterable<ObjectType> getCtorExtendedInterfaces()
+
+
Gets the interfaces extended by the interface associated with this type. + Intended to be overriden by subclasses. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/RecordTypeBuilder.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/RecordTypeBuilder.html new file mode 100644 index 0000000..19860e6 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/RecordTypeBuilder.html @@ -0,0 +1,298 @@ + + + + + +RecordTypeBuilder (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class RecordTypeBuilder

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.RecordTypeBuilder
+
+
+
+
public class RecordTypeBuilder
extends Object
+ + +

+A builder for record types. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
RecordTypeBuilder(JSTypeRegistry registry) + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+ RecordTypeBuilderaddProperty(String name, + JSType type, + Node propertyNode) + +
+          Adds a property with the given name and type to the record type.
+ JSTypebuild() + +
+          Creates a record.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+RecordTypeBuilder

+
+public RecordTypeBuilder(JSTypeRegistry registry)
+
+
+ + + + + + + + +
+Method Detail
+ +

+addProperty

+
+public RecordTypeBuilder addProperty(String name,
+                                     JSType type,
+                                     Node propertyNode)
+
+
Adds a property with the given name and type to the record type. +

+

+
Parameters:
name - the name of the new property
type - the JSType of the new property
propertyNode - the node that holds this property definition +
Returns:
The builder itself for chaining purposes, or null if there's + a duplicate.
+
+
+
+ +

+build

+
+public JSType build()
+
+
Creates a record. +

+

+ +
Returns:
The record type.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/SimpleReference.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/SimpleReference.html new file mode 100644 index 0000000..5dae212 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/SimpleReference.html @@ -0,0 +1,354 @@ + + + + + +SimpleReference (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class SimpleReference<T extends StaticSlot<JSType>>

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.SimpleReference<T>
+
+
+
All Implemented Interfaces:
StaticReference<JSType>
+
+
+
Direct Known Subclasses:
SymbolTable.Reference
+
+
+
+
public class SimpleReference<T extends StaticSlot<JSType>>
extends Object
implements StaticReference<JSType>
+ + +

+A simple immutable reference. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
SimpleReference(T symbol, + Node node) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ NodegetNode() + +
+          The node where the reference lives.
+ StaticSourceFilegetSourceFile() + +
+          The source file where the reference lives.
+ TgetSymbol() + +
+          The variable that this reference points to.
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SimpleReference

+
+public SimpleReference(T symbol,
+                       Node node)
+
+
+ + + + + + + + +
+Method Detail
+ +

+getSymbol

+
+public final T getSymbol()
+
+
Description copied from interface: StaticReference
+
The variable that this reference points to. +

+

+
Specified by:
getSymbol in interface StaticReference<JSType>
+
+
+
+
+
+
+ +

+getNode

+
+public final Node getNode()
+
+
Description copied from interface: StaticReference
+
The node where the reference lives. +

+

+
Specified by:
getNode in interface StaticReference<JSType>
+
+
+
+
+
+
+ +

+getSourceFile

+
+public final StaticSourceFile getSourceFile()
+
+
Description copied from interface: StaticReference
+
The source file where the reference lives. +

+

+
Specified by:
getSourceFile in interface StaticReference<JSType>
+
+
+
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/SimpleSlot.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/SimpleSlot.html new file mode 100644 index 0000000..2f5eb33 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/SimpleSlot.html @@ -0,0 +1,387 @@ + + + + + +SimpleSlot (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class SimpleSlot

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.SimpleSlot
+
+
+
All Implemented Interfaces:
StaticSlot<JSType>, Serializable
+
+
+
Direct Known Subclasses:
SymbolTable.Symbol
+
+
+
+
public class SimpleSlot
extends Object
implements StaticSlot<JSType>, Serializable
+ + +

+The minimum implementation of StaticSlot. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Constructor Summary
SimpleSlot(String name, + JSType type, + boolean inferred) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StaticReference<JSType>getDeclaration() + +
+          Gets the declaration of this symbol.
+ JSDocInfogetJSDocInfo() + +
+          Gets the JSDoc for this slot.
+ StringgetName() + +
+          Gets the name of the slot.
+ JSTypegetType() + +
+          Returns the type information, if any, for this slot.
+ booleanisTypeInferred() + +
+          Returns whether the type has been inferred (as opposed to declared).
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SimpleSlot

+
+public SimpleSlot(String name,
+                  JSType type,
+                  boolean inferred)
+
+
+ + + + + + + + +
+Method Detail
+ +

+getName

+
+public String getName()
+
+
Description copied from interface: StaticSlot
+
Gets the name of the slot. +

+

+
Specified by:
getName in interface StaticSlot<JSType>
+
+
+
+
+
+
+ +

+getType

+
+public JSType getType()
+
+
Description copied from interface: StaticSlot
+
Returns the type information, if any, for this slot. +

+

+
Specified by:
getType in interface StaticSlot<JSType>
+
+
+ +
Returns:
The type or null if no type is declared for it.
+
+
+
+ +

+isTypeInferred

+
+public boolean isTypeInferred()
+
+
Description copied from interface: StaticSlot
+
Returns whether the type has been inferred (as opposed to declared). +

+

+
Specified by:
isTypeInferred in interface StaticSlot<JSType>
+
+
+
+
+
+
+ +

+getDeclaration

+
+public StaticReference<JSType> getDeclaration()
+
+
Description copied from interface: StaticSlot
+
Gets the declaration of this symbol. May not exist. +

+

+
Specified by:
getDeclaration in interface StaticSlot<JSType>
+
+
+
+
+
+
+ +

+getJSDocInfo

+
+public JSDocInfo getJSDocInfo()
+
+
Description copied from interface: StaticSlot
+
Gets the JSDoc for this slot. +

+

+
Specified by:
getJSDocInfo in interface StaticSlot<JSType>
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/SimpleSourceFile.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/SimpleSourceFile.html new file mode 100644 index 0000000..8db2122 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/SimpleSourceFile.html @@ -0,0 +1,357 @@ + + + + + +SimpleSourceFile (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class SimpleSourceFile

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.SimpleSourceFile
+
+
+
All Implemented Interfaces:
StaticSourceFile
+
+
+
+
public final class SimpleSourceFile
extends Object
implements StaticSourceFile
+ + +

+A simple implementation of StaticSourceFile for testing. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
SimpleSourceFile(String name, + boolean extern) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ intgetLineOffset(int line) + +
+          Returns the offset of the given line number relative to the file start.
+ StringgetName() + +
+          The name of the file.
+ booleanisExtern() + +
+          Returns whether this is an externs file.
+ StringtoString() + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SimpleSourceFile

+
+public SimpleSourceFile(String name,
+                        boolean extern)
+
+
+ + + + + + + + +
+Method Detail
+ +

+getName

+
+public String getName()
+
+
Description copied from interface: StaticSourceFile
+
The name of the file. Must be unique across all files in the compilation. +

+

+
Specified by:
getName in interface StaticSourceFile
+
+
+
+
+
+
+ +

+isExtern

+
+public boolean isExtern()
+
+
Description copied from interface: StaticSourceFile
+
Returns whether this is an externs file. +

+

+
Specified by:
isExtern in interface StaticSourceFile
+
+
+
+
+
+
+ +

+getLineOffset

+
+public int getLineOffset(int line)
+
+
Description copied from interface: StaticSourceFile
+
Returns the offset of the given line number relative to the file start. + Line number should be 1-based. + + If the source file doesn't have line information, it should return + Integer.MIN_VALUE. The negative offsets will make it more obvious + what happened. +

+

+
Specified by:
getLineOffset in interface StaticSourceFile
+
+
+
Parameters:
line - the line of the input to get the absolute offset of. +
Returns:
the absolute offset of the start of the provided line.
+
+
+
+ +

+toString

+
+public String toString()
+
+
+
Overrides:
toString in class Object
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StaticReference.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StaticReference.html new file mode 100644 index 0000000..9fadefd --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StaticReference.html @@ -0,0 +1,270 @@ + + + + + +StaticReference (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Interface StaticReference<T>

+
+
All Known Implementing Classes:
ObjectType.Property, Scope.Arguments, Scope.Var, SimpleReference, SymbolTable.Reference
+
+
+
+
public interface StaticReference<T>
+ + +

+The StaticReference tells us all the ways that a StaticSlot + is used in a program. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ NodegetNode() + +
+          The node where the reference lives.
+ StaticSourceFilegetSourceFile() + +
+          The source file where the reference lives.
+ StaticSlot<T>getSymbol() + +
+          The variable that this reference points to.
+  +

+ + + + + + + + +
+Method Detail
+ +

+getSymbol

+
+StaticSlot<T> getSymbol()
+
+
The variable that this reference points to. +

+

+
+
+
+
+ +

+getNode

+
+Node getNode()
+
+
The node where the reference lives. +

+

+
+
+
+
+ +

+getSourceFile

+
+StaticSourceFile getSourceFile()
+
+
The source file where the reference lives. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StaticScope.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StaticScope.html new file mode 100644 index 0000000..80ba6da --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StaticScope.html @@ -0,0 +1,320 @@ + + + + + +StaticScope (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Interface StaticScope<T>

+
+
Type Parameters:
T - The type of information stored about the slot
+
+
All Known Implementing Classes:
AbstractStaticScope, EnumElementType, EnumType, FunctionType, MapBasedScope, NoObjectType, NoType, ObjectType, Scope, SymbolTable.SymbolScope, TemplateType, UnknownType
+
+
+
+
public interface StaticScope<T>
+ + +

+The StaticScope interface must be implemented by any object that + defines variables for the purposes of static analysis. It is distinguished + from the Scriptable class that Rhino normally uses to represent a + runtime scope. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StaticSlot<T>getOwnSlot(String name) + +
+          Like getSlot but does not recurse into parent scopes.
+ StaticScope<T>getParentScope() + +
+          Returns the scope enclosing this one or null if none.
+ NodegetRootNode() + +
+          Returns the root node associated with this scope.
+ StaticSlot<T>getSlot(String name) + +
+          Returns any defined slot within this scope for this name.
+ TgetTypeOfThis() + +
+          Returns the expected type of this in the current scope.
+  +

+ + + + + + + + +
+Method Detail
+ +

+getRootNode

+
+Node getRootNode()
+
+
Returns the root node associated with this scope. May be null. +

+

+
+
+
+
+ +

+getParentScope

+
+StaticScope<T> getParentScope()
+
+
Returns the scope enclosing this one or null if none. +

+

+
+
+
+
+ +

+getSlot

+
+StaticSlot<T> getSlot(String name)
+
+
Returns any defined slot within this scope for this name. This call + continues searching through parent scopes if a slot with this name is not + found in the current scope. +

+

+
Parameters:
name - The name of the variable slot to look up. +
Returns:
The defined slot for the variable, or null if no + definition exists.
+
+
+
+ +

+getOwnSlot

+
+StaticSlot<T> getOwnSlot(String name)
+
+
Like getSlot but does not recurse into parent scopes. +

+

+
+
+
+
+ +

+getTypeOfThis

+
+T getTypeOfThis()
+
+
Returns the expected type of this in the current scope. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StaticSlot.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StaticSlot.html new file mode 100644 index 0000000..e883351 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StaticSlot.html @@ -0,0 +1,315 @@ + + + + + +StaticSlot (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Interface StaticSlot<T>

+
+
Type Parameters:
T - The type of information stored about the slot
+
+
All Known Implementing Classes:
ObjectType.Property, Scope.Arguments, Scope.Var, SimpleSlot, SymbolTable.Symbol
+
+
+
+
public interface StaticSlot<T>
+ + +

+The StaticSlot interface must be implemented by variables that can + appear as members of a StaticScope. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StaticReference<T>getDeclaration() + +
+          Gets the declaration of this symbol.
+ JSDocInfogetJSDocInfo() + +
+          Gets the JSDoc for this slot.
+ StringgetName() + +
+          Gets the name of the slot.
+ TgetType() + +
+          Returns the type information, if any, for this slot.
+ booleanisTypeInferred() + +
+          Returns whether the type has been inferred (as opposed to declared).
+  +

+ + + + + + + + +
+Method Detail
+ +

+getName

+
+String getName()
+
+
Gets the name of the slot. +

+

+
+
+
+
+ +

+getType

+
+T getType()
+
+
Returns the type information, if any, for this slot. +

+

+ +
Returns:
The type or null if no type is declared for it.
+
+
+
+ +

+isTypeInferred

+
+boolean isTypeInferred()
+
+
Returns whether the type has been inferred (as opposed to declared). +

+

+
+
+
+
+ +

+getDeclaration

+
+StaticReference<T> getDeclaration()
+
+
Gets the declaration of this symbol. May not exist. +

+

+
+
+
+
+ +

+getJSDocInfo

+
+JSDocInfo getJSDocInfo()
+
+
Gets the JSDoc for this slot. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StaticSourceFile.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StaticSourceFile.html new file mode 100644 index 0000000..c4a8159 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StaticSourceFile.html @@ -0,0 +1,278 @@ + + + + + +StaticSourceFile (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Interface StaticSourceFile

+
+
All Known Implementing Classes:
JSSourceFile, SimpleSourceFile, SourceFile
+
+
+
+
public interface StaticSourceFile
+ + +

+The StaticSourceFile contains information about a compiler input. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ intgetLineOffset(int lineNumber) + +
+          Returns the offset of the given line number relative to the file start.
+ StringgetName() + +
+          The name of the file.
+ booleanisExtern() + +
+          Returns whether this is an externs file.
+  +

+ + + + + + + + +
+Method Detail
+ +

+getName

+
+String getName()
+
+
The name of the file. Must be unique across all files in the compilation. +

+

+
+
+
+
+ +

+isExtern

+
+boolean isExtern()
+
+
Returns whether this is an externs file. +

+

+
+
+
+
+ +

+getLineOffset

+
+int getLineOffset(int lineNumber)
+
+
Returns the offset of the given line number relative to the file start. + Line number should be 1-based. + + If the source file doesn't have line information, it should return + Integer.MIN_VALUE. The negative offsets will make it more obvious + what happened. +

+

+
Parameters:
lineNumber - the line of the input to get the absolute offset of. +
Returns:
the absolute offset of the start of the provided line. +
Throws: +
IllegalArgumentException - if lineno is less than 1 or greater than + the number of lines in the source.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StaticSymbolTable.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StaticSymbolTable.html new file mode 100644 index 0000000..d410206 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StaticSymbolTable.html @@ -0,0 +1,269 @@ + + + + + +StaticSymbolTable (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Interface StaticSymbolTable<S extends StaticSlot<JSType>,R extends StaticReference<JSType>>

+
+
All Known Implementing Classes:
Scope, SymbolTable
+
+
+
+
public interface StaticSymbolTable<S extends StaticSlot<JSType>,R extends StaticReference<JSType>>
+ + +

+Lookup references by the symbols that they refer to. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ Iterable<S>getAllSymbols() + +
+          Returns all variables in this symbol table.
+ Iterable<R>getReferences(S symbol) + +
+          Returns the references that point to the given symbol.
+ StaticScope<JSType>getScope(S symbol) + +
+          Returns the scope for a given symbol.
+  +

+ + + + + + + + +
+Method Detail
+ +

+getReferences

+
+Iterable<R> getReferences(S symbol)
+
+
Returns the references that point to the given symbol. +

+

+
+
+
+
+ +

+getScope

+
+StaticScope<JSType> getScope(S symbol)
+
+
Returns the scope for a given symbol. +

+

+
+
+
+
+ +

+getAllSymbols

+
+Iterable<S> getAllSymbols()
+
+
Returns all variables in this symbol table. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StringType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StringType.html new file mode 100644 index 0000000..709aec2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/StringType.html @@ -0,0 +1,565 @@ + + + + + +StringType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class StringType

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType
+      extended by com.google.javascript.rhino.jstype.StringType
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public final class StringType
extends JSType
+ + +

+String type. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.JSType
JSType.TypePair
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.rhino.jstype.JSType
EMPTY_TYPE_COMPONENT, ENUMDECL, NOT_A_CLASS, NOT_A_TYPE, NOT_ENUMDECL, UNKNOWN_NAME
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ JSTypeautoboxesTo() + +
+          Gets the type to which this type auto-boxes.
+ StringgetDisplayName() + +
+          Returns a user meaningful label for the JSType instance.
+ BooleanLiteralSetgetPossibleToBooleanOutcomes() + +
+          Computes the set of possible outcomes of the ToBoolean predicate + for this type.
+ booleanhasDisplayName() + +
+           
+ booleanisStringValueType() + +
+           
+ booleanmatchesNumberContext() + +
+          This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator.
+ booleanmatchesObjectContext() + +
+          This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement.
+ booleanmatchesStringContext() + +
+          This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator.
+ TernaryValuetestForEquality(JSType that) + +
+          Compares this and that.
+ + + + + +
+<T> T
+
visit(Visitor<T> visitor) + +
+          Visit this type with the given visitor.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.JSType
autobox, canAssignTo, canBeCalled, canTestForEqualityWith, canTestForShallowEqualityWith, clearResolved, collapseUnion, dereference, differsFrom, equals, findPropertyType, forceResolve, getGreatestSubtype, getJSDocInfo, getLeastSupertype, getRestrictedTypeGivenToBooleanOutcome, getTypesUnderEquality, getTypesUnderInequality, getTypesUnderShallowEquality, getTypesUnderShallowInequality, hashCode, isAllType, isArrayType, isBooleanObjectType, isBooleanValueType, isCheckedUnknownType, isConstructor, isDateType, isEmptyType, isEnumElementType, isEnumType, isEquivalent, isEquivalentTo, isFunctionPrototypeType, isFunctionType, isGlobalThisType, isInstanceType, isInterface, isNominalConstructor, isNominalType, isNoObjectType, isNoResolvedType, isNoType, isNullable, isNullType, isNumber, isNumberObjectType, isNumberValueType, isObject, isOrdinaryFunction, isRecordType, isRegexpType, isResolved, isString, isStringObjectType, isSubtype, isTemplateType, isUnionType, isUnknownType, isVoidType, matchConstraint, matchesInt32Context, matchesUint32Context, resolve, restrictByNotNullOrUndefined, setValidator, toAnnotationString, toDebugHashCodeString, toMaybeEnumElementType, toMaybeEnumType, toMaybeFunctionType, toMaybeFunctionType, toMaybeUnionType, toObjectType, toString, unboxesTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+testForEquality

+
+public TernaryValue testForEquality(JSType that)
+
+
Description copied from class: JSType
+
Compares this and that. +

+

+
Overrides:
testForEquality in class JSType
+
+
+ +
Returns:
    +
  • TernaryValue.TRUE if the comparison of values of + this type and that always succeed (such as + undefined compared to null)
  • +
  • TernaryValue.FALSE if the comparison of values of + this type and that always fails (such as + undefined compared to number)
  • +
  • TernaryValue.UNKNOWN if the comparison can succeed or + fail depending on the concrete values
  • +
+
+
+
+ +

+isStringValueType

+
+public boolean isStringValueType()
+
+
+
Overrides:
isStringValueType in class JSType
+
+
+
+
+
+
+ +

+matchesNumberContext

+
+public boolean matchesNumberContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator. +

+

+
Overrides:
matchesNumberContext in class JSType
+
+
+
+
+
+
+ +

+matchesStringContext

+
+public boolean matchesStringContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator. + + All types have at least the potential for converting to String. + When we add externally defined types, such as a browser OM, we may choose + to add types that do not automatically convert to String. +

+

+
Overrides:
matchesStringContext in class JSType
+
+
+
+
+
+
+ +

+matchesObjectContext

+
+public boolean matchesObjectContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement. + + Most types we will encounter, except notably null, have at least + the potential for converting to Object. Host defined objects can + get peculiar. +

+

+
Overrides:
matchesObjectContext in class JSType
+
+
+
+
+
+
+ +

+getDisplayName

+
+public String getDisplayName()
+
+
Description copied from class: JSType
+
Returns a user meaningful label for the JSType instance. For example, + Functions and Enums will return their declaration name (if they have one). + Some types will not have a meaningful display name. Calls to + hasDisplayName() will return true IFF getDisplayName() will return null + or a zero length string. +

+

+
Overrides:
getDisplayName in class JSType
+
+
+ +
Returns:
the display name of the type, or null if one is not available
+
+
+
+ +

+autoboxesTo

+
+public JSType autoboxesTo()
+
+
Description copied from class: JSType
+
Gets the type to which this type auto-boxes. +

+

+
Overrides:
autoboxesTo in class JSType
+
+
+ +
Returns:
the auto-boxed type or null if this type does not auto-box
+
+
+
+ +

+getPossibleToBooleanOutcomes

+
+public BooleanLiteralSet getPossibleToBooleanOutcomes()
+
+
Description copied from class: JSType
+
Computes the set of possible outcomes of the ToBoolean predicate + for this type. The ToBoolean predicate is defined by the ECMA-262 + standard, 3rd edition. Its behavior for simple types can be + summarized by the following table: + + + + + + + + +
typeresult
undefined{false}
null{false}
boolean{true, false}
number{true, false}
string{true, false}
Object{true}
+

+

+
Specified by:
getPossibleToBooleanOutcomes in class JSType
+
+
+ +
Returns:
the set of boolean literals for this type
+
+
+
+ +

+visit

+
+public <T> T visit(Visitor<T> visitor)
+
+
Description copied from class: JSType
+
Visit this type with the given visitor. +

+

+
Specified by:
visit in class JSType
+
+
+ +
Returns:
the value returned by the visitor
See Also:
Visitor
+
+
+
+ +

+hasDisplayName

+
+public boolean hasDisplayName()
+
+
+
Overrides:
hasDisplayName in class JSType
+
+
+ +
Returns:
true if the JSType has a user meaningful label.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/TemplateType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/TemplateType.html new file mode 100644 index 0000000..0444e09 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/TemplateType.html @@ -0,0 +1,1824 @@ + + + + + +TemplateType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class TemplateType

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType
+      extended by com.google.javascript.rhino.jstype.ObjectType
+          extended by com.google.javascript.rhino.jstype.TemplateType
+
+
+
All Implemented Interfaces:
StaticScope<JSType>, Serializable
+
+
+
+
public class TemplateType
extends ObjectType
+ + +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.ObjectType
ObjectType.Property
+  + + + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.JSType
JSType.TypePair
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.rhino.jstype.JSType
EMPTY_TYPE_COMPONENT, ENUMDECL, NOT_A_CLASS, NOT_A_TYPE, NOT_ENUMDECL, UNKNOWN_NAME
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleancanAssignTo(JSType that) + +
+          Tests whether values of this type can be safely assigned + to values of that type.
+ booleancanBeCalled() + +
+          This predicate is used to test whether a given type can be used as the + 'function' in a function call.
+ JSTypecollapseUnion() + +
+          Gets the least supertype of this that's not a union.
+protected  voidcollectPropertyNames(Set<String> props) + +
+          Adds any properties defined on this type or its supertypes to the set.
+ JSTypefindPropertyType(String propertyName) + +
+          Coerces this type to an Object type, then gets the type of the property + whose name is given.
+ FunctionTypegetConstructor() + +
+          Gets this object's constructor.
+ Iterable<ObjectType>getCtorImplementedInterfaces() + +
+          Gets the interfaces implemented by the ctor associated with this type.
+ ObjectTypegetImplicitPrototype() + +
+          Gets the implicit prototype (a.k.a.
+ JSTypegetIndexType() + +
+          Gets the declared default index type.
+ JSDocInfogetJSDocInfo() + +
+          Gets the docInfo for this type.
+ FunctionTypegetOwnerFunction() + +
+          Gets the owner of this if it's a function prototype.
+ JSDocInfogetOwnPropertyJSDocInfo(String propertyName) + +
+          Gets the docInfo on the specified property on this type.
+ Set<String>getOwnPropertyNames() + +
+          Returns the names of all the properties directly on this type.
+ JSTypegetParameterType() + +
+          Gets the declared default element type.
+ intgetPropertiesCount() + +
+          Gets the number of properties of this object.
+ NodegetPropertyNode(String propertyName) + +
+          Gets the node corresponding to the definition of the specified property.
+ JSTypegetPropertyType(String propertyName) + +
+          Gets the property type of the property whose name is given.
+ StringgetReferenceName() + +
+          Gets the reference name for this object.
+ ObjectType.PropertygetSlot(String name) + +
+          Returns any defined slot within this scope for this name.
+ ObjectTypegetTypeOfThis() + +
+          Returns the expected type of this in the current scope.
+ inthashCode() + +
+           
+ booleanhasOwnProperty(String propertyName) + +
+          Checks whether the property whose name is given is present directly on + the object.
+ booleanhasProperty(String propertyName) + +
+          Checks whether the property whose name is given is present on the + object.
+ booleanhasReferenceName() + +
+          Returns true if the object is named.
+ booleanisAllType() + +
+           
+ booleanisCheckedUnknownType() + +
+           
+ booleanisConstructor() + +
+          Whether this type is a FunctionType that is a constructor or a + named type that points to such a type.
+ booleanisEquivalentTo(JSType that) + +
+          Checks if two types are equivalent.
+ booleanisInstanceType() + +
+          Whether this type is an Instance object of some constructor.
+ booleanisInterface() + +
+          Whether this type is a FunctionType that is an interface or a named + type that points to such a type.
+ booleanisNativeObjectType() + +
+          Whether this is a built-in object.
+ booleanisNominalType() + +
+          Whether this type is a nominal type (a named instance object or + a named enum).
+ booleanisNoObjectType() + +
+           
+ booleanisNoResolvedType() + +
+           
+ booleanisNoType() + +
+           
+ booleanisNullable() + +
+          Tests whether this type is nullable.
+ booleanisOrdinaryFunction() + +
+          Whether this type is a FunctionType that is an ordinary function or + a named type that points to such a type.
+ booleanisPropertyInExterns(String propertyName) + +
+          Checks whether the property was defined in the externs.
+ booleanisPropertyTypeDeclared(String propertyName) + +
+          Checks whether the property's type is declared.
+ booleanisPropertyTypeInferred(String propertyName) + +
+          Checks whether the property's type is inferred.
+ booleanisSubtype(JSType that) + +
+          Checks whether this is a subtype of that.
+ booleanisTemplateType() + +
+           
+ booleanisUnknownType() + +
+          We treat this as the unknown type if any of its implicit prototype + properties is unknown.
+ voidmatchConstraint(ObjectType contraint) + +
+          Modify this type so that it matches the specified type.
+ booleanmatchesNumberContext() + +
+          This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator.
+ booleanmatchesObjectContext() + +
+          This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement.
+ booleanmatchesStringContext() + +
+          This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator.
+ booleanremoveProperty(String name) + +
+          Removes the declared or inferred property from this ObjectType.
+ voidsetJSDocInfo(JSDocInfo info) + +
+          Sets the docInfo for this type from the given + JSDocInfo.
+ voidsetPropertyJSDocInfo(String propertyName, + JSDocInfo info) + +
+          Sets the docInfo for the specified property from the + JSDocInfo on its definition.
+ TernaryValuetestForEquality(JSType that) + +
+          Compares this and that.
+ StringtoDebugHashCodeString() + +
+          A hash code function for diagnosing complicated issues + around type-identity.
+ EnumElementTypetoMaybeEnumElementType() + +
+          Downcasts this to an EnumElementType, or returns null if this is not an EnumElementType.
+ EnumTypetoMaybeEnumType() + +
+          Downcasts this to an EnumType, or returns null if this is not an EnumType.
+ FunctionTypetoMaybeFunctionType() + +
+          Downcasts this to a FunctionType, or returns null if this is not + a function.
+ UnionTypetoMaybeUnionType() + +
+          Downcasts this to a UnionType, or returns null if this is not a UnionType.
+ + + + + +
+<T> T
+
visit(Visitor<T> visitor) + +
+          Visit this type with the given visitor.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.ObjectType
cast, clearCachedValues, createDelegateSuffix, defineDeclaredProperty, defineInferredProperty, getCtorExtendedInterfaces, getDisplayName, getNormalizedReferenceName, getOwnSlot, getParentScope, getPossibleToBooleanOutcomes, getPropertyNames, getRootNode, hasCachedValues, isFunctionPrototypeType, isObject
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.JSType
autobox, autoboxesTo, canTestForEqualityWith, canTestForShallowEqualityWith, clearResolved, dereference, differsFrom, equals, forceResolve, getGreatestSubtype, getLeastSupertype, getRestrictedTypeGivenToBooleanOutcome, getTypesUnderEquality, getTypesUnderInequality, getTypesUnderShallowEquality, getTypesUnderShallowInequality, hasDisplayName, isArrayType, isBooleanObjectType, isBooleanValueType, isDateType, isEmptyType, isEnumElementType, isEnumType, isEquivalent, isFunctionType, isGlobalThisType, isNominalConstructor, isNullType, isNumber, isNumberObjectType, isNumberValueType, isRecordType, isRegexpType, isResolved, isString, isStringObjectType, isStringValueType, isUnionType, isVoidType, matchesInt32Context, matchesUint32Context, resolve, restrictByNotNullOrUndefined, setValidator, toAnnotationString, toMaybeFunctionType, toObjectType, toString, unboxesTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getReferenceName

+
+public String getReferenceName()
+
+
Description copied from class: ObjectType
+
Gets the reference name for this object. This includes named types + like constructors, prototypes, and enums. It notably does not include + literal types like strings and booleans and structural types. +

+

+
+
+
+ +
Returns:
the object's name or null if this is an anonymous + object
+
+
+
+ +

+isTemplateType

+
+public boolean isTemplateType()
+
+
+
Overrides:
isTemplateType in class JSType
+
+
+
+
+
+
+ +

+getSlot

+
+public ObjectType.Property getSlot(String name)
+
+
Description copied from interface: StaticScope
+
Returns any defined slot within this scope for this name. This call + continues searching through parent scopes if a slot with this name is not + found in the current scope. +

+

+
Specified by:
getSlot in interface StaticScope<JSType>
Specified by:
getSlot in class ObjectType
+
+
+
Parameters:
name - The name of the variable slot to look up. +
Returns:
The defined slot for the variable, or null if no + definition exists.
+
+
+
+ +

+hasReferenceName

+
+public boolean hasReferenceName()
+
+
Description copied from class: ObjectType
+
Returns true if the object is named. +

+

+
Overrides:
hasReferenceName in class ObjectType
+
+
+ +
Returns:
true if the object is named, false if it is anonymous
+
+
+
+ +

+matchesNumberContext

+
+public boolean matchesNumberContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator. +

+

+
Overrides:
matchesNumberContext in class JSType
+
+
+
+
+
+
+ +

+matchesStringContext

+
+public boolean matchesStringContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator. + + All types have at least the potential for converting to String. + When we add externally defined types, such as a browser OM, we may choose + to add types that do not automatically convert to String. +

+

+
Overrides:
matchesStringContext in class JSType
+
+
+
+
+
+
+ +

+matchesObjectContext

+
+public boolean matchesObjectContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement. + + Most types we will encounter, except notably null, have at least + the potential for converting to Object. Host defined objects can + get peculiar. +

+

+
Overrides:
matchesObjectContext in class JSType
+
+
+
+
+
+
+ +

+canBeCalled

+
+public boolean canBeCalled()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can be used as the + 'function' in a function call. +

+

+
Overrides:
canBeCalled in class JSType
+
+
+ +
Returns:
true if this type might be callable.
+
+
+
+ +

+isNoType

+
+public boolean isNoType()
+
+
+
Overrides:
isNoType in class JSType
+
+
+
+
+
+
+ +

+isNoObjectType

+
+public boolean isNoObjectType()
+
+
+
Overrides:
isNoObjectType in class JSType
+
+
+
+
+
+
+ +

+isNoResolvedType

+
+public boolean isNoResolvedType()
+
+
+
Overrides:
isNoResolvedType in class JSType
+
+
+
+
+
+
+ +

+isUnknownType

+
+public boolean isUnknownType()
+
+
Description copied from class: ObjectType
+
We treat this as the unknown type if any of its implicit prototype + properties is unknown. +

+

+
Overrides:
isUnknownType in class ObjectType
+
+
+
+
+
+
+ +

+isCheckedUnknownType

+
+public boolean isCheckedUnknownType()
+
+
+
Overrides:
isCheckedUnknownType in class JSType
+
+
+
+
+
+
+ +

+isNullable

+
+public boolean isNullable()
+
+
Description copied from class: JSType
+
Tests whether this type is nullable. +

+

+
Overrides:
isNullable in class JSType
+
+
+
+
+
+
+ +

+toMaybeEnumType

+
+public EnumType toMaybeEnumType()
+
+
Description copied from class: JSType
+
Downcasts this to an EnumType, or returns null if this is not an EnumType. +

+

+
Overrides:
toMaybeEnumType in class JSType
+
+
+
+
+
+
+ +

+isConstructor

+
+public boolean isConstructor()
+
+
Description copied from class: JSType
+
Whether this type is a FunctionType that is a constructor or a + named type that points to such a type. +

+

+
Overrides:
isConstructor in class JSType
+
+
+
+
+
+
+ +

+isNominalType

+
+public boolean isNominalType()
+
+
Description copied from class: JSType
+
Whether this type is a nominal type (a named instance object or + a named enum). +

+

+
Overrides:
isNominalType in class JSType
+
+
+
+
+
+
+ +

+isInstanceType

+
+public boolean isInstanceType()
+
+
Description copied from class: JSType
+
Whether this type is an Instance object of some constructor. + Does not necessarily mean this is an InstanceObjectType. +

+

+
Overrides:
isInstanceType in class JSType
+
+
+
+
+
+
+ +

+isInterface

+
+public boolean isInterface()
+
+
Description copied from class: JSType
+
Whether this type is a FunctionType that is an interface or a named + type that points to such a type. +

+

+
Overrides:
isInterface in class JSType
+
+
+
+
+
+
+ +

+isOrdinaryFunction

+
+public boolean isOrdinaryFunction()
+
+
Description copied from class: JSType
+
Whether this type is a FunctionType that is an ordinary function or + a named type that points to such a type. +

+

+
Overrides:
isOrdinaryFunction in class JSType
+
+
+
+
+
+
+ +

+isAllType

+
+public boolean isAllType()
+
+
+
Overrides:
isAllType in class JSType
+
+
+
+
+
+
+ +

+isNativeObjectType

+
+public boolean isNativeObjectType()
+
+
Description copied from class: ObjectType
+
Whether this is a built-in object. +

+

+
Overrides:
isNativeObjectType in class ObjectType
+
+
+
+
+
+
+ +

+toMaybeUnionType

+
+public UnionType toMaybeUnionType()
+
+
Description copied from class: JSType
+
Downcasts this to a UnionType, or returns null if this is not a UnionType. + + Named in honor of Haskell's Maybe type constructor. +

+

+
Overrides:
toMaybeUnionType in class JSType
+
+
+
+
+
+
+ +

+toMaybeFunctionType

+
+public FunctionType toMaybeFunctionType()
+
+
Description copied from class: JSType
+
Downcasts this to a FunctionType, or returns null if this is not + a function. + + For the purposes of this function, we define a MaybeFunctionType as any + type in the sub-lattice + { x | LEAST_FUNCTION_TYPE <= x <= GREATEST_FUNCTION_TYPE } + This definition excludes bottom types like NoType and NoObjectType. + + This definition is somewhat arbitrary and axiomatic, but this is the + definition that makes the most sense for the most callers. +

+

+
Overrides:
toMaybeFunctionType in class JSType
+
+
+
+
+
+
+ +

+toMaybeEnumElementType

+
+public EnumElementType toMaybeEnumElementType()
+
+
Description copied from class: JSType
+
Downcasts this to an EnumElementType, or returns null if this is not an EnumElementType. +

+

+
Overrides:
toMaybeEnumElementType in class JSType
+
+
+
+
+
+
+ +

+testForEquality

+
+public TernaryValue testForEquality(JSType that)
+
+
Description copied from class: JSType
+
Compares this and that. +

+

+
Overrides:
testForEquality in class ObjectType
+
+
+ +
Returns:
    +
  • TernaryValue.TRUE if the comparison of values of + this type and that always succeed (such as + undefined compared to null)
  • +
  • TernaryValue.FALSE if the comparison of values of + this type and that always fails (such as + undefined compared to number)
  • +
  • TernaryValue.UNKNOWN if the comparison can succeed or + fail depending on the concrete values
  • +
+
+
+
+ +

+isSubtype

+
+public boolean isSubtype(JSType that)
+
+
Description copied from class: JSType
+
Checks whether this is a subtype of that.

+ + Subtyping rules: +

    +
  • (unknown) — every type is a subtype of the Unknown type.
  • +
  • (no) — the No type is a subtype of every type.
  • +
  • (no-object) — the NoObject type is a subtype of every object + type (i.e. subtypes of the Object type).
  • +
  • (ref) — a type is a subtype of itself.
  • +
  • (union-l) — A union type is a subtype of a type U if all the + union type's constituents are a subtype of U. Formally
    + (T<sub>1</sub>, &hellip;, T<sub>n</sub>) &lt;: U if and only + T<sub>k</sub> &lt;: U for all k &isin; 1..n.
  • +
  • (union-r) — A type U is a subtype of a union type if it is a + subtype of one of the union type's constituents. Formally
    + U &lt;: (T<sub>1</sub>, &hellip;, T<sub>n</sub>) if and only + if U &lt;: T<sub>k</sub> for some index k.
  • +
  • (objects) — an Object O<sub>1</sub> is a subtype + of an object O<sub>2</sub> if it has more properties + than O<sub>2</sub> and all common properties are + pairwise subtypes.
  • +
+

+

+
Overrides:
isSubtype in class JSType
+
+
+ +
Returns:
this &lt;: that
+
+
+
+ +

+getOwnerFunction

+
+public FunctionType getOwnerFunction()
+
+
Description copied from class: ObjectType
+
Gets the owner of this if it's a function prototype. +

+

+
Overrides:
getOwnerFunction in class ObjectType
+
+
+
+
+
+
+ +

+getCtorImplementedInterfaces

+
+public Iterable<ObjectType> getCtorImplementedInterfaces()
+
+
Description copied from class: ObjectType
+
Gets the interfaces implemented by the ctor associated with this type. + Intended to be overridden by subclasses. +

+

+
Overrides:
getCtorImplementedInterfaces in class ObjectType
+
+
+
+
+
+
+ +

+canAssignTo

+
+public boolean canAssignTo(JSType that)
+
+
Description copied from class: JSType
+
Tests whether values of this type can be safely assigned + to values of that type.

+ + The default implementation verifies that this is a subtype + of that.

+

+

+
Overrides:
canAssignTo in class JSType
+
+
+
+
+
+
+ +

+isEquivalentTo

+
+public boolean isEquivalentTo(JSType that)
+
+
Description copied from class: JSType
+
Checks if two types are equivalent. +

+

+
Overrides:
isEquivalentTo in class JSType
+
+
+
+
+
+
+ +

+hashCode

+
+public int hashCode()
+
+
+
Overrides:
hashCode in class JSType
+
+
+
+
+
+
+ +

+getImplicitPrototype

+
+public ObjectType getImplicitPrototype()
+
+
Description copied from class: ObjectType
+
Gets the implicit prototype (a.k.a. the [[Prototype]] property). +

+

+
Specified by:
getImplicitPrototype in class ObjectType
+
+
+
+
+
+
+ +

+removeProperty

+
+public boolean removeProperty(String name)
+
+
Description copied from class: ObjectType
+
Removes the declared or inferred property from this ObjectType. +

+

+
Overrides:
removeProperty in class ObjectType
+
+
+
Parameters:
name - the property's name +
Returns:
true if the property was removed successfully. False if the + property did not exist, or could not be removed.
+
+
+
+ +

+isPropertyTypeDeclared

+
+public boolean isPropertyTypeDeclared(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property's type is declared. +

+

+
Specified by:
isPropertyTypeDeclared in class ObjectType
+
+
+
+
+
+
+ +

+getPropertyNode

+
+public Node getPropertyNode(String propertyName)
+
+
Description copied from class: ObjectType
+
Gets the node corresponding to the definition of the specified property. + This could be the node corresponding to declaration of the property or the + node corresponding to the first reference to this property, e.g., + "this.propertyName" in a constructor. Note this is mainly intended to be + an estimate of where in the source code a property is defined. Sometime + the returned node is not even part of the global AST but in the AST of the + JsDoc that defines a type. +

+

+
Overrides:
getPropertyNode in class ObjectType
+
+
+
Parameters:
propertyName - the name of the property +
Returns:
the Node corresponding to the property or null.
+
+
+
+ +

+isPropertyTypeInferred

+
+public boolean isPropertyTypeInferred(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property's type is inferred. +

+

+
Specified by:
isPropertyTypeInferred in class ObjectType
+
+
+
+
+
+
+ +

+isPropertyInExterns

+
+public boolean isPropertyInExterns(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property was defined in the externs. +

+

+
Overrides:
isPropertyInExterns in class ObjectType
+
+
+
+
+
+
+ +

+getPropertiesCount

+
+public int getPropertiesCount()
+
+
Description copied from class: ObjectType
+
Gets the number of properties of this object. +

+

+
Specified by:
getPropertiesCount in class ObjectType
+
+
+
+
+
+
+ +

+collectPropertyNames

+
+protected void collectPropertyNames(Set<String> props)
+
+
Description copied from class: ObjectType
+
Adds any properties defined on this type or its supertypes to the set. +

+

+
+
+
+
+
+
+
+ +

+findPropertyType

+
+public JSType findPropertyType(String propertyName)
+
+
Description copied from class: JSType
+
Coerces this type to an Object type, then gets the type of the property + whose name is given. + + Unlike ObjectType.getPropertyType(java.lang.String), returns null if the property + is not found. +

+

+
Overrides:
findPropertyType in class ObjectType
+
+
+ +
Returns:
The property's type. null if the current type cannot + have properties, or if the type is not found.
+
+
+
+ +

+getPropertyType

+
+public JSType getPropertyType(String propertyName)
+
+
Description copied from class: ObjectType
+
Gets the property type of the property whose name is given. If the + underlying object does not have this property, the Unknown type is + returned to indicate that no information is available on this property. +

+

+
Specified by:
getPropertyType in class ObjectType
+
+
+ +
Returns:
the property's type or UnknownType. This method never + returns null.
+
+
+
+ +

+getJSDocInfo

+
+public JSDocInfo getJSDocInfo()
+
+
Description copied from class: ObjectType
+
Gets the docInfo for this type. +

+

+
Overrides:
getJSDocInfo in class ObjectType
+
+
+
+
+
+
+ +

+setJSDocInfo

+
+public void setJSDocInfo(JSDocInfo info)
+
+
Description copied from class: ObjectType
+
Sets the docInfo for this type from the given + JSDocInfo. The JSDocInfo may be null. +

+

+
Overrides:
setJSDocInfo in class ObjectType
+
+
+
+
+
+
+ +

+getOwnPropertyJSDocInfo

+
+public JSDocInfo getOwnPropertyJSDocInfo(String propertyName)
+
+
Description copied from class: ObjectType
+
Gets the docInfo on the specified property on this type. This should not + be done implemented recursively, as you generally need to know exactly on + which type in the prototype chain the JSDocInfo exists. +

+

+
Overrides:
getOwnPropertyJSDocInfo in class ObjectType
+
+
+
+
+
+
+ +

+setPropertyJSDocInfo

+
+public void setPropertyJSDocInfo(String propertyName,
+                                 JSDocInfo info)
+
+
Description copied from class: ObjectType
+
Sets the docInfo for the specified property from the + JSDocInfo on its definition. +

+

+
Overrides:
setPropertyJSDocInfo in class ObjectType
+
+
+
info - JSDocInfo for the property definition. May be + null.
+
+
+
+ +

+hasProperty

+
+public boolean hasProperty(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property whose name is given is present on the + object. +

+

+
Specified by:
hasProperty in class ObjectType
+
+
+
+
+
+
+ +

+hasOwnProperty

+
+public boolean hasOwnProperty(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property whose name is given is present directly on + the object. Returns false even if it is declared on a supertype. +

+

+
Overrides:
hasOwnProperty in class ObjectType
+
+
+
+
+
+
+ +

+getOwnPropertyNames

+
+public Set<String> getOwnPropertyNames()
+
+
Description copied from class: ObjectType
+
Returns the names of all the properties directly on this type. +

+

+
Overrides:
getOwnPropertyNames in class ObjectType
+
+
+
+
+
+
+ +

+getConstructor

+
+public FunctionType getConstructor()
+
+
Description copied from class: ObjectType
+
Gets this object's constructor. +

+

+
Specified by:
getConstructor in class ObjectType
+
+
+ +
Returns:
this object's constructor or null if it is a native + object (constructed natively v.s. by instantiation of a function)
+
+
+
+ +

+getParameterType

+
+public JSType getParameterType()
+
+
Description copied from class: ObjectType
+
Gets the declared default element type. +

+

+
Overrides:
getParameterType in class ObjectType
+
+
+
See Also:
ParameterizedType
+
+
+
+ +

+getIndexType

+
+public JSType getIndexType()
+
+
Description copied from class: ObjectType
+
Gets the declared default index type. +

+

+
Overrides:
getIndexType in class ObjectType
+
+
+
See Also:
IndexedType
+
+
+
+ +

+visit

+
+public <T> T visit(Visitor<T> visitor)
+
+
Description copied from class: JSType
+
Visit this type with the given visitor. +

+

+
Overrides:
visit in class ObjectType
+
+
+ +
Returns:
the value returned by the visitor
See Also:
Visitor
+
+
+
+ +

+toDebugHashCodeString

+
+public String toDebugHashCodeString()
+
+
Description copied from class: JSType
+
A hash code function for diagnosing complicated issues + around type-identity. +

+

+
Overrides:
toDebugHashCodeString in class JSType
+
+
+
+
+
+
+ +

+getTypeOfThis

+
+public ObjectType getTypeOfThis()
+
+
Description copied from interface: StaticScope
+
Returns the expected type of this in the current scope. +

+

+
Specified by:
getTypeOfThis in interface StaticScope<JSType>
Overrides:
getTypeOfThis in class ObjectType
+
+
+
+
+
+
+ +

+collapseUnion

+
+public JSType collapseUnion()
+
+
Description copied from class: JSType
+
Gets the least supertype of this that's not a union. +

+

+
Overrides:
collapseUnion in class JSType
+
+
+
+
+
+
+ +

+matchConstraint

+
+public void matchConstraint(ObjectType contraint)
+
+
Description copied from class: JSType
+
Modify this type so that it matches the specified type. + + This is useful for reverse type-inference, where we want to + infer that an object literal matches its contraint (much like + how the java compiler does reverse-inference to figure out generics). +

+

+
Overrides:
matchConstraint in class JSType
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/TernaryValue.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/TernaryValue.html new file mode 100644 index 0000000..8297411 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/TernaryValue.html @@ -0,0 +1,507 @@ + + + + + +TernaryValue (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Enum TernaryValue

+
+java.lang.Object
+  extended by java.lang.Enum<TernaryValue>
+      extended by com.google.javascript.rhino.jstype.TernaryValue
+
+
+
All Implemented Interfaces:
Serializable, Comparable<TernaryValue>
+
+
+
+
public enum TernaryValue
extends Enum<TernaryValue>
+ + +

+

An enum for ternary logic. The TRUE and FALSE values + are equivalent to typical booleans, and the UNKNOWN value plays the + role of a placeholder, which can be either TRUE or + FALSE.

+ +

A ternary value expression evaluates to TRUE or + FALSE only if all replacements of UNKNOWN in this + expression yield the same result. Therefore, the ternary logic coincides + with typical boolean logic if the UNKNOWN value is not + present in an expression.

+

+ +

+

+
See Also:
Ternary Logic
+
+ +

+ + + + + + + + + + + + + + + + +
+Enum Constant Summary
FALSE + +
+          false
TRUE + +
+          true
UNKNOWN + +
+          unknown, it represents lack of knowledge about whether this value + is true or false.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+abstract  TernaryValueand(TernaryValue that) + +
+          Gets the and of this and that.
+static TernaryValueforBoolean(boolean val) + +
+          Gets the TernaryValue for the given boolean.
+abstract  TernaryValuenot() + +
+          Gets the not of this.
+abstract  TernaryValueor(TernaryValue that) + +
+          Gets the or of this and that.
+abstract  booleantoBoolean(boolean unknown) + +
+          Converts this ternary value to boolean.
+static TernaryValuevalueOf(String name) + +
+          Returns the enum constant of this type with the specified name.
+static TernaryValue[]values() + +
+          Returns an array containing the constants of this enum type, in +the order they are declared.
+abstract  TernaryValuexor(TernaryValue that) + +
+          Gets the xor of this and that.
+ + + + + + + +
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
+ + + + + + + +
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Enum Constant Detail
+ +

+FALSE

+
+public static final TernaryValue FALSE
+
+
false +

+

+
+
+
+ +

+TRUE

+
+public static final TernaryValue TRUE
+
+
true +

+

+
+
+
+ +

+UNKNOWN

+
+public static final TernaryValue UNKNOWN
+
+
unknown, it represents lack of knowledge about whether this value + is true or false. +

+

+
+
+ + + + + + + + +
+Method Detail
+ +

+values

+
+public static TernaryValue[] values()
+
+
Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
+for (TernaryValue c : TernaryValue.values())
+    System.out.println(c);
+
+

+

+ +
Returns:
an array containing the constants of this enum type, in +the order they are declared
+
+
+
+ +

+valueOf

+
+public static TernaryValue valueOf(String name)
+
+
Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.) +

+

+
Parameters:
name - the name of the enum constant to be returned. +
Returns:
the enum constant with the specified name +
Throws: +
IllegalArgumentException - if this enum type has no constant +with the specified name +
NullPointerException - if the argument is null
+
+
+
+ +

+and

+
+public abstract TernaryValue and(TernaryValue that)
+
+
Gets the and of this and that. +

+

+
+
+
+
+ +

+not

+
+public abstract TernaryValue not()
+
+
Gets the not of this. +

+

+
+
+
+
+ +

+or

+
+public abstract TernaryValue or(TernaryValue that)
+
+
Gets the or of this and that. +

+

+
+
+
+
+ +

+xor

+
+public abstract TernaryValue xor(TernaryValue that)
+
+
Gets the xor of this and that. +

+

+
+
+
+
+ +

+toBoolean

+
+public abstract boolean toBoolean(boolean unknown)
+
+
Converts this ternary value to boolean. The #TRUE and + #FALSE values are simply converted to true and + false respectively, whilst the UNKNOWN is converted + to the specified unknown value. +

+

+
Parameters:
unknown - the boolean value to which the UNKNOWN value is + converted +
Returns:
return
+     this == TRUE ? true :
+     this == FALSE ? false :
+     unknown
+
+
+
+ +

+forBoolean

+
+public static TernaryValue forBoolean(boolean val)
+
+
Gets the TernaryValue for the given boolean. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/UnionType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/UnionType.html new file mode 100644 index 0000000..6ec9a77 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/UnionType.html @@ -0,0 +1,1182 @@ + + + + + +UnionType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class UnionType

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType
+      extended by com.google.javascript.rhino.jstype.UnionType
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class UnionType
extends JSType
+ + +

+The UnionType implements a common JavaScript idiom in which the + code is specifically designed to work with multiple input types. Because + JavaScript always knows the runtime type of an object value, this is safer + than a C union.

+ + For instance, values of the union type (String,boolean) can be of + type String or of type boolean. The commutativity of the + statement is captured by making (String,boolean) and + (boolean,String) equal.

+ + The implementation of this class prevents the creation of nested + unions.

+

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.JSType
JSType.TypePair
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.rhino.jstype.JSType
EMPTY_TYPE_COMPONENT, ENUMDECL, NOT_A_CLASS, NOT_A_TYPE, NOT_ENUMDECL, UNKNOWN_NAME
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ JSTypeautobox() + +
+          Dereference a type for property access.
+ booleancanAssignTo(JSType that) + +
+          Tests whether values of this type can be safely assigned + to values of that type.
+ booleancanBeCalled() + +
+          This predicate is used to test whether a given type can be used as the + 'function' in a function call.
+ JSTypecollapseUnion() + +
+          Gets the least supertype of this that's not a union.
+ booleancontains(JSType type) + +
+          A UnionType contains a given type (alternate) iff the member + vector contains it.
+ JSTypefindPropertyType(String propertyName) + +
+          Coerces this type to an Object type, then gets the type of the property + whose name is given.
+ Iterable<JSType>getAlternates() + +
+          Gets the alternate types of this union type.
+ JSTypegetLeastSupertype(JSType that) + +
+          Gets the least supertype of this and that.
+ BooleanLiteralSetgetPossibleToBooleanOutcomes() + +
+          Computes the set of possible outcomes of the ToBoolean predicate + for this type.
+ JSTypegetRestrictedTypeGivenToBooleanOutcome(boolean outcome) + +
+          Computes the restricted type of this type knowing that the + ToBoolean predicate has a specific value.
+ JSTypegetRestrictedUnion(JSType type) + +
+          Returns a more restricted union type than this one, in which all + subtypes of type have been removed.
+ JSType.TypePairgetTypesUnderEquality(JSType that) + +
+          Computes the subset of this and that types if equality + is observed.
+ JSType.TypePairgetTypesUnderInequality(JSType that) + +
+          Computes the subset of this and that types if inequality + is observed.
+ JSType.TypePairgetTypesUnderShallowInequality(JSType that) + +
+          Computes the subset of this and that types under + shallow inequality.
+ inthashCode() + +
+           
+ booleanisEquivalentTo(JSType object) + +
+          Two union types are equal if they have the same number of alternates + and all alternates are equal.
+ booleanisNullable() + +
+          This predicate determines whether objects of this type can have the + null value, and therefore can appear in contexts where + null is expected.
+ booleanisObject() + +
+          Tests whether this type is an Object, or any subtype thereof.
+ booleanisSubtype(JSType that) + +
+          Checks whether this is a subtype of that.
+ booleanisUnknownType() + +
+           
+ voidmatchConstraint(ObjectType constraintObj) + +
+          Modify this type so that it matches the specified type.
+ booleanmatchesNumberContext() + +
+          This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator.
+ booleanmatchesObjectContext() + +
+          This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with + statement.
+ booleanmatchesStringContext() + +
+          This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) + operator.
+ JSTyperestrictByNotNullOrUndefined() + +
+          If this is a union type, returns a union type that does not include + the null or undefined type.
+ booleansetValidator(com.google.common.base.Predicate<JSType> validator) + +
+          Certain types have constraints on them at resolution-time.
+ TernaryValuetestForEquality(JSType that) + +
+          Compares this and that.
+ StringtoDebugHashCodeString() + +
+          A hash code function for diagnosing complicated issues + around type-identity.
+ UnionTypetoMaybeUnionType() + +
+          Downcasts this to a UnionType, or returns null if this is not a UnionType.
+ + + + + +
+<T> T
+
visit(Visitor<T> visitor) + +
+          Visit this type with the given visitor.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.JSType
autoboxesTo, canTestForEqualityWith, canTestForShallowEqualityWith, clearResolved, dereference, differsFrom, equals, forceResolve, getDisplayName, getGreatestSubtype, getJSDocInfo, getTypesUnderShallowEquality, hasDisplayName, isAllType, isArrayType, isBooleanObjectType, isBooleanValueType, isCheckedUnknownType, isConstructor, isDateType, isEmptyType, isEnumElementType, isEnumType, isEquivalent, isFunctionPrototypeType, isFunctionType, isGlobalThisType, isInstanceType, isInterface, isNominalConstructor, isNominalType, isNoObjectType, isNoResolvedType, isNoType, isNullType, isNumber, isNumberObjectType, isNumberValueType, isOrdinaryFunction, isRecordType, isRegexpType, isResolved, isString, isStringObjectType, isStringValueType, isTemplateType, isUnionType, isVoidType, matchesInt32Context, matchesUint32Context, resolve, toAnnotationString, toMaybeEnumElementType, toMaybeEnumType, toMaybeFunctionType, toMaybeFunctionType, toObjectType, toString, unboxesTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getAlternates

+
+public Iterable<JSType> getAlternates()
+
+
Gets the alternate types of this union type. +

+

+ +
Returns:
The alternate types of this union type. The returned set is + immutable.
+
+
+
+ +

+matchesNumberContext

+
+public boolean matchesNumberContext()
+
+
This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator. +

+

+
Overrides:
matchesNumberContext in class JSType
+
+
+ +
Returns:
true if the type can appear in a numeric context.
+
+
+
+ +

+matchesStringContext

+
+public boolean matchesStringContext()
+
+
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) + operator.

+ + All types have at least the potential for converting to String. + When we add externally defined types, such as a browser OM, we may choose + to add types that do not automatically convert to String. +

+

+
Overrides:
matchesStringContext in class JSType
+
+
+ +
Returns:
true if not VoidType
+
+
+
+ +

+matchesObjectContext

+
+public boolean matchesObjectContext()
+
+
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with + statement.

+ + Most types we will encounter, except notably null, have at least + the potential for converting to Object. Host defined objects can + get peculiar.

+ + VOID type is included here because while it is not part of the JavaScript + language, functions returning 'void' type can't be used as operands of + any operator or statement.

+

+

+
Overrides:
matchesObjectContext in class JSType
+
+
+ +
Returns:
true if the type is not NullType or + VoidType
+
+
+
+ +

+findPropertyType

+
+public JSType findPropertyType(String propertyName)
+
+
Description copied from class: JSType
+
Coerces this type to an Object type, then gets the type of the property + whose name is given. + + Unlike ObjectType.getPropertyType(java.lang.String), returns null if the property + is not found. +

+

+
Overrides:
findPropertyType in class JSType
+
+
+ +
Returns:
The property's type. null if the current type cannot + have properties, or if the type is not found.
+
+
+
+ +

+canAssignTo

+
+public boolean canAssignTo(JSType that)
+
+
Description copied from class: JSType
+
Tests whether values of this type can be safely assigned + to values of that type.

+ + The default implementation verifies that this is a subtype + of that.

+

+

+
Overrides:
canAssignTo in class JSType
+
+
+
+
+
+
+ +

+canBeCalled

+
+public boolean canBeCalled()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can be used as the + 'function' in a function call. +

+

+
Overrides:
canBeCalled in class JSType
+
+
+ +
Returns:
true if this type might be callable.
+
+
+
+ +

+autobox

+
+public JSType autobox()
+
+
Description copied from class: JSType
+
Dereference a type for property access. + + Autoboxes the type, and filters null/undefined, and returns the result. +

+

+
Overrides:
autobox in class JSType
+
+
+
+
+
+
+ +

+restrictByNotNullOrUndefined

+
+public JSType restrictByNotNullOrUndefined()
+
+
Description copied from class: JSType
+
If this is a union type, returns a union type that does not include + the null or undefined type. +

+

+
Overrides:
restrictByNotNullOrUndefined in class JSType
+
+
+
+
+
+
+ +

+testForEquality

+
+public TernaryValue testForEquality(JSType that)
+
+
Description copied from class: JSType
+
Compares this and that. +

+

+
Overrides:
testForEquality in class JSType
+
+
+ +
Returns:
    +
  • TernaryValue.TRUE if the comparison of values of + this type and that always succeed (such as + undefined compared to null)
  • +
  • TernaryValue.FALSE if the comparison of values of + this type and that always fails (such as + undefined compared to number)
  • +
  • TernaryValue.UNKNOWN if the comparison can succeed or + fail depending on the concrete values
  • +
+
+
+
+ +

+isNullable

+
+public boolean isNullable()
+
+
This predicate determines whether objects of this type can have the + null value, and therefore can appear in contexts where + null is expected. +

+

+
Overrides:
isNullable in class JSType
+
+
+ +
Returns:
true for everything but Number and + Boolean types.
+
+
+
+ +

+isUnknownType

+
+public boolean isUnknownType()
+
+
+
Overrides:
isUnknownType in class JSType
+
+
+
+
+
+
+ +

+getLeastSupertype

+
+public JSType getLeastSupertype(JSType that)
+
+
Description copied from class: JSType
+
Gets the least supertype of this and that. + The least supertype is the join (∨) or supremum of both types in the + type lattice.

+ Examples: +

    +
  • number &#8744; * = *
  • +
  • number &#8744; Object = (number, Object)
  • +
  • Number &#8744; Object = Object
  • +
+

+

+
Overrides:
getLeastSupertype in class JSType
+
+
+ +
Returns:
this &#8744; that
+
+
+
+ +

+isEquivalentTo

+
+public boolean isEquivalentTo(JSType object)
+
+
Two union types are equal if they have the same number of alternates + and all alternates are equal. +

+

+
Overrides:
isEquivalentTo in class JSType
+
+
+
+
+
+
+ +

+hashCode

+
+public int hashCode()
+
+
+
Overrides:
hashCode in class JSType
+
+
+
+
+
+
+ +

+toMaybeUnionType

+
+public UnionType toMaybeUnionType()
+
+
Description copied from class: JSType
+
Downcasts this to a UnionType, or returns null if this is not a UnionType. + + Named in honor of Haskell's Maybe type constructor. +

+

+
Overrides:
toMaybeUnionType in class JSType
+
+
+
+
+
+
+ +

+isObject

+
+public boolean isObject()
+
+
Description copied from class: JSType
+
Tests whether this type is an Object, or any subtype thereof. +

+

+
Overrides:
isObject in class JSType
+
+
+ +
Returns:
this &lt;: Object
+
+
+
+ +

+contains

+
+public boolean contains(JSType type)
+
+
A UnionType contains a given type (alternate) iff the member + vector contains it. +

+

+
Parameters:
type - The alternate which might be in this union. +
Returns:
true if the alternate is in the union
+
+
+
+ +

+getRestrictedUnion

+
+public JSType getRestrictedUnion(JSType type)
+
+
Returns a more restricted union type than this one, in which all + subtypes of type have been removed.

+ + Examples: +

    +
  • (number,string) restricted by number is + string
  • +
  • (null, EvalError, URIError) restricted by + Error is null
  • +
+

+

+
Parameters:
type - the supertype of the types to remove from this union type
+
+
+
+ +

+isSubtype

+
+public boolean isSubtype(JSType that)
+
+
Description copied from class: JSType
+
Checks whether this is a subtype of that.

+ + Subtyping rules: +

    +
  • (unknown) — every type is a subtype of the Unknown type.
  • +
  • (no) — the No type is a subtype of every type.
  • +
  • (no-object) — the NoObject type is a subtype of every object + type (i.e. subtypes of the Object type).
  • +
  • (ref) — a type is a subtype of itself.
  • +
  • (union-l) — A union type is a subtype of a type U if all the + union type's constituents are a subtype of U. Formally
    + (T<sub>1</sub>, &hellip;, T<sub>n</sub>) &lt;: U if and only + T<sub>k</sub> &lt;: U for all k &isin; 1..n.
  • +
  • (union-r) — A type U is a subtype of a union type if it is a + subtype of one of the union type's constituents. Formally
    + U &lt;: (T<sub>1</sub>, &hellip;, T<sub>n</sub>) if and only + if U &lt;: T<sub>k</sub> for some index k.
  • +
  • (objects) — an Object O<sub>1</sub> is a subtype + of an object O<sub>2</sub> if it has more properties + than O<sub>2</sub> and all common properties are + pairwise subtypes.
  • +
+

+

+
Overrides:
isSubtype in class JSType
+
+
+ +
Returns:
this &lt;: that
+
+
+
+ +

+getRestrictedTypeGivenToBooleanOutcome

+
+public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome)
+
+
Description copied from class: JSType
+
Computes the restricted type of this type knowing that the + ToBoolean predicate has a specific value. For more information + about the ToBoolean predicate, see + JSType.getPossibleToBooleanOutcomes(). +

+

+
Overrides:
getRestrictedTypeGivenToBooleanOutcome in class JSType
+
+
+
Parameters:
outcome - the value of the ToBoolean predicate +
Returns:
the restricted type, or the Any Type if the underlying type could + not have yielded this ToBoolean value + + TODO(user): Move this method to the SemanticRAI and use the visit + method of types to get the restricted type.
+
+
+
+ +

+getPossibleToBooleanOutcomes

+
+public BooleanLiteralSet getPossibleToBooleanOutcomes()
+
+
Description copied from class: JSType
+
Computes the set of possible outcomes of the ToBoolean predicate + for this type. The ToBoolean predicate is defined by the ECMA-262 + standard, 3rd edition. Its behavior for simple types can be + summarized by the following table: + + + + + + + + +
typeresult
undefined{false}
null{false}
boolean{true, false}
number{true, false}
string{true, false}
Object{true}
+

+

+
Specified by:
getPossibleToBooleanOutcomes in class JSType
+
+
+ +
Returns:
the set of boolean literals for this type
+
+
+
+ +

+getTypesUnderEquality

+
+public JSType.TypePair getTypesUnderEquality(JSType that)
+
+
Description copied from class: JSType
+
Computes the subset of this and that types if equality + is observed. If a value v1 of type null is equal to a value + v2 of type (undefined,number), we can infer that the + type of v1 is null and the type of v2 is + undefined. +

+

+
Overrides:
getTypesUnderEquality in class JSType
+
+
+ +
Returns:
a pair containing the restricted type of this as the first + component and the restricted type of that as the second + element. The returned pair is never null even though its + components may be null
+
+
+
+ +

+getTypesUnderInequality

+
+public JSType.TypePair getTypesUnderInequality(JSType that)
+
+
Description copied from class: JSType
+
Computes the subset of this and that types if inequality + is observed. If a value v1 of type number is not equal to a + value v2 of type (undefined,number), we can infer that the + type of v1 is number and the type of v2 is + number as well. +

+

+
Overrides:
getTypesUnderInequality in class JSType
+
+
+ +
Returns:
a pair containing the restricted type of this as the first + component and the restricted type of that as the second + element. The returned pair is never null even though its + components may be null
+
+
+
+ +

+getTypesUnderShallowInequality

+
+public JSType.TypePair getTypesUnderShallowInequality(JSType that)
+
+
Description copied from class: JSType
+
Computes the subset of this and that types under + shallow inequality. +

+

+
Overrides:
getTypesUnderShallowInequality in class JSType
+
+
+ +
Returns:
A pair containing the restricted type of this as the first + component and the restricted type of that as the second + element. The returned pair is never null even though its + components may be null
+
+
+
+ +

+visit

+
+public <T> T visit(Visitor<T> visitor)
+
+
Description copied from class: JSType
+
Visit this type with the given visitor. +

+

+
Specified by:
visit in class JSType
+
+
+ +
Returns:
the value returned by the visitor
See Also:
Visitor
+
+
+
+ +

+toDebugHashCodeString

+
+public String toDebugHashCodeString()
+
+
Description copied from class: JSType
+
A hash code function for diagnosing complicated issues + around type-identity. +

+

+
Overrides:
toDebugHashCodeString in class JSType
+
+
+
+
+
+
+ +

+setValidator

+
+public boolean setValidator(com.google.common.base.Predicate<JSType> validator)
+
+
Description copied from class: JSType
+
Certain types have constraints on them at resolution-time. + For example, a type in an @extends annotation must be an + object. Clients should inject a validator that emits a warning + if the type does not validate, and return false. +

+

+
Overrides:
setValidator in class JSType
+
+
+
+
+
+
+ +

+collapseUnion

+
+public JSType collapseUnion()
+
+
Description copied from class: JSType
+
Gets the least supertype of this that's not a union. +

+

+
Overrides:
collapseUnion in class JSType
+
+
+
+
+
+
+ +

+matchConstraint

+
+public void matchConstraint(ObjectType constraintObj)
+
+
Description copied from class: JSType
+
Modify this type so that it matches the specified type. + + This is useful for reverse type-inference, where we want to + infer that an object literal matches its contraint (much like + how the java compiler does reverse-inference to figure out generics). +

+

+
Overrides:
matchConstraint in class JSType
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/UnknownType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/UnknownType.html new file mode 100644 index 0000000..38b8785 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/UnknownType.html @@ -0,0 +1,957 @@ + + + + + +UnknownType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class UnknownType

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType
+      extended by com.google.javascript.rhino.jstype.ObjectType
+          extended by com.google.javascript.rhino.jstype.UnknownType
+
+
+
All Implemented Interfaces:
StaticScope<JSType>, Serializable
+
+
+
+
public class UnknownType
extends ObjectType
+ + +

+The Unknown type. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.ObjectType
ObjectType.Property
+  + + + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.JSType
JSType.TypePair
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.rhino.jstype.JSType
EMPTY_TYPE_COMPONENT, ENUMDECL, NOT_A_CLASS, NOT_A_TYPE, NOT_ENUMDECL, UNKNOWN_NAME
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ booleancanAssignTo(JSType that) + +
+          Tests whether values of this type can be safely assigned + to values of that type.
+ booleancanBeCalled() + +
+          This predicate is used to test whether a given type can be used as the + 'function' in a function call.
+ FunctionTypegetConstructor() + +
+          Gets this object's constructor.
+ StringgetDisplayName() + +
+          Returns a user meaningful label for the JSType instance.
+ ObjectTypegetImplicitPrototype() + +
+          Gets the implicit prototype (a.k.a.
+ BooleanLiteralSetgetPossibleToBooleanOutcomes() + +
+          Computes the set of possible outcomes of the ToBoolean predicate + for this type.
+ intgetPropertiesCount() + +
+          Gets the number of properties of this object.
+ JSTypegetPropertyType(String propertyName) + +
+          Gets the property type of the property whose name is given.
+ StringgetReferenceName() + +
+          Gets the reference name for this object.
+ ObjectType.PropertygetSlot(String name) + +
+          Returns any defined slot within this scope for this name.
+ booleanhasDisplayName() + +
+           
+ booleanhasProperty(String propertyName) + +
+          Checks whether the property whose name is given is present on the + object.
+ booleanisCheckedUnknownType() + +
+           
+ booleanisNullable() + +
+          Tests whether this type is nullable.
+ booleanisPropertyTypeDeclared(String propertyName) + +
+          Checks whether the property's type is declared.
+ booleanisPropertyTypeInferred(String propertyName) + +
+          Checks whether the property's type is inferred.
+ booleanisSubtype(JSType that) + +
+          Checks whether this is a subtype of that.
+ booleanisUnknownType() + +
+          We treat this as the unknown type if any of its implicit prototype + properties is unknown.
+ booleanmatchesNumberContext() + +
+          This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator.
+ booleanmatchesObjectContext() + +
+          This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement.
+ booleanmatchesStringContext() + +
+          This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator.
+ TernaryValuetestForEquality(JSType that) + +
+          Compares this and that.
+ + + + + +
+<T> T
+
visit(Visitor<T> visitor) + +
+          Visit this type with the given visitor.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.ObjectType
cast, clearCachedValues, createDelegateSuffix, defineDeclaredProperty, defineInferredProperty, findPropertyType, getCtorExtendedInterfaces, getCtorImplementedInterfaces, getIndexType, getJSDocInfo, getNormalizedReferenceName, getOwnerFunction, getOwnPropertyJSDocInfo, getOwnPropertyNames, getOwnSlot, getParameterType, getParentScope, getPropertyNames, getPropertyNode, getRootNode, getTypeOfThis, hasCachedValues, hasOwnProperty, hasReferenceName, isFunctionPrototypeType, isNativeObjectType, isObject, isPropertyInExterns, removeProperty, setJSDocInfo, setPropertyJSDocInfo
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.JSType
autobox, autoboxesTo, canTestForEqualityWith, canTestForShallowEqualityWith, clearResolved, collapseUnion, dereference, differsFrom, equals, forceResolve, getGreatestSubtype, getLeastSupertype, getRestrictedTypeGivenToBooleanOutcome, getTypesUnderEquality, getTypesUnderInequality, getTypesUnderShallowEquality, getTypesUnderShallowInequality, hashCode, isAllType, isArrayType, isBooleanObjectType, isBooleanValueType, isConstructor, isDateType, isEmptyType, isEnumElementType, isEnumType, isEquivalent, isEquivalentTo, isFunctionType, isGlobalThisType, isInstanceType, isInterface, isNominalConstructor, isNominalType, isNoObjectType, isNoResolvedType, isNoType, isNullType, isNumber, isNumberObjectType, isNumberValueType, isOrdinaryFunction, isRecordType, isRegexpType, isResolved, isString, isStringObjectType, isStringValueType, isTemplateType, isUnionType, isVoidType, matchConstraint, matchesInt32Context, matchesUint32Context, resolve, restrictByNotNullOrUndefined, setValidator, toAnnotationString, toDebugHashCodeString, toMaybeEnumElementType, toMaybeEnumType, toMaybeFunctionType, toMaybeFunctionType, toMaybeUnionType, toObjectType, toString, unboxesTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+getSlot

+
+public ObjectType.Property getSlot(String name)
+
+
Description copied from interface: StaticScope
+
Returns any defined slot within this scope for this name. This call + continues searching through parent scopes if a slot with this name is not + found in the current scope. +

+

+
Specified by:
getSlot in interface StaticScope<JSType>
Specified by:
getSlot in class ObjectType
+
+
+
Parameters:
name - The name of the variable slot to look up. +
Returns:
The defined slot for the variable, or null if no + definition exists.
+
+
+
+ +

+isUnknownType

+
+public boolean isUnknownType()
+
+
Description copied from class: ObjectType
+
We treat this as the unknown type if any of its implicit prototype + properties is unknown. +

+

+
Overrides:
isUnknownType in class ObjectType
+
+
+
+
+
+
+ +

+isCheckedUnknownType

+
+public boolean isCheckedUnknownType()
+
+
+
Overrides:
isCheckedUnknownType in class JSType
+
+
+
+
+
+
+ +

+canAssignTo

+
+public boolean canAssignTo(JSType that)
+
+
Description copied from class: JSType
+
Tests whether values of this type can be safely assigned + to values of that type.

+ + The default implementation verifies that this is a subtype + of that.

+

+

+
Overrides:
canAssignTo in class JSType
+
+
+
+
+
+
+ +

+canBeCalled

+
+public boolean canBeCalled()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can be used as the + 'function' in a function call. +

+

+
Overrides:
canBeCalled in class JSType
+
+
+ +
Returns:
true if this type might be callable.
+
+
+
+ +

+matchesNumberContext

+
+public boolean matchesNumberContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator. +

+

+
Overrides:
matchesNumberContext in class JSType
+
+
+
+
+
+
+ +

+matchesObjectContext

+
+public boolean matchesObjectContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement. + + Most types we will encounter, except notably null, have at least + the potential for converting to Object. Host defined objects can + get peculiar. +

+

+
Overrides:
matchesObjectContext in class JSType
+
+
+
+
+
+
+ +

+matchesStringContext

+
+public boolean matchesStringContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator. + + All types have at least the potential for converting to String. + When we add externally defined types, such as a browser OM, we may choose + to add types that do not automatically convert to String. +

+

+
Overrides:
matchesStringContext in class JSType
+
+
+
+
+
+
+ +

+testForEquality

+
+public TernaryValue testForEquality(JSType that)
+
+
Description copied from class: JSType
+
Compares this and that. +

+

+
Overrides:
testForEquality in class ObjectType
+
+
+ +
Returns:
    +
  • TernaryValue.TRUE if the comparison of values of + this type and that always succeed (such as + undefined compared to null)
  • +
  • TernaryValue.FALSE if the comparison of values of + this type and that always fails (such as + undefined compared to number)
  • +
  • TernaryValue.UNKNOWN if the comparison can succeed or + fail depending on the concrete values
  • +
+
+
+
+ +

+isNullable

+
+public boolean isNullable()
+
+
Description copied from class: JSType
+
Tests whether this type is nullable. +

+

+
Overrides:
isNullable in class JSType
+
+
+
+
+
+
+ +

+isSubtype

+
+public boolean isSubtype(JSType that)
+
+
Description copied from class: JSType
+
Checks whether this is a subtype of that.

+ + Subtyping rules: +

    +
  • (unknown) — every type is a subtype of the Unknown type.
  • +
  • (no) — the No type is a subtype of every type.
  • +
  • (no-object) — the NoObject type is a subtype of every object + type (i.e. subtypes of the Object type).
  • +
  • (ref) — a type is a subtype of itself.
  • +
  • (union-l) — A union type is a subtype of a type U if all the + union type's constituents are a subtype of U. Formally
    + (T<sub>1</sub>, &hellip;, T<sub>n</sub>) &lt;: U if and only + T<sub>k</sub> &lt;: U for all k &isin; 1..n.
  • +
  • (union-r) — A type U is a subtype of a union type if it is a + subtype of one of the union type's constituents. Formally
    + U &lt;: (T<sub>1</sub>, &hellip;, T<sub>n</sub>) if and only + if U &lt;: T<sub>k</sub> for some index k.
  • +
  • (objects) — an Object O<sub>1</sub> is a subtype + of an object O<sub>2</sub> if it has more properties + than O<sub>2</sub> and all common properties are + pairwise subtypes.
  • +
+

+

+
Overrides:
isSubtype in class JSType
+
+
+ +
Returns:
this &lt;: that
+
+
+
+ +

+visit

+
+public <T> T visit(Visitor<T> visitor)
+
+
Description copied from class: JSType
+
Visit this type with the given visitor. +

+

+
Overrides:
visit in class ObjectType
+
+
+ +
Returns:
the value returned by the visitor
See Also:
Visitor
+
+
+
+ +

+getImplicitPrototype

+
+public ObjectType getImplicitPrototype()
+
+
Description copied from class: ObjectType
+
Gets the implicit prototype (a.k.a. the [[Prototype]] property). +

+

+
Specified by:
getImplicitPrototype in class ObjectType
+
+
+
+
+
+
+ +

+getPropertiesCount

+
+public int getPropertiesCount()
+
+
Description copied from class: ObjectType
+
Gets the number of properties of this object. +

+

+
Specified by:
getPropertiesCount in class ObjectType
+
+
+
+
+
+
+ +

+getPropertyType

+
+public JSType getPropertyType(String propertyName)
+
+
Description copied from class: ObjectType
+
Gets the property type of the property whose name is given. If the + underlying object does not have this property, the Unknown type is + returned to indicate that no information is available on this property. +

+

+
Specified by:
getPropertyType in class ObjectType
+
+
+ +
Returns:
the property's type or UnknownType. This method never + returns null.
+
+
+
+ +

+hasProperty

+
+public boolean hasProperty(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property whose name is given is present on the + object. +

+

+
Specified by:
hasProperty in class ObjectType
+
+
+
+
+
+
+ +

+getConstructor

+
+public FunctionType getConstructor()
+
+
Description copied from class: ObjectType
+
Gets this object's constructor. +

+

+
Specified by:
getConstructor in class ObjectType
+
+
+ +
Returns:
this object's constructor or null if it is a native + object (constructed natively v.s. by instantiation of a function)
+
+
+
+ +

+getReferenceName

+
+public String getReferenceName()
+
+
Description copied from class: ObjectType
+
Gets the reference name for this object. This includes named types + like constructors, prototypes, and enums. It notably does not include + literal types like strings and booleans and structural types. +

+

+
Specified by:
getReferenceName in class ObjectType
+
+
+ +
Returns:
the object's name or null if this is an anonymous + object
+
+
+
+ +

+getDisplayName

+
+public String getDisplayName()
+
+
Description copied from class: JSType
+
Returns a user meaningful label for the JSType instance. For example, + Functions and Enums will return their declaration name (if they have one). + Some types will not have a meaningful display name. Calls to + hasDisplayName() will return true IFF getDisplayName() will return null + or a zero length string. +

+

+
Overrides:
getDisplayName in class ObjectType
+
+
+ +
Returns:
the display name of the type, or null if one is not available
+
+
+
+ +

+hasDisplayName

+
+public boolean hasDisplayName()
+
+
+
Overrides:
hasDisplayName in class JSType
+
+
+ +
Returns:
true if the JSType has a user meaningful label.
+
+
+
+ +

+isPropertyTypeDeclared

+
+public boolean isPropertyTypeDeclared(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property's type is declared. +

+

+
Specified by:
isPropertyTypeDeclared in class ObjectType
+
+
+
+
+
+
+ +

+isPropertyTypeInferred

+
+public boolean isPropertyTypeInferred(String propertyName)
+
+
Description copied from class: ObjectType
+
Checks whether the property's type is inferred. +

+

+
Specified by:
isPropertyTypeInferred in class ObjectType
+
+
+
+
+
+
+ +

+getPossibleToBooleanOutcomes

+
+public BooleanLiteralSet getPossibleToBooleanOutcomes()
+
+
Description copied from class: JSType
+
Computes the set of possible outcomes of the ToBoolean predicate + for this type. The ToBoolean predicate is defined by the ECMA-262 + standard, 3rd edition. Its behavior for simple types can be + summarized by the following table: + + + + + + + + +
typeresult
undefined{false}
null{false}
boolean{true, false}
number{true, false}
string{true, false}
Object{true}
+

+

+
Overrides:
getPossibleToBooleanOutcomes in class ObjectType
+
+
+ +
Returns:
the set of boolean literals for this type
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/Visitor.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/Visitor.html new file mode 100644 index 0000000..e17cdbc --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/Visitor.html @@ -0,0 +1,484 @@ + + + + + +Visitor (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Interface Visitor<T>

+
+
+
public interface Visitor<T>
+ + +

+A type visitor.

+ + This code will calculate a specific value of type T from a type + based on its structure: + +

JSType type = …;
+ T value = type.visit(new Visitor<T>() {
+   …
+ });
+

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ TcaseAllType() + +
+          All type's case.
+ TcaseBooleanType() + +
+          Boolean value type's case.
+ TcaseEnumElementType(EnumElementType type) + +
+          Enum element type's case.
+ TcaseFunctionType(FunctionType type) + +
+          Function type's case.
+ TcaseNoObjectType() + +
+          Bottom Object type's case.
+ TcaseNoType() + +
+          Bottom type's case.
+ TcaseNullType() + +
+          Null type's case.
+ TcaseNumberType() + +
+          Number value type's case.
+ TcaseObjectType(ObjectType type) + +
+          Object type's case.
+ TcaseStringType() + +
+          String value type's case.
+ TcaseUnionType(UnionType type) + +
+          Union type's case.
+ TcaseUnknownType() + +
+          Unknown type's case.
+ TcaseVoidType() + +
+          Void type's case.
+  +

+ + + + + + + + +
+Method Detail
+ +

+caseNoType

+
+T caseNoType()
+
+
Bottom type's case. +

+

+
+
+
+
+ +

+caseEnumElementType

+
+T caseEnumElementType(EnumElementType type)
+
+
Enum element type's case. +

+

+
+
+
+
+ +

+caseAllType

+
+T caseAllType()
+
+
All type's case. +

+

+
+
+
+
+ +

+caseBooleanType

+
+T caseBooleanType()
+
+
Boolean value type's case. +

+

+
+
+
+
+ +

+caseNoObjectType

+
+T caseNoObjectType()
+
+
Bottom Object type's case. +

+

+
+
+
+
+ +

+caseFunctionType

+
+T caseFunctionType(FunctionType type)
+
+
Function type's case. +

+

+
+
+
+
+ +

+caseObjectType

+
+T caseObjectType(ObjectType type)
+
+
Object type's case. +

+

+
+
+
+
+ +

+caseUnknownType

+
+T caseUnknownType()
+
+
Unknown type's case. +

+

+
+
+
+
+ +

+caseNullType

+
+T caseNullType()
+
+
Null type's case. +

+

+
+
+
+
+ +

+caseNumberType

+
+T caseNumberType()
+
+
Number value type's case. +

+

+
+
+
+
+ +

+caseStringType

+
+T caseStringType()
+
+
String value type's case. +

+

+
+
+
+
+ +

+caseVoidType

+
+T caseVoidType()
+
+
Void type's case. +

+

+
+
+
+
+ +

+caseUnionType

+
+T caseUnionType(UnionType type)
+
+
Union type's case. +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/VoidType.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/VoidType.html new file mode 100644 index 0000000..0800959 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/VoidType.html @@ -0,0 +1,566 @@ + + + + + +VoidType (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.jstype +
+Class VoidType

+
+java.lang.Object
+  extended by com.google.javascript.rhino.jstype.JSType
+      extended by com.google.javascript.rhino.jstype.VoidType
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class VoidType
extends JSType
+ + +

+Void type whose only element is the undefined value. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + +
+Nested Class Summary
+ + + + + + + +
Nested classes/interfaces inherited from class com.google.javascript.rhino.jstype.JSType
JSType.TypePair
+  + + + + + + + +
+Field Summary
+ + + + + + + +
Fields inherited from class com.google.javascript.rhino.jstype.JSType
EMPTY_TYPE_COMPONENT, ENUMDECL, NOT_A_CLASS, NOT_A_TYPE, NOT_ENUMDECL, UNKNOWN_NAME
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StringgetDisplayName() + +
+          Returns a user meaningful label for the JSType instance.
+ BooleanLiteralSetgetPossibleToBooleanOutcomes() + +
+          Computes the set of possible outcomes of the ToBoolean predicate + for this type.
+ booleanhasDisplayName() + +
+           
+ booleanisVoidType() + +
+           
+ booleanmatchesNumberContext() + +
+          This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator.
+ booleanmatchesObjectContext() + +
+          This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement.
+ booleanmatchesStringContext() + +
+          This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator.
+ JSTyperestrictByNotNullOrUndefined() + +
+          If this is a union type, returns a union type that does not include + the null or undefined type.
+ TernaryValuetestForEquality(JSType that) + +
+          Compares this and that.
+ + + + + +
+<T> T
+
visit(Visitor<T> visitor) + +
+          Visit this type with the given visitor.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.jstype.JSType
autobox, autoboxesTo, canAssignTo, canBeCalled, canTestForEqualityWith, canTestForShallowEqualityWith, clearResolved, collapseUnion, dereference, differsFrom, equals, findPropertyType, forceResolve, getGreatestSubtype, getJSDocInfo, getLeastSupertype, getRestrictedTypeGivenToBooleanOutcome, getTypesUnderEquality, getTypesUnderInequality, getTypesUnderShallowEquality, getTypesUnderShallowInequality, hashCode, isAllType, isArrayType, isBooleanObjectType, isBooleanValueType, isCheckedUnknownType, isConstructor, isDateType, isEmptyType, isEnumElementType, isEnumType, isEquivalent, isEquivalentTo, isFunctionPrototypeType, isFunctionType, isGlobalThisType, isInstanceType, isInterface, isNominalConstructor, isNominalType, isNoObjectType, isNoResolvedType, isNoType, isNullable, isNullType, isNumber, isNumberObjectType, isNumberValueType, isObject, isOrdinaryFunction, isRecordType, isRegexpType, isResolved, isString, isStringObjectType, isStringValueType, isSubtype, isTemplateType, isUnionType, isUnknownType, matchConstraint, matchesInt32Context, matchesUint32Context, resolve, setValidator, toAnnotationString, toDebugHashCodeString, toMaybeEnumElementType, toMaybeEnumType, toMaybeFunctionType, toMaybeFunctionType, toMaybeUnionType, toObjectType, toString, unboxesTo
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+restrictByNotNullOrUndefined

+
+public JSType restrictByNotNullOrUndefined()
+
+
Description copied from class: JSType
+
If this is a union type, returns a union type that does not include + the null or undefined type. +

+

+
Overrides:
restrictByNotNullOrUndefined in class JSType
+
+
+
+
+
+
+ +

+testForEquality

+
+public TernaryValue testForEquality(JSType that)
+
+
Description copied from class: JSType
+
Compares this and that. +

+

+
Overrides:
testForEquality in class JSType
+
+
+ +
Returns:
    +
  • TernaryValue.TRUE if the comparison of values of + this type and that always succeed (such as + undefined compared to null)
  • +
  • TernaryValue.FALSE if the comparison of values of + this type and that always fails (such as + undefined compared to number)
  • +
  • TernaryValue.UNKNOWN if the comparison can succeed or + fail depending on the concrete values
  • +
+
+
+
+ +

+matchesNumberContext

+
+public boolean matchesNumberContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator. +

+

+
Overrides:
matchesNumberContext in class JSType
+
+
+
+
+
+
+ +

+matchesObjectContext

+
+public boolean matchesObjectContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement. + + Most types we will encounter, except notably null, have at least + the potential for converting to Object. Host defined objects can + get peculiar. +

+

+
Overrides:
matchesObjectContext in class JSType
+
+
+
+
+
+
+ +

+matchesStringContext

+
+public boolean matchesStringContext()
+
+
Description copied from class: JSType
+
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator. + + All types have at least the potential for converting to String. + When we add externally defined types, such as a browser OM, we may choose + to add types that do not automatically convert to String. +

+

+
Overrides:
matchesStringContext in class JSType
+
+
+
+
+
+
+ +

+isVoidType

+
+public boolean isVoidType()
+
+
+
Overrides:
isVoidType in class JSType
+
+
+
+
+
+
+ +

+getDisplayName

+
+public String getDisplayName()
+
+
Description copied from class: JSType
+
Returns a user meaningful label for the JSType instance. For example, + Functions and Enums will return their declaration name (if they have one). + Some types will not have a meaningful display name. Calls to + hasDisplayName() will return true IFF getDisplayName() will return null + or a zero length string. +

+

+
Overrides:
getDisplayName in class JSType
+
+
+ +
Returns:
the display name of the type, or null if one is not available
+
+
+
+ +

+getPossibleToBooleanOutcomes

+
+public BooleanLiteralSet getPossibleToBooleanOutcomes()
+
+
Description copied from class: JSType
+
Computes the set of possible outcomes of the ToBoolean predicate + for this type. The ToBoolean predicate is defined by the ECMA-262 + standard, 3rd edition. Its behavior for simple types can be + summarized by the following table: + + + + + + + + +
typeresult
undefined{false}
null{false}
boolean{true, false}
number{true, false}
string{true, false}
Object{true}
+

+

+
Specified by:
getPossibleToBooleanOutcomes in class JSType
+
+
+ +
Returns:
the set of boolean literals for this type
+
+
+
+ +

+visit

+
+public <T> T visit(Visitor<T> visitor)
+
+
Description copied from class: JSType
+
Visit this type with the given visitor. +

+

+
Specified by:
visit in class JSType
+
+
+ +
Returns:
the value returned by the visitor
See Also:
Visitor
+
+
+
+ +

+hasDisplayName

+
+public boolean hasDisplayName()
+
+
+
Overrides:
hasDisplayName in class JSType
+
+
+ +
Returns:
true if the JSType has a user meaningful label.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/package-frame.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/package-frame.html new file mode 100644 index 0000000..5537a2b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/package-frame.html @@ -0,0 +1,116 @@ + + + + + +com.google.javascript.rhino.jstype (Compiler) + + + + + + + + + + +com.google.javascript.rhino.jstype + + + + +
+Interfaces  + +
+StaticReference +
+StaticScope +
+StaticSlot +
+StaticSourceFile +
+StaticSymbolTable +
+Visitor
+ + + + + + +
+Classes  + +
+AllType +
+BooleanType +
+EnumElementType +
+EnumType +
+FunctionBuilder +
+FunctionParamBuilder +
+FunctionType +
+JSType +
+JSType.TypePair +
+JSTypeRegistry +
+NoObjectType +
+NoType +
+NullType +
+NumberType +
+ObjectType +
+ObjectType.Property +
+RecordTypeBuilder +
+SimpleReference +
+SimpleSlot +
+SimpleSourceFile +
+StringType +
+TemplateType +
+UnionType +
+UnknownType +
+VoidType
+ + + + + + +
+Enums  + +
+BooleanLiteralSet +
+JSTypeNative +
+JSTypeRegistry.ResolveMode +
+TernaryValue
+ + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/package-summary.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/package-summary.html new file mode 100644 index 0000000..970037c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/package-summary.html @@ -0,0 +1,352 @@ + + + + + +com.google.javascript.rhino.jstype (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+

+Package com.google.javascript.rhino.jstype +

+Provides abstractions to represent types in JavaScript. +

+See: +
+          Description +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Interface Summary
StaticReference<T>The StaticReference tells us all the ways that a StaticSlot + is used in a program.
StaticScope<T>The StaticScope interface must be implemented by any object that + defines variables for the purposes of static analysis.
StaticSlot<T>The StaticSlot interface must be implemented by variables that can + appear as members of a StaticScope.
StaticSourceFileThe StaticSourceFile contains information about a compiler input.
StaticSymbolTable<S extends StaticSlot<JSType>,R extends StaticReference<JSType>>Lookup references by the symbols that they refer to.
Visitor<T>A type visitor.
+  + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Class Summary
AllTypeAll type, representing all values.
BooleanTypeBoolean type.
EnumElementTypeThe type of individual elements of an enum type + (see EnumType).
EnumTypeAn enum type representing a branded collection of elements.
FunctionBuilderA builder class for function and arrow types.
FunctionParamBuilderA builder for the Rhino Node representing Function parameters.
FunctionTypeThis derived type provides extended information about a function, including + its return type and argument types.
JSTypeRepresents JavaScript value types.
JSType.TypePair 
JSTypeRegistryThe type registry is used to resolve named types.
NoObjectTypeThe bottom Object type, representing the subclass of all objects.
NoTypeBottom type, representing the subclass of any value or object.
NullTypeNull type.
NumberTypeNumber type.
ObjectTypeObject type.
ObjectType.Property 
RecordTypeBuilderA builder for record types.
SimpleReference<T extends StaticSlot<JSType>>A simple immutable reference.
SimpleSlotThe minimum implementation of StaticSlot.
SimpleSourceFileA simple implementation of StaticSourceFile for testing.
StringTypeString type.
TemplateType 
UnionTypeThe UnionType implements a common JavaScript idiom in which the + code is specifically designed to work with multiple input types.
UnknownTypeThe Unknown type.
VoidTypeVoid type whose only element is the undefined value.
+  + +

+ + + + + + + + + + + + + + + + + + + + + +
+Enum Summary
BooleanLiteralSetA set in the domain {true,false}.
JSTypeNativeConstants corresponding to types that are built into a JavaScript engine + and other types that occur very often in the type system.
JSTypeRegistry.ResolveModeThe type registry has three modes, which control how type ASTs are + converted to types in JSTypeRegistry.createFromTypeNodes(com.google.javascript.rhino.Node, java.lang.String, com.google.javascript.rhino.jstype.StaticScope).
TernaryValueAn enum for ternary logic.
+  + +

+

+Package com.google.javascript.rhino.jstype Description +

+ +

+Provides abstractions to represent types in JavaScript. + +Rhino is an open-source implementation of JavaScript written entirely in Java. +It is typically embedded into Java applications to provide scripting to end +users. +

+ +

+

+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/package-tree.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/package-tree.html new file mode 100644 index 0000000..38a8005 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/jstype/package-tree.html @@ -0,0 +1,195 @@ + + + + + +com.google.javascript.rhino.jstype Class Hierarchy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Hierarchy For Package com.google.javascript.rhino.jstype +

+
+
+
Package Hierarchies:
All Packages
+
+

+Class Hierarchy +

+ +

+Interface Hierarchy +

+ +

+Enum Hierarchy +

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/package-frame.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/package-frame.html new file mode 100644 index 0000000..5afba0e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/package-frame.html @@ -0,0 +1,84 @@ + + + + + +com.google.javascript.rhino (Compiler) + + + + + + + + + + +com.google.javascript.rhino + + + + +
+Interfaces  + +
+ErrorReporter
+ + + + + + +
+Classes  + +
+InputId +
+IR +
+JSDocInfo +
+JSDocInfo.Marker +
+JSDocInfo.NamePosition +
+JSDocInfo.StringPosition +
+JSDocInfo.TypePosition +
+JSDocInfoBuilder +
+JSTypeExpression +
+Node +
+Node.AncestorIterable +
+Node.SideEffectFlags +
+ScriptRuntime +
+SimpleErrorReporter +
+SourcePosition +
+Token +
+TokenStream
+ + + + + + +
+Enums  + +
+JSDocInfo.Visibility
+ + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/package-summary.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/package-summary.html new file mode 100644 index 0000000..49bd801 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/package-summary.html @@ -0,0 +1,288 @@ + + + + + +com.google.javascript.rhino (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+

+Package com.google.javascript.rhino +

+The core AST from Rhino. +

+See: +
+          Description +

+ + + + + + + + + +
+Interface Summary
ErrorReporterThis is interface defines a protocol for the reporting of + errors during JavaScript translation or execution.
+  + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Class Summary
InputIdAn id used uniquely identify a CompilerInput
IRAn AST construction helper class
JSDocInfoJSDoc information describing JavaScript code.
JSDocInfo.MarkerDefines a class for containing the parsing information + for this JSDocInfo.
JSDocInfo.NamePositionA piece of information (found in a marker) which contains a position + with a name node.
JSDocInfo.StringPositionA piece of information (found in a marker) which contains a position + with a string.
JSDocInfo.TypePositionA piece of information (found in a marker) which contains a position + with a type expression syntax tree.
JSDocInfoBuilderA builder for JSDocInfo objects.
JSTypeExpressionRepresents a type expression as a miniture Rhino AST, so that the + type expression can be evaluated later.
NodeThis class implements the root of the intermediate representation.
Node.AncestorIterableIterator to go up the ancestor tree.
Node.SideEffectFlagsA helper class for getting and setting the side-effect flags.
ScriptRuntimeThis is the class that implements the runtime.
SimpleErrorReporterA simple ErrorReporter that collects warnings and errors and makes + them accessible via SimpleErrorReporter.errors() and SimpleErrorReporter.warnings().
SourcePosition<T>Represents a position in some piece of source code, with an associated + item of type T found at that position.
TokenThis class implements the JavaScript scanner.
TokenStreamThis class implements the JavaScript scanner.
+  + +

+ + + + + + + + + +
+Enum Summary
JSDocInfo.VisibilityVisibility categories.
+  + +

+

+Package com.google.javascript.rhino Description +

+ +

+The core AST from Rhino. + +Rhino is an open-source implementation of JavaScript written entirely in Java. +It is typically embedded into Java applications to provide scripting to end +users. +

+ +

+

+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/package-tree.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/package-tree.html new file mode 100644 index 0000000..54d7cc9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/package-tree.html @@ -0,0 +1,188 @@ + + + + + +com.google.javascript.rhino Class Hierarchy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Hierarchy For Package com.google.javascript.rhino +

+
+
+
Package Hierarchies:
All Packages
+
+

+Class Hierarchy +

+ +

+Interface Hierarchy +

+ +

+Enum Hierarchy +

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/AbstractStaticScope.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/AbstractStaticScope.html new file mode 100644 index 0000000..e638234 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/AbstractStaticScope.html @@ -0,0 +1,384 @@ + + + + + +AbstractStaticScope (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.testing +
+Class AbstractStaticScope<T>

+
+java.lang.Object
+  extended by com.google.javascript.rhino.testing.AbstractStaticScope<T>
+
+
+
All Implemented Interfaces:
StaticScope<T>
+
+
+
Direct Known Subclasses:
MapBasedScope
+
+
+
+
public abstract class AbstractStaticScope<T>
extends Object
implements StaticScope<T>
+ + +

+A scope that just returns null for everything. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
AbstractStaticScope() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ StaticSlot<T>getOwnSlot(String name) + +
+          Like getSlot but does not recurse into parent scopes.
+ StaticScope<T>getParentScope() + +
+          Returns the scope enclosing this one or null if none.
+ NodegetRootNode() + +
+          Returns the root node associated with this scope.
+abstract  StaticSlot<T>getSlot(String name) + +
+          Returns any defined slot within this scope for this name.
+ TgetTypeOfThis() + +
+          Returns the expected type of this in the current scope.
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+AbstractStaticScope

+
+public AbstractStaticScope()
+
+
+ + + + + + + + +
+Method Detail
+ +

+getRootNode

+
+public Node getRootNode()
+
+
Description copied from interface: StaticScope
+
Returns the root node associated with this scope. May be null. +

+

+
Specified by:
getRootNode in interface StaticScope<T>
+
+
+
+
+
+
+ +

+getParentScope

+
+public StaticScope<T> getParentScope()
+
+
Description copied from interface: StaticScope
+
Returns the scope enclosing this one or null if none. +

+

+
Specified by:
getParentScope in interface StaticScope<T>
+
+
+
+
+
+
+ +

+getSlot

+
+public abstract StaticSlot<T> getSlot(String name)
+
+
Description copied from interface: StaticScope
+
Returns any defined slot within this scope for this name. This call + continues searching through parent scopes if a slot with this name is not + found in the current scope. +

+

+
Specified by:
getSlot in interface StaticScope<T>
+
+
+
Parameters:
name - The name of the variable slot to look up. +
Returns:
The defined slot for the variable, or null if no + definition exists.
+
+
+
+ +

+getOwnSlot

+
+public StaticSlot<T> getOwnSlot(String name)
+
+
Description copied from interface: StaticScope
+
Like getSlot but does not recurse into parent scopes. +

+

+
Specified by:
getOwnSlot in interface StaticScope<T>
+
+
+
+
+
+
+ +

+getTypeOfThis

+
+public T getTypeOfThis()
+
+
Description copied from interface: StaticScope
+
Returns the expected type of this in the current scope. +

+

+
Specified by:
getTypeOfThis in interface StaticScope<T>
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/Asserts.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/Asserts.html new file mode 100644 index 0000000..602a7df --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/Asserts.html @@ -0,0 +1,390 @@ + + + + + +Asserts (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.testing +
+Class Asserts

+
+java.lang.Object
+  extended by com.google.javascript.rhino.testing.Asserts
+
+
+
+
public class Asserts
extends Object
+ + +

+Helper methods for making assertions about the validity of types. +

+ +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static voidassertEquivalenceOperations(JSType a, + JSType b) + +
+          For the given equivalent types, run all type operations that + should have trivial solutions (getGreatestSubtype, isEquivalentTo, etc)
+static JSTypeassertResolvesToSame(JSType type) + +
+           
+static voidassertTypeEquals(JSType a, + JSType b) + +
+           
+static voidassertTypeEquals(String message, + JSType a, + JSType b) + +
+           
+static voidassertTypeNotEquals(JSType a, + JSType b) + +
+           
+static voidassertTypeNotEquals(String message, + JSType a, + JSType b) + +
+           
+static JSTypeassertValidResolve(JSType type) + +
+           
+static JSTypeassertValidResolve(JSType type, + StaticScope<JSType> scope) + +
+           
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Method Detail
+ +

+assertResolvesToSame

+
+public static JSType assertResolvesToSame(JSType type)
+
+
+
+
+
+
+ +

+assertValidResolve

+
+public static JSType assertValidResolve(JSType type)
+
+
+ +
Returns:
The resolved type
+
+
+
+ +

+assertValidResolve

+
+public static JSType assertValidResolve(JSType type,
+                                        StaticScope<JSType> scope)
+
+
+ +
Returns:
The resolved type
+
+
+
+ +

+assertTypeNotEquals

+
+public static void assertTypeNotEquals(JSType a,
+                                       JSType b)
+
+
+
+
+
+
+ +

+assertTypeNotEquals

+
+public static void assertTypeNotEquals(String message,
+                                       JSType a,
+                                       JSType b)
+
+
+
+
+
+
+ +

+assertTypeEquals

+
+public static void assertTypeEquals(JSType a,
+                                    JSType b)
+
+
+
+
+
+
+ +

+assertTypeEquals

+
+public static void assertTypeEquals(String message,
+                                    JSType a,
+                                    JSType b)
+
+
+
+
+
+
+ +

+assertEquivalenceOperations

+
+public static void assertEquivalenceOperations(JSType a,
+                                               JSType b)
+
+
For the given equivalent types, run all type operations that + should have trivial solutions (getGreatestSubtype, isEquivalentTo, etc) +

+

+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/BaseJSTypeTestCase.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/BaseJSTypeTestCase.html new file mode 100644 index 0000000..12d3def --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/BaseJSTypeTestCase.html @@ -0,0 +1,1582 @@ + + + + + +BaseJSTypeTestCase (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.testing +
+Class BaseJSTypeTestCase

+
+java.lang.Object
+  extended by junit.framework.Assert
+      extended by junit.framework.TestCase
+          extended by com.google.javascript.rhino.testing.BaseJSTypeTestCase
+
+
+
All Implemented Interfaces:
junit.framework.Test
+
+
+
+
public abstract class BaseJSTypeTestCase
extends junit.framework.TestCase
+ + +

+


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Field Summary
+static StringALL_NATIVE_EXTERN_TYPES + +
+          A definition of all extern types.
+protected  JSTypeALL_TYPE + +
+           
+protected  JSTypeARRAY_FUNCTION_TYPE + +
+           
+protected  ObjectTypeARRAY_TYPE + +
+           
+protected  JSTypeBOOLEAN_OBJECT_FUNCTION_TYPE + +
+           
+protected  ObjectTypeBOOLEAN_OBJECT_TYPE + +
+           
+protected  JSTypeBOOLEAN_TYPE + +
+           
+protected  JSTypeCHECKED_UNKNOWN_TYPE + +
+           
+protected  JSTypeDATE_FUNCTION_TYPE + +
+           
+protected  ObjectTypeDATE_TYPE + +
+           
+protected  JSTypeERROR_FUNCTION_TYPE + +
+           
+protected  ObjectTypeERROR_TYPE + +
+           
+protected  TestErrorReportererrorReporter + +
+           
+protected  JSTypeEVAL_ERROR_FUNCTION_TYPE + +
+           
+protected  ObjectTypeEVAL_ERROR_TYPE + +
+           
+protected  FunctionTypeFUNCTION_FUNCTION_TYPE + +
+           
+protected  FunctionTypeFUNCTION_INSTANCE_TYPE + +
+           
+protected  ObjectTypeFUNCTION_PROTOTYPE + +
+           
+protected  JSTypeGREATEST_FUNCTION_TYPE + +
+           
+protected  JSTypeLEAST_FUNCTION_TYPE + +
+           
+protected  JSTypeMATH_TYPE + +
+           
+protected  intNATIVE_PROPERTIES_COUNT + +
+           
+protected  ObjectTypeNO_OBJECT_TYPE + +
+           
+protected  ObjectTypeNO_RESOLVED_TYPE + +
+           
+protected  ObjectTypeNO_TYPE + +
+           
+protected  JSTypeNULL_TYPE + +
+           
+protected  JSTypeNUMBER_OBJECT_FUNCTION_TYPE + +
+           
+protected  ObjectTypeNUMBER_OBJECT_TYPE + +
+           
+protected  JSTypeNUMBER_STRING_BOOLEAN + +
+           
+protected  JSTypeNUMBER_TYPE + +
+           
+protected  FunctionTypeOBJECT_FUNCTION_TYPE + +
+           
+protected  JSTypeOBJECT_NUMBER_STRING + +
+           
+protected  JSTypeOBJECT_NUMBER_STRING_BOOLEAN + +
+           
+protected  JSTypeOBJECT_PROTOTYPE + +
+           
+protected  ObjectTypeOBJECT_TYPE + +
+           
+protected  JSTypeRANGE_ERROR_FUNCTION_TYPE + +
+           
+protected  ObjectTypeRANGE_ERROR_TYPE + +
+           
+protected  JSTypeREFERENCE_ERROR_FUNCTION_TYPE + +
+           
+protected  ObjectTypeREFERENCE_ERROR_TYPE + +
+           
+protected  JSTypeREGEXP_FUNCTION_TYPE + +
+           
+protected  ObjectTypeREGEXP_TYPE + +
+           
+protected  JSTypeRegistryregistry + +
+           
+protected  JSTypeSTRING_OBJECT_FUNCTION_TYPE + +
+           
+protected  ObjectTypeSTRING_OBJECT_TYPE + +
+           
+protected  JSTypeSTRING_TYPE + +
+           
+protected  JSTypeSYNTAX_ERROR_FUNCTION_TYPE + +
+           
+protected  ObjectTypeSYNTAX_ERROR_TYPE + +
+           
+protected  JSTypeTYPE_ERROR_FUNCTION_TYPE + +
+           
+protected  ObjectTypeTYPE_ERROR_TYPE + +
+           
+protected  FunctionTypeU2U_CONSTRUCTOR_TYPE + +
+           
+protected  FunctionTypeU2U_FUNCTION_TYPE + +
+           
+protected  ObjectTypeUNKNOWN_TYPE + +
+           
+protected  JSTypeURI_ERROR_FUNCTION_TYPE + +
+           
+protected  ObjectTypeURI_ERROR_TYPE + +
+           
+protected  JSTypeVOID_TYPE + +
+           
+  + + + + + + + + + + +
+Constructor Summary
BaseJSTypeTestCase() + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+static voidaddNativeProperties(JSTypeRegistry registry) + +
+          Adds a basic set of properties to the native types.
+protected  voidassertTypeEquals(JSType a, + JSType b) + +
+           
+protected  voidassertTypeEquals(JSType expected, + JSTypeExpression actual) + +
+          Asserts that a a type expression resolves to the correct JSType.
+protected  voidassertTypeEquals(JSType expected, + Node actual) + +
+          Asserts that a Node representing a type expression resolves to the + correct JSType.
+protected  voidassertTypeEquals(String msg, + JSType a, + JSType b) + +
+           
+protected  voidassertTypeNotEquals(JSType a, + JSType b) + +
+           
+protected  voidassertTypeNotEquals(String msg, + JSType a, + JSType b) + +
+           
+protected  JSTypecreateNullableType(JSType type) + +
+           
+protected  JSTypecreateOptionalType(JSType type) + +
+           
+protected  RecordTypeBuildercreateRecordTypeBuilder() + +
+           
+protected  JSTypecreateUnionType(JSType... variants) + +
+           
+protected  voidinitTypes() + +
+           
+protected  JSTyperesolve(JSTypeExpression n, + String... warnings) + +
+          Resolves a type expression, expecting the given warnings.
+protected  voidsetUp() + +
+           
+ + + + + + + +
Methods inherited from class junit.framework.TestCase
countTestCases, createResult, getName, run, run, runBare, runTest, setName, tearDown, toString
+ + + + + + + +
Methods inherited from class junit.framework.Assert
assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, fail, fail, failNotEquals, failNotSame, failSame, format
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
+  +

+ + + + + + + + +
+Field Detail
+ +

+registry

+
+protected JSTypeRegistry registry
+
+
+
+
+
+ +

+errorReporter

+
+protected TestErrorReporter errorReporter
+
+
+
+
+
+ +

+ALL_TYPE

+
+protected JSType ALL_TYPE
+
+
+
+
+
+ +

+NO_OBJECT_TYPE

+
+protected ObjectType NO_OBJECT_TYPE
+
+
+
+
+
+ +

+NO_TYPE

+
+protected ObjectType NO_TYPE
+
+
+
+
+
+ +

+NO_RESOLVED_TYPE

+
+protected ObjectType NO_RESOLVED_TYPE
+
+
+
+
+
+ +

+ARRAY_FUNCTION_TYPE

+
+protected JSType ARRAY_FUNCTION_TYPE
+
+
+
+
+
+ +

+ARRAY_TYPE

+
+protected ObjectType ARRAY_TYPE
+
+
+
+
+
+ +

+BOOLEAN_OBJECT_FUNCTION_TYPE

+
+protected JSType BOOLEAN_OBJECT_FUNCTION_TYPE
+
+
+
+
+
+ +

+BOOLEAN_OBJECT_TYPE

+
+protected ObjectType BOOLEAN_OBJECT_TYPE
+
+
+
+
+
+ +

+BOOLEAN_TYPE

+
+protected JSType BOOLEAN_TYPE
+
+
+
+
+
+ +

+CHECKED_UNKNOWN_TYPE

+
+protected JSType CHECKED_UNKNOWN_TYPE
+
+
+
+
+
+ +

+DATE_FUNCTION_TYPE

+
+protected JSType DATE_FUNCTION_TYPE
+
+
+
+
+
+ +

+DATE_TYPE

+
+protected ObjectType DATE_TYPE
+
+
+
+
+
+ +

+ERROR_FUNCTION_TYPE

+
+protected JSType ERROR_FUNCTION_TYPE
+
+
+
+
+
+ +

+ERROR_TYPE

+
+protected ObjectType ERROR_TYPE
+
+
+
+
+
+ +

+EVAL_ERROR_FUNCTION_TYPE

+
+protected JSType EVAL_ERROR_FUNCTION_TYPE
+
+
+
+
+
+ +

+EVAL_ERROR_TYPE

+
+protected ObjectType EVAL_ERROR_TYPE
+
+
+
+
+
+ +

+FUNCTION_FUNCTION_TYPE

+
+protected FunctionType FUNCTION_FUNCTION_TYPE
+
+
+
+
+
+ +

+FUNCTION_INSTANCE_TYPE

+
+protected FunctionType FUNCTION_INSTANCE_TYPE
+
+
+
+
+
+ +

+FUNCTION_PROTOTYPE

+
+protected ObjectType FUNCTION_PROTOTYPE
+
+
+
+
+
+ +

+GREATEST_FUNCTION_TYPE

+
+protected JSType GREATEST_FUNCTION_TYPE
+
+
+
+
+
+ +

+LEAST_FUNCTION_TYPE

+
+protected JSType LEAST_FUNCTION_TYPE
+
+
+
+
+
+ +

+MATH_TYPE

+
+protected JSType MATH_TYPE
+
+
+
+
+
+ +

+NULL_TYPE

+
+protected JSType NULL_TYPE
+
+
+
+
+
+ +

+NUMBER_OBJECT_FUNCTION_TYPE

+
+protected JSType NUMBER_OBJECT_FUNCTION_TYPE
+
+
+
+
+
+ +

+NUMBER_OBJECT_TYPE

+
+protected ObjectType NUMBER_OBJECT_TYPE
+
+
+
+
+
+ +

+NUMBER_STRING_BOOLEAN

+
+protected JSType NUMBER_STRING_BOOLEAN
+
+
+
+
+
+ +

+NUMBER_TYPE

+
+protected JSType NUMBER_TYPE
+
+
+
+
+
+ +

+OBJECT_FUNCTION_TYPE

+
+protected FunctionType OBJECT_FUNCTION_TYPE
+
+
+
+
+
+ +

+OBJECT_NUMBER_STRING

+
+protected JSType OBJECT_NUMBER_STRING
+
+
+
+
+
+ +

+OBJECT_NUMBER_STRING_BOOLEAN

+
+protected JSType OBJECT_NUMBER_STRING_BOOLEAN
+
+
+
+
+
+ +

+OBJECT_PROTOTYPE

+
+protected JSType OBJECT_PROTOTYPE
+
+
+
+
+
+ +

+OBJECT_TYPE

+
+protected ObjectType OBJECT_TYPE
+
+
+
+
+
+ +

+RANGE_ERROR_FUNCTION_TYPE

+
+protected JSType RANGE_ERROR_FUNCTION_TYPE
+
+
+
+
+
+ +

+RANGE_ERROR_TYPE

+
+protected ObjectType RANGE_ERROR_TYPE
+
+
+
+
+
+ +

+REFERENCE_ERROR_FUNCTION_TYPE

+
+protected JSType REFERENCE_ERROR_FUNCTION_TYPE
+
+
+
+
+
+ +

+REFERENCE_ERROR_TYPE

+
+protected ObjectType REFERENCE_ERROR_TYPE
+
+
+
+
+
+ +

+REGEXP_FUNCTION_TYPE

+
+protected JSType REGEXP_FUNCTION_TYPE
+
+
+
+
+
+ +

+REGEXP_TYPE

+
+protected ObjectType REGEXP_TYPE
+
+
+
+
+
+ +

+STRING_OBJECT_FUNCTION_TYPE

+
+protected JSType STRING_OBJECT_FUNCTION_TYPE
+
+
+
+
+
+ +

+STRING_OBJECT_TYPE

+
+protected ObjectType STRING_OBJECT_TYPE
+
+
+
+
+
+ +

+STRING_TYPE

+
+protected JSType STRING_TYPE
+
+
+
+
+
+ +

+SYNTAX_ERROR_FUNCTION_TYPE

+
+protected JSType SYNTAX_ERROR_FUNCTION_TYPE
+
+
+
+
+
+ +

+SYNTAX_ERROR_TYPE

+
+protected ObjectType SYNTAX_ERROR_TYPE
+
+
+
+
+
+ +

+TYPE_ERROR_FUNCTION_TYPE

+
+protected JSType TYPE_ERROR_FUNCTION_TYPE
+
+
+
+
+
+ +

+TYPE_ERROR_TYPE

+
+protected ObjectType TYPE_ERROR_TYPE
+
+
+
+
+
+ +

+U2U_CONSTRUCTOR_TYPE

+
+protected FunctionType U2U_CONSTRUCTOR_TYPE
+
+
+
+
+
+ +

+U2U_FUNCTION_TYPE

+
+protected FunctionType U2U_FUNCTION_TYPE
+
+
+
+
+
+ +

+UNKNOWN_TYPE

+
+protected ObjectType UNKNOWN_TYPE
+
+
+
+
+
+ +

+URI_ERROR_FUNCTION_TYPE

+
+protected JSType URI_ERROR_FUNCTION_TYPE
+
+
+
+
+
+ +

+URI_ERROR_TYPE

+
+protected ObjectType URI_ERROR_TYPE
+
+
+
+
+
+ +

+VOID_TYPE

+
+protected JSType VOID_TYPE
+
+
+
+
+
+ +

+NATIVE_PROPERTIES_COUNT

+
+protected int NATIVE_PROPERTIES_COUNT
+
+
+
+
+
+ +

+ALL_NATIVE_EXTERN_TYPES

+
+public static final String ALL_NATIVE_EXTERN_TYPES
+
+
A definition of all extern types. This should be kept in sync with + javascript/externs/es3.js. This is used to check that the builtin types + declared in JSTypeRegistry have the same type as that in the + externs. It can also be used for any tests that want to use builtin types + in their externs. +

+

+
See Also:
Constant Field Values
+
+ + + + + + + + +
+Constructor Detail
+ +

+BaseJSTypeTestCase

+
+public BaseJSTypeTestCase()
+
+
+ + + + + + + + +
+Method Detail
+ +

+setUp

+
+protected void setUp()
+              throws Exception
+
+
+
Overrides:
setUp in class junit.framework.TestCase
+
+
+ +
Throws: +
Exception
+
+
+
+ +

+initTypes

+
+protected void initTypes()
+
+
+
+
+
+
+ +

+addNativeProperties

+
+public static void addNativeProperties(JSTypeRegistry registry)
+
+
Adds a basic set of properties to the native types. +

+

+
+
+
+
+ +

+createUnionType

+
+protected JSType createUnionType(JSType... variants)
+
+
+
+
+
+
+ +

+createRecordTypeBuilder

+
+protected RecordTypeBuilder createRecordTypeBuilder()
+
+
+
+
+
+
+ +

+createNullableType

+
+protected JSType createNullableType(JSType type)
+
+
+
+
+
+
+ +

+createOptionalType

+
+protected JSType createOptionalType(JSType type)
+
+
+
+
+
+
+ +

+assertTypeEquals

+
+protected void assertTypeEquals(JSType expected,
+                                Node actual)
+
+
Asserts that a Node representing a type expression resolves to the + correct JSType. +

+

+
+
+
+
+ +

+assertTypeEquals

+
+protected void assertTypeEquals(JSType expected,
+                                JSTypeExpression actual)
+
+
Asserts that a a type expression resolves to the correct JSType. +

+

+
+
+
+
+ +

+resolve

+
+protected JSType resolve(JSTypeExpression n,
+                         String... warnings)
+
+
Resolves a type expression, expecting the given warnings. +

+

+
+
+
+
+ +

+assertTypeEquals

+
+protected final void assertTypeEquals(JSType a,
+                                      JSType b)
+
+
+
+
+
+
+ +

+assertTypeEquals

+
+protected final void assertTypeEquals(String msg,
+                                      JSType a,
+                                      JSType b)
+
+
+
+
+
+
+ +

+assertTypeNotEquals

+
+protected final void assertTypeNotEquals(JSType a,
+                                         JSType b)
+
+
+
+
+
+
+ +

+assertTypeNotEquals

+
+protected final void assertTypeNotEquals(String msg,
+                                         JSType a,
+                                         JSType b)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/MapBasedScope.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/MapBasedScope.html new file mode 100644 index 0000000..e609c5e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/MapBasedScope.html @@ -0,0 +1,310 @@ + + + + + +MapBasedScope (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.testing +
+Class MapBasedScope

+
+java.lang.Object
+  extended by com.google.javascript.rhino.testing.AbstractStaticScope<JSType>
+      extended by com.google.javascript.rhino.testing.MapBasedScope
+
+
+
All Implemented Interfaces:
StaticScope<JSType>
+
+
+
+
public class MapBasedScope
extends AbstractStaticScope<JSType>
+ + +

+A scope based on a simple hashmap. +

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
MapBasedScope(Map<String,? extends JSType> namesToTypes) + +
+           
+  + + + + + + + + + + + + + + + +
+Method Summary
+static MapBasedScopeemptyScope() + +
+           
+ StaticSlot<JSType>getSlot(String name) + +
+          Returns any defined slot within this scope for this name.
+ + + + + + + +
Methods inherited from class com.google.javascript.rhino.testing.AbstractStaticScope
getOwnSlot, getParentScope, getRootNode, getTypeOfThis
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+MapBasedScope

+
+public MapBasedScope(Map<String,? extends JSType> namesToTypes)
+
+
+ + + + + + + + +
+Method Detail
+ +

+emptyScope

+
+public static MapBasedScope emptyScope()
+
+
+
+
+
+
+ +

+getSlot

+
+public StaticSlot<JSType> getSlot(String name)
+
+
Description copied from interface: StaticScope
+
Returns any defined slot within this scope for this name. This call + continues searching through parent scopes if a slot with this name is not + found in the current scope. +

+

+
Specified by:
getSlot in interface StaticScope<JSType>
Specified by:
getSlot in class AbstractStaticScope<JSType>
+
+
+
Parameters:
name - The name of the variable slot to look up. +
Returns:
The defined slot for the variable, or null if no + definition exists.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/TestErrorReporter.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/TestErrorReporter.html new file mode 100644 index 0000000..c426800 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/TestErrorReporter.html @@ -0,0 +1,462 @@ + + + + + +TestErrorReporter (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +com.google.javascript.rhino.testing +
+Class TestErrorReporter

+
+java.lang.Object
+  extended by junit.framework.Assert
+      extended by com.google.javascript.rhino.testing.TestErrorReporter
+
+
+
All Implemented Interfaces:
ErrorReporter
+
+
+
+
public final class TestErrorReporter
extends junit.framework.Assert
implements ErrorReporter
+ + +

+

An error reporter for testing that verifies that messages reported to the + reporter are expected.

+ +

Sample use

+
+ TestErrorReporter e =
+   new TestErrorReporter(null, new String[] { "first warning" });
+ ...
+ assertTrue(e.hasEncounteredAllWarnings());
+ 
+

+ +

+


+ +

+ + + + + + + + + + + +
+Constructor Summary
TestErrorReporter(String[] errors, + String[] warnings) + +
+           
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ voiderror(String message, + String sourceName, + int line, + int lineOffset) + +
+          Report an error.
+static TestErrorReporterforNoExpectedReports() + +
+           
+ booleanhasEncounteredAllErrors() + +
+          Returns whether all errors were reported to this reporter.
+ booleanhasEncounteredAllWarnings() + +
+          Returns whether all warnings were reported to this reporter.
+ voidsetErrors(String[] errors) + +
+           
+ voidsetWarnings(String[] warnings) + +
+           
+ voidwarning(String message, + String sourceName, + int line, + int lineOffset) + +
+          Report a warning.
+ + + + + + + +
Methods inherited from class junit.framework.Assert
assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, fail, fail, failNotEquals, failNotSame, failSame, format
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+TestErrorReporter

+
+public TestErrorReporter(String[] errors,
+                         String[] warnings)
+
+
+ + + + + + + + +
+Method Detail
+ +

+forNoExpectedReports

+
+public static TestErrorReporter forNoExpectedReports()
+
+
+
+
+
+
+
+
+
+ +

+setErrors

+
+public void setErrors(String[] errors)
+
+
+
+
+
+
+
+
+
+ +

+setWarnings

+
+public void setWarnings(String[] warnings)
+
+
+
+
+
+
+
+
+
+ +

+error

+
+public void error(String message,
+                  String sourceName,
+                  int line,
+                  int lineOffset)
+
+
Description copied from interface: ErrorReporter
+
Report an error. + + The implementing class is free to throw an exception if + it desires. + + If execution has not yet begun, the JavaScript engine is + free to find additional errors rather than terminating + the translation. It will not execute a script that had + errors, however. +

+

+
Specified by:
error in interface ErrorReporter
+
+
+
Parameters:
message - a String describing the error
sourceName - a String describing the JavaScript source + where the error occured; typically a filename or URL
line - the line number associated with the error
lineOffset - the offset into lineSource where problem was detected
+
+
+
+ +

+warning

+
+public void warning(String message,
+                    String sourceName,
+                    int line,
+                    int lineOffset)
+
+
Description copied from interface: ErrorReporter
+
Report a warning. + + The implementing class may choose to ignore the warning + if it desires. +

+

+
Specified by:
warning in interface ErrorReporter
+
+
+
Parameters:
message - a String describing the warning
sourceName - a String describing the JavaScript source + where the warning occured; typically a filename or URL
line - the line number associated with the warning
lineOffset - the offset into lineSource where problem was detected
+
+
+
+ +

+hasEncounteredAllWarnings

+
+public boolean hasEncounteredAllWarnings()
+
+
Returns whether all warnings were reported to this reporter. +

+

+
+
+
+
+
+
+
+ +

+hasEncounteredAllErrors

+
+public boolean hasEncounteredAllErrors()
+
+
Returns whether all errors were reported to this reporter. +

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/package-frame.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/package-frame.html new file mode 100644 index 0000000..69d10e3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/package-frame.html @@ -0,0 +1,38 @@ + + + + + +com.google.javascript.rhino.testing (Compiler) + + + + + + + + + + +com.google.javascript.rhino.testing + + + + +
+Classes  + +
+AbstractStaticScope +
+Asserts +
+BaseJSTypeTestCase +
+MapBasedScope +
+TestErrorReporter
+ + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/package-summary.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/package-summary.html new file mode 100644 index 0000000..bf455b4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/package-summary.html @@ -0,0 +1,186 @@ + + + + + +com.google.javascript.rhino.testing (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+

+Package com.google.javascript.rhino.testing +

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+Class Summary
AbstractStaticScope<T>A scope that just returns null for everything.
AssertsHelper methods for making assertions about the validity of types.
BaseJSTypeTestCase 
MapBasedScopeA scope based on a simple hashmap.
TestErrorReporterAn error reporter for testing that verifies that messages reported to the + reporter are expected.
+  + +

+

+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/package-tree.html b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/package-tree.html new file mode 100644 index 0000000..10f2863 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/com/google/javascript/rhino/testing/package-tree.html @@ -0,0 +1,174 @@ + + + + + +com.google.javascript.rhino.testing Class Hierarchy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Hierarchy For Package com.google.javascript.rhino.testing +

+
+
+
Package Hierarchies:
All Packages
+
+

+Class Hierarchy +

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/constant-values.html b/resources/defects4j-checkout-closure-1f/javadoc/constant-values.html new file mode 100644 index 0000000..1abd2d3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/constant-values.html @@ -0,0 +1,1464 @@ + + + + + +Constant Field Values (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Constant Field Values

+
+
+Contents + + + + + + +
+com.google.*
+ +

+ + + + + + + + + + + + + + + + + + + + + + +
com.google.debugging.sourcemap.proto.Mapping.LineMapping
+public static final intCOLUMN_POSITION_FIELD_NUMBER2
+public static final intLINE_NUMBER_FIELD_NUMBER1
+public static final intORIGINAL_MAPPING_FIELD_NUMBER3
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
com.google.debugging.sourcemap.proto.Mapping.OriginalMapping
+public static final intCOLUMN_POSITION_FIELD_NUMBER3
+public static final intIDENTIFIER_FIELD_NUMBER4
+public static final intLINE_NUMBER_FIELD_NUMBER2
+public static final intORIGINAL_FILE_FIELD_NUMBER1
+ +

+ +

+ + + + + + + + + + + + +
com.google.javascript.jscomp.CallGraph
+public static final StringMAIN_FUNCTION_NAME"{main}"
+ +

+ +

+ + + + + + + + + + + + + + + + + +
com.google.javascript.jscomp.FunctionInformationMap
+public static final intENTRY_FIELD_NUMBER1
+public static final intMODULE_FIELD_NUMBER101
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
com.google.javascript.jscomp.FunctionInformationMap.Entry
+public static final intCOMPILED_SOURCE_FIELD_NUMBER8
+public static final intID_FIELD_NUMBER2
+public static final intLINE_NUMBER_FIELD_NUMBER4
+public static final intMODULE_NAME_FIELD_NUMBER5
+public static final intNAME_FIELD_NUMBER7
+public static final intSIZE_FIELD_NUMBER6
+public static final intSOURCE_NAME_FIELD_NUMBER3
+ +

+ +

+ + + + + + + + + + + + + + + + + +
com.google.javascript.jscomp.FunctionInformationMap.Module
+public static final intCOMPILED_SOURCE_FIELD_NUMBER103
+public static final intNAME_FIELD_NUMBER102
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
com.google.javascript.jscomp.Instrumentation
+public static final intAPP_NAME_SETTER_FIELD_NUMBER5
+public static final intDECLARATION_TO_REMOVE_FIELD_NUMBER3
+public static final intINIT_FIELD_NUMBER4
+public static final intREPORT_CALL_FIELD_NUMBER2
+public static final intREPORT_DEFINED_FIELD_NUMBER1
+public static final intREPORT_EXIT_FIELD_NUMBER6
+ +

+ +

+ + + + + + + + + + + + +
com.google.javascript.jscomp.ObjectPropertyStringPreprocess
+public static final StringEXTERN_OBJECT_PROPERTY_STRING"JSCompiler_ObjectPropertyString"
+ +

+ +

+ + + + + + + + + + + + +
com.google.javascript.jscomp.SymbolTable
+public static final StringGLOBAL_THIS"*global*"
+ +

+ +

+ + + + + + + + + + + + +
com.google.javascript.jscomp.TypeCheck
+protected static final StringOVERRIDING_PROTOTYPE_WITH_NON_OBJECT"overriding prototype with non-object"
+ +

+ +

+ + + + + + + + + + + + +
com.google.javascript.jscomp.graph.FixedPointGraphTraversal<N,E>
+public static final StringNON_HALTING_ERROR_MSG"Fixed point computation not halting"
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
com.google.javascript.jscomp.jsonml.Validator
+public static final StringMISSING_ARGUMENT"No %s attribute specified for %s."
+public static final StringNOT_ENOUGH_CHILDREN_FMT"Not enough children for %s. Expected: %d. Found: %d."
+public static final StringTOO_MANY_CHILDREN_FMT"Too many children for %s. Expected: %d. Found: %d."
+public static final StringWRONG_CHILD_TYPE_FMT"Wrong type of child number %d for %s. Expected: %s. Found: %s."
+ +

+ +

+ + + + + + + + + + + + +
com.google.javascript.rhino.InputId
+public static final longserialVersionUID1L
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
com.google.javascript.rhino.Node
+public static final intBRACELESS_TYPE41
+public static final intCOLUMN_BITS12
+public static final intCOLUMN_MASK4095
+public static final intDECR_FLAG1
+public static final intDIRECT_EVAL49
+public static final intDIRECTIVES48
+public static final intEMPTY_BLOCK39
+public static final intFLAG_ARGUMENTS_UNMODIFIED4
+public static final intFLAG_GLOBAL_STATE_UNMODIFIED1
+public static final intFLAG_LOCAL_RESULTS16
+public static final intFLAG_NO_THROWS8
+public static final intFLAG_THIS_UNMODIFIED2
+public static final intFREE_CALL50
+public static final intINCRDECR_PROP32
+public static final intINPUT_ID53
+public static final intIS_CONSTANT_NAME43
+public static final intIS_DISPATCHER47
+public static final intIS_NAMESPACE46
+public static final intIS_OPTIONAL_PARAM44
+public static final intIS_VAR_ARGS_PARAM45
+public static final intJSDOC_INFO_PROP29
+public static final intLAST_PROP54
+public static final intLENGTH52
+public static final intMAX_COLUMN_NUMBER4095
+public static final intNO_SIDE_EFFECTS15
+public static final intOPT_ARG_NAME37
+public static final intORIGINALNAME_PROP40
+public static final intPARENTHESIZED_PROP35
+public static final intPOST_FLAG2
+public static final intQUOTED_PROP36
+public static final intSIDE_EFFECT_FLAGS42
+public static final intSIDE_EFFECTS_ALL0
+public static final intSIDE_EFFECTS_FLAGS_MASK31
+public static final intSLASH_V54
+public static final intSOURCENAME_PROP16
+public static final intSTATIC_SOURCE_FILE51
+public static final intSYNTHETIC_BLOCK_PROP38
+public static final intVAR_ARGS_NAME30
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
com.google.javascript.rhino.Token
+public static final intADD21
+public static final intAND101
+public static final intANNOTATION300
+public static final intARRAYLIT63
+public static final intASSIGN86
+public static final intASSIGN_ADD93
+public static final intASSIGN_BITAND89
+public static final intASSIGN_BITOR87
+public static final intASSIGN_BITXOR88
+public static final intASSIGN_DIV96
+public static final intASSIGN_LSH90
+public static final intASSIGN_MOD97
+public static final intASSIGN_MUL95
+public static final intASSIGN_RSH91
+public static final intASSIGN_SUB94
+public static final intASSIGN_URSH92
+public static final intBANG306
+public static final intBITAND11
+public static final intBITNOT27
+public static final intBITOR9
+public static final intBITXOR10
+public static final intBLOCK125
+public static final intBREAK116
+public static final intCALL37
+public static final intCASE111
+public static final intCATCH120
+public static final intCOLON310
+public static final intCOMMA85
+public static final intCONST149
+public static final intCONTINUE117
+public static final intDEBUGGER152
+public static final intDEC103
+public static final intDEFAULT112
+public static final intDEFAULT_CASE112
+public static final intDELPROP31
+public static final intDIV24
+public static final intDO114
+public static final intELLIPSIS305
+public static final intEMPTY124
+public static final intEOC303
+public static final intEQ12
+public static final intEQUALS307
+public static final intERROR-1
+public static final intEXPR_RESULT130
+public static final intFALSE43
+public static final intFOR115
+public static final intFUNCTION105
+public static final intGE17
+public static final intGET147
+public static final intGETELEM35
+public static final intGETPROP33
+public static final intGETTER_DEF147
+public static final intGT16
+public static final intHOOK98
+public static final intIF108
+public static final intIN51
+public static final intINC102
+public static final intINSTANCEOF52
+public static final intLABEL126
+public static final intLABEL_NAME153
+public static final intLB308
+public static final intLC309
+public static final intLE15
+public static final intLP83
+public static final intLSH18
+public static final intLT14
+public static final intMOD25
+public static final intMUL23
+public static final intNAME38
+public static final intNE13
+public static final intNEG29
+public static final intNEW30
+public static final intNOT26
+public static final intNULL41
+public static final intNUMBER39
+public static final intOBJECTLIT64
+public static final intOR100
+public static final intPARAM_LIST83
+public static final intPIPE301
+public static final intPOS28
+public static final intQMARK304
+public static final intREGEXP47
+public static final intRETURN4
+public static final intRSH19
+public static final intSCRIPT132
+public static final intSET148
+public static final intSETTER_DEF148
+public static final intSHEQ45
+public static final intSHNE46
+public static final intSTAR302
+public static final intSTRING40
+public static final intSUB22
+public static final intSWITCH110
+public static final intTHIS42
+public static final intTHROW49
+public static final intTRUE44
+public static final intTRY77
+public static final intTYPEOF32
+public static final intURSH20
+public static final intVAR118
+public static final intVOID122
+public static final intWHILE113
+public static final intWITH119
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
com.google.javascript.rhino.jstype.JSType
+public static final StringEMPTY_TYPE_COMPONENT"Named type with empty name component"
+public static final intENUMDECL1
+public static final StringNOT_A_CLASS"Not declared as a constructor"
+public static final StringNOT_A_TYPE"Not declared as a type name"
+public static final intNOT_ENUMDECL0
+public static final StringUNKNOWN_NAME"Unknown class name"
+ +

+ +

+ + + + + + + + + + + + +
com.google.javascript.rhino.testing.BaseJSTypeTestCase
+public static final StringALL_NATIVE_EXTERN_TYPES"/**\n * @constructor\n * @param {*} opt_value\n */\nfunction Object(opt_value) {}\n\n/**\n * @constructor\n * @extends {Object}\n * @param {*} var_args\n */\n\nfunction Function(var_args) {}\n/**\n * @constructor\n * @extends {Object}\n * @param {*} var_args\n * @return {!Array}\n */\nfunction Array(var_args) {}\n\n/**\n * @constructor\n * @param {*} opt_value\n * @return {boolean}\n */\nfunction Boolean(opt_value) {}\n\n/**\n * @constructor\n * @param {*} opt_value\n * @return {number}\n */\nfunction Number(opt_value) {}\n\n/**\n * @constructor\n * @return {string}\n */\nfunction Date(opt_yr_num, opt_mo_num, opt_day_num, opt_hr_num, opt_min_num, opt_sec_num, opt_ms_num) {}\n\n/**\n * @constructor\n * @extends {Object}\n * @param {*} opt_str\n * @return {string}\n */\nfunction String(opt_str) {}\n\n/**\n * @constructor\n * @param {*} opt_pattern\n * @param {*} opt_flags\n * @return {!RegExp}\n */\nfunction RegExp(opt_pattern, opt_flags) {}\n\n/**\n * @constructor\n * @param {*} opt_message\n * @param {*} opt_file\n * @param {*} opt_line\n * @return {!Error}\n */\nfunction Error(opt_message, opt_file, opt_line) {}\n\n/**\n * @constructor\n * @extends {Error}\n * @param {*} opt_message\n * @param {*} opt_file\n * @param {*} opt_line\n * @return {!EvalError}\n */\nfunction EvalError(opt_message, opt_file, opt_line) {}\n\n/**\n * @constructor\n * @extends {Error}\n * @param {*} opt_message\n * @param {*} opt_file\n * @param {*} opt_line\n * @return {!RangeError}\n */\nfunction RangeError(opt_message, opt_file, opt_line) {}\n\n/**\n * @constructor\n * @extends {Error}\n * @param {*} opt_message\n * @param {*} opt_file\n * @param {*} opt_line\n * @return {!ReferenceError}\n */\nfunction ReferenceError(opt_message, opt_file, opt_line) {}\n\n/**\n * @constructor\n * @extends {Error}\n * @param {*} opt_message\n * @param {*} opt_file\n * @param {*} opt_line\n * @return {!SyntaxError}\n */\nfunction SyntaxError(opt_message, opt_file, opt_line) {}\n\n/**\n * @constructor\n * @extends {Error}\n * @param {*} opt_message\n * @param {*} opt_file\n * @param {*} opt_line\n * @return {!TypeError}\n */\nfunction TypeError(opt_message, opt_file, opt_line) {}\n\n/**\n * @constructor\n * @extends {Error}\n * @param {*} opt_message\n * @param {*} opt_file\n * @param {*} opt_line\n * @return {!URIError}\n */\nfunction URIError(opt_message, opt_file, opt_line) {}\n\n/**\n * @param {string} progId\n * @param {string} opt_location\n * @constructor\n */\nfunction ActiveXObject(progId, opt_location) {}\n"
+ +

+ +

+


+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/deprecated-list.html b/resources/defects4j-checkout-closure-1f/javadoc/deprecated-list.html new file mode 100644 index 0000000..eb8f02f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/deprecated-list.html @@ -0,0 +1,228 @@ + + + + + +Deprecated List (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Deprecated API

+
+
+Contents + + + + + + + + + +
+Deprecated Fields
com.google.javascript.jscomp.JSError.level +
+          Use #getDefaultLevel 
+  +

+ + + + + + + + + + + + + + + + + + + + + + + +
+Deprecated Methods
com.google.javascript.jscomp.parsing.ParserRunner.createConfig(boolean) +
+           
com.google.javascript.jscomp.CompilerOptions.enableExternExports(boolean) +
+          replaced by CompilerOptions.setExternExports(boolean) 
com.google.javascript.rhino.JSDocInfo.Marker.getName() +
+          Use #getNameNode 
com.google.debugging.sourcemap.SourceMapSection.getSectionUrl() +
+            
com.google.javascript.rhino.JSDocInfoBuilder.markName(String, int, int) +
+          Use #markName(String, StaticSourceFile, int, int) 
com.google.javascript.jscomp.CompilerOptions.setRemoveUnusedVariable(CompilerOptions.Reach) +
+           
+  +

+ + + + + + + + +
+Deprecated Constructors
com.google.debugging.sourcemap.SourceMapSection(String, int, int) +
+            
+  +

+


+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/dev_javadoc.css b/resources/defects4j-checkout-closure-1f/javadoc/dev_javadoc.css new file mode 100644 index 0000000..64aec21 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/dev_javadoc.css @@ -0,0 +1,109 @@ +/* Javadoc style sheet */ +/* Author: Doug Kramer */ +/* Purpose: For use with Sun Javadoc tool, to define colors, fonts and other style attributes for Google look */ +/* Apply by specifying javadoc option "-stylesheetfile dev_javadoc.css" */ + +body { + font-family: arial, sans-serif; + background-color:#fff; + font-size: small; + margin: 3px 8px 8px 8px; /* Increasing top margin moves "All Classes" down in lower left frame, */ + color:#000; /* but unnecessarily moves increases gap in other 2 frames */ + } + +/* Needed to turn off "border=1" */ +table { + border: none; + border-collapse: collapse; + } + +/* Need "th" to bypass "bgcolor" embedded in */ +.TableSubHeadingColor th, +.TableHeadingColor th, +.TableRowColor td { + background: #E5ECF9; /* light blue */ + border-top: #3366CC solid thin; /* dark blue */ + border-bottom: #3366CC solid thin; + border-left: #3366CC solid thin; + border-right: #3366CC solid thin; + font-family: arial, sans-serif; + font-size: 100%; + } + +.TableHeadingColor th { + /* border-bottom: none; */ + /* border-left: none; */ + /* border-right: none; */ + } + +.TableRowColor td { + background: #FFFFFF; + padding: 6px 6px 6px 12px; + } + +/* Need "b" to bypass "font-size=+2" between and */ +.TableHeadingColor th b, +.TableSubHeadingColor th b { + font-size: 10pt; +} + +/* Font used in left-hand frame lists */ +.FrameTitleFont { font-size: 105%; font-family: Helvetica, Arial, sans-serif; } +.FrameHeadingFont { font-size: 100%; font-family: Helvetica, Arial, sans-serif; + font-weight: bold; margin-top: .3em; } +.FrameItemFont { font-size: 100%; font-family: Helvetica, Arial, sans-serif; } + +/* Navigation bar fonts and colors */ +.NavBarCell1 { padding-left: 2px; padding-right: 2px; font-size: 12px; + background-color:#EEEEFF;} /* Light mauve */ +.NavBarCell1Rev { background-color:#00008B;} /* Dark Blue */ +.NavBarFont1 { font-family: Arial, Helvetica, sans-serif; color:#000000; font-size: 120%;} +.NavBarFont1Rev { font-family: Arial, Helvetica, sans-serif; color:#FFFFFF; font-size: 120%;} + +.NavBarCell2 { font-family: Arial, Helvetica, sans-serif; background-color:#EEEEFF;} +.NavBarCell3 { font-family: Arial, Helvetica, sans-serif; background-color:#EEEEFF;} + +.NavBarCell1 em { font-size: 14px; } +.NavBarCell2 b { font-size: 9px; } +.NavBarCell3 b { font-size: 9px; } + +#searchbox { + margin-top: 0px; + margin-bottom: 0px; + } + +#navheader { + font-weight: bold; + margin-top: -1em; + margin-bottom: 0.3em; + } + +#doc_title{ /* in header */ + font-size:large; + font-weight:bold; + border-top: 1px solid; + background-color: #e5ecf9; + border-color: #3366CC; + margin:5px 0px 16px 0px; + padding:1px 0 1px 3px; + } + +#footer{ + height: 55px; + padding-top: 20px; + clear:both; + } + +#footerlogo{ /* in footer */ + position: absolute; + width: 175px; + padding:0; + margin:0; + left:10px; + } + +#copyright { /* in footer */ + text-align: center; + margin:0px; + padding-top:8px; + } diff --git a/resources/defects4j-checkout-closure-1f/javadoc/help-doc.html b/resources/defects4j-checkout-closure-1f/javadoc/help-doc.html new file mode 100644 index 0000000..99c686e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/help-doc.html @@ -0,0 +1,231 @@ + + + + + +API Help (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+How This API Document Is Organized

+
+This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.

+Overview

+
+ +

+The Overview page is the front page of this API document and provides a list of all packages with a summary for each. This page can also contain an overall description of the set of packages.

+

+Package

+
+ +

+Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain four categories:

    +
  • Interfaces (italic)
  • Classes
  • Enums
  • Exceptions
  • Errors
  • Annotation Types
+
+

+Class/Interface

+
+ +

+Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:

    +
  • Class inheritance diagram
  • Direct Subclasses
  • All Known Subinterfaces
  • All Known Implementing Classes
  • Class/interface declaration
  • Class/interface description +

    +

  • Nested Class Summary
  • Field Summary
  • Constructor Summary
  • Method Summary +

    +

  • Field Detail
  • Constructor Detail
  • Method Detail
+Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+

+Annotation Type

+
+ +

+Each annotation type has its own separate page with the following sections:

    +
  • Annotation Type declaration
  • Annotation Type description
  • Required Element Summary
  • Optional Element Summary
  • Element Detail
+
+ +

+Enum

+
+ +

+Each enum has its own separate page with the following sections:

    +
  • Enum declaration
  • Enum description
  • Enum Constant Summary
  • Enum Constant Detail
+
+

+Tree (Class Hierarchy)

+
+There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
    +
  • When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
  • When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+

+Deprecated API

+
+The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+

+Index

+
+The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+

+Prev/Next

+These links take you to the next or previous class, interface, package, or related page.

+Frames/No Frames

+These links show and hide the HTML frames. All pages are available with or without frames. +

+

+Serialized Form

+Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description. +

+

+Constant Field Values

+The Constant Field Values page lists the static final fields and their values. +

+ + +This help file applies to API documentation generated using the standard doclet. + +
+


+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/index-all.html b/resources/defects4j-checkout-closure-1f/javadoc/index-all.html new file mode 100644 index 0000000..088e21a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/index-all.html @@ -0,0 +1,10636 @@ + + + + + +Index (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +A B C D E F G H I J K L M N O P Q R S T U V W X
+

+A

+
+
AbstractCompiler - Class in com.google.javascript.jscomp
An abstract compiler, to help remove the circular dependency of + passes on JSCompiler.
AbstractCompiler() - +Constructor for class com.google.javascript.jscomp.AbstractCompiler +
  +
AbstractMessageFormatter - Class in com.google.javascript.jscomp
Abstract message formatter providing default behavior for implementations + of MessageFormatter needing a SourceExcerptProvider.
AbstractMessageFormatter(SourceExcerptProvider) - +Constructor for class com.google.javascript.jscomp.AbstractMessageFormatter +
  +
AbstractStaticScope<T> - Class in com.google.javascript.rhino.testing
A scope that just returns null for everything.
AbstractStaticScope() - +Constructor for class com.google.javascript.rhino.testing.AbstractStaticScope +
  +
acceptConstKeyword() - +Method in class com.google.javascript.jscomp.Compiler +
  +
acceptEcmaScript5() - +Method in class com.google.javascript.jscomp.Compiler +
  +
ACCESS_CONTROLS - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
add(E) - +Method in class com.google.javascript.jscomp.graph.StandardUnionFind +
  +
add(E) - +Method in interface com.google.javascript.jscomp.graph.UnionFind +
Adds the given element to a new set if it is not already in a set. +
add(SourceFile) - +Method in class com.google.javascript.jscomp.JSModule +
Adds a source file input to this module. +
add(CompilerInput) - +Method in class com.google.javascript.jscomp.JSModule +
Adds a source code input to this module. +
add(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
ADD - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
addAfter(CompilerInput, CompilerInput) - +Method in class com.google.javascript.jscomp.JSModule +
Adds a source code input to this module directly after other. +
addAlias(String, String) - +Method in interface com.google.javascript.jscomp.CompilerOptions.AliasTransformation +
Adds an alias definition to the AliasTransformation instance. +
addAllDeclarationToRemove(Iterable<String>) - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
addAllEntry(Iterable<? extends FunctionInformationMap.Entry>) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
addAllInit(Iterable<String>) - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
addAllModule(Iterable<? extends FunctionInformationMap.Module>) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
addAnonymousFunctions() - +Method in class com.google.javascript.jscomp.SymbolTable +
Finds anonymous functions in local scopes, and gives them names + and symbols. +
addAuthor(String) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Adds an author to the current information. +
addChild(int, JsonML) - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Inserts the given JsonML element at the given position in the + list of children. +
addChildAfter(Node, Node) - +Method in class com.google.javascript.rhino.Node +
Add 'child' after 'node'. +
addChildBefore(Node, Node) - +Method in class com.google.javascript.rhino.Node +
Add 'child' before 'node'. +
addChildrenAfter(Node, Node) - +Method in class com.google.javascript.rhino.Node +
Add all children after 'node'. +
addChildrenToBack(Node) - +Method in class com.google.javascript.rhino.Node +
  +
addChildrenToFront(Node) - +Method in class com.google.javascript.rhino.Node +
  +
addChildToBack(Node) - +Method in class com.google.javascript.rhino.Node +
  +
addChildToFront(Node) - +Method in class com.google.javascript.rhino.Node +
  +
addDeclarationToRemove(String) - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
addDependency(JSModule) - +Method in class com.google.javascript.jscomp.JSModule +
Adds a dependency on another module. +
addEntry(FunctionInformationMap.Entry) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
addEntry(int, FunctionInformationMap.Entry) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
addEntry(FunctionInformationMap.Entry.Builder) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
addEntry(int, FunctionInformationMap.Entry.Builder) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
addEntryBuilder() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
addEntryBuilder(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
addExterns(FileList) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Sets the externs file. +
addFirst(SourceFile) - +Method in class com.google.javascript.jscomp.JSModule +
Adds a source file input to this module. +
addFirst(CompilerInput) - +Method in class com.google.javascript.jscomp.JSModule +
Adds a source code input to this module. +
addInit(String) - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
addMapping(String, String, FilePosition, FilePosition, FilePosition) - +Method in interface com.google.debugging.sourcemap.SourceMapGenerator +
Adds a mapping for the given node. +
addMapping(String, String, FilePosition, FilePosition, FilePosition) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV1 +
Adds a mapping for the given node. +
addMapping(String, String, FilePosition, FilePosition, FilePosition) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV2 +
Adds a mapping for the given node. +
addMapping(String, String, FilePosition, FilePosition, FilePosition) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV3 +
Adds a mapping for the given node. +
addMapping(Node, FilePosition, FilePosition) - +Method in class com.google.javascript.jscomp.SourceMap +
  +
addModule(FunctionInformationMap.Module) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
addModule(int, FunctionInformationMap.Module) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
addModule(FunctionInformationMap.Module.Builder) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
addModule(int, FunctionInformationMap.Module.Builder) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
addModuleBuilder() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
addModuleBuilder(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
addNativeProperties(JSTypeRegistry) - +Static method in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
Adds a basic set of properties to the native types. +
addNewScript(JsAst) - +Method in class com.google.javascript.jscomp.Compiler +
Adds a new Script AST to the compile state. +
addNode(N) - +Method in interface com.google.javascript.jscomp.graph.SubGraph +
Adds the node into this subgraph. +
addOptionalParams(JSType...) - +Method in class com.google.javascript.rhino.jstype.FunctionParamBuilder +
Add optional parameters of the given type to the end of the param list. +
addPath(Path) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Adds a entry. +
addProperty(String, JSType, Node) - +Method in class com.google.javascript.rhino.jstype.RecordTypeBuilder +
Adds a property with the given name and type to the record type. +
addReference(String) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Adds a reference ("@see") to the current information. +
addRequiredParams(JSType...) - +Method in class com.google.javascript.rhino.jstype.FunctionParamBuilder +
Add parameters of the given type to the end of the param list. +
addSources(FileList) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Sets the source files. +
addSuppression(String) - +Method in class com.google.javascript.rhino.JSDocInfo +
Add a suppressed warning. +
addSuppression(String) - +Method in class com.google.javascript.rhino.Node +
Adds a warning to be suppressed. +
addVarArgs(JSType) - +Method in class com.google.javascript.rhino.jstype.FunctionParamBuilder +
Add variable arguments to the end of the parameter list. +
addWarning(Warning) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Adds a entry + + Each warning entry must have two attributes, group and level. +
addWarningsGuard(WarningsGuard) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Add a guard to the set of warnings guards. +
AdjacencyGraph<N,E> - Interface in com.google.javascript.jscomp.graph
A minimal graph interface.
aggressiveVarCheck - +Variable in class com.google.javascript.jscomp.CompilerOptions +
  +
aliasableStrings - +Variable in class com.google.javascript.jscomp.CompilerOptions +
If set to a non-empty set, those strings literals will be aliased to a + single global instance per string, to avoid creating more objects than + necessary. +
aliasAllStrings - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Aliases all string literals to global instances, to avoid creating more + objects than necessary (if true, overrides any set of strings passed in + to aliasableStrings) +
aliasExternals - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Adds variable aliases for externals to reduce code size +
aliasKeywords - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Aliases true, false, and null to variables with shorter names. +
aliasStringsBlacklist - +Variable in class com.google.javascript.jscomp.CompilerOptions +
A blacklist in the form of a regular expression to block strings that + contains certain words from being aliased. +
ALL_NATIVE_EXTERN_TYPES - +Static variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
A definition of all extern types. +
ALL_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
allEquivalenceClasses() - +Method in class com.google.javascript.jscomp.graph.StandardUnionFind +
  +
allEquivalenceClasses() - +Method in interface com.google.javascript.jscomp.graph.UnionFind +
Returns an immutable collection containing all equivalence classes. +
AllType - Class in com.google.javascript.rhino.jstype
All type, representing all values.
ambiguateProperties - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Rename unrelated properties to the same name to reduce code size. +
AMBIGUOUS_FUNCTION_DECL - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
and(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
and(TernaryValue) - +Method in enum com.google.javascript.rhino.jstype.TernaryValue +
Gets the and of this and that. +
AND - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
Annotatable - Interface in com.google.javascript.jscomp.graph
Object that has an annotation.
Annotation - Interface in com.google.javascript.jscomp.graph
Information that can be annotated to a GraphNode or + Graph.GraphEdge.
ANNOTATION - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
anonymousFunctionNaming - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Give anonymous functions names for easier debugging +
AnonymousFunctionNamingPolicy - Enum in com.google.javascript.jscomp
Strategies for how to do naming of anonymous functions that occur as + r-values in assignments and variable declarations.
AntErrorManager - Class in com.google.javascript.jscomp.ant
An error manager that pipes warnings and errors properly into the Ant + task infrastructure.
AntErrorManager(MessageFormatter, Task) - +Constructor for class com.google.javascript.jscomp.ant.AntErrorManager +
  +
APP_NAME_SETTER_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.Instrumentation +
  +
append(String) - +Method in class com.google.javascript.rhino.Node.FileLevelJsDocBuilder +
  +
appendChild(JsonML) - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Appends a given child element to the list of children. +
appendChildren(Collection<? extends JsonML>) - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Appends a collection of children to the back of the list of children. +
appendDebugInfo(StringBuilder) - +Method in class com.google.javascript.jscomp.regex.RegExpTree +
  +
appendIndexMapTo(Appendable, String, List<SourceMapSection>) - +Method in interface com.google.debugging.sourcemap.SourceMapGenerator +
Appends the index source map to the given buffer. +
appendIndexMapTo(Appendable, String, List<SourceMapSection>) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV1 +
  +
appendIndexMapTo(Appendable, String, List<SourceMapSection>) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV2 +
  +
appendIndexMapTo(Appendable, String, List<SourceMapSection>) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV3 +
Appends the index source map to the given buffer. +
appendPlaceholderReference(String) - +Method in class com.google.javascript.jscomp.JsMessage.Builder +
Appends a placeholder reference to the message +
appendSourceCode(StringBuilder) - +Method in class com.google.javascript.jscomp.regex.RegExpTree +
Appends this regular expression source to the given buffer. +
appendStringPart(String) - +Method in class com.google.javascript.jscomp.JsMessage.Builder +
Appends a translatable string literal to the message. +
appendStringTree(Appendable) - +Method in class com.google.javascript.rhino.Node +
  +
appendTo(Appendable, String) - +Method in interface com.google.debugging.sourcemap.SourceMapGenerator +
Appends the source map to the given buffer. +
appendTo(Appendable, String) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV1 +
Appends the source map in LavaBug format to the given buffer. +
appendTo(Appendable, String) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV2 +
Writes out the source map in the following format (line numbers are for + reference only and are not part of the format): + + 1. +
appendTo(Appendable, String) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV3 +
Writes out the source map in the following format (line numbers are for + reference only and are not part of the format): + + 1. +
appendTo(Appendable, String) - +Method in class com.google.javascript.jscomp.SourceMap +
  +
appendWhitelist(PrintStream) - +Method in class com.google.javascript.jscomp.WhitelistWarningsGuard.WhitelistBuilder +
Writes the warnings collected in a format that the WhitelistWarningsGuard + can read back later. +
applyDelegateRelationship(ObjectType, ObjectType, ObjectType, FunctionType, FunctionType) - +Method in interface com.google.javascript.jscomp.CodingConvention +
In many JS libraries, the function that creates a delegate relationship + also adds properties to the delegator and delegate base. +
applyDelegateRelationship(ObjectType, ObjectType, ObjectType, FunctionType, FunctionType) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
applySingletonGetter(FunctionType, FunctionType, ObjectType) - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
  +
applySingletonGetter(FunctionType, FunctionType, ObjectType) - +Method in interface com.google.javascript.jscomp.CodingConvention +
In many JS libraries, the function that adds a singleton getter to a class + adds properties to the class. +
applySingletonGetter(FunctionType, FunctionType, ObjectType) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
applySubclassRelationship(FunctionType, FunctionType, CodingConvention.SubclassType) - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
Closure's goog.inherits adds a superClass_ property to the + subclass, and a constructor property. +
applySubclassRelationship(FunctionType, FunctionType, CodingConvention.SubclassType) - +Method in interface com.google.javascript.jscomp.CodingConvention +
In many JS libraries, the function that produces inheritance also + adds properties to the superclass and/or subclass. +
applySubclassRelationship(FunctionType, FunctionType, CodingConvention.SubclassType) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
areAllFlagsSet() - +Method in class com.google.javascript.rhino.Node.SideEffectFlags +
  +
areEquivalent(E, E) - +Method in class com.google.javascript.jscomp.graph.StandardUnionFind +
  +
areEquivalent(E, E) - +Method in interface com.google.javascript.jscomp.graph.UnionFind +
Returns true if a and b belong to the same equivalence + class. +
ARRAY_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
ARRAY_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
arraylit(Node...) - +Static method in class com.google.javascript.rhino.IR +
  +
ARRAYLIT - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
assertEquivalenceOperations(JSType, JSType) - +Static method in class com.google.javascript.rhino.testing.Asserts +
For the given equivalent types, run all type operations that + should have trivial solutions (getGreatestSubtype, isEquivalentTo, etc) +
assertResolvesToSame(JSType) - +Static method in class com.google.javascript.rhino.testing.Asserts +
  +
Asserts - Class in com.google.javascript.rhino.testing
Helper methods for making assertions about the validity of types.
assertTypeEquals(JSType, JSType) - +Static method in class com.google.javascript.rhino.testing.Asserts +
  +
assertTypeEquals(String, JSType, JSType) - +Static method in class com.google.javascript.rhino.testing.Asserts +
  +
assertTypeEquals(JSType, Node) - +Method in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
Asserts that a Node representing a type expression resolves to the + correct JSType. +
assertTypeEquals(JSType, JSTypeExpression) - +Method in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
Asserts that a a type expression resolves to the correct JSType. +
assertTypeEquals(JSType, JSType) - +Method in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
assertTypeEquals(String, JSType, JSType) - +Method in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
assertTypeNotEquals(JSType, JSType) - +Static method in class com.google.javascript.rhino.testing.Asserts +
  +
assertTypeNotEquals(String, JSType, JSType) - +Static method in class com.google.javascript.rhino.testing.Asserts +
  +
assertTypeNotEquals(JSType, JSType) - +Method in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
assertTypeNotEquals(String, JSType, JSType) - +Method in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
assertValidResolve(JSType) - +Static method in class com.google.javascript.rhino.testing.Asserts +
  +
assertValidResolve(JSType, StaticScope<JSType>) - +Static method in class com.google.javascript.rhino.testing.Asserts +
  +
assign(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
ASSIGN - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
ASSIGN_ADD - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
ASSIGN_BITAND - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
ASSIGN_BITOR - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
ASSIGN_BITXOR - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
ASSIGN_DIV - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
ASSIGN_LSH - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
ASSIGN_MOD - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
ASSIGN_MUL - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
ASSIGN_RSH - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
ASSIGN_SUB - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
ASSIGN_URSH - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
assumeClosuresOnlyCaptureReferences() - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
assumeStrictThis() - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
AstValidator - Class in com.google.javascript.jscomp
This class walks the AST and validates that the structure is correct.
AstValidator(AstValidator.ViolationHandler) - +Constructor for class com.google.javascript.jscomp.AstValidator +
  +
AstValidator() - +Constructor for class com.google.javascript.jscomp.AstValidator +
  +
AstValidator.ViolationHandler - Interface in com.google.javascript.jscomp
 
autobox() - +Method in class com.google.javascript.rhino.jstype.JSType +
Dereference a type for property access. +
autobox() - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
autoboxesTo() - +Method in class com.google.javascript.rhino.jstype.BooleanType +
  +
autoboxesTo() - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
autoboxesTo() - +Method in class com.google.javascript.rhino.jstype.JSType +
Gets the type to which this type auto-boxes. +
autoboxesTo() - +Method in class com.google.javascript.rhino.jstype.NumberType +
  +
autoboxesTo() - +Method in class com.google.javascript.rhino.jstype.StringType +
  +
+
+

+B

+
+
BANG - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
BaseJSTypeTestCase - Class in com.google.javascript.rhino.testing
 
BaseJSTypeTestCase() - +Constructor for class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
BasicErrorManager - Class in com.google.javascript.jscomp
A basic error manager that sorts all errors and warnings reported to it to + generate a sorted report when the BasicErrorManager.generateReport() method + is called.
BasicErrorManager() - +Constructor for class com.google.javascript.jscomp.BasicErrorManager +
  +
BITAND - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
BITNOT - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
BITOR - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
BITXOR - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
block() - +Static method in class com.google.javascript.rhino.IR +
  +
block(Node) - +Static method in class com.google.javascript.rhino.IR +
  +
block(Node...) - +Static method in class com.google.javascript.rhino.IR +
  +
BLOCK - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
BOOLEAN_OBJECT_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
BOOLEAN_OBJECT_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
BOOLEAN_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
BooleanLiteralSet - Enum in com.google.javascript.rhino.jstype
A set in the domain {true,false}.
BooleanType - Class in com.google.javascript.rhino.jstype
Boolean type.
BRACELESS_TYPE - +Static variable in class com.google.javascript.rhino.Node +
  +
BREAK - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
breakNode() - +Static method in class com.google.javascript.rhino.IR +
  +
breakNode(Node) - +Static method in class com.google.javascript.rhino.IR +
  +
brokenClosureRequiresLevel - +Variable in class com.google.javascript.jscomp.CompilerOptions +
  +
build() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
build() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
build() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
build() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
build() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
build() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
build() - +Method in class com.google.javascript.jscomp.JsMessage.Builder +
  +
build(JsMessage.IdGenerator) - +Method in class com.google.javascript.jscomp.JsMessage.Builder +
  +
build(Node) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Builds a JSDocInfo object based on the populated information and + returns it. +
build() - +Method in class com.google.javascript.rhino.jstype.FunctionBuilder +
Construct a new function type. +
build() - +Method in class com.google.javascript.rhino.jstype.FunctionParamBuilder +
  +
build() - +Method in class com.google.javascript.rhino.jstype.RecordTypeBuilder +
Creates a record. +
builder() - +Static method in class com.google.javascript.jscomp.SourceFile +
Create a new builder for source files. +
buildFromCode(String, String) - +Method in class com.google.javascript.jscomp.SourceFile.Builder +
  +
buildFromFile(String) - +Method in class com.google.javascript.jscomp.SourceFile.Builder +
  +
buildFromFile(File) - +Method in class com.google.javascript.jscomp.SourceFile.Builder +
  +
buildFromGenerator(String, SourceFile.Generator) - +Method in class com.google.javascript.jscomp.SourceFile.Builder +
  +
buildFromInputStream(String, InputStream) - +Method in class com.google.javascript.jscomp.SourceFile.Builder +
  +
buildFromReader(String, Reader) - +Method in class com.google.javascript.jscomp.SourceFile.Builder +
  +
buildKnownSymbolTable() - +Method in class com.google.javascript.jscomp.Compiler +
  +
buildPartial() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
buildPartial() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
buildPartial() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
buildPartial() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
buildPartial() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
buildPartial() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
+
+

+C

+
+
call(Node, Node...) - +Static method in class com.google.javascript.rhino.IR +
  +
CALL - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
CallGraph - Class in com.google.javascript.jscomp
A pass the uses a DefinitionProvider to compute a call graph for an + AST.
CallGraph(AbstractCompiler, boolean, boolean) - +Constructor for class com.google.javascript.jscomp.CallGraph +
Creates a call graph object supporting the specified lookups. +
CallGraph(AbstractCompiler) - +Constructor for class com.google.javascript.jscomp.CallGraph +
Creates a call graph object support both forward and backward lookups. +
CallGraph.Callsite - Class in com.google.javascript.jscomp
An inner class that represents call sites in the call graph.
CallGraph.Function - Class in com.google.javascript.jscomp
An inner class that represents functions in the call graph.
canAssignTo(JSType) - +Method in class com.google.javascript.rhino.jstype.JSType +
Tests whether values of this type can be safely assigned + to values of that type. +
canAssignTo(JSType) - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
canAssignTo(JSType) - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
canBeCalled() - +Method in class com.google.javascript.rhino.jstype.AllType +
  +
canBeCalled() - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
canBeCalled() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
canBeCalled() - +Method in class com.google.javascript.rhino.jstype.JSType +
This predicate is used to test whether a given type can be used as the + 'function' in a function call. +
canBeCalled() - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
canBeCalled() - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
canPropertyBeDefined(JSType, String) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Returns whether the given property can possibly be set on the given type. +
canTestForEqualityWith(JSType) - +Method in class com.google.javascript.rhino.jstype.JSType +
Tests whether this and that are meaningfully + comparable. +
canTestForShallowEqualityWith(JSType) - +Method in class com.google.javascript.rhino.jstype.JSType +
Tests whether this and that are meaningfully + comparable using shallow comparison. +
CASE - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
CASE_SENSITIVE - +Static variable in class com.google.javascript.jscomp.regex.CaseCanonicalize +
Set of code units that are case-insensitively equivalent to some other + code unit according to the EcmaScript + Canonicalize operation + described in section 15.10.2.8. +
caseAllType() - +Method in interface com.google.javascript.rhino.jstype.Visitor +
All type's case. +
caseBooleanType() - +Method in interface com.google.javascript.rhino.jstype.Visitor +
Boolean value type's case. +
CaseCanonicalize - Class in com.google.javascript.jscomp.regex
Implements the EcmaScript 5 + Canonicalize operation + used to specify how case-insensitive regular expressions match.
caseCanonicalize(String) - +Static method in class com.google.javascript.jscomp.regex.CaseCanonicalize +
Returns the case canonical version of the given string. +
caseCanonicalize(char) - +Static method in class com.google.javascript.jscomp.regex.CaseCanonicalize +
Returns the case canonical version of the given code-unit. +
caseEnumElementType(EnumElementType) - +Method in interface com.google.javascript.rhino.jstype.Visitor +
Enum element type's case. +
caseFunctionType(FunctionType) - +Method in interface com.google.javascript.rhino.jstype.Visitor +
Function type's case. +
caseNode(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
caseNoObjectType() - +Method in interface com.google.javascript.rhino.jstype.Visitor +
Bottom Object type's case. +
caseNoType() - +Method in interface com.google.javascript.rhino.jstype.Visitor +
Bottom type's case. +
caseNullType() - +Method in interface com.google.javascript.rhino.jstype.Visitor +
Null type's case. +
caseNumberType() - +Method in interface com.google.javascript.rhino.jstype.Visitor +
Number value type's case. +
caseObjectType(ObjectType) - +Method in interface com.google.javascript.rhino.jstype.Visitor +
Object type's case. +
caseStringType() - +Method in interface com.google.javascript.rhino.jstype.Visitor +
String value type's case. +
caseUnionType(UnionType) - +Method in interface com.google.javascript.rhino.jstype.Visitor +
Union type's case. +
caseUnknownType() - +Method in interface com.google.javascript.rhino.jstype.Visitor +
Unknown type's case. +
caseVoidType() - +Method in interface com.google.javascript.rhino.jstype.Visitor +
Void type's case. +
cast(JSType) - +Static method in class com.google.javascript.rhino.jstype.ObjectType +
A null-safe version of JSType#toObjectType. +
CATCH - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
catchNode(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
changes - +Variable in class com.google.javascript.jscomp.PerformanceTracker.Stats +
  +
charAt(int) - +Method in class com.google.javascript.jscomp.JsMessage.PlaceholderReference +
  +
check() - +Method in class com.google.javascript.jscomp.Compiler +
  +
check(Node, boolean) - +Method in class com.google.javascript.jscomp.TypeCheck +
  +
CHECK_PROVIDES - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
CHECK_REGEXP - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
CHECK_TYPES - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
CHECK_USELESS_CODE - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
CHECK_VARIABLES - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
checkControlStructures - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Checks for invalid control structures +
CHECKED_UNKNOWN_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
checkForCallingConventionDefiningCalls(Node, Map<String, String>) - +Method in interface com.google.javascript.jscomp.CodingConvention +
Checks for function calls that set the calling conventions on delegate + methods. +
checkForCallingConventionDefiningCalls(Node, Map<String, String>) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
checkGlobalNamesLevel - +Variable in class com.google.javascript.jscomp.CompilerOptions +
  +
checkGlobalThisLevel - +Variable in class com.google.javascript.jscomp.CompilerOptions +
  +
CheckLevel - Enum in com.google.javascript.jscomp
Controls checking levels of certain options.
CheckLevelLegacy - Enum in com.google.javascript.jscomp
Enum used in flags to control the behavior of JS compiler checks.
checkMissingGetCssNameBlacklist - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Regex of string literals that may only appear in goog.getCssName arguments. +
checkMissingGetCssNameLevel - +Variable in class com.google.javascript.jscomp.CompilerOptions +
  +
checkMissingReturn - +Variable in class com.google.javascript.jscomp.CompilerOptions +
  +
checkProvides - +Variable in class com.google.javascript.jscomp.CompilerOptions +
  +
checkRequires - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Checks for missing goog.require() calls +
checkSuspiciousCode - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Checks for suspicious statements that have no effect +
checkSymbols - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Checks that all symbols are defined +
checkTreeEquals(Node) - +Method in class com.google.javascript.rhino.Node +
Checks if the subtree under this node is the same as another subtree. +
checkTypes - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Checks types on expressions +
checkUnreachableCode - +Variable in class com.google.javascript.jscomp.CompilerOptions +
  +
children() - +Method in class com.google.javascript.jscomp.regex.RegExpTree +
The children of this node. +
children() - +Method in class com.google.javascript.rhino.Node +
Return an iterable object that iterates over this nodes's children. +
childrenSize() - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Returns number of the children. +
cleanUpDuplicatedFiles(Map<String, DependencyInfo>, Map<String, DependencyInfo>) - +Method in class com.google.javascript.jscomp.deps.DepsGenerator +
Removes duplicated depsInfo from jsFiles if this info already present in + some of the parsed deps.js +
clear() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
clear() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
clear() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
clear() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
clear() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
clear() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
clearAllFlags() - +Method in class com.google.javascript.rhino.Node.SideEffectFlags +
No side-effects occur and the returned results are local. +
clearAppNameSetter() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
clearAst() - +Method in class com.google.javascript.jscomp.CompilerInput +
  +
clearAst() - +Method in class com.google.javascript.jscomp.JsAst +
  +
clearAst() - +Method in class com.google.javascript.jscomp.jsonml.JsonMLAst +
  +
clearAst() - +Method in interface com.google.javascript.jscomp.SourceAst +
Removes any references to root node of the AST. +
clearAst() - +Method in class com.google.javascript.jscomp.SyntheticAst +
  +
clearAsts() - +Method in class com.google.javascript.jscomp.JSModule +
Removes any references to nodes of the AST. +
clearCachedSource() - +Method in class com.google.javascript.jscomp.JSSourceFile +
  +
clearCachedSource() - +Method in class com.google.javascript.jscomp.SourceFile +
  +
clearCachedValues() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
clearCachedValues() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Clear cached values. +
clearChildren() - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Removes all elements from the list of children. +
clearColumnPosition() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
clearColumnPosition() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
clearCompiledSource() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
clearCompiledSource() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
clearDeclarationToRemove() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
clearEdgeAnnotations() - +Method in class com.google.javascript.jscomp.graph.Graph +
Makes each edge's annotation null. +
clearEntry() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
clearId() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
clearIdentifier() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
clearInit() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
clearLineNumber() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
clearLineNumber() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
clearLineNumber() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
clearModule() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
clearModuleName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
clearName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
clearName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
clearNamedTypes() - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Flushes out the current resolved and unresovled Named Types from + the type registry. +
clearNodeAnnotations() - +Method in interface com.google.javascript.jscomp.graph.AdjacencyGraph +
Makes each node's annotation null. +
clearNodeAnnotations() - +Method in class com.google.javascript.jscomp.graph.Graph +
  +
clearOriginalFile() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
clearOriginalMapping() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
clearReportCall() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
clearReportDefined() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
clearReportExit() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
clearResolved() - +Method in class com.google.javascript.rhino.jstype.JSType +
Clears the resolved field. +
clearSideEffectFlags() - +Method in class com.google.javascript.rhino.Node.SideEffectFlags +
Preserve the return result flag, but clear the others: + no global state change, no throws, no this change, no arguments change +
clearSize() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
clearSourceName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
clearTemplateTypeName() - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Clears the template type name. +
clone() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
clone() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
clone() - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
clone() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
clone() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
clone() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
clone() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
cloneNode() - +Method in class com.google.javascript.rhino.Node +
  +
clonePropsFrom(Node) - +Method in class com.google.javascript.rhino.Node +
Clone the properties from the provided node without copying + the property object. +
cloneTree() - +Method in class com.google.javascript.rhino.Node +
  +
ClosureCodingConvention - Class in com.google.javascript.jscomp
This describes the Closure-specific JavaScript coding conventions.
ClosureCodingConvention() - +Constructor for class com.google.javascript.jscomp.ClosureCodingConvention +
  +
ClosureCodingConvention(CodingConvention) - +Constructor for class com.google.javascript.jscomp.ClosureCodingConvention +
  +
closurePass - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Processes goog.provide() and goog.require() calls +
coalesceDuplicateFiles() - +Method in class com.google.javascript.jscomp.JSModuleGraph +
Replaces any files that are found multiple times with a single instance in + the closest parent module that is common to all modules where it appears. +
coalesceVariableNames - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Merge two variables together as one. +
CodingConvention - Interface in com.google.javascript.jscomp
CodingConvention defines a set of hooks to customize the behavior of the + Compiler for a specific team/company.
CodingConvention.AssertionFunctionSpec - Class in com.google.javascript.jscomp
A function that will throw an exception when either: + -One or more of its parameters evaluate to false.
CodingConvention.AssertionFunctionSpec(String) - +Constructor for class com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec +
  +
CodingConvention.AssertionFunctionSpec(String, JSTypeNative) - +Constructor for class com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec +
  +
CodingConvention.Bind - Class in com.google.javascript.jscomp
 
CodingConvention.Bind(Node, Node, Node) - +Constructor for class com.google.javascript.jscomp.CodingConvention.Bind +
  +
CodingConvention.DelegateRelationship - Class in com.google.javascript.jscomp
Delegates provides a mechanism and structure for identifying where classes + can call out to optional code to augment their functionality.
CodingConvention.ObjectLiteralCast - Class in com.google.javascript.jscomp
An object literal cast provides a mechanism to cast object literals to + other types without a warning.
CodingConvention.SubclassRelationship - Class in com.google.javascript.jscomp
 
CodingConvention.SubclassRelationship(CodingConvention.SubclassType, Node, Node) - +Constructor for class com.google.javascript.jscomp.CodingConvention.SubclassRelationship +
  +
CodingConvention.SubclassType - Enum in com.google.javascript.jscomp
 
CodingConventions - Class in com.google.javascript.jscomp
Helper classes for dealing with coding conventions.
CodingConventions.Proxy - Class in com.google.javascript.jscomp
A convention that wraps another.
CodingConventions.Proxy(CodingConvention) - +Constructor for class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
collapseAnonymousFunctions - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Collapses anonymous function declarations into named function + declarations +
collapseDots(String) - +Static method in class com.google.javascript.jscomp.deps.PathUtil +
Removes all ../ and ./ entries from within the given path. +
collapseProperties - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Flattens multi-level property names (e.g. +
collapseUnion() - +Method in class com.google.javascript.rhino.jstype.JSType +
Gets the least supertype of this that's not a union. +
collapseUnion() - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
collapseVariableDeclarations - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Collapses multiple variable declarations into one +
COLON - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
color() - +Method in class com.google.javascript.jscomp.graph.GraphColoring +
Annotates the graph with GraphColoring.Color objects using + Annotatable.setAnnotation(Annotation). +
color() - +Method in class com.google.javascript.jscomp.graph.GraphColoring.GreedyGraphColoring +
  +
colorToNodeMap - +Variable in class com.google.javascript.jscomp.graph.GraphColoring +
  +
COLUMN_BITS - +Static variable in class com.google.javascript.rhino.Node +
COLUMN_BITS represents how many of the lower-order bits of + sourcePosition are reserved for storing the column number. +
COLUMN_MASK - +Static variable in class com.google.javascript.rhino.Node +
COLUMN_MASK stores a value where bits storing the column number + are set, and bits storing the line are not set. +
COLUMN_POSITION_FIELD_NUMBER - +Static variable in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
COLUMN_POSITION_FIELD_NUMBER - +Static variable in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
com.google.debugging.sourcemap - package com.google.debugging.sourcemap
Provides utilities to the creation and use of source maps.
com.google.debugging.sourcemap.proto - package com.google.debugging.sourcemap.proto
 
com.google.javascript.jscomp - package com.google.javascript.jscomp
Provides the core compiler and its public API.
com.google.javascript.jscomp.ant - package com.google.javascript.jscomp.ant
 
com.google.javascript.jscomp.deps - package com.google.javascript.jscomp.deps
Analyzes information about dependencies between files.
com.google.javascript.jscomp.graph - package com.google.javascript.jscomp.graph
Provides graph data structures and algorithms for coloring and fixed-point +computations.
com.google.javascript.jscomp.jsonml - package com.google.javascript.jscomp.jsonml
Provides the classes to support JsonML format and secure compiler.
com.google.javascript.jscomp.parsing - package com.google.javascript.jscomp.parsing
Provides utilities to help with parsing JSDoc annotations and performing AST +transformations.
com.google.javascript.jscomp.regex - package com.google.javascript.jscomp.regex
 
com.google.javascript.jscomp.testing - package com.google.javascript.jscomp.testing
 
com.google.javascript.rhino - package com.google.javascript.rhino
The core AST from Rhino.
com.google.javascript.rhino.jstype - package com.google.javascript.rhino.jstype
Provides abstractions to represent types in JavaScript.
com.google.javascript.rhino.testing - package com.google.javascript.rhino.testing
 
comma(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
COMMA - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
CommandLineRunner - Class in com.google.javascript.jscomp
CommandLineRunner translates flags into Java API calls on the Compiler.
CommandLineRunner(String[]) - +Constructor for class com.google.javascript.jscomp.CommandLineRunner +
Create a new command-line runner. +
CommandLineRunner(String[], PrintStream, PrintStream) - +Constructor for class com.google.javascript.jscomp.CommandLineRunner +
  +
compare(JsonML, JsonML) - +Static method in class com.google.javascript.jscomp.jsonml.JsonMLUtil +
Compares two specified JsonML trees. +
compareTo(DiagnosticType) - +Method in class com.google.javascript.jscomp.DiagnosticType +
  +
CompilationLevel - Enum in com.google.javascript.jscomp
A CompilationLevel represents the level of optimization that should be + applied when compiling JavaScript code.
compile(SourceFile, SourceFile, CompilerOptions) - +Method in class com.google.javascript.jscomp.Compiler +
  +
compile(SourceFile, JSSourceFile[], CompilerOptions) - +Method in class com.google.javascript.jscomp.Compiler +
  +
compile(JSSourceFile, JSModule[], CompilerOptions) - +Method in class com.google.javascript.jscomp.Compiler +
  +
compile(JSSourceFile[], JSSourceFile[], CompilerOptions) - +Method in class com.google.javascript.jscomp.Compiler +
Compiles a list of inputs. +
compile(List<T1>, List<T2>, CompilerOptions) - +Method in class com.google.javascript.jscomp.Compiler +
Compiles a list of inputs. +
compile(JSSourceFile[], JSModule[], CompilerOptions) - +Method in class com.google.javascript.jscomp.Compiler +
Compiles a list of modules. +
compile(JsonML) - +Method in class com.google.javascript.jscomp.jsonml.SecureCompiler +
  +
COMPILED_SOURCE_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
COMPILED_SOURCE_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
compileModules(List<T>, List<JSModule>, CompilerOptions) - +Method in class com.google.javascript.jscomp.Compiler +
Compiles a list of modules. +
Compiler - Class in com.google.javascript.jscomp
Compiler (and the other classes in this package) does the following: + + parses JS code + checks for undefined variables + performs optimizations such as constant folding and constants inlining + renames variables (to short names) + outputs compact javascript code + + + External variables are declared in 'externs' files.
Compiler() - +Constructor for class com.google.javascript.jscomp.Compiler +
Creates a Compiler that reports errors and warnings to its logger. +
Compiler(PrintStream) - +Constructor for class com.google.javascript.jscomp.Compiler +
Creates n Compiler that reports errors and warnings to an output + stream. +
Compiler(ErrorManager) - +Constructor for class com.google.javascript.jscomp.Compiler +
Creates a Compiler that uses a custom error manager. +
Compiler.CodeBuilder - Class in com.google.javascript.jscomp
Stores a buffer of text to which more can be appended.
Compiler.CodeBuilder() - +Constructor for class com.google.javascript.jscomp.Compiler.CodeBuilder +
  +
Compiler.IntermediateState - Class in com.google.javascript.jscomp
Stores the internal compiler state just before optimization is performed.
CompilerInput - Class in com.google.javascript.jscomp
A class for the internal representation of an input to the compiler.
CompilerInput(SourceAst) - +Constructor for class com.google.javascript.jscomp.CompilerInput +
  +
CompilerInput(SourceAst, boolean) - +Constructor for class com.google.javascript.jscomp.CompilerInput +
  +
CompilerInput(SourceAst, String, boolean) - +Constructor for class com.google.javascript.jscomp.CompilerInput +
  +
CompilerInput(SourceAst, InputId, boolean) - +Constructor for class com.google.javascript.jscomp.CompilerInput +
  +
CompilerInput(SourceFile) - +Constructor for class com.google.javascript.jscomp.CompilerInput +
  +
CompilerInput(SourceFile, boolean) - +Constructor for class com.google.javascript.jscomp.CompilerInput +
  +
CompilerOptions - Class in com.google.javascript.jscomp
Compiler options
CompilerOptions() - +Constructor for class com.google.javascript.jscomp.CompilerOptions +
Initializes compiler options. +
CompilerOptions.AliasTransformation - Interface in com.google.javascript.jscomp
A Role Specific Interface for the JsCompiler to report aliases used to + change the code during a compile.
CompilerOptions.AliasTransformationHandler - Interface in com.google.javascript.jscomp
A Role Specific Interface for JsCompiler that represents a data holder + object which is used to store goog.scope alias code changes to code made + during a compile.
CompilerOptions.LanguageMode - Enum in com.google.javascript.jscomp
When to do the extra sanity checks
CompilerOptions.Reach - Enum in com.google.javascript.jscomp
 
CompilerOptions.TracerMode - Enum in com.google.javascript.jscomp
 
CompilerOptions.TweakProcessing - Enum in com.google.javascript.jscomp
 
CompilerPass - Interface in com.google.javascript.jscomp
Interface for classes that can compile JS.
CompileTask - Class in com.google.javascript.jscomp.ant
This class implements a simple Ant task to do almost the same as + CommandLineRunner.
CompileTask() - +Constructor for class com.google.javascript.jscomp.ant.CompileTask +
  +
ComposeWarningsGuard - Class in com.google.javascript.jscomp
WarningsGuard that represents just a chain of other guards.
ComposeWarningsGuard(List<WarningsGuard>) - +Constructor for class com.google.javascript.jscomp.ComposeWarningsGuard +
  +
ComposeWarningsGuard(WarningsGuard...) - +Constructor for class com.google.javascript.jscomp.ComposeWarningsGuard +
  +
compute(N) - +Method in class com.google.javascript.jscomp.graph.GraphReachability +
  +
computeDependencyCalls() - +Method in class com.google.javascript.jscomp.deps.DepsGenerator +
Performs the parsing inputs and writing of outputs. +
computeFixedPoint(DiGraph<N, E>) - +Method in class com.google.javascript.jscomp.graph.FixedPointGraphTraversal +
Compute a fixed point for the given graph. +
computeFixedPoint(DiGraph<N, E>, N) - +Method in class com.google.javascript.jscomp.graph.FixedPointGraphTraversal +
Compute a fixed point for the given graph, entering from the given node. +
computeFixedPoint(DiGraph<N, E>, Set<N>) - +Method in class com.google.javascript.jscomp.graph.FixedPointGraphTraversal +
Compute a fixed point for the given graph, entering from the given nodes. +
computeFunctionSideEffects - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Use @nosideeffects annotations, function bodies and name graph + to determine if calls have side effects. +
Config - Class in com.google.javascript.jscomp.parsing
Configuration for the AST factory.
Config.LanguageMode - Enum in com.google.javascript.jscomp.parsing
 
connect(N, E, N) - +Method in class com.google.javascript.jscomp.graph.Graph +
Connects two nodes in the graph with an edge. +
connect(N, E, N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
connect(N, E, N) - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
connectIfNotFound(N, E, N) - +Method in class com.google.javascript.jscomp.graph.Graph +
Connects two nodes in the graph with an edge if such edge does not already + exists between the nodes. +
CONST - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
CONST - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
CONSTANT_PROPERTY - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
contains(boolean) - +Method in enum com.google.javascript.rhino.jstype.BooleanLiteralSet +
Returns whether this contains the given literal value. +
contains(JSType) - +Method in class com.google.javascript.rhino.jstype.UnionType +
A UnionType contains a given type (alternate) iff the member + vector contains it. +
containsAnchor() - +Method in class com.google.javascript.jscomp.regex.RegExpTree +
True if the regular expression contains an anchor : ^ or $. +
containsDeclaration() - +Method in class com.google.javascript.rhino.JSDocInfo +
  +
containWarning(String) - +Method in class com.google.javascript.jscomp.WhitelistWarningsGuard +
Determines whether a given warning is included in the white-list. +
CONTINUE - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
continueNode() - +Static method in class com.google.javascript.rhino.IR +
  +
continueNode(Node) - +Static method in class com.google.javascript.rhino.IR +
  +
convertToDottedProperties - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Converts quoted property accesses to dot syntax (a['b'] -> a.b) +
convertToJsonML() - +Method in class com.google.javascript.jscomp.jsonml.JsonMLAst +
  +
copyFromOtherFunction(FunctionType) - +Method in class com.google.javascript.rhino.jstype.FunctionBuilder +
Copies all the information from another function type. +
copyInformationFrom(Node) - +Method in class com.google.javascript.rhino.Node +
Copies source file and name information from the other + node given to the current node. +
copyInformationFromForTree(Node) - +Method in class com.google.javascript.rhino.Node +
Copies source file and name information from the other node to the + entire tree rooted at this node. +
create() - +Static method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
create() - +Static method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
createAnonymousObjectType() - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Create an anonymous object type. +
createCompiler() - +Method in class com.google.javascript.jscomp.CommandLineRunner +
  +
createConfig(boolean) - +Static method in class com.google.javascript.jscomp.parsing.ParserRunner +
Deprecated.  +
createConfig(boolean, Config.LanguageMode, boolean) - +Static method in class com.google.javascript.jscomp.parsing.ParserRunner +
  +
createConfig(boolean, Config.LanguageMode, boolean, Set<String>) - +Static method in class com.google.javascript.jscomp.parsing.ParserRunner +
  +
createConstructorType(JSType, JSType...) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a function type which can act as a constructor. +
createConstructorType(JSType, boolean, JSType...) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a function type which can act as a constructor. +
createConstructorType(String, Node, Node, JSType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a constructor function type. +
createConstructorTypeWithVarArgs(JSType, JSType...) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a function type which can act as a constructor. +
createDefaultObjectUnion(JSType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a type representing nullable values of the given type. +
createDefine() - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Creates a new <define/> nested element. +
createDelegateSuffix(String) - +Static method in class com.google.javascript.rhino.jstype.ObjectType +
Creates a suffix for a proxy delegate. +
createDepsFileParser() - +Method in class com.google.javascript.jscomp.deps.DepsGenerator +
  +
createDirectedGraphNode(N) - +Method in class com.google.javascript.jscomp.graph.DiGraph +
  +
createDirectedGraphNode(N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
createEnumType(String, Node, JSType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates an enum type. +
createExterns() - +Method in class com.google.javascript.jscomp.CommandLineRunner +
  +
createFromTypeNodes(Node, String, StaticScope<JSType>) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a JSType from the nodes representing a type. +
createFunctionType(JSType, JSType...) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a function type. +
createFunctionType(JSType, List<JSType>) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a function type. +
createFunctionType(ObjectType, JSType, List<JSType>) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a function type in which this refers to an object instance. +
createFunctionType(JSType, boolean, JSType...) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a function type. +
createFunctionType(JSType, Node) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
  +
createFunctionTypeWithNewReturnType(FunctionType, JSType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a new function type based on an existing function type but + with a new return type. +
createFunctionTypeWithNewThisType(FunctionType, ObjectType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a new function type based on an existing function type but + with a new this type. +
createFunctionTypeWithVarArgs(JSType, List<JSType>) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a function type. +
createFunctionTypeWithVarArgs(JSType, JSType...) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a function type. +
createFunctionTypeWithVarArgs(ObjectType, JSType, List<JSType>) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a function type in which this refers to an object instance. +
createInterfaceType(String, Node) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates an interface function type. +
createInternal(AbstractCompiler) - +Method in class com.google.javascript.jscomp.PassFactory +
Creates a new compiler pass to be run. +
createNamedType(String, String, int, int) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a named type. +
createNode(N) - +Method in class com.google.javascript.jscomp.graph.Graph +
Gets a node from the graph given a value. +
createNode(N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
createNode(N) - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
createNullableType(JSType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a type representing nullable values of the given type. +
createNullableType(JSType) - +Method in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
createObjectType(ObjectType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Create an object type. +
createObjectType(String, Node, ObjectType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Create an object type. +
createOptionalNullableType(JSType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a nullabel and undefine-able value of the given type. +
createOptionalParameters(JSType...) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a tree hierarchy representing a typed parameter list in which + every parameter is optional. +
createOptionalType(JSType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a type representing optional values of the given type. +
createOptionalType(JSType) - +Method in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
createOptions() - +Method in class com.google.javascript.jscomp.CommandLineRunner +
  +
createParameterizedType(ObjectType, JSType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a parameterized type. +
createParameters(List<JSType>) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a tree hierarchy representing a typed argument list. +
createParameters(JSType...) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a tree hierarchy representing a typed argument list. +
createParametersWithVarArgs(List<JSType>) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a tree hierarchy representing a typed argument list. +
createParametersWithVarArgs(JSType...) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a tree hierarchy representing a typed argument list. +
createRecordType(Map<String, RecordTypeBuilder.RecordProperty>) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a record type. +
createRecordTypeBuilder() - +Method in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
createUndirectedGraphNode(N) - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
createUnionType(JSType...) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a union type whose variants are the arguments. +
createUnionType(JSTypeNative...) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Creates a union type whose variants are the builtin types specified + by the arguments. +
createUnionType(JSType...) - +Method in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
createWithEdgeAnnotations() - +Static method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
createWithEdgeAnnotations() - +Static method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
createWithNodeAnnotations() - +Static method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
createWithNodeAnnotations() - +Static method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
createWithoutAnnotations() - +Static method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
createWithoutAnnotations() - +Static method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
crossModuleCodeMotion - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Move code to a deeper module +
crossModuleMethodMotion - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Move methds to a deeper module +
cssNames - +Variable in class com.google.javascript.jscomp.Result +
  +
cssRenamingMap - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Map used in the renaming of CSS class names. +
CssRenamingMap - Interface in com.google.javascript.jscomp
Interface used by ReplaceCssNames to substitute CSS class names.
CssRenamingMap.ByPart - Class in com.google.javascript.jscomp
 
CssRenamingMap.ByPart() - +Constructor for class com.google.javascript.jscomp.CssRenamingMap.ByPart +
  +
CssRenamingMap.ByWhole - Class in com.google.javascript.jscomp
 
CssRenamingMap.ByWhole() - +Constructor for class com.google.javascript.jscomp.CssRenamingMap.ByWhole +
  +
CssRenamingMap.Style - Enum in com.google.javascript.jscomp
 
customPasses - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Custom passes +
CustomPassExecutionTime - Enum in com.google.javascript.jscomp
Custom pass type.
+
+

+D

+
+
DATE_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
DATE_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
deadAssignmentElimination - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Remove assignments to values that can not be referenced +
DEBUGGER - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
DEBUGGER_STATEMENT_PRESENT - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
debugLog - +Variable in class com.google.javascript.jscomp.Result +
  +
DEC - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
DECLARATION_TO_REMOVE_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.Instrumentation +
  +
declareInferredSymbol(SymbolTable.SymbolScope, String, Node) - +Method in class com.google.javascript.jscomp.SymbolTable +
Declare a symbol after the main symbol table was constructed. +
declareType(String, JSType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Records declared global type names. +
DECR_FLAG - +Static variable in class com.google.javascript.rhino.Node +
  +
DEFAULT - +Static variable in class com.google.javascript.rhino.Token +
  +
DEFAULT_CASE - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
DEFAULT_FILENAME_PREFIX - +Static variable in class com.google.javascript.jscomp.ProcessCommonJSModules +
  +
defaultCase(Node) - +Static method in class com.google.javascript.rhino.IR +
  +
defaultLevel - +Variable in class com.google.javascript.jscomp.DiagnosticType +
Default level +
DefaultPassConfig - Class in com.google.javascript.jscomp
Pass factories and meta-data for native JSCompiler passes.
DefaultPassConfig(CompilerOptions) - +Constructor for class com.google.javascript.jscomp.DefaultPassConfig +
  +
defineDeclaredProperty(String, JSType, Node) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Defines a property whose type is synthesized (i.e. +
defineDelegateProxyPrototypeProperties(JSTypeRegistry, Scope, List<ObjectType>, Map<String, String>) - +Method in interface com.google.javascript.jscomp.CodingConvention +
Defines the delegate proxy prototype properties. +
defineDelegateProxyPrototypeProperties(JSTypeRegistry, Scope, List<ObjectType>, Map<String, String>) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
defineElement(String, Node) - +Method in class com.google.javascript.rhino.jstype.EnumType +
Defines a new element on this enum. +
defineInferredProperty(String, JSType, Node) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Defines a property whose type is inferred. +
defineReferenceAt(Node) - +Method in class com.google.javascript.jscomp.SymbolTable.Symbol +
  +
DELPROP - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
DependencyInfo - Interface in com.google.javascript.jscomp.deps
A data structure for JS dependency information for a single .js file.
DependencyOptions - Class in com.google.javascript.jscomp
Options for how to manage dependencies between input files.
DependencyOptions() - +Constructor for class com.google.javascript.jscomp.DependencyOptions +
  +
dependsOn(JSModule, JSModule) - +Method in class com.google.javascript.jscomp.JSModuleGraph +
Determines whether this module depends on a given module. +
DEPRECATED - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
DepsFileParser - Class in com.google.javascript.jscomp.deps
A parser that can extract dependency information from existing deps.js files.
DepsFileParser(ErrorManager) - +Constructor for class com.google.javascript.jscomp.deps.DepsFileParser +
Constructor +
DepsFileParser(Function<String, String>, ErrorManager) - +Constructor for class com.google.javascript.jscomp.deps.DepsFileParser +
  +
DepsGenerator - Class in com.google.javascript.jscomp.deps
Generates deps.js files by scanning javascript files for + calls to goog.provide(), goog.require() and goog.addDependency().
DepsGenerator(Collection<SourceFile>, Collection<SourceFile>, DepsGenerator.InclusionStrategy, String, ErrorManager) - +Constructor for class com.google.javascript.jscomp.deps.DepsGenerator +
Creates a new DepsGenerator. +
dereference() - +Method in class com.google.javascript.rhino.jstype.JSType +
Dereference a type for property access. +
describeFunctionBind(Node) - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
  +
describeFunctionBind(Node) - +Method in interface com.google.javascript.jscomp.CodingConvention +
A Bind instance or null. +
describeFunctionBind(Node) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
description - +Variable in class com.google.javascript.jscomp.JSError +
Description of the error +
description - +Variable in class com.google.javascript.jscomp.jsonml.JsonMLError +
Description of the error +
destNode - +Variable in class com.google.javascript.jscomp.graph.GraphReachability.EdgeTuple +
  +
detachChildren() - +Method in class com.google.javascript.rhino.Node +
Removes all children from this node and isolates the children from each + other. +
detachFromParent() - +Method in class com.google.javascript.rhino.Node +
Removes this node from its parent. +
devirtualizePrototypeMethods - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Devirtualize prototype method by rewriting them to be static calls that + take the this pointer as their first argument +
DiagnosticGroup - Class in com.google.javascript.jscomp
Group a set of related diagnostic types together, so that they can + be toggled on and off as one unit.
DiagnosticGroup(DiagnosticType...) - +Constructor for class com.google.javascript.jscomp.DiagnosticGroup +
Create a group that matches all errors of the given types. +
DiagnosticGroup(DiagnosticGroup...) - +Constructor for class com.google.javascript.jscomp.DiagnosticGroup +
Create a composite group. +
DiagnosticGroup(String, DiagnosticGroup...) - +Constructor for class com.google.javascript.jscomp.DiagnosticGroup +
Create a composite group. +
DiagnosticGroups - Class in com.google.javascript.jscomp
Named groups of DiagnosticTypes exposed by Compiler.
DiagnosticGroups() - +Constructor for class com.google.javascript.jscomp.DiagnosticGroups +
  +
DiagnosticGroupWarningsGuard - Class in com.google.javascript.jscomp
Sets the level for a particular DiagnosticGroup.
DiagnosticGroupWarningsGuard(DiagnosticGroup, CheckLevel) - +Constructor for class com.google.javascript.jscomp.DiagnosticGroupWarningsGuard +
  +
DiagnosticType - Class in com.google.javascript.jscomp
The type of a compile or analysis error.
didParseSucceed() - +Method in class com.google.javascript.jscomp.deps.JsFileLineParser +
  +
diff - +Variable in class com.google.javascript.jscomp.PerformanceTracker.Stats +
  +
differsFrom(JSType) - +Method in class com.google.javascript.rhino.jstype.JSType +
Whether this type is meaningfully different from that type. +
DiGraph<N,E> - Class in com.google.javascript.jscomp.graph
A generic directed graph.
DiGraph() - +Constructor for class com.google.javascript.jscomp.graph.DiGraph +
  +
DiGraph.DiGraphEdge<N,E> - Interface in com.google.javascript.jscomp.graph
A generic directed graph edge.
DiGraph.DiGraphNode<N,E> - Interface in com.google.javascript.jscomp.graph
A generic directed graph node.
DIRECT_EVAL - +Static variable in class com.google.javascript.rhino.Node +
  +
DIRECTIVES - +Static variable in class com.google.javascript.rhino.Node +
  +
disabled(String, String) - +Static method in class com.google.javascript.jscomp.DiagnosticType +
Create a DiagnosticType at level CheckLevel.OFF +
disableRuntimeTypeCheck() - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
disables(DiagnosticGroup) - +Method in class com.google.javascript.jscomp.ComposeWarningsGuard +
  +
disables(DiagnosticGroup) - +Method in class com.google.javascript.jscomp.DiagnosticGroupWarningsGuard +
  +
disables(DiagnosticGroup) - +Method in class com.google.javascript.jscomp.WarningsGuard +
Returns whether all warnings in the given diagnostic group will be + filtered out. +
disableThreads() - +Method in class com.google.javascript.jscomp.Compiler +
Disable threads. +
disambiguateProperties - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Rename properties to disambiguate between unrelated fields based on + type information. +
disconnect(N, N) - +Method in class com.google.javascript.jscomp.graph.Graph +
Disconnects two nodes in the graph by removing all edges between them. +
disconnect(N, N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
disconnect(N, N) - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
disconnectInDirection(N, N) - +Method in class com.google.javascript.jscomp.graph.DiGraph +
Disconnects all edges from n1 to n2. +
disconnectInDirection(N, N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
DIV - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
DO - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
doNode(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
DotFormatter - Class in com.google.javascript.jscomp
DotFormatter prints out a dot file of the Abstract Syntax Tree.
DUPLICATE_MESSAGE - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
DUPLICATE_VARS - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
+
+

+E

+
+
edge - +Variable in class com.google.javascript.jscomp.graph.GraphReachability.EdgeTuple +
  +
element - +Variable in class com.google.javascript.jscomp.jsonml.JsonMLError +
Node where the warning occurred. +
elements() - +Method in class com.google.javascript.jscomp.graph.StandardUnionFind +
  +
elements() - +Method in interface com.google.javascript.jscomp.graph.UnionFind +
Returns an unmodifiable set of all elements added to the UnionFind. +
ELLIPSIS - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
empty() - +Static method in class com.google.javascript.rhino.IR +
  +
EMPTY - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
EMPTY_BLOCK - +Static variable in class com.google.javascript.rhino.Node +
  +
EMPTY_TYPE_COMPONENT - +Static variable in class com.google.javascript.rhino.jstype.JSType +
  +
EmptyMessageBundle - Class in com.google.javascript.jscomp
An implementation of MessageBundle that has no translations.
EmptyMessageBundle() - +Constructor for class com.google.javascript.jscomp.EmptyMessageBundle +
  +
emptyScope() - +Static method in class com.google.javascript.rhino.testing.MapBasedScope +
  +
enableExternExports(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Deprecated. replaced by CompilerOptions.setExternExports(boolean) +
enableFoldConstant() - +Method in class com.google.javascript.jscomp.jsonml.SecureCompiler +
  +
enableRuntimeTypeCheck(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Enable runtime type checking, which adds JS type assertions for debugging. +
enables(DiagnosticGroup) - +Method in class com.google.javascript.jscomp.ComposeWarningsGuard +
Determines whether this guard will "elevate" the status of any disabled + diagnostic type in the group to a warning or an error. +
enables(DiagnosticGroup) - +Method in class com.google.javascript.jscomp.DiagnosticGroupWarningsGuard +
  +
enables(DiagnosticGroup) - +Method in class com.google.javascript.jscomp.WarningsGuard +
Returns whether any of the warnings in the given diagnostic group will be + upgraded to a warning or error. +
encodeEntry(Appendable, int, int, int) - +Static method in class com.google.debugging.sourcemap.SourceMapGeneratorV2.LineMapEncoder +
The source map line map is consists of a series of entries each + representing a map entry and a repetition count of that entry. +
enterScope(NodeTraversal) - +Method in class com.google.javascript.jscomp.NodeTraversal.AbstractScopedCallback +
  +
enterScope(NodeTraversal) - +Method in interface com.google.javascript.jscomp.NodeTraversal.ScopedCallback +
Called immediately after entering a new scope. +
ENTRY_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.FunctionInformationMap +
  +
ENUMDECL - +Static variable in class com.google.javascript.rhino.jstype.JSType +
  +
EnumElementType - Class in com.google.javascript.rhino.jstype
The type of individual elements of an enum type + (see EnumType).
EnumType - Class in com.google.javascript.rhino.jstype
An enum type representing a branded collection of elements.
EOC - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
eq(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
"==" +
EQ - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
equals(Object) - +Method in class com.google.javascript.jscomp.deps.SimpleDependencyInfo +
  +
equals(Object) - +Method in class com.google.javascript.jscomp.DiagnosticType +
  +
equals(Object) - +Method in class com.google.javascript.jscomp.graph.GraphColoring.Color +
  +
equals(Object) - +Method in class com.google.javascript.jscomp.JSError +
  +
equals(Object) - +Method in class com.google.javascript.jscomp.JsMessage +
  +
equals(Object) - +Method in class com.google.javascript.jscomp.JsMessage.PlaceholderReference +
  +
equals(Object) - +Method in class com.google.javascript.jscomp.regex.RegExpTree +
  +
equals(Object) - +Method in class com.google.javascript.jscomp.Scope.Arguments +
  +
equals(Object) - +Method in class com.google.javascript.jscomp.Scope.Var +
  +
equals(Object) - +Method in class com.google.javascript.rhino.InputId +
  +
equals(Object) - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
equals(Object) - +Method in class com.google.javascript.rhino.JSTypeExpression +
  +
EQUALS - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
error(String, String) - +Static method in class com.google.javascript.jscomp.DiagnosticType +
Create a DiagnosticType at level CheckLevel.ERROR +
error(String, String, int, int) - +Method in class com.google.javascript.jscomp.parsing.NullErrorReporter +
  +
error(String, String, int, String, int) - +Method in class com.google.javascript.jscomp.testing.TestErrorReporter +
  +
error(String, String, int, int) - +Method in interface com.google.javascript.rhino.ErrorReporter +
Report an error. +
error(String, String, int, int) - +Method in class com.google.javascript.rhino.SimpleErrorReporter +
  +
error(String, String, int, int) - +Method in class com.google.javascript.rhino.testing.TestErrorReporter +
  +
ERROR - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
ERROR_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
ERROR_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
errorFormat - +Variable in class com.google.javascript.jscomp.CompilerOptions +
  +
ErrorFormat - Enum in com.google.javascript.jscomp
Error formats available.
ErrorHandler - Interface in com.google.javascript.jscomp
The error handler is any generic sink for warnings and errors, + after they've passed through any filtering WarningsGuards.
ErrorLevel - Enum in com.google.javascript.jscomp.jsonml
Represents possible error levels for JsonML errors.
ErrorManager - Interface in com.google.javascript.jscomp
The error manager is in charge of storing, organizing and displaying + errors and warnings generated by the compiler.
ErrorReporter - Interface in com.google.javascript.rhino
This is interface defines a protocol for the reporting of + errors during JavaScript translation or execution.
errorReporter - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
errors - +Variable in class com.google.javascript.jscomp.Result +
  +
errors() - +Method in class com.google.javascript.rhino.SimpleErrorReporter +
Returns the list of errors, or null if there were none. +
ES5_STRICT - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
escapeString(String) - +Static method in class com.google.javascript.rhino.ScriptRuntime +
  +
escapeString(String, char) - +Static method in class com.google.javascript.rhino.ScriptRuntime +
For escaping strings printed by object and array literals; not quite + the same as 'escape.' +
EVAL_ERROR_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
EVAL_ERROR_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
evaluate(StaticScope<JSType>, JSTypeRegistry) - +Method in class com.google.javascript.rhino.JSTypeExpression +
Evaluates the type expression into a JSType object. +
execute() - +Method in class com.google.javascript.jscomp.ant.CompileTask +
  +
exitScope(NodeTraversal) - +Method in class com.google.javascript.jscomp.NodeTraversal.AbstractScopedCallback +
  +
exitScope(NodeTraversal) - +Method in interface com.google.javascript.jscomp.NodeTraversal.ScopedCallback +
Called immediately before exiting a scope. +
expandToAllMatched(CharRanges) - +Static method in class com.google.javascript.jscomp.regex.CaseCanonicalize +
Given a character range that may include case sensitive code-units, + such as [0-9B-M], returns the character range that includes all + the code-units in the input and those that are case-insensitively + equivalent to a code-unit in the input. +
exportTestFunctions - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Whether to export test functions. +
EXPR_RESULT - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
exprResult(Node) - +Static method in class com.google.javascript.rhino.IR +
  +
exprTypes - +Static variable in class com.google.javascript.jscomp.jsonml.Validator +
  +
EXTERN_OBJECT_PROPERTY_STRING - +Static variable in class com.google.javascript.jscomp.ObjectPropertyStringPreprocess +
  +
externExport - +Variable in class com.google.javascript.jscomp.Result +
  +
EXTERNS_VALIDATION - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
extractCharno(int) - +Static method in class com.google.javascript.rhino.Node +
Extracts the character number and character number from a merged line + char number (see Node.mergeLineCharNo(int, int)). +
extractClassNameIfProvide(Node, Node) - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
Exctracts X from goog.provide('X'), if the applied Node is goog. +
extractClassNameIfProvide(Node, Node) - +Method in interface com.google.javascript.jscomp.CodingConvention +
Convenience method for determining provided dependencies amongst different + js scripts. +
extractClassNameIfProvide(Node, Node) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
extractClassNameIfRequire(Node, Node) - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
Exctracts X from goog.require('X'), if the applied Node is goog. +
extractClassNameIfRequire(Node, Node) - +Method in interface com.google.javascript.jscomp.CodingConvention +
Convenience method for determining required dependencies amongst different + js scripts. +
extractClassNameIfRequire(Node, Node) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
extractLineno(int) - +Static method in class com.google.javascript.rhino.Node +
Extracts the line number and character number from a merged line char + number (see Node.mergeLineCharNo(int, int)). +
extractMessages(SourceFile...) - +Method in class com.google.javascript.jscomp.JsMessageExtractor +
Extracts js messages from javascript code. +
extractMessages(Iterable<T>) - +Method in class com.google.javascript.jscomp.JsMessageExtractor +
Extracts js messages from javascript code. +
extractPrototypeMemberDeclarations - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Extracts common prototype member declarations +
+
+

+F

+
+
FALSE - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
falseNode() - +Static method in class com.google.javascript.rhino.IR +
  +
FieldCleanupPass - Class in com.google.javascript.jscomp
A CleanupPass implementation that will remove all field declarations on + JSTypes contributed by the original file.
FieldCleanupPass(AbstractCompiler) - +Constructor for class com.google.javascript.jscomp.FieldCleanupPass +
  +
FILEOVERVIEW_JSDOC - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
FilePosition - Class in com.google.debugging.sourcemap
Represents a position in a source file.
FilePosition(int, int) - +Constructor for class com.google.debugging.sourcemap.FilePosition +
  +
find(E) - +Method in class com.google.javascript.jscomp.graph.StandardUnionFind +
  +
find(E) - +Method in interface com.google.javascript.jscomp.graph.UnionFind +
Returns the representative of the equivalence class of e. +
findAll(E) - +Method in class com.google.javascript.jscomp.graph.StandardUnionFind +
  +
findAll(E) - +Method in interface com.google.javascript.jscomp.graph.UnionFind +
Returns the elements in the same equivalence class as value. +
FindExportableNodes - Class in com.google.javascript.jscomp
Records all of the symbols and properties that should be exported.
FindExportableNodes(AbstractCompiler) - +Constructor for class com.google.javascript.jscomp.FindExportableNodes +
  +
FindExportableNodes.GenerateNodeContext - Class in com.google.javascript.jscomp
Context holding the node references required for generating the export + calls.
FindExportableNodes.GenerateNodeContext(Node, Node, Node) - +Constructor for class com.google.javascript.jscomp.FindExportableNodes.GenerateNodeContext +
  +
findPropertyType(String) - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
findPropertyType(String) - +Method in class com.google.javascript.rhino.jstype.JSType +
Coerces this type to an Object type, then gets the type of the property + whose name is given. +
findPropertyType(String) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
  +
findPropertyType(String) - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
FixedPointGraphTraversal<N,E> - Class in com.google.javascript.jscomp.graph
A utility class for doing fixed-point computations.
FixedPointGraphTraversal(FixedPointGraphTraversal.EdgeCallback<N, E>) - +Constructor for class com.google.javascript.jscomp.graph.FixedPointGraphTraversal +
Create a new traversal. +
FixedPointGraphTraversal.EdgeCallback<Node,Edge> - Interface in com.google.javascript.jscomp.graph
 
FLAG_ARGUMENTS_UNMODIFIED - +Static variable in class com.google.javascript.rhino.Node +
  +
FLAG_GLOBAL_STATE_UNMODIFIED - +Static variable in class com.google.javascript.rhino.Node +
  +
FLAG_LOCAL_RESULTS - +Static variable in class com.google.javascript.rhino.Node +
  +
FLAG_NO_THROWS - +Static variable in class com.google.javascript.rhino.Node +
  +
FLAG_THIS_UNMODIFIED - +Static variable in class com.google.javascript.rhino.Node +
  +
flowSensitiveInlineVariables - +Variable in class com.google.javascript.jscomp.CompilerOptions +
  +
foldConstants - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Folds constants (e.g. +
FOR - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
forBoolean(boolean) - +Static method in enum com.google.javascript.rhino.jstype.TernaryValue +
Gets the TernaryValue for the given boolean. +
forceResolve(ErrorReporter, StaticScope<JSType>) - +Method in class com.google.javascript.rhino.jstype.JSType +
Force this type to resolve, even if the registry is in a lazy + resolving mode. +
forConstructor() - +Method in class com.google.javascript.rhino.jstype.FunctionBuilder +
Make this a constructor. +
forIn(Node, Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
forMap(String, int, int) - +Static method in class com.google.debugging.sourcemap.SourceMapSection +
  +
format - +Variable in class com.google.javascript.jscomp.DiagnosticType +
The default way to format errors +
format(CheckLevel, MessageFormatter) - +Method in class com.google.javascript.jscomp.JSError +
Format a message at the given level. +
formatError(JSError) - +Method in class com.google.javascript.jscomp.LightweightMessageFormatter +
  +
formatError(JSError) - +Method in interface com.google.javascript.jscomp.MessageFormatter +
Format an error. +
formatLine(String, int) - +Method in interface com.google.javascript.jscomp.SourceExcerptProvider.ExcerptFormatter +
Format a line excerpt. +
formatPathToDepsFile(String) - +Method in class com.google.javascript.jscomp.deps.DepsGenerator +
Format the deps file path so that it can be included in the output file. +
formatRegion(Region) - +Method in interface com.google.javascript.jscomp.SourceExcerptProvider.ExcerptFormatter +
Format a region excerpt. +
formatWarning(JSError) - +Method in class com.google.javascript.jscomp.LightweightMessageFormatter +
  +
formatWarning(JSError) - +Method in interface com.google.javascript.jscomp.MessageFormatter +
Format a warning. +
formatWarning(JSError) - +Static method in class com.google.javascript.jscomp.WhitelistWarningsGuard +
  +
formatWarning(JSError, boolean) - +Static method in class com.google.javascript.jscomp.WhitelistWarningsGuard +
  +
forName(String) - +Method in class com.google.javascript.jscomp.DiagnosticGroups +
Find the diagnostic group registered under the given name. +
forNewRhino() - +Static method in class com.google.javascript.jscomp.parsing.NullErrorReporter +
  +
forNode(Node, Node, Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
forNoExpectedReports() - +Static method in class com.google.javascript.rhino.testing.TestErrorReporter +
  +
forOldRhino() - +Static method in class com.google.javascript.jscomp.parsing.NullErrorReporter +
  +
forType(DiagnosticType) - +Static method in class com.google.javascript.jscomp.DiagnosticGroup +
Create a diagnostic group that matches only the given type. +
forURL(String, int, int) - +Static method in class com.google.debugging.sourcemap.SourceMapSection +
  +
forwardDeclareType(String) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Records a forward-declared type name. +
FREE_CALL - +Static variable in class com.google.javascript.rhino.Node +
  +
fromBytes(byte[]) - +Static method in class com.google.javascript.jscomp.VariableMap +
Deserializes the variable map from a byte array returned by + VariableMap.toBytes(). +
fromCode(String, String) - +Static method in class com.google.javascript.jscomp.JSSourceFile +
  +
fromCode(String, String) - +Static method in class com.google.javascript.jscomp.SourceFile +
  +
fromCode(String, String, String) - +Static method in class com.google.javascript.jscomp.SourceFile +
  +
fromFile(String, Charset) - +Static method in class com.google.javascript.jscomp.JSSourceFile +
  +
fromFile(String) - +Static method in class com.google.javascript.jscomp.JSSourceFile +
  +
fromFile(File, Charset) - +Static method in class com.google.javascript.jscomp.JSSourceFile +
  +
fromFile(File) - +Static method in class com.google.javascript.jscomp.JSSourceFile +
  +
fromFile(String, Charset) - +Static method in class com.google.javascript.jscomp.SourceFile +
  +
fromFile(String) - +Static method in class com.google.javascript.jscomp.SourceFile +
  +
fromFile(File, Charset) - +Static method in class com.google.javascript.jscomp.SourceFile +
  +
fromFile(File) - +Static method in class com.google.javascript.jscomp.SourceFile +
  +
fromFile(File) - +Static method in class com.google.javascript.jscomp.WhitelistWarningsGuard +
Creates a warnings guard from a file. +
fromGenerator(String, SourceFile.Generator) - +Static method in class com.google.javascript.jscomp.JSSourceFile +
  +
fromGenerator(String, SourceFile.Generator) - +Static method in class com.google.javascript.jscomp.SourceFile +
  +
fromInputStream(String, InputStream) - +Static method in class com.google.javascript.jscomp.JSSourceFile +
  +
fromInputStream(String, InputStream) - +Static method in class com.google.javascript.jscomp.SourceFile +
  +
fromInputStream(String, String, InputStream) - +Static method in class com.google.javascript.jscomp.SourceFile +
  +
fromMap(Map<String, String>) - +Static method in class com.google.javascript.jscomp.VariableMap +
Initializes the variable map from an existing map. +
fromReader(String, Reader) - +Static method in class com.google.javascript.jscomp.SourceFile +
  +
function(Node, Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
FUNCTION - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
FUNCTION_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
FUNCTION_INSTANCE_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
FUNCTION_PROTOTYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
FunctionBuilder - Class in com.google.javascript.rhino.jstype
A builder class for function and arrow types.
FunctionBuilder(JSTypeRegistry) - +Constructor for class com.google.javascript.rhino.jstype.FunctionBuilder +
  +
FunctionInfo - Class in com.google.javascript.jscomp
 
FunctionInformationMap - Class in com.google.javascript.jscomp
 
functionInformationMap - +Variable in class com.google.javascript.jscomp.Result +
  +
FunctionInformationMap.Builder - Class in com.google.javascript.jscomp
 
FunctionInformationMap.Entry - Class in com.google.javascript.jscomp
 
FunctionInformationMap.Entry.Builder - Class in com.google.javascript.jscomp
 
FunctionInformationMap.EntryOrBuilder - Interface in com.google.javascript.jscomp
 
FunctionInformationMap.Module - Class in com.google.javascript.jscomp
 
FunctionInformationMap.Module.Builder - Class in com.google.javascript.jscomp
 
FunctionInformationMap.ModuleOrBuilder - Interface in com.google.javascript.jscomp
 
FunctionInformationMapOrBuilder - Interface in com.google.javascript.jscomp
 
functionName - +Variable in class com.google.javascript.jscomp.deps.JsFunctionParser.SymbolInfo +
  +
FunctionParamBuilder - Class in com.google.javascript.rhino.jstype
A builder for the Rhino Node representing Function parameters.
FunctionParamBuilder(JSTypeRegistry) - +Constructor for class com.google.javascript.rhino.jstype.FunctionParamBuilder +
  +
FunctionType - Class in com.google.javascript.rhino.jstype
This derived type provides extended information about a function, including + its return type and argument types.
+
+

+G

+
+
gatherCssNames - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Gather CSS names (requires closurePass) +
GE - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
generateExports - +Variable in class com.google.javascript.jscomp.CompilerOptions +
  +
generateId(String, List<CharSequence>) - +Method in class com.google.javascript.jscomp.GoogleJsMessageIdGenerator +
  +
generateId(String, List<CharSequence>) - +Method in interface com.google.javascript.jscomp.JsMessage.IdGenerator +
Generate the ID for the message. +
generatePseudoNames - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Generate pseudo names for variables and properties for debugging purposes. +
generateReport() - +Method in class com.google.javascript.jscomp.BasicErrorManager +
  +
generateReport() - +Method in interface com.google.javascript.jscomp.ErrorManager +
Writes a report to an implementation-specific medium. +
get(String) - +Method in class com.google.javascript.jscomp.CssRenamingMap.ByPart +
  +
get(String) - +Method in class com.google.javascript.jscomp.CssRenamingMap.ByWhole +
  +
get(String) - +Method in interface com.google.javascript.jscomp.CssRenamingMap +
  +
get(String) - +Static method in enum com.google.javascript.jscomp.jsonml.TagAttr +
  +
get(SourceExcerptProvider, String, int, SourceExcerptProvider.ExcerptFormatter) - +Method in enum com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt +
Get a source excerpt string based on the type of the source excerpt. +
get(boolean) - +Static method in enum com.google.javascript.rhino.jstype.BooleanLiteralSet +
Returns the singleton set {literalValue}. +
GET - +Static variable in class com.google.javascript.rhino.Token +
  +
getAbstractMethodName() - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
  +
getAbstractMethodName() - +Method in interface com.google.javascript.jscomp.CodingConvention +
Function name for abstract methods. +
getAbstractMethodName() - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
getAliasTransformationHandler() - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
getAllCallsites() - +Method in class com.google.javascript.jscomp.CallGraph +
Returns a collection of all callsites in the call graph. +
getAllDependencies() - +Method in class com.google.javascript.jscomp.JSModule +
Returns the transitive closure of dependencies starting from the + dependencies of this module. +
getAllExtendedInterfaces() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Returns all extended interfaces declared by an interfaces or its super- + interfaces. +
getAllFunctions() - +Method in class com.google.javascript.jscomp.CallGraph +
Returns a collection of all functions (including the main function) + in the call graph. +
getAllImplementedInterfaces() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Returns all interfaces implemented by a class or its superclass and any + superclasses for any of those interfaces. +
getAllJSDocInfo() - +Method in class com.google.javascript.jscomp.SymbolTable +
  +
getAllMessages() - +Method in class com.google.javascript.jscomp.EmptyMessageBundle +
Returns an empty list of messages. +
getAllMessages() - +Method in interface com.google.javascript.jscomp.MessageBundle +
Returns an iterable over the keys that this object has replacements for. +
getAllMessages() - +Method in class com.google.javascript.jscomp.XtbMessageBundle +
  +
getAllScopes() - +Method in class com.google.javascript.jscomp.SymbolTable +
Gets all the scopes in this symbol table. +
getAllSymbols() - +Method in class com.google.javascript.jscomp.Scope +
  +
getAllSymbols() - +Method in class com.google.javascript.jscomp.SymbolTable +
  +
getAllSymbols() - +Method in interface com.google.javascript.rhino.jstype.StaticSymbolTable +
Returns all variables in this symbol table. +
getAllSymbolsForType(JSType) - +Method in class com.google.javascript.jscomp.SymbolTable +
Gets all symbols associated with the given type. +
getAllSymbolsForTypeOf(SymbolTable.Symbol) - +Method in class com.google.javascript.jscomp.SymbolTable +
Get all symbols associated with the type of the given symbol. +
getAllSymbolsSorted() - +Method in class com.google.javascript.jscomp.SymbolTable +
Get the symbols in their natural ordering. +
getAlternates() - +Method in class com.google.javascript.rhino.jstype.UnionType +
Gets the alternate types of this union type. +
getAncestor(int) - +Method in class com.google.javascript.rhino.Node +
Gets the ancestor node relative to this. +
getAncestors() - +Method in class com.google.javascript.rhino.Node +
Iterates all of the node's ancestors excluding itself. +
getAnnotation() - +Method in interface com.google.javascript.jscomp.graph.Annotatable +
Retrieves a piece of information that has been annotated. +
getAnnotation() - +Method in class com.google.javascript.rhino.JSDocInfo.Marker +
Gets the position information for the annotation name. +
getAppNameSetter() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
getAppNameSetter() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
getAppNameSetter() - +Method in interface com.google.javascript.jscomp.InstrumentationOrBuilder +
  +
getArgumentsVar() - +Method in class com.google.javascript.jscomp.Scope +
Get a unique VAR object to represents "arguments" within this scope +
getAssertedParam(Node) - +Method in class com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec +
Returns the parameter of the assertion function that is being checked. +
getAssertedType() - +Method in class com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec +
Returns the type for a type assertion, or null if the function asserts + that the node must not be null or undefined. +
getAssertionFunctions() - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
  +
getAssertionFunctions() - +Method in interface com.google.javascript.jscomp.CodingConvention +
Returns the set of AssertionFunction. +
getAssertionFunctions() - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
getAssociatedNode() - +Method in class com.google.javascript.rhino.JSDocInfo +
  +
getAst() - +Method in class com.google.javascript.jscomp.CompilerInput +
  +
getAstDotGraph() - +Method in class com.google.javascript.jscomp.Compiler +
Gets the DOT graph of the AST generated at the end of compilation. +
getAstNode() - +Method in class com.google.javascript.jscomp.CallGraph.Callsite +
  +
getAstNode() - +Method in class com.google.javascript.jscomp.CallGraph.Function +
Returns the underlying AST node for the function. +
getAstRoot(AbstractCompiler) - +Method in class com.google.javascript.jscomp.CompilerInput +
  +
getAstRoot(AbstractCompiler) - +Method in class com.google.javascript.jscomp.JsAst +
  +
getAstRoot(AbstractCompiler) - +Method in class com.google.javascript.jscomp.jsonml.JsonMLAst +
Generates AST based on AST representation +
getAstRoot(AbstractCompiler) - +Method in interface com.google.javascript.jscomp.SourceAst +
Gets the root node of the AST for the source file this represents. +
getAstRoot(AbstractCompiler) - +Method in class com.google.javascript.jscomp.SyntheticAst +
  +
getAttribute(TagAttr) - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Returns value associated with a given attribute. +
getAttributes() - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Returns a map with attributes and respective values. +
getAuthors() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns the list of authors or null if none. +
getBackwardDirectedGraph() - +Method in class com.google.javascript.jscomp.CallGraph +
Constructs and returns a directed graph where the nodes are functions and + the edges are callsites connecting callees to callers. +
getBaseType() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the base type specified by the @extends annotation. +
getBeginningLineNumber() - +Method in interface com.google.javascript.jscomp.Region +
Get the beginning line number. +
getBeginningLineNumber() - +Method in class com.google.javascript.jscomp.SimpleRegion +
  +
getBindReturnType(int) - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Get the return value of calling "bind" on this function + with the specified number of arguments. +
getBlockDescription() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns the block-level description or null if none specified. +
getBodyNode() - +Method in class com.google.javascript.jscomp.CallGraph.Function +
Returns the AST node for the body of the function. +
getBooleanProp(int) - +Method in class com.google.javascript.rhino.Node +
  +
getByName(String) - +Method in class com.google.javascript.jscomp.JSModule +
Returns the input with the given name or null if none. +
getCallsiteForAstNode(Node) - +Method in class com.google.javascript.jscomp.CallGraph +
Returns the call graph Callsite object corresponding to the provided + AST Token.CALL or Token.NEW node, or null if no such object exists. +
getCallsitesInFunction() - +Method in class com.google.javascript.jscomp.CallGraph.Function +
Returns the callsites in this function. +
getCallsitesPossiblyTargetingFunction() - +Method in class com.google.javascript.jscomp.CallGraph.Function +
Returns a collection of callsites that might call this function. +
getCharno() - +Method in class com.google.javascript.jscomp.JSError +
Get the character number. +
getCharno() - +Method in class com.google.javascript.rhino.Node +
  +
getChecks() - +Method in class com.google.javascript.jscomp.DefaultPassConfig +
  +
getChecks() - +Method in class com.google.javascript.jscomp.PassConfig +
Gets the checking passes to run. +
getChild(int) - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Returns child at a given position. +
getChildAtIndex(int) - +Method in class com.google.javascript.rhino.Node +
  +
getChildBefore(Node) - +Method in class com.google.javascript.rhino.Node +
  +
getChildCount() - +Method in class com.google.javascript.rhino.Node +
  +
getChildren() - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Returns a list of all children. +
getChildren(int, int) - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Returns the portion of children list between the specified + fromIndex, inclusive, and toIndex, exclusive. +
getClassesDefinedByCall(Node) - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
Checks if the given method defines a subclass relationship, + and if it does, returns information on that relationship. +
getClassesDefinedByCall(Node) - +Method in interface com.google.javascript.jscomp.CodingConvention +
Checks if the given method defines a subclass relationship, + and if it does, returns information on that relationship. +
getClassesDefinedByCall(Node) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
getCode() - +Method in class com.google.javascript.jscomp.CompilerInput +
  +
getCode() - +Method in class com.google.javascript.jscomp.JSSourceFile +
  +
getCode() - +Method in interface com.google.javascript.jscomp.SourceFile.Generator +
  +
getCode() - +Method in class com.google.javascript.jscomp.SourceFile +
Gets all the code in this source file. +
getCodeReader() - +Method in class com.google.javascript.jscomp.SourceFile +
Gets a reader for the code in this source file. +
getCodeSizeRecord() - +Method in class com.google.javascript.jscomp.PerformanceTracker +
  +
getCodingConvention() - +Method in class com.google.javascript.jscomp.AbstractCompiler +
Gets the current coding convention. +
getCodingConvention() - +Method in class com.google.javascript.jscomp.Compiler +
  +
getCodingConvention() - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
getColor() - +Method in interface com.google.javascript.jscomp.graph.GraphvizGraph.GraphvizEdge +
Retrieves color of the edge. +
getColor() - +Method in interface com.google.javascript.jscomp.graph.GraphvizGraph.GraphvizNode +
Retrieves color of the node. +
getColumn() - +Method in class com.google.debugging.sourcemap.FilePosition +
  +
getColumn() - +Method in class com.google.debugging.sourcemap.SourceMapSection +
  +
getColumnPosition() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
getColumnPosition() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
getColumnPosition() - +Method in interface com.google.debugging.sourcemap.proto.Mapping.LineMappingOrBuilder +
  +
getColumnPosition() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
getColumnPosition() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
getColumnPosition() - +Method in interface com.google.debugging.sourcemap.proto.Mapping.OriginalMappingOrBuilder +
  +
getCompiledSource() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
getCompiledSource() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
getCompiledSource() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.EntryOrBuilder +
  +
getCompiledSource() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
getCompiledSource() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
getCompiledSource() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.ModuleOrBuilder +
  +
getCompiler() - +Method in class com.google.javascript.jscomp.NodeTraversal +
Gets the compiler. +
getConstructor() - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
getConstructor() - +Method in class com.google.javascript.rhino.jstype.EnumType +
  +
getConstructor() - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
getConstructor() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Gets this object's constructor. +
getConstructor() - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
getContainingFunction() - +Method in class com.google.javascript.jscomp.CallGraph.Callsite +
  +
getContextNode() - +Method in class com.google.javascript.jscomp.FindExportableNodes.GenerateNodeContext +
  +
getControlFlowGraph() - +Method in class com.google.javascript.jscomp.NodeTraversal +
Gets the control flow graph for the current JS scope. +
getCtorExtendedInterfaces() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Gets the interfaces extended by the interface associated with this type. +
getCtorImplementedInterfaces() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Gets the interfaces implemented by the ctor associated with this type. +
getCurrentNode() - +Method in class com.google.javascript.jscomp.NodeTraversal +
Returns the node currently being traversed. +
getDeclaration() - +Method in class com.google.javascript.jscomp.Scope.Var +
  +
getDeclaration() - +Method in class com.google.javascript.jscomp.SymbolTable.Symbol +
  +
getDeclaration() - +Method in class com.google.javascript.rhino.jstype.ObjectType.Property +
  +
getDeclaration() - +Method in class com.google.javascript.rhino.jstype.SimpleSlot +
  +
getDeclaration() - +Method in interface com.google.javascript.rhino.jstype.StaticSlot +
Gets the declaration of this symbol. +
getDeclarationNode() - +Method in class com.google.javascript.jscomp.SymbolTable.Symbol +
  +
getDeclarationToRemove(int) - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
getDeclarationToRemove(int) - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
getDeclarationToRemove(int) - +Method in interface com.google.javascript.jscomp.InstrumentationOrBuilder +
  +
getDeclarationToRemoveCount() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
getDeclarationToRemoveCount() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
getDeclarationToRemoveCount() - +Method in interface com.google.javascript.jscomp.InstrumentationOrBuilder +
  +
getDeclarationToRemoveList() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
getDeclarationToRemoveList() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
getDeclarationToRemoveList() - +Method in interface com.google.javascript.jscomp.InstrumentationOrBuilder +
  +
getDeclarativelyUnboundVarsWithoutTypes() - +Method in class com.google.javascript.jscomp.Scope +
Gets all variables declared with "var" but without declared types attached. +
getDeepestCommonDependencyInclusive(JSModule, JSModule) - +Method in class com.google.javascript.jscomp.JSModuleGraph +
Finds the deepest common dependency of two modules, including the + modules themselves. +
getDeepestCommonDependencyInclusive(Collection<JSModule>) - +Method in class com.google.javascript.jscomp.JSModuleGraph +
Returns the deepest common dependency of the given modules. +
getDefault() - +Static method in class com.google.javascript.jscomp.CodingConventions +
Gets the default coding convention. +
getDefaultExterns() - +Static method in class com.google.javascript.jscomp.CommandLineRunner +
  +
getDefaultInstance() - +Static method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
getDefaultInstance() - +Static method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
getDefaultInstance() - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
getDefaultInstance() - +Static method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
getDefaultInstance() - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
getDefaultInstance() - +Static method in class com.google.javascript.jscomp.Instrumentation +
  +
getDefaultInstanceForType() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
getDefaultInstanceForType() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
getDefaultInstanceForType() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
getDefaultInstanceForType() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
getDefaultInstanceForType() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getDefaultInstanceForType() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
getDefaultInstanceForType() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
getDefaultInstanceForType() - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
getDefaultInstanceForType() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
getDefaultInstanceForType() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
getDefaultInstanceForType() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
getDefaultInstanceForType() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
getDefaultLevel() - +Method in class com.google.javascript.jscomp.JSError +
The default level, before any of the WarningsGuards are applied. +
getDefineReplacements() - +Method in class com.google.javascript.jscomp.CompilerOptions +
Returns the map of define replacements. +
getDelegateRelationship(Node) - +Method in interface com.google.javascript.jscomp.CodingConvention +
  +
getDelegateRelationship(Node) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
getDelegateSuperclassName() - +Method in interface com.google.javascript.jscomp.CodingConvention +
  +
getDelegateSuperclassName() - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
getDependencies() - +Method in class com.google.javascript.jscomp.JSModule +
Gets the list of modules that this module depends on. +
getDependenciesOf(List<INPUT>, boolean) - +Method in class com.google.javascript.jscomp.deps.SortedDependencies +
Gets all the dependencies of the given roots. +
getDependentModule() - +Method in exception com.google.javascript.jscomp.JSModuleGraph.ModuleDependenceException +
  +
getDeprecationReason() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns the deprecation reason or null if none specified. +
getDepth() - +Method in class com.google.javascript.jscomp.JSModule +
  +
getDesc() - +Method in class com.google.javascript.jscomp.JsMessage +
Gets the description associated with this message, intended to help + translators, or null if this message has no description. +
getDescription() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the description specified by the @desc annotation. +
getDescription() - +Method in class com.google.javascript.rhino.JSDocInfo.Marker +
Gets the position information for the description found + in a block tag. +
getDescriptionForParameter(String) - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns the description for the parameter with the given name, if its + exists. +
getDescriptor() - +Static method in class com.google.debugging.sourcemap.proto.Mapping +
  +
getDescriptor() - +Static method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
getDescriptor() - +Static method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
getDescriptor() - +Static method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
getDescriptor() - +Static method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
getDescriptor() - +Static method in class com.google.javascript.jscomp.FunctionInfo +
  +
getDescriptor() - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getDescriptor() - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
getDescriptor() - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
getDescriptor() - +Static method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
getDescriptor() - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
getDescriptor() - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
getDescriptor() - +Static method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
getDescriptor() - +Static method in class com.google.javascript.jscomp.Instrumentation +
  +
getDescriptor() - +Static method in class com.google.javascript.jscomp.InstrumentationTemplate +
  +
getDescriptorForType() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
getDescriptorForType() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
getDescriptorForType() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getDescriptorForType() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
getDescriptorForType() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
getDescriptorForType() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
getDestination() - +Method in interface com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge +
  +
getDiagnosticGroups() - +Method in class com.google.javascript.jscomp.Compiler +
The warning classes that are available from the command-line, and + are suppressable by the @suppress annotation. +
getDirectedGraphEdges(N, N) - +Method in class com.google.javascript.jscomp.graph.DiGraph +
  +
getDirectedGraphEdges(N, N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getDirectedGraphNode(N) - +Method in class com.google.javascript.jscomp.graph.DiGraph +
  +
getDirectedGraphNode(N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getDirectedGraphNodes() - +Method in class com.google.javascript.jscomp.graph.DiGraph +
Gets an immutable iterable over all the nodes in the graph. +
getDirectedGraphNodes() - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getDirectedPredNodes(DiGraph.DiGraphNode<N, E>) - +Method in class com.google.javascript.jscomp.graph.DiGraph +
  +
getDirectedPredNodes(N) - +Method in class com.google.javascript.jscomp.graph.DiGraph +
  +
getDirectedPredNodes(N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getDirectedPredNodes(DiGraph.DiGraphNode<N, E>) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getDirectedSuccNodes(DiGraph.DiGraphNode<N, E>) - +Method in class com.google.javascript.jscomp.graph.DiGraph +
  +
getDirectedSuccNodes(N) - +Method in class com.google.javascript.jscomp.graph.DiGraph +
  +
getDirectedSuccNodes(N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getDirectedSuccNodes(DiGraph.DiGraphNode<N, E>) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getDirectImplementors(ObjectType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Returns a collection of types that directly implement interfaceInstance. +
getDirectives() - +Method in class com.google.javascript.rhino.Node +
Returns the set of ES5 directives for this node. +
getDisplayName() - +Method in class com.google.javascript.rhino.jstype.AllType +
  +
getDisplayName() - +Method in class com.google.javascript.rhino.jstype.BooleanType +
  +
getDisplayName() - +Method in class com.google.javascript.rhino.jstype.EnumType +
  +
getDisplayName() - +Method in class com.google.javascript.rhino.jstype.JSType +
Returns a user meaningful label for the JSType instance. +
getDisplayName() - +Method in class com.google.javascript.rhino.jstype.NullType +
  +
getDisplayName() - +Method in class com.google.javascript.rhino.jstype.NumberType +
  +
getDisplayName() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
  +
getDisplayName() - +Method in class com.google.javascript.rhino.jstype.StringType +
  +
getDisplayName() - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
getDisplayName() - +Method in class com.google.javascript.rhino.jstype.VoidType +
  +
getDouble() - +Method in class com.google.javascript.rhino.Node +
Can only be called when getType() == TokenStream.NUMBER +
getEachReferenceTypeWithProperty(String) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Returns each reference type that has a property propertyName + defined on it. +
getEdges() - +Method in class com.google.javascript.jscomp.graph.Graph +
Gets an immutable list of all edges. +
getEdges(N, N) - +Method in class com.google.javascript.jscomp.graph.Graph +
Retrieves an edge from the graph. +
getEdges(N, N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getEdges() - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getEdges(N, N) - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
getEdges() - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
getelem(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
GETELEM - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
getElementPreOrder(int) - +Method in class com.google.javascript.jscomp.jsonml.JsonMLAst +
Returns a JsonML element with the specified number from the tree in + pre-order walk. +
getElements() - +Method in class com.google.javascript.rhino.jstype.EnumType +
Gets the elements defined on this enum. +
getElementsType() - +Method in class com.google.javascript.rhino.jstype.EnumType +
Gets the elements' type. +
getEnclosingFunction() - +Method in class com.google.javascript.jscomp.NodeTraversal +
Examines the functions stack for the last instance of a function node. +
getEnclosingScope(Node) - +Method in class com.google.javascript.jscomp.SymbolTable +
Gets the scope that contains the given node. +
getEndingLineNumber() - +Method in interface com.google.javascript.jscomp.Region +
Get the ending line number. +
getEndingLineNumber() - +Method in class com.google.javascript.jscomp.SimpleRegion +
  +
getEndLine() - +Method in class com.google.javascript.rhino.SourcePosition +
Returns the ending line number of this position. +
getEntry(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getEntry(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
getEntry(int) - +Method in interface com.google.javascript.jscomp.FunctionInformationMapOrBuilder +
  +
getEntryBuilder(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getEntryBuilderList() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getEntryCount() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getEntryCount() - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
getEntryCount() - +Method in interface com.google.javascript.jscomp.FunctionInformationMapOrBuilder +
  +
getEntryList() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getEntryList() - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
getEntryList() - +Method in interface com.google.javascript.jscomp.FunctionInformationMapOrBuilder +
  +
getEntryOrBuilder(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getEntryOrBuilder(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
getEntryOrBuilder(int) - +Method in interface com.google.javascript.jscomp.FunctionInformationMapOrBuilder +
  +
getEntryOrBuilderList() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getEntryOrBuilderList() - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
getEntryOrBuilderList() - +Method in interface com.google.javascript.jscomp.FunctionInformationMapOrBuilder +
  +
getEnumParameterType() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the enum parameter type specified by the @enum annotation. +
getErrorCount() - +Method in class com.google.javascript.jscomp.BasicErrorManager +
  +
getErrorCount() - +Method in class com.google.javascript.jscomp.Compiler +
Gets the number of errors. +
getErrorCount() - +Method in interface com.google.javascript.jscomp.ErrorManager +
Gets the number of reported errors. +
getErrorLevel(JSError) - +Method in class com.google.javascript.jscomp.Compiler +
  +
getErrorManager() - +Method in class com.google.javascript.jscomp.AbstractCompiler +
Gets the error manager. +
getErrorManager() - +Method in class com.google.javascript.jscomp.Compiler +
  +
getErrorReporter() - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
  +
getErrors() - +Method in class com.google.javascript.jscomp.BasicErrorManager +
  +
getErrors() - +Method in class com.google.javascript.jscomp.Compiler +
Returns the array of errors (never null). +
getErrors() - +Method in interface com.google.javascript.jscomp.ErrorManager +
Gets all the errors. +
getErrors() - +Method in class com.google.javascript.jscomp.jsonml.SecureCompiler.Report +
  +
getExistingIntProp(int) - +Method in class com.google.javascript.rhino.Node +
  +
getExportPropertyFunction() - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
Use closure's implementation. +
getExportPropertyFunction() - +Method in interface com.google.javascript.jscomp.CodingConvention +
Function name used when exporting properties. +
getExportPropertyFunction() - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
getExports() - +Method in class com.google.javascript.jscomp.FindExportableNodes +
  +
getExportSymbolFunction() - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
Use closure's implementation. +
getExportSymbolFunction() - +Method in interface com.google.javascript.jscomp.CodingConvention +
Function name used when exporting symbols. +
getExportSymbolFunction() - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
getExtendedInterfaces() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns the interfaces extended by an interface +
getExtendedInterfaces() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Returns interfaces directly extended by an interface +
getExtendedInterfacesCount() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the number of extended interfaces specified +
getExtendedInterfacesCount() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Returns the number of interfaces directly extended by an interface +
getFileOverview() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns the file overview or null if none specified. +
getFirstChild() - +Method in class com.google.javascript.rhino.Node +
  +
getFirstEdge(N, N) - +Method in class com.google.javascript.jscomp.graph.Graph +
Retrieves any edge from the graph. +
getFirstEdge(N, N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getFirstEdge(N, N) - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
getFirstLine(String) - +Static method in class com.google.javascript.jscomp.WhitelistWarningsGuard +
  +
getForwardDirectedGraph() - +Method in class com.google.javascript.jscomp.CallGraph +
Constructs and returns a directed graph where the nodes are functions and + the edges are callsites connecting callers to callees. +
getFunctionForAstNode(Node) - +Method in class com.google.javascript.jscomp.CallGraph +
Returns the call graph Function object corresponding to the provided + AST Token.FUNCTION node, or null if no such object exists. +
getFunctionJSDocInfo(Node) - +Static method in class com.google.javascript.jscomp.NodeUtil +
Get the JSDocInfo for a function. +
getFunctionName() - +Method in class com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec +
Returns the name of the function. +
getFunctionParameters(Node) - +Static method in class com.google.javascript.jscomp.NodeUtil +
  +
getFunctionType() - +Method in class com.google.javascript.jscomp.SymbolTable.Symbol +
  +
getGlobalObject() - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
  +
getGlobalObject() - +Method in interface com.google.javascript.jscomp.CodingConvention +
Gets the name of the global object. +
getGlobalObject() - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
getGlobalObject() - +Method in class com.google.javascript.jscomp.JqueryCodingConvention +
  +
getGlobalScope() - +Method in class com.google.javascript.jscomp.SymbolTable +
Returns the global scope. +
getGraph() - +Method in class com.google.javascript.jscomp.graph.GraphColoring +
  +
getGraphvizEdges() - +Method in interface com.google.javascript.jscomp.graph.GraphvizGraph +
Retrieve a list of edges in the graph. +
getGraphvizEdges() - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getGraphvizEdges() - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
getGraphvizNodes() - +Method in interface com.google.javascript.jscomp.graph.GraphvizGraph +
Retrieve a list of nodes in the graph. +
getGraphvizNodes() - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getGraphvizNodes() - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
getGreatestSubtype(JSType) - +Method in class com.google.javascript.rhino.jstype.JSType +
Gets the greatest subtype of this and that. +
getGreatestSubtypeWithProperty(JSType, String) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Gets the greatest subtype of the type that has a property + propertyName defined on it. +
getGroup() - +Method in class com.google.javascript.jscomp.ant.Warning +
  +
getId() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
getId() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
getId() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.EntryOrBuilder +
  +
getId() - +Method in interface com.google.javascript.jscomp.graph.GraphvizGraph.GraphvizNode +
Retrieves the unique ID. +
getId() - +Method in class com.google.javascript.jscomp.JsMessage +
Gets the message's id, or name (e.g. +
getIdentifier() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
getIdentifier() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
getIdentifier() - +Method in interface com.google.debugging.sourcemap.proto.Mapping.OriginalMappingOrBuilder +
  +
getIdName() - +Method in class com.google.javascript.rhino.InputId +
  +
getImplementedInterfaceCount() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the number of interfaces specified by the @implements + annotation. +
getImplementedInterfaces() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns the types specified by the @implements annotation. +
getImplementedInterfaces() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Returns interfaces implemented directly by a class or its superclass. +
getImplicitPrototype() - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
getImplicitPrototype() - +Method in class com.google.javascript.rhino.jstype.EnumType +
  +
getImplicitPrototype() - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
getImplicitPrototype() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Gets the implicit prototype (a.k.a. +
getImplicitPrototype() - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
getIndexOfChild(Node) - +Method in class com.google.javascript.rhino.Node +
  +
getIndexOfSymbol(SymbolTable.Symbol) - +Method in class com.google.javascript.jscomp.SymbolTable.SymbolScope +
Gets a unique index for the symbol in this scope. +
getIndexType() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Gets the declared default index type. +
getInEdges() - +Method in interface com.google.javascript.jscomp.graph.DiGraph.DiGraphNode +
  +
getInEdges(N) - +Method in class com.google.javascript.jscomp.graph.DiGraph +
Gets an immutable list of in edges of the given node. +
getInEdges(N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getInferTypes() - +Method in class com.google.javascript.jscomp.CompilerOptions +
Gets the inferTypes flag. +
getInit(int) - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
getInit(int) - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
getInit(int) - +Method in interface com.google.javascript.jscomp.InstrumentationOrBuilder +
  +
getInitCount() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
getInitCount() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
getInitCount() - +Method in interface com.google.javascript.jscomp.InstrumentationOrBuilder +
  +
getInitialValue() - +Method in class com.google.javascript.jscomp.Scope.Var +
  +
getInitList() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
getInitList() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
getInitList() - +Method in interface com.google.javascript.jscomp.InstrumentationOrBuilder +
  +
getInput(InputId) - +Method in class com.google.javascript.jscomp.AbstractCompiler +
Looks up an input (possibly an externs input) by name. +
getInput(InputId) - +Method in class com.google.javascript.jscomp.Compiler +
  +
getInput() - +Method in class com.google.javascript.jscomp.NodeTraversal +
Gets the current input source. +
getInputId() - +Method in class com.google.javascript.jscomp.CompilerInput +
Returns a name for this input. +
getInputId() - +Method in class com.google.javascript.jscomp.JsAst +
  +
getInputId() - +Method in class com.google.javascript.jscomp.jsonml.JsonMLAst +
  +
getInputId(Node) - +Static method in class com.google.javascript.jscomp.NodeUtil +
  +
getInputId() - +Method in interface com.google.javascript.jscomp.SourceAst +
  +
getInputId() - +Method in class com.google.javascript.jscomp.SyntheticAst +
  +
getInputId() - +Method in class com.google.javascript.rhino.Node +
  +
getInputName() - +Method in class com.google.javascript.jscomp.Scope.Var +
  +
getInputProviding(String) - +Method in class com.google.javascript.jscomp.deps.SortedDependencies +
Return the input that gives us the given symbol. +
getInputs() - +Method in class com.google.javascript.jscomp.JSModule +
Gets this module's list of source code inputs. +
getInputsById() - +Method in class com.google.javascript.jscomp.Compiler +
Returns an unmodifiable view of the compiler inputs indexed by id. +
getInputsWithoutProvides() - +Method in class com.google.javascript.jscomp.deps.SortedDependencies +
  +
getInstance() - +Static method in class com.google.debugging.sourcemap.SourceMapGeneratorFactory +
  +
getInstance(SourceMapFormat) - +Static method in class com.google.debugging.sourcemap.SourceMapGeneratorFactory +
  +
getInstanceType() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Gets the type of instance of this function. +
getIntProp(int) - +Method in class com.google.javascript.rhino.Node +
Returns the integer value for the property, or 0 if the property + is not defined. +
getInverseOperator(int) - +Static method in class com.google.javascript.jscomp.NodeUtil +
Returns the inverse of an operator if it is invertible. +
getItem() - +Method in class com.google.javascript.rhino.SourcePosition +
Returns the item found at this source position. +
getJsDocBuilderForNode() - +Method in class com.google.javascript.rhino.Node +
  +
getJSDocInfo() - +Method in class com.google.javascript.jscomp.Scope.Var +
Gets the JSDocInfo for the variable. +
getJSDocInfo() - +Method in class com.google.javascript.jscomp.SymbolTable.Symbol +
  +
getJSDocInfo() - +Method in class com.google.javascript.rhino.jstype.JSType +
Gets the docInfo for this type. +
getJSDocInfo() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Gets the docInfo for this type. +
getJSDocInfo() - +Method in class com.google.javascript.rhino.jstype.ObjectType.Property +
  +
getJSDocInfo() - +Method in class com.google.javascript.rhino.jstype.SimpleSlot +
  +
getJSDocInfo() - +Method in interface com.google.javascript.rhino.jstype.StaticSlot +
Gets the JSDoc for this slot. +
getJSDocInfo() - +Method in class com.google.javascript.rhino.Node +
Get the JSDocInfo attached to this node. +
getJsonML() - +Method in class com.google.javascript.jscomp.jsonml.SecureCompiler +
Returns compiled source in JsonML format. +
getJSType() - +Method in class com.google.javascript.rhino.Node +
  +
getKey() - +Method in class com.google.javascript.jscomp.JsMessage.Builder +
Gets the message's key (e.g. +
getKey() - +Method in class com.google.javascript.jscomp.JsMessage +
Gets the message's key, or name (e.g. +
getLabel() - +Method in interface com.google.javascript.jscomp.graph.GraphvizGraph.GraphvizEdge +
Retrieves the label of the edge. +
getLabel() - +Method in interface com.google.javascript.jscomp.graph.GraphvizGraph.GraphvizNode +
Retrieves the label of the node. +
getLanguageIn() - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
getLanguageOut() - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
getLastChild() - +Method in class com.google.javascript.rhino.Node +
  +
getLastSibling() - +Method in class com.google.javascript.rhino.Node +
  +
getLeastSupertype(JSType) - +Method in class com.google.javascript.rhino.jstype.JSType +
Gets the least supertype of this and that. +
getLeastSupertype(JSType) - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
getLendsName() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the name we're lending to in a @lends annotation. +
getLength() - +Method in class com.google.javascript.jscomp.Compiler.CodeBuilder +
Returns the length of the text buffer. +
getLength() - +Method in class com.google.javascript.rhino.Node +
  +
getLevel() - +Method in class com.google.javascript.jscomp.ant.Warning +
  +
getLicense() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the description specified by the @license annotation. +
getLine() - +Method in class com.google.debugging.sourcemap.FilePosition +
Returns the line number of this position. +
getLine() - +Method in class com.google.debugging.sourcemap.SourceMapSection +
  +
getLine(int) - +Method in class com.google.javascript.jscomp.CompilerInput +
Gets the source line for the indicated line number. +
getLine(int) - +Method in class com.google.javascript.jscomp.SourceFile +
Gets the source line for the indicated line number. +
getLineno() - +Method in class com.google.javascript.rhino.Node +
  +
getLineNumber() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
getLineNumber() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
getLineNumber() - +Method in interface com.google.debugging.sourcemap.proto.Mapping.LineMappingOrBuilder +
  +
getLineNumber() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
getLineNumber() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
getLineNumber() - +Method in interface com.google.debugging.sourcemap.proto.Mapping.OriginalMappingOrBuilder +
  +
getLineNumber() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
getLineNumber() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
getLineNumber() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.EntryOrBuilder +
  +
getLineNumber() - +Method in class com.google.javascript.jscomp.NodeTraversal +
Gets the current line number, or zero if it cannot be determined. +
getLineOffset(int) - +Method in class com.google.javascript.jscomp.CompilerInput +
  +
getLineOffset(int) - +Method in class com.google.javascript.jscomp.SourceFile +
  +
getLineOffset(int) - +Method in class com.google.javascript.rhino.jstype.SimpleSourceFile +
  +
getLineOffset(int) - +Method in interface com.google.javascript.rhino.jstype.StaticSourceFile +
Returns the offset of the given line number relative to the file start. +
getLog() - +Method in class com.google.javascript.jscomp.PerformanceTracker +
  +
getMainFunction() - +Method in class com.google.javascript.jscomp.CallGraph +
Returns a Function object representing the "main" global function. +
getMappingForLine(int, int) - +Method in class com.google.debugging.sourcemap.SourceMapConsumerV1 +
  +
getMappingForLine(int, int) - +Method in class com.google.debugging.sourcemap.SourceMapConsumerV2 +
  +
getMappingForLine(int, int) - +Method in class com.google.debugging.sourcemap.SourceMapConsumerV3 +
  +
getMappingForLine(int, int) - +Method in interface com.google.debugging.sourcemap.SourceMapping +
Returns the original mapping for the line number and column position found + in the source map. +
getMarkers() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the list of all markers for the documentation in this JSDoc. +
getMaxArguments() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Gets the maximum number of arguments that this function requires, + or Integer.MAX_VALUE if this is a variable argument function. +
getMeaning() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the meaning specified by the @meaning annotation. +
getMessage(String) - +Method in class com.google.javascript.jscomp.EmptyMessageBundle +
Returns null, to indicate it has no message replacements. +
getMessage(String) - +Method in interface com.google.javascript.jscomp.MessageBundle +
Gets a message replacement. +
getMessage(String) - +Method in class com.google.javascript.jscomp.XtbMessageBundle +
  +
getMessage(String, Object[]) - +Static method in class com.google.javascript.rhino.ScriptRuntime +
  +
getMessage0(String) - +Static method in class com.google.javascript.rhino.ScriptRuntime +
  +
getMessage1(String, Object) - +Static method in class com.google.javascript.rhino.ScriptRuntime +
  +
getMessages() - +Method in class com.google.javascript.jscomp.Compiler +
Returns an array constructed from errors + temporary warnings. +
getMinArguments() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Gets the minimum number of arguments that this function requires. +
getModifies() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns the set of sideeffect notations. +
getModule() - +Method in class com.google.javascript.jscomp.CompilerInput +
Returns the module to which the input belongs. +
getModule(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getModule(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
getModule(int) - +Method in interface com.google.javascript.jscomp.FunctionInformationMapOrBuilder +
  +
getModule() - +Method in exception com.google.javascript.jscomp.JSModuleGraph.ModuleDependenceException +
  +
getModule() - +Method in class com.google.javascript.jscomp.NodeTraversal +
Gets the current input module. +
getModuleBuilder(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getModuleBuilderList() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getModuleCount() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getModuleCount() - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
getModuleCount() - +Method in interface com.google.javascript.jscomp.FunctionInformationMapOrBuilder +
  +
getModuleList() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getModuleList() - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
getModuleList() - +Method in interface com.google.javascript.jscomp.FunctionInformationMapOrBuilder +
  +
getModuleName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
getModuleName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
getModuleName() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.EntryOrBuilder +
  +
getModuleOrBuilder(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getModuleOrBuilder(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
getModuleOrBuilder(int) - +Method in interface com.google.javascript.jscomp.FunctionInformationMapOrBuilder +
  +
getModuleOrBuilderList() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
getModuleOrBuilderList() - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
getModuleOrBuilderList() - +Method in interface com.google.javascript.jscomp.FunctionInformationMapOrBuilder +
  +
getName() - +Method in class com.google.javascript.jscomp.CallGraph.Function +
Gets the name of this function. +
getName() - +Method in class com.google.javascript.jscomp.CompilerInput +
Returns a name for this input. +
getName() - +Method in interface com.google.javascript.jscomp.deps.DependencyInfo +
Gets the unique name / path of this file. +
getName() - +Method in class com.google.javascript.jscomp.deps.SimpleDependencyInfo +
  +
getName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
getName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
getName() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.EntryOrBuilder +
  +
getName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
getName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
getName() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.ModuleOrBuilder +
  +
getName() - +Method in interface com.google.javascript.jscomp.graph.GraphvizGraph +
Name of the graph. +
getName() - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getName() - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
getName() - +Method in class com.google.javascript.jscomp.JsMessage.PlaceholderReference +
  +
getName() - +Method in class com.google.javascript.jscomp.JSModule +
Gets the module name. +
getName() - +Method in class com.google.javascript.jscomp.Scope.Var +
Gets the name of the variable. +
getName() - +Method in class com.google.javascript.jscomp.SourceFile +
Returns a unique name for the source file. +
getName() - +Method in class com.google.javascript.rhino.JSDocInfo.Marker +
Deprecated. Use #getNameNode +
getName() - +Method in class com.google.javascript.rhino.jstype.ObjectType.Property +
  +
getName() - +Method in class com.google.javascript.rhino.jstype.SimpleSlot +
  +
getName() - +Method in class com.google.javascript.rhino.jstype.SimpleSourceFile +
  +
getName() - +Method in interface com.google.javascript.rhino.jstype.StaticSlot +
Gets the name of the slot. +
getName() - +Method in interface com.google.javascript.rhino.jstype.StaticSourceFile +
The name of the file. +
getNameNode() - +Method in class com.google.javascript.jscomp.Scope.Var +
Returns the name node that produced this variable. +
getNameNode() - +Method in class com.google.javascript.rhino.JSDocInfo.Marker +
Gets the position information for the name found + in an @param tag. +
getNativeFunctionType(JSTypeNative) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
  +
getNativeObjectType(JSTypeNative) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
  +
getNativeType(JSTypeNative) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
  +
getNaturalSymbolOrdering() - +Method in class com.google.javascript.jscomp.SymbolTable +
Gets the 'natural' ordering of symbols. +
getNearestFunctionName(Node) - +Static method in class com.google.javascript.jscomp.NodeUtil +
Gets the function's name. +
getNeighborEdges() - +Method in interface com.google.javascript.jscomp.graph.UndiGraph.UndiGraphNode +
  +
getNeighborEdgesIterator() - +Method in interface com.google.javascript.jscomp.graph.UndiGraph.UndiGraphNode +
  +
getNeighborNodes(N) - +Method in class com.google.javascript.jscomp.graph.Graph +
Gets the neighboring nodes. +
getNeighborNodes(N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getNeighborNodes(DiGraph.DiGraphNode<N, E>) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getNeighborNodes(N) - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
getNeighborNodesIterator(N) - +Method in class com.google.javascript.jscomp.graph.Graph +
  +
getNeighborNodesIterator(N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getNeighborNodesIterator(N) - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
getNewNameToOriginalNameMap() - +Method in class com.google.javascript.jscomp.VariableMap +
Returns an unmodifiable mapping from new names to original names. +
getNext() - +Method in class com.google.javascript.rhino.Node +
  +
getNode() - +Method in class com.google.javascript.jscomp.FindExportableNodes.GenerateNodeContext +
  +
getNode(N) - +Method in interface com.google.javascript.jscomp.graph.AdjacencyGraph +
Gets a node from the graph given a value. +
getNode(N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getNode(N) - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
getNode() - +Method in class com.google.javascript.jscomp.Scope.Var +
Gets the node for the name of the variable. +
getNode() - +Method in class com.google.javascript.rhino.jstype.ObjectType.Property +
  +
getNode() - +Method in class com.google.javascript.rhino.jstype.SimpleReference +
  +
getNode() - +Method in interface com.google.javascript.rhino.jstype.StaticReference +
The node where the reference lives. +
getNode1Id() - +Method in interface com.google.javascript.jscomp.graph.GraphvizGraph.GraphvizEdge +
Get the first node in the edge. +
getNode2Id() - +Method in interface com.google.javascript.jscomp.graph.GraphvizGraph.GraphvizEdge +
Get the second node in the edge. +
getNodeA() - +Method in interface com.google.javascript.jscomp.graph.Graph.GraphEdge +
  +
getNodeB() - +Method in interface com.google.javascript.jscomp.graph.Graph.GraphEdge +
  +
getNodeDegree(N) - +Method in class com.google.javascript.jscomp.graph.Graph +
Gets the degree of a node. +
getNodeDegree(N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getNodeDegree(N) - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
getNodeLength() - +Method in class com.google.javascript.jscomp.JSError +
  +
getNodes() - +Method in interface com.google.javascript.jscomp.graph.AdjacencyGraph +
Gets an immutable list of all nodes. +
getNodes() - +Method in class com.google.javascript.jscomp.graph.Graph +
Gets an immutable list of all nodes. +
getNodes() - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getNodes() - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
getNodeSourceOffset() - +Method in class com.google.javascript.jscomp.JSError +
  +
getNormalizedReferenceName() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Due to the complexity of some of our internal type systems, sometimes + we have different types constructed by the same constructor. +
getNumLines() - +Method in class com.google.javascript.jscomp.CompilerInput +
  +
getObjectLiteralCast(NodeTraversal, Node) - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
  +
getObjectLiteralCast(NodeTraversal, Node) - +Method in interface com.google.javascript.jscomp.CodingConvention +
Checks if the given method performs a object literal cast, and if it does, + returns information on the cast. +
getObjectLiteralCast(NodeTraversal, Node) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
getOptimizations() - +Method in class com.google.javascript.jscomp.DefaultPassConfig +
  +
getOptimizations() - +Method in class com.google.javascript.jscomp.PassConfig +
Gets the optimization passes to run. +
getOriginalCommentString() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns the original JSDoc comment string. +
getOriginalFile() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
getOriginalFile() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
getOriginalFile() - +Method in interface com.google.debugging.sourcemap.proto.Mapping.OriginalMappingOrBuilder +
  +
getOriginalMapping() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
getOriginalMapping() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
getOriginalMapping() - +Method in interface com.google.debugging.sourcemap.proto.Mapping.LineMappingOrBuilder +
  +
getOriginalMappingBuilder() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
getOriginalMappingOrBuilder() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
getOriginalMappingOrBuilder() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
getOriginalMappingOrBuilder() - +Method in interface com.google.debugging.sourcemap.proto.Mapping.LineMappingOrBuilder +
  +
getOriginalNameToNewNameMap() - +Method in class com.google.javascript.jscomp.VariableMap +
Returns an unmodifiable mapping from original names to new names. +
getOriginalPath() - +Method in class com.google.javascript.jscomp.SourceFile +
  +
getOriginalSources() - +Method in class com.google.debugging.sourcemap.SourceMapConsumerV3 +
  +
getOriginalSources() - +Method in interface com.google.debugging.sourcemap.SourceMappingReversable +
  +
getOutEdges() - +Method in interface com.google.javascript.jscomp.graph.DiGraph.DiGraphNode +
  +
getOutEdges(N) - +Method in class com.google.javascript.jscomp.graph.DiGraph +
Gets an immutable list of out edges of the given node. +
getOutEdges(N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
getOwnerFunction() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Gets the owner of this if it's a function prototype. +
getOwnImplementedInterfaces() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Returns interfaces directly implemented by the class. +
getOwnPropertyJSDocInfo(String) - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
getOwnPropertyJSDocInfo(String) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Gets the docInfo on the specified property on this type. +
getOwnPropertyNames() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Includes the prototype iff someone has created it. +
getOwnPropertyNames() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Returns the names of all the properties directly on this type. +
getOwnSlot(String) - +Method in class com.google.javascript.jscomp.Scope +
  +
getOwnSlot(String) - +Method in class com.google.javascript.jscomp.SymbolTable.SymbolScope +
  +
getOwnSlot(String) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
  +
getOwnSlot(String) - +Method in interface com.google.javascript.rhino.jstype.StaticScope +
Like getSlot but does not recurse into parent scopes. +
getOwnSlot(String) - +Method in class com.google.javascript.rhino.testing.AbstractStaticScope +
  +
getParameterCount() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the number of parameters defined. +
getParameterInFunction(SymbolTable.Symbol, String) - +Method in class com.google.javascript.jscomp.SymbolTable +
If sym is a function, try to find a Symbol for + a parameter with the given name. +
getParameterNames() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns the set of names of the defined parameters. +
getParameters() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
getParametersNode() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Gets an LP node that contains all params. +
getParameterType(String) - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the parameter type. +
getParameterType() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Gets the declared default element type. +
getParent() - +Method in class com.google.javascript.jscomp.Scope +
  +
getParent() - +Method in class com.google.javascript.rhino.Node +
  +
getParentNode() - +Method in class com.google.javascript.jscomp.Scope.Var +
Gets the parent of the name node. +
getParentScope() - +Method in class com.google.javascript.jscomp.Scope +
  +
getParentScope() - +Method in class com.google.javascript.jscomp.SymbolTable.SymbolScope +
  +
getParentScope() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
  +
getParentScope() - +Method in interface com.google.javascript.rhino.jstype.StaticScope +
Returns the scope enclosing this one or null if none. +
getParentScope() - +Method in class com.google.javascript.rhino.testing.AbstractStaticScope +
  +
getPartitionSuperNode(N) - +Method in class com.google.javascript.jscomp.graph.GraphColoring +
Using the coloring as partitions, finds the node that represents that + partition as the super node. +
getParts() - +Method in class com.google.javascript.jscomp.JsMessage.Builder +
  +
getPathRelativeToClosureBase() - +Method in class com.google.javascript.jscomp.CompilerInput +
Gets the path relative to closure-base, if one is available. +
getPathRelativeToClosureBase() - +Method in interface com.google.javascript.jscomp.deps.DependencyInfo +
Gets the path of this file relative to Closure's base.js file. +
getPathRelativeToClosureBase() - +Method in class com.google.javascript.jscomp.deps.SimpleDependencyInfo +
  +
getPathRelativeToClosureBase() - +Method in class com.google.javascript.jscomp.JSModule +
  +
getPlaceholders() - +Method in class com.google.javascript.jscomp.JsMessage.Builder +
Returns the message registred placeholders +
getPositionOnEndLine() - +Method in class com.google.javascript.rhino.SourcePosition +
Returns the character position on the ending line. +
getPositionOnStartLine() - +Method in class com.google.javascript.rhino.SourcePosition +
Returns the character position on the starting line. +
getPossibleTargets() - +Method in class com.google.javascript.jscomp.CallGraph.Callsite +
Returns the possible target functions that this callsite could call. +
getPossibleToBooleanOutcomes() - +Method in class com.google.javascript.rhino.jstype.AllType +
  +
getPossibleToBooleanOutcomes() - +Method in class com.google.javascript.rhino.jstype.BooleanType +
  +
getPossibleToBooleanOutcomes() - +Method in class com.google.javascript.rhino.jstype.JSType +
Computes the set of possible outcomes of the ToBoolean predicate + for this type. +
getPossibleToBooleanOutcomes() - +Method in class com.google.javascript.rhino.jstype.NoType +
  +
getPossibleToBooleanOutcomes() - +Method in class com.google.javascript.rhino.jstype.NullType +
  +
getPossibleToBooleanOutcomes() - +Method in class com.google.javascript.rhino.jstype.NumberType +
  +
getPossibleToBooleanOutcomes() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
  +
getPossibleToBooleanOutcomes() - +Method in class com.google.javascript.rhino.jstype.StringType +
  +
getPossibleToBooleanOutcomes() - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
getPossibleToBooleanOutcomes() - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
getPossibleToBooleanOutcomes() - +Method in class com.google.javascript.rhino.jstype.VoidType +
  +
getPrimitiveType() - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
Gets the primitive type of this enum element. +
getPriority() - +Method in class com.google.javascript.jscomp.ShowByPathWarningsGuard +
  +
getPriority() - +Method in class com.google.javascript.jscomp.StrictWarningsGuard +
  +
getPriority() - +Method in class com.google.javascript.jscomp.WarningsGuard +
The priority in which warnings guards are applied. +
getPriority() - +Method in class com.google.javascript.jscomp.WhitelistWarningsGuard +
  +
getProgress() - +Method in class com.google.javascript.jscomp.AbstractCompiler +
  +
getProgress() - +Method in class com.google.javascript.jscomp.Compiler +
  +
getprop(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
getProp(int) - +Method in class com.google.javascript.rhino.Node +
  +
GETPROP - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
getPropertiesCount() - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
getPropertiesCount() - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
getPropertiesCount() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Gets the number of properties of this object. +
getPropertiesCount() - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
getPropertyNames() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Returns a list of properties defined or inferred on this type and any of + its supertypes. +
getPropertyNode(String) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Gets the node corresponding to the definition of the specified property. +
getPropertyScope() - +Method in class com.google.javascript.jscomp.SymbolTable.Symbol +
  +
getPropertyType(String) - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
getPropertyType(String) - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
getPropertyType(String) - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
getPropertyType(String) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Gets the property type of the property whose name is given. +
getPropertyType(String) - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
getPrototype() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Gets the prototype property of this function type. +
getProvides() - +Method in class com.google.javascript.jscomp.CompilerInput +
Gets a list of types provided by this input. +
getProvides() - +Method in interface com.google.javascript.jscomp.deps.DependencyInfo +
Gets the symbols provided by this file. +
getProvides() - +Method in class com.google.javascript.jscomp.deps.SimpleDependencyInfo +
  +
getProvides() - +Method in class com.google.javascript.jscomp.JSModule +
  +
getQualifiedName() - +Method in class com.google.javascript.rhino.Node +
This function takes a set of GETPROP nodes and produces a string that is + each property separated by dots. +
getQualifiedSlot(String) - +Method in class com.google.javascript.jscomp.SymbolTable.SymbolScope +
Get the slot for a fully-qualified name (e.g., "a.b.c") by trying + to find property scopes at each part of the path. +
getReferenceList(SymbolTable.Symbol) - +Method in class com.google.javascript.jscomp.SymbolTable +
  +
getReferenceName() - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
getReferenceName() - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
getReferenceName() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Gets the reference name for this object. +
getReferenceName() - +Method in class com.google.javascript.rhino.jstype.TemplateType +
  +
getReferenceName() - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
getReferences(Scope.Var) - +Method in class com.google.javascript.jscomp.Scope +
  +
getReferences(SymbolTable.Symbol) - +Method in class com.google.javascript.jscomp.SymbolTable +
  +
getReferences() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns the list of references or null if none. +
getReferences(S) - +Method in interface com.google.javascript.rhino.jstype.StaticSymbolTable +
Returns the references that point to the given symbol. +
getRegion(int) - +Method in class com.google.javascript.jscomp.CompilerInput +
Get a region around the indicated line number. +
getRegion(int) - +Method in class com.google.javascript.jscomp.SourceFile +
Get a region around the indicated line number. +
getRegisteredGroups() - +Method in class com.google.javascript.jscomp.DiagnosticGroups +
Get the registered diagnostic groups, indexed by name. +
getRelativeMappingId(int, int, int) - +Static method in class com.google.debugging.sourcemap.SourceMapGeneratorV2.LineMapEncoder +
  +
getRelativeMappingIdLength(int, int) - +Static method in class com.google.debugging.sourcemap.SourceMapGeneratorV2.LineMapEncoder +
  +
getReport() - +Method in class com.google.javascript.jscomp.jsonml.SecureCompiler +
Returns report from the last compilation. +
getReportCall() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
getReportCall() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
getReportCall() - +Method in interface com.google.javascript.jscomp.InstrumentationOrBuilder +
  +
getReportDefined() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
getReportDefined() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
getReportDefined() - +Method in interface com.google.javascript.jscomp.InstrumentationOrBuilder +
  +
getReportExit() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
getReportExit() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
getReportExit() - +Method in interface com.google.javascript.jscomp.InstrumentationOrBuilder +
  +
getRequires() - +Method in class com.google.javascript.jscomp.CompilerInput +
Gets a list of types depended on by this input. +
getRequires() - +Method in interface com.google.javascript.jscomp.deps.DependencyInfo +
Gets the symbols required by this file. +
getRequires() - +Method in class com.google.javascript.jscomp.deps.SimpleDependencyInfo +
  +
getRequires() - +Method in class com.google.javascript.jscomp.JSModule +
  +
getReservedCharacters() - +Method in enum com.google.javascript.jscomp.AnonymousFunctionNamingPolicy +
Gets characters that are reserved for use in anonymous function names and + can't be used in variable or property names. +
getRestrictedTypeGivenToBooleanOutcome(boolean) - +Method in class com.google.javascript.rhino.jstype.JSType +
Computes the restricted type of this type knowing that the + ToBoolean predicate has a specific value. +
getRestrictedTypeGivenToBooleanOutcome(boolean) - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
getRestrictedUnion(JSType) - +Method in class com.google.javascript.rhino.jstype.UnionType +
Returns a more restricted union type than this one, in which all + subtypes of type have been removed. +
getResult() - +Method in class com.google.javascript.jscomp.Compiler +
Returns the result of the compilation. +
getReturnDescription() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns the description of the returned object or null if none specified. +
getReturnType() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the return type specified by the @return annotation. +
getReturnType() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
getReverseAbstractInterpreter() - +Method in class com.google.javascript.jscomp.AbstractCompiler +
Get an interpreter for type analysis. +
getReverseAbstractInterpreter() - +Method in class com.google.javascript.jscomp.Compiler +
  +
getReverseMapping(String, int, int) - +Method in class com.google.debugging.sourcemap.SourceMapConsumerV3 +
  +
getReverseMapping(String, int, int) - +Method in interface com.google.debugging.sourcemap.SourceMappingReversable +
Given a source file, line, and column, return the reverse mapping (source --> target). +
getRoot() - +Method in class com.google.javascript.jscomp.Compiler +
  +
getRoot() - +Method in class com.google.javascript.rhino.JSTypeExpression +
  +
getRootNode() - +Method in class com.google.javascript.jscomp.Scope +
Gets the container node of the scope. +
getRootNode() - +Method in class com.google.javascript.jscomp.SymbolTable.SymbolScope +
  +
getRootNode() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
  +
getRootNode() - +Method in interface com.google.javascript.rhino.jstype.StaticScope +
Returns the root node associated with this scope. +
getRootNode() - +Method in class com.google.javascript.rhino.testing.AbstractStaticScope +
  +
getRootOfQualifiedName(Node) - +Static method in class com.google.javascript.jscomp.NodeUtil +
Gets the root node of a qualified name. +
getRuntimeRecord() - +Method in class com.google.javascript.jscomp.PerformanceTracker +
  +
getScope() - +Method in class com.google.javascript.jscomp.NodeTraversal +
Gets the current scope. +
getScope(Scope.Var) - +Method in class com.google.javascript.jscomp.Scope +
  +
getScope(SymbolTable.Symbol) - +Method in class com.google.javascript.jscomp.SymbolTable +
  +
getScope(S) - +Method in interface com.google.javascript.rhino.jstype.StaticSymbolTable +
Returns the scope for a given symbol. +
getScopeDepth() - +Method in class com.google.javascript.jscomp.SymbolTable.SymbolScope +
  +
getScopeRoot() - +Method in class com.google.javascript.jscomp.NodeTraversal +
Returns the current scope's root. +
getScriptNode() - +Method in class com.google.javascript.jscomp.FindExportableNodes.GenerateNodeContext +
  +
getSectionType() - +Method in class com.google.debugging.sourcemap.SourceMapSection +
  +
getSectionUrl() - +Method in class com.google.debugging.sourcemap.SourceMapSection +
Deprecated.   +
getSectionValue() - +Method in class com.google.debugging.sourcemap.SourceMapSection +
  +
getSerializedSize() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
getSerializedSize() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
getSerializedSize() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
getSerializedSize() - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
getSerializedSize() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
getSerializedSize() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
getSideEffectFlags() - +Method in class com.google.javascript.rhino.Node +
Returns the side effects flags for this node. +
getSingletonGetterClassName(Node) - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
  +
getSingletonGetterClassName(Node) - +Method in interface com.google.javascript.jscomp.CodingConvention +
Checks if the given method defines a singleton getter, and if it does, + returns the name of the class with the singleton getter. +
getSingletonGetterClassName(Node) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
getSize() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
getSize() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
getSize() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.EntryOrBuilder +
  +
getSlot(String) - +Method in class com.google.javascript.jscomp.Scope +
  +
getSlot(String) - +Method in class com.google.javascript.jscomp.SymbolTable.SymbolScope +
  +
getSlot(String) - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
getSlot(String) - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
getSlot(String) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
  +
getSlot(String) - +Method in interface com.google.javascript.rhino.jstype.StaticScope +
Returns any defined slot within this scope for this name. +
getSlot(String) - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
getSlot(String) - +Method in class com.google.javascript.rhino.testing.AbstractStaticScope +
  +
getSlot(String) - +Method in class com.google.javascript.rhino.testing.MapBasedScope +
  +
getSortedDependenciesOf(List<INPUT>) - +Method in class com.google.javascript.jscomp.deps.SortedDependencies +
Gets all the dependencies of the given roots. +
getSortedList() - +Method in class com.google.javascript.jscomp.deps.SortedDependencies +
  +
getSource() - +Method in class com.google.javascript.jscomp.AbstractMessageFormatter +
Get the source excerpt provider. +
getSource() - +Method in interface com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge +
  +
getSource() - +Method in class com.google.javascript.rhino.jstype.EnumType +
Gets the source node or null if this is an unknown enum. +
getSource() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Gets the source node or null if this is an unknown function. +
getSourceAst() - +Method in class com.google.javascript.jscomp.CompilerInput +
Returns the SourceAst object on which this input is based. +
getSourceExcerpt() - +Method in interface com.google.javascript.jscomp.Region +
Get the source region. +
getSourceExcerpt() - +Method in class com.google.javascript.jscomp.SimpleRegion +
  +
getSourceFile() - +Method in class com.google.javascript.jscomp.CompilerInput +
  +
getSourceFile() - +Method in class com.google.javascript.jscomp.JsAst +
  +
getSourceFile() - +Method in class com.google.javascript.jscomp.jsonml.JsonMLAst +
  +
getSourceFile(Node) - +Static method in class com.google.javascript.jscomp.NodeUtil +
  +
getSourceFile() - +Method in class com.google.javascript.jscomp.Scope.Var +
  +
getSourceFile() - +Method in interface com.google.javascript.jscomp.SourceAst +
Returns the source file the generated AST represents. +
getSourceFile() - +Method in class com.google.javascript.jscomp.SyntheticAst +
  +
getSourceFile() - +Method in class com.google.javascript.rhino.jstype.ObjectType.Property +
  +
getSourceFile() - +Method in class com.google.javascript.rhino.jstype.SimpleReference +
  +
getSourceFile() - +Method in interface com.google.javascript.rhino.jstype.StaticReference +
The source file where the reference lives. +
getSourceFileName() - +Method in class com.google.javascript.jscomp.SymbolTable.Symbol +
  +
getSourceFileName() - +Method in class com.google.javascript.rhino.Node +
  +
getSourceLine(String, int) - +Method in class com.google.javascript.jscomp.Compiler +
  +
getSourceLine(String, int) - +Method in interface com.google.javascript.jscomp.SourceExcerptProvider +
Get the line indicated by the line number. +
getSourceLine(String, int) - +Method in class com.google.javascript.jscomp.testing.SimpleSourceExcerptProvider +
  +
getSourceMap(String) - +Method in interface com.google.debugging.sourcemap.SourceMapSupplier +
  +
getSourceMap() - +Method in class com.google.javascript.jscomp.Compiler +
  +
getSourceName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
getSourceName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
getSourceName() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.EntryOrBuilder +
  +
getSourceName() - +Method in class com.google.javascript.jscomp.JsMessage +
Gets the message's sourceName. +
getSourceName() - +Method in class com.google.javascript.jscomp.jsonml.JsonMLAst +
  +
getSourceName() - +Method in class com.google.javascript.jscomp.NodeTraversal +
Gets the current input source name. +
getSourceName(Node) - +Static method in class com.google.javascript.jscomp.NodeUtil +
  +
getSourceName() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the name of the source file that contains this JSDoc. +
getSourceOffset() - +Method in class com.google.javascript.rhino.Node +
  +
getSourcePosition() - +Method in class com.google.javascript.rhino.Node +
  +
getSourceRegion(String, int) - +Method in class com.google.javascript.jscomp.Compiler +
  +
getSourceRegion(String, int) - +Method in interface com.google.javascript.jscomp.SourceExcerptProvider +
Get a region around the indicated line number. +
getSourceRegion(String, int) - +Method in class com.google.javascript.jscomp.testing.SimpleSourceExcerptProvider +
  +
getStartLine() - +Method in class com.google.javascript.rhino.SourcePosition +
Returns the starting line number of this position. +
getState() - +Method in class com.google.javascript.jscomp.Compiler +
Returns the current internal state, excluding the input files and modules. +
getStaticSourceFile() - +Method in class com.google.javascript.rhino.Node +
Returns the source file associated with this input. +
getStats() - +Method in class com.google.javascript.jscomp.PerformanceTracker +
  +
getString() - +Method in class com.google.javascript.jscomp.jsonml.SecureCompiler +
Returns compiled source as a JavaScript. +
getString() - +Method in class com.google.javascript.rhino.Node +
Can only be called when node has String context. +
getStyle() - +Method in class com.google.javascript.jscomp.CssRenamingMap.ByPart +
  +
getStyle() - +Method in class com.google.javascript.jscomp.CssRenamingMap.ByWhole +
  +
getStyle() - +Method in interface com.google.javascript.jscomp.CssRenamingMap +
  +
getSubTypes() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Returns a list of types that are subtypes of this type. +
getSuperClassConstructor() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Given a constructor or an interface type, get its superclass constructor + or null if none exists. +
getSuppressions() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns the set of suppressed warnings. +
getSymbol() - +Method in class com.google.javascript.jscomp.Scope.Var +
  +
getSymbol() - +Method in class com.google.javascript.rhino.jstype.ObjectType.Property +
  +
getSymbol() - +Method in class com.google.javascript.rhino.jstype.SimpleReference +
  +
getSymbol() - +Method in interface com.google.javascript.rhino.jstype.StaticReference +
The variable that this reference points to. +
getSymbolDeclaredBy(FunctionType) - +Method in class com.google.javascript.jscomp.SymbolTable +
Gets the symbol for the given constuctor or interface. +
getSymbolDeclaredBy(EnumType) - +Method in class com.google.javascript.jscomp.SymbolTable +
Gets the symbol for the given enum. +
getSymbolForInstancesOf(SymbolTable.Symbol) - +Method in class com.google.javascript.jscomp.SymbolTable +
Gets the symbol for the prototype if this is the symbol for a constructor + or interface. +
getSymbolForInstancesOf(FunctionType) - +Method in class com.google.javascript.jscomp.SymbolTable +
Gets the symbol for the prototype of the given constructor or interface. +
getSymbolForScope(SymbolTable.SymbolScope) - +Method in class com.google.javascript.jscomp.SymbolTable +
All local scopes are associated with a function, and some functions + are associated with a symbol. +
getTemplateTypeName() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the template type name. +
getTemplateTypeName() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Gets the template type name. +
GETTER_DEF - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
getThisAndAllDependencies() - +Method in class com.google.javascript.jscomp.JSModule +
Returns this module and all of its dependencies in one list. +
getThisType() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the type specified by the @this annotation. +
getThrownTypes() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns the list of thrown types. +
getTopDefiningInterface(ObjectType, String) - +Static method in class com.google.javascript.rhino.jstype.FunctionType +
Given an interface and a property, finds the top-most super interface + that has the property defined (including this interface). +
getTopMostDefiningType(String) - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Given a constructor or an interface type and a property, finds the + top-most superclass that has the property defined (including this + constructor). +
getTopScope() - +Method in class com.google.javascript.jscomp.AbstractCompiler +
Gets the top scope. +
getTopScope() - +Method in class com.google.javascript.jscomp.Compiler +
  +
getTracerMode() - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
getTweakProcessing() - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
getTweakReplacements() - +Method in class com.google.javascript.jscomp.CompilerOptions +
Returns the map of tweak replacements. +
getType() - +Method in class com.google.javascript.jscomp.JSError +
  +
getType() - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Returns type of the JsonML element. +
getType() - +Method in class com.google.javascript.jscomp.Scope.Var +
Gets this variable's type. +
getType() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the type specified by the @type annotation. +
getType() - +Method in class com.google.javascript.rhino.JSDocInfo.Marker +
Gets the position information for the type expression found + in some block tags, like "@param" and "@return". +
getType(String) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Looks up a type by name. +
getType(StaticScope<JSType>, String, String, int, int) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Looks up a type by name. +
getType() - +Method in class com.google.javascript.rhino.jstype.ObjectType.Property +
  +
getType() - +Method in class com.google.javascript.rhino.jstype.SimpleSlot +
  +
getType() - +Method in interface com.google.javascript.rhino.jstype.StaticSlot +
Returns the type information, if any, for this slot. +
getType() - +Method in class com.google.javascript.rhino.Node +
  +
getTypedefType() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the typedef type specified by the @type annotation. +
getTypedPercent() - +Method in class com.google.javascript.jscomp.BasicErrorManager +
  +
getTypedPercent() - +Method in interface com.google.javascript.jscomp.ErrorManager +
Gets the percentage of typed expressions. +
getTypedScopeCreator() - +Method in class com.google.javascript.jscomp.Compiler +
  +
getTypeNodes() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns a collection of all type nodes that are a part of this JSDocInfo. +
getTypeOfThis() - +Method in class com.google.javascript.jscomp.Scope +
Gets the type of this in the current scope. +
getTypeOfThis() - +Method in class com.google.javascript.jscomp.SymbolTable.SymbolScope +
  +
getTypeOfThis() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Gets the type of this in this function. +
getTypeOfThis() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
  +
getTypeOfThis() - +Method in interface com.google.javascript.rhino.jstype.StaticScope +
Returns the expected type of this in the current scope. +
getTypeOfThis() - +Method in class com.google.javascript.rhino.testing.AbstractStaticScope +
  +
getTypeRegistry() - +Method in class com.google.javascript.jscomp.AbstractCompiler +
Gets a central registry of type information from the compiled JS. +
getTypeRegistry() - +Method in class com.google.javascript.jscomp.Compiler +
  +
getTypes() - +Method in class com.google.javascript.jscomp.DiagnosticGroup +
Returns an iterable over all the types in this group. +
getTypesUnderEquality(JSType) - +Method in class com.google.javascript.rhino.jstype.JSType +
Computes the subset of this and that types if equality + is observed. +
getTypesUnderEquality(JSType) - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
getTypesUnderInequality(JSType) - +Method in class com.google.javascript.rhino.jstype.JSType +
Computes the subset of this and that types if inequality + is observed. +
getTypesUnderInequality(JSType) - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
getTypesUnderShallowEquality(JSType) - +Method in class com.google.javascript.rhino.jstype.JSType +
Computes the subset of this and that types under shallow + equality. +
getTypesUnderShallowInequality(JSType) - +Method in class com.google.javascript.rhino.jstype.JSType +
Computes the subset of this and that types under + shallow inequality. +
getTypesUnderShallowInequality(JSType) - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
getTypesWithProperty(String) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Returns each type that has a property propertyName defined on it. +
getUndirectedGraphEdges(N, N) - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
getUndirectedGraphNode(N) - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
getUndirectedGraphNode(N) - +Method in class com.google.javascript.jscomp.graph.UndiGraph +
  +
getUndirectedGraphNodes() - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
getUniqueFunctionWithName(String) - +Method in class com.google.javascript.jscomp.CallGraph +
Finds a function with the given name. +
getValue() - +Method in interface com.google.javascript.jscomp.graph.Graph.GraphEdge +
Retrieves the edge's value. +
getValue() - +Method in interface com.google.javascript.jscomp.graph.GraphNode +
Retrieves the node's value. +
getValue() - +Method in enum com.google.javascript.jscomp.WarningsGuard.Priority +
  +
getVar(String) - +Method in class com.google.javascript.jscomp.Scope +
Returns the variable, may be null +
getVarCount() - +Method in class com.google.javascript.jscomp.Scope +
Returns number of variables in this scope +
getVars() - +Method in class com.google.javascript.jscomp.Scope +
Return an iterator over all of the variables declared in this scope. +
getVarsDeclaredInBranch(Node) - +Static method in class com.google.javascript.jscomp.NodeUtil +
Retrieves vars declared in the current node tree, excluding descent scopes. +
getVersion() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns the version or null if none. +
getVisibility() - +Method in class com.google.javascript.rhino.JSDocInfo +
Gets the visibility specified by @private, @protected or + @public annotation. +
getWarningCount() - +Method in class com.google.javascript.jscomp.BasicErrorManager +
  +
getWarningCount() - +Method in class com.google.javascript.jscomp.Compiler +
Gets the number of warnings. +
getWarningCount() - +Method in interface com.google.javascript.jscomp.ErrorManager +
Gets the number of reported warnings. +
getWarnings() - +Method in class com.google.javascript.jscomp.BasicErrorManager +
  +
getWarnings() - +Method in class com.google.javascript.jscomp.Compiler +
Returns the array of warnings (never null). +
getWarnings() - +Method in interface com.google.javascript.jscomp.ErrorManager +
Gets all the warnings. +
getWarnings() - +Method in class com.google.javascript.jscomp.jsonml.SecureCompiler.Report +
  +
getWeight(N) - +Method in interface com.google.javascript.jscomp.graph.AdjacencyGraph +
Returns a weight for the given value to be used in ordering nodes, e.g. +
getWeight(N) - +Method in class com.google.javascript.jscomp.graph.Graph +
  +
getZippedCodeSizeRecord() - +Method in class com.google.javascript.jscomp.PerformanceTracker +
  +
GLOBAL_THIS - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
GLOBAL_THIS - +Static variable in class com.google.javascript.jscomp.SymbolTable +
The name we use for the JavaScript built-in Global object. +
GoogleCodingConvention - Class in com.google.javascript.jscomp
This describes the Google-specific JavaScript coding conventions.
GoogleCodingConvention() - +Constructor for class com.google.javascript.jscomp.GoogleCodingConvention +
By default, decorate the ClosureCodingConvention. +
GoogleCodingConvention(CodingConvention) - +Constructor for class com.google.javascript.jscomp.GoogleCodingConvention +
Decorates a wrapped CodingConvention. +
GoogleJsMessageIdGenerator - Class in com.google.javascript.jscomp
An JsMessage.IdGenerator designed to play nicely with Google's Translation + systems.
GoogleJsMessageIdGenerator(String) - +Constructor for class com.google.javascript.jscomp.GoogleJsMessageIdGenerator +
Creates an instance. +
Graph<N,E> - Class in com.google.javascript.jscomp.graph
The base generic class for graph-like data structure and algorithms in + the compiler.
Graph() - +Constructor for class com.google.javascript.jscomp.graph.Graph +
  +
graph - +Variable in class com.google.javascript.jscomp.graph.GraphColoring +
  +
Graph.GraphEdge<N,E> - Interface in com.google.javascript.jscomp.graph
A generic edge.
GraphColoring<N,E> - Class in com.google.javascript.jscomp.graph
Annotates the graph with a color in a way that no connected node will have + the same color.
GraphColoring(AdjacencyGraph<N, E>) - +Constructor for class com.google.javascript.jscomp.graph.GraphColoring +
  +
GraphColoring.Color - Class in com.google.javascript.jscomp.graph
 
GraphColoring.GreedyGraphColoring<N,E> - Class in com.google.javascript.jscomp.graph
Greedily assign nodes with high degree unique colors.
GraphColoring.GreedyGraphColoring(AdjacencyGraph<N, E>) - +Constructor for class com.google.javascript.jscomp.graph.GraphColoring.GreedyGraphColoring +
  +
GraphColoring.GreedyGraphColoring(AdjacencyGraph<N, E>, Comparator<N>) - +Constructor for class com.google.javascript.jscomp.graph.GraphColoring.GreedyGraphColoring +
  +
GraphNode<N,E> - Interface in com.google.javascript.jscomp.graph
A generic node.
GraphPruner<N,E> - Class in com.google.javascript.jscomp.graph
Prunes a graph, creating a new graph with nodes removed.
GraphPruner(DiGraph<N, E>) - +Constructor for class com.google.javascript.jscomp.graph.GraphPruner +
  +
GraphReachability<N,E> - Class in com.google.javascript.jscomp.graph
Computes all the reachable nodes.
GraphReachability(DiGraph<N, E>) - +Constructor for class com.google.javascript.jscomp.graph.GraphReachability +
  +
GraphReachability(DiGraph<N, E>, Predicate<GraphReachability.EdgeTuple<N, E>>) - +Constructor for class com.google.javascript.jscomp.graph.GraphReachability +
  +
GraphReachability.EdgeTuple<N,E> - Class in com.google.javascript.jscomp.graph
Represents Source Node, Edge and Destination Node.
GraphReachability.EdgeTuple(N, E, N) - +Constructor for class com.google.javascript.jscomp.graph.GraphReachability.EdgeTuple +
  +
GraphvizGraph - Interface in com.google.javascript.jscomp.graph
A graph that can be dumped to a Graphviz DOT file.
GraphvizGraph.GraphvizEdge - Interface in com.google.javascript.jscomp.graph
A Graphviz edge.
GraphvizGraph.GraphvizNode - Interface in com.google.javascript.jscomp.graph
A Graphviz node.
GREATEST_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
GT - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
gzDiff - +Variable in class com.google.javascript.jscomp.PerformanceTracker.Stats +
  +
gzSize - +Variable in class com.google.javascript.jscomp.PerformanceTracker.Stats +
  +
+
+

+H

+
+
handleViolation(String, Node) - +Method in interface com.google.javascript.jscomp.AstValidator.ViolationHandler +
  +
hasAppNameSetter() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
hasAppNameSetter() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
hasAppNameSetter() - +Method in interface com.google.javascript.jscomp.InstrumentationOrBuilder +
  +
hasBaseType() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether this JSDocInfo contains a type for @extends + annotation. +
hasBrackets() - +Method in class com.google.javascript.rhino.JSDocInfo.TypePosition +
Returns whether the type has curly braces around it. +
hasCachedValues() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
hasCachedValues() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Returns true if any cached values have been set for this type. +
hasCapturingGroup() - +Method in class com.google.javascript.jscomp.regex.RegExpTree +
True if the regular expression contains capturing groups. +
hasChild(Node) - +Method in class com.google.javascript.rhino.Node +
  +
hasChildren() - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Returns true if the JsonML element has at least one child. +
hasChildren() - +Method in class com.google.javascript.rhino.Node +
  +
hasColumnPosition() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
hasColumnPosition() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
hasColumnPosition() - +Method in interface com.google.debugging.sourcemap.proto.Mapping.LineMappingOrBuilder +
  +
hasColumnPosition() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
hasColumnPosition() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
hasColumnPosition() - +Method in interface com.google.debugging.sourcemap.proto.Mapping.OriginalMappingOrBuilder +
  +
hasCompiledSource() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
hasCompiledSource() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
hasCompiledSource() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.EntryOrBuilder +
  +
hasCompiledSource() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
hasCompiledSource() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
hasCompiledSource() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.ModuleOrBuilder +
  +
hasDescriptionForParameter(String) - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether a description exists for the parameter with the specified + name. +
hasDisplayName() - +Method in class com.google.javascript.rhino.jstype.AllType +
  +
hasDisplayName() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
hasDisplayName() - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
hasEncounteredAllErrors() - +Method in class com.google.javascript.jscomp.testing.TestErrorReporter +
Returns whether all errors were reported to this reporter. +
hasEncounteredAllErrors() - +Method in class com.google.javascript.rhino.testing.TestErrorReporter +
Returns whether all errors were reported to this reporter. +
hasEncounteredAllWarnings() - +Method in class com.google.javascript.jscomp.testing.TestErrorReporter +
Returns whether all warnings were reported to this reporter. +
hasEncounteredAllWarnings() - +Method in class com.google.javascript.rhino.testing.TestErrorReporter +
Returns whether all warnings were reported to this reporter. +
hasEnumParameterType() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether an enum parameter type, specified using the @enum + annotation, is present on this JSDoc. +
hasEqualCallType(FunctionType) - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
hasErrors() - +Method in class com.google.javascript.jscomp.Compiler +
Consults the ErrorManager to see if we've encountered errors + that should halt compilation. +
hasExternTarget() - +Method in class com.google.javascript.jscomp.CallGraph.Callsite +
If true, then this callsite could target a function defined in the + externs. +
hasFileOverview() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether this has a fileoverview flag. +
hashCode() - +Method in class com.google.javascript.jscomp.DiagnosticType +
  +
hashCode() - +Method in class com.google.javascript.jscomp.graph.GraphColoring.Color +
  +
hashCode() - +Method in class com.google.javascript.jscomp.JSError +
  +
hashCode() - +Method in class com.google.javascript.jscomp.JsMessage +
  +
hashCode() - +Method in class com.google.javascript.jscomp.JsMessage.PlaceholderReference +
  +
hashCode() - +Method in class com.google.javascript.jscomp.regex.RegExpTree +
  +
hashCode() - +Method in class com.google.javascript.jscomp.Scope.Arguments +
  +
hashCode() - +Method in class com.google.javascript.jscomp.Scope.Var +
  +
hashCode() - +Method in class com.google.javascript.rhino.InputId +
  +
hashCode() - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
If this is equal to a NamedType object, its hashCode must be equal + to the hashCode of the NamedType object. +
hashCode() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
hashCode() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
hashCode() - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
hashCode() - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
hashCode() - +Method in class com.google.javascript.rhino.JSTypeExpression +
  +
hasId() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
hasId() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
hasId() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.EntryOrBuilder +
  +
hasIdentifier() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
hasIdentifier() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
hasIdentifier() - +Method in interface com.google.debugging.sourcemap.proto.Mapping.OriginalMappingOrBuilder +
  +
hasImplementedInterfaces() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
hasInstanceType() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Returns whether this function type has an instance type. +
hasLineNumber() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
hasLineNumber() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
hasLineNumber() - +Method in interface com.google.debugging.sourcemap.proto.Mapping.LineMappingOrBuilder +
  +
hasLineNumber() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
hasLineNumber() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
hasLineNumber() - +Method in interface com.google.debugging.sourcemap.proto.Mapping.OriginalMappingOrBuilder +
  +
hasLineNumber() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
hasLineNumber() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
hasLineNumber() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.EntryOrBuilder +
  +
hasModifies() - +Method in class com.google.javascript.rhino.JSDocInfo +
  +
hasModuleName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
hasModuleName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
hasModuleName() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.EntryOrBuilder +
  +
hasMoreThanOneChild() - +Method in class com.google.javascript.rhino.Node +
Check for more than one child more efficiently than by iterating over all + the children as is done with Node.getChildCount(). +
hasName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
hasName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
hasName() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.EntryOrBuilder +
  +
hasName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
hasName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
hasName() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.ModuleOrBuilder +
  +
hasNamespace(String) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Determines whether the given JS package exists. +
hasNode(N) - +Method in class com.google.javascript.jscomp.graph.Graph +
Checks whether the node exists in the graph (Graph.createNode(Object) + has been called with that value). +
hasOneChild() - +Method in class com.google.javascript.rhino.Node +
Check for one child more efficiently than by iterating over all the + children as is done with Node.getChildCount(). +
hasOriginalFile() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
hasOriginalFile() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
hasOriginalFile() - +Method in interface com.google.debugging.sourcemap.proto.Mapping.OriginalMappingOrBuilder +
  +
hasOriginalMapping() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
hasOriginalMapping() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
hasOriginalMapping() - +Method in interface com.google.debugging.sourcemap.proto.Mapping.LineMappingOrBuilder +
  +
hasOwnProperty(String) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Checks whether the property whose name is given is present directly on + the object. +
hasParameter(String) - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the parameter is defined. +
hasParameter(String) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
  +
hasParameterType(String) - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the parameter has an attached type. +
hasParts() - +Method in class com.google.javascript.jscomp.JsMessage.Builder +
Gets whether at least one part has been appended. +
hasProperty(String) - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
hasProperty(String) - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
hasProperty(String) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Checks whether the property whose name is given is present on the + object. +
hasProperty(String) - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
hasReferenceName() - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
hasReferenceName() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Returns true if the object is named. +
hasReportCall() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
hasReportCall() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
hasReportCall() - +Method in interface com.google.javascript.jscomp.InstrumentationOrBuilder +
  +
hasReportDefined() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
hasReportDefined() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
hasReportDefined() - +Method in interface com.google.javascript.jscomp.InstrumentationOrBuilder +
  +
hasReportExit() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
hasReportExit() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
hasReportExit() - +Method in interface com.google.javascript.jscomp.InstrumentationOrBuilder +
  +
hasReturnType() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether this JSDocInfo contains a type for @return + annotation. +
hasScope() - +Method in class com.google.javascript.jscomp.NodeTraversal +
  +
hasSize() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
hasSize() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
hasSize() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.EntryOrBuilder +
  +
hasSourceName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
hasSourceName() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
hasSourceName() - +Method in interface com.google.javascript.jscomp.FunctionInformationMap.EntryOrBuilder +
  +
hasThisType() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether this JSDocInfo contains a type for @this + annotation. +
hasType() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether a type, specified using the @type annotation, is + present on this JSDoc. +
hasTypedefType() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether a typedef parameter type, specified using the + @typedef annotation, is present on this JSDoc. +
hasUnknownTarget() - +Method in class com.google.javascript.jscomp.CallGraph.Callsite +
If true, then DefinitionProvider used in callgraph construction + was unable find all target functions of this callsite. +
hasVarArgs() - +Method in class com.google.javascript.rhino.jstype.FunctionParamBuilder +
  +
hook(Node, Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
HOOK - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
HotSwapCompilerPass - Interface in com.google.javascript.jscomp
Interface for compiler passes that can be used in a hot-swap fashion.
hotSwapScript(Node, Node) - +Method in class com.google.javascript.jscomp.FieldCleanupPass +
  +
hotSwapScript(Node, Node) - +Method in interface com.google.javascript.jscomp.HotSwapCompilerPass +
Process the JS with root node root. +
+
+

+I

+
+
ID_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
ideMode - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Configures the compiler for use as an IDE backend. +
IDENTIFIER_FIELD_NUMBER - +Static variable in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
identifyNonNullableName(String) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Identifies the name of a typedef or enum before we actually declare it. +
identifyTypeDeclarationCall(Node) - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
  +
identifyTypeDeclarationCall(Node) - +Method in interface com.google.javascript.jscomp.CodingConvention +
Checks if the given CALL node is forward-declaring any types, + and returns the name of the types if it is. +
identifyTypeDeclarationCall(Node) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
idGenerator() - +Method in class com.google.javascript.jscomp.EmptyMessageBundle +
Gets a dummy message ID generator. +
idGenerator() - +Method in interface com.google.javascript.jscomp.MessageBundle +
Gets the message ID generator to use to compute message IDs for this + type of bundle. +
idGenerator() - +Method in class com.google.javascript.jscomp.XtbMessageBundle +
  +
idGeneratorMap - +Variable in class com.google.javascript.jscomp.Result +
  +
IF - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
ifNode(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
ifNode(Node, Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
IN - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
INC - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
INCRDECR_PROP - +Static variable in class com.google.javascript.rhino.Node +
  +
incrementGeneration() - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Increments the current generation. +
inExterns() - +Method in class com.google.javascript.jscomp.SymbolTable.Symbol +
  +
inGlobalScope() - +Method in class com.google.javascript.jscomp.SymbolTable.Symbol +
  +
init(JSSourceFile[], JSSourceFile[], CompilerOptions) - +Method in class com.google.javascript.jscomp.Compiler +
Initializes the instance state needed for a compile job. +
init(List<T1>, List<T2>, CompilerOptions) - +Method in class com.google.javascript.jscomp.Compiler +
Initializes the instance state needed for a compile job. +
init(JSSourceFile[], JSModule[], CompilerOptions) - +Method in class com.google.javascript.jscomp.Compiler +
Initializes the instance state needed for a compile job if the sources + are in modules. +
INIT_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.Instrumentation +
  +
initModules(List<T>, List<JSModule>, CompilerOptions) - +Method in class com.google.javascript.jscomp.Compiler +
Initializes the instance state needed for a compile job if the sources + are in modules. +
initOptions(CompilerOptions) - +Method in class com.google.javascript.jscomp.Compiler +
Initialize the compiler options. +
initTypes() - +Method in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
inlineConstantVars - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Inlines constants (symbols that are all CAPS) +
inlineFunctions - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Inlines short functions +
inlineGetters - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Inlines trivial getters +
inlineLocalFunctions - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Enhanced function inlining +
inlineVariables - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Inlines variables +
INPUT_ID - +Static variable in class com.google.javascript.rhino.Node +
  +
inputDelimiter - +Variable in class com.google.javascript.jscomp.CompilerOptions +
The string to use as the separator for printInputDelimiter +
InputId - Class in com.google.javascript.rhino
An id used uniquely identify a CompilerInput
InputId(String) - +Constructor for class com.google.javascript.rhino.InputId +
  +
inputPropertyMapSerialized - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Serialized input property renaming map. +
inputVariableMapSerialized - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Serialized input variable renaming map. +
INSTANCEOF - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
Instrumentation - Class in com.google.javascript.jscomp
 
Instrumentation.Builder - Class in com.google.javascript.jscomp
 
InstrumentationOrBuilder - Interface in com.google.javascript.jscomp
 
instrumentationTemplate - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Instrumentation template to use with #recordFunctionInformation +
InstrumentationTemplate - Class in com.google.javascript.jscomp
 
internalGetFieldAccessorTable() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
internalGetFieldAccessorTable() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
internalGetFieldAccessorTable() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
internalGetFieldAccessorTable() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
internalGetFieldAccessorTable() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
internalGetFieldAccessorTable() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
internalGetFieldAccessorTable() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
internalGetFieldAccessorTable() - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
internalGetFieldAccessorTable() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
internalGetFieldAccessorTable() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
internalGetFieldAccessorTable() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
internalGetFieldAccessorTable() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
INTERNET_EXPLORER_CHECKS - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
intersection(BooleanLiteralSet) - +Method in enum com.google.javascript.rhino.jstype.BooleanLiteralSet +
Computes the intersection of this set and that. +
INVALID_CASTS - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
IR - Class in com.google.javascript.rhino
An AST construction helper class
IS_CONSTANT_NAME - +Static variable in class com.google.javascript.rhino.Node +
  +
IS_DISPATCHER - +Static variable in class com.google.javascript.rhino.Node +
  +
IS_NAMESPACE - +Static variable in class com.google.javascript.rhino.Node +
  +
IS_OPTIONAL_PARAM - +Static variable in class com.google.javascript.rhino.Node +
  +
IS_VAR_ARGS_PARAM - +Static variable in class com.google.javascript.rhino.Node +
  +
isAdd() - +Method in class com.google.javascript.rhino.Node +
AST type check methods +
isAliased() - +Method in class com.google.javascript.jscomp.CallGraph.Function +
Returns true if the function is aliased. +
isAllType() - +Method in class com.google.javascript.rhino.jstype.AllType +
  +
isAllType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isAnd() - +Method in class com.google.javascript.rhino.Node +
  +
isAnonymous() - +Method in class com.google.javascript.jscomp.JsMessage +
  +
isArrayLit() - +Method in class com.google.javascript.rhino.Node +
  +
isArrayType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isAssign() - +Method in class com.google.javascript.rhino.Node +
  +
isAssignAdd() - +Method in class com.google.javascript.rhino.Node +
  +
isBleedingFunction() - +Method in class com.google.javascript.jscomp.Scope.Var +
Whether this is a bleeding function (an anonymous named function + that bleeds into the inner scope. +
isBlock() - +Method in class com.google.javascript.rhino.Node +
  +
isBooleanObjectType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isBooleanValueType() - +Method in class com.google.javascript.rhino.jstype.BooleanType +
  +
isBooleanValueType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isBreak() - +Method in class com.google.javascript.rhino.Node +
  +
isCall() - +Method in class com.google.javascript.rhino.Node +
  +
isCase() - +Method in class com.google.javascript.rhino.Node +
  +
isCaseSensitive() - +Method in class com.google.javascript.jscomp.regex.RegExpTree +
True if the presence or absence of an "i" flag would change the + meaning of this regular expression. +
isCatch() - +Method in class com.google.javascript.rhino.Node +
  +
isCheckedUnknownType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isCheckedUnknownType() - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
isComma() - +Method in class com.google.javascript.rhino.Node +
  +
isConnected(N, N) - +Method in class com.google.javascript.jscomp.graph.DiGraph +
  +
isConnected(N, E, N) - +Method in class com.google.javascript.jscomp.graph.DiGraph +
  +
isConnected(N, N) - +Method in class com.google.javascript.jscomp.graph.Graph +
Checks whether two nodes in the graph are connected. +
isConnected(N, E, N) - +Method in class com.google.javascript.jscomp.graph.Graph +
Checks whether two nodes in the graph are connected by the given + edge type. +
isConnected(N, N) - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
isConnected(N, E, N) - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
isConnectedInDirection(N, N) - +Method in class com.google.javascript.jscomp.graph.DiGraph +
Checks whether two nodes in the graph are connected via a directed edge. +
isConnectedInDirection(N, E, N) - +Method in class com.google.javascript.jscomp.graph.DiGraph +
Checks whether two nodes in the graph are connected via a directed edge + with the given value. +
isConnectedInDirection(N, N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
isConnectedInDirection(N, E, N) - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
isConsistentIdGenerator() - +Method in class com.google.javascript.rhino.JSDocInfo +
  +
isConst() - +Method in class com.google.javascript.jscomp.Scope.Var +
Returns true if the variable is declared as a constant, + based on the value reported by NodeUtil. +
isConstant(String) - +Method in interface com.google.javascript.jscomp.CodingConvention +
This checks whether a given variable name, such as a name in all-caps + should be treated as if it had the @const annotation. +
isConstant(String) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
isConstant(String) - +Method in class com.google.javascript.jscomp.GoogleCodingConvention +
This checks whether a given variable name, such as a name in all-caps + should be treated as if it had the @const annotation. +
isConstant() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @const annotation is present on this + JSDocInfo. +
isConstantKey(String) - +Method in interface com.google.javascript.jscomp.CodingConvention +
This checks whether a given key of an object literal, such as a + name in all-caps should be treated as if it had the @const + annotation. +
isConstantKey(String) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
isConstantKey(String) - +Method in class com.google.javascript.jscomp.GoogleCodingConvention +
  +
isConstructor() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @constructor annotation is present on this + JSDocInfo. +
isConstructor() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
isConstructor() - +Method in class com.google.javascript.rhino.jstype.JSType +
Whether this type is a FunctionType that is a constructor or a + named type that points to such a type. +
isConstructorRecorded() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Whether the JSDocInfo being built will have its + JSDocInfo.isConstructor() flag set to true. +
isContinue() - +Method in class com.google.javascript.rhino.Node +
  +
isDateType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isDebugger() - +Method in class com.google.javascript.rhino.Node +
  +
isDec() - +Method in class com.google.javascript.rhino.Node +
  +
isDeclared(String, boolean) - +Method in class com.google.javascript.jscomp.Scope +
Returns true if a variable is declared. +
isDefaultCase() - +Method in class com.google.javascript.rhino.Node +
  +
isDefine() - +Method in class com.google.javascript.jscomp.Scope.Var +
Returns true if the variable is declared as a define. +
isDefine() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @define annotation is present on this + JSDocInfo. +
isDelProp() - +Method in class com.google.javascript.rhino.Node +
  +
isDeprecated() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @deprecated annotation is present on this + JSDocInfo. +
isDescriptionRecorded() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Returns whether this builder recorded a description. +
isDirected() - +Method in interface com.google.javascript.jscomp.graph.GraphvizGraph +
Graph type. +
isDirected() - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
isDirected() - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
isDo() - +Method in class com.google.javascript.rhino.Node +
  +
isDocOnlyParameter() - +Method in class com.google.javascript.jscomp.SymbolTable.Symbol +
Whether this is a variable that's only in JSDoc. +
isDocScope() - +Method in class com.google.javascript.jscomp.SymbolTable.SymbolScope +
Returns whether this is a doc scope. +
isEmpty() - +Method in class com.google.javascript.jscomp.JsMessage +
  +
isEmpty() - +Method in class com.google.javascript.rhino.Node +
  +
isEmptyType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isEnumElementType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isEnumType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isEquivalent(JSType, JSType) - +Static method in class com.google.javascript.rhino.jstype.JSType +
  +
isEquivalentTo(JSType) - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
isEquivalentTo(JSType) - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Two function types are equal if their signatures match. +
isEquivalentTo(JSType) - +Method in class com.google.javascript.rhino.jstype.JSType +
Checks if two types are equivalent. +
isEquivalentTo(JSType) - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
isEquivalentTo(JSType) - +Method in class com.google.javascript.rhino.jstype.UnionType +
Two union types are equal if they have the same number of alternates + and all alternates are equal. +
isEquivalentTo(Node) - +Method in class com.google.javascript.rhino.Node +
Returns true if this node is equivalent semantically to another +
isEquivalentToTyped(Node) - +Method in class com.google.javascript.rhino.Node +
Returns true if this node is equivalent semantically to another and + the types are equivalent. +
isExport() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @export annotation is present on this + JSDocInfo. +
isExported(String, boolean) - +Method in interface com.google.javascript.jscomp.CodingConvention +
Checks whether a global variable or function name should be treated as + exported, or externally referenceable. +
isExported(String) - +Method in interface com.google.javascript.jscomp.CodingConvention +
Should be isExported(name, true) || isExported(name, false); +
isExported(String, boolean) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
isExported(String) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
isExported(String, boolean) - +Method in class com.google.javascript.jscomp.GoogleCodingConvention +
Checks whether a global variable or function name should be treated as + exported, or externally referenceable. +
isExpose() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @expose annotation is present on this + JSDocInfo. +
isExposedToCallOrApply() - +Method in class com.google.javascript.jscomp.CallGraph.Function +
Returns true if the function is ever exposed to ".call" or ".apply". +
isExpression(JsonML) - +Static method in class com.google.javascript.jscomp.jsonml.JsonMLUtil +
Checks if the specified JsonML element represents an expression. +
isExprResult() - +Method in class com.google.javascript.rhino.Node +
  +
isExtern() - +Method in class com.google.javascript.jscomp.CompilerInput +
  +
isExtern() - +Method in class com.google.javascript.jscomp.SourceFile +
Returns whether this is an extern. +
isExtern() - +Method in class com.google.javascript.rhino.jstype.SimpleSourceFile +
  +
isExtern() - +Method in interface com.google.javascript.rhino.jstype.StaticSourceFile +
Returns whether this is an externs file. +
isExternal() - +Method in class com.google.javascript.jscomp.JsMessage +
  +
isExternExportsEnabled() - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
isExterns() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @externs annotation is present on this + JSDocInfo. +
isFalse() - +Method in class com.google.javascript.rhino.Node +
  +
isFor() - +Method in class com.google.javascript.rhino.Node +
  +
isForwardDeclaredType(String) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Whether this is a forward-declared type name. +
isFromExterns() - +Method in class com.google.javascript.rhino.Node +
  +
isFunction() - +Method in class com.google.javascript.rhino.Node +
  +
isFunctionPrototypeType() - +Method in class com.google.javascript.rhino.jstype.JSType +
Whether this is the prototype of a function. +
isFunctionPrototypeType() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
  +
isFunctionType() - +Method in class com.google.javascript.rhino.jstype.JSType +
Returns true if toMaybeFunctionType returns a non-null FunctionType. +
isGetElem() - +Method in class com.google.javascript.rhino.Node +
  +
isGetProp() - +Method in class com.google.javascript.rhino.Node +
  +
isGetterDef() - +Method in class com.google.javascript.rhino.Node +
  +
isGlobal() - +Method in class com.google.javascript.jscomp.Scope +
Returns whether this is the global scope. +
isGlobal() - +Method in class com.google.javascript.jscomp.Scope.Var +
Returns whether this is a global variable. +
isGlobalScope() - +Method in class com.google.javascript.jscomp.SymbolTable.SymbolScope +
  +
isGlobalThisType() - +Method in class com.google.javascript.rhino.jstype.JSType +
Returns true if this is a global this type. +
isHidden() - +Method in class com.google.javascript.jscomp.JsMessage +
Gets whether this message should be hidden from volunteer translators (to + reduce the chances of a new feature leak). +
isHidden() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @hidden annotation is present on this + JSDocInfo. +
isHook() - +Method in class com.google.javascript.rhino.Node +
  +
isIdeMode() - +Method in class com.google.javascript.jscomp.Compiler +
  +
isIdGenerator() - +Method in class com.google.javascript.rhino.JSDocInfo +
  +
isIf() - +Method in class com.google.javascript.rhino.Node +
  +
isImplicitCast() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @implicitCast annotation is present on this + JSDocInfo. +
isIn() - +Method in class com.google.javascript.rhino.Node +
  +
isInc() - +Method in class com.google.javascript.rhino.Node +
  +
isIndependentOf(N) - +Method in interface com.google.javascript.jscomp.graph.SubGraph +
Returns true if the node is a neighbor of any node in this SubGraph. +
isInitialized() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
isInitialized() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
isInitialized() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
isInitialized() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
isInitialized() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
isInitialized() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
isInitialized() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
isInitialized() - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
isInitialized() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
isInitialized() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
isInitialized() - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
isInitialized() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
isInstanceOf() - +Method in class com.google.javascript.rhino.Node +
  +
isInstanceType() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
isInstanceType() - +Method in class com.google.javascript.rhino.jstype.JSType +
Whether this type is an Instance object of some constructor. +
isInterface() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @interface annotation is present on this + JSDocInfo. +
isInterface() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
isInterface() - +Method in class com.google.javascript.rhino.jstype.JSType +
Whether this type is a FunctionType that is an interface or a named + type that points to such a type. +
isInterfaceRecorded() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Whether the JSDocInfo being built will have its + JSDocInfo.isInterface() flag set to true. +
isJavaDispatch() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @javadispath annotation is present on this + JSDocInfo. +
isJavaDispatch() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Whether the JSDocInfo being built will have its + JSDocInfo.isJavaDispatch() flag set to true. +
isJSIdentifier(String) - +Static method in class com.google.javascript.rhino.TokenStream +
  +
isJSLineTerminator(int) - +Static method in class com.google.javascript.rhino.ScriptRuntime +
  +
isKeyword(String) - +Static method in class com.google.javascript.rhino.TokenStream +
  +
isLabel() - +Method in class com.google.javascript.rhino.Node +
  +
isLabelName() - +Method in class com.google.javascript.rhino.Node +
  +
isLexicalScope() - +Method in class com.google.javascript.jscomp.SymbolTable.SymbolScope +
  +
isLexicalVariable() - +Method in class com.google.javascript.jscomp.SymbolTable.Symbol +
Whether this is a variable in a lexical scope. +
isLocal() - +Method in class com.google.javascript.jscomp.Scope +
Returns whether this is a local scope (i.e. +
isLocal() - +Method in class com.google.javascript.jscomp.Scope.Var +
Returns whether this is a local variable. +
isLocalResultCall() - +Method in class com.google.javascript.rhino.Node +
Returns true if this node is a function or constructor call that + returns a primitive or a local object (an object that has no other + references). +
isMain() - +Method in class com.google.javascript.jscomp.CallGraph.Function +
Does this function represent the global "main" function? +
isName() - +Method in class com.google.javascript.rhino.Node +
  +
isNativeObjectType() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Whether this is a built-in object. +
isNE() - +Method in class com.google.javascript.rhino.Node +
  +
isNew() - +Method in class com.google.javascript.rhino.Node +
  +
isNoAlias() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @noalias annotation is present on this + JSDocInfo. +
isNoCompile() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @nocompile annotation is present on this + JSDocInfo. +
isNominalConstructor() - +Method in class com.google.javascript.rhino.jstype.JSType +
Whether this type is the original constructor of a nominal type. +
isNominalType() - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
isNominalType() - +Method in class com.google.javascript.rhino.jstype.JSType +
Whether this type is a nominal type (a named instance object or + a named enum). +
isNoObjectType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isNoObjectType() - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
isNoObjectType() - +Method in class com.google.javascript.rhino.jstype.NoType +
  +
isNoResolvedType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isNoShadow() - +Method in class com.google.javascript.jscomp.Scope.Var +
  +
isNoShadow() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @noshadow annotation is present on this + JSDocInfo. +
isNoSideEffects() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @nosideeffects annotation is present on this + JSDocInfo. +
isNoSideEffectsCall() - +Method in class com.google.javascript.rhino.Node +
Returns true if this node is a function or constructor call that + has no side effects. +
isNot() - +Method in class com.google.javascript.rhino.Node +
  +
isNoType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isNoType() - +Method in class com.google.javascript.rhino.jstype.NoType +
  +
isNoTypeCheck() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @nocheck annotation is present on this + JSDocInfo. +
isNull() - +Method in class com.google.javascript.rhino.Node +
  +
isNullable() - +Method in class com.google.javascript.rhino.jstype.BooleanType +
  +
isNullable() - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
This predicate determines whether objects of this type can have the null + value, and therefore can appear in contexts where null is expected. +
isNullable() - +Method in class com.google.javascript.rhino.jstype.JSType +
Tests whether this type is nullable. +
isNullable() - +Method in class com.google.javascript.rhino.jstype.NoType +
  +
isNullable() - +Method in class com.google.javascript.rhino.jstype.NullType +
  +
isNullable() - +Method in class com.google.javascript.rhino.jstype.NumberType +
  +
isNullable() - +Method in class com.google.javascript.rhino.jstype.UnionType +
This predicate determines whether objects of this type can have the + null value, and therefore can appear in contexts where + null is expected. +
isNullable() - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
isNullType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isNullType() - +Method in class com.google.javascript.rhino.jstype.NullType +
  +
isNumber() - +Method in class com.google.javascript.rhino.jstype.JSType +
Tests whether the type is a number (value or Object). +
isNumber() - +Method in class com.google.javascript.rhino.Node +
  +
isNumberObjectType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isNumberValueType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isNumberValueType() - +Method in class com.google.javascript.rhino.jstype.NumberType +
  +
isObject() - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
isObject() - +Method in class com.google.javascript.rhino.jstype.JSType +
Tests whether this type is an Object, or any subtype thereof. +
isObject() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
  +
isObject() - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
isObjectLit() - +Method in class com.google.javascript.rhino.Node +
  +
isOn() - +Method in enum com.google.javascript.jscomp.CompilerOptions.TweakProcessing +
  +
isOnlyModifiesThisCall() - +Method in class com.google.javascript.rhino.Node +
  +
isOptionalArg() - +Method in class com.google.javascript.rhino.JSTypeExpression +
  +
isOptionalArg() - +Method in class com.google.javascript.rhino.Node +
Returns whether this node is an optional argument node. +
isOptionalParameter(Node) - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
  +
isOptionalParameter(Node) - +Method in interface com.google.javascript.jscomp.CodingConvention +
This checks whether a given parameter name should be treated as an + optional parameter as far as type checking or function call arg count + checking is concerned. +
isOptionalParameter(Node) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
isOptionalParameter(Node) - +Method in class com.google.javascript.jscomp.GoogleCodingConvention +
This checks whether a given parameter name should be treated as an + optional parameter as far as type checking or function call arg count + checking is concerned. +
isOr() - +Method in class com.google.javascript.rhino.Node +
  +
isOrdinaryFunction() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
isOrdinaryFunction() - +Method in class com.google.javascript.rhino.jstype.JSType +
Whether this type is a FunctionType that is an ordinary function or + a named type that points to such a type. +
isOverride() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @override annotation is present on this + JSDocInfo. +
isParamList() - +Method in class com.google.javascript.rhino.Node +
  +
isPopulated() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Returns whether this builder is populated with information that can be + used to JSDocInfoBuilder.build(com.google.javascript.rhino.Node) a JSDocInfo object. +
isPopulatedWithFileOverview() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Returns whether this builder is populated with information that can be + used to JSDocInfoBuilder.build(com.google.javascript.rhino.Node) a JSDocInfo object that has a + fileoverview tag. +
isPrivate(String) - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
  +
isPrivate(String) - +Method in interface com.google.javascript.jscomp.CodingConvention +
Checks whether a name should be considered private. +
isPrivate(String) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
isPrivate(String) - +Method in class com.google.javascript.jscomp.GoogleCodingConvention +
Checks whether a name should be considered private. +
isProperty() - +Method in class com.google.javascript.jscomp.SymbolTable.Symbol +
Whether this is a property of another variable. +
isPropertyInExterns(String) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Checks whether the property was defined in the externs. +
isPropertyScope() - +Method in class com.google.javascript.jscomp.SymbolTable.SymbolScope +
  +
isPropertyTestFunction(Node) - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
  +
isPropertyTestFunction(Node) - +Method in interface com.google.javascript.jscomp.CodingConvention +
Whether this CALL function is testing for the existence of a property. +
isPropertyTestFunction(Node) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
isPropertyTestFunction(Node) - +Method in class com.google.javascript.jscomp.JqueryCodingConvention +
  +
isPropertyTypeDeclared(String) - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
isPropertyTypeDeclared(String) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Checks whether the property's type is declared. +
isPropertyTypeDeclared(String) - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
isPropertyTypeInferred(String) - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
isPropertyTypeInferred(String) - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
isPropertyTypeInferred(String) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Checks whether the property's type is inferred. +
isPropertyTypeInferred(String) - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
isPrototypeAlias(Node) - +Method in interface com.google.javascript.jscomp.CodingConvention +
Whether this GETPROP node is an alias for an object prototype. +
isPrototypeAlias(Node) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
isPrototypeAlias(Node) - +Method in class com.google.javascript.jscomp.JqueryCodingConvention +
  +
isQualifiedName() - +Method in class com.google.javascript.rhino.Node +
Returns whether a node corresponds to a simple or a qualified name, such as + x or a.b.c or this.a. +
isQuotedString() - +Method in class com.google.javascript.rhino.Node +
This should only be called for STRING nodes children of OBJECTLIT. +
isRecordType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isRegExp() - +Method in class com.google.javascript.rhino.Node +
  +
isRegexpType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isRelationalOperation(Node) - +Static method in class com.google.javascript.jscomp.NodeUtil +
Returns true if the operator on this node is relational. +
isResolved() - +Method in class com.google.javascript.rhino.jstype.JSType +
Whether the type has been resolved. +
isReturn() - +Method in class com.google.javascript.rhino.Node +
  +
isReturnTypeInferred() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
isScript() - +Method in class com.google.javascript.rhino.Node +
  +
isSetterDef() - +Method in class com.google.javascript.rhino.Node +
  +
isString() - +Method in class com.google.javascript.rhino.jstype.JSType +
Tests whether the type is a string (value or Object). +
isString() - +Method in class com.google.javascript.rhino.Node +
  +
isStringObjectType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isStringValueType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isStringValueType() - +Method in class com.google.javascript.rhino.jstype.StringType +
  +
isSubtype(JSType) - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
isSubtype(JSType) - +Method in class com.google.javascript.rhino.jstype.EnumType +
  +
isSubtype(JSType) - +Method in class com.google.javascript.rhino.jstype.FunctionType +
A function is a subtype of another if their call methods are related via + subtyping and this is a subtype of that with regard to + the prototype chain. +
isSubtype(JSType) - +Method in class com.google.javascript.rhino.jstype.JSType +
Checks whether this is a subtype of that. +
isSubtype(JSType) - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
isSubtype(JSType) - +Method in class com.google.javascript.rhino.jstype.NoType +
  +
isSubtype(JSType) - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
isSubtype(JSType) - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
isSuccessful() - +Method in class com.google.javascript.jscomp.jsonml.SecureCompiler.Report +
  +
isSuperClassReference(String) - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
  +
isSuperClassReference(String) - +Method in interface com.google.javascript.jscomp.CodingConvention +
Returns true if passed a string referring to the superclass. +
isSuperClassReference(String) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
isSwitch() - +Method in class com.google.javascript.rhino.Node +
  +
isSymmetricOperation(Node) - +Static method in class com.google.javascript.jscomp.NodeUtil +
Returns true if the operator on this node is symmetric +
isSyntheticBlock() - +Method in class com.google.javascript.rhino.Node +
Returns whether this is a synthetic block that should not be considered + a real source block. +
isTemplateType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isTemplateType() - +Method in class com.google.javascript.rhino.jstype.TemplateType +
  +
isThis() - +Method in class com.google.javascript.rhino.Node +
  +
isThrow() - +Method in class com.google.javascript.rhino.Node +
  +
isTrue() - +Method in class com.google.javascript.rhino.Node +
  +
isTry() - +Method in class com.google.javascript.rhino.Node +
  +
isTypeCheckingEnabled() - +Method in class com.google.javascript.jscomp.Compiler +
  +
isTypeInferred() - +Method in class com.google.javascript.jscomp.Scope.Var +
Returns whether this variable's type is inferred. +
isTypeInferred() - +Method in class com.google.javascript.rhino.jstype.ObjectType.Property +
  +
isTypeInferred() - +Method in class com.google.javascript.rhino.jstype.SimpleSlot +
  +
isTypeInferred() - +Method in interface com.google.javascript.rhino.jstype.StaticSlot +
Returns whether the type has been inferred (as opposed to declared). +
isTypeOf() - +Method in class com.google.javascript.rhino.Node +
  +
isUnionType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isUnknownType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isUnknownType() - +Method in class com.google.javascript.rhino.jstype.ObjectType +
We treat this as the unknown type if any of its implicit prototype + properties is unknown. +
isUnknownType() - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
isUnknownType() - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
isUnscopedQualifiedName() - +Method in class com.google.javascript.rhino.Node +
Returns whether a node corresponds to a simple or a qualified name without + a "this" reference, such as a.b.c, but not this.a + . +
isValidEnumKey(String) - +Method in interface com.google.javascript.jscomp.CodingConvention +
This checks that a given key may be used as a key for an enum. +
isValidEnumKey(String) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
isValidEnumKey(String) - +Method in class com.google.javascript.jscomp.GoogleCodingConvention +
This checks that a given key may be used as a key for an enum. +
isValidQualifiedName(String) - +Static method in class com.google.javascript.jscomp.NodeUtil +
Determines whether the given name is a valid qualified name. +
isValidSimpleName(String) - +Static method in class com.google.javascript.jscomp.NodeUtil +
Determines whether the given name is a valid variable name. +
isVar() - +Method in class com.google.javascript.rhino.Node +
  +
isVarArgs() - +Method in class com.google.javascript.rhino.JSTypeExpression +
  +
isVarArgs() - +Method in class com.google.javascript.rhino.Node +
Returns whether this node is a variable length argument node. +
isVarArgsParameter(Node) - +Method in class com.google.javascript.jscomp.ClosureCodingConvention +
  +
isVarArgsParameter(Node) - +Method in interface com.google.javascript.jscomp.CodingConvention +
This checks whether a given parameter should be treated as a marker + for a variable argument list function. +
isVarArgsParameter(Node) - +Method in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
isVarArgsParameter(Node) - +Method in class com.google.javascript.jscomp.GoogleCodingConvention +
  +
isVoid() - +Method in class com.google.javascript.rhino.Node +
  +
isVoidType() - +Method in class com.google.javascript.rhino.jstype.JSType +
  +
isVoidType() - +Method in class com.google.javascript.rhino.jstype.VoidType +
  +
isWhile() - +Method in class com.google.javascript.rhino.Node +
  +
isWith() - +Method in class com.google.javascript.rhino.Node +
  +
iterator() - +Method in class com.google.javascript.rhino.Node.AncestorIterable +
  +
+
+

+J

+
+
JqueryCodingConvention - Class in com.google.javascript.jscomp
This describes the jQuery specific JavaScript coding conventions.
JqueryCodingConvention() - +Constructor for class com.google.javascript.jscomp.JqueryCodingConvention +
  +
JqueryCodingConvention(CodingConvention) - +Constructor for class com.google.javascript.jscomp.JqueryCodingConvention +
  +
jqueryPass - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Processes jQuery aliases +
JsAst - Class in com.google.javascript.jscomp
Generates an AST for a JavaScript source file.
JsAst(SourceFile) - +Constructor for class com.google.javascript.jscomp.JsAst +
  +
JSDOC_INFO_PROP - +Static variable in class com.google.javascript.rhino.Node +
  +
JSDocInfo - Class in com.google.javascript.rhino
JSDoc information describing JavaScript code.
JSDocInfo() - +Constructor for class com.google.javascript.rhino.JSDocInfo +
  +
JSDocInfo.Marker - Class in com.google.javascript.rhino
Defines a class for containing the parsing information + for this JSDocInfo.
JSDocInfo.Marker() - +Constructor for class com.google.javascript.rhino.JSDocInfo.Marker +
  +
JSDocInfo.NamePosition - Class in com.google.javascript.rhino
A piece of information (found in a marker) which contains a position + with a name node.
JSDocInfo.NamePosition() - +Constructor for class com.google.javascript.rhino.JSDocInfo.NamePosition +
  +
JSDocInfo.StringPosition - Class in com.google.javascript.rhino
A piece of information (found in a marker) which contains a position + with a string.
JSDocInfo.StringPosition() - +Constructor for class com.google.javascript.rhino.JSDocInfo.StringPosition +
  +
JSDocInfo.TypePosition - Class in com.google.javascript.rhino
A piece of information (found in a marker) which contains a position + with a type expression syntax tree.
JSDocInfo.TypePosition() - +Constructor for class com.google.javascript.rhino.JSDocInfo.TypePosition +
  +
JSDocInfo.Visibility - Enum in com.google.javascript.rhino
Visibility categories.
JSDocInfoBuilder - Class in com.google.javascript.rhino
A builder for JSDocInfo objects.
JSDocInfoBuilder(boolean) - +Constructor for class com.google.javascript.rhino.JSDocInfoBuilder +
  +
JsDocInfoParser - Class in com.google.javascript.jscomp.parsing
A parser for JSDoc comments.
JSError - Class in com.google.javascript.jscomp
Compile error description
JsFileLineParser - Class in com.google.javascript.jscomp.deps
Base class for classes that parse Javascript sources on a line-by-line basis.
JsFileLineParser(ErrorManager) - +Constructor for class com.google.javascript.jscomp.deps.JsFileLineParser +
Constructor. +
JsFileParser - Class in com.google.javascript.jscomp.deps
A parser that can extract goog.require() and goog.provide() dependency + information from a .js file.
JsFileParser(ErrorManager) - +Constructor for class com.google.javascript.jscomp.deps.JsFileParser +
Constructor +
JsFunctionParser - Class in com.google.javascript.jscomp.deps
A parser that can extract dependency information from a .js file.
JsFunctionParser(Collection<String>, ErrorManager) - +Constructor for class com.google.javascript.jscomp.deps.JsFunctionParser +
Constructor +
JsFunctionParser.SymbolInfo - Class in com.google.javascript.jscomp.deps
 
JsMessage - Class in com.google.javascript.jscomp
A representation of a translatable message in JavaScript source code.
JsMessage.Builder - Class in com.google.javascript.jscomp
Contains functionality for creating js messages.
JsMessage.Builder() - +Constructor for class com.google.javascript.jscomp.JsMessage.Builder +
  +
JsMessage.Builder(String) - +Constructor for class com.google.javascript.jscomp.JsMessage.Builder +
Creates an instance. +
JsMessage.IdGenerator - Interface in com.google.javascript.jscomp
 
JsMessage.PlaceholderReference - Class in com.google.javascript.jscomp
A reference to a placeholder in a translatable message.
JsMessage.Style - Enum in com.google.javascript.jscomp
Message style that could be used for JS code parsing.
JsMessageExtractor - Class in com.google.javascript.jscomp
Extracts messages and message comments from JS code.
JsMessageExtractor(JsMessage.IdGenerator, JsMessage.Style) - +Constructor for class com.google.javascript.jscomp.JsMessageExtractor +
  +
JSModule - Class in com.google.javascript.jscomp
A JavaScript module has a unique name, consists of a list of compiler inputs, + and can depend on other modules.
JSModule(String) - +Constructor for class com.google.javascript.jscomp.JSModule +
Creates an instance. +
JSModuleGraph - Class in com.google.javascript.jscomp
A JSModule dependency graph that assigns a depth to each module and + can answer depth-related queries about them.
JSModuleGraph(JSModule[]) - +Constructor for class com.google.javascript.jscomp.JSModuleGraph +
Creates a module graph from a list of modules in dependency order. +
JSModuleGraph(List<JSModule>) - +Constructor for class com.google.javascript.jscomp.JSModuleGraph +
Creates a module graph from a list of modules in dependency order. +
JSModuleGraph.ModuleDependenceException - Exception in com.google.javascript.jscomp
 
JSModuleGraph.ModuleDependenceException(String, JSModule, JSModule) - +Constructor for exception com.google.javascript.jscomp.JSModuleGraph.ModuleDependenceException +
  +
JsonML - Class in com.google.javascript.jscomp.jsonml
Class which represents JsonML element according to the specification at + "http://code.google.com/p/es-lab/wiki/JsonMLASTFormat"
JsonML(TagType) - +Constructor for class com.google.javascript.jscomp.jsonml.JsonML +
Creates a new element with a given type. +
JsonML(TagType, JsonML...) - +Constructor for class com.google.javascript.jscomp.jsonml.JsonML +
Creates a new element. +
JsonML(TagType, List<? extends JsonML>) - +Constructor for class com.google.javascript.jscomp.jsonml.JsonML +
  +
JsonML(TagType, Map<? extends TagAttr, ?>) - +Constructor for class com.google.javascript.jscomp.jsonml.JsonML +
  +
JsonML(TagType, Map<? extends TagAttr, ?>, List<? extends JsonML>) - +Constructor for class com.google.javascript.jscomp.jsonml.JsonML +
  +
JsonMLAst - Class in com.google.javascript.jscomp.jsonml
Generates an AST from a JsonML source file.
JsonMLAst(JsonML) - +Constructor for class com.google.javascript.jscomp.jsonml.JsonMLAst +
  +
JsonMLError - Class in com.google.javascript.jscomp.jsonml
Class used to represent errors which correspond to JsonML elements.
JsonMLUtil - Class in com.google.javascript.jscomp.jsonml
JsonMLUtil contains utilities for the JsonML object.
JsonMLUtil() - +Constructor for class com.google.javascript.jscomp.jsonml.JsonMLUtil +
  +
JSSourceFile - Class in com.google.javascript.jscomp
An abstract representation of a JavaScript source file, as input to + JSCompiler.
JSType - Class in com.google.javascript.rhino.jstype
Represents JavaScript value types.
JSType.TypePair - Class in com.google.javascript.rhino.jstype
 
JSType.TypePair(JSType, JSType) - +Constructor for class com.google.javascript.rhino.jstype.JSType.TypePair +
  +
JSTypeExpression - Class in com.google.javascript.rhino
Represents a type expression as a miniture Rhino AST, so that the + type expression can be evaluated later.
JSTypeExpression(Node, String) - +Constructor for class com.google.javascript.rhino.JSTypeExpression +
  +
JSTypeNative - Enum in com.google.javascript.rhino.jstype
Constants corresponding to types that are built into a JavaScript engine + and other types that occur very often in the type system.
JSTypeRegistry - Class in com.google.javascript.rhino.jstype
The type registry is used to resolve named types.
JSTypeRegistry(ErrorReporter) - +Constructor for class com.google.javascript.rhino.jstype.JSTypeRegistry +
Constructs a new type registry populated with the built-in types. +
JSTypeRegistry(ErrorReporter, boolean) - +Constructor for class com.google.javascript.rhino.jstype.JSTypeRegistry +
Constructs a new type registry populated with the built-in types. +
JSTypeRegistry.ResolveMode - Enum in com.google.javascript.rhino.jstype
The type registry has three modes, which control how type ASTs are + converted to types in JSTypeRegistry.createFromTypeNodes(com.google.javascript.rhino.Node, java.lang.String, com.google.javascript.rhino.jstype.StaticScope).
+
+

+K

+
+
key - +Variable in class com.google.javascript.jscomp.DiagnosticType +
The error type. +
+
+

+L

+
+
label(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
LABEL - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
LABEL_NAME - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
labelName(String) - +Static method in class com.google.javascript.rhino.IR +
  +
labelRenaming - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Controls label renaming. +
languageMode() - +Method in class com.google.javascript.jscomp.Compiler +
  +
LAST_PROP - +Static variable in class com.google.javascript.rhino.Node +
  +
LB - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
LC - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
LE - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
LEAST_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
length() - +Method in class com.google.javascript.jscomp.JsMessage.PlaceholderReference +
  +
LENGTH - +Static variable in class com.google.javascript.rhino.Node +
  +
level(JSError) - +Method in class com.google.javascript.jscomp.ComposeWarningsGuard +
  +
level(JSError) - +Method in class com.google.javascript.jscomp.DiagnosticGroupWarningsGuard +
  +
level - +Variable in class com.google.javascript.jscomp.DiagnosticType +
Reporting level, initially the defaultLevel but may be changed. +
level - +Variable in class com.google.javascript.jscomp.JSError +
Deprecated. Use #getDefaultLevel +
level - +Variable in class com.google.javascript.jscomp.jsonml.JsonMLError +
Level +
level(JSError) - +Method in class com.google.javascript.jscomp.ShowByPathWarningsGuard +
  +
level(JSError) - +Method in class com.google.javascript.jscomp.StrictWarningsGuard +
  +
level(JSError) - +Method in class com.google.javascript.jscomp.WarningsGuard +
Returns a new check level for a given error. +
level(JSError) - +Method in class com.google.javascript.jscomp.WhitelistWarningsGuard +
  +
LightweightMessageFormatter - Class in com.google.javascript.jscomp
Lightweight message formatter.
LightweightMessageFormatter(SourceExcerptProvider) - +Constructor for class com.google.javascript.jscomp.LightweightMessageFormatter +
  +
LightweightMessageFormatter(SourceExcerptProvider, SourceExcerptProvider.SourceExcerpt) - +Constructor for class com.google.javascript.jscomp.LightweightMessageFormatter +
  +
LINE_NUMBER_FIELD_NUMBER - +Static variable in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
LINE_NUMBER_FIELD_NUMBER - +Static variable in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
LINE_NUMBER_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
lineBreak - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Line break the output a bit more aggressively +
lineNumber - +Variable in class com.google.javascript.jscomp.JSError +
Line number of the source +
lineNumber - +Variable in class com.google.javascript.jscomp.jsonml.JsonMLError +
Line number of the source +
LinkedDirectedGraph<N,E> - Class in com.google.javascript.jscomp.graph
A directed graph using linked list within nodes to store edge information.
LinkedDirectedGraph(boolean, boolean) - +Constructor for class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
LinkedUndirectedGraph<N,E> - Class in com.google.javascript.jscomp.graph
An undirected graph using linked list within nodes to store edge + information.
LinkedUndirectedGraph(boolean, boolean) - +Constructor for class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
load(String) - +Static method in class com.google.javascript.jscomp.VariableMap +
Reads the variable map from a file written via VariableMap.save(String). +
loadWhitelistedJsWarnings(File) - +Static method in class com.google.javascript.jscomp.WhitelistWarningsGuard +
Loads legacy warnings list from the file. +
loadWhitelistedJsWarnings(InputSupplier<InputStreamReader>) - +Static method in class com.google.javascript.jscomp.WhitelistWarningsGuard +
Loads legacy warnings list from the file. +
locale - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Compiling locale +
logAliasTransformation(String, SourcePosition<CompilerOptions.AliasTransformation>) - +Method in interface com.google.javascript.jscomp.CompilerOptions.AliasTransformationHandler +
Builds an AliasTransformation implementation and returns it to the + caller. +
LoggerErrorManager - Class in com.google.javascript.jscomp
An error manager that logs errors and warnings using a logger in addition to + collecting them in memory.
LoggerErrorManager(MessageFormatter, Logger) - +Constructor for class com.google.javascript.jscomp.LoggerErrorManager +
Creates an instance. +
LoggerErrorManager(Logger) - +Constructor for class com.google.javascript.jscomp.LoggerErrorManager +
Creates an instance with a source-less error formatter. +
lookupNewName(String) - +Method in class com.google.javascript.jscomp.VariableMap +
Given an original variable name, look up new name, may return null + if it's not found. +
lookupSourceName(String) - +Method in class com.google.javascript.jscomp.VariableMap +
Given a new variable name, lookup the source name, may return null + if it's not found. +
LP - +Static variable in class com.google.javascript.rhino.Token +
  +
LSH - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
LT - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
+
+

+M

+
+
main(String[]) - +Static method in class com.google.javascript.jscomp.CommandLineRunner +
Runs the Compiler. +
MAIN_FUNCTION_NAME - +Static variable in class com.google.javascript.jscomp.CallGraph +
The name we give the main function. +
make(String, CheckLevel, String) - +Static method in class com.google.javascript.jscomp.DiagnosticType +
Create a DiagnosticType at a given CheckLevel. +
make(DiagnosticType, String...) - +Static method in class com.google.javascript.jscomp.JSError +
Creates a JSError with no source information +
make(String, int, int, DiagnosticType, String...) - +Static method in class com.google.javascript.jscomp.JSError +
Creates a JSError at a given source location +
make(String, int, int, CheckLevel, DiagnosticType, String...) - +Static method in class com.google.javascript.jscomp.JSError +
Creates a JSError at a given source location +
make(String, Node, DiagnosticType, String...) - +Static method in class com.google.javascript.jscomp.JSError +
Creates a JSError from a file and Node position. +
make(String, Node, CheckLevel, DiagnosticType, String...) - +Static method in class com.google.javascript.jscomp.JSError +
Creates a JSError from a file and Node position. +
make(DiagnosticType, String, JsonML, int, ErrorLevel, String...) - +Static method in class com.google.javascript.jscomp.jsonml.JsonMLError +
  +
make(JSError, JsonMLAst) - +Static method in class com.google.javascript.jscomp.jsonml.JsonMLError +
  +
makeAbsolute(String) - +Static method in class com.google.javascript.jscomp.deps.PathUtil +
Converts the given path into an absolute one. +
makeAbsolute(String, String) - +Static method in class com.google.javascript.jscomp.deps.PathUtil +
Converts the given path into an absolute one. +
makeError(Node, CheckLevel, DiagnosticType, String...) - +Method in class com.google.javascript.jscomp.NodeTraversal +
Creates a JSError during NodeTraversal. +
makeError(Node, DiagnosticType, String...) - +Method in class com.google.javascript.jscomp.NodeTraversal +
Creates a JSError during NodeTraversal. +
makeOptionalArg(JSTypeExpression) - +Static method in class com.google.javascript.rhino.JSTypeExpression +
Make the given type expression into an optional type expression, + if possible. +
makeRelative(String, String) - +Static method in class com.google.javascript.jscomp.deps.PathUtil +
Returns targetPath relative to basePath. +
manageDependencies(List<String>, List<CompilerInput>) - +Method in class com.google.javascript.jscomp.JSModuleGraph +
Applies a DependencyOptions in "dependency sorting" and "dependency pruning" + mode to the given list of inputs. +
manageDependencies(DependencyOptions, List<CompilerInput>) - +Method in class com.google.javascript.jscomp.JSModuleGraph +
Apply the dependency options to the list of sources, returning a new + source list re-ordering and dropping files as necessary. +
MapBasedScope - Class in com.google.javascript.rhino.testing
A scope based on a simple hashmap.
MapBasedScope(Map<String, ? extends JSType>) - +Constructor for class com.google.javascript.rhino.testing.MapBasedScope +
  +
Mapping - Class in com.google.debugging.sourcemap.proto
 
Mapping.LineMapping - Class in com.google.debugging.sourcemap.proto
 
Mapping.LineMapping.Builder - Class in com.google.debugging.sourcemap.proto
 
Mapping.LineMappingOrBuilder - Interface in com.google.debugging.sourcemap.proto
 
Mapping.OriginalMapping - Class in com.google.debugging.sourcemap.proto
 
Mapping.OriginalMapping.Builder - Class in com.google.debugging.sourcemap.proto
 
Mapping.OriginalMappingOrBuilder - Interface in com.google.debugging.sourcemap.proto
 
markAnnotation(String, int, int) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Adds a marker to the current JSDocInfo and populates the marker with the + annotation information. +
markAsCompiled - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Sets the special "COMPILED" value to true +
markName(String, int, int) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Deprecated. Use #markName(String, StaticSourceFile, int, int) +
markName(String, StaticSourceFile, int, int) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Adds a name declaration to the current marker. +
markNoSideEffectCalls - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Mark no side effect calls +
markText(String, int, int, int, int) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Adds a textual block to the current marker. +
markTypeNode(Node, int, int, int, int, boolean) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Adds a type declaration to the current marker. +
matchConstraint(ObjectType) - +Method in class com.google.javascript.rhino.jstype.JSType +
Modify this type so that it matches the specified type. +
matchConstraint(ObjectType) - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
matches(JSError) - +Method in class com.google.javascript.jscomp.DiagnosticGroup +
Returns whether the given error's type matches a type + in this group. +
matches(DiagnosticType) - +Method in class com.google.javascript.jscomp.DiagnosticGroup +
Returns whether the given type matches a type in this group. +
matchesInt32Context() - +Method in class com.google.javascript.rhino.jstype.JSType +
This predicate is used to test whether a given type can appear in a + 'Int32' context. +
matchesNumberContext() - +Method in class com.google.javascript.rhino.jstype.BooleanType +
  +
matchesNumberContext() - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
matchesNumberContext() - +Method in class com.google.javascript.rhino.jstype.EnumType +
  +
matchesNumberContext() - +Method in class com.google.javascript.rhino.jstype.JSType +
This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator. +
matchesNumberContext() - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
matchesNumberContext() - +Method in class com.google.javascript.rhino.jstype.NoType +
  +
matchesNumberContext() - +Method in class com.google.javascript.rhino.jstype.NullType +
  +
matchesNumberContext() - +Method in class com.google.javascript.rhino.jstype.NumberType +
  +
matchesNumberContext() - +Method in class com.google.javascript.rhino.jstype.StringType +
  +
matchesNumberContext() - +Method in class com.google.javascript.rhino.jstype.UnionType +
This predicate is used to test whether a given type can appear in a + numeric context, such as an operand of a multiply operator. +
matchesNumberContext() - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
matchesNumberContext() - +Method in class com.google.javascript.rhino.jstype.VoidType +
  +
matchesObjectContext() - +Method in class com.google.javascript.rhino.jstype.AllType +
  +
matchesObjectContext() - +Method in class com.google.javascript.rhino.jstype.BooleanType +
  +
matchesObjectContext() - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
matchesObjectContext() - +Method in class com.google.javascript.rhino.jstype.EnumType +
  +
matchesObjectContext() - +Method in class com.google.javascript.rhino.jstype.JSType +
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with statement. +
matchesObjectContext() - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
matchesObjectContext() - +Method in class com.google.javascript.rhino.jstype.NoType +
  +
matchesObjectContext() - +Method in class com.google.javascript.rhino.jstype.NullType +
  +
matchesObjectContext() - +Method in class com.google.javascript.rhino.jstype.NumberType +
  +
matchesObjectContext() - +Method in class com.google.javascript.rhino.jstype.StringType +
  +
matchesObjectContext() - +Method in class com.google.javascript.rhino.jstype.UnionType +
This predicate is used to test whether a given type can appear in an + Object context, such as the expression in a with + statement. +
matchesObjectContext() - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
matchesObjectContext() - +Method in class com.google.javascript.rhino.jstype.VoidType +
  +
matchesStringContext() - +Method in class com.google.javascript.rhino.jstype.AllType +
  +
matchesStringContext() - +Method in class com.google.javascript.rhino.jstype.BooleanType +
  +
matchesStringContext() - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
matchesStringContext() - +Method in class com.google.javascript.rhino.jstype.EnumType +
  +
matchesStringContext() - +Method in class com.google.javascript.rhino.jstype.JSType +
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) operator. +
matchesStringContext() - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
matchesStringContext() - +Method in class com.google.javascript.rhino.jstype.NoType +
  +
matchesStringContext() - +Method in class com.google.javascript.rhino.jstype.NullType +
  +
matchesStringContext() - +Method in class com.google.javascript.rhino.jstype.NumberType +
  +
matchesStringContext() - +Method in class com.google.javascript.rhino.jstype.StringType +
  +
matchesStringContext() - +Method in class com.google.javascript.rhino.jstype.UnionType +
This predicate is used to test whether a given type can appear in a + String context, such as an operand of a string concat (+) + operator. +
matchesStringContext() - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
matchesStringContext() - +Method in class com.google.javascript.rhino.jstype.VoidType +
  +
matchesUint32Context() - +Method in class com.google.javascript.rhino.jstype.JSType +
This predicate is used to test whether a given type can appear in a + 'Uint32' context. +
matchesWholeInput(RegExpTree, String) - +Static method in class com.google.javascript.jscomp.regex.RegExpTree +
True if, but not necessarily always when the, given regular expression + must match the whole input or none of it. +
MATH_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
MAX_COLUMN_NUMBER - +Static variable in class com.google.javascript.rhino.Node +
MAX_COLUMN_NUMBER represents the maximum column number that can + be represented. +
mergeFrom(Message) - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
mergeFrom(Mapping.LineMapping) - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
mergeFrom(CodedInputStream, ExtensionRegistryLite) - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
mergeFrom(Message) - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
mergeFrom(Mapping.OriginalMapping) - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
mergeFrom(CodedInputStream, ExtensionRegistryLite) - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
mergeFrom(Message) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
mergeFrom(FunctionInformationMap) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
mergeFrom(CodedInputStream, ExtensionRegistryLite) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
mergeFrom(Message) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
mergeFrom(FunctionInformationMap.Entry) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
mergeFrom(CodedInputStream, ExtensionRegistryLite) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
mergeFrom(Message) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
mergeFrom(FunctionInformationMap.Module) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
mergeFrom(CodedInputStream, ExtensionRegistryLite) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
mergeFrom(Message) - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
mergeFrom(Instrumentation) - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
mergeFrom(CodedInputStream, ExtensionRegistryLite) - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
mergeLineCharNo(int, int) - +Static method in class com.google.javascript.rhino.Node +
Merges the line number and character number in one integer. +
mergeMapSection(int, int, String) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV3 +
  +
mergeOriginalMapping(Mapping.OriginalMapping) - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
messageBundle - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Returns localized replacement for MSG_* variables +
MessageBundle - Interface in com.google.javascript.jscomp
An interface for providing alterative values for user-visible messages in + javascript code.
MessageFormatter - Interface in com.google.javascript.jscomp
Format warnings and errors.
MISSING_ARGUMENT - +Static variable in class com.google.javascript.jscomp.jsonml.Validator +
  +
MISSING_PROPERTIES - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
MOD - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
MODULE_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.FunctionInformationMap +
  +
MODULE_NAME_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
MOTION_ITERATIONS_ERROR - +Static variable in class com.google.javascript.jscomp.Compiler +
  +
moveFunctionDeclarations - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Move top level function declarations to the top +
MUL - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
+
+

+N

+
+
name(String) - +Static method in class com.google.javascript.rhino.IR +
  +
NAME - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
name(int) - +Static method in class com.google.javascript.rhino.Token +
  +
NAME_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
NAME_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
namedAnonFunctionMap - +Variable in class com.google.javascript.jscomp.Result +
  +
NaN - +Static variable in class com.google.javascript.rhino.ScriptRuntime +
  +
NATIVE_PROPERTIES_COUNT - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
NE - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
neg(Node) - +Static method in class com.google.javascript.rhino.IR +
  +
NEG - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
negativeZero - +Static variable in class com.google.javascript.rhino.ScriptRuntime +
  +
NEW - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
newBuilder() - +Static method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
newBuilder(Mapping.LineMapping) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
newBuilder() - +Static method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
newBuilder(Mapping.OriginalMapping) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
newBuilder() - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
newBuilder(FunctionInformationMap.Entry) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
newBuilder() - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
newBuilder(FunctionInformationMap.Module) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
newBuilder() - +Static method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
newBuilder(FunctionInformationMap) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
newBuilder() - +Static method in class com.google.javascript.jscomp.Instrumentation +
  +
newBuilder(Instrumentation) - +Static method in class com.google.javascript.jscomp.Instrumentation +
  +
newBuilderForType() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
newBuilderForType(GeneratedMessage.BuilderParent) - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
newBuilderForType() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
newBuilderForType(GeneratedMessage.BuilderParent) - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
newBuilderForType() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
newBuilderForType(GeneratedMessage.BuilderParent) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
newBuilderForType() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
newBuilderForType(GeneratedMessage.BuilderParent) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
newBuilderForType() - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
newBuilderForType(GeneratedMessage.BuilderParent) - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
newBuilderForType() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
newBuilderForType(GeneratedMessage.BuilderParent) - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
newCompilerOptions() - +Method in class com.google.javascript.jscomp.Compiler +
Allow subclasses to override the default CompileOptions object. +
newExpr(Node) - +Static method in class com.google.javascript.jscomp.NodeUtil +
Creates an EXPR_RESULT. +
newExternInput(String) - +Method in class com.google.javascript.jscomp.Compiler +
  +
newNode(Node, Node...) - +Static method in class com.google.javascript.rhino.IR +
  +
newNumber(double) - +Static method in class com.google.javascript.rhino.Node +
  +
newNumber(double, int, int) - +Static method in class com.google.javascript.rhino.Node +
  +
newOptionalParameterFromNode(Node) - +Method in class com.google.javascript.rhino.jstype.FunctionParamBuilder +
Copies the parameter specification from the given node, + but makes sure it's optional. +
newParameterFromNode(Node) - +Method in class com.google.javascript.rhino.jstype.FunctionParamBuilder +
Copies the parameter specification from the given node. +
newQualifiedNameNode(CodingConvention, String) - +Static method in class com.google.javascript.jscomp.NodeUtil +
Creates a node representing a qualified name. +
newString(String) - +Static method in class com.google.javascript.rhino.Node +
  +
newString(int, String) - +Static method in class com.google.javascript.rhino.Node +
  +
newString(String, int, int) - +Static method in class com.google.javascript.rhino.Node +
  +
newString(int, String, int, int) - +Static method in class com.google.javascript.rhino.Node +
  +
newSubGraph() - +Method in interface com.google.javascript.jscomp.graph.AdjacencyGraph +
Returns an empty SubGraph for this Graph. +
newSubGraph() - +Method in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
newSubGraph() - +Method in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
newTraversal(FixedPointGraphTraversal.EdgeCallback<NODE, EDGE>) - +Static method in class com.google.javascript.jscomp.graph.FixedPointGraphTraversal +
Helper method for creating new traversals. +
nextConvention - +Variable in class com.google.javascript.jscomp.CodingConventions.Proxy +
  +
NO_OBJECT_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
NO_RESOLVED_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
NO_SIDE_EFFECTS - +Static variable in class com.google.javascript.rhino.Node +
  +
NO_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
Node - Class in com.google.javascript.rhino
This class implements the root of the intermediate representation.
Node(int) - +Constructor for class com.google.javascript.rhino.Node +
  +
Node(int, Node) - +Constructor for class com.google.javascript.rhino.Node +
  +
Node(int, Node, Node) - +Constructor for class com.google.javascript.rhino.Node +
  +
Node(int, Node, Node, Node) - +Constructor for class com.google.javascript.rhino.Node +
  +
Node(int, Node, Node, Node, Node) - +Constructor for class com.google.javascript.rhino.Node +
  +
Node(int, int, int) - +Constructor for class com.google.javascript.rhino.Node +
  +
Node(int, Node, int, int) - +Constructor for class com.google.javascript.rhino.Node +
  +
Node(int, Node, Node, int, int) - +Constructor for class com.google.javascript.rhino.Node +
  +
Node(int, Node, Node, Node, int, int) - +Constructor for class com.google.javascript.rhino.Node +
  +
Node(int, Node, Node, Node, Node, int, int) - +Constructor for class com.google.javascript.rhino.Node +
  +
Node(int, Node[], int, int) - +Constructor for class com.google.javascript.rhino.Node +
  +
Node(int, Node[]) - +Constructor for class com.google.javascript.rhino.Node +
  +
Node.AncestorIterable - Class in com.google.javascript.rhino
Iterator to go up the ancestor tree.
Node.FileLevelJsDocBuilder - Class in com.google.javascript.rhino
An inner class that provides back-door access to the license + property of the JSDocInfo property for this node.
Node.FileLevelJsDocBuilder() - +Constructor for class com.google.javascript.rhino.Node.FileLevelJsDocBuilder +
  +
Node.SideEffectFlags - Class in com.google.javascript.rhino
A helper class for getting and setting the side-effect flags.
Node.SideEffectFlags() - +Constructor for class com.google.javascript.rhino.Node.SideEffectFlags +
  +
Node.SideEffectFlags(int) - +Constructor for class com.google.javascript.rhino.Node.SideEffectFlags +
  +
NODE_TRAVERSAL_ERROR - +Static variable in class com.google.javascript.jscomp.NodeTraversal +
  +
nodes - +Variable in class com.google.javascript.jscomp.graph.LinkedDirectedGraph +
  +
nodes - +Variable in class com.google.javascript.jscomp.graph.LinkedUndirectedGraph +
  +
NodeTraversal - Class in com.google.javascript.jscomp
Nodetraversal allows an iteration through the nodes in the parse tree, + and facilitates the optimizations on the parse tree.
NodeTraversal(AbstractCompiler, NodeTraversal.Callback) - +Constructor for class com.google.javascript.jscomp.NodeTraversal +
Creates a node traversal using the specified callback interface. +
NodeTraversal(AbstractCompiler, NodeTraversal.Callback, ScopeCreator) - +Constructor for class com.google.javascript.jscomp.NodeTraversal +
Creates a node traversal using the specified callback interface + and the scope creator. +
NodeTraversal.AbstractNodeTypePruningCallback - Class in com.google.javascript.jscomp
Abstract callback to visit a pruned set of nodes.
NodeTraversal.AbstractNodeTypePruningCallback(Set<Integer>) - +Constructor for class com.google.javascript.jscomp.NodeTraversal.AbstractNodeTypePruningCallback +
Creates an abstract pruned callback. +
NodeTraversal.AbstractNodeTypePruningCallback(Set<Integer>, boolean) - +Constructor for class com.google.javascript.jscomp.NodeTraversal.AbstractNodeTypePruningCallback +
Creates an abstract pruned callback. +
NodeTraversal.AbstractPostOrderCallback - Class in com.google.javascript.jscomp
Abstract callback to visit all nodes in post order.
NodeTraversal.AbstractPostOrderCallback() - +Constructor for class com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback +
  +
NodeTraversal.AbstractScopedCallback - Class in com.google.javascript.jscomp
Abstract scoped callback to visit all nodes in post order.
NodeTraversal.AbstractScopedCallback() - +Constructor for class com.google.javascript.jscomp.NodeTraversal.AbstractScopedCallback +
  +
NodeTraversal.AbstractShallowCallback - Class in com.google.javascript.jscomp
Abstract callback to visit all nodes but not traverse into function + bodies.
NodeTraversal.AbstractShallowCallback() - +Constructor for class com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback +
  +
NodeTraversal.AbstractShallowStatementCallback - Class in com.google.javascript.jscomp
Abstract callback to visit all structure and statement nodes but doesn't + traverse into functions or expressions.
NodeTraversal.AbstractShallowStatementCallback() - +Constructor for class com.google.javascript.jscomp.NodeTraversal.AbstractShallowStatementCallback +
  +
NodeTraversal.Callback - Interface in com.google.javascript.jscomp
Callback
NodeTraversal.ScopedCallback - Interface in com.google.javascript.jscomp
Callback that also knows about scope changes
NodeUtil - Class in com.google.javascript.jscomp
NodeUtil contains utilities that get properties from the Node object.
NON_HALTING_ERROR_MSG - +Static variable in class com.google.javascript.jscomp.graph.FixedPointGraphTraversal +
  +
NON_STANDARD_JSDOC - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
NoObjectType - Class in com.google.javascript.rhino.jstype
The bottom Object type, representing the subclass of all objects.
normalize() - +Method in class com.google.javascript.jscomp.Compiler +
  +
not(Node) - +Static method in class com.google.javascript.rhino.IR +
  +
not() - +Method in enum com.google.javascript.rhino.jstype.TernaryValue +
Gets the not of this. +
NOT - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
NOT_A_CLASS - +Static variable in class com.google.javascript.rhino.jstype.JSType +
  +
NOT_A_CONSTRUCTOR - +Static variable in class com.google.javascript.jscomp.TypeCheck +
  +
NOT_A_TYPE - +Static variable in class com.google.javascript.rhino.jstype.JSType +
  +
NOT_ENOUGH_CHILDREN_FMT - +Static variable in class com.google.javascript.jscomp.jsonml.Validator +
  +
NOT_ENUMDECL - +Static variable in class com.google.javascript.rhino.jstype.JSType +
  +
NoType - Class in com.google.javascript.rhino.jstype
Bottom type, representing the subclass of any value or object.
NULL - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
NULL_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
NullErrorReporter - Class in com.google.javascript.jscomp.parsing
An error reporter which consumes all calls and performs no actions.
nullNode() - +Static method in class com.google.javascript.rhino.IR +
  +
NullType - Class in com.google.javascript.rhino.jstype
Null type.
number(double) - +Static method in class com.google.javascript.rhino.IR +
  +
NUMBER - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
NUMBER_OBJECT_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
NUMBER_OBJECT_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
NUMBER_STRING_BOOLEAN - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
NUMBER_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
NumberType - Class in com.google.javascript.rhino.jstype
Number type.
numCapturingGroups() - +Method in class com.google.javascript.jscomp.regex.RegExpTree +
The number of capturing groups. +
+
+

+O

+
+
OBJECT_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
OBJECT_NUMBER_STRING - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
OBJECT_NUMBER_STRING_BOOLEAN - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
OBJECT_PROTOTYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
OBJECT_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
objectlit(Node...) - +Static method in class com.google.javascript.rhino.IR +
  +
OBJECTLIT - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
ObjectPropertyStringPreprocess - Class in com.google.javascript.jscomp
Rewrites new goog.testing.ObjectPropertyString(foo, 'bar') to + new JSCompiler_ObjectPropertyString(window, foo.bar).
ObjectType - Class in com.google.javascript.rhino.jstype
Object type.
ObjectType.Property - Class in com.google.javascript.rhino.jstype
 
OPT_ARG_NAME - +Static variable in class com.google.javascript.rhino.Node +
  +
optimize() - +Method in class com.google.javascript.jscomp.Compiler +
  +
OPTIMIZE_LOOP_ERROR - +Static variable in class com.google.javascript.jscomp.Compiler +
Error strings used for reporting JSErrors +
optimizeArgumentsArray - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Provide formal names for elements of arguments array. +
optimizeCalls - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Remove unused parameters from call sites. +
optimizeParameters - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Remove unused and constant parameters. +
optimizeReturns - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Remove unused return values. +
or(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
or(TernaryValue) - +Method in enum com.google.javascript.rhino.jstype.TernaryValue +
Gets the or of this and that. +
OR - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
ORIGINAL_FILE_FIELD_NUMBER - +Static variable in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
ORIGINAL_MAPPING_FIELD_NUMBER - +Static variable in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
ORIGINALNAME_PROP - +Static variable in class com.google.javascript.rhino.Node +
  +
OVERRIDING_PROTOTYPE_WITH_NON_OBJECT - +Static variable in class com.google.javascript.jscomp.TypeCheck +
  +
overwriteDeclaredType(String, JSType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Overrides a declared global type name. +
+
+

+P

+
+
PARAM_LIST - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
paramList() - +Static method in class com.google.javascript.rhino.IR +
  +
paramList(Node) - +Static method in class com.google.javascript.rhino.IR +
  +
paramList(Node...) - +Static method in class com.google.javascript.rhino.IR +
  +
paramList(List<Node>) - +Static method in class com.google.javascript.rhino.IR +
  +
PARENTHESIZED_PROP - +Static variable in class com.google.javascript.rhino.Node +
  +
parse(String) - +Method in interface com.google.debugging.sourcemap.SourceMapConsumer +
Parses the given contents containing a source map to provide initialize + a class providing SourceMapping. +
parse(String) - +Static method in class com.google.debugging.sourcemap.SourceMapConsumerFactory +
  +
parse(String, SourceMapSupplier) - +Static method in class com.google.debugging.sourcemap.SourceMapConsumerFactory +
  +
parse(String) - +Method in class com.google.debugging.sourcemap.SourceMapConsumerV1 +
Parses the given contents containing a source map. +
parse(String) - +Method in class com.google.debugging.sourcemap.SourceMapConsumerV2 +
Parses the given contents containing a source map. +
parse(JSONObject) - +Method in class com.google.debugging.sourcemap.SourceMapConsumerV2 +
Parses the given contents containing a source map. +
parse(String) - +Method in class com.google.debugging.sourcemap.SourceMapConsumerV3 +
Parses the given contents containing a source map. +
parse(String, SourceMapSupplier) - +Method in class com.google.debugging.sourcemap.SourceMapConsumerV3 +
Parses the given contents containing a source map. +
parse(JSONObject) - +Method in class com.google.debugging.sourcemap.SourceMapConsumerV3 +
Parses the given contents containing a source map. +
parse(JSONObject, SourceMapSupplier) - +Method in class com.google.debugging.sourcemap.SourceMapConsumerV3 +
Parses the given contents containing a source map. +
parse() - +Method in class com.google.javascript.jscomp.Compiler +
  +
parse(SourceFile) - +Method in class com.google.javascript.jscomp.Compiler +
  +
parse(AbstractCompiler) - +Method in class com.google.javascript.jscomp.jsonml.Reader +
Generates AST for a specified JsonML source file. +
parse(StaticSourceFile, String, Config, ErrorReporter, Logger) - +Static method in class com.google.javascript.jscomp.parsing.ParserRunner +
Parses the JavaScript text given by a reader. +
parseDelimitedFrom(InputStream) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
parseDelimitedFrom(InputStream, ExtensionRegistryLite) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
parseDelimitedFrom(InputStream) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
parseDelimitedFrom(InputStream, ExtensionRegistryLite) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
parseDelimitedFrom(InputStream) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
parseDelimitedFrom(InputStream, ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
parseDelimitedFrom(InputStream) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
parseDelimitedFrom(InputStream, ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
parseDelimitedFrom(InputStream) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
parseDelimitedFrom(InputStream, ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
parseDelimitedFrom(InputStream) - +Static method in class com.google.javascript.jscomp.Instrumentation +
  +
parseDelimitedFrom(InputStream, ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.Instrumentation +
  +
parseFile(String) - +Method in class com.google.javascript.jscomp.deps.DepsFileParser +
Parses the given file and returns a list of dependency information that it + contained. +
parseFile(String, String) - +Method in class com.google.javascript.jscomp.deps.DepsFileParser +
Parses the given file and returns a list of dependency information that it + contained. +
parseFile(String, String) - +Method in class com.google.javascript.jscomp.deps.JsFileParser +
Parses the given file and returns the dependency information that it + contained. +
parseFile(String, String, String) - +Method in class com.google.javascript.jscomp.deps.JsFileParser +
Parses the given file and returns the dependency information that it + contained. +
parseFile(String, String) - +Method in class com.google.javascript.jscomp.deps.JsFunctionParser +
Parses the given file and returns the dependency information that it + contained. +
parseFileReader(String, Reader) - +Method in class com.google.javascript.jscomp.deps.DepsFileParser +
Parses the file from the given reader and returns a list of + dependency information that it contained. +
parseFrom(ByteString) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
parseFrom(ByteString, ExtensionRegistryLite) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
parseFrom(byte[]) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
parseFrom(byte[], ExtensionRegistryLite) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
parseFrom(InputStream) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
parseFrom(InputStream, ExtensionRegistryLite) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
parseFrom(CodedInputStream) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
parseFrom(CodedInputStream, ExtensionRegistryLite) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
parseFrom(ByteString) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
parseFrom(ByteString, ExtensionRegistryLite) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
parseFrom(byte[]) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
parseFrom(byte[], ExtensionRegistryLite) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
parseFrom(InputStream) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
parseFrom(InputStream, ExtensionRegistryLite) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
parseFrom(CodedInputStream) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
parseFrom(CodedInputStream, ExtensionRegistryLite) - +Static method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
parseFrom(ByteString) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
parseFrom(ByteString, ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
parseFrom(byte[]) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
parseFrom(byte[], ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
parseFrom(InputStream) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
parseFrom(InputStream, ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
parseFrom(CodedInputStream) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
parseFrom(CodedInputStream, ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
parseFrom(ByteString) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
parseFrom(ByteString, ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
parseFrom(byte[]) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
parseFrom(byte[], ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
parseFrom(InputStream) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
parseFrom(InputStream, ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
parseFrom(CodedInputStream) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
parseFrom(CodedInputStream, ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
parseFrom(ByteString) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
parseFrom(ByteString, ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
parseFrom(byte[]) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
parseFrom(byte[], ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
parseFrom(InputStream) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
parseFrom(InputStream, ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
parseFrom(CodedInputStream) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
parseFrom(CodedInputStream, ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
parseFrom(ByteString) - +Static method in class com.google.javascript.jscomp.Instrumentation +
  +
parseFrom(ByteString, ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.Instrumentation +
  +
parseFrom(byte[]) - +Static method in class com.google.javascript.jscomp.Instrumentation +
  +
parseFrom(byte[], ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.Instrumentation +
  +
parseFrom(InputStream) - +Static method in class com.google.javascript.jscomp.Instrumentation +
  +
parseFrom(InputStream, ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.Instrumentation +
  +
parseFrom(CodedInputStream) - +Static method in class com.google.javascript.jscomp.Instrumentation +
  +
parseFrom(CodedInputStream, ExtensionRegistryLite) - +Static method in class com.google.javascript.jscomp.Instrumentation +
  +
parseLine(String) - +Method in class com.google.javascript.jscomp.deps.DepsFileParser +
Extracts dependency information from lines that look like + goog.addDependency('pathRelativeToClosure', ['provides'], ['requires']); + Adds the dependencies to depInfos. +
parseLine(String) - +Method in class com.google.javascript.jscomp.deps.JsFileParser +
Parses a line of javascript, extracting goog.provide and goog.require + information. +
parseLine(String) - +Method in class com.google.javascript.jscomp.deps.JsFunctionParser +
Parses a line of javascript, extracting dependency information. +
parseRegExp(String, String) - +Static method in class com.google.javascript.jscomp.regex.RegExpTree +
Parses a regular expression to an AST. +
ParserRunner - Class in com.google.javascript.jscomp.parsing
 
parseString(String) - +Static method in class com.google.javascript.jscomp.jsonml.JsonMLUtil +
Parses JSON string which contains serialized JsonML content. +
parseTypeString(String) - +Static method in class com.google.javascript.jscomp.parsing.JsDocInfoParser +
Parses a string containing a JsDoc type declaration, returning the + type if the parsing succeeded or null if it failed. +
parts() - +Method in class com.google.javascript.jscomp.JsMessage +
Gets a read-only list of the parts of this message. +
pass - +Variable in class com.google.javascript.jscomp.PerformanceTracker.Stats +
  +
PassConfig - Class in com.google.javascript.jscomp
Pass factories and meta-data for native Compiler passes.
PassConfig(CompilerOptions) - +Constructor for class com.google.javascript.jscomp.PassConfig +
  +
PassFactory - Class in com.google.javascript.jscomp
A factory for creating JSCompiler passes based on the Options + injected.
PassFactory(String, boolean) - +Constructor for class com.google.javascript.jscomp.PassFactory +
  +
PathUtil - Class in com.google.javascript.jscomp.deps
Utility methods for manipulation of UNIX-like paths.
PeepholeCollectPropertyAssignments - Class in com.google.javascript.jscomp
A pass that looks for assignments to properties of an object or array + immediately following its creation using the abbreviated syntax.
PeepholeCollectPropertyAssignments() - +Constructor for class com.google.javascript.jscomp.PeepholeCollectPropertyAssignments +
  +
PerformanceTracker - Class in com.google.javascript.jscomp
 
PerformanceTracker.Stats - Class in com.google.javascript.jscomp
 
PerformanceTracker.Stats(String) - +Constructor for class com.google.javascript.jscomp.PerformanceTracker.Stats +
  +
PIPE - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
placeholders() - +Method in class com.google.javascript.jscomp.JsMessage +
Gets a read-only set of the registered placeholders in this message. +
popEdgeAnnotations() - +Method in class com.google.javascript.jscomp.graph.Graph +
Restores edges' annotation values to state before last + Graph.pushEdgeAnnotations(). +
popNodeAnnotations() - +Method in class com.google.javascript.jscomp.graph.Graph +
Restores nodes' annotation values to state before last + Graph.pushNodeAnnotations(). +
pos(Node) - +Static method in class com.google.javascript.rhino.IR +
  +
POS - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
POST_FLAG - +Static variable in class com.google.javascript.rhino.Node +
  +
preferLineBreakAtEndOfFile - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Prefer line breaks at end of file +
prettyPrint - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Output in pretty indented format +
printInputDelimiter - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Prints a separator comment before each js script +
printList(Object[]) - +Static method in class com.google.javascript.jscomp.jsonml.Validator +
  +
println(CheckLevel, JSError) - +Method in class com.google.javascript.jscomp.ant.AntErrorManager +
  +
println(CheckLevel, JSError) - +Method in class com.google.javascript.jscomp.BasicErrorManager +
Print a message with a trailing new line. +
println(CheckLevel, JSError) - +Method in class com.google.javascript.jscomp.LoggerErrorManager +
  +
println(CheckLevel, JSError) - +Method in class com.google.javascript.jscomp.PrintStreamErrorManager +
  +
PrintStreamErrorManager - Class in com.google.javascript.jscomp
An error manager that prints errors and warnings to the print stream + provided in addition to the functionality of the + BasicErrorManager.
PrintStreamErrorManager(MessageFormatter, PrintStream) - +Constructor for class com.google.javascript.jscomp.PrintStreamErrorManager +
Creates an error manager. +
PrintStreamErrorManager(PrintStream) - +Constructor for class com.google.javascript.jscomp.PrintStreamErrorManager +
Creates an instance with a source-less error formatter. +
printSummary() - +Method in class com.google.javascript.jscomp.ant.AntErrorManager +
  +
printSummary() - +Method in class com.google.javascript.jscomp.BasicErrorManager +
Print the summary of the compilation - number of errors and warnings. +
printSummary() - +Method in class com.google.javascript.jscomp.LoggerErrorManager +
  +
printSummary() - +Method in class com.google.javascript.jscomp.PrintStreamErrorManager +
  +
process(Node, Node) - +Method in class com.google.javascript.jscomp.AstValidator +
  +
process(Node, Node) - +Method in class com.google.javascript.jscomp.CallGraph +
Builds a call graph for the given externsRoot and jsRoot. +
process(Node, Node) - +Method in interface com.google.javascript.jscomp.CompilerPass +
Process the JS with root node root. +
process(Node, Node) - +Method in class com.google.javascript.jscomp.FieldCleanupPass +
  +
process(Node, Node) - +Method in class com.google.javascript.jscomp.ObjectPropertyStringPreprocess +
  +
process(Node, Node) - +Method in class com.google.javascript.jscomp.ProcessCommonJSModules +
  +
process(Node, Node) - +Method in class com.google.javascript.jscomp.TypeCheck +
Main entry point for this phase of processing. +
processAst(Node) - +Method in class com.google.javascript.jscomp.jsonml.Writer +
Creates JsonML tree based on a specified AST. +
ProcessCommonJSModules - Class in com.google.javascript.jscomp
Rewrites a Common JS module http://wiki.commonjs.org/wiki/Modules/1.1.1 + into a form that can be safely concatenated.
processDefines() - +Method in class com.google.javascript.jscomp.Compiler +
Reprocesses the current defines over the AST. +
processForTesting(Node, Node) - +Method in class com.google.javascript.jscomp.TypeCheck +
Main entry point of this phase for testing code. +
propdef(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
propertyMap - +Variable in class com.google.javascript.jscomp.Result +
  +
propertyRenaming - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Controls which properties get renamed. +
PropertyRenamingPolicy - Enum in com.google.javascript.jscomp
Policies to determine how properties should be renamed.
prune(Predicate<N>) - +Method in class com.google.javascript.jscomp.graph.GraphPruner +
  +
pushEdgeAnnotations() - +Method in class com.google.javascript.jscomp.graph.Graph +
Pushes edges' annotation values. +
pushNodeAnnotations() - +Method in class com.google.javascript.jscomp.graph.Graph +
Pushes nodes' annotation values. +
putBooleanProp(int, boolean) - +Method in class com.google.javascript.rhino.Node +
  +
putIntProp(int, int) - +Method in class com.google.javascript.rhino.Node +
  +
putProp(int, Object) - +Method in class com.google.javascript.rhino.Node +
  +
+
+

+Q

+
+
QMARK - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
QUOTED_PROP - +Static variable in class com.google.javascript.rhino.Node +
  +
+
+

+R

+
+
RANGE_ERROR_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
RANGE_ERROR_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
REACHABLE - +Static variable in class com.google.javascript.jscomp.graph.GraphReachability +
  +
Reader - Class in com.google.javascript.jscomp.jsonml
Traverse JsonML source tree and generates AST.
Reader() - +Constructor for class com.google.javascript.jscomp.jsonml.Reader +
  +
rebuildInputsFromModules() - +Method in class com.google.javascript.jscomp.Compiler +
Rebuilds the internal list of inputs by iterating over all modules. +
recentChange - +Variable in class com.google.javascript.jscomp.Compiler +
  +
recompute(N) - +Method in class com.google.javascript.jscomp.graph.GraphReachability +
  +
recordBaseType(JSTypeExpression) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records a base type. +
recordBlockDescription(String) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records a block-level description. +
recordConsistentIdGenerator() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isConsistentIdGenerator() flag set to + true. +
recordConstancy() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isConstant() flag set to true. +
recordConstructor() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isConstructor() flag set to true. +
recordDefineType(JSTypeExpression) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records the type of a define. +
recordDeprecated() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isDeprecated() flag set to true. +
recordDeprecationReason(String) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records the deprecation reason. +
recordDescription(String) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records a description giving context for translation (i18n). +
recordEnumParameterType(JSTypeExpression) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records a parameter type to an enum. +
recordExport() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isExport() flag set to true. +
recordExpose() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isExpose() flag set to true. +
recordExtendedInterface(JSTypeExpression) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records an extended interface type. +
recordExterns() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isExterns() flag set to true. +
recordFileOverview(String) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records a fileoverview description. +
recordFunctionInformation - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Record function information +
recordHiddenness() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isHidden() flag set to true. +
recordIdGenerator() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isIdGenerator() flag set to + true. +
recordImplementedInterface(JSTypeExpression) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records an implemented interface. +
recordImplicitCast() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isImplicitCast() flag set to true. +
recordInterface() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isInterface() flag set to true. +
recordJavaDispatch() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isJavaDispatch() flag set to true. +
recordLends(String) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that we're lending to another name. +
recordMeaning(String) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records a meaning giving context for translation (i18n). +
recordModifies(Set<String>) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records the list of modifies warnings. +
recordNoAlias() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isNoAlias() flag set to true. +
recordNoCompile() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isNoCompile() flag set to true. +
recordNoShadow() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isNoShadow() flag set to true. +
recordNoSideEffects() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isNoSideEffects() flag set to true. +
recordNoTypeCheck() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isNoTypeCheck() flag set to true. +
recordOriginalCommentString(String) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Sets the original JSDoc comment string. +
recordOverride() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.isOverride() flag set to true. +
recordParameter(String, JSTypeExpression) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records a typed parameter. +
recordParameterDescription(String, String) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records a parameter's description. +
recordPreserveTry() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should have its + JSDocInfo.shouldPreserveTry() flag set to true. +
recordReturnDescription(String) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records a return description +
recordReturnType(JSTypeExpression) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records a return type. +
recordSuppressions(Set<String>) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records the list of suppressed warnings. +
recordTemplateTypeName(String) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records a template type name. +
recordThisType(JSTypeExpression) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records a type for @this annotation. +
recordThrowDescription(JSTypeExpression, String) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records a throw type's description. +
recordThrowType(JSTypeExpression) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records a thrown type. +
recordType(JSTypeExpression) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records a type. +
RecordTypeBuilder - Class in com.google.javascript.rhino.jstype
A builder for record types.
RecordTypeBuilder(JSTypeRegistry) - +Constructor for class com.google.javascript.rhino.jstype.RecordTypeBuilder +
  +
recordTypedef(JSTypeExpression) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records that the JSDocInfo being built should be populated + with a typedef'd type. +
recordVersion(String) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records the version. +
recordVisibility(JSDocInfo.Visibility) - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
Records a visibility. +
reduceToMinimum(CharRanges) - +Static method in class com.google.javascript.jscomp.regex.CaseCanonicalize +
Given a character range that may include case sensitive code-units, + such as [0-9B-M], returns the character range that includes + the minimal set of code units such that for every code unit in the + input there is a case-sensitively equivalent canonical code unit in the + output. +
REFERENCE_ERROR_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
REFERENCE_ERROR_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
regexp(Node) - +Static method in class com.google.javascript.rhino.IR +
  +
regexp(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
REGEXP - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
REGEXP_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
REGEXP_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
RegExpTree - Class in com.google.javascript.jscomp.regex
An AST for JavaScript regular expressions.
RegExpTree() - +Constructor for class com.google.javascript.jscomp.regex.RegExpTree +
  +
Region - Interface in com.google.javascript.jscomp
Source code region.
registerAllExtensions(ExtensionRegistry) - +Static method in class com.google.debugging.sourcemap.proto.Mapping +
  +
registerAllExtensions(ExtensionRegistry) - +Static method in class com.google.javascript.jscomp.FunctionInfo +
  +
registerAllExtensions(ExtensionRegistry) - +Static method in class com.google.javascript.jscomp.InstrumentationTemplate +
  +
registerPropertyOnType(String, JSType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Tells the type system that owner may have a property named + propertyName. +
registry - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
remove(CompilerInput) - +Method in class com.google.javascript.jscomp.JSModule +
Removes an input from this module. +
removeAll() - +Method in class com.google.javascript.jscomp.JSModule +
Removes all of the inputs from this module. +
removeByName(String) - +Method in class com.google.javascript.jscomp.JSModule +
Removes any input with the given name. +
removeChild(Node) - +Method in class com.google.javascript.rhino.Node +
Detach a child from its parent and siblings. +
removeChildAfter(Node) - +Method in class com.google.javascript.rhino.Node +
  +
removeChildren() - +Method in class com.google.javascript.rhino.Node +
  +
removeDeadCode - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Removes code that will never execute +
removeEntry(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
removeExternInput(InputId) - +Method in class com.google.javascript.jscomp.Compiler +
Removes an input file from AST. +
removeFirstChild() - +Method in class com.google.javascript.rhino.Node +
Removes the first child of Node. +
removeModule(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
removeProp(int) - +Method in class com.google.javascript.rhino.Node +
  +
removeProperty(String) - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
removeProperty(String) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Removes the declared or inferred property from this ObjectType. +
removeRequire(String) - +Method in class com.google.javascript.jscomp.CompilerInput +
  +
removeTryCatchFinally - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Removes try...catch...finally blocks for easier debugging +
removeUnusedLocalVars - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Removes unused variables in local scope. +
removeUnusedPrototypeProperties - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Removes unused member prototypes +
removeUnusedPrototypePropertiesInExterns - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Tells AnalyzePrototypeProperties it can remove externed props. +
removeUnusedVars - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Removes unused variables +
renamePrefix - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Specifies a prefix for all globals +
renamePrefixNamespace - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Specifies the name of an object that will be used to store all non-extern + globals. +
replaceChild(Node, Node) - +Method in class com.google.javascript.rhino.Node +
Detaches child from Node and replaces it with newChild. +
replaceChildAfter(Node, Node) - +Method in class com.google.javascript.rhino.Node +
  +
replaceScript(JsAst) - +Method in class com.google.javascript.jscomp.Compiler +
Replaces one file in a hot-swap mode. +
report(JSError) - +Method in class com.google.javascript.jscomp.AbstractCompiler +
Report an error or warning. +
report(CheckLevel, JSError) - +Method in class com.google.javascript.jscomp.BasicErrorManager +
  +
report(JSError) - +Method in class com.google.javascript.jscomp.Compiler +
  +
report(CheckLevel, JSError) - +Method in interface com.google.javascript.jscomp.ErrorHandler +
  +
report(CheckLevel, JSError) - +Method in interface com.google.javascript.jscomp.ErrorManager +
Reports an error. +
report(Node, DiagnosticType, String...) - +Method in class com.google.javascript.jscomp.NodeTraversal +
Reports a diagnostic (error or warning) +
report(CheckLevel, JSError) - +Method in class com.google.javascript.jscomp.WhitelistWarningsGuard.WhitelistBuilder +
  +
REPORT_CALL_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.Instrumentation +
  +
REPORT_DEFINED_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.Instrumentation +
  +
REPORT_EXIT_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.Instrumentation +
  +
reportCodeChange() - +Method in class com.google.javascript.jscomp.AbstractCompiler +
Report code changes. +
reportCodeChange() - +Method in class com.google.javascript.jscomp.Compiler +
All passes should call reportCodeChange() when they alter + the JS tree structure. +
reportMissingOverride - +Variable in class com.google.javascript.jscomp.CompilerOptions +
  +
reserveRawExports - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Reserve property names on the global this object. +
reset() - +Method in interface com.google.debugging.sourcemap.SourceMapGenerator +
Resets the source map for reuse. +
reset() - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV1 +
Resets the source map for reuse for the generation of a new source file. +
reset() - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV2 +
Resets the source map for reuse. +
reset() - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV3 +
Resets the source map for reuse. +
reset() - +Method in class com.google.javascript.jscomp.SourceMap +
  +
resetForTypeCheck() - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Reset to run the TypeCheck pass. +
resetImplicitPrototype(JSType, ObjectType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Set the implicit prototype if it's possible to do so. +
resetWarningsGuard() - +Method in class com.google.javascript.jscomp.CompilerOptions +
Reset the warnings guard. +
resolve(ErrorReporter, StaticScope<JSType>) - +Method in class com.google.javascript.rhino.jstype.JSType +
Resolve this type in the given scope. +
resolve(JSTypeExpression, String...) - +Method in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
Resolves a type expression, expecting the given warnings. +
resolveTypesInScope(StaticScope<JSType>) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Resolve all the unresolved types in the given scope. +
restrictByNotNullOrUndefined() - +Method in class com.google.javascript.rhino.jstype.JSType +
If this is a union type, returns a union type that does not include + the null or undefined type. +
restrictByNotNullOrUndefined() - +Method in class com.google.javascript.rhino.jstype.NullType +
  +
restrictByNotNullOrUndefined() - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
restrictByNotNullOrUndefined() - +Method in class com.google.javascript.rhino.jstype.VoidType +
  +
Result - Class in com.google.javascript.jscomp
Compilation results
Result(JSError[], JSError[], String, VariableMap, VariableMap, VariableMap, FunctionInformationMap, SourceMap, String) - +Constructor for class com.google.javascript.jscomp.Result +
  +
RETURN - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
returnNode() - +Static method in class com.google.javascript.rhino.IR +
  +
returnNode(Node) - +Static method in class com.google.javascript.rhino.IR +
  +
rewriteFunctionExpressions - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Reduces the size of common function expressions. +
RSH - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
runs - +Variable in class com.google.javascript.jscomp.PerformanceTracker.Stats +
  +
runtime - +Variable in class com.google.javascript.jscomp.PerformanceTracker.Stats +
  +
runtimeError(String, String, int, String, int) - +Method in class com.google.javascript.jscomp.testing.TestErrorReporter +
  +
+
+

+S

+
+
save(String) - +Method in class com.google.javascript.jscomp.VariableMap +
Saves the variable map to a file. +
Scope - Class in com.google.javascript.jscomp
Scope contains information about a variable scope in javascript.
Scope.Arguments - Class in com.google.javascript.jscomp
A special subclass of Var used to distinguish "arguments" in the current + scope.
Scope.Var - Class in com.google.javascript.jscomp
Stores info about a variable
script(Node...) - +Static method in class com.google.javascript.rhino.IR +
  +
SCRIPT - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
ScriptRuntime - Class in com.google.javascript.rhino
This is the class that implements the runtime.
ScriptRuntime() - +Constructor for class com.google.javascript.rhino.ScriptRuntime +
No instances should be created. +
SecureCompiler - Class in com.google.javascript.jscomp.jsonml
Compilation of JavaScript code which guarantees that all security + capabilities are preserved after the process.
SecureCompiler() - +Constructor for class com.google.javascript.jscomp.jsonml.SecureCompiler +
  +
SecureCompiler.Report - Class in com.google.javascript.jscomp.jsonml
 
serialVersionUID - +Static variable in class com.google.javascript.rhino.InputId +
  +
SET - +Static variable in class com.google.javascript.rhino.Token +
  +
setAcceptConstKeyword(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
If true, accept `const' keyword. +
setAggressiveVarCheck(CheckLevel) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Checks for suspicious variable definitions and undefined variables +
setAliasableGlobals(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
A comma separated white-list of global names. +
setAliasableStrings(Set<String>) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setAliasAllStrings(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setAliasExternals(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setAliasKeywords(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setAliasStringsBlacklist(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setAliasTransformationHandler(CompilerOptions.AliasTransformationHandler) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setAllFlags() - +Method in class com.google.javascript.rhino.Node.SideEffectFlags +
All side-effect occur and the returned results are non-local. +
setAmbiguateProperties(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setAnnotation(Annotation) - +Method in interface com.google.javascript.jscomp.graph.Annotatable +
Annotates a piece of information to the object. +
setAnonymousFunctionNaming(AnonymousFunctionNamingPolicy) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setAppNameSetter(String) - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
setAppNameStr(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
App identifier string for use by the instrumentation template's + app_name_setter. +
setAssociatedNode(Node) - +Method in class com.google.javascript.rhino.JSDocInfo +
Sets the node associated with this JSDoc. +
setAssumeClosuresOnlyCaptureReferences(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Whether to assume closures capture only what they reference. +
setAssumeStrictThis(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
If true, enables enables additional optimizations. +
setAttribute(TagAttr, Object) - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Sets value for a given attribute. +
setAttributes(Map<TagAttr, Object>) - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Sets attributes of the JsonML element. +
setBrokenClosureRequiresLevel(CheckLevel) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets the check level for bad Closure require calls. +
setChainCalls(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
If true, chain calls to functions that return this. +
setCharno(int) - +Method in class com.google.javascript.rhino.Node +
  +
setCheckCaja(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCheckControlStructures(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCheckGlobalNamesLevel(CheckLevel) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Checks the integrity of references to qualified global names. +
setCheckGlobalThisLevel(CheckLevel) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Checks for certain uses of the this keyword that are considered + unsafe because they are likely to reference the global this + object unintentionally. +
setCheckMissingGetCssNameBlacklist(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCheckMissingGetCssNameLevel(CheckLevel) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Checks that certain string literals only appear in strings used as + goog.getCssName arguments. +
setCheckMissingReturn(CheckLevel) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Checks for missing return statements +
setCheckProvides(CheckLevel) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Checks for missing goog.provides() calls +
setCheckRequires(CheckLevel) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCheckSuspiciousCode(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCheckSymbols(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCheckTypes(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCheckUnreachableCode(CheckLevel) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Checks for unreachable code +
setChild(int, JsonML) - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Replaces the element at the given position in the list of children wit + the given JsonML element. +
setChildren(JsonML...) - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Replaces all elements in the list of children with the given + JsonML elements. +
setChildren(List<JsonML>) - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Replaces all elements in the list of children with the given + list of JsonML elements.. +
setClosurePass(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCoalesceVariableNames(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCodingConvention(CodingConvention) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCollapseAnonymousFunctions(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCollapseObjectLiterals(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCollapseProperties(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCollapsePropertiesOnExternTypes(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
If true, flattens multi-level property names on extern types + (e.g. +
setCollapseVariableDeclarations(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setColorize(boolean) - +Method in class com.google.javascript.jscomp.AbstractMessageFormatter +
  +
setColorizeErrorOutput(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setColumnPosition(int) - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
setColumnPosition(int) - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
setCommonJSModulePathPrefix(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets a path prefix for Common JS modules. +
setCompilationLevel(String) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Set the compilation level. +
setCompiledSource(String) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
setCompiledSource(String) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
setCompiler(AbstractCompiler) - +Method in class com.google.javascript.jscomp.CompilerInput +
Sets an abstract compiler for doing parsing. +
setComputeFunctionSideEffects(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setConvertToDottedProperties(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCrossModuleCodeMotion(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCrossModuleMethodMotion(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCssRenamingMap(CssRenamingMap) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setCustomExternsOnly(boolean) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Use only custom externs. +
setCustomPasses(Multimap<CustomPassExecutionTime, CompilerPass>) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setDeadAssignmentElimination(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setDebug(boolean) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Enable debugging options. +
setDebugFunctionSideEffectsPath(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setDebugOptionsForCompilationLevel(CompilerOptions) - +Method in enum com.google.javascript.jscomp.CompilationLevel +
  +
setDeclarationToRemove(int, String) - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
setDefineReplacements(Map<String, Object>) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setDefineToBooleanLiteral(String, boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets the value of the @define variable in JS + to a boolean literal. +
setDefineToDoubleLiteral(String, double) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets the value of the @define variable in JS to a + number literal. +
setDefineToNumberLiteral(String, int) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets the value of the @define variable in JS to a + number literal. +
setDefineToStringLiteral(String, String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets the value of the @define variable in JS to a + String literal. +
setDependencyOptions(DependencyOptions) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets dependency options. +
setDependencyPruning(boolean) - +Method in class com.google.javascript.jscomp.DependencyOptions +
Enables or disables dependency pruning mode. +
setDependencySorting(boolean) - +Method in class com.google.javascript.jscomp.DependencyOptions +
Enables or disables dependency sorting mode. +
setDeprecated(boolean) - +Method in class com.google.javascript.rhino.JSDocInfo +
  +
setDepth(int) - +Method in class com.google.javascript.jscomp.JSModule +
  +
setDesc(String) - +Method in class com.google.javascript.jscomp.JsMessage.Builder +
Sets the description of the message, which helps translators. +
setDestination(DiGraph.DiGraphNode<N, E>) - +Method in interface com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge +
  +
setDevirtualizePrototypeMethods(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setDevMode(CompilerOptions.DevMode) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setDirectives(Set<String>) - +Method in class com.google.javascript.rhino.Node +
Sets the ES5 directives on this node. +
setDisambiguateProperties(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setDouble(double) - +Method in class com.google.javascript.rhino.Node +
Can only be called when getType() == TokenStream.NUMBER +
setEncoding(String) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Set input file encoding +
setEntry(int, FunctionInformationMap.Entry) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
setEntry(int, FunctionInformationMap.Entry.Builder) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
setEntryPoints(Collection<String>) - +Method in class com.google.javascript.jscomp.DependencyOptions +
Adds a collection of symbols to always keep. +
setErrorFormat(ErrorFormat) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setErrorHandler(ErrorHandler) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Set a custom handler for warnings and errors. +
setErrorManager(ErrorManager) - +Method in class com.google.javascript.jscomp.Compiler +
Sets the error manager. +
setErrors(String[]) - +Method in class com.google.javascript.rhino.testing.TestErrorReporter +
  +
setExportTestFunctions(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setExtendedInterfaces(List<ObjectType>) - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
setExternExports(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setExternExportsPath(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setExtraAnnotationNames(Set<String>) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setExtractPrototypeMemberDeclarations(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setFlowSensitiveInlineVariables(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setFoldConstants(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setForceRecompile(boolean) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Set force recompile option +
setGatherCssNames(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setGenerateExports(boolean) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Set generateExports option +
setGenerateExports(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setGeneratePseudoNames(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setGeneratorTarget(String) - +Method in class com.google.javascript.jscomp.WhitelistWarningsGuard.WhitelistBuilder +
Fill in instructions on how to generate this whitelist. +
setGroup(String) - +Method in class com.google.javascript.jscomp.ant.Warning +
  +
setGroupVariableDeclarations(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setId(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
setIdeMode(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setIdentifier(String) - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
setIdGenerators(Set<String>) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets the id generators to replace. +
setIgnoreCajaProperties(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Add code to skip properties that Caja adds to Object.prototype +
setImplementedInterfaces(List<ObjectType>) - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
setInferTypes(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
If true, enables type inference. +
setInit(int, String) - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
setInlineConstantVars(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setInlineFunctions(CompilerOptions.Reach) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Set the function inlining policy for the compiler. +
setInlineFunctions(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setInlineGetters(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setInlineLocalFunctions(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setInlineLocalVariables(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setInlineVariables(CompilerOptions.Reach) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Set the variable inlining policy for the compiler. +
setInlineVariables(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setInputDelimiter(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setInputId(InputId) - +Method in class com.google.javascript.rhino.Node +
  +
setInputPropertyMapSerialized(byte[]) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setInputVariableMapSerialized(byte[]) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setInstrumentationTemplate(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setIsConstructor(boolean) - +Method in class com.google.javascript.rhino.jstype.FunctionBuilder +
Set whether this is a constructor. +
setIsHidden(boolean) - +Method in class com.google.javascript.jscomp.JsMessage.Builder +
Sets whether the message should be hidden from volunteer translators. +
setIsSyntheticBlock(boolean) - +Method in class com.google.javascript.rhino.Node +
Sets whether this is a synthetic block that should not be considered + a real source block. +
setItem(T) - +Method in class com.google.javascript.rhino.SourcePosition +
Sets the item that this source position references. +
setJSDocInfo(JSDocInfo) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Sets the docInfo for this type from the given + JSDocInfo. +
setJSDocInfo(JSDocInfo) - +Method in class com.google.javascript.rhino.Node +
Sets the JSDocInfo attached to this node. +
setJSType(JSType) - +Method in class com.google.javascript.rhino.Node +
  +
setKey(String) - +Method in class com.google.javascript.jscomp.JsMessage.Builder +
  +
setLabelRenaming(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setLanguageIn(CompilerOptions.LanguageMode) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets how goog.tweak calls are processed. +
setLanguageOut(CompilerOptions.LanguageMode) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setLastGeneration(boolean) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Sets whether this is the last generation. +
setLength(int) - +Method in class com.google.javascript.rhino.Node +
  +
setLevel(CheckLevel) - +Method in class com.google.javascript.jscomp.ant.Warning +
  +
setLicense(String) - +Method in class com.google.javascript.rhino.JSDocInfo +
License directives can appear in multiple comments, and always + apply to the entire file. +
setLineBreak(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setLineLengthThreshold(int) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setLineno(int) - +Method in class com.google.javascript.rhino.Node +
  +
setLineNumber(int) - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
setLineNumber(int) - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
setLineNumber(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
setLocale(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setLoggingLevel(Level) - +Static method in class com.google.javascript.jscomp.Compiler +
Sets the logging level for the com.google.javascript.jscomp package. +
setLooseTypes(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Whether to include "undefined" in the default types. +
setManageClosureDependencies(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sort inputs by their goog.provide/goog.require calls, and prune inputs + whose symbols are not required. +
setManageClosureDependencies(List<String>) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sort inputs by their goog.provide/goog.require calls. +
setManageDependencies(boolean) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
  +
setMarkAsCompiled(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setMarkNoSideEffectCalls(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setMeaning(String) - +Method in class com.google.javascript.jscomp.JsMessage.Builder +
Sets the programmer-specified meaning of this message, which + forces this message to translate differently. +
setMessageBundle(MessageBundle) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setModule(JSModule) - +Method in class com.google.javascript.jscomp.CompilerInput +
Sets the module to which the input belongs. +
setModule(int, FunctionInformationMap.Module) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
setModule(int, FunctionInformationMap.Module.Builder) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Builder +
  +
setModuleName(String) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
setMoocherDropping(boolean) - +Method in class com.google.javascript.jscomp.DependencyOptions +
Enables or disables moocher dropping mode. +
setMoveFunctionDeclarations(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setMutatesArguments() - +Method in class com.google.javascript.rhino.Node.SideEffectFlags +
  +
setMutatesGlobalState() - +Method in class com.google.javascript.rhino.Node.SideEffectFlags +
  +
setMutatesThis() - +Method in class com.google.javascript.rhino.Node.SideEffectFlags +
  +
setName(String) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
setName(String) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module.Builder +
  +
setNameAnonymousFunctionsOnly(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
If true, name anonymous functions only. +
setNameReferenceGraphPath(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Where to save the name reference graph +
setNameReferenceReportPath(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Where to save a cross-reference report from the name reference graph +
setNode(Node) - +Method in class com.google.javascript.rhino.jstype.ObjectType.Property +
  +
setOptimizeArgumentsArray(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setOptimizeCalls(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setOptimizeParameters(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setOptimizeReturns(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setOptionalArg(boolean) - +Method in class com.google.javascript.rhino.Node +
Sets whether this node is an optional argument node. +
setOptionsForCompilationLevel(CompilerOptions) - +Method in enum com.google.javascript.jscomp.CompilationLevel +
  +
setOptionsForWarningLevel(CompilerOptions) - +Method in enum com.google.javascript.jscomp.WarningLevel +
  +
setOriginalFile(String) - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder +
  +
setOriginalMapping(Mapping.OriginalMapping) - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
setOriginalMapping(Mapping.OriginalMapping.Builder) - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping.Builder +
  +
setOriginalPath(String) - +Method in class com.google.javascript.jscomp.SourceFile +
  +
setOutput(File) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Set output file. +
setOutputCharset(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets the output charset by name. +
setOutputEncoding(String) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Set output file encoding +
setOutputJsStringUsage(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setPassConfig(PassConfig) - +Method in class com.google.javascript.jscomp.Compiler +
  +
setPositionInformation(int, int, int, int) - +Method in class com.google.javascript.rhino.SourcePosition +
Sets the position information contained in this source position. +
setPreferLineBreakAtEndOfFile(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setPrefixMappings(List<SourceMap.LocationMapping>) - +Method in class com.google.javascript.jscomp.SourceMap +
  +
setPrettyPrint(boolean) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Set pretty print formatting option +
setPrettyPrint(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setPrintInputDelimiter(boolean) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Set print input delimitter formatting option +
setPrintInputDelimiter(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setProcessCommonJSModules(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Rewrites CommonJS modulee so that modules can be concatenated together, + by renaming all globals to avoid conflicting with other modules. +
setProcessObjectPropertyString(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
If true, process goog.testing.ObjectPropertyString instances. +
setProductName(String) - +Method in class com.google.javascript.jscomp.WhitelistWarningsGuard.WhitelistBuilder +
Fill in your product name to get a fun message! +
setPropertyAffinity(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setPropertyInvalidationErrors(Map<String, CheckLevel>) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets the list of properties that we report property invalidation errors + for. +
setPropertyJSDocInfo(String, JSDocInfo) - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
setPropertyJSDocInfo(String, JSDocInfo) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
Sets the docInfo for the specified property from the + JSDocInfo on its definition. +
setPropertyRenaming(PropertyRenamingPolicy) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setProtectHiddenSideEffects(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
When enabled, assume that apparently side-effect free code is meaningful. +
setPrototypeBasedOn(ObjectType) - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Sets the prototype, creating the prototype object from the given + base type. +
setQuotedString() - +Method in class com.google.javascript.rhino.Node +
This should only be called for STRING nodes children of OBJECTLIT. +
setRecordFunctionInformation(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setRemoveAbstractMethods(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setRemoveClosureAsserts(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setRemoveDeadCode(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setRemoveTryCatchFinally(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setRemoveUnusedLocalVars(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setRemoveUnusedPrototypeProperties(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setRemoveUnusedPrototypePropertiesInExterns(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setRemoveUnusedVariable(CompilerOptions.Reach) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Deprecated.  +
setRemoveUnusedVariables(CompilerOptions.Reach) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Set the variable removal policy for the compiler. +
setRemoveUnusedVars(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setRenamePrefix(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setRenamePrefixNamespace(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setRenamingPolicy(VariableRenamingPolicy, PropertyRenamingPolicy) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets the variable and property renaming policies for the compiler, + in a way that clears warnings about the renaming policy being + uninitialized from flags. +
setReplaceIdGenerators(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setReplaceProperties(boolean) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Whether to replace @define lines with properties +
setReplacePropertiesPrefix(String) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Set the replacement property prefix. +
setReplaceStringsConfiguration(String, List<String>) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets the functions whose debug strings to replace. +
setReplaceStringsFunctionDescriptions(List<String>) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setReplaceStringsPlaceholderToken(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setReplaceStringsReservedStrings(Set<String>) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setReportCall(String) - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
setReportDefined(String) - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
setReportExit(String) - +Method in class com.google.javascript.jscomp.Instrumentation.Builder +
  +
setReportMissingOverride(CheckLevel) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Flags a warning if a property is missing the @override annotation, but it + overrides a base class property. +
setReportPath(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Where to save a report of global name usage +
setReportUnknownTypes(CheckLevel) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Flags a warning for every node whose type could not be determined. +
setReserveRawExports(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setResolveMode(JSTypeRegistry.ResolveMode) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Set the current resolving mode of the type registry. +
setReturnsTainted() - +Method in class com.google.javascript.rhino.Node.SideEffectFlags +
  +
setRewriteFunctionExpressions(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setRewriteNewDateGoogNow(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setRootElement(JsonML) - +Method in class com.google.javascript.jscomp.jsonml.Reader +
  +
setRuntimeTypeCheck(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setRuntimeTypeCheckLogFunction(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setShadowVariables(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Should shadow outer scope variable name during renaming. +
setShortcutMode(boolean) - +Method in class com.google.javascript.jscomp.deps.JsFileLineParser +
In shortcut mode, the file line parser can stop reading early if + it thinks it found enough information. +
setSideEffectFlags(int) - +Method in class com.google.javascript.rhino.Node +
Marks this function or constructor call's side effect flags. +
setSideEffectFlags(Node.SideEffectFlags) - +Method in class com.google.javascript.rhino.Node +
  +
setSize(int) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
setSkipAllPasses(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setSmartNameRemoval(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setSource(DiGraph.DiGraphNode<N, E>) - +Method in interface com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge +
  +
setSource(Node) - +Method in class com.google.javascript.rhino.jstype.FunctionType +
Sets the source node. +
setSourceEncodedPosition(int) - +Method in class com.google.javascript.rhino.Node +
  +
setSourceEncodedPositionForTree(int) - +Method in class com.google.javascript.rhino.Node +
  +
setSourceFile(SourceFile) - +Method in class com.google.javascript.jscomp.CompilerInput +
  +
setSourceFile(SourceFile) - +Method in class com.google.javascript.jscomp.JsAst +
  +
setSourceFile(SourceFile) - +Method in class com.google.javascript.jscomp.jsonml.JsonMLAst +
  +
setSourceFile(SourceFile) - +Method in interface com.google.javascript.jscomp.SourceAst +
Sets the source file the generated AST represents. +
setSourceFile(SourceFile) - +Method in class com.google.javascript.jscomp.SyntheticAst +
  +
setSourceFileForTesting(String) - +Method in class com.google.javascript.rhino.Node +
Sets the source file to a non-extern file of the given name. +
setSourceMapDetailLevel(SourceMap.DetailLevel) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setSourceMapFormat(SourceMap.Format) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setSourceMapLocationMappings(List<SourceMap.LocationMapping>) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setSourceMapOutputPath(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setSourceName(String) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry.Builder +
  +
setSourceName(String) - +Method in class com.google.javascript.jscomp.JsMessage.Builder +
  +
setSpecializeInitialModule(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Specialize the initial module at the cost of later modules +
setStartingPosition(int, int) - +Method in interface com.google.debugging.sourcemap.SourceMapGenerator +
Sets the source code that exists in the buffer for which the + generated code is being generated. +
setStartingPosition(int, int) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV1 +
Sets the source code that exists in the buffer to which the + generated code is being generated. +
setStartingPosition(int, int) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV2 +
Sets the source code that exists in the buffer for which the + generated code is being generated. +
setStartingPosition(int, int) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV3 +
Sets the source code that exists in the buffer for which the + generated code is being generated. +
setStartingPosition(int, int) - +Method in class com.google.javascript.jscomp.SourceMap +
  +
setState(Compiler.IntermediateState) - +Method in class com.google.javascript.jscomp.Compiler +
Sets the internal state to the capture given. +
setStaticSourceFile(StaticSourceFile) - +Method in class com.google.javascript.rhino.Node +
  +
setString(String) - +Method in class com.google.javascript.rhino.Node +
Can only be called when node has String context. +
setStripNamePrefixes(Set<String>) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setStripNameSuffixes(Set<String>) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setStripTypePrefixes(Set<String>) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setStripTypes(Set<String>) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setSummaryDetailLevel(int) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Controls how detailed the compilation summary is. +
setSummaryDetailLevel(int) - +Method in class com.google.javascript.jscomp.PrintStreamErrorManager +
  +
setSyntheticBlockEndMarker(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setSyntheticBlockStartMarker(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setTemplateTypeName(String) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Sets the template type name. +
SETTER_DEF - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
setThrows() - +Method in class com.google.javascript.rhino.Node.SideEffectFlags +
  +
setTightenTypes(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Tightens types based on a global analysis. +
setTracer(CompilerOptions.TracerMode) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setTracerMode(CompilerOptions.TracerMode) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setTransformAMDToCJSModules(boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Activates transformation of AMD to CJS modules. +
setTweakProcessing(CompilerOptions.TweakProcessing) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets how goog.tweak calls are processed. +
setTweakReplacements(Map<String, Object>) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setTweakToBooleanLiteral(String, boolean) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets the value of the tweak in JS + to a boolean literal. +
setTweakToDoubleLiteral(String, double) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets the value of the tweak in JS to a + number literal. +
setTweakToNumberLiteral(String, int) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets the value of the tweak in JS to a + number literal. +
setTweakToStringLiteral(String, String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Sets the value of the tweak in JS to a + String literal. +
setType(int) - +Method in class com.google.javascript.rhino.Node +
  +
setTypedPercent(double) - +Method in class com.google.javascript.jscomp.BasicErrorManager +
  +
setTypedPercent(double) - +Method in interface com.google.javascript.jscomp.ErrorManager +
Sets the percentage of typed expressions. +
setUnaliasableGlobals(String) - +Method in class com.google.javascript.jscomp.CompilerOptions +
A comma separated white-list of global names. +
setUp() - +Method in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
setValidator(Predicate<JSType>) - +Method in class com.google.javascript.rhino.jstype.JSType +
Certain types have constraints on them at resolution-time. +
setValidator(Predicate<JSType>) - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
setVarArgs(boolean) - +Method in class com.google.javascript.rhino.Node +
Sets whether this node is a variable length argument node. +
setVariableRenaming(VariableRenamingPolicy) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setVisibility(JSDocInfo.Visibility) - +Method in class com.google.javascript.rhino.JSDocInfo +
  +
setWarning(String) - +Method in class com.google.javascript.jscomp.ant.CompileTask +
Set the warning level. +
setWarningLevel(DiagnosticGroup, CheckLevel) - +Method in class com.google.javascript.jscomp.CompilerOptions +
Configure the given type of warning to the given level. +
setWarnings(String[]) - +Method in class com.google.javascript.rhino.testing.TestErrorReporter +
  +
setWarningsGuard(ComposeWarningsGuard) - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
setWasEmptyNode(boolean) - +Method in class com.google.javascript.rhino.Node +
Sets whether this is a synthetic block that should not be considered + a real source block. +
setWithLineNumber(boolean) - +Method in class com.google.javascript.jscomp.WhitelistWarningsGuard.WhitelistBuilder +
Sets whether line number are recorded in the whitelist. +
setWrapperPrefix(String) - +Method in interface com.google.debugging.sourcemap.SourceMapGenerator +
Sets the prefix used for wrapping the generated source file before + it is written. +
setWrapperPrefix(String) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV1 +
Sets the prefix used for wrapping the generated source file before + it is output. +
setWrapperPrefix(String) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV2 +
Sets the prefix used for wrapping the generated source file before + it is written. +
setWrapperPrefix(String) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV3 +
Sets the prefix used for wrapping the generated source file before + it is written. +
setWrapperPrefix(String) - +Method in class com.google.javascript.jscomp.SourceMap +
  +
sheq(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
"===" +
SHEQ - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
SHNE - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
shouldColorizeErrorOutput() - +Method in class com.google.javascript.jscomp.CompilerOptions +
  +
shouldParseDocumentation() - +Method in class com.google.javascript.rhino.JSDocInfoBuilder +
  +
shouldPreserveTry() - +Method in class com.google.javascript.rhino.JSDocInfo +
Returns whether the @preserveTry annotation is present on this + JSDocInfo. +
shouldRunCompiler() - +Method in class com.google.javascript.jscomp.CommandLineRunner +
  +
shouldSkipDepsFile(SourceFile) - +Method in class com.google.javascript.jscomp.deps.DepsGenerator +
Returns whether we should ignore dependency info in the given deps file. +
shouldStrip() - +Method in enum com.google.javascript.jscomp.CompilerOptions.TweakProcessing +
  +
shouldTolerateUndefinedValues() - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
  +
shouldTraverse(NodeTraversal, Node, Node) - +Method in class com.google.javascript.jscomp.NodeTraversal.AbstractNodeTypePruningCallback +
  +
shouldTraverse(NodeTraversal, Node, Node) - +Method in class com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback +
  +
shouldTraverse(NodeTraversal, Node, Node) - +Method in class com.google.javascript.jscomp.NodeTraversal.AbstractScopedCallback +
  +
shouldTraverse(NodeTraversal, Node, Node) - +Method in class com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback +
  +
shouldTraverse(NodeTraversal, Node, Node) - +Method in class com.google.javascript.jscomp.NodeTraversal.AbstractShallowStatementCallback +
  +
shouldTraverse(NodeTraversal, Node, Node) - +Method in interface com.google.javascript.jscomp.NodeTraversal.Callback +
Visits a node in pre order (before visiting its children) and decides + whether this node's children should be traversed. +
shouldTraverse(NodeTraversal, Node, Node) - +Method in class com.google.javascript.jscomp.TypeCheck +
  +
ShowByPathWarningsGuard - Class in com.google.javascript.jscomp
Control whether warnings should be restricted or suppressed for specified + paths.
ShowByPathWarningsGuard(String) - +Constructor for class com.google.javascript.jscomp.ShowByPathWarningsGuard +
  +
ShowByPathWarningsGuard(String[]) - +Constructor for class com.google.javascript.jscomp.ShowByPathWarningsGuard +
  +
ShowByPathWarningsGuard(String, ShowByPathWarningsGuard.ShowType) - +Constructor for class com.google.javascript.jscomp.ShowByPathWarningsGuard +
  +
ShowByPathWarningsGuard(String[], ShowByPathWarningsGuard.ShowType) - +Constructor for class com.google.javascript.jscomp.ShowByPathWarningsGuard +
  +
ShowByPathWarningsGuard.ShowType - Enum in com.google.javascript.jscomp
Controls whether warnings should be restricted to a specified path or + suppressed within the specified path.
siblings() - +Method in class com.google.javascript.rhino.Node +
Return an iterable object that iterates over this nodes's siblings. +
SIDE_EFFECT_FLAGS - +Static variable in class com.google.javascript.rhino.Node +
  +
SIDE_EFFECTS_ALL - +Static variable in class com.google.javascript.rhino.Node +
  +
SIDE_EFFECTS_FLAGS_MASK - +Static variable in class com.google.javascript.rhino.Node +
  +
SimpleDependencyInfo - Class in com.google.javascript.jscomp.deps
A class to hold JS dependency information for a single .js file.
SimpleDependencyInfo(String, String, List<String>, List<String>) - +Constructor for class com.google.javascript.jscomp.deps.SimpleDependencyInfo +
Constructs a DependencyInfo object with the given list of provides & + requires. +
SimpleErrorReporter - Class in com.google.javascript.rhino
A simple ErrorReporter that collects warnings and errors and makes + them accessible via SimpleErrorReporter.errors() and SimpleErrorReporter.warnings().
SimpleErrorReporter() - +Constructor for class com.google.javascript.rhino.SimpleErrorReporter +
  +
SimpleReference<T extends StaticSlot<JSType>> - Class in com.google.javascript.rhino.jstype
A simple immutable reference.
SimpleReference(T, Node) - +Constructor for class com.google.javascript.rhino.jstype.SimpleReference +
  +
SimpleRegion - Class in com.google.javascript.jscomp
Simple region.
SimpleRegion(int, int, String) - +Constructor for class com.google.javascript.jscomp.SimpleRegion +
  +
SimpleSlot - Class in com.google.javascript.rhino.jstype
The minimum implementation of StaticSlot.
SimpleSlot(String, JSType, boolean) - +Constructor for class com.google.javascript.rhino.jstype.SimpleSlot +
  +
SimpleSourceExcerptProvider - Class in com.google.javascript.jscomp.testing
A simple source excerpt provider for testing.
SimpleSourceExcerptProvider(String) - +Constructor for class com.google.javascript.jscomp.testing.SimpleSourceExcerptProvider +
  +
SimpleSourceFile - Class in com.google.javascript.rhino.jstype
A simple implementation of StaticSourceFile for testing.
SimpleSourceFile(String, boolean) - +Constructor for class com.google.javascript.rhino.jstype.SimpleSourceFile +
  +
simplify(String) - +Method in class com.google.javascript.jscomp.regex.RegExpTree +
Returns a simpler regular expression that is semantically the same assuming + the given flags. +
size - +Variable in class com.google.javascript.jscomp.PerformanceTracker.Stats +
  +
SIZE_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
skipAllCompilerPasses() - +Method in class com.google.javascript.jscomp.CompilerOptions +
Skip all possible passes, to make the compiler as fast as possible. +
SLASH_V - +Static variable in class com.google.javascript.rhino.Node +
  +
smartNameRemoval - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Removes code associated with unused global names +
SortedDependencies<INPUT extends DependencyInfo> - Class in com.google.javascript.jscomp.deps
A sorted list of inputs with dependency information.
SortedDependencies(List<INPUT>) - +Constructor for class com.google.javascript.jscomp.deps.SortedDependencies +
  +
SortedDependencies.CircularDependencyException - Exception in com.google.javascript.jscomp.deps
 
SortedDependencies.MissingProvideException - Exception in com.google.javascript.jscomp.deps
 
sortInputsByDeps(Compiler) - +Method in class com.google.javascript.jscomp.JSModule +
Puts the JS files into a topologically sorted order by their dependencies. +
sortJsModules(Collection<JSModule>) - +Static method in class com.google.javascript.jscomp.JSModule +
Returns the given collection of modules in topological order. +
SOURCE_NAME_FIELD_NUMBER - +Static variable in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
SourceAst - Interface in com.google.javascript.jscomp
An interface for accessing the AST root of an input.
SourceExcerptProvider - Interface in com.google.javascript.jscomp
A source excerpt provider is responsible for building source code excerpt + of specific locations, such as a specific line or a region around a + given line number.
SourceExcerptProvider.ExcerptFormatter - Interface in com.google.javascript.jscomp
A excerpt formatter is responsible of formatting source excerpts.
SourceExcerptProvider.SourceExcerpt - Enum in com.google.javascript.jscomp
Source excerpt variety.
SourceFile - Class in com.google.javascript.jscomp
An abstract representation of a source file that provides access to + language-neutral features.
SourceFile(String) - +Constructor for class com.google.javascript.jscomp.SourceFile +
Construct a new abstract source file. +
SourceFile.Builder - Class in com.google.javascript.jscomp
A builder interface for source files.
SourceFile.Builder() - +Constructor for class com.google.javascript.jscomp.SourceFile.Builder +
  +
SourceFile.Generator - Interface in com.google.javascript.jscomp
A JavaScript source code provider.
sourceMap - +Variable in class com.google.javascript.jscomp.Result +
  +
SourceMap - Class in com.google.javascript.jscomp
Collects information mapping the generated (compiled) source back to + its original source for debugging purposes.
SourceMap.DetailLevel - Enum in com.google.javascript.jscomp
Source maps can be very large different levels of detail can be specified.
SourceMap.Format - Enum in com.google.javascript.jscomp
 
SourceMap.LocationMapping - Class in com.google.javascript.jscomp
 
SourceMap.LocationMapping(String, String) - +Constructor for class com.google.javascript.jscomp.SourceMap.LocationMapping +
  +
SourceMapConsumer - Interface in com.google.debugging.sourcemap
A SourceMapConsumer is a SourceMapping provide that can parse from a raw + string.
SourceMapConsumerFactory - Class in com.google.debugging.sourcemap
Detect and parse the provided source map.
SourceMapConsumerV1 - Class in com.google.debugging.sourcemap
Class for parsing and representing a SourceMap, as produced by the + Closure Compiler, Caja-Compiler, etc.
SourceMapConsumerV1() - +Constructor for class com.google.debugging.sourcemap.SourceMapConsumerV1 +
  +
SourceMapConsumerV2 - Class in com.google.debugging.sourcemap
Class for parsing version 2 of the SourceMap format, as produced by the + Closure Compiler, etc.
SourceMapConsumerV2() - +Constructor for class com.google.debugging.sourcemap.SourceMapConsumerV2 +
  +
SourceMapConsumerV3 - Class in com.google.debugging.sourcemap
Class for parsing version 3 of the SourceMap format, as produced by the + Closure Compiler, etc.
SourceMapConsumerV3() - +Constructor for class com.google.debugging.sourcemap.SourceMapConsumerV3 +
  +
SourceMapConsumerV3.EntryVisitor - Interface in com.google.debugging.sourcemap
 
sourceMapDetailLevel - +Variable in class com.google.javascript.jscomp.CompilerOptions +
The detail level for the generated source map. +
SourceMapFormat - Enum in com.google.debugging.sourcemap
A list of currently support SourceMap format revisions.
sourceMapFormat - +Variable in class com.google.javascript.jscomp.CompilerOptions +
The source map file format +
SourceMapGenerator - Interface in com.google.debugging.sourcemap
Collects information mapping the generated (compiled) source back to + its original source for debugging purposes
SourceMapGeneratorFactory - Class in com.google.debugging.sourcemap
 
SourceMapGeneratorFactory() - +Constructor for class com.google.debugging.sourcemap.SourceMapGeneratorFactory +
  +
SourceMapGeneratorV1 - Class in com.google.debugging.sourcemap
Collects information mapping the generated (compiled) source back to + its original source for debugging purposes.
SourceMapGeneratorV1() - +Constructor for class com.google.debugging.sourcemap.SourceMapGeneratorV1 +
  +
SourceMapGeneratorV2 - Class in com.google.debugging.sourcemap
Collects information mapping the generated (compiled) source back to + its original source for debugging purposes.
SourceMapGeneratorV2() - +Constructor for class com.google.debugging.sourcemap.SourceMapGeneratorV2 +
  +
SourceMapGeneratorV2.LineMapEncoder - Class in com.google.debugging.sourcemap
 
SourceMapGeneratorV2.LineMapEncoder() - +Constructor for class com.google.debugging.sourcemap.SourceMapGeneratorV2.LineMapEncoder +
  +
SourceMapGeneratorV3 - Class in com.google.debugging.sourcemap
Collects information mapping the generated (compiled) source back to + its original source for debugging purposes.
SourceMapGeneratorV3() - +Constructor for class com.google.debugging.sourcemap.SourceMapGeneratorV3 +
  +
sourceMapLocationMappings - +Variable in class com.google.javascript.jscomp.CompilerOptions +
  +
sourceMapOutputPath - +Variable in class com.google.javascript.jscomp.CompilerOptions +
The output path for the source map. +
SourceMapParseException - Exception in com.google.debugging.sourcemap
Throw if an invalid or unknown source map is encountered.
SourceMapParseException(String) - +Constructor for exception com.google.debugging.sourcemap.SourceMapParseException +
  +
SourceMapping - Interface in com.google.debugging.sourcemap
Interface for provide a way of mapping (line, column) positions back to + positions in the original (uncompiled) source code.
SourceMappingReversable - Interface in com.google.debugging.sourcemap
A SourceMappingReversable is a SourceMapping that can provide the reverse + (source --> target) source mapping.
SourceMapSection - Class in com.google.debugging.sourcemap
A class representing a partial source map.
SourceMapSection(String, int, int) - +Constructor for class com.google.debugging.sourcemap.SourceMapSection +
Deprecated.   +
SourceMapSection.SectionType - Enum in com.google.debugging.sourcemap
 
SourceMapSupplier - Interface in com.google.debugging.sourcemap
A class for mapping source map names to the actual contents.
sourceName - +Variable in class com.google.javascript.jscomp.JSError +
Name of the source +
sourceName - +Variable in class com.google.javascript.jscomp.jsonml.JsonMLError +
Name of the source +
SOURCENAME_PROP - +Static variable in class com.google.javascript.rhino.Node +
  +
sourceNode - +Variable in class com.google.javascript.jscomp.graph.GraphReachability.EdgeTuple +
  +
SourcePosition<T> - Class in com.google.javascript.rhino
Represents a position in some piece of source code, with an associated + item of type T found at that position.
SourcePosition() - +Constructor for class com.google.javascript.rhino.SourcePosition +
  +
srcref(Node) - +Method in class com.google.javascript.rhino.Node +
  +
srcrefTree(Node) - +Method in class com.google.javascript.rhino.Node +
  +
StandardUnionFind<E> - Class in com.google.javascript.jscomp.graph
A Union-Find implementation.
StandardUnionFind() - +Constructor for class com.google.javascript.jscomp.graph.StandardUnionFind +
Creates an empty UnionFind structure. +
StandardUnionFind(UnionFind<E>) - +Constructor for class com.google.javascript.jscomp.graph.StandardUnionFind +
Creates an UnionFind structure being a copy of other structure. +
STAR - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
StatementFusion - Class in com.google.javascript.jscomp
Tries to fuse all the statements in a block into a one statement by using + COMMAs.
StatementFusion() - +Constructor for class com.google.javascript.jscomp.StatementFusion +
  +
STATIC_SOURCE_FILE - +Static variable in class com.google.javascript.rhino.Node +
  +
StaticReference<T> - Interface in com.google.javascript.rhino.jstype
The StaticReference tells us all the ways that a StaticSlot + is used in a program.
StaticScope<T> - Interface in com.google.javascript.rhino.jstype
The StaticScope interface must be implemented by any object that + defines variables for the purposes of static analysis.
StaticSlot<T> - Interface in com.google.javascript.rhino.jstype
The StaticSlot interface must be implemented by variables that can + appear as members of a StaticScope.
StaticSourceFile - Interface in com.google.javascript.rhino.jstype
The StaticSourceFile contains information about a compiler input.
StaticSymbolTable<S extends StaticSlot<JSType>,R extends StaticReference<JSType>> - Interface in com.google.javascript.rhino.jstype
Lookup references by the symbols that they refer to.
STRICT_MODULE_DEP_CHECK - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
StrictWarningsGuard - Class in com.google.javascript.jscomp
All warnings should be reported as errors.
StrictWarningsGuard() - +Constructor for class com.google.javascript.jscomp.StrictWarningsGuard +
  +
string(String) - +Static method in class com.google.javascript.rhino.IR +
  +
STRING - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
STRING_OBJECT_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
STRING_OBJECT_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
STRING_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
stringMap - +Variable in class com.google.javascript.jscomp.Result +
  +
StringType - Class in com.google.javascript.rhino.jstype
String type.
stripNamePrefixes - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Name prefixes that determine which variables and properties to strip +
stripNameSuffixes - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Name suffixes that determine which variables and properties to strip +
stripTypePrefixes - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Qualified type name prefixes that determine which types to strip +
stripTypes - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Names of types to strip +
sub(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
SUB - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
SubGraph<N,E> - Interface in com.google.javascript.jscomp.graph
An interface representing a subgraph that provides adjacency calculation to + a node.
subSequence(int, int) - +Method in class com.google.javascript.jscomp.JsMessage.PlaceholderReference +
  +
success - +Variable in class com.google.javascript.jscomp.Result +
  +
SWITCH - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
switchNode(Node, Node...) - +Static method in class com.google.javascript.rhino.IR +
  +
symbol - +Variable in class com.google.javascript.jscomp.deps.JsFunctionParser.SymbolInfo +
  +
SymbolTable - Class in com.google.javascript.jscomp
A symbol table for people that want to use Closure Compiler as an indexer.
SymbolTable.Reference - Class in com.google.javascript.jscomp
 
SymbolTable.Symbol - Class in com.google.javascript.jscomp
 
SymbolTable.SymbolScope - Class in com.google.javascript.jscomp
 
SYNTAX_ERROR_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
SYNTAX_ERROR_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
SYNTHETIC_BLOCK_PROP - +Static variable in class com.google.javascript.rhino.Node +
  +
SyntheticAst - Class in com.google.javascript.jscomp
An AST generated totally by the compiler.
syntheticBlockEndMarker - +Variable in class com.google.javascript.jscomp.CompilerOptions +
  +
syntheticBlockStartMarker - +Variable in class com.google.javascript.jscomp.CompilerOptions +
  +
+
+

+T

+
+
TagAttr - Enum in com.google.javascript.jscomp.jsonml
List of attributes that a JsonML element may have.
TagType - Enum in com.google.javascript.jscomp.jsonml
List of types allowed for JsonML elements.
TemplateType - Class in com.google.javascript.rhino.jstype
 
TernaryValue - Enum in com.google.javascript.rhino.jstype
An enum for ternary logic.
TestErrorReporter - Class in com.google.javascript.jscomp.testing
An error reporter for testing that verifies that messages reported to the + reporter are expected.
TestErrorReporter(String[], String[]) - +Constructor for class com.google.javascript.jscomp.testing.TestErrorReporter +
  +
TestErrorReporter - Class in com.google.javascript.rhino.testing
An error reporter for testing that verifies that messages reported to the + reporter are expected.
TestErrorReporter(String[], String[]) - +Constructor for class com.google.javascript.rhino.testing.TestErrorReporter +
  +
testForEquality(JSType) - +Method in class com.google.javascript.rhino.jstype.AllType +
  +
testForEquality(JSType) - +Method in class com.google.javascript.rhino.jstype.BooleanType +
  +
testForEquality(JSType) - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
testForEquality(JSType) - +Method in class com.google.javascript.rhino.jstype.EnumType +
  +
testForEquality(JSType) - +Method in class com.google.javascript.rhino.jstype.JSType +
Compares this and that. +
testForEquality(JSType) - +Method in class com.google.javascript.rhino.jstype.NullType +
  +
testForEquality(JSType) - +Method in class com.google.javascript.rhino.jstype.NumberType +
  +
testForEquality(JSType) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
  +
testForEquality(JSType) - +Method in class com.google.javascript.rhino.jstype.StringType +
  +
testForEquality(JSType) - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
testForEquality(JSType) - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
testForEquality(JSType) - +Method in class com.google.javascript.rhino.jstype.VoidType +
  +
testUint32String(String) - +Static method in class com.google.javascript.rhino.ScriptRuntime +
If str is a decimal presentation of Uint32 value, return it as long. +
THIS - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
thisNode() - +Static method in class com.google.javascript.rhino.IR +
  +
THROW - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
throwNode(Node) - +Static method in class com.google.javascript.rhino.IR +
  +
toAnnotationString() - +Method in class com.google.javascript.rhino.jstype.JSType +
A string representation of this type, suitable for printing + in type annotations at code generation time. +
toBoolean(boolean) - +Method in enum com.google.javascript.rhino.jstype.TernaryValue +
Converts this ternary value to boolean. +
toBuilder() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
toBuilder() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
toBuilder() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
toBuilder() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
toBuilder() - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
toBuilder() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
toBytes() - +Method in class com.google.javascript.jscomp.VariableMap +
Serializes the variable map to a byte array. +
toDebugHashCodeString() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
toDebugHashCodeString() - +Method in class com.google.javascript.rhino.jstype.JSType +
A hash code function for diagnosing complicated issues + around type-identity. +
toDebugHashCodeString() - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
toDebugString() - +Method in class com.google.javascript.jscomp.regex.RegExpTree +
  +
toDebugString() - +Method in class com.google.javascript.jscomp.SymbolTable +
  +
toDot(Node) - +Static method in class com.google.javascript.jscomp.DotFormatter +
Converts an AST to dot representation. +
toDot(GraphvizGraph) - +Static method in class com.google.javascript.jscomp.DotFormatter +
Outputs a string in DOT format that presents the graph. +
toFormatter(SourceExcerptProvider, boolean) - +Method in enum com.google.javascript.jscomp.ErrorFormat +
Convert to a concrete formatter. +
Token - Class in com.google.javascript.rhino
This class implements the JavaScript scanner.
Token() - +Constructor for class com.google.javascript.rhino.Token +
  +
TokenStream - Class in com.google.javascript.rhino
This class implements the JavaScript scanner.
TokenStream() - +Constructor for class com.google.javascript.rhino.TokenStream +
  +
toMaybeEnumElementType() - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
toMaybeEnumElementType() - +Method in class com.google.javascript.rhino.jstype.JSType +
Downcasts this to an EnumElementType, or returns null if this is not an EnumElementType. +
toMaybeEnumType() - +Method in class com.google.javascript.rhino.jstype.EnumType +
  +
toMaybeEnumType() - +Method in class com.google.javascript.rhino.jstype.JSType +
Downcasts this to an EnumType, or returns null if this is not an EnumType. +
toMaybeFunctionType() - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
toMaybeFunctionType() - +Method in class com.google.javascript.rhino.jstype.JSType +
Downcasts this to a FunctionType, or returns null if this is not + a function. +
toMaybeFunctionType(JSType) - +Static method in class com.google.javascript.rhino.jstype.JSType +
Null-safe version of toMaybeFunctionType(). +
toMaybeFunctionType() - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
toMaybeUnionType() - +Method in class com.google.javascript.rhino.jstype.JSType +
Downcasts this to a UnionType, or returns null if this is not a UnionType. +
toMaybeUnionType() - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
toModuleName(String) - +Static method in class com.google.javascript.jscomp.ProcessCommonJSModules +
Turns a filename into a JS identifier that is used for moduleNames in + rewritten code. +
toModuleName(String, String) - +Static method in class com.google.javascript.jscomp.ProcessCommonJSModules +
Turn a filename into a moduleName with support for relative addressing + with ./ and ../ based on currentFilename; +
TOO_MANY_CHILDREN_FMT - +Static variable in class com.google.javascript.jscomp.jsonml.Validator +
  +
toObjectType() - +Method in class com.google.javascript.rhino.jstype.JSType +
Casts this to an ObjectType, or returns null if this is not an ObjectType. +
toSource() - +Method in class com.google.javascript.jscomp.Compiler +
Converts the main parse tree back to js code. +
toSource(JSModule) - +Method in class com.google.javascript.jscomp.Compiler +
Converts the parse tree for a module back to js code. +
toSource(Compiler.CodeBuilder, int, Node) - +Method in class com.google.javascript.jscomp.Compiler +
Writes out js code from a root node. +
toSourceArray() - +Method in class com.google.javascript.jscomp.Compiler +
Converts the parse tree for each input back to js code. +
toSourceArray(JSModule) - +Method in class com.google.javascript.jscomp.Compiler +
Converts the parse tree for each input in a module back to js code. +
toString() - +Method in class com.google.javascript.jscomp.Compiler.CodeBuilder +
Returns all text in the text buffer. +
toString() - +Method in class com.google.javascript.jscomp.CompilerInput +
  +
toString() - +Method in class com.google.javascript.jscomp.ComposeWarningsGuard +
  +
toString() - +Method in class com.google.javascript.jscomp.deps.SimpleDependencyInfo +
  +
toString() - +Method in class com.google.javascript.jscomp.DiagnosticGroup +
  +
toString() - +Method in class com.google.javascript.jscomp.DiagnosticGroupWarningsGuard +
  +
toString() - +Method in class com.google.javascript.jscomp.DiagnosticType +
  +
toString() - +Method in class com.google.javascript.jscomp.JSError +
  +
toString() - +Method in class com.google.javascript.jscomp.JsMessage.PlaceholderReference +
  +
toString() - +Method in class com.google.javascript.jscomp.JsMessage +
  +
toString() - +Method in class com.google.javascript.jscomp.JSModule +
Returns the module name (primarily for debugging). +
toString() - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
  +
toString() - +Method in enum com.google.javascript.jscomp.jsonml.TagAttr +
  +
toString() - +Method in class com.google.javascript.jscomp.regex.RegExpTree +
  +
toString() - +Method in class com.google.javascript.jscomp.Scope.Var +
  +
toString() - +Method in class com.google.javascript.jscomp.SourceFile +
  +
toString() - +Method in class com.google.javascript.jscomp.SymbolTable.Symbol +
  +
toString() - +Method in class com.google.javascript.jscomp.SymbolTable.SymbolScope +
  +
toString() - +Method in class com.google.javascript.rhino.InputId +
  +
toString() - +Method in class com.google.javascript.rhino.JSDocInfo +
  +
toString() - +Method in class com.google.javascript.rhino.jstype.JSType +
A string representation of this type, suitable for printing + in warnings. +
toString() - +Method in class com.google.javascript.rhino.jstype.SimpleReference +
  +
toString() - +Method in class com.google.javascript.rhino.jstype.SimpleSourceFile +
  +
toString() - +Method in class com.google.javascript.rhino.Node +
  +
toString(boolean, boolean, boolean) - +Method in class com.google.javascript.rhino.Node +
  +
toStringTree() - +Method in class com.google.javascript.jscomp.jsonml.JsonML +
Prints a JsonML tree in a human readable format. +
toStringTree() - +Method in class com.google.javascript.rhino.Node +
  +
tracker - +Variable in class com.google.javascript.jscomp.Compiler +
  +
traverse(Node) - +Method in class com.google.javascript.jscomp.NodeTraversal +
Traverses a parse tree recursively. +
traverse(AbstractCompiler, Node, NodeTraversal.Callback) - +Static method in class com.google.javascript.jscomp.NodeTraversal +
Traverses a node recursively. +
traverseEdge(Node, Edge, Node) - +Method in interface com.google.javascript.jscomp.graph.FixedPointGraphTraversal.EdgeCallback +
Update the state of the destination node when the given edge + is traversed. +
traverseEdge(N, E, N) - +Method in class com.google.javascript.jscomp.graph.GraphReachability +
  +
traverseInnerNode(Node, Node, Scope) - +Method in class com.google.javascript.jscomp.NodeTraversal +
Traverses an inner node recursively with a refined scope. +
traverseRoots(Node...) - +Method in class com.google.javascript.jscomp.NodeTraversal +
  +
traverseRoots(List<Node>) - +Method in class com.google.javascript.jscomp.NodeTraversal +
  +
traverseRoots(AbstractCompiler, List<Node>, NodeTraversal.Callback) - +Static method in class com.google.javascript.jscomp.NodeTraversal +
Traverses a list of node trees. +
traverseRoots(AbstractCompiler, NodeTraversal.Callback, Node...) - +Static method in class com.google.javascript.jscomp.NodeTraversal +
  +
TRUE - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
trueNode() - +Static method in class com.google.javascript.rhino.IR +
  +
TRY - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
tryCatch(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
tryCatchFinally(Node, Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
tryFinally(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
TWEAKS - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
TYPE_ERROR_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
TYPE_ERROR_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
TYPE_INVALIDATION - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
typeA - +Variable in class com.google.javascript.rhino.jstype.JSType.TypePair +
  +
typeB - +Variable in class com.google.javascript.rhino.jstype.JSType.TypePair +
  +
TypeCheck - Class in com.google.javascript.jscomp
Checks the types of JS expressions against any declared type + information.
TypeCheck(AbstractCompiler, ReverseAbstractInterpreter, JSTypeRegistry, Scope, ScopeCreator, CheckLevel, CheckLevel) - +Constructor for class com.google.javascript.jscomp.TypeCheck +
  +
TypeCheck(AbstractCompiler, ReverseAbstractInterpreter, JSTypeRegistry, CheckLevel, CheckLevel) - +Constructor for class com.google.javascript.jscomp.TypeCheck +
  +
TYPEOF - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
+
+

+U

+
+
U2U_CONSTRUCTOR_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
U2U_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
unboxesTo() - +Method in class com.google.javascript.rhino.jstype.JSType +
Gets the type to which this type unboxes. +
UNDEFINED_NAMES - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
UNDEFINED_VARIABLES - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
UndiGraph<N,E> - Class in com.google.javascript.jscomp.graph
A generic undirected graph.
UndiGraph() - +Constructor for class com.google.javascript.jscomp.graph.UndiGraph +
  +
UndiGraph.UndiGraphEdge<N,E> - Interface in com.google.javascript.jscomp.graph
A generic undirected graph edge.
UndiGraph.UndiGraphNode<N,E> - Interface in com.google.javascript.jscomp.graph
A generic undirected graph node.
union(E, E) - +Method in class com.google.javascript.jscomp.graph.StandardUnionFind +
  +
union(E, E) - +Method in interface com.google.javascript.jscomp.graph.UnionFind +
Unions the equivalence classes of a and b and returns the + representative of the resulting equivalence class. +
union(BooleanLiteralSet) - +Method in enum com.google.javascript.rhino.jstype.BooleanLiteralSet +
Computes the union of this set and that. +
UnionFind<E> - Interface in com.google.javascript.jscomp.graph
Union-Find is a classical algorithm used to find connected components in + graph theory.
UnionType - Class in com.google.javascript.rhino.jstype
The UnionType implements a common JavaScript idiom in which the + code is specifically designed to work with multiple input types.
UNKNOWN_DEFINES - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
UNKNOWN_NAME - +Static variable in class com.google.javascript.rhino.jstype.JSType +
  +
UNKNOWN_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
UnknownType - Class in com.google.javascript.rhino.jstype
The Unknown type.
unregisterPropertyOnType(String, JSType) - +Method in class com.google.javascript.rhino.jstype.JSTypeRegistry +
Removes the index's reference to a property on the given type (if it is + currently registered). +
URI_ERROR_FUNCTION_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
URI_ERROR_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
URSH - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
useSourceInfoFrom(Node) - +Method in class com.google.javascript.rhino.Node +
Overwrite all the source information in this node with + that of other. +
useSourceInfoFromForTree(Node) - +Method in class com.google.javascript.rhino.Node +
Overwrite all the source information in this node and its subtree with + that of other. +
useSourceInfoIfMissingFrom(Node) - +Method in class com.google.javascript.rhino.Node +
Overwrite all the source information in this node with + that of other iff the source info is missing. +
useSourceInfoIfMissingFromForTree(Node) - +Method in class com.google.javascript.rhino.Node +
Overwrite all the source information in this node and its subtree with + that of other iff the source info is missing. +
+
+

+V

+
+
validate(boolean) - +Method in interface com.google.debugging.sourcemap.SourceMapGenerator +
Whether to perform additional validation on the source map. +
validate(boolean) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV1 +
  +
validate(boolean) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV2 +
  +
validate(boolean) - +Method in class com.google.debugging.sourcemap.SourceMapGeneratorV3 +
  +
validate(JsonML) - +Static method in class com.google.javascript.jscomp.jsonml.Validator +
Validates the specified JsonML element. +
validate(boolean) - +Method in class com.google.javascript.jscomp.SourceMap +
  +
validateCodeRoot(Node) - +Method in class com.google.javascript.jscomp.AstValidator +
  +
validateExpression(Node) - +Method in class com.google.javascript.jscomp.AstValidator +
  +
validateRoot(Node) - +Method in class com.google.javascript.jscomp.AstValidator +
  +
validateScript(Node) - +Method in class com.google.javascript.jscomp.AstValidator +
  +
validateStatement(Node) - +Method in class com.google.javascript.jscomp.AstValidator +
  +
Validator - Class in com.google.javascript.jscomp.jsonml
Statically validates JsonML elements.
valueOf(String) - +Static method in enum com.google.debugging.sourcemap.SourceMapFormat +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.debugging.sourcemap.SourceMapSection.SectionType +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.AnonymousFunctionNamingPolicy +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.CheckLevel +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.CheckLevelLegacy +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.CodingConvention.SubclassType +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.CompilationLevel +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.CompilerOptions.LanguageMode +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.CompilerOptions.Reach +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.CompilerOptions.TracerMode +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.CompilerOptions.TweakProcessing +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.CssRenamingMap.Style +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.CustomPassExecutionTime +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.ErrorFormat +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.JsMessage.Style +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.jsonml.ErrorLevel +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.jsonml.TagAttr +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.jsonml.TagType +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.parsing.Config.LanguageMode +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.PropertyRenamingPolicy +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.ShowByPathWarningsGuard.ShowType +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.SourceMap.DetailLevel +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.SourceMap.Format +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.VariableRenamingPolicy +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.WarningLevel +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.jscomp.WarningsGuard.Priority +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.rhino.JSDocInfo.Visibility +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.rhino.jstype.BooleanLiteralSet +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.rhino.jstype.JSTypeNative +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.rhino.jstype.JSTypeRegistry.ResolveMode +
Returns the enum constant of this type with the specified name. +
valueOf(String) - +Static method in enum com.google.javascript.rhino.jstype.TernaryValue +
Returns the enum constant of this type with the specified name. +
valueOf() - +Method in class com.google.javascript.rhino.Node.SideEffectFlags +
  +
values() - +Static method in enum com.google.debugging.sourcemap.SourceMapFormat +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.debugging.sourcemap.SourceMapSection.SectionType +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.AnonymousFunctionNamingPolicy +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.CheckLevel +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.CheckLevelLegacy +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.CodingConvention.SubclassType +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.CompilationLevel +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.CompilerOptions.LanguageMode +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.CompilerOptions.Reach +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.CompilerOptions.TracerMode +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.CompilerOptions.TweakProcessing +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.CssRenamingMap.Style +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.CustomPassExecutionTime +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.ErrorFormat +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.JsMessage.Style +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.jsonml.ErrorLevel +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.jsonml.TagAttr +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.jsonml.TagType +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.parsing.Config.LanguageMode +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.PropertyRenamingPolicy +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.ShowByPathWarningsGuard.ShowType +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.SourceMap.DetailLevel +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.SourceMap.Format +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.VariableRenamingPolicy +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.WarningLevel +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.jscomp.WarningsGuard.Priority +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.rhino.JSDocInfo.Visibility +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.rhino.jstype.BooleanLiteralSet +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.rhino.jstype.JSTypeNative +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.rhino.jstype.JSTypeRegistry.ResolveMode +
Returns an array containing the constants of this enum type, in +the order they are declared. +
values() - +Static method in enum com.google.javascript.rhino.jstype.TernaryValue +
Returns an array containing the constants of this enum type, in +the order they are declared. +
var(Node, Node) - +Static method in class com.google.javascript.rhino.IR +
  +
var(Node) - +Static method in class com.google.javascript.rhino.IR +
  +
VAR - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
VAR_ARGS_NAME - +Static variable in class com.google.javascript.rhino.Node +
  +
variableMap - +Variable in class com.google.javascript.jscomp.Result +
  +
VariableMap - Class in com.google.javascript.jscomp
Stores the mapping from original variable name to new variable names.
variableRenaming - +Variable in class com.google.javascript.jscomp.CompilerOptions +
Controls which variables get renamed. +
VariableRenamingPolicy - Enum in com.google.javascript.jscomp
Policies to determine which variables should be renamed.
VISIBILITY - +Static variable in class com.google.javascript.jscomp.DiagnosticGroups +
  +
visit(String, String, FilePosition, FilePosition, FilePosition) - +Method in interface com.google.debugging.sourcemap.SourceMapConsumerV3.EntryVisitor +
  +
visit(NodeTraversal, Node, Node) - +Method in class com.google.javascript.jscomp.FindExportableNodes +
  +
visit(NodeTraversal, Node, Node) - +Method in interface com.google.javascript.jscomp.NodeTraversal.Callback +
Visits a node in post order (after its children have been visited). +
visit(NodeTraversal, Node, Node) - +Method in class com.google.javascript.jscomp.TypeCheck +
This is the meat of the type checking. +
visit(Visitor<T>) - +Method in class com.google.javascript.rhino.jstype.AllType +
  +
visit(Visitor<T>) - +Method in class com.google.javascript.rhino.jstype.BooleanType +
  +
visit(Visitor<T>) - +Method in class com.google.javascript.rhino.jstype.EnumElementType +
  +
visit(Visitor<T>) - +Method in class com.google.javascript.rhino.jstype.EnumType +
  +
visit(Visitor<T>) - +Method in class com.google.javascript.rhino.jstype.FunctionType +
  +
visit(Visitor<T>) - +Method in class com.google.javascript.rhino.jstype.JSType +
Visit this type with the given visitor. +
visit(Visitor<T>) - +Method in class com.google.javascript.rhino.jstype.NoObjectType +
  +
visit(Visitor<T>) - +Method in class com.google.javascript.rhino.jstype.NoType +
  +
visit(Visitor<T>) - +Method in class com.google.javascript.rhino.jstype.NullType +
  +
visit(Visitor<T>) - +Method in class com.google.javascript.rhino.jstype.NumberType +
  +
visit(Visitor<T>) - +Method in class com.google.javascript.rhino.jstype.ObjectType +
  +
visit(Visitor<T>) - +Method in class com.google.javascript.rhino.jstype.StringType +
  +
visit(Visitor<T>) - +Method in class com.google.javascript.rhino.jstype.UnionType +
  +
visit(Visitor<T>) - +Method in class com.google.javascript.rhino.jstype.UnknownType +
  +
visit(Visitor<T>) - +Method in class com.google.javascript.rhino.jstype.VoidType +
  +
visitMappings(SourceMapConsumerV3.EntryVisitor) - +Method in class com.google.debugging.sourcemap.SourceMapConsumerV3 +
  +
Visitor<T> - Interface in com.google.javascript.rhino.jstype
A type visitor.
VOID - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
VOID_TYPE - +Variable in class com.google.javascript.rhino.testing.BaseJSTypeTestCase +
  +
voidNode(Node) - +Static method in class com.google.javascript.rhino.IR +
  +
VoidType - Class in com.google.javascript.rhino.jstype
Void type whose only element is the undefined value.
+
+

+W

+
+
Warning - Class in com.google.javascript.jscomp.ant
Simple representation of a warning flag in Ant
Warning() - +Constructor for class com.google.javascript.jscomp.ant.Warning +
  +
warning(String, String) - +Static method in class com.google.javascript.jscomp.DiagnosticType +
Create a DiagnosticType at level CheckLevel.WARNING +
warning(String, String, int, int) - +Method in class com.google.javascript.jscomp.parsing.NullErrorReporter +
  +
warning(String, String, int, String, int) - +Method in class com.google.javascript.jscomp.testing.TestErrorReporter +
  +
warning(String, String, int, int) - +Method in interface com.google.javascript.rhino.ErrorReporter +
Report a warning. +
warning(String, String, int, int) - +Method in class com.google.javascript.rhino.SimpleErrorReporter +
  +
warning(String, String, int, int) - +Method in class com.google.javascript.rhino.testing.TestErrorReporter +
  +
WarningLevel - Enum in com.google.javascript.jscomp
Convert the warnings level to an Options object.
warnings - +Variable in class com.google.javascript.jscomp.Result +
  +
warnings() - +Method in class com.google.javascript.rhino.SimpleErrorReporter +
Returns the list of warnings, or null if there were none. +
WarningsGuard - Class in com.google.javascript.jscomp
Class that allows to flexibly manage what to do with a reported + warning/error.
WarningsGuard() - +Constructor for class com.google.javascript.jscomp.WarningsGuard +
  +
WarningsGuard.Priority - Enum in com.google.javascript.jscomp
 
wasEmptyNode() - +Method in class com.google.javascript.rhino.Node +
Returns whether this is a synthetic block that should not be considered + a real source block. +
WHILE - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
WhitelistWarningsGuard - Class in com.google.javascript.jscomp
An extension of WarningsGuard that provides functionality to maintain + a list of warnings (white-list).
WhitelistWarningsGuard(Set<String>) - +Constructor for class com.google.javascript.jscomp.WhitelistWarningsGuard +
This class depends on an input set that contains the white-list. +
WhitelistWarningsGuard.WhitelistBuilder - Class in com.google.javascript.jscomp
 
WhitelistWarningsGuard.WhitelistBuilder() - +Constructor for class com.google.javascript.jscomp.WhitelistWarningsGuard.WhitelistBuilder +
  +
WITH - +Static variable in class com.google.javascript.rhino.Token +
Token types. +
withCharset(Charset) - +Method in class com.google.javascript.jscomp.SourceFile.Builder +
Set the charset to use when reading from an input stream or file. +
withInferredReturnType(JSType) - +Method in class com.google.javascript.rhino.jstype.FunctionBuilder +
Sets an inferred return type. +
withName(String) - +Method in class com.google.javascript.rhino.jstype.FunctionBuilder +
Set the name of the function type. +
withOriginalPath(String) - +Method in class com.google.javascript.jscomp.SourceFile.Builder +
Set the original path to use. +
withParams(FunctionParamBuilder) - +Method in class com.google.javascript.rhino.jstype.FunctionBuilder +
Set the parameters of the function type from a FunctionParamBuilder. +
withParamsNode(Node) - +Method in class com.google.javascript.rhino.jstype.FunctionBuilder +
Set the parameters of the function type with a specially-formatted node. +
withReturnType(JSType) - +Method in class com.google.javascript.rhino.jstype.FunctionBuilder +
Set the return type. +
withReturnType(JSType, boolean) - +Method in class com.google.javascript.rhino.jstype.FunctionBuilder +
Set the return type and whether it's inferred. +
withSourceNode(Node) - +Method in class com.google.javascript.rhino.jstype.FunctionBuilder +
Set the source node of the function type. +
withTemplateName(String) - +Method in class com.google.javascript.rhino.jstype.FunctionBuilder +
Set the template name. +
withTypeOfThis(ObjectType) - +Method in class com.google.javascript.rhino.jstype.FunctionBuilder +
Set the "this" type. +
Writer - Class in com.google.javascript.jscomp.jsonml
Converts internal AST into JsonML tree.
Writer() - +Constructor for class com.google.javascript.jscomp.jsonml.Writer +
  +
writeReplace() - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
writeReplace() - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
writeReplace() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
writeReplace() - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
writeReplace() - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
writeReplace() - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
writeTo(CodedOutputStream) - +Method in class com.google.debugging.sourcemap.proto.Mapping.LineMapping +
  +
writeTo(CodedOutputStream) - +Method in class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping +
  +
writeTo(CodedOutputStream) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Entry +
  +
writeTo(CodedOutputStream) - +Method in class com.google.javascript.jscomp.FunctionInformationMap.Module +
  +
writeTo(CodedOutputStream) - +Method in class com.google.javascript.jscomp.FunctionInformationMap +
  +
writeTo(CodedOutputStream) - +Method in class com.google.javascript.jscomp.Instrumentation +
  +
writeWhitelist(File) - +Method in class com.google.javascript.jscomp.WhitelistWarningsGuard.WhitelistBuilder +
Writes the warnings collected in a format that the WhitelistWarningsGuard + can read back later. +
WRONG_CHILD_TYPE_FMT - +Static variable in class com.google.javascript.jscomp.jsonml.Validator +
  +
+
+

+X

+
+
xor(TernaryValue) - +Method in enum com.google.javascript.rhino.jstype.TernaryValue +
Gets the xor of this and that. +
XtbMessageBundle - Class in com.google.javascript.jscomp
A MessageBundle that parses messages from an XML Translation Bundle (XTB) + file.
XtbMessageBundle(InputStream, String, boolean) - +Constructor for class com.google.javascript.jscomp.XtbMessageBundle +
  +
XtbMessageBundle(InputStream, String) - +Constructor for class com.google.javascript.jscomp.XtbMessageBundle +
Creates an instance and initializes it with the messages in an XTB file. +
+
+A B C D E F G H I J K L M N O P Q R S T U V W X + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/index.html b/resources/defects4j-checkout-closure-1f/javadoc/index.html new file mode 100644 index 0000000..d141fd1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/index.html @@ -0,0 +1,38 @@ + + + + + +Compiler + + + + + + + + + + + +<H2> +Frame Alert</H2> + +<P> +This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. +<BR> +Link to<A HREF="overview-summary.html">Non-frame version.</A> + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/overview-frame.html b/resources/defects4j-checkout-closure-1f/javadoc/overview-frame.html new file mode 100644 index 0000000..588cbaf --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/overview-frame.html @@ -0,0 +1,64 @@ + + + + + +Overview List (Compiler) + + + + + + + + + + + + + + +
+
+ + + + + +
All Classes +

+ +Packages +
+com.google.debugging.sourcemap +
+com.google.debugging.sourcemap.proto +
+com.google.javascript.jscomp +
+com.google.javascript.jscomp.ant +
+com.google.javascript.jscomp.deps +
+com.google.javascript.jscomp.graph +
+com.google.javascript.jscomp.jsonml +
+com.google.javascript.jscomp.parsing +
+com.google.javascript.jscomp.regex +
+com.google.javascript.jscomp.testing +
+com.google.javascript.rhino +
+com.google.javascript.rhino.jstype +
+com.google.javascript.rhino.testing +
+

+ +

+  + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/overview-summary.html b/resources/defects4j-checkout-closure-1f/javadoc/overview-summary.html new file mode 100644 index 0000000..2d197a2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/overview-summary.html @@ -0,0 +1,213 @@ + + + + + +Overview (Compiler) + + + + + + + + + + + +


+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Packages
com.google.debugging.sourcemapProvides utilities to the creation and use of source maps.
com.google.debugging.sourcemap.proto 
com.google.javascript.jscompProvides the core compiler and its public API.
com.google.javascript.jscomp.ant 
com.google.javascript.jscomp.depsAnalyzes information about dependencies between files.
com.google.javascript.jscomp.graphProvides graph data structures and algorithms for coloring and fixed-point +computations.
com.google.javascript.jscomp.jsonmlProvides the classes to support JsonML format and secure compiler.
com.google.javascript.jscomp.parsingProvides utilities to help with parsing JSDoc annotations and performing AST +transformations.
com.google.javascript.jscomp.regex 
com.google.javascript.jscomp.testing 
com.google.javascript.rhinoThe core AST from Rhino.
com.google.javascript.rhino.jstypeProvides abstractions to represent types in JavaScript.
com.google.javascript.rhino.testing 
+ +


+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/overview-tree.html b/resources/defects4j-checkout-closure-1f/javadoc/overview-tree.html new file mode 100644 index 0000000..e74ca93 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/overview-tree.html @@ -0,0 +1,362 @@ + + + + + +Class Hierarchy (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Hierarchy For All Packages

+
+
+
Package Hierarchies:
com.google.debugging.sourcemap, com.google.debugging.sourcemap.proto, com.google.javascript.jscomp, com.google.javascript.jscomp.ant, com.google.javascript.jscomp.deps, com.google.javascript.jscomp.graph, com.google.javascript.jscomp.jsonml, com.google.javascript.jscomp.parsing, com.google.javascript.jscomp.regex, com.google.javascript.jscomp.testing, com.google.javascript.rhino, com.google.javascript.rhino.jstype, com.google.javascript.rhino.testing
+
+

+Class Hierarchy +

+ +

+Interface Hierarchy +

+ +

+Enum Hierarchy +

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/javadoc/package-list b/resources/defects4j-checkout-closure-1f/javadoc/package-list new file mode 100644 index 0000000..76f4e4f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/package-list @@ -0,0 +1,13 @@ +com.google.debugging.sourcemap +com.google.debugging.sourcemap.proto +com.google.javascript.jscomp +com.google.javascript.jscomp.ant +com.google.javascript.jscomp.deps +com.google.javascript.jscomp.graph +com.google.javascript.jscomp.jsonml +com.google.javascript.jscomp.parsing +com.google.javascript.jscomp.regex +com.google.javascript.jscomp.testing +com.google.javascript.rhino +com.google.javascript.rhino.jstype +com.google.javascript.rhino.testing diff --git a/resources/defects4j-checkout-closure-1f/javadoc/resources/inherit.gif b/resources/defects4j-checkout-closure-1f/javadoc/resources/inherit.gif new file mode 100644 index 0000000..c814867 Binary files /dev/null and b/resources/defects4j-checkout-closure-1f/javadoc/resources/inherit.gif differ diff --git a/resources/defects4j-checkout-closure-1f/javadoc/serialized-form.html b/resources/defects4j-checkout-closure-1f/javadoc/serialized-form.html new file mode 100644 index 0000000..d43aaa9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/javadoc/serialized-form.html @@ -0,0 +1,4758 @@ + + + + + +Serialized Form (Compiler) + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Serialized Form

+
+
+ + + + + +
+Package com.google.debugging.sourcemap
+ +

+ + + + + +
+Class com.google.debugging.sourcemap.SourceMapParseException extends Exception implements Serializable
+ +

+


+ + + + + +
+Package com.google.debugging.sourcemap.proto
+ +

+ + + + + +
+Class com.google.debugging.sourcemap.proto.Mapping.LineMapping extends com.google.protobuf.GeneratedMessage implements Serializable
+ +

+serialVersionUID: 0L + +

+ + + + + +
+Serialization Methods
+ +

+

+writeReplace

+
+protected Object writeReplace()
+                       throws ObjectStreamException
+
+
+ +
Throws: +
ObjectStreamException
+
+
+ + + + + +
+Serialized Fields
+ +

+bitField0_

+
+int bitField0_
+
+
+
+
+
+

+lineNumber_

+
+int lineNumber_
+
+
+
+
+
+

+columnPosition_

+
+int columnPosition_
+
+
+
+
+
+

+originalMapping_

+
+Mapping.OriginalMapping originalMapping_
+
+
+
+
+
+

+memoizedIsInitialized

+
+byte memoizedIsInitialized
+
+
+
+
+
+

+memoizedSerializedSize

+
+int memoizedSerializedSize
+
+
+
+
+ +

+ + + + + +
+Class com.google.debugging.sourcemap.proto.Mapping.OriginalMapping extends com.google.protobuf.GeneratedMessage implements Serializable
+ +

+serialVersionUID: 0L + +

+ + + + + +
+Serialization Methods
+ +

+

+writeReplace

+
+protected Object writeReplace()
+                       throws ObjectStreamException
+
+
+ +
Throws: +
ObjectStreamException
+
+
+ + + + + +
+Serialized Fields
+ +

+bitField0_

+
+int bitField0_
+
+
+
+
+
+

+originalFile_

+
+Object originalFile_
+
+
+
+
+
+

+lineNumber_

+
+int lineNumber_
+
+
+
+
+
+

+columnPosition_

+
+int columnPosition_
+
+
+
+
+
+

+identifier_

+
+Object identifier_
+
+
+
+
+
+

+memoizedIsInitialized

+
+byte memoizedIsInitialized
+
+
+
+
+
+

+memoizedSerializedSize

+
+int memoizedSerializedSize
+
+
+
+
+
+ + + + + +
+Package com.google.javascript.jscomp
+ +

+ + + + + +
+Class com.google.javascript.jscomp.AbstractCommandLineRunner.FlagUsageException extends Exception implements Serializable
+ +

+serialVersionUID: 1L + +

+ +

+ + + + + +
+Class com.google.javascript.jscomp.ClosureCodingConvention extends CodingConventions.Proxy implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+propertyTestFunctions

+
+Set<E> propertyTestFunctions
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.CodingConventions.Proxy extends Object implements Serializable
+ +

+ + + + + +
+Serialized Fields
+ +

+nextConvention

+
+CodingConvention nextConvention
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.Compiler.IntermediateState extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+externsRoot

+
+Node externsRoot
+
+
+
+
+
+

+jsRoot

+
+Node jsRoot
+
+
+
+
+
+

+externs

+
+List<E> externs
+
+
+
+
+
+

+inputs

+
+List<E> inputs
+
+
+
+
+
+

+modules

+
+List<E> modules
+
+
+
+
+
+

+passConfigState

+
+com.google.javascript.jscomp.PassConfig.State passConfigState
+
+
+
+
+
+

+typeRegistry

+
+JSTypeRegistry typeRegistry
+
+
+
+
+
+

+lifeCycleStage

+
+com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage lifeCycleStage
+
+
+
+
+
+

+injectedLibraries

+
+Map<K,V> injectedLibraries
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.CompilerInput extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+module

+
+JSModule module
+
+
+
+
+
+

+id

+
+InputId id
+
+
+
+
+
+

+ast

+
+SourceAst ast
+
+
+
+
+
+

+provides

+
+Set<E> provides
+
+
+
+
+
+

+requires

+
+Set<E> requires
+
+
+
+
+
+

+generatedDependencyInfoFromSource

+
+boolean generatedDependencyInfoFromSource
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.CompilerOptions extends Object implements Serializable
+ +

+serialVersionUID: 7L + +

+ + + + + +
+Serialized Fields
+ +

+manageClosureDependencies

+
+boolean manageClosureDependencies
+
+
+
+
+
+

+languageIn

+
+CompilerOptions.LanguageMode languageIn
+
+
The JavaScript language version accepted. +

+

+
+
+
+

+languageOut

+
+CompilerOptions.LanguageMode languageOut
+
+
The JavaScript language version that should be produced. + Currently, this is always the same as CompilerOptions.languageIn. +

+

+
+
+
+

+acceptConstKeyword

+
+boolean acceptConstKeyword
+
+
Whether the compiler accepts the `const' keyword. +

+

+
+
+
+

+assumeStrictThis

+
+boolean assumeStrictThis
+
+
Whether the compiler should assume that a function's "this" value + never needs coercion (for example in non-strict "null" or "undefined" will + be coerced to the global "this" and primitives to objects). +

+

+
+
+
+

+ideMode

+
+boolean ideMode
+
+
Configures the compiler for use as an IDE backend. In this mode: +
    +
  • No optimization passes will run.
  • +
  • The last time custom passes are invoked is + CustomPassExecutionTime.BEFORE_OPTIMIZATIONS
  • +
  • The compiler will always try to process all inputs fully, even + if it encounters errors.
  • +
  • The compiler may record more information than is strictly + needed for codegen.
  • +
+

+

+
+
+
+

+inferTypes

+
+boolean inferTypes
+
+
Even if checkTypes is disabled, clients might want to still infer types. + This is mostly used when ideMode is enabled. +

+

+
+
+
+

+skipAllPasses

+
+boolean skipAllPasses
+
+
Configures the compiler to skip as many passes as possible. +

+

+
+
+
+

+nameAnonymousFunctionsOnly

+
+boolean nameAnonymousFunctionsOnly
+
+
If true, name anonymous functions only. All others passes will be skipped. +

+

+
+
+
+

+devMode

+
+com.google.javascript.jscomp.CompilerOptions.DevMode devMode
+
+
Configures the compiler to run expensive sanity checks after + every pass. Only intended for internal development. +

+

+
+
+
+

+dependencyOptions

+
+DependencyOptions dependencyOptions
+
+
+
+
+
+

+checkSymbols

+
+boolean checkSymbols
+
+
Checks that all symbols are defined +

+

+
+
+
+

+aggressiveVarCheck

+
+CheckLevel aggressiveVarCheck
+
+
+
+
+
+

+checkSuspiciousCode

+
+boolean checkSuspiciousCode
+
+
Checks for suspicious statements that have no effect +

+

+
+
+
+

+checkControlStructures

+
+boolean checkControlStructures
+
+
Checks for invalid control structures +

+

+
+
+
+

+checkTypes

+
+boolean checkTypes
+
+
Checks types on expressions +

+

+
+
+
+

+tightenTypes

+
+boolean tightenTypes
+
+
+
+
+
+

+reportMissingOverride

+
+CheckLevel reportMissingOverride
+
+
+
+
+
+

+reportUnknownTypes

+
+CheckLevel reportUnknownTypes
+
+
+
+
+
+

+checkRequires

+
+CheckLevel checkRequires
+
+
Checks for missing goog.require() calls +

+

+
+
+
+

+checkProvides

+
+CheckLevel checkProvides
+
+
+
+
+
+

+checkGlobalNamesLevel

+
+CheckLevel checkGlobalNamesLevel
+
+
+
+
+
+

+brokenClosureRequiresLevel

+
+CheckLevel brokenClosureRequiresLevel
+
+
+
+
+
+

+checkGlobalThisLevel

+
+CheckLevel checkGlobalThisLevel
+
+
+
+
+
+

+checkMissingGetCssNameLevel

+
+CheckLevel checkMissingGetCssNameLevel
+
+
+
+
+
+

+checkMissingGetCssNameBlacklist

+
+String checkMissingGetCssNameBlacklist
+
+
Regex of string literals that may only appear in goog.getCssName arguments. +

+

+
+
+
+

+checkCaja

+
+boolean checkCaja
+
+
Checks that the synctactic restrictions of Caja are met. +

+

+
+
+
+

+extraAnnotationNames

+
+Set<E> extraAnnotationNames
+
+
A set of extra annotation names which are accepted and silently ignored + when encountered in a source file. Defaults to null which has the same + effect as specifying an empty set. +

+

+
+
+
+

+foldConstants

+
+boolean foldConstants
+
+
Folds constants (e.g. (2 + 3) to 5) +

+

+
+
+
+

+deadAssignmentElimination

+
+boolean deadAssignmentElimination
+
+
Remove assignments to values that can not be referenced +

+

+
+
+
+

+inlineConstantVars

+
+boolean inlineConstantVars
+
+
Inlines constants (symbols that are all CAPS) +

+

+
+
+
+

+inlineFunctions

+
+boolean inlineFunctions
+
+
Inlines short functions +

+

+
+
+
+

+inlineLocalFunctions

+
+boolean inlineLocalFunctions
+
+
Enhanced function inlining +

+

+
+
+
+

+assumeClosuresOnlyCaptureReferences

+
+boolean assumeClosuresOnlyCaptureReferences
+
+
+
+
+
+

+crossModuleCodeMotion

+
+boolean crossModuleCodeMotion
+
+
Move code to a deeper module +

+

+
+
+
+

+coalesceVariableNames

+
+boolean coalesceVariableNames
+
+
Merge two variables together as one. +

+

+
+
+
+

+crossModuleMethodMotion

+
+boolean crossModuleMethodMotion
+
+
Move methds to a deeper module +

+

+
+
+
+

+inlineGetters

+
+boolean inlineGetters
+
+
Inlines trivial getters +

+

+
+
+
+

+inlineVariables

+
+boolean inlineVariables
+
+
Inlines variables +

+

+
+
+
+

+inlineLocalVariables

+
+boolean inlineLocalVariables
+
+
Inlines variables +

+

+
+
+
+

+flowSensitiveInlineVariables

+
+boolean flowSensitiveInlineVariables
+
+
+
+
+
+

+smartNameRemoval

+
+boolean smartNameRemoval
+
+
Removes code associated with unused global names +

+

+
+
+
+

+removeDeadCode

+
+boolean removeDeadCode
+
+
Removes code that will never execute +

+

+
+
+
+

+checkUnreachableCode

+
+CheckLevel checkUnreachableCode
+
+
+
+
+
+

+checkMissingReturn

+
+CheckLevel checkMissingReturn
+
+
+
+
+
+

+extractPrototypeMemberDeclarations

+
+boolean extractPrototypeMemberDeclarations
+
+
Extracts common prototype member declarations +

+

+
+
+
+

+removeUnusedPrototypeProperties

+
+boolean removeUnusedPrototypeProperties
+
+
Removes unused member prototypes +

+

+
+
+
+

+removeUnusedPrototypePropertiesInExterns

+
+boolean removeUnusedPrototypePropertiesInExterns
+
+
Tells AnalyzePrototypeProperties it can remove externed props. +

+

+
+
+
+

+removeUnusedVars

+
+boolean removeUnusedVars
+
+
Removes unused variables +

+

+
+
+
+

+removeUnusedLocalVars

+
+boolean removeUnusedLocalVars
+
+
Removes unused variables in local scope. +

+

+
+
+
+

+aliasExternals

+
+boolean aliasExternals
+
+
Adds variable aliases for externals to reduce code size +

+

+
+
+
+

+aliasableGlobals

+
+String aliasableGlobals
+
+
+
+
+
+

+unaliasableGlobals

+
+String unaliasableGlobals
+
+
+
+
+
+

+collapseVariableDeclarations

+
+boolean collapseVariableDeclarations
+
+
Collapses multiple variable declarations into one +

+

+
+
+
+

+groupVariableDeclarations

+
+boolean groupVariableDeclarations
+
+
Group multiple variable declarations into one +

+

+
+
+
+

+collapseAnonymousFunctions

+
+boolean collapseAnonymousFunctions
+
+
Collapses anonymous function declarations into named function + declarations +

+

+
+
+
+

+aliasableStrings

+
+Set<E> aliasableStrings
+
+
If set to a non-empty set, those strings literals will be aliased to a + single global instance per string, to avoid creating more objects than + necessary. +

+

+
+
+
+

+aliasStringsBlacklist

+
+String aliasStringsBlacklist
+
+
A blacklist in the form of a regular expression to block strings that + contains certain words from being aliased. + If the value is the empty string, no words are blacklisted. +

+

+
+
+
+

+aliasAllStrings

+
+boolean aliasAllStrings
+
+
Aliases all string literals to global instances, to avoid creating more + objects than necessary (if true, overrides any set of strings passed in + to aliasableStrings) +

+

+
+
+
+

+outputJsStringUsage

+
+boolean outputJsStringUsage
+
+
Print string usage as part of the compilation log. +

+

+
+
+
+

+convertToDottedProperties

+
+boolean convertToDottedProperties
+
+
Converts quoted property accesses to dot syntax (a['b'] -> a.b) +

+

+
+
+
+

+rewriteFunctionExpressions

+
+boolean rewriteFunctionExpressions
+
+
Reduces the size of common function expressions. +

+

+
+
+
+

+optimizeParameters

+
+boolean optimizeParameters
+
+
Remove unused and constant parameters. +

+

+
+
+
+

+optimizeReturns

+
+boolean optimizeReturns
+
+
Remove unused return values. +

+

+
+
+
+

+optimizeCalls

+
+boolean optimizeCalls
+
+
Remove unused parameters from call sites. +

+

+
+
+
+

+optimizeArgumentsArray

+
+boolean optimizeArgumentsArray
+
+
Provide formal names for elements of arguments array. +

+

+
+
+
+

+chainCalls

+
+boolean chainCalls
+
+
Chains calls to functions that return this. +

+

+
+
+
+

+variableRenaming

+
+VariableRenamingPolicy variableRenaming
+
+
Controls which variables get renamed. +

+

+
+
+
+

+propertyRenaming

+
+PropertyRenamingPolicy propertyRenaming
+
+
Controls which properties get renamed. +

+

+
+
+
+

+propertyAffinity

+
+boolean propertyAffinity
+
+
Should we use affinity information when generating property names. +

+

+
+
+
+

+labelRenaming

+
+boolean labelRenaming
+
+
Controls label renaming. +

+

+
+
+
+

+reserveRawExports

+
+boolean reserveRawExports
+
+
Reserve property names on the global this object. +

+

+
+
+
+

+shadowVariables

+
+boolean shadowVariables
+
+
Should shadow variable names in outer scope. +

+

+
+
+
+

+generatePseudoNames

+
+boolean generatePseudoNames
+
+
Generate pseudo names for variables and properties for debugging purposes. +

+

+
+
+
+

+renamePrefix

+
+String renamePrefix
+
+
Specifies a prefix for all globals +

+

+
+
+
+

+renamePrefixNamespace

+
+String renamePrefixNamespace
+
+
Specifies the name of an object that will be used to store all non-extern + globals. +

+

+
+
+
+

+aliasKeywords

+
+boolean aliasKeywords
+
+
Aliases true, false, and null to variables with shorter names. +

+

+
+
+
+

+collapseProperties

+
+boolean collapseProperties
+
+
Flattens multi-level property names (e.g. a$b = x) +

+

+
+
+
+

+collapseObjectLiterals

+
+boolean collapseObjectLiterals
+
+
Split object literals into individual variables when possible. +

+

+
+
+
+

+collapsePropertiesOnExternTypes

+
+boolean collapsePropertiesOnExternTypes
+
+
Flattens multi-level property names on extern types (e.g. String$f = x) +

+

+
+
+
+

+devirtualizePrototypeMethods

+
+boolean devirtualizePrototypeMethods
+
+
Devirtualize prototype method by rewriting them to be static calls that + take the this pointer as their first argument +

+

+
+
+
+

+computeFunctionSideEffects

+
+boolean computeFunctionSideEffects
+
+
Use @nosideeffects annotations, function bodies and name graph + to determine if calls have side effects. Requires --check_types. +

+

+
+
+
+

+debugFunctionSideEffectsPath

+
+String debugFunctionSideEffectsPath
+
+
Where to save debug report for compute function side effects. +

+

+
+
+
+

+disambiguateProperties

+
+boolean disambiguateProperties
+
+
Rename properties to disambiguate between unrelated fields based on + type information. +

+

+
+
+
+

+ambiguateProperties

+
+boolean ambiguateProperties
+
+
Rename unrelated properties to the same name to reduce code size. +

+

+
+
+
+

+anonymousFunctionNaming

+
+AnonymousFunctionNamingPolicy anonymousFunctionNaming
+
+
Give anonymous functions names for easier debugging +

+

+
+
+
+

+inputVariableMapSerialized

+
+byte[] inputVariableMapSerialized
+
+
Serialized input variable renaming map. +

+

+
+
+
+

+inputPropertyMapSerialized

+
+byte[] inputPropertyMapSerialized
+
+
Serialized input property renaming map. +

+

+
+
+
+

+exportTestFunctions

+
+boolean exportTestFunctions
+
+
Whether to export test functions. +

+

+
+
+
+

+specializeInitialModule

+
+boolean specializeInitialModule
+
+
+
+
+
+

+runtimeTypeCheck

+
+boolean runtimeTypeCheck
+
+
Inserts runtime type assertions for debugging. +

+

+
+
+
+

+runtimeTypeCheckLogFunction

+
+String runtimeTypeCheckLogFunction
+
+
A JS function to be used for logging runtime type assertion + failures. It will be passed the warning as a string and the + faulty expression as arguments. +

+

+
+
+
+

+codingConvention

+
+CodingConvention codingConvention
+
+
A CodingConvention to use during the compile. +

+

+
+
+
+

+ignoreCajaProperties

+
+boolean ignoreCajaProperties
+
+
+
+
+
+

+syntheticBlockStartMarker

+
+String syntheticBlockStartMarker
+
+
+
+
+
+

+syntheticBlockEndMarker

+
+String syntheticBlockEndMarker
+
+
+
+
+
+

+locale

+
+String locale
+
+
Compiling locale +

+

+
+
+
+

+markAsCompiled

+
+boolean markAsCompiled
+
+
Sets the special "COMPILED" value to true +

+

+
+
+
+

+removeTryCatchFinally

+
+boolean removeTryCatchFinally
+
+
Removes try...catch...finally blocks for easier debugging +

+

+
+
+
+

+closurePass

+
+boolean closurePass
+
+
Processes goog.provide() and goog.require() calls +

+

+
+
+
+

+jqueryPass

+
+boolean jqueryPass
+
+
Processes jQuery aliases +

+

+
+
+
+

+rewriteNewDateGoogNow

+
+boolean rewriteNewDateGoogNow
+
+
Rewrite new Date(goog.now()) to new Date(). +

+

+
+
+
+

+removeAbstractMethods

+
+boolean removeAbstractMethods
+
+
Remove goog.abstractMethod assignments. +

+

+
+
+
+

+removeClosureAsserts

+
+boolean removeClosureAsserts
+
+
Remove goog.asserts calls. +

+

+
+
+
+

+gatherCssNames

+
+boolean gatherCssNames
+
+
Gather CSS names (requires closurePass) +

+

+
+
+
+

+stripTypes

+
+Set<E> stripTypes
+
+
Names of types to strip +

+

+
+
+
+

+stripNameSuffixes

+
+Set<E> stripNameSuffixes
+
+
Name suffixes that determine which variables and properties to strip +

+

+
+
+
+

+stripNamePrefixes

+
+Set<E> stripNamePrefixes
+
+
Name prefixes that determine which variables and properties to strip +

+

+
+
+
+

+stripTypePrefixes

+
+Set<E> stripTypePrefixes
+
+
Qualified type name prefixes that determine which types to strip +

+

+
+
+
+

+markNoSideEffectCalls

+
+boolean markNoSideEffectCalls
+
+
Mark no side effect calls +

+

+
+
+
+

+defineReplacements

+
+Map<K,V> defineReplacements
+
+
Replacements for @defines. Will be Boolean, Numbers, or Strings +

+

+
+
+
+

+tweakProcessing

+
+CompilerOptions.TweakProcessing tweakProcessing
+
+
What kind of processing to do for goog.tweak functions. +

+

+
+
+
+

+tweakReplacements

+
+Map<K,V> tweakReplacements
+
+
Replacements for tweaks. Will be Boolean, Numbers, or Strings +

+

+
+
+
+

+moveFunctionDeclarations

+
+boolean moveFunctionDeclarations
+
+
Move top level function declarations to the top +

+

+
+
+
+

+instrumentationTemplate

+
+String instrumentationTemplate
+
+
Instrumentation template to use with #recordFunctionInformation +

+

+
+
+
+

+appNameStr

+
+String appNameStr
+
+
+
+
+
+

+recordFunctionInformation

+
+boolean recordFunctionInformation
+
+
Record function information +

+

+
+
+
+

+generateExports

+
+boolean generateExports
+
+
+
+
+
+

+cssRenamingMap

+
+CssRenamingMap cssRenamingMap
+
+
Map used in the renaming of CSS class names. +

+

+
+
+
+

+processObjectPropertyString

+
+boolean processObjectPropertyString
+
+
Process instances of goog.testing.ObjectPropertyString. +

+

+
+
+
+

+replaceIdGenerators

+
+boolean replaceIdGenerators
+
+
Replace id generators +

+

+
+
+
+

+idGenerators

+
+Set<E> idGenerators
+
+
Id generators to replace. +

+

+
+
+
+

+replaceStringsFunctionDescriptions

+
+List<E> replaceStringsFunctionDescriptions
+
+
Configuration strings +

+

+
+
+
+

+replaceStringsPlaceholderToken

+
+String replaceStringsPlaceholderToken
+
+
+
+
+
+

+replaceStringsReservedStrings

+
+Set<E> replaceStringsReservedStrings
+
+
+
+
+
+

+propertyInvalidationErrors

+
+Map<K,V> propertyInvalidationErrors
+
+
List of properties that we report invalidation errors for. +

+

+
+
+
+

+transformAMDToCJSModules

+
+boolean transformAMDToCJSModules
+
+
Transform AMD to Common JS modules. +

+

+
+
+
+

+processCommonJSModules

+
+boolean processCommonJSModules
+
+
Rewrite Common JS modules so that they can be concatenated together. +

+

+
+
+
+

+commonJSModulePathPrefix

+
+String commonJSModulePathPrefix
+
+
Common JS module prefix. +

+

+
+
+
+

+prettyPrint

+
+boolean prettyPrint
+
+
Output in pretty indented format +

+

+
+
+
+

+lineBreak

+
+boolean lineBreak
+
+
Line break the output a bit more aggressively +

+

+
+
+
+

+preferLineBreakAtEndOfFile

+
+boolean preferLineBreakAtEndOfFile
+
+
Prefer line breaks at end of file +

+

+
+
+
+

+printInputDelimiter

+
+boolean printInputDelimiter
+
+
Prints a separator comment before each js script +

+

+
+
+
+

+inputDelimiter

+
+String inputDelimiter
+
+
The string to use as the separator for printInputDelimiter +

+

+
+
+
+

+reportPath

+
+String reportPath
+
+
+
+
+
+

+tracer

+
+CompilerOptions.TracerMode tracer
+
+
+
+
+
+

+colorizeErrorOutput

+
+boolean colorizeErrorOutput
+
+
+
+
+
+

+errorFormat

+
+ErrorFormat errorFormat
+
+
+
+
+
+

+warningsGuard

+
+ComposeWarningsGuard warningsGuard
+
+
+
+
+
+

+summaryDetailLevel

+
+int summaryDetailLevel
+
+
+
+
+
+

+lineLengthThreshold

+
+int lineLengthThreshold
+
+
+
+
+
+

+externExports

+
+boolean externExports
+
+
Whether the exports should be made available via Result after + compilation. This is implicitly true if CompilerOptions.externExportsPath is set. +

+

+
+
+
+

+externExportsPath

+
+String externExportsPath
+
+
The output path for the created externs file. +

+

+
+
+
+

+nameReferenceReportPath

+
+String nameReferenceReportPath
+
+
+
+
+
+

+nameReferenceGraphPath

+
+String nameReferenceGraphPath
+
+
+
+
+
+

+sourceMapOutputPath

+
+String sourceMapOutputPath
+
+
The output path for the source map. +

+

+
+
+
+

+sourceMapDetailLevel

+
+SourceMap.DetailLevel sourceMapDetailLevel
+
+
The detail level for the generated source map. +

+

+
+
+
+

+sourceMapFormat

+
+SourceMap.Format sourceMapFormat
+
+
The source map file format +

+

+
+
+
+

+sourceMapLocationMappings

+
+List<E> sourceMapLocationMappings
+
+
+
+
+
+

+outputCharset

+
+String outputCharset
+
+
Charset to use when generating code. If null, then output ASCII. + This needs to be a string because CompilerOptions is serializable. +

+

+
+
+
+

+looseTypes

+
+boolean looseTypes
+
+
Whether the named objects types included 'undefined' by default. +

+

+
+
+
+

+protectHiddenSideEffects

+
+boolean protectHiddenSideEffects
+
+
When set, assume that apparently side-effect free code is meaningful. +

+

+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.ComposeWarningsGuard extends WarningsGuard implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+orderOfAddition

+
+Map<K,V> orderOfAddition
+
+
+
+
+
+

+numberOfAdds

+
+int numberOfAdds
+
+
+
+
+
+

+guardComparator

+
+Comparator<T> guardComparator
+
+
+
+
+
+

+guards

+
+TreeSet<E> guards
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.DependencyOptions extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+sortDependencies

+
+boolean sortDependencies
+
+
+
+
+
+

+pruneDependencies

+
+boolean pruneDependencies
+
+
+
+
+
+

+dropMoochers

+
+boolean dropMoochers
+
+
+
+
+
+

+entryPoints

+
+Set<E> entryPoints
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.DiagnosticGroup extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+types

+
+Set<E> types
+
+
+
+
+
+

+name

+
+String name
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.DiagnosticGroupWarningsGuard extends WarningsGuard implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+group

+
+DiagnosticGroup group
+
+
+
+
+
+

+level

+
+CheckLevel level
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.DiagnosticType extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+key

+
+String key
+
+
The error type. Used as the BugPattern and BugInstance types by + BugBot's XML +

+

+
+
+
+

+format

+
+MessageFormat format
+
+
The default way to format errors +

+

+
+
+
+

+defaultLevel

+
+CheckLevel defaultLevel
+
+
Default level +

+

+
+
+
+

+level

+
+CheckLevel level
+
+
Reporting level, initially the defaultLevel but may be changed. +

+

+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.FunctionInformationMap extends com.google.protobuf.GeneratedMessage implements Serializable
+ +

+serialVersionUID: 0L + +

+ + + + + +
+Serialization Methods
+ +

+

+writeReplace

+
+protected Object writeReplace()
+                       throws ObjectStreamException
+
+
+ +
Throws: +
ObjectStreamException
+
+
+ + + + + +
+Serialized Fields
+ +

+entry_

+
+List<E> entry_
+
+
+
+
+
+

+module_

+
+List<E> module_
+
+
+
+
+
+

+memoizedIsInitialized

+
+byte memoizedIsInitialized
+
+
+
+
+
+

+memoizedSerializedSize

+
+int memoizedSerializedSize
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.FunctionInformationMap.Entry extends com.google.protobuf.GeneratedMessage implements Serializable
+ +

+serialVersionUID: 0L + +

+ + + + + +
+Serialization Methods
+ +

+

+writeReplace

+
+protected Object writeReplace()
+                       throws ObjectStreamException
+
+
+ +
Throws: +
ObjectStreamException
+
+
+ + + + + +
+Serialized Fields
+ +

+bitField0_

+
+int bitField0_
+
+
+
+
+
+

+id_

+
+int id_
+
+
+
+
+
+

+sourceName_

+
+Object sourceName_
+
+
+
+
+
+

+lineNumber_

+
+int lineNumber_
+
+
+
+
+
+

+moduleName_

+
+Object moduleName_
+
+
+
+
+
+

+size_

+
+int size_
+
+
+
+
+
+

+name_

+
+Object name_
+
+
+
+
+
+

+compiledSource_

+
+Object compiledSource_
+
+
+
+
+
+

+memoizedIsInitialized

+
+byte memoizedIsInitialized
+
+
+
+
+
+

+memoizedSerializedSize

+
+int memoizedSerializedSize
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.FunctionInformationMap.Module extends com.google.protobuf.GeneratedMessage implements Serializable
+ +

+serialVersionUID: 0L + +

+ + + + + +
+Serialization Methods
+ +

+

+writeReplace

+
+protected Object writeReplace()
+                       throws ObjectStreamException
+
+
+ +
Throws: +
ObjectStreamException
+
+
+ + + + + +
+Serialized Fields
+ +

+bitField0_

+
+int bitField0_
+
+
+
+
+
+

+name_

+
+Object name_
+
+
+
+
+
+

+compiledSource_

+
+Object compiledSource_
+
+
+
+
+
+

+memoizedIsInitialized

+
+byte memoizedIsInitialized
+
+
+
+
+
+

+memoizedSerializedSize

+
+int memoizedSerializedSize
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.GoogleCodingConvention extends CodingConventions.Proxy implements Serializable
+ +

+serialVersionUID: 1L + +

+ +

+ + + + + +
+Class com.google.javascript.jscomp.Instrumentation extends com.google.protobuf.GeneratedMessage implements Serializable
+ +

+serialVersionUID: 0L + +

+ + + + + +
+Serialization Methods
+ +

+

+writeReplace

+
+protected Object writeReplace()
+                       throws ObjectStreamException
+
+
+ +
Throws: +
ObjectStreamException
+
+
+ + + + + +
+Serialized Fields
+ +

+bitField0_

+
+int bitField0_
+
+
+
+
+
+

+reportDefined_

+
+Object reportDefined_
+
+
+
+
+
+

+reportCall_

+
+Object reportCall_
+
+
+
+
+
+

+reportExit_

+
+Object reportExit_
+
+
+
+
+
+

+declarationToRemove_

+
+com.google.protobuf.LazyStringList declarationToRemove_
+
+
+
+
+
+

+init_

+
+com.google.protobuf.LazyStringList init_
+
+
+
+
+
+

+appNameSetter_

+
+Object appNameSetter_
+
+
+
+
+
+

+memoizedIsInitialized

+
+byte memoizedIsInitialized
+
+
+
+
+
+

+memoizedSerializedSize

+
+int memoizedSerializedSize
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.JqueryCodingConvention extends CodingConventions.Proxy implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+propertyTestFunctions

+
+Set<E> propertyTestFunctions
+
+
+
+
+
+

+prototypeAliases

+
+Set<E> prototypeAliases
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.JsAst extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+fileName

+
+String fileName
+
+
+
+
+
+

+root

+
+Node root
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.JSModule extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+name

+
+String name
+
+
Module name +

+

+
+
+
+

+inputs

+
+List<E> inputs
+
+
Source code inputs +

+

+
+
+
+

+deps

+
+List<E> deps
+
+
Modules that this module depends on +

+

+
+
+
+

+depth

+
+int depth
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.JSModuleGraph.ModuleDependenceException extends IllegalArgumentException implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+module

+
+JSModule module
+
+
+
+
+
+

+dependentModule

+
+JSModule dependentModule
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.JSSourceFile extends SourceFile implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+referenced

+
+SourceFile referenced
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.ShowByPathWarningsGuard extends WarningsGuard implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+paths

+
+String[] paths
+
+
+
+
+
+

+showType

+
+ShowByPathWarningsGuard.ShowType showType
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.SourceFile extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+fileName

+
+String fileName
+
+
+
+
+
+

+isExternFile

+
+boolean isExternFile
+
+
+
+
+
+

+originalPath

+
+String originalPath
+
+
+
+
+
+

+lineOffsets

+
+int[] lineOffsets
+
+
+
+
+
+

+lastOffset

+
+int lastOffset
+
+
+
+
+
+

+lastLine

+
+int lastLine
+
+
+
+
+
+

+code

+
+String code
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.StrictWarningsGuard extends WarningsGuard implements Serializable
+ +

+serialVersionUID: 1L + +

+ +

+ + + + + +
+Class com.google.javascript.jscomp.SymbolTable.Symbol extends SimpleSlot implements Serializable
+ +

+ + + + + +
+Serialized Fields
+ +

+references

+
+Map<K,V> references
+
+
+
+
+
+

+scope

+
+SymbolTable.SymbolScope scope
+
+
+
+
+
+

+propertyScope

+
+SymbolTable.SymbolScope propertyScope
+
+
+
+
+
+

+declaration

+
+SymbolTable.Reference declaration
+
+
+
+
+
+

+docInfo

+
+JSDocInfo docInfo
+
+
+
+
+
+

+docScope

+
+SymbolTable.SymbolScope docScope
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.SyntheticAst extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+inputId

+
+InputId inputId
+
+
+
+
+
+

+sourceFile

+
+SourceFile sourceFile
+
+
+
+
+
+

+root

+
+Node root
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.jscomp.WarningsGuard extends Object implements Serializable
+ +

+ +

+ + + + + +
+Class com.google.javascript.jscomp.WhitelistWarningsGuard extends WarningsGuard implements Serializable
+ +

+ + + + + +
+Serialized Fields
+ +

+whiteList

+
+Set<E> whiteList
+
+
The set of white-listed warnings, same format as formatWarning. +

+

+
+
+
+ + + + + +
+Package com.google.javascript.jscomp.deps
+ +

+ + + + + +
+Class com.google.javascript.jscomp.deps.SortedDependencies.CircularDependencyException extends Exception implements Serializable
+ +

+ +

+ + + + + +
+Class com.google.javascript.jscomp.deps.SortedDependencies.MissingProvideException extends Exception implements Serializable
+ +

+


+ + + + + +
+Package com.google.javascript.jscomp.graph
+ +

+ + + + + +
+Class com.google.javascript.jscomp.graph.StandardUnionFind extends Object implements Serializable
+ +

+serialVersionUID: -1L + +

+ + + + + +
+Serialized Fields
+ +

+elmap

+
+Map<K,V> elmap
+
+
All values with the same root node are in the same equivalence set. +

+

+
+
+
+ + + + + +
+Package com.google.javascript.jscomp.jsonml
+ +

+ + + + + +
+Class com.google.javascript.jscomp.jsonml.JsonMLAst extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+jsonml

+
+JsonML jsonml
+
+
+
+
+
+

+root

+
+Node root
+
+
+
+
+
+

+sourceFile

+
+SourceFile sourceFile
+
+
+
+
+
+

+inputId

+
+InputId inputId
+
+
+
+
+
+ + + + + +
+Package com.google.javascript.rhino
+ +

+ + + + + +
+Class com.google.javascript.rhino.InputId extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+id

+
+String id
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.rhino.JSDocInfo extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+info

+
+com.google.javascript.rhino.JSDocInfo.LazilyInitializedInfo info
+
+
+
+
+
+

+documentation

+
+com.google.javascript.rhino.JSDocInfo.LazilyInitializedDocumentation documentation
+
+
+
+
+
+

+associatedNode

+
+Node associatedNode
+
+
+
+
+
+

+visibility

+
+JSDocInfo.Visibility visibility
+
+
+
+
+
+

+bitset

+
+int bitset
+
+
The JSDocInfo.isConstant(), JSDocInfo.isConstructor(), JSDocInfo.isInterface(), + JSDocInfo.isHidden() and JSDocInfo.shouldPreserveTry() flags as well as + whether the JSDocInfo.type field stores a value for JSDocInfo.getType(), + JSDocInfo.getReturnType() or JSDocInfo.getEnumParameterType(). +

+

+
See Also:
JSDocInfo.setFlag(boolean, int), +JSDocInfo.getFlag(int), +JSDocInfo.setType(JSTypeExpression, int), +JSDocInfo.getType(int)
+
+
+

+type

+
+JSTypeExpression type
+
+
The type for JSDocInfo.getType(), JSDocInfo.getReturnType() or + JSDocInfo.getEnumParameterType(). The knowledge of which one is recorded is + stored in the JSDocInfo.bitset field. +

+

+
See Also:
JSDocInfo.setType(JSTypeExpression, int), +JSDocInfo.getType(int)
+
+
+

+thisType

+
+JSTypeExpression thisType
+
+
The type for JSDocInfo.getThisType(). +

+

+
+
+
+

+includeDocumentation

+
+boolean includeDocumentation
+
+
Whether to include documentation. +

+

+
See Also:
JSDocInfo.LazilyInitializedDocumentation
+
+ +

+ + + + + +
+Class com.google.javascript.rhino.JSTypeExpression extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+root

+
+Node root
+
+
The root of the AST. +

+

+
+
+
+

+sourceName

+
+String sourceName
+
+
The source name where the type expression appears. +

+

+
+
+ +

+ + + + + +
+Class com.google.javascript.rhino.Node extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+type

+
+int type
+
+
+
+
+
+

+next

+
+Node next
+
+
+
+
+
+

+first

+
+Node first
+
+
+
+
+
+

+last

+
+Node last
+
+
+
+
+
+

+propListHead

+
+com.google.javascript.rhino.Node.PropListItem propListHead
+
+
Linked list of properties. Since vast majority of nodes would have + no more then 2 properties, linked list saves memory and provides + fast lookup. If this does not holds, propListHead can be replaced + by UintMap. +

+

+
+
+
+

+sourcePosition

+
+int sourcePosition
+
+
Source position of this node. The position is encoded with the + column number in the low 12 bits of the integer, and the line + number in the rest. Create some handy constants so we can change this + size if we want. +

+

+
+
+
+

+jsType

+
+JSType jsType
+
+
+
+
+
+

+parent

+
+Node parent
+
+
+
+
+
+ + + + + +
+Package com.google.javascript.rhino.jstype
+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.AllType extends JSType implements Serializable
+ +

+serialVersionUID: 1L + +

+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.BooleanType extends com.google.javascript.rhino.jstype.ValueType implements Serializable
+ +

+serialVersionUID: 1L + +

+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.EnumElementType extends ObjectType implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+primitiveType

+
+JSType primitiveType
+
+
The primitive type this enum element type wraps. For instance, in + the following code defining the LOCAL_CODES enum +
var LOCAL_CODES = {A: 3, B: 9, C: 8}
+ the primitive type of the the constants is number. +

+

+
+
+
+

+primitiveObjectType

+
+ObjectType primitiveObjectType
+
+
+
+
+
+

+name

+
+String name
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.EnumType extends com.google.javascript.rhino.jstype.PrototypeObjectType implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+source

+
+Node source
+
+
The object literal or alias which this type represents. + It may be null. +

+

+
+
+
+

+elementsType

+
+EnumElementType elementsType
+
+
+
+
+
+

+elements

+
+Set<E> elements
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.FunctionType extends com.google.javascript.rhino.jstype.PrototypeObjectType implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+call

+
+com.google.javascript.rhino.jstype.ArrowType call
+
+
[[Call]] property. +

+

+
+
+
+

+prototypeSlot

+
+ObjectType.Property prototypeSlot
+
+
The prototype property. This field is lazily initialized by + #getPrototype(). The most important reason for lazily + initializing this field is that there are cycles in the native types + graph, so some prototypes must temporarily be null during + the construction of the graph. + + If non-null, the type must be a PrototypeObjectType. +

+

+
+
+
+

+kind

+
+com.google.javascript.rhino.jstype.FunctionType.Kind kind
+
+
Whether a function is a constructor, an interface, or just an ordinary + function. +

+

+
+
+
+

+typeOfThis

+
+ObjectType typeOfThis
+
+
The type of this in the scope of this function. +

+

+
+
+
+

+source

+
+Node source
+
+
The function node which this type represents. It may be null. +

+

+
+
+
+

+implementedInterfaces

+
+List<E> implementedInterfaces
+
+
The interfaces directly implemented by this function (for constructors) + It is only relevant for constructors. May not be null. +

+

+
+
+
+

+extendedInterfaces

+
+List<E> extendedInterfaces
+
+
The interfaces directly extendeded by this function (for interfaces) + It is only relevant for constructors. May not be null. +

+

+
+
+
+

+subTypes

+
+List<E> subTypes
+
+
The types which are subtypes of this function. It is only relevant for + constructors and may be null. +

+

+
+
+
+

+templateTypeName

+
+String templateTypeName
+
+
The template type name. May be null. +

+

+
+
+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.JSType extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+resolved

+
+boolean resolved
+
+
+
+
+
+

+resolveResult

+
+JSType resolveResult
+
+
+
+
+
+

+registry

+
+JSTypeRegistry registry
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.JSTypeRegistry extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+nativeTypes

+
+JSType[] nativeTypes
+
+
+
+
+
+

+namesToTypes

+
+Map<K,V> namesToTypes
+
+
+
+
+
+

+namespaces

+
+Set<E> namespaces
+
+
+
+
+
+

+nonNullableTypeNames

+
+Set<E> nonNullableTypeNames
+
+
+
+
+
+

+forwardDeclaredTypes

+
+Set<E> forwardDeclaredTypes
+
+
+
+
+
+

+typesIndexedByProperty

+
+Map<K,V> typesIndexedByProperty
+
+
+
+
+
+

+eachRefTypeIndexedByProperty

+
+Map<K,V> eachRefTypeIndexedByProperty
+
+
+
+
+
+

+greatestSubtypeByProperty

+
+Map<K,V> greatestSubtypeByProperty
+
+
+
+
+
+

+interfaceToImplementors

+
+com.google.common.collect.Multimap<K,V> interfaceToImplementors
+
+
+
+
+
+

+unresolvedNamedTypes

+
+com.google.common.collect.Multimap<K,V> unresolvedNamedTypes
+
+
+
+
+
+

+resolvedNamedTypes

+
+com.google.common.collect.Multimap<K,V> resolvedNamedTypes
+
+
+
+
+
+

+lastGeneration

+
+boolean lastGeneration
+
+
+
+
+
+

+templateTypeName

+
+String templateTypeName
+
+
+
+
+
+

+templateType

+
+TemplateType templateType
+
+
+
+
+
+

+tolerateUndefinedValues

+
+boolean tolerateUndefinedValues
+
+
+
+
+
+

+resolveMode

+
+JSTypeRegistry.ResolveMode resolveMode
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.NoObjectType extends FunctionType implements Serializable
+ +

+serialVersionUID: 1L + +

+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.NoType extends NoObjectType implements Serializable
+ +

+serialVersionUID: 1L + +

+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.NullType extends com.google.javascript.rhino.jstype.ValueType implements Serializable
+ +

+serialVersionUID: 1L + +

+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.NumberType extends com.google.javascript.rhino.jstype.ValueType implements Serializable
+ +

+serialVersionUID: 1L + +

+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.ObjectType extends JSType implements Serializable
+ +

+ + + + + +
+Serialized Fields
+ +

+visited

+
+boolean visited
+
+
+
+
+
+

+docInfo

+
+JSDocInfo docInfo
+
+
+
+
+
+

+unknown

+
+boolean unknown
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.ObjectType.Property extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+name

+
+String name
+
+
Property's name. +

+

+
+
+
+

+type

+
+JSType type
+
+
Property's type. +

+

+
+
+
+

+inferred

+
+boolean inferred
+
+
Whether the property's type is inferred. +

+

+
+
+
+

+propertyNode

+
+Node propertyNode
+
+
The node corresponding to this property, e.g., a GETPROP node that + declares this property. +

+

+
+
+
+

+docInfo

+
+JSDocInfo docInfo
+
+
The JSDocInfo for this property. +

+

+
+
+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.SimpleSlot extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+name

+
+String name
+
+
+
+
+
+

+type

+
+JSType type
+
+
+
+
+
+

+inferred

+
+boolean inferred
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.StringType extends com.google.javascript.rhino.jstype.ValueType implements Serializable
+ +

+serialVersionUID: 1L + +

+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.TemplateType extends com.google.javascript.rhino.jstype.ProxyObjectType implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+name

+
+String name
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.UnionType extends JSType implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+alternates

+
+Collection<E> alternates
+
+
+
+
+
+

+hashcode

+
+int hashcode
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.UnknownType extends ObjectType implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+isChecked

+
+boolean isChecked
+
+
+
+
+ +

+ + + + + +
+Class com.google.javascript.rhino.jstype.VoidType extends com.google.javascript.rhino.jstype.ValueType implements Serializable
+ +

+serialVersionUID: 1L + +

+ +

+


+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + + diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/Base64.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/Base64.java new file mode 100644 index 0000000..d05f2be --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/Base64.java @@ -0,0 +1,81 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import java.util.Arrays; + +/** + * A utility class for working with Base64 values. + * @author johnlenz@google.com (John Lenz) + */ +public final class Base64 { + + // This is a utility class + private Base64() {} + + /** + * A map used to convert integer values in the range 0-63 to their base64 + * values. + */ + private static final String BASE64_MAP = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "abcdefghijklmnopqrstuvwxyz" + + "0123456789+/"; + + /** + * A map used to convert base64 character into integer values. + */ + private static final int[] BASE64_DECODE_MAP = new int[256]; + static { + Arrays.fill(BASE64_DECODE_MAP, -1); + for (int i = 0; i < BASE64_MAP.length(); i++) { + BASE64_DECODE_MAP[BASE64_MAP.charAt(i)] = i; + } + } + + /** + * @param value A value in the range of 0-63. + * @return a base64 digit. + */ + public static char toBase64(int value) { + assert (value <= 63 && value >= 0) : "value out of range:" + value; + return BASE64_MAP.charAt(value); + } + + /** + * @param c A base64 digit. + * @return A value in the range of 0-63. + */ + public static int fromBase64(char c) { + int result = BASE64_DECODE_MAP[c]; + assert (result != -1) : "invalid char"; + return BASE64_DECODE_MAP[c]; + } + + /** + * @param value an integer to base64 encode. + * @return the six digit long base64 encoded value of the integer. + */ + public static String base64EncodeInt(int value) { + char[] c = new char[6]; + for (int i = 0; i < 5; i++) { + c[i] = Base64.toBase64((value >> (26 - i * 6)) & 0x3f); + } + c[5] = Base64.toBase64((value << 4) & 0x3f); + return new String(c); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/Base64VLQ.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/Base64VLQ.java new file mode 100644 index 0000000..bb9b616 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/Base64VLQ.java @@ -0,0 +1,127 @@ +/* + * Copyright 2011 The Closure Compiler Authors. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.debugging.sourcemap; + +import java.io.IOException; + +/** + * We encode our variable length numbers as base64 encoded strings with + * the least significant digit coming first. Each base64 digit encodes + * a 5-bit value (0-31) and a continuation bit. Signed values can be + * represented by using the least significant bit of the value as the + * sign bit. + * + * @author johnlenz@google.com (John Lenz) + */ +final class Base64VLQ { + // Utility class. + private Base64VLQ() {} + + // A Base64 VLQ digit can represent 5 bits, so it is base-32. + private static final int VLQ_BASE_SHIFT = 5; + private static final int VLQ_BASE = 1 << VLQ_BASE_SHIFT; + + // A mask of bits for a VLQ digit (11111), 31 decimal. + private static final int VLQ_BASE_MASK = VLQ_BASE-1; + + // The continuation bit is the 6th bit. + private static final int VLQ_CONTINUATION_BIT = VLQ_BASE; + + /** + * Converts from a two-complement value to a value where the sign bit is + * is placed in the least significant bit. For example, as decimals: + * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) + * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) + */ + private static int toVLQSigned(int value) { + if (value < 0) { + return ((-value) << 1) + 1; + } else { + return (value << 1) + 0; + } + } + + /** + * Converts to a two-complement value from a value where the sign bit is + * is placed in the least significant bit. For example, as decimals: + * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 + * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 + */ + private static int fromVLQSigned(int value) { + boolean negate = (value & 1) == 1; + value = value >> 1; + return negate ? -value : value; + } + + /** + * Writes a VLQ encoded value to the provide appendable. + * @throws IOException + */ + public static void encode(Appendable out, int value) + throws IOException { + value = toVLQSigned(value); + do { + int digit = value & VLQ_BASE_MASK; + value >>>= VLQ_BASE_SHIFT; + if (value > 0) { + digit |= VLQ_CONTINUATION_BIT; + } + out.append(Base64.toBase64(digit)); + } while (value > 0); + } + + /** + * A simple interface for advancing through a sequence of characters, that + * communicates that advance back to the source. + */ + interface CharIterator { + boolean hasNext(); + char next(); + } + + /** + * Decodes the next VLQValue from the provided CharIterator. + */ + public static int decode(CharIterator in) { + int result = 0; + boolean continuation; + int shift = 0; + do { + char c = in.next(); + int digit = Base64.fromBase64(c); + continuation = (digit & VLQ_CONTINUATION_BIT) != 0; + digit &= VLQ_BASE_MASK; + result = result + (digit << shift); + shift = shift + VLQ_BASE_SHIFT; + } while (continuation); + + return fromVLQSigned(result); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/FilePosition.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/FilePosition.java new file mode 100644 index 0000000..f84320a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/FilePosition.java @@ -0,0 +1,49 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +/** + * Represents a position in a source file. + * + */ +public class FilePosition { + private final int line; + private final int column; + + public FilePosition(int line, int column) { + this.line = line; + this.column = column; + } + + /** + * Returns the line number of this position. + * Note: The v1 and v2 source maps use a line number with the first line + * being 1, whereas the v3 source map corrects this and uses a first line + * number of 0 to be consistent with the column representation. + */ + public int getLine() { + return line; + } + + /** + * @return the character index on the line + * of this position, with the first column being 0. + */ + public int getColumn() { + return column; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapConsumer.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapConsumer.java new file mode 100644 index 0000000..61bcb97 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapConsumer.java @@ -0,0 +1,32 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +/** + * A SourceMapConsumer is a SourceMapping provide that can parse from a raw + * string. + * + * @author johnlenz@google.com (John Lenz) + */ +public interface SourceMapConsumer extends SourceMapping { + + /** + * Parses the given contents containing a source map to provide initialize + * a class providing SourceMapping. + */ + public void parse(String contents) throws SourceMapParseException; +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapConsumerFactory.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapConsumerFactory.java new file mode 100644 index 0000000..9edb224 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapConsumerFactory.java @@ -0,0 +1,82 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Detect and parse the provided source map. + * @author johnlenz@google.com (John Lenz) + */ +public class SourceMapConsumerFactory { + + /** not constructible */ + private SourceMapConsumerFactory() {} + + /** + * @param contents The string representing the source map file contents. + * @return The parsed source map. + * @throws SourceMapParseException + */ + public static SourceMapping parse(String contents) + throws SourceMapParseException { + return parse(contents, null); + } + + /** + * @param contents The string representing the source map file contents. + * @param supplier A supplier for any referenced maps. + * @return The parsed source map. + * @throws SourceMapParseException + */ + public static SourceMapping parse(String contents, SourceMapSupplier supplier) + throws SourceMapParseException { + // Version 1, starts with a magic string + if (contents.startsWith("/** Begin line maps. **/")) { + SourceMapConsumerV1 consumer = new SourceMapConsumerV1(); + consumer.parse(contents); + return consumer; + } else if (contents.startsWith("{")){ + try { + // Revision 2 and 3, are JSON Objects + JSONObject sourceMapRoot = new JSONObject(contents); + // Check basic assertions about the format. + int version = sourceMapRoot.getInt("version"); + switch (version) { + case 2: { + SourceMapConsumerV2 consumer = new SourceMapConsumerV2(); + consumer.parse(sourceMapRoot); + return consumer; + } + case 3: { + SourceMapConsumerV3 consumer = new SourceMapConsumerV3(); + consumer.parse(sourceMapRoot, supplier); + return consumer; + } + default: + throw new SourceMapParseException( + "Unknown source map version:" + version); + } + } catch (JSONException ex) { + throw new SourceMapParseException("JSON parse exception: " + ex); + } + } + + throw new SourceMapParseException("unable to detect source map format"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapConsumerV1.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapConsumerV1.java new file mode 100644 index 0000000..8bd554f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapConsumerV1.java @@ -0,0 +1,601 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Interner; +import com.google.common.collect.Interners; +import com.google.common.collect.Lists; +import com.google.common.primitives.Bytes; +import com.google.common.primitives.Shorts; +import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class for parsing and representing a SourceMap, as produced by the + * Closure Compiler, Caja-Compiler, etc. + */ +public class SourceMapConsumerV1 implements SourceMapConsumer { + private static final String LINEMAP_HEADER = "/** Begin line maps. **/"; + private static final String FILEINFO_HEADER = + "/** Begin file information. **/"; + + private static final String DEFINITION_HEADER = + "/** Begin mapping definitions. **/"; + + /** + * Internal class for parsing the SourceMap. Used to maintain parser + * state in an easy to use instance. + */ + private static class ParseState { + final String contents; + int currentPosition = 0; + + ParseState(String contents) { + this.contents = contents; + } + + /** Reads a line, returning null at EOF. */ + String readLineOrNull() { + if (currentPosition >= contents.length()) { + return null; + } + int index = contents.indexOf('\n', currentPosition); + if (index < 0) { + index = contents.length(); + } + String line = contents.substring(currentPosition, index); + currentPosition = index + 1; + return line; + } + + /** Reads a line, throwing a parse exception at EOF. */ + String readLine() throws SourceMapParseException { + String line = readLineOrNull(); + if (line == null) { + fail("EOF"); + } + return line; + } + + /** + * Reads a line and throws an parse exception if the line does not + * equal the argument. + */ + void expectLine(String expect) throws SourceMapParseException { + String line = readLine(); + if (!expect.equals(line)) { + fail("Expected " + expect + " got " + line); + } + } + + /** + * Indicates that parsing has failed by throwing a parse exception. + */ + void fail(String message) throws SourceMapParseException { + throw new SourceMapParseException(message); + } + } + + /** + * Mapping from a line number (0-indexed), to a list of mapping IDs, one for + * each character on the line. For example, if the array for line 2 is + * {@code [4,,,,5,6,,,7]}, then there will be the entry: + * + *
+   * 1 => {4, 4, 4, 4, 5, 6, 6, 6, 7}
+   * 
+ */ + private ImmutableList> characterMap; + + /** + * Map of Mapping IDs to the actual mapping object. + */ + private ImmutableList mappings; + + /** + * Parses the given contents containing a source map. + */ + @Override + public void parse(String contents) throws SourceMapParseException { + ParseState parser = new ParseState(contents); + try { + parseInternal(parser); + } catch (JSONException ex) { + parser.fail("JSON parse exception: " + ex); + } + } + + /** + * Parses the first section of the source map file that has character + * mappings. + * @param parser The parser to use + * @param lineCount The number of lines in the generated JS + * @return The max id found in the file + */ + private int parseCharacterMap( + ParseState parser, int lineCount, + ImmutableList.Builder> characterMapBuilder) + throws SourceMapParseException, JSONException { + int maxID = -1; + // [0,,,,,,1,2] + for (int i = 0; i < lineCount; ++i) { + String currentLine = parser.readLine(); + + // Blank lines are allowed in the spec to indicate no mapping + // information for the line. + if (currentLine.isEmpty()) { + continue; + } + + ImmutableList.Builder fragmentList = + ImmutableList.builder(); + // We need the start index to initialize this, needs to be done in the + // loop. + LineFragment myLineFragment = null; + + JSONArray charArray = new JSONArray(currentLine); + int numOffsets = charArray.length(); + int lastID = -1; + int startID = Integer.MIN_VALUE; + List currentOffsets = Lists.newArrayList(); + for (int j = 0; j < charArray.length(); ++j) { + // Keep track of the current mappingID, if the next element in the + // array is empty we reuse the existing mappingID for the column. + int mappingID = lastID; + if (!charArray.isNull(j)) { + mappingID = charArray.optInt(j); + if (mappingID > maxID) { + maxID = mappingID; + } + } + + if (startID == Integer.MIN_VALUE) { + startID = mappingID; + } else { + // If the difference is bigger than a byte we need to keep track of + // a new line fragment with a new start value. + if (mappingID - lastID > Byte.MAX_VALUE + || mappingID - lastID < Byte.MIN_VALUE) { + myLineFragment = new LineFragment( + startID, Bytes.toArray(currentOffsets)); + currentOffsets.clear(); + // Start a new section. + fragmentList.add(myLineFragment); + startID = mappingID; + } else { + currentOffsets.add((byte) (mappingID - lastID)); + } + } + + lastID = mappingID; + } + if (startID != Integer.MIN_VALUE) { + myLineFragment = new LineFragment( + startID, Bytes.toArray(currentOffsets)); + fragmentList.add(myLineFragment); + } + characterMapBuilder.add(fragmentList.build()); + } + return maxID; + } + + private class FileName { + private final String dir; + private final String name; + + FileName(String directory, String name) { + this.dir = directory; + this.name = name; + } + } + + /** + * Split the file into a filename/directory pair. + * + * @param interner The interner to use for interning the strings. + * @param input The input to split. + * @return The pair of directory, filename. + */ + private FileName splitFileName( + Interner interner, String input) { + int hashIndex = input.lastIndexOf('/'); + String dir = interner.intern(input.substring(0, hashIndex + 1)); + String fileName = interner.intern(input.substring(hashIndex + 1)); + return new FileName(dir, fileName); + } + + /** + * Parse the file mappings section of the source map file. This maps the + * ids to the filename, line number and column number in the original + * files. + * @param parser The parser to get the data from. + * @param maxID The maximum id found in the character mapping section. + */ + private void parseFileMappings(ParseState parser, int maxID) + throws SourceMapParseException, JSONException { + // ['d.js', 3, 78, 'foo'] + // Intern the strings to save memory. + Interner interner = Interners.newStrongInterner(); + ImmutableList.Builder mappingsBuilder = ImmutableList.builder(); + + // Setup all the arrays to keep track of the various details about the + // source file. + ArrayList lineOffsets = Lists.newArrayList(); + ArrayList columns = Lists.newArrayList(); + ArrayList identifiers = Lists.newArrayList(); + + // The indexes and details about the current position in the file to do + // diffs against. + String currentFile = null; + int lastLine = -1; + int startLine = -1; + int startMapId = -1; + for (int mappingId = 0; mappingId <= maxID; ++mappingId) { + String currentLine = parser.readLine(); + JSONArray mapArray = new JSONArray(currentLine); + if (mapArray.length() < 3) { + parser.fail("Invalid mapping array"); + } + + // Split up the file and directory names to reduce memory usage. + String myFile = mapArray.getString(0); + int line = mapArray.getInt(1); + if (!myFile.equals(currentFile) || (line - lastLine) > Byte.MAX_VALUE + || (line - lastLine) < Byte.MIN_VALUE) { + if (currentFile != null) { + FileName dirFile = splitFileName(interner, currentFile); + SourceFile.Builder builder = SourceFile.newBuilder() + .setDir(dirFile.dir) + .setFileName(dirFile.name) + .setStartLine(startLine) + .setStartMapId(startMapId) + .setLineOffsets(lineOffsets) + .setColumns(columns) + .setIdentifiers(identifiers); + mappingsBuilder.add(builder.build()); + } + // Reset all the positions back to the start and clear out the arrays + // to start afresh. + currentFile = myFile; + startLine = line; + lastLine = line; + startMapId = mappingId; + columns.clear(); + lineOffsets.clear(); + identifiers.clear(); + } + // We need to add on the columns and identifiers for all the lines, even + // for the first line. + lineOffsets.add((byte) (line - lastLine)); + columns.add((short) mapArray.getInt(2)); + identifiers.add(interner.intern(mapArray.optString(3, ""))); + lastLine = line; + } + if (currentFile != null) { + FileName dirFile = splitFileName(interner, currentFile); + SourceFile.Builder builder = SourceFile.newBuilder() + .setDir(dirFile.dir) + .setFileName(dirFile.name) + .setStartLine(startLine) + .setStartMapId(startMapId) + .setLineOffsets(lineOffsets) + .setColumns(columns) + .setIdentifiers(identifiers); + mappingsBuilder.add(builder.build()); + } + mappings = mappingsBuilder.build(); + } + + private void parseInternal(ParseState parser) + throws SourceMapParseException, JSONException { + + // /** Begin line maps. **/{ count: 2 } + String headerCount = parser.readLine(); + Preconditions.checkArgument(headerCount.startsWith(LINEMAP_HEADER), + "Expected %s", LINEMAP_HEADER); + JSONObject countObject = new JSONObject( + headerCount.substring(LINEMAP_HEADER.length())); + if (!countObject.has("count")) { + parser.fail("Missing 'count'"); + } + + int lineCount = countObject.getInt("count"); + if (lineCount <= 0) { + parser.fail("Count must be >= 1"); + } + ImmutableList.Builder> characterMapBuilder = + ImmutableList.builder(); + int maxId = parseCharacterMap(parser, lineCount, characterMapBuilder); + characterMap = characterMapBuilder.build(); + + // /** Begin file information. **/ + parser.expectLine(FILEINFO_HEADER); + + // File information. Not used, so we just consume it. + for (int i = 0; i < lineCount; i++) { + parser.readLine(); + } + + // /** Begin mapping definitions. **/ + parser.expectLine(DEFINITION_HEADER); + + parseFileMappings(parser, maxId); + } + + @Override + public OriginalMapping getMappingForLine(int lineNumber, int columnIndex) { + Preconditions.checkNotNull(characterMap, "parse() must be called first"); + + if (lineNumber < 1 || lineNumber > characterMap.size() || columnIndex < 1) { + return null; + } + + List lineFragments = characterMap.get(lineNumber - 1); + if (lineFragments == null || lineFragments.isEmpty()) { + return null; + } + + int columnOffset = 0; + // The code assumes everything past the end is the same as the last item + // so we default to the last item in the line. + LineFragment lastFragment = lineFragments.get(lineFragments.size() - 1); + int mapId = lastFragment.valueAtColumn(lastFragment.length()); + for (LineFragment lineFragment : lineFragments) { + int columnPosition = columnIndex - columnOffset; + if (columnPosition <= lineFragment.length()) { + mapId = lineFragment.valueAtColumn(columnPosition); + break; + } + columnOffset += lineFragment.length(); + } + + if (mapId < 0) { + return null; + } + + return getMappingFromId(mapId); + } + + /** + * Do a binary search for the correct mapping array to use. + * + * @param mapId The mapping array to find + * @return The source file mapping to use. + */ + private SourceFile binarySearch(int mapId) { + int lower = 0; + int upper = mappings.size() - 1; + + while (lower <= upper) { + int middle = lower + (upper - lower) / 2; + SourceFile middleCompare = mappings.get(middle); + if (mapId < middleCompare.getStartMapId()) { + upper = middle - 1; + } else if (mapId < (middleCompare.getStartMapId() + + middleCompare.getLength())) { + return middleCompare; + } else { + lower = middle + 1; + } + } + + return null; + } + + /** + * Find the original mapping for the specified mapping id. + * + * @param mapID The mapID to lookup. + * @return The originalMapping protocol buffer for the id. + */ + private OriginalMapping getMappingFromId(int mapID) { + SourceFile match = binarySearch(mapID); + if (match == null) { + return null; + } + int pos = mapID - match.getStartMapId(); + return match.getOriginalMapping(pos); + } + + /** + * Keeps track of the information about the line in a more compact way. It + * represents a fragment of the line starting at a specific index and then + * looks at offsets from that index stored as a byte, this dramatically + * reduces the memory usage of this array. + */ + private static final class LineFragment { + private final int startIndex; + private final byte[] offsets; + + /** + * Create a new line fragment to store information about. + * + * @param startIndex The start index for this line. + * @param offsets The byte array of offsets to store. + */ + LineFragment(int startIndex, byte[] offsets) { + this.startIndex = startIndex; + this.offsets = offsets; + } + + /** + * The length of columns stored in the line. One is added because we + * store the start index outside of the offsets array. + */ + int length() { + return offsets.length + 1; + } + + /** + * Find the mapping id at the specified column. + * + * @param column The column to lookup + * @return the value at that point in the column + */ + int valueAtColumn(int column) { + Preconditions.checkArgument(column > 0); + int pos = startIndex; + for (int i = 0; i < column - 1; i++) { + pos += offsets[i]; + } + return pos; + } + } + + /** + * Keeps track of data about the source file itself. This is contains a list + * of line offsets and columns to track down where exactly a line falls into + * the data. + */ + private static final class SourceFile { + final String dir; + final String fileName; + final int startMapId; + final int startLine; + final byte[] lineOffsets; + final short[] columns; + final String[] identifiers; + + private SourceFile( + String dir, String fileName, int startLine, int startMapId, + byte[] lineOffsets, short[] columns, String[] identifiers) { + this.fileName = Preconditions.checkNotNull(fileName); + this.dir = Preconditions.checkNotNull(dir); + this.startLine = startLine; + this.startMapId = startMapId; + this.lineOffsets = Preconditions.checkNotNull(lineOffsets); + this.columns = Preconditions.checkNotNull(columns); + this.identifiers = Preconditions.checkNotNull(identifiers); + Preconditions.checkArgument(lineOffsets.length == columns.length && + columns.length == identifiers.length); + } + + private SourceFile(int startMapId) { + // Only used for binary searches. + this.startMapId = startMapId; + + this.fileName = null; + this.dir = null; + this.startLine = 0; + this.lineOffsets = null; + this.columns = null; + this.identifiers = null; + } + + /** + * Returns the number of elements in this source file. + */ + int getLength() { + return lineOffsets.length; + } + + /** + * Returns the number of elements in this source file. + */ + int getStartMapId() { + return startMapId; + } + + /** + * Creates an original mapping from the data. + * + * @param offset The offset into the array to find the mapping for. + * @return A new original mapping object. + */ + OriginalMapping getOriginalMapping(int offset) { + int lineNumber = this.startLine; + // Offset is an index into this array and we need to include it. + for (int i = 0; i <= offset; i++) { + lineNumber += lineOffsets[i]; + } + OriginalMapping.Builder builder = OriginalMapping.newBuilder() + .setOriginalFile(dir + fileName) + .setLineNumber(lineNumber) + .setColumnPosition(columns[offset]) + .setIdentifier(identifiers[offset]); + return builder.build(); + } + + /** + * Builder to make a new SourceFile object. + */ + static final class Builder { + String dir; + String fileName; + int startMapId; + int startLine; + byte[] lineOffsets; + short[] columns; + String[] identifiers; + + Builder setDir(String dir) { + this.dir = dir; + return this; + } + + Builder setFileName(String fileName) { + this.fileName = fileName; + return this; + } + + Builder setStartMapId(int startMapId) { + this.startMapId = startMapId; + return this; + } + + Builder setStartLine(int startLine) { + this.startLine = startLine; + return this; + } + + Builder setLineOffsets(List lineOffsets) { + this.lineOffsets = Bytes.toArray(lineOffsets); + return this; + } + + Builder setColumns(List columns) { + this.columns = Shorts.toArray(columns); + return this; + } + + Builder setIdentifiers(List identifiers) { + this.identifiers = identifiers.toArray(new String[0]); + return this; + } + + /** + * Creates a new SourceFile from the parameters. + */ + SourceFile build() { + return new SourceFile(dir, fileName, startLine, startMapId, + lineOffsets, columns, identifiers); + } + } + + static Builder newBuilder() { + return new Builder(); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapConsumerV2.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapConsumerV2.java new file mode 100644 index 0000000..9320ada --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapConsumerV2.java @@ -0,0 +1,181 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.List; +import java.util.Map; + +/** + * Class for parsing version 2 of the SourceMap format, as produced by the + * Closure Compiler, etc. + * @author johnlenz@google.com (John Lenz) + * @author jschorr@google.com (Joseph Schorr) + */ +public class SourceMapConsumerV2 implements SourceMapConsumer { + /** + * The character map for each line. If a line does not have an entry, + * then it has not yet been decoded. + */ + private Map> characterMap = null; + + /** + * The undecoded line maps. Will be accessed to decode lines as needed. + */ + private JSONArray lineMaps = null; + + /** + * Map of Mapping IDs to the actual mapping object. + */ + private List mappings; + + public SourceMapConsumerV2() {} + + /** + * Parses the given contents containing a source map. + */ + @Override + public void parse(String contents) throws SourceMapParseException { + try { + JSONObject sourceMapRoot = new JSONObject(contents); + parse(sourceMapRoot); + } catch (JSONException ex) { + throw new SourceMapParseException("JSON parse exception: " + ex); + } + } + + /** + * Parses the given contents containing a source map. + */ + public void parse(JSONObject sourceMapRoot) throws SourceMapParseException { + try { + parseInternal(sourceMapRoot); + } catch (JSONException ex) { + throw new SourceMapParseException("JSON parse exception: " + ex); + } + } + + /** + * Parses the given contents as version 2 of a SourceMap. + */ + private void parseInternal(JSONObject sourceMapRoot) + throws JSONException, SourceMapParseException { + + // Check basic assertions about the format. + int version = sourceMapRoot.getInt("version"); + if (version != 2) { + throw new SourceMapParseException("Unknown version: " + version); + } + + String file = sourceMapRoot.getString("file"); + if (file.isEmpty()) { + throw new SourceMapParseException("File entry is missing or empty"); + } + + int lineCount = sourceMapRoot.getInt("lineCount"); + lineMaps = sourceMapRoot.getJSONArray("lineMaps"); + if (lineCount != lineMaps.length()) { + throw new SourceMapParseException( + "lineMaps length does not match lineCount"); + } + + // Build an empty character map. The character map will be filled in as + // lines are requested. + characterMap = Maps.newHashMap(); + + JSONArray sources = sourceMapRoot.getJSONArray("sources"); + JSONArray names = sourceMapRoot.has("names") + ? sourceMapRoot.getJSONArray("names") : null; + + // Create each of the OriginalMappings. + JSONArray jsonMappings = sourceMapRoot.getJSONArray("mappings"); + mappings = Lists.newArrayListWithCapacity(lineCount); + + for (int i = 0; i < jsonMappings.length(); i++) { + JSONArray entry = jsonMappings.getJSONArray(i); + + // The name can be accessed in two ways: Directly (i.e. a string) or + // indirectly (i.e. an index into the name map). + String name = entry.optString(3, ""); + if (names != null) { + try { + int nameIndex = entry.getInt(3); + name = names.getString(nameIndex); + } catch (JSONException e) { + } + } + + // Build the new OriginalMapping entry. + String sourceFile = sources.getString(entry.getInt(0)); + int lineNumber = entry.getInt(1); + int column = entry.getInt(2); + + OriginalMapping.Builder builder = OriginalMapping.newBuilder() + .setOriginalFile(sourceFile) + .setLineNumber(lineNumber) + .setColumnPosition(column) + .setIdentifier(name); + mappings.add(builder.build()); + } + } + + @Override + public OriginalMapping getMappingForLine(int lineNumber, int columnIndex) { + // Normalize the line and column numbers to 0. + lineNumber--; + columnIndex--; + + if (lineNumber >= lineMaps.length()) { + return null; + } + + Preconditions.checkState(lineNumber >= 0, "Line number must be >= 0"); + Preconditions.checkState(columnIndex >= 0, "Column index must be >= 0"); + + if (!characterMap.containsKey(lineNumber)) { + // Parse the line map entry and place it into the character map. + try { + characterMap.put(lineNumber, + SourceMapLineDecoder.decodeLine(lineMaps.getString(lineNumber))); + } catch (JSONException jse) { + throw new IllegalStateException( + "JSON exception when retrieving line map", jse); + } + } + + List map = characterMap.get(lineNumber); + if (map == null || map.size() <= columnIndex) { + return null; + } + + int index = map.get(columnIndex); + if (index == -1) { + return null; + } + Preconditions.checkState(index < mappings.size(), + "Invalid mapping reference"); + return mappings.get(index); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapConsumerV3.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapConsumerV3.java new file mode 100644 index 0000000..e970d3a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapConsumerV3.java @@ -0,0 +1,724 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.debugging.sourcemap.Base64VLQ.CharIterator; +import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; +import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Class for parsing version 3 of the SourceMap format, as produced by the + * Closure Compiler, etc. + * http://code.google.com/p/closure-compiler/wiki/SourceMaps + * @author johnlenz@google.com (John Lenz) + */ +public class SourceMapConsumerV3 implements SourceMapConsumer, + SourceMappingReversable { + static final int UNMAPPED = -1; + + private String[] sources; + private String[] names; + private int lineCount; + // Slots in the lines list will be null if the line does not have any entries. + private ArrayList> lines = null; + /** originalFile path ==> original line ==> target mappings */ + private Map>> + reverseSourceMapping; + + public SourceMapConsumerV3() { + + } + + static class DefaultSourceMapSupplier implements SourceMapSupplier { + @Override + public String getSourceMap(String url) { + return null; + } + } + + /** + * Parses the given contents containing a source map. + */ + @Override + public void parse(String contents) throws SourceMapParseException { + parse(contents, null); + } + + /** + * Parses the given contents containing a source map. + */ + public void parse(String contents, SourceMapSupplier sectionSupplier) + throws SourceMapParseException { + try { + JSONObject sourceMapRoot = new JSONObject(contents); + parse(sourceMapRoot, sectionSupplier); + } catch (JSONException ex) { + throw new SourceMapParseException("JSON parse exception: " + ex); + } + } + + /** + * Parses the given contents containing a source map. + */ + public void parse(JSONObject sourceMapRoot) throws SourceMapParseException { + parse(sourceMapRoot, null); + } + + /** + * Parses the given contents containing a source map. + */ + public void parse(JSONObject sourceMapRoot, SourceMapSupplier sectionSupplier) + throws SourceMapParseException { + try { + // Check basic assertions about the format. + int version = sourceMapRoot.getInt("version"); + if (version != 3) { + throw new SourceMapParseException("Unknown version: " + version); + } + + String file = sourceMapRoot.getString("file"); + if (file.isEmpty()) { + throw new SourceMapParseException("File entry is missing or empty"); + } + + if (sourceMapRoot.has("sections")) { + // Looks like a index map, try to parse it that way. + parseMetaMap(sourceMapRoot, sectionSupplier); + return; + } + + lineCount = sourceMapRoot.getInt("lineCount"); + String lineMap = sourceMapRoot.getString("mappings"); + + sources = getJavaStringArray(sourceMapRoot.getJSONArray("sources")); + names = getJavaStringArray(sourceMapRoot.getJSONArray("names")); + + lines = Lists.newArrayListWithCapacity(lineCount); + + new MappingBuilder(lineMap).build(); + } catch (JSONException ex) { + throw new SourceMapParseException("JSON parse exception: " + ex); + } + } + + /** + * @param sourceMapRoot + * @throws SourceMapParseException + */ + private void parseMetaMap( + JSONObject sourceMapRoot, SourceMapSupplier sectionSupplier) + throws SourceMapParseException { + if (sectionSupplier == null) { + sectionSupplier = new DefaultSourceMapSupplier(); + } + + try { + // Check basic assertions about the format. + int version = sourceMapRoot.getInt("version"); + if (version != 3) { + throw new SourceMapParseException("Unknown version: " + version); + } + + String file = sourceMapRoot.getString("file"); + if (file.isEmpty()) { + throw new SourceMapParseException("File entry is missing or empty"); + } + + if (sourceMapRoot.has("lineCount") + || sourceMapRoot.has("mappings") + || sourceMapRoot.has("sources") + || sourceMapRoot.has("names")) { + throw new SourceMapParseException("Invalid map format"); + } + + SourceMapGeneratorV3 generator = new SourceMapGeneratorV3(); + JSONArray sections = sourceMapRoot.getJSONArray("sections"); + for (int i = 0, count = sections.length(); i < count; i++) { + JSONObject section = sections.getJSONObject(i); + if (section.has("map") && section.has("url")) { + throw new SourceMapParseException( + "Invalid map format: section may not have both 'map' and 'url'"); + } + JSONObject offset = section.getJSONObject("offset"); + int line = offset.getInt("line"); + int column = offset.getInt("column"); + String mapSectionContents; + if (section.has("url")) { + String url = section.getString("url"); + mapSectionContents = sectionSupplier.getSourceMap(url); + if (mapSectionContents == null) { + throw new SourceMapParseException("Unable to retrieve: " + url); + } + } else if (section.has("map")) { + mapSectionContents = section.getString("map"); + } else { + throw new SourceMapParseException( + "Invalid map format: section must have either 'map' or 'url'"); + } + generator.mergeMapSection(line, column, mapSectionContents); + } + + StringBuilder sb = new StringBuilder(); + try { + generator.appendTo(sb, file); + } catch (IOException e) { + // Can't happen. + throw new RuntimeException(e); + } + + parse(sb.toString()); + } catch (IOException ex) { + throw new SourceMapParseException("IO exception: " + ex); + } catch (JSONException ex) { + throw new SourceMapParseException("JSON parse exception: " + ex); + } + } + + @Override + public OriginalMapping getMappingForLine(int lineNumber, int column) { + // Normalize the line and column numbers to 0. + lineNumber--; + column--; + + if (lineNumber < 0 || lineNumber >= lines.size()) { + return null; + } + + Preconditions.checkState(lineNumber >= 0); + Preconditions.checkState(column >= 0); + + + // If the line is empty return the previous mapping. + if (lines.get(lineNumber) == null) { + return getPreviousMapping(lineNumber); + } + + ArrayList entries = lines.get(lineNumber); + // No empty lists. + Preconditions.checkState(entries.size() > 0); + if (entries.get(0).getGeneratedColumn() > column) { + return getPreviousMapping(lineNumber); + } + + int index = search(entries, column, 0, entries.size() - 1); + Preconditions.checkState(index >= 0, "unexpected:%s", index); + return getOriginalMappingForEntry(entries.get(index)); + } + + @Override + public Collection getOriginalSources() { + return Arrays.asList(sources); + } + + @Override + public Collection getReverseMapping(String originalFile, + int line, int column) { + // TODO(user): This implementation currently does not make use of the column + // parameter. + + // Synchronization needs to be handled by callers. + if (reverseSourceMapping == null) { + createReverseMapping(); + } + + Map> sourceLineToCollectionMap = + reverseSourceMapping.get(originalFile); + + if (sourceLineToCollectionMap == null) { + return Collections.emptyList(); + } else { + Collection mappings = + sourceLineToCollectionMap.get(line); + + if (mappings == null) { + return Collections.emptyList(); + } else { + return mappings; + } + } + } + + private String[] getJavaStringArray(JSONArray array) throws JSONException { + int len = array.length(); + String[] result = new String[len]; + for(int i = 0; i < len; i++) { + result[i] = array.getString(i); + } + return result; + } + + private class MappingBuilder { + private static final int MAX_ENTRY_VALUES = 5; + private final StringCharIterator content; + private int line = 0; + private int previousCol = 0; + private int previousSrcId = 0; + private int previousSrcLine = 0; + private int previousSrcColumn = 0; + private int previousNameId = 0; + + MappingBuilder(String lineMap) { + this.content = new StringCharIterator(lineMap); + } + + void build() { + int [] temp = new int[MAX_ENTRY_VALUES]; + ArrayList entries = new ArrayList(); + while (content.hasNext()) { + // ';' denotes a new line. + if (tryConsumeToken(';')) { + // The line is complete, store the result for the line, + // null if the line is empty. + ArrayList result; + if (entries.size() > 0) { + result = entries; + // A new array list for the next line. + entries = new ArrayList(); + } else { + result = null; + } + lines.add(result); + entries.clear(); + line++; + previousCol = 0; + } else { + // grab the next entry for the current line. + int entryValues = 0; + while (!entryComplete()) { + temp[entryValues] = nextValue(); + entryValues++; + } + Entry entry = decodeEntry(temp, entryValues); + + validateEntry(entry); + entries.add(entry); + + // Consume the separating token, if there is one. + tryConsumeToken(','); + } + } + } + + /** + * Sanity check the entry. + */ + private void validateEntry(Entry entry) { + Preconditions.checkState(line < lineCount); + Preconditions.checkState(entry.getSourceFileId() == UNMAPPED + || entry.getSourceFileId() < sources.length); + Preconditions.checkState(entry.getNameId() == UNMAPPED + || entry.getNameId() < names.length); + } + + /** + * Decodes the next entry, using the previous encountered values to + * decode the relative values. + * + * @param vals An array of integers that represent values in the entry. + * @param entryValues The number of entries in the array. + * @return The entry object. + */ + private Entry decodeEntry(int[] vals, int entryValues) { + Entry entry; + switch (entryValues) { + // The first values, if present are in the following order: + // 0: the starting column in the current line of the generated file + // 1: the id of the original source file + // 2: the starting line in the original source + // 3: the starting column in the original source + // 4: the id of the original symbol name + // The values are relative to the last encountered value for that field. + // Note: the previously column value for the generated file is reset + // to '0' when a new line is encountered. This is done in the 'build' + // method. + + case 1: + // An unmapped section of the generated file. + entry = new UnmappedEntry( + vals[0] + previousCol); + // Set the values see for the next entry. + previousCol = entry.getGeneratedColumn(); + return entry; + + case 4: + // A mapped section of the generated file. + entry = new UnnamedEntry( + vals[0] + previousCol, + vals[1] + previousSrcId, + vals[2] + previousSrcLine, + vals[3] + previousSrcColumn); + // Set the values see for the next entry. + previousCol = entry.getGeneratedColumn(); + previousSrcId = entry.getSourceFileId(); + previousSrcLine = entry.getSourceLine(); + previousSrcColumn = entry.getSourceColumn(); + return entry; + + case 5: + // A mapped section of the generated file, that has an associated + // name. + entry = new NamedEntry( + vals[0] + previousCol, + vals[1] + previousSrcId, + vals[2] + previousSrcLine, + vals[3] + previousSrcColumn, + vals[4] + previousNameId); + // Set the values see for the next entry. + previousCol = entry.getGeneratedColumn(); + previousSrcId = entry.getSourceFileId(); + previousSrcLine = entry.getSourceLine(); + previousSrcColumn = entry.getSourceColumn(); + previousNameId = entry.getNameId(); + return entry; + + default: + throw new IllegalStateException( + "Unexpected number of values for entry:" + entryValues); + } + } + + private boolean tryConsumeToken(char token) { + if (content.hasNext() && content.peek() == token) { + // consume the comma + content.next(); + return true; + } + return false; + } + + private boolean entryComplete() { + if (!content.hasNext()) { + return true; + } + + char c = content.peek(); + return (c == ';' || c == ','); + } + + private int nextValue() { + return Base64VLQ.decode(content); + } + } + + /** + * Perform a binary search on the array to find a section that covers + * the target column. + */ + private int search(ArrayList entries, int target, int start, int end) { + while (true) { + int mid = ((end - start) / 2) + start; + int compare = compareEntry(entries, mid, target); + if (compare == 0) { + return mid; + } else if (compare < 0) { + // it is in the upper half + start = mid + 1; + if (start > end) { + return end; + } + } else { + // it is in the lower half + end = mid - 1; + if (end < start) { + return end; + } + } + } + } + + /** + * Compare an array entry's column value to the target column value. + */ + private int compareEntry(ArrayList entries, int entry, int target) { + return entries.get(entry).getGeneratedColumn() - target; + } + + /** + * Returns the mapping entry that proceeds the supplied line or null if no + * such entry exists. + */ + private OriginalMapping getPreviousMapping(int lineNumber) { + do { + if (lineNumber == 0) { + return null; + } + lineNumber--; + } while (lines.get(lineNumber) == null); + ArrayList entries = lines.get(lineNumber); + return getOriginalMappingForEntry(entries.get(entries.size() - 1)); + } + + /** + * Creates an "OriginalMapping" object for the given entry object. + */ + private OriginalMapping getOriginalMappingForEntry(Entry entry) { + if (entry.getSourceFileId() == UNMAPPED) { + return null; + } else { + // Adjust the line/column here to be start at 1. + Builder x = OriginalMapping.newBuilder() + .setOriginalFile(sources[entry.getSourceFileId()]) + .setLineNumber(entry.getSourceLine() + 1) + .setColumnPosition(entry.getSourceColumn() + 1); + if (entry.getNameId() != UNMAPPED) { + x.setIdentifier(names[entry.getNameId()]); + } + return x.build(); + } + } + + /** + * Reverse the source map; the created mapping will allow us to quickly go + * from a source file and line number to a collection of target + * OriginalMappings. + */ + private void createReverseMapping() { + reverseSourceMapping = + new HashMap>>(); + + for (int targetLine = 0; targetLine < lines.size(); targetLine++) { + ArrayList entries = lines.get(targetLine); + + if (entries != null) { + for (Entry entry : entries) { + if (entry.getSourceFileId() != UNMAPPED + && entry.getSourceLine() != UNMAPPED) { + String originalFile = sources[entry.getSourceFileId()]; + + if (!reverseSourceMapping.containsKey(originalFile)) { + reverseSourceMapping.put(originalFile, + new HashMap>()); + } + + Map> lineToCollectionMap = + reverseSourceMapping.get(originalFile); + + int sourceLine = entry.getSourceLine(); + + if (!lineToCollectionMap.containsKey(sourceLine)) { + lineToCollectionMap.put(sourceLine, + new ArrayList(1)); + } + + Collection mappings = + lineToCollectionMap.get(sourceLine); + + Builder builder = OriginalMapping.newBuilder().setLineNumber( + targetLine).setColumnPosition(entry.getGeneratedColumn()); + + mappings.add(builder.build()); + } + } + } + } + } + + /** + * A implementation of the Base64VLQ CharIterator used for decoding the + * mappings encoded in the JSON string. + */ + private static class StringCharIterator implements CharIterator { + final String content; + final int length; + int current = 0; + + StringCharIterator(String content) { + this.content = content; + this.length = content.length(); + } + + @Override + public char next() { + return content.charAt(current++); + } + + char peek() { + return content.charAt(current); + } + + @Override + public boolean hasNext() { + return current < length; + } + } + + /** + * Represents a mapping entry in the source map. + */ + private interface Entry { + int getGeneratedColumn(); + int getSourceFileId(); + int getSourceLine(); + int getSourceColumn(); + int getNameId(); + } + + /** + * This class represents a portion of the generated file, that is not mapped + * to a section in the original source. + */ + private static class UnmappedEntry implements Entry { + private final int column; + + UnmappedEntry(int column) { + this.column = column; + } + + @Override + public int getGeneratedColumn() { + return column; + } + + @Override + public int getSourceFileId() { + return UNMAPPED; + } + + @Override + public int getSourceLine() { + return UNMAPPED; + } + + @Override + public int getSourceColumn() { + return UNMAPPED; + } + + @Override + public int getNameId() { + return UNMAPPED; + } + } + + /** + * This class represents a portion of the generated file, that is mapped + * to a section in the original source. + */ + private static class UnnamedEntry extends UnmappedEntry { + private final int srcFile; + private final int srcLine; + private final int srcColumn; + + UnnamedEntry(int column, int srcFile, int srcLine, int srcColumn) { + super(column); + this.srcFile = srcFile; + this.srcLine = srcLine; + this.srcColumn = srcColumn; + } + + @Override + public int getSourceFileId() { + return srcFile; + } + + @Override + public int getSourceLine() { + return srcLine; + } + + @Override + public int getSourceColumn() { + return srcColumn; + } + + @Override + public int getNameId() { + return UNMAPPED; + } + } + + /** + * This class represents a portion of the generated file, that is mapped + * to a section in the original source, and is associated with a name. + */ + private static class NamedEntry extends UnnamedEntry { + private final int name; + + NamedEntry(int column, int srcFile, int srcLine, int srcColumn, int name) { + super(column, srcFile, srcLine, srcColumn); + this.name = name; + } + + @Override + public int getNameId() { + return name; + } + } + + public static interface EntryVisitor { + void visit(String sourceName, + String symbolName, + FilePosition sourceStartPosition, + FilePosition startPosition, + FilePosition endPosition); + } + + public void visitMappings(EntryVisitor visitor) { + boolean pending = false; + String sourceName = null; + String symbolName = null; + FilePosition sourceStartPosition = null; + FilePosition startPosition = null; + + final int lineCount = lines.size(); + for (int i = 0; i < lineCount; i++) { + ArrayList line = lines.get(i); + if (line != null) { + final int entryCount = line.size(); + for (int j = 0; j < entryCount; j++) { + Entry entry = line.get(j); + if (pending) { + FilePosition endPosition = new FilePosition( + i, entry.getGeneratedColumn()); + visitor.visit( + sourceName, + symbolName, + sourceStartPosition, + startPosition, + endPosition); + pending = false; + } + + if (entry.getSourceFileId() != UNMAPPED) { + pending = true; + sourceName = sources[entry.getSourceFileId()]; + symbolName = (entry.getNameId() != UNMAPPED) + ? names[entry.getNameId()] : null; + sourceStartPosition = new FilePosition( + entry.getSourceLine(), entry.getSourceColumn()); + startPosition = new FilePosition( + i, entry.getGeneratedColumn()); + } + } + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapFormat.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapFormat.java new file mode 100644 index 0000000..3e5642f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapFormat.java @@ -0,0 +1,34 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.debugging.sourcemap; + +/** + * A list of currently support SourceMap format revisions. + * @author johnlenz@google.com (John Lenz) + */ +public enum SourceMapFormat { + /** The latest "stable" format */ + DEFAULT, + + /** V1: The original Closure Inspector format */ + V1, + + /** V2: A more compact format */ + V2, + + /** V3: An even more compact format */ + V3; +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapGenerator.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapGenerator.java new file mode 100644 index 0000000..72d56c4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapGenerator.java @@ -0,0 +1,103 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + + +import java.io.IOException; +import java.util.List; + +import javax.annotation.Nullable; + +/** + * Collects information mapping the generated (compiled) source back to + * its original source for debugging purposes + * + * @author johnlenz@google.com (John Lenz) + */ +public interface SourceMapGenerator { + + /** + * Appends the source map to the given buffer. + * + * @param out The stream to which the map will be appended. + * @param name The name of the generated source file that this source map + * represents. + */ + void appendTo(Appendable out, String name) throws IOException; + + /** + * Appends the index source map to the given buffer. + * + * @param out The stream to which the map will be appended. + * @param name The name of the generated source file that this source map + * represents. + * @param sections An ordered list of map sections to include in the index. + * @throws IOException + */ + void appendIndexMapTo( + Appendable out, String name, List sections) + throws IOException; + + /** + * Resets the source map for reuse. A reset needs to be called between + * each generated output file. + */ + void reset(); + + /** + * Adds a mapping for the given node. Mappings must be added in order. + * @param sourceName The file name to use in the generate source map + * to represent this source. + * @param symbolName The symbol name associated with this position in the + * source map. + * @param sourceStartPosition The starting position in the original source for + * represented range outputStartPosition to outputEndPosition in the + * generated file. + * @param outputStartPosition The position on the starting line + * @param outputEndPosition The position on the ending line. + */ + void addMapping(String sourceName, @Nullable String symbolName, + FilePosition sourceStartPosition, + FilePosition outputStartPosition, FilePosition outputEndPosition); + + /** + * Sets the prefix used for wrapping the generated source file before + * it is written. This ensures that the source map is adjusted for the + * change in character offsets. + * + * @param prefix The prefix that is added before the generated source code. + */ + void setWrapperPrefix(String prefix); + + /** + * Sets the source code that exists in the buffer for which the + * generated code is being generated. This ensures that the source map + * accurately reflects the fact that the source is being appended to + * an existing buffer and as such, does not start at line 0, position 0 + * but rather some other line and position. + * + * @param offsetLine The index of the current line being printed. + * @param offsetIndex The column index of the current character being printed. + */ + void setStartingPosition(int offsetLine, int offsetIndex); + + /** + * Whether to perform additional validation on the source map. + * @param validate + */ + void validate(boolean validate); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapGeneratorFactory.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapGeneratorFactory.java new file mode 100644 index 0000000..35112f0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapGeneratorFactory.java @@ -0,0 +1,46 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.debugging.sourcemap; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class SourceMapGeneratorFactory { + + /** + * @return The appropriate source map object for the given source map format. + */ + public static SourceMapGenerator getInstance() { + return getInstance(SourceMapFormat.DEFAULT); + } + + /** + * @return The appropriate source map object for the given source map format. + */ + public static SourceMapGenerator getInstance(SourceMapFormat format) { + switch (format) { + case V1: + return new SourceMapGeneratorV1(); + case V2: + return new SourceMapGeneratorV2(); + case DEFAULT: + case V3: + return new SourceMapGeneratorV3(); + default: + throw new IllegalStateException("unsupported source map format"); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapGeneratorV1.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapGeneratorV1.java new file mode 100644 index 0000000..c25e7eb --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapGeneratorV1.java @@ -0,0 +1,646 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; + +import java.io.IOException; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.List; + +import javax.annotation.Nullable; + +/** + * Collects information mapping the generated (compiled) source back to + * its original source for debugging purposes. + * + */ +public class SourceMapGeneratorV1 implements SourceMapGenerator { + + private final static int UNMAPPED = -1; + + + /** + * A mapping from a given position in an input source file to a given position + * in the generated code. + */ + static class Mapping { + /** + * A unique ID for this mapping for record keeping purposes. + */ + int id = UNMAPPED; + + /** + * The input source file. + */ + String sourceFile; + + /** + * The position of the code in the input source file. Both + * the line number and the character index are indexed by + * 1 for legacy reasons via the Rhino Node class. + */ + FilePosition originalPosition; + + /** + * The starting position of the code in the generated source + * file which this mapping represents. Indexed by 0. + */ + FilePosition startPosition; + + /** + * The ending position of the code in the generated source + * file which this mapping represents. Indexed by 0. + */ + FilePosition endPosition; + + /** + * The original name of the token found at the position + * represented by this mapping (if any). + */ + String originalName; + + /** + * Whether the mapping is actually used by the source map. + */ + boolean used = false; + } + + private class MappingWriter { + /** + * Cache of escaped source file name. + */ + private String lastSourceFile = null; + private String lastSourceFileEscaped = null; + private int lastLine = 0; + private String lastLineString = String.valueOf(0); + + /** + * Appends the mapping to the given buffer. + */ + private void appendMappingTo( + Mapping m, Appendable out) throws IOException { + out.append("["); + + String sourceFile = m.sourceFile; + // The source file rarely changes, so cache the escaped string. + String escapedSourceFile; + if (lastSourceFile != sourceFile) { // yes, s1 != s2, not !s1.equals(s2) + lastSourceFile = sourceFile; + lastSourceFileEscaped = escapeString(sourceFile); + } + escapedSourceFile = lastSourceFileEscaped; + + out.append(escapedSourceFile); + out.append(","); + + int line = m.originalPosition.getLine(); + if (line != lastLine) { + lastLineString = String.valueOf(line); + } + String lineValue = lastLineString; + + out.append(lineValue); + + out.append(","); + out.append(String.valueOf( + m.originalPosition.getColumn())); + + if (m.originalName != null) { + out.append(","); + out.append(escapeString(m.originalName)); + } + + out.append("]\n"); + } + + /** + * Add used mappings to the supplied Appendable. + */ + void appendMappings(Appendable out) throws IOException { + for (Mapping m : mappings) { + if (m.used) { + appendMappingTo(m, out); + } + } + } + } + + /** + * A pre-order traversal ordered list of mappings stored in this map. + */ + private List mappings = Lists.newArrayList(); + + /** + * For validation store the start of the last mapping added. + */ + private Mapping lastMapping; + + /** + * The position that the current source map is offset in the + * buffer being used to generated the compiled source file. + */ + private FilePosition offsetPosition = new FilePosition(0, 0); + + /** + * The position that the current source map is offset in the + * generated the compiled source file by the addition of a + * an output wrapper prefix. + */ + private FilePosition prefixPosition = new FilePosition(0, 0); + + /** + * Escapes the given string for JSON. + */ + private static String escapeString(String value) { + return Util.escapeString(value); + } + + /** + * Adds a mapping for the given node. Mappings must be added in order. + * @param startPosition The position on the starting line + * @param endPosition The position on the ending line. + */ + @Override + public void addMapping( + String sourceName, @Nullable String symbolName, + FilePosition sourceStartPosition, + FilePosition startPosition, FilePosition endPosition) { + + // Don't bother if there is not sufficient information to be useful. + if (sourceName == null || sourceStartPosition.getLine() < 0) { + return; + } + + // Create the new mapping. + Mapping mapping = new Mapping(); + mapping.sourceFile = sourceName; + mapping.originalPosition = sourceStartPosition; + mapping.originalName = symbolName; // may be null + + // NOTE: When multiple outputs are concatenated together, the positions in + // the mapping are relative to offsetPosition. + if (offsetPosition.getLine() == 0 + && offsetPosition.getColumn() == 0) { + mapping.startPosition = startPosition; + mapping.endPosition = endPosition; + } else { + // If the mapping is found on the first line, we need to offset + // its character position by the number of characters found on + // the *last* line of the source file to which the code is + // being generated. + int offsetLine = offsetPosition.getLine(); + int startOffsetPosition = offsetPosition.getColumn(); + int endOffsetPosition = offsetPosition.getColumn(); + + if (startPosition.getLine() > 0) { + startOffsetPosition = 0; + } + + if (endPosition.getLine() > 0) { + endOffsetPosition = 0; + } + + mapping.startPosition = + new FilePosition(startPosition.getLine() + offsetLine, + startPosition.getColumn() + startOffsetPosition); + + mapping.endPosition = + new FilePosition(endPosition.getLine() + offsetLine, + endPosition.getColumn() + endOffsetPosition); + } + + // Validate the mappings are in a proper order. + if (lastMapping != null) { + int lastLine = lastMapping.startPosition.getLine(); + int lastColumn = lastMapping.startPosition.getColumn(); + int nextLine = mapping.startPosition.getLine(); + int nextColumn = mapping.startPosition.getColumn(); + Preconditions.checkState(nextLine > lastLine + || (nextLine == lastLine && nextColumn >= lastColumn), + "Incorrect source mappings order, previous : (%s,%s)\n" + + "new : (%s,%s)\nnode : %s", + lastLine, lastColumn, nextLine, nextColumn); + } + + lastMapping = mapping; + mappings.add(mapping); + } + + /** + * Sets the prefix used for wrapping the generated source file before + * it is output. This ensures that the source map is adjusted as + * needed. + * + * @param prefix The prefix that is added before the generated source code. + */ + @Override + public void setWrapperPrefix(String prefix) { + // Determine the current line and character position. + int prefixLine = 0; + int prefixIndex = 0; + + for (int i = 0; i < prefix.length(); ++i) { + if (prefix.charAt(i) == '\n') { + prefixLine++; + prefixIndex = 0; + } else { + prefixIndex++; + } + } + + prefixPosition = new FilePosition(prefixLine, prefixIndex); + } + + /** + * Sets the source code that exists in the buffer to which the + * generated code is being generated. This ensures that the source map + * accurately reflects the fact that the source is being appended to + * an existing buffer and as such, does not start at line 0, position 0 + * but rather some other line and position. + * + * @param offsetLine The index of the current line being printed. + * @param offsetIndex The column index of the current character being printed. + */ + @Override + public void setStartingPosition(int offsetLine, int offsetIndex) { + Preconditions.checkState(offsetLine >= 0); + Preconditions.checkState(offsetIndex >= 0); + offsetPosition = new FilePosition(offsetLine, offsetIndex); + } + + /** + * Resets the source map for reuse for the generation of a new source file. + */ + @Override + public void reset() { + mappings = Lists.newArrayList(); + lastMapping = null; + offsetPosition = new FilePosition(0, 0); + prefixPosition = new FilePosition(0, 0); + } + + /** + * Appends the source map in LavaBug format to the given buffer. + * + * @param out The stream to which the map will be appended. + * @param name The name of the generated source file that this source map + * represents. + */ + @Override + public void appendTo(Appendable out, String name) throws IOException { + // Write the mappings out to the file. The format of the generated + // source map is three sections, each delimited by a magic comment. + // + // The first section contains an array for each line of the generated + // code, where each element in the array is the ID of the mapping which + // best represents the index-th character found on that line of the + // generated source code. + // + // The second section contains an array per generated line. Unused. + // + // The third and final section contains an array per line, each of which + // represents a mapping with a unique ID. The mappings are added in order. + // The array itself contains a tuple representing + // ['source file', line, col (, 'original name')] + // + // Example for 2 lines of generated code (with line numbers added for + // readability): + // + // 1) /** Begin line maps. **/{ "count": 2 } + // 2) [0,0,0,0,0,0,1,1,1,1,2] + // 3) [2,2,2,2,2,2,3,4,4,4,4,4] + // 4) /** Begin file information. **/ + // 5) [] + // 6) [] + // 7) /** Begin mapping definitions. **/ + // 8) ["a.js", 1, 34] + // 9) ["a.js", 5, 2] + // 10) ["b.js", 1, 3, "event"] + // 11) ["c.js", 1, 4] + // 12) ["d.js", 3, 78, "foo"] + + int maxLine = prepMappings(); + + // Add the line character maps. + out.append("/** Begin line maps. **/{ \"file\" : "); + out.append(escapeString(name)); + out.append(", \"count\": "); + out.append(String.valueOf(maxLine + 1)); + out.append(" }\n"); + (new LineMapper(out)).appendLineMappings(); + + // Add the source file maps. + out.append("/** Begin file information. **/\n"); + + // This section is unused but we need one entry per line to + // prevent changing the format. + for (int i = 0; i <= maxLine; ++i) { + out.append("[]\n"); + } + + // Add the mappings themselves. + out.append("/** Begin mapping definitions. **/\n"); + + (new MappingWriter()).appendMappings(out); + } + + /** + * Assigns sequential ids to used mappings, and returns the last line mapped. + */ + private int prepMappings() throws IOException { + // Mark any unused mappings. + (new MappingTraversal()).traverse(new UsedMappingCheck()); + + // Renumber used mappings and keep track of the last line. + int id = 0; + int maxLine = 0; + for (Mapping m : mappings) { + if (m.used) { + m.id = id++; + int endPositionLine = m.endPosition.getLine(); + maxLine = Math.max(maxLine, endPositionLine); + } + } + + // Adjust for the prefix. + return maxLine + prefixPosition.getLine(); + } + + private class LineMapper implements MappingVisitor { + // The destination. + private final Appendable out; + + // Whether the current line has had a value written yet. + private boolean firstChar = true; + + private final static String UNMAPPED_STRING = "-1"; + + private int lastId = UNMAPPED; + private String lastIdString = UNMAPPED_STRING; + + LineMapper(Appendable out) { + this.out = out; + } + + /** + * As each segment is visited write out the appropriate line mapping. + */ + @Override + public void visit(Mapping m, int line, int col, int nextLine, int nextCol) + throws IOException { + + int id = (m != null) ? m.id : UNMAPPED; + if (lastId != id) { + // Prevent the creation of unnecessary temporary stings for often + // repeated values. + lastIdString = (id == UNMAPPED) ? UNMAPPED_STRING : String.valueOf(id); + lastId = id; + } + String idString = lastIdString; + + for (int i = line; i <= nextLine; i++) { + if (i == nextLine) { + for (int j = col; j < nextCol; j++) { + addCharEntry(idString); + } + break; + } + + closeLine(); + openLine(); + + // Set the starting location for the next line. + col = 0; + } + } + + // Append the line mapping entries. + void appendLineMappings() throws IOException { + // Start the first line. + openLine(); + + (new MappingTraversal()).traverse(this); + + // And close the final line. + closeLine(); + } + + /** + * Begin the entry for a new line. + */ + private void openLine() throws IOException { + if (out != null) { + out.append("["); + this.firstChar = true; + } + } + + /** + * End the entry for a line. + */ + private void closeLine() throws IOException { + if (out != null) { + out.append("]\n"); + } + } + + /** + * Add a new char position entry. + * @param id The mapping id to record. + */ + private void addCharEntry(String id) throws IOException { + if (out != null) { + if (firstChar) { + firstChar = false; + } else { + out.append(","); + } + out.append(id); + } + } + } + + /** + * Mark any visited mapping as "used". + */ + private class UsedMappingCheck implements MappingVisitor { + /** + * @throws IOException + */ + @Override + public void visit(Mapping m, int line, int col, int nextLine, int nextCol) + throws IOException { + if (m != null) { + m.used = true; + } + } + } + + private interface MappingVisitor { + /** + * @param m The mapping for the current code segment. null if the segment + * is unmapped. + * @param line The starting line for this code segment. + * @param col The starting column for this code segment. + * @param endLine The ending line + * @param endCol The ending column + * @throws IOException + */ + void visit(Mapping m, int line, int col, int endLine, int endCol) + throws IOException; + } + + /** + * Walk the mappings and visit each segment of the mappings, unmapped + * segments are visited with a null mapping, unused mapping are not visited. + */ + private class MappingTraversal { + // The last line and column written + private int line; + private int col; + + MappingTraversal() { + } + + // Append the line mapping entries. + void traverse(MappingVisitor v) throws IOException { + // The mapping list is ordered as a pre-order traversal. The mapping + // positions give us enough information to rebuild the stack and this + // allows the building of the source map in O(n) time. + Deque stack = new ArrayDeque(); + for (Mapping m : mappings) { + // Find the closest ancestor of the current mapping: + // An overlapping mapping is an ancestor of the current mapping, any + // non-overlapping mappings are siblings (or cousins) and must be + // closed in the reverse order of when they encountered. + while (!stack.isEmpty() && !isOverlapped(stack.peek(), m)) { + Mapping previous = stack.pop(); + maybeVisit(v, previous); + } + + // Any gaps between the current line position and the start of the + // current mapping belong to the parent. + Mapping parent = stack.peek(); + maybeVisitParent(v, parent, m); + + stack.push(m); + } + + // There are no more children to be had, simply close the remaining + // mappings in the reverse order of when they encountered. + while (!stack.isEmpty()) { + Mapping m = stack.pop(); + maybeVisit(v, m); + } + } + + /** + * @return The line adjusted for the prefix position. + */ + private int getAdjustedLine(FilePosition p) { + return p.getLine() + prefixPosition.getLine(); + } + + /** + * @return The column adjusted for the prefix position. + */ + private int getAdjustedCol(FilePosition p) { + int rawLine = p.getLine(); + int rawCol = p.getColumn(); + // Only the first line needs the character position adjusted. + return (rawLine != 0) + ? rawCol : rawCol + prefixPosition.getColumn(); + } + + /** + * @return Whether m1 ends before m2 starts. + */ + private boolean isOverlapped(Mapping m1, Mapping m2) { + // No need to use adjusted values here, relative positions are sufficient. + int l1 = m1.endPosition.getLine(); + int l2 = m2.startPosition.getLine(); + int c1 = m1.endPosition.getColumn(); + int c2 = m2.startPosition.getColumn(); + + return (l1 == l2 && c1 >= c2) || l1 > l2; + } + + /** + * Write any needed entries from the current position to the end of the + * provided mapping. + */ + private void maybeVisit(MappingVisitor v, Mapping m) throws IOException { + int nextLine = getAdjustedLine(m.endPosition); + int nextCol = getAdjustedCol(m.endPosition); + // If this anything remaining in this mapping beyond the + // current line and column position, write it out now. + if (line < nextLine || (line == nextLine && col < nextCol)) { + visit(v, m, nextLine, nextCol); + } + } + + /** + * Write any needed entries to complete the provided mapping. + */ + private void maybeVisitParent(MappingVisitor v, Mapping parent, Mapping m) + throws IOException { + int nextLine = getAdjustedLine(m.startPosition); + int nextCol = getAdjustedCol(m.startPosition); + // If the previous value is null, no mapping exists. + Preconditions.checkState(line < nextLine || col <= nextCol); + if (line < nextLine || (line == nextLine && col < nextCol)) { + visit(v, parent, nextLine, nextCol); + } + } + + /** + * Write any entries needed between the current position the next position + * and update the current position. + */ + private void visit(MappingVisitor v, Mapping m, + int nextLine, int nextCol) + throws IOException { + Preconditions.checkState(line <= nextLine); + Preconditions.checkState(line < nextLine || col < nextCol); + + if (line == nextLine && col == nextCol) { + // Nothing to do. + Preconditions.checkState(false); + return; + } + + v.visit(m, line, col, nextLine, nextCol); + + line = nextLine; + col = nextCol; + } + } + + @Override + public void validate(boolean validate) { + // No additional validation to do. + } + + @Override + public void appendIndexMapTo( + Appendable out, String name, List appSections) { + throw new UnsupportedOperationException(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapGeneratorV2.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapGeneratorV2.java new file mode 100644 index 0000000..8d33887 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapGeneratorV2.java @@ -0,0 +1,888 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import java.io.IOException; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.Nullable; + +/** + * Collects information mapping the generated (compiled) source back to + * its original source for debugging purposes. + * + */ +public class SourceMapGeneratorV2 implements SourceMapGenerator { + + private boolean validate = false; + + private static final int UNMAPPED = -1; + + /** + * A pre-order traversal ordered list of mappings stored in this map. + */ + private List mappings = Lists.newArrayList(); + + /** + * A map of source names to source name index + */ + private LinkedHashMap sourceFileMap = + Maps.newLinkedHashMap(); + + /** + * A map of symbol names to symbol name index + */ + private LinkedHashMap originalNameMap = + Maps.newLinkedHashMap(); + + /** + * Cache of the last mappings source name. + */ + private String lastSourceFile = null; + + /** + * Cache of the last mappings source name index. + */ + private int lastSourceFileIndex = -1; + + /** + * For validation store the last mapping added. + */ + private Mapping lastMapping; + + /** + * The position that the current source map is offset in the + * buffer being used to generated the compiled source file. + */ + private FilePosition offsetPosition = new FilePosition(0, 0); + + /** + * The position that the current source map is offset in the + * generated the compiled source file by the addition of a + * an output wrapper prefix. + */ + private FilePosition prefixPosition = new FilePosition(0, 0); + + /** + * {@inheritDoc} + */ + @Override + public void reset() { + mappings.clear(); + lastMapping = null; + sourceFileMap.clear(); + originalNameMap.clear(); + lastSourceFile = null; + lastSourceFileIndex = -1; + offsetPosition = new FilePosition(0, 0); + prefixPosition = new FilePosition(0, 0); + } + + /** + * @param validate Whether to perform (potentially costly) validation on the + * generated source map. + */ + @Override + @VisibleForTesting + public void validate(boolean validate) { + this.validate = validate; + } + + /** + * Sets the prefix used for wrapping the generated source file before + * it is written. This ensures that the source map is adjusted for the + * change in character offsets. + * + * @param prefix The prefix that is added before the generated source code. + */ + @Override + public void setWrapperPrefix(String prefix) { + // Determine the current line and character position. + int prefixLine = 0; + int prefixIndex = 0; + + for (int i = 0; i < prefix.length(); ++i) { + if (prefix.charAt(i) == '\n') { + prefixLine++; + prefixIndex = 0; + } else { + prefixIndex++; + } + } + + prefixPosition = new FilePosition(prefixLine, prefixIndex); + } + + /** + * Sets the source code that exists in the buffer for which the + * generated code is being generated. This ensures that the source map + * accurately reflects the fact that the source is being appended to + * an existing buffer and as such, does not start at line 0, position 0 + * but rather some other line and position. + * + * @param offsetLine The index of the current line being printed. + * @param offsetIndex The column index of the current character being printed. + */ + @Override + public void setStartingPosition(int offsetLine, int offsetIndex) { + Preconditions.checkState(offsetLine >= 0); + Preconditions.checkState(offsetIndex >= 0); + offsetPosition = new FilePosition(offsetLine, offsetIndex); + } + + /** + * Adds a mapping for the given node. Mappings must be added in order. + * @param startPosition The position on the starting line + * @param endPosition The position on the ending line. + */ + @Override + public void addMapping( + String sourceName, @Nullable String symbolName, + FilePosition sourceStartPosition, + FilePosition startPosition, FilePosition endPosition) { + + // Don't bother if there is not sufficient information to be useful. + if (sourceName == null || sourceStartPosition.getLine() < 0) { + return; + } + + FilePosition adjustedStart = startPosition; + FilePosition adjustedEnd = endPosition; + + if (offsetPosition.getLine() != 0 + || offsetPosition.getColumn() != 0) { + // If the mapping is found on the first line, we need to offset + // its character position by the number of characters found on + // the *last* line of the source file to which the code is + // being generated. + int offsetLine = offsetPosition.getLine(); + int startOffsetPosition = offsetPosition.getColumn(); + int endOffsetPosition = offsetPosition.getColumn(); + + if (startPosition.getLine() > 0) { + startOffsetPosition = 0; + } + + if (endPosition.getLine() > 0) { + endOffsetPosition = 0; + } + + adjustedStart = new FilePosition( + startPosition.getLine() + offsetLine, + startPosition.getColumn() + startOffsetPosition); + + adjustedEnd = new FilePosition( + endPosition.getLine() + offsetLine, + endPosition.getColumn() + endOffsetPosition); + } + + // Create the new mapping. + Mapping mapping = new Mapping(); + mapping.sourceFile = getSourceId(sourceName); + mapping.originalPosition = sourceStartPosition; + mapping.originalName = symbolName; + mapping.startPosition = adjustedStart; + mapping.endPosition = adjustedEnd; + + // Validate the mappings are in a proper order. + if (lastMapping != null) { + int lastLine = lastMapping.startPosition.getLine(); + int lastColumn = lastMapping.startPosition.getColumn(); + int nextLine = mapping.startPosition.getLine(); + int nextColumn = mapping.startPosition.getColumn(); + Preconditions.checkState(nextLine > lastLine + || (nextLine == lastLine && nextColumn >= lastColumn), + "Incorrect source mappings order, previous : (%s,%s)\n" + + "new : (%s,%s)\nnode : %s", + lastLine, lastColumn, nextLine, nextColumn); + } + + lastMapping = mapping; + mappings.add(mapping); + } + + /** + * Writes out the source map in the following format (line numbers are for + * reference only and are not part of the format): + * + * 1. { + * 2. version: 2, + * 3. file: "out.js" + * 4. lineCount: 2 + * 5. lineMaps: [ + * 6. "ABAAA", + * 7. "ABAA" + * 8. ], + * 9. sourceRoot: "", + * 10. sources: ["foo.js", "bar.js"], + * 11. names: ["src", "maps", "are", "fun"], + * 12. mappings: [ + * 13. [1, 1, 2, 4], + * 14. [2, 1, 2, "yack"], + * 15. ], + * 16. } + * + * Line 1: The entire file is a single JSON object + * Line 2: File revision (always the first entry in the object) + * Line 3: The name of the file that this source map is associated with. + * Line 4: The number of lines represented in the source map. + * Line 5: "lineMaps" field is a JSON array, where each entry represents a + * line in the generated text. + * Line 6: A line entry, representing a series of line segments, where each + * segment encodes an mappings-id and repetition count. + * Line 9: An optional source root, useful for relocating source files on a + * server or removing repeated prefix values in the "sources" entry. + * Line 10: A list of sources used by the "mappings" entry relative to the + * sourceRoot. + * Line 11: A list of symbol names used by the "mapping" entry. This list + * may be incomplete. + * Line 12: The mappings field. + * Line 13: Each entry represent a block of text in the original source, and + * consists four fields: + * The source file name + * The line in the source file the text begins + * The column in the line that the text begins + * An optional name (from the original source) that this entry represents. + * This can either be an string or index into the "names" field. + */ + @Override + public void appendTo(Appendable out, String name) throws IOException { + int maxLine = prepMappings(); + + // Add the header fields. + out.append("{\n"); + appendFirstField(out, "version", "2"); + appendField(out, "file", escapeString(name)); + appendField(out, "lineCount", String.valueOf(maxLine + 1)); + + // Add the line character maps. + appendFieldStart(out, "lineMaps"); + out.append("["); + (new LineMapper(out)).appendLineMappings(); + out.append("]"); + appendFieldEnd(out); + + // Add the mappings themselves. + appendFieldStart(out, "mappings"); + out.append("["); + (new MappingWriter()).appendMappings(out); + out.append("]"); + appendFieldEnd(out); + + // Files names + appendFieldStart(out, "sources"); + out.append("["); + addSourceNameMap(out); + out.append("]"); + appendFieldEnd(out); + + // Files names + appendFieldStart(out, "names"); + out.append("["); + addOriginalNameMap(out); + out.append("]"); + appendFieldEnd(out); + + out.append("\n}\n"); + } + + /** + * Writes the source name map to 'out'. + */ + private void addSourceNameMap(Appendable out) throws IOException { + addMap(out, sourceFileMap); + } + + /** + * Writes the original name map to 'out'. + */ + private void addOriginalNameMap(Appendable out) throws IOException { + addMap(out, originalNameMap); + } + + /** + * Writes the source name map to 'out'. + */ + private void addMap(Appendable out, Map map) + throws IOException { + int i = 0; + for (Entry entry : map.entrySet()) { + String key = entry.getKey(); + if (i != 0) { + out.append(","); + } + out.append(escapeString(key)); + i++; + } + } + + /** + * Escapes the given string for JSON. + */ + private static String escapeString(String value) { + return Util.escapeString(value); + } + + // Source map field helpers. + + private static void appendFirstField( + Appendable out, String name, CharSequence value) + throws IOException { + out.append("\""); + out.append(name); + out.append("\""); + out.append(":"); + out.append(value); + } + + private static void appendField( + Appendable out, String name, CharSequence value) + throws IOException { + out.append(",\n"); + out.append("\""); + out.append(name); + out.append("\""); + out.append(":"); + out.append(value); + } + + private static void appendFieldStart(Appendable out, String name) + throws IOException { + appendField(out, name, ""); + } + + @SuppressWarnings("unused") + private static void appendFieldEnd(Appendable out) + throws IOException { + } + + /** + * Assigns sequential ids to used mappings, and returns the last line mapped. + */ + private int prepMappings() throws IOException { + // Mark any unused mappings. + (new MappingTraversal()).traverse(new UsedMappingCheck()); + + // Renumber used mappings and keep track of the last line. + int id = 0; + int maxLine = 0; + for (Mapping m : mappings) { + if (m.used) { + m.id = id++; + int endPositionLine = m.endPosition.getLine(); + maxLine = Math.max(maxLine, endPositionLine); + } + } + + // Adjust for the prefix. + return maxLine + prefixPosition.getLine(); + } + + /** + * Pools source names. + * @param sourceName The source location to index. + * @return The id to represent the source name in the output. + */ + private int getSourceId(String sourceName) { + if (sourceName != lastSourceFile) { + lastSourceFile = sourceName; + Integer index = sourceFileMap.get(sourceName); + if (index != null) { + lastSourceFileIndex = index; + } else { + lastSourceFileIndex = sourceFileMap.size(); + sourceFileMap.put(sourceName, lastSourceFileIndex); + } + } + return lastSourceFileIndex; + } + + /** + * Pools symbol names + * @param symbolName The symbol name to index. + * @return The id to represent the symbol name in the output. + */ + private int getNameId(String symbolName) { + int originalNameIndex; + Integer index = originalNameMap.get(symbolName); + if (index != null) { + originalNameIndex = index; + } else { + originalNameIndex = originalNameMap.size(); + originalNameMap.put(symbolName, originalNameIndex); + } + return originalNameIndex; + } + + /** + * A mapping from a given position in an input source file to a given position + * in the generated code. + */ + static class Mapping { + /** + * A unique ID for this mapping for record keeping purposes. + */ + int id = UNMAPPED; + + /** + * The source file index. + */ + int sourceFile; + + /** + * The position of the code in the input source file. Both + * the line number and the character index are indexed by + * 1 for legacy reasons via the Rhino Node class. + */ + FilePosition originalPosition; + + /** + * The starting position of the code in the generated source + * file which this mapping represents. Indexed by 0. + */ + FilePosition startPosition; + + /** + * The ending position of the code in the generated source + * file which this mapping represents. Indexed by 0. + */ + FilePosition endPosition; + + /** + * The original name of the token found at the position + * represented by this mapping (if any). + */ + String originalName; + + /** + * Whether the mapping is actually used by the source map. + */ + boolean used = false; + } + + private class MappingWriter { + /** + * Cache of escaped source file name. + */ + private int lastLine = 0; + private String lastLineString = String.valueOf(0); + + /** + * Appends the mapping to the given buffer. + */ + private void appendMappingTo( + Mapping m, Appendable out) throws IOException { + out.append("["); + + out.append(String.valueOf(m.sourceFile)); + out.append(","); + + int line = m.originalPosition.getLine(); + if (line != lastLine) { + lastLineString = String.valueOf(line); + } + String lineValue = lastLineString; + + out.append(lineValue); + + out.append(","); + out.append(String.valueOf(m.originalPosition.getColumn())); + + if (m.originalName != null) { + out.append(","); + out.append(String.valueOf(getNameId(m.originalName))); + } + + out.append("],\n"); + } + + /** + * Add used mappings to the supplied Appendable. + */ + void appendMappings(Appendable out) throws IOException { + for (Mapping m : mappings) { + if (m.used) { + appendMappingTo(m, out); + } + } + } + } + + private class LineMapper implements MappingVisitor { + // The destination. + private final Appendable out; + + // Whether the current line has had a value written yet. + private int lastId = UNMAPPED; + + LineMapper(Appendable out) { + this.out = out; + } + + /** + * As each segment is visited write out the appropriate line mapping. + */ + @Override + public void visit(Mapping m, int line, int col, int nextLine, int nextCol) + throws IOException { + + int id = (m != null) ? m.id : UNMAPPED; + + for (int i = line; i <= nextLine; i++) { + if (i == nextLine) { + closeEntry(id, nextCol - col); + break; + } + + closeLine(false); + openLine(); + + // Set the starting location for the next line. + col = 0; + } + } + + // Append the line mapping entries. + void appendLineMappings() throws IOException { + // Start the first line. + openLine(); + + (new MappingTraversal()).traverse(this); + + // And close the final line. + closeLine(true); + } + + /** + * Begin the entry for a new line. + */ + private void openLine() throws IOException { + out.append("\""); + // The first id of the line is not relative. + this.lastId = 0; + } + + /** + * End the entry for a line. + */ + private void closeLine(boolean finalEntry) throws IOException { + if (finalEntry) { + out.append("\""); + } else { + out.append("\",\n"); + } + } + + private void closeEntry(int id, int reps) throws IOException { + if (reps == 0) { + return; + } + + StringBuilder sb = new StringBuilder(); + LineMapEncoder.encodeEntry(sb, id, lastId, reps); + + if (validate) { + SourceMapLineDecoder.LineEntry entry = + SourceMapLineDecoder.decodeLineEntry(sb.toString(), lastId); + Preconditions.checkState(entry.id == id && entry.reps == reps, + "expected (%s,%s) but got (%s,%s)", + id, reps, entry.id, entry.reps); + } + + out.append(sb); + lastId = id; + } + } + + @VisibleForTesting + public static class LineMapEncoder { + /** + * The source map line map is consists of a series of entries each + * representing a map entry and a repetition count of that entry. + * + * @param out The entry destination. + * @param id The id for the entry. + * @param lastId The previous id written, used to generate a relative + * map id. + * @param reps The number of times the id is repeated in the map. + * @throws IOException + */ + public static void encodeEntry(Appendable out, int id, int lastId, int reps) + throws IOException { + Preconditions.checkState(reps > 0); + int relativeIdLength = getRelativeMappingIdLength(id, lastId); + int relativeId = getRelativeMappingId(id, relativeIdLength, lastId); + + String relativeIdString = valueToBase64(relativeId, relativeIdLength); + + // If we can, we use a single base64 digit to encode both the id length + // and the repetition count. The current best division of the base64 + // digit (which has 6 bits) is 2 bits for the id length (1-4 digits) and + // 4 bit for the repetition count (1-16 repetitions). If either of these + // two values are exceeded a "!" is written (a non-base64 character) to + // signal the a full base64 character is used for repetition count and + // the mapping id length. As the repetition count can exceed 64, we + // allow the "escape" ("!") to be repeated to signal additional + // repetition count length characters. It is extremely unlikely that + // mapping id length will exceed 64 base64 characters in length so + // additional "!" don't signal additional id length characters. + if (reps > 16 || relativeIdLength > 4) { + String repsString = valueToBase64(reps - 1, 1); + for (int i = 0; i < repsString.length(); i++) { + // TODO(johnlenz): update this to whatever is agreed to. + out.append('!'); + } + String sizeId = valueToBase64(relativeIdString.length() - 1, 1); + + out.append(sizeId); + out.append(repsString); + } else { + int prefix = ((reps - 1) << 2) + (relativeIdString.length() - 1); + Preconditions.checkState(prefix < 64 && prefix >= 0, + "prefix (%s) reps(%s) map id size(%s)", + prefix, reps, relativeIdString.length()); + out.append(valueToBase64(prefix, 1)); + } + out.append(relativeIdString); + } + + /** + * @param idLength the length relative id, when encoded in as a base64 + * value. @see #getRelativeMappingIdLength + * @return A value relative to the the lastId. Negative value are + * represented as a two-complement value. + */ + public static int getRelativeMappingId(int id, int idLength, int lastId) { + int base = 1 << (idLength * 6); + int relativeId = id - lastId; + return (relativeId < 0) ? relativeId + base : relativeId; + } + + /** + * @return The length of the base64 number needed to include the id. + */ + public static int getRelativeMappingIdLength(int rawId, int lastId) { + Preconditions.checkState(rawId >= 0 || rawId == UNMAPPED); + int relativeId = rawId - lastId; + int id = (relativeId < 0 ? Math.abs(relativeId) - 1 : relativeId) << 1; + int digits = 1; + int base = 64; + while (id >= base) { + digits++; + base *= 64; + } + return digits; + } + + /** + * @return return the base64 number encoding the provided value, + * padded if necessary to create a number with the given minimum length. + */ + static String valueToBase64(int value, int minimumSize) { + int size = 0; + char chars[] = new char[4]; + do { + int charValue = value & 63; // base64 chars + value = value >>> 6; // get the next value; + chars[size++] = Base64.toBase64(charValue); + } while (value > 0); + + StringBuilder sb = new StringBuilder(size); + + while (minimumSize > size) { + sb.append(Base64.toBase64(0)); + minimumSize--; + } + while (size > 0) { + sb.append(chars[--size]); + } + return sb.toString(); + } + } + + /** + * Mark any visited mapping as "used". + */ + private class UsedMappingCheck implements MappingVisitor { + /** + * @throws IOException + */ + @Override + public void visit(Mapping m, int line, int col, int nextLine, int nextCol) + throws IOException { + if (m != null) { + m.used = true; + } + } + } + + private interface MappingVisitor { + /** + * @param m The mapping for the current code segment. null if the segment + * is unmapped. + * @param line The starting line for this code segment. + * @param col The starting column for this code segment. + * @param endLine The ending line + * @param endCol The ending column + * @throws IOException + */ + void visit(Mapping m, int line, int col, int endLine, int endCol) + throws IOException; + } + + /** + * Walk the mappings and visit each segment of the mappings, unmapped + * segments are visited with a null mapping, unused mapping are not visited. + */ + private class MappingTraversal { + // The last line and column written + private int line; + private int col; + + MappingTraversal() { + } + + // Append the line mapping entries. + void traverse(MappingVisitor v) throws IOException { + // The mapping list is ordered as a pre-order traversal. The mapping + // positions give us enough information to rebuild the stack and this + // allows the building of the source map in O(n) time. + Deque stack = new ArrayDeque(); + for (Mapping m : mappings) { + // Find the closest ancestor of the current mapping: + // An overlapping mapping is an ancestor of the current mapping, any + // non-overlapping mappings are siblings (or cousins) and must be + // closed in the reverse order of when they encountered. + while (!stack.isEmpty() && !isOverlapped(stack.peek(), m)) { + Mapping previous = stack.pop(); + maybeVisit(v, previous); + } + + // Any gaps between the current line position and the start of the + // current mapping belong to the parent. + Mapping parent = stack.peek(); + maybeVisitParent(v, parent, m); + + stack.push(m); + } + + // There are no more children to be had, simply close the remaining + // mappings in the reverse order of when they encountered. + while (!stack.isEmpty()) { + Mapping m = stack.pop(); + maybeVisit(v, m); + } + } + + /** + * @return The line adjusted for the prefix position. + */ + private int getAdjustedLine(FilePosition p) { + return p.getLine() + prefixPosition.getLine(); + } + + /** + * @return The column adjusted for the prefix position. + */ + private int getAdjustedCol(FilePosition p) { + int rawLine = p.getLine(); + int rawCol = p.getColumn(); + // Only the first line needs the character position adjusted. + return (rawLine != 0) + ? rawCol : rawCol + prefixPosition.getColumn(); + } + + /** + * @return Whether m1 ends before m2 starts. + */ + private boolean isOverlapped(Mapping m1, Mapping m2) { + // No need to use adjusted values here, relative positions are sufficient. + int l1 = m1.endPosition.getLine(); + int l2 = m2.startPosition.getLine(); + int c1 = m1.endPosition.getColumn(); + int c2 = m2.startPosition.getColumn(); + + return (l1 == l2 && c1 >= c2) || l1 > l2; + } + + /** + * Write any needed entries from the current position to the end of the + * provided mapping. + */ + private void maybeVisit(MappingVisitor v, Mapping m) throws IOException { + int nextLine = getAdjustedLine(m.endPosition); + int nextCol = getAdjustedCol(m.endPosition); + // If this anything remaining in this mapping beyond the + // current line and column position, write it out now. + if (line < nextLine || (line == nextLine && col < nextCol)) { + visit(v, m, nextLine, nextCol); + } + } + + /** + * Write any needed entries to complete the provided mapping. + */ + private void maybeVisitParent(MappingVisitor v, Mapping parent, Mapping m) + throws IOException { + int nextLine = getAdjustedLine(m.startPosition); + int nextCol = getAdjustedCol(m.startPosition); + // If the previous value is null, no mapping exists. + Preconditions.checkState(line < nextLine || col <= nextCol); + if (line < nextLine || (line == nextLine && col < nextCol)) { + visit(v, parent, nextLine, nextCol); + } + } + + /** + * Write any entries needed between the current position the next position + * and update the current position. + */ + private void visit(MappingVisitor v, Mapping m, + int nextLine, int nextCol) + throws IOException { + Preconditions.checkState(line <= nextLine); + Preconditions.checkState(line < nextLine || col < nextCol); + + if (line == nextLine && col == nextCol) { + // Nothing to do. + Preconditions.checkState(false); + return; + } + + v.visit(m, line, col, nextLine, nextCol); + + line = nextLine; + col = nextCol; + } + } + + @Override + public void appendIndexMapTo( + Appendable out, String name, List appSections) { + throw new UnsupportedOperationException(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapGeneratorV3.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapGeneratorV3.java new file mode 100644 index 0000000..5ba6e4b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapGeneratorV3.java @@ -0,0 +1,798 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.debugging.sourcemap.SourceMapConsumerV3.EntryVisitor; + +import java.io.IOException; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.Nullable; + +/** + * Collects information mapping the generated (compiled) source back to + * its original source for debugging purposes. + * + * @author johnlenz@google.com (John Lenz) + */ +public class SourceMapGeneratorV3 implements SourceMapGenerator { + + private static final int UNMAPPED = -1; + + + /** + * A pre-order traversal ordered list of mappings stored in this map. + */ + private List mappings = Lists.newArrayList(); + + /** + * A map of source names to source name index + */ + private LinkedHashMap sourceFileMap = + Maps.newLinkedHashMap(); + + /** + * A map of source names to source name index + */ + private LinkedHashMap originalNameMap = + Maps.newLinkedHashMap(); + + /** + * Cache of the last mappings source name. + */ + private String lastSourceFile = null; + + /** + * Cache of the last mappings source name index. + */ + private int lastSourceFileIndex = -1; + + /** + * For validation store the last mapping added. + */ + private Mapping lastMapping; + + /** + * The position that the current source map is offset in the + * buffer being used to generated the compiled source file. + */ + private FilePosition offsetPosition = new FilePosition(0, 0); + + /** + * The position that the current source map is offset in the + * generated the compiled source file by the addition of a + * an output wrapper prefix. + */ + private FilePosition prefixPosition = new FilePosition(0, 0); + + + /** + * {@inheritDoc} + */ + @Override + public void reset() { + mappings.clear(); + lastMapping = null; + sourceFileMap.clear(); + originalNameMap.clear(); + lastSourceFile = null; + lastSourceFileIndex = -1; + offsetPosition = new FilePosition(0, 0); + prefixPosition = new FilePosition(0, 0); + } + + /** + * @param validate Whether to perform (potentially costly) validation on the + * generated source map. + */ + @Override + public void validate(boolean validate) { + // Nothing currently. + } + + /** + * Sets the prefix used for wrapping the generated source file before + * it is written. This ensures that the source map is adjusted for the + * change in character offsets. + * + * @param prefix The prefix that is added before the generated source code. + */ + @Override + public void setWrapperPrefix(String prefix) { + // Determine the current line and character position. + int prefixLine = 0; + int prefixIndex = 0; + + for (int i = 0; i < prefix.length(); ++i) { + if (prefix.charAt(i) == '\n') { + prefixLine++; + prefixIndex = 0; + } else { + prefixIndex++; + } + } + + prefixPosition = new FilePosition(prefixLine, prefixIndex); + } + + /** + * Sets the source code that exists in the buffer for which the + * generated code is being generated. This ensures that the source map + * accurately reflects the fact that the source is being appended to + * an existing buffer and as such, does not start at line 0, position 0 + * but rather some other line and position. + * + * @param offsetLine The index of the current line being printed. + * @param offsetIndex The column index of the current character being printed. + */ + @Override + public void setStartingPosition(int offsetLine, int offsetIndex) { + Preconditions.checkState(offsetLine >= 0); + Preconditions.checkState(offsetIndex >= 0); + offsetPosition = new FilePosition(offsetLine, offsetIndex); + } + + /** + * Adds a mapping for the given node. Mappings must be added in order. + * @param startPosition The position on the starting line + * @param endPosition The position on the ending line. + */ + @Override + public void addMapping( + String sourceName, @Nullable String symbolName, + FilePosition sourceStartPosition, + FilePosition startPosition, FilePosition endPosition) { + + // Don't bother if there is not sufficient information to be useful. + if (sourceName == null || sourceStartPosition.getLine() < 0) { + return; + } + + FilePosition adjustedStart = startPosition; + FilePosition adjustedEnd = endPosition; + + if (offsetPosition.getLine() != 0 + || offsetPosition.getColumn() != 0) { + // If the mapping is found on the first line, we need to offset + // its character position by the number of characters found on + // the *last* line of the source file to which the code is + // being generated. + int offsetLine = offsetPosition.getLine(); + int startOffsetPosition = offsetPosition.getColumn(); + int endOffsetPosition = offsetPosition.getColumn(); + + if (startPosition.getLine() > 0) { + startOffsetPosition = 0; + } + + if (endPosition.getLine() > 0) { + endOffsetPosition = 0; + } + + adjustedStart = new FilePosition( + startPosition.getLine() + offsetLine, + startPosition.getColumn() + startOffsetPosition); + + adjustedEnd = new FilePosition( + endPosition.getLine() + offsetLine, + endPosition.getColumn() + endOffsetPosition); + } + + // Create the new mapping. + Mapping mapping = new Mapping(); + mapping.sourceFile = sourceName; + mapping.originalPosition = sourceStartPosition; + mapping.originalName = symbolName; + mapping.startPosition = adjustedStart; + mapping.endPosition = adjustedEnd; + + // Validate the mappings are in a proper order. + if (lastMapping != null) { + int lastLine = lastMapping.startPosition.getLine(); + int lastColumn = lastMapping.startPosition.getColumn(); + int nextLine = mapping.startPosition.getLine(); + int nextColumn = mapping.startPosition.getColumn(); + Preconditions.checkState(nextLine > lastLine + || (nextLine == lastLine && nextColumn >= lastColumn), + "Incorrect source mappings order, previous : (%s,%s)\n" + + "new : (%s,%s)\nnode : %s", + lastLine, lastColumn, nextLine, nextColumn); + } + + lastMapping = mapping; + mappings.add(mapping); + } + + class ConsumerEntryVisitor implements EntryVisitor { + + @Override + public void visit( + String sourceName, String symbolName, + FilePosition sourceStartPosition, + FilePosition startPosition, FilePosition endPosition) { + addMapping(sourceName, symbolName, + sourceStartPosition, startPosition, endPosition); + } + } + + public void mergeMapSection(int line, int column, String mapSectionContents) + throws SourceMapParseException { + setStartingPosition(line, column); + SourceMapConsumerV3 section = new SourceMapConsumerV3(); + section.parse(mapSectionContents); + section.visitMappings(new ConsumerEntryVisitor()); + } + + /** + * Writes out the source map in the following format (line numbers are for + * reference only and are not part of the format): + * + * 1. { + * 2. version: 3, + * 3. file: "out.js", + * 4. lineCount: 2, + * 5. sourceRoot: "", + * 6. sources: ["foo.js", "bar.js"], + * 7. names: ["src", "maps", "are", "fun"], + * 8. mappings: "a;;abcde,abcd,a;" + * 9. } + * + * Line 1: The entire file is a single JSON object + * Line 2: File revision (always the first entry in the object) + * Line 3: The name of the file that this source map is associated with. + * Line 4: The number of lines represented in the source map. + * Line 5: An optional source root, useful for relocating source files on a + * server or removing repeated prefix values in the "sources" entry. + * Line 6: A list of sources used by the "mappings" entry relative to the + * sourceRoot. + * Line 7: A list of symbol names used by the "mapping" entry. This list + * may be incomplete. + * Line 8: The mappings field. + */ + @Override + public void appendTo(Appendable out, String name) throws IOException { + int maxLine = prepMappings(); + + // Add the header fields. + out.append("{\n"); + appendFirstField(out, "version", "3"); + appendField(out, "file", escapeString(name)); + appendField(out, "lineCount", String.valueOf(maxLine + 1)); + + // Add the mappings themselves. + appendFieldStart(out, "mappings"); + // out.append("["); + (new LineMapper(out)).appendLineMappings(); + // out.append("]"); + appendFieldEnd(out); + + // Files names + appendFieldStart(out, "sources"); + out.append("["); + addSourceNameMap(out); + out.append("]"); + appendFieldEnd(out); + + // Files names + appendFieldStart(out, "names"); + out.append("["); + addSymbolNameMap(out); + out.append("]"); + appendFieldEnd(out); + + out.append("\n}\n"); + } + + /** + * Writes the source name map to 'out'. + */ + private void addSourceNameMap(Appendable out) throws IOException { + addNameMap(out, sourceFileMap); + } + + /** + * Writes the source name map to 'out'. + */ + private void addSymbolNameMap(Appendable out) throws IOException { + addNameMap(out, originalNameMap); + } + + private void addNameMap(Appendable out, Map map) + throws IOException { + int i = 0; + for (Entry entry : map.entrySet()) { + String key = entry.getKey(); + if (i != 0) { + out.append(","); + } + out.append(escapeString(key)); + i++; + } + } + + /** + * Escapes the given string for JSON. + */ + private static String escapeString(String value) { + return Util.escapeString(value); + } + + // Source map field helpers. + + private static void appendFirstField( + Appendable out, String name, CharSequence value) + throws IOException { + out.append("\""); + out.append(name); + out.append("\""); + out.append(":"); + out.append(value); + } + + private static void appendField( + Appendable out, String name, CharSequence value) + throws IOException { + out.append(",\n"); + out.append("\""); + out.append(name); + out.append("\""); + out.append(":"); + out.append(value); + } + + private static void appendFieldStart(Appendable out, String name) + throws IOException { + appendField(out, name, ""); + } + + @SuppressWarnings("unused") + private static void appendFieldEnd(Appendable out) + throws IOException { + } + + /** + * Assigns sequential ids to used mappings, and returns the last line mapped. + */ + private int prepMappings() throws IOException { + // Mark any unused mappings. + (new MappingTraversal()).traverse(new UsedMappingCheck()); + + // Renumber used mappings and keep track of the last line. + int id = 0; + int maxLine = 0; + int sourceId = 0; + int nameId = 0; + for (Mapping m : mappings) { + if (m.used) { + m.id = id++; + int endPositionLine = m.endPosition.getLine(); + maxLine = Math.max(maxLine, endPositionLine); + } + } + + // Adjust for the prefix. + return maxLine + prefixPosition.getLine(); + } + + /** + * A mapping from a given position in an input source file to a given position + * in the generated code. + */ + static class Mapping { + /** + * A unique ID for this mapping for record keeping purposes. + */ + int id = UNMAPPED; + + /** + * The source file index. + */ + String sourceFile; + + /** + * The position of the code in the input source file. Both + * the line number and the character index are indexed by + * 1 for legacy reasons via the Rhino Node class. + */ + FilePosition originalPosition; + + /** + * The starting position of the code in the generated source + * file which this mapping represents. Indexed by 0. + */ + FilePosition startPosition; + + /** + * The ending position of the code in the generated source + * file which this mapping represents. Indexed by 0. + */ + FilePosition endPosition; + + /** + * The original name of the token found at the position + * represented by this mapping (if any). + */ + String originalName; + + /** + * Whether the mapping is actually used by the source map. + */ + boolean used = false; + } + + /** + * Mark any visited mapping as "used". + */ + private class UsedMappingCheck implements MappingVisitor { + /** + * @throws IOException + */ + @Override + public void visit(Mapping m, int line, int col, int nextLine, int nextCol) + throws IOException { + if (m != null) { + m.used = true; + } + } + } + + private interface MappingVisitor { + /** + * @param m The mapping for the current code segment. null if the segment + * is unmapped. + * @param line The starting line for this code segment. + * @param col The starting column for this code segment. + * @param endLine The ending line + * @param endCol The ending column + * @throws IOException + */ + void visit(Mapping m, int line, int col, int endLine, int endCol) + throws IOException; + } + + /** + * Walk the mappings and visit each segment of the mappings, unmapped + * segments are visited with a null mapping, unused mapping are not visited. + */ + private class MappingTraversal { + // The last line and column written + private int line; + private int col; + + MappingTraversal() { + } + + // Append the line mapping entries. + void traverse(MappingVisitor v) throws IOException { + // The mapping list is ordered as a pre-order traversal. The mapping + // positions give us enough information to rebuild the stack and this + // allows the building of the source map in O(n) time. + Deque stack = new ArrayDeque(); + for (Mapping m : mappings) { + // Find the closest ancestor of the current mapping: + // An overlapping mapping is an ancestor of the current mapping, any + // non-overlapping mappings are siblings (or cousins) and must be + // closed in the reverse order of when they encountered. + while (!stack.isEmpty() && !isOverlapped(stack.peek(), m)) { + Mapping previous = stack.pop(); + maybeVisit(v, previous); + } + + // Any gaps between the current line position and the start of the + // current mapping belong to the parent. + Mapping parent = stack.peek(); + maybeVisitParent(v, parent, m); + + stack.push(m); + } + + // There are no more children to be had, simply close the remaining + // mappings in the reverse order of when they encountered. + while (!stack.isEmpty()) { + Mapping m = stack.pop(); + maybeVisit(v, m); + } + } + + /** + * @return The line adjusted for the prefix position. + */ + private int getAdjustedLine(FilePosition p) { + return p.getLine() + prefixPosition.getLine(); + } + + /** + * @return The column adjusted for the prefix position. + */ + private int getAdjustedCol(FilePosition p) { + int rawLine = p.getLine(); + int rawCol = p.getColumn(); + // Only the first line needs the character position adjusted. + return (rawLine != 0) + ? rawCol : rawCol + prefixPosition.getColumn(); + } + + /** + * @return Whether m1 ends before m2 starts. + */ + private boolean isOverlapped(Mapping m1, Mapping m2) { + // No need to use adjusted values here, relative positions are sufficient. + int l1 = m1.endPosition.getLine(); + int l2 = m2.startPosition.getLine(); + int c1 = m1.endPosition.getColumn(); + int c2 = m2.startPosition.getColumn(); + + return (l1 == l2 && c1 >= c2) || l1 > l2; + } + + /** + * Write any needed entries from the current position to the end of the + * provided mapping. + */ + private void maybeVisit(MappingVisitor v, Mapping m) throws IOException { + int nextLine = getAdjustedLine(m.endPosition); + int nextCol = getAdjustedCol(m.endPosition); + // If this anything remaining in this mapping beyond the + // current line and column position, write it out now. + if (line < nextLine || (line == nextLine && col < nextCol)) { + visit(v, m, nextLine, nextCol); + } + } + + /** + * Write any needed entries to complete the provided mapping. + */ + private void maybeVisitParent(MappingVisitor v, Mapping parent, Mapping m) + throws IOException { + int nextLine = getAdjustedLine(m.startPosition); + int nextCol = getAdjustedCol(m.startPosition); + // If the previous value is null, no mapping exists. + Preconditions.checkState(line < nextLine || col <= nextCol); + if (line < nextLine || (line == nextLine && col < nextCol)) { + visit(v, parent, nextLine, nextCol); + } + } + + /** + * Write any entries needed between the current position the next position + * and update the current position. + */ + private void visit(MappingVisitor v, Mapping m, + int nextLine, int nextCol) + throws IOException { + Preconditions.checkState(line <= nextLine); + Preconditions.checkState(line < nextLine || col < nextCol); + + if (line == nextLine && col == nextCol) { + // Nothing to do. + Preconditions.checkState(false); + return; + } + + v.visit(m, line, col, nextLine, nextCol); + + line = nextLine; + col = nextCol; + } + } + + /** + * Appends the index source map to the given buffer. + * + * @param out The stream to which the map will be appended. + * @param name The name of the generated source file that this source map + * represents. + * @param sections An ordered list of map sections to include in the index. + * @throws IOException + */ + @Override + public void appendIndexMapTo( + Appendable out, String name, List sections) + throws IOException { + // Add the header fields. + out.append("{\n"); + appendFirstField(out, "version", "3"); + appendField(out, "file", escapeString(name)); + + // Add the line character maps. + appendFieldStart(out, "sections"); + out.append("[\n"); + boolean first = true; + int line = 0, column = 0; + for (SourceMapSection section : sections) { + if (first) { + first = false; + } else { + out.append(",\n"); + } + out.append("{\n"); + appendFirstField(out, "offset", + offsetValue(section.getLine(), section.getColumn())); + if (section.getSectionType() == SourceMapSection.SectionType.URL) { + appendField(out, "url", escapeString(section.getSectionValue())); + } else if (section.getSectionType() == SourceMapSection.SectionType.MAP) { + appendField(out, "map", section.getSectionValue()); + } else { + throw new IOException("Unexpected section type"); + } + out.append("\n}"); + } + + out.append("\n]"); + appendFieldEnd(out); + + out.append("\n}\n"); + } + + private CharSequence offsetValue(int line, int column) throws IOException { + StringBuilder out = new StringBuilder(); + out.append("{\n"); + appendFirstField(out, "line", String.valueOf(line)); + appendField(out, "column", String.valueOf(column)); + out.append("\n}"); + return out; + } + + private int getSourceId(String sourceName) { + if (sourceName != lastSourceFile) { + lastSourceFile = sourceName; + Integer index = sourceFileMap.get(sourceName); + if (index != null) { + lastSourceFileIndex = index; + } else { + lastSourceFileIndex = sourceFileMap.size(); + sourceFileMap.put(sourceName, lastSourceFileIndex); + } + } + return lastSourceFileIndex; + } + + private int getNameId(String symbolName) { + int originalNameIndex; + Integer index = originalNameMap.get(symbolName); + if (index != null) { + originalNameIndex = index; + } else { + originalNameIndex = originalNameMap.size(); + originalNameMap.put(symbolName, originalNameIndex); + } + return originalNameIndex; + } + + private class LineMapper implements MappingVisitor { + // The destination. + private final Appendable out; + + private int previousLine = -1; + private int previousColumn = 0; + + // Previous values used for storing relative ids. + private int previousSourceFileId; + private int previousSourceLine; + private int previousSourceColumn; + private int previousNameId; + + LineMapper(Appendable out) { + this.out = out; + } + + /** + * As each segment is visited write out the appropriate line mapping. + */ + @Override + public void visit(Mapping m, int line, int col, int nextLine, int nextCol) + throws IOException { + + int id = (m != null) ? m.id : UNMAPPED; + + if (previousLine != line) { + previousColumn = 0; + } + + if (line != nextLine || col != nextCol) { + if (previousLine == line) { // not the first entry for the line + out.append(','); + } + writeEntry(m, col); + previousLine = line; + previousColumn = col; + } + + for (int i = line; i <= nextLine; i++) { + if (i == nextLine) { + break; + } + + closeLine(false); + openLine(false); + } + } + + /** + * Writes an entry for the given column (of the generated text) and + * associated mapping. + * The values are stored as relative to the last seen values for each + * field and encoded as Base64VLQs. + */ + void writeEntry(Mapping m, int column) throws IOException { + // The relative generated column number + Base64VLQ.encode(out, column - previousColumn); + previousColumn = column; + if (m != null) { + // The relative source file id + int sourceId = getSourceId(m.sourceFile); + Base64VLQ.encode(out, sourceId - previousSourceFileId); + previousSourceFileId = sourceId; + + // The relative source file line and column + int srcline = m.originalPosition.getLine(); + int srcColumn = m.originalPosition.getColumn(); + Base64VLQ.encode(out, srcline - previousSourceLine); + previousSourceLine = srcline; + + Base64VLQ.encode(out, srcColumn - previousSourceColumn); + previousSourceColumn = srcColumn; + + if (m.originalName != null) { + // The relative id for the associated symbol name + int nameId = getNameId(m.originalName); + Base64VLQ.encode(out, (nameId - previousNameId)); + previousNameId = nameId; + } + } + } + + // Append the line mapping entries. + void appendLineMappings() throws IOException { + // Start the first line. + openLine(true); + + (new MappingTraversal()).traverse(this); + + // And close the final line. + closeLine(true); + } + + /** + * Begin the entry for a new line. + */ + private void openLine(boolean firstEntry) throws IOException { + if (firstEntry) { + out.append('\"'); + } + } + + /** + * End the entry for a line. + */ + private void closeLine(boolean finalEntry) throws IOException { + out.append(';'); + if (finalEntry) { + out.append('\"'); + } + } + } + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapLineDecoder.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapLineDecoder.java new file mode 100644 index 0000000..7805b12 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapLineDecoder.java @@ -0,0 +1,157 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import com.google.common.collect.Lists; + +import java.util.List; + +/** + * Class for parsing the line maps in SourceMap v2. + * + * @author johnlenz@google.com (John Lenz) + * @author jschorr@google.com (Joseph Schorr) + */ +class SourceMapLineDecoder { + + /** + * Decodes a line in a character map into a list of mapping IDs. + */ + static List decodeLine(String lineSource) { + return decodeLine(new StringParser(lineSource)); + } + + private SourceMapLineDecoder() {} + + static LineEntry decodeLineEntry(String in, int lastId) { + return decodeLineEntry(new StringParser(in), lastId); + } + + private static LineEntry decodeLineEntry(StringParser reader, int lastId) { + int repDigits = 0; + + // Determine the number of digits used for the repetition count. + // Each "!" indicates another base64 digit. + for (char peek = reader.peek(); peek == '!'; peek = reader.peek()) { + repDigits++; + reader.next(); // consume the "!" + } + + int idDigits = 0; + int reps = 0; + if (repDigits == 0) { + // No repetition digit escapes, so the next character represents the + // number of digits in the id (bottom 2 bits) and the number of + // repetitions (top 4 digits). + char digit = reader.next(); + int value = addBase64Digit(digit, 0); + reps = (value >> 2); + idDigits = (value & 3); + } else { + char digit = reader.next(); + idDigits = addBase64Digit(digit, 0); + + int value = 0; + for (int i = 0; i < repDigits; i++) { + digit = reader.next(); + value = addBase64Digit(digit, value); + } + reps = value; + } + + // Adjust for 1 offset encoding. + reps += 1; + idDigits += 1; + + // Decode the id token. + int value = 0; + for (int i = 0; i < idDigits; i++) { + char digit = reader.next(); + value = addBase64Digit(digit, value); + } + int mappingId = getIdFromRelativeId(value, idDigits, lastId); + return new LineEntry(mappingId, reps); + } + + private static List decodeLine(StringParser reader) { + List result = Lists.newArrayListWithCapacity(512); + int lastId = 0; + while (reader.hasNext()) { + LineEntry entry = decodeLineEntry(reader, lastId); + lastId = entry.id; + + for (int i=0; i < entry.reps; i++) { + result.add(entry.id); + } + } + + return result; + } + + /** + * Build base64 number a digit at a time, most significant digit first. + */ + private static int addBase64Digit(char digit, int previousValue) { + return (previousValue * 64) + Base64.fromBase64(digit); + } + + /** + * @return the id from the relative id. + */ + static int getIdFromRelativeId(int rawId, int digits, int lastId) { + // The value range depends on the number of digits + int base = 1 << (digits * 6); + return ((rawId >= base/2) ? rawId - base : rawId) + lastId; + } + + /** + * Simple class for tracking a single entry in a line map. + */ + static class LineEntry { + final int id; + final int reps; + public LineEntry(int id, int reps) { + this.id = id; + this.reps = reps; + } + } + + /** + * A simple class for maintaining the current location + * in the input. + */ + static class StringParser { + final String content; + int current = 0; + + StringParser(String content) { + this.content = content; + } + + char next() { + return content.charAt(current++); + } + + char peek() { + return content.charAt(current); + } + + boolean hasNext() { + return current < content.length() -1; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapParseException.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapParseException.java new file mode 100644 index 0000000..6ddd5e7 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapParseException.java @@ -0,0 +1,26 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +/** + * Throw if an invalid or unknown source map is encountered. + */ +public class SourceMapParseException extends Exception { + public SourceMapParseException(String message) { + super(message); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapSection.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapSection.java new file mode 100644 index 0000000..3f41dbb --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapSection.java @@ -0,0 +1,105 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +/** + * A class representing a partial source map. + * @author johnlenz@google.com (John Lenz) + */ +public class SourceMapSection { + + /** + * A URL for a valid source map file that represents a section of a generate + * source file such as when multiple files are concatenated together. + */ + private final String value; + private final int line; + private final int column; + private final SectionType type; + + public static enum SectionType { + URL, + MAP + } + + /** + * @param sectionUrl The URL for the partial source map + * @param line The number of lines into the file where the represented section + * starts. + * @param column The number of characters into the line where the represented + * section starts. + * @deprecated + */ + @Deprecated + public SourceMapSection(String sectionUrl, int line, int column) { + this.type = SectionType.URL; + this.value = sectionUrl; + this.line = line; + this.column = column; + } + + private SourceMapSection( + SectionType type, String value, int line, int column) { + this.type = type; + this.value = value; + this.line = line; + this.column = column; + } + + public static SourceMapSection forMap(String value, int line, int column) { + return new SourceMapSection(SectionType.MAP, value, line, column); + } + + public static SourceMapSection forURL(String value, int line, int column) { + return new SourceMapSection(SectionType.URL, value, line, column); + } + + public SectionType getSectionType() { + return this.type; + } + + /** + * @return the name of the map + * @deprecated + */ + @Deprecated + public String getSectionUrl() { + assert(type.equals(SectionType.URL)); + return value; + } + + /** + * @return the value that represents the map for this section. + */ + public String getSectionValue() { + return value; + } + + /** + * @return the starting line for this section + */ + public int getLine() { + return line; + } + + /** + * @return the column for this section + */ + public int getColumn() { + return column; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapSupplier.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapSupplier.java new file mode 100644 index 0000000..17ede87 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapSupplier.java @@ -0,0 +1,36 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import java.io.IOException; + + +/** + * A class for mapping source map names to the actual contents. Used + * when parsing index maps. + * + * @author johnlenz@google.com (John Lenz) + */ +public interface SourceMapSupplier { + + /** + * @param url The URL of the source map. + * @return The contents of the map associated with the URL + * @throws IOException + */ + String getSourceMap(String url) throws IOException; +} \ No newline at end of file diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapping.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapping.java new file mode 100644 index 0000000..b1bd557 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMapping.java @@ -0,0 +1,35 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; + +/** + * Interface for provide a way of mapping (line, column) positions back to + * positions in the original (uncompiled) source code. + * + */ +public interface SourceMapping { + /** + * Returns the original mapping for the line number and column position found + * in the source map. Returns null if none is found. + * + * @param lineNumber The line number, with the first being '1'. + * @param columnIndex The column index, with the first being '1'. + */ + OriginalMapping getMappingForLine(int lineNumber, int columnIndex); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMappingReversable.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMappingReversable.java new file mode 100644 index 0000000..892eff1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/SourceMappingReversable.java @@ -0,0 +1,46 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; + +import java.util.Collection; + +/** + * A SourceMappingReversable is a SourceMapping that can provide the reverse + * (source --> target) source mapping. + */ +public interface SourceMappingReversable extends SourceMapping { + + /** + * @return the collection of original sources in this source mapping + */ + public Collection getOriginalSources(); + + /** + * Given a source file, line, and column, return the reverse mapping (source --> target). + * A collection is returned as in some cases (like a function being inlined), one source line + * may map to more then one target location. An empty collection is returned if there were + * no matches. + * @param originalFile the source file + * @param line the source line + * @param column the source column + * @return the reverse mapping (source --> target) + */ + public Collection getReverseMapping(String originalFile, int line, int column); + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/Util.java b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/Util.java new file mode 100644 index 0000000..260c1e4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/Util.java @@ -0,0 +1,149 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.debugging.sourcemap; + +import java.io.IOException; +import java.nio.charset.CharsetEncoder; + +/** + * @author johnlenz@google.com (John Lenz) + */ +class Util { + + private static final char[] HEX_CHARS + = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + /** + * Escapes the given string to a double quoted (") JavaScript/JSON string + */ + static String escapeString(String s) { + return escapeString(s, '"', "\\\"", "\'", "\\\\", null); + } + + /** Helper to escape JavaScript string as well as regular expression */ + static String escapeString(String s, char quote, + String doublequoteEscape, + String singlequoteEscape, + String backslashEscape, + CharsetEncoder outputCharsetEncoder) { + StringBuilder sb = new StringBuilder(s.length() + 2); + sb.append(quote); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + switch (c) { + case '\n': sb.append("\\n"); break; + case '\r': sb.append("\\r"); break; + case '\t': sb.append("\\t"); break; + case '\\': sb.append(backslashEscape); break; + case '\"': sb.append(doublequoteEscape); break; + case '\'': sb.append(singlequoteEscape); break; + case '>': // Break --> into --\> or ]]> into ]]\> + if (i >= 2 && + ((s.charAt(i - 1) == '-' && s.charAt(i - 2) == '-') || + (s.charAt(i - 1) == ']' && s.charAt(i - 2) == ']'))) { + sb.append("\\>"); + } else { + sb.append(c); + } + break; + case '<': + // Break 0x1f && c <= 0x7f) { + sb.append(c); + } else { + // Other characters can be misinterpreted by some JS parsers, + // or perhaps mangled by proxies along the way, + // so we play it safe and Unicode escape them. + appendCharAsHex(sb, c); + } + } + } + } + sb.append(quote); + return sb.toString(); + } + + /** + * @see #appendHexJavaScriptRepresentation(Appendable, int) + */ + @SuppressWarnings("cast") + private static void appendCharAsHex( + StringBuilder sb, char c) { + try { + appendHexJavaScriptRepresentation(sb, (int)c); + } catch (IOException ex) { + // StringBuilder does not throw IOException. + throw new RuntimeException(ex); + } + } + + /** + * Returns a JavaScript representation of the character in a hex escaped + * format. + * @param out The buffer to which the hex representation should be appended. + * @param codePoint The code point to append. + */ + private static void appendHexJavaScriptRepresentation( + Appendable out, int codePoint) + throws IOException { + if (Character.isSupplementaryCodePoint(codePoint)) { + // Handle supplementary Unicode values which are not representable in + // JavaScript. We deal with these by escaping them as two 4B sequences + // so that they will round-trip properly when sent from Java to JavaScript + // and back. + char[] surrogates = Character.toChars(codePoint); + appendHexJavaScriptRepresentation(out, surrogates[0]); + appendHexJavaScriptRepresentation(out, surrogates[1]); + return; + } + out.append("\\u") + .append(HEX_CHARS[(codePoint >>> 12) & 0xf]) + .append(HEX_CHARS[(codePoint >>> 8) & 0xf]) + .append(HEX_CHARS[(codePoint >>> 4) & 0xf]) + .append(HEX_CHARS[codePoint & 0xf]); + } + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/package.html b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/package.html new file mode 100644 index 0000000..ee22855 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/package.html @@ -0,0 +1,11 @@ + + + + + + + +Provides utilities to the creation and use of source maps. + + + diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/proto/mapping.proto b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/proto/mapping.proto new file mode 100644 index 0000000..145e681 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/debugging/sourcemap/proto/mapping.proto @@ -0,0 +1,39 @@ +// Copyright 2009 Google Inc. All rights reserved. +// +// Protocol Buffer definitions of the various source map structures. +// +// Author: jschorr@google.com (Joseph Schorr) + +syntax = "proto2"; + +package sourcemap; + +option java_package = "com.google.debugging.sourcemap.proto"; +//option java_api_version = 2; + +// Maps a position on a given line to the mapping describing +// the original code. +message LineMapping { + // The line number of the generated code. + optional int32 line_number = 1; + + // The column position on the line. + optional int32 column_position = 2; + + // The original mapping for this line mapping. + optional OriginalMapping original_mapping = 3; +} + +message OriginalMapping { + // The original source file. + optional string original_file = 1; + + // The line in the original file. + optional int32 line_number = 2; + + // The column number on the line. + optional int32 column_position = 3; + + // The original name of the identifier. + optional string identifier = 4; +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AbstractCommandLineRunner.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AbstractCommandLineRunner.java new file mode 100644 index 0000000..ffa8bfe --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AbstractCommandLineRunner.java @@ -0,0 +1,2101 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Charsets; +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.base.Supplier; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.common.io.Files; +import com.google.javascript.jscomp.CompilerOptions.TweakProcessing; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.TokenStream; +import com.google.protobuf.CodedOutputStream; + +import java.io.BufferedWriter; +import java.io.Closeable; +import java.io.File; +import java.io.FileOutputStream; +import java.io.Flushable; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; + +import javax.annotation.Nullable; + +/** + * Implementations of AbstractCommandLineRunner translate flags into Java + * API calls on the Compiler. AbstractCompiler contains common flags and logic + * to make that happen. + * + * This class may be extended and used to create other Java classes + * that behave the same as running the Compiler from the command line. Example: + * + *
+ * class MyCommandLineRunner extends
+ *     AbstractCommandLineRunner {
+ *   MyCommandLineRunner(String[] args) {
+ *     super(args);
+ *   }
+ *
+ *   @Override
+ *   protected MyOptions createOptions() {
+ *     MyOptions options = new MyOptions();
+ *     CompilerFlagTranslator.setOptionsFromFlags(options);
+ *     addMyCrazyCompilerPassThatOutputsAnExtraFile(options);
+ *     return options;
+ *   }
+ *
+ *   @Override
+ *   protected MyCompiler createCompiler() {
+ *     return new MyCompiler();
+ *   }
+ *
+ *   public static void main(String[] args) {
+ *     (new MyCommandLineRunner(args)).run();
+ *   }
+ * }
+ * 
+ * + * @author bolinfest@google.com (Michael Bolin) + */ +abstract class AbstractCommandLineRunner { + static final DiagnosticType OUTPUT_SAME_AS_INPUT_ERROR = DiagnosticType.error( + "JSC_OUTPUT_SAME_AS_INPUT_ERROR", + "Bad output file (already listed as input file): {0}"); + + private final CommandLineConfig config; + + private Appendable jsOutput; + private final PrintStream err; + private A compiler; + + private Charset inputCharset; + + // NOTE(nicksantos): JSCompiler has always used ASCII as the default + // output charset. This was done to solve legacy problems with + // bad proxies, etc. We are not sure if these issues are still problems, + // and changing the encoding would require a big obnoxious migration plan. + // + // New outputs should use outputCharset2, which is how I would have + // designed this if I had a time machine. + private Charset outputCharset2; + private String legacyOutputCharset; + + private boolean testMode = false; + private Supplier> externsSupplierForTesting = null; + private Supplier> inputsSupplierForTesting = null; + private Supplier> modulesSupplierForTesting = null; + private Function exitCodeReceiverForTesting = null; + private Map rootRelativePathsMap = null; + + private Map parsedModuleWrappers = null; + + // Bookkeeping to measure optimal phase orderings. + private static final int NUM_RUNS_TO_DETERMINE_OPTIMAL_ORDER = 100; + + private static final String OUTPUT_MARKER = "%output%"; + private static final String OUTPUT_MARKER_JS_STRING = "%output|jsstring%"; + + private final RunTimeStats runTimeStats = new RunTimeStats(); + + AbstractCommandLineRunner() { + this(System.out, System.err); + } + + AbstractCommandLineRunner(PrintStream out, PrintStream err) { + this.config = new CommandLineConfig(); + this.jsOutput = Preconditions.checkNotNull(out); + this.err = Preconditions.checkNotNull(err); + } + + /** + * Put the command line runner into test mode. In test mode, + * all outputs will be blackholed. + * @param externsSupplier A provider for externs. + * @param inputsSupplier A provider for source inputs. + * @param modulesSupplier A provider for modules. Only one of inputsSupplier + * and modulesSupplier may be non-null. + * @param exitCodeReceiver A receiver for the status code that would + * have been passed to System.exit in non-test mode. + */ + @VisibleForTesting + void enableTestMode( + Supplier> externsSupplier, + Supplier> inputsSupplier, + Supplier> modulesSupplier, + Function exitCodeReceiver) { + Preconditions.checkArgument( + inputsSupplier == null ^ modulesSupplier == null); + testMode = true; + this.externsSupplierForTesting = externsSupplier; + this.inputsSupplierForTesting = inputsSupplier; + this.modulesSupplierForTesting = modulesSupplier; + this.exitCodeReceiverForTesting = exitCodeReceiver; + } + + /** + * Returns whether we're in test mode. + */ + protected boolean isInTestMode() { + return testMode; + } + + /** + * Get the command line config, so that it can be initialized. + */ + protected CommandLineConfig getCommandLineConfig() { + return config; + } + + /** + * Returns the instance of the Compiler to use when {@link #run()} is + * called. + */ + protected abstract A createCompiler(); + + /** + * Returns the instance of the Options to use when {@link #run()} is called. + * createCompiler() is called before createOptions(), so getCompiler() + * will not return null when createOptions() is called. + */ + protected abstract B createOptions(); + + /** + * The warning classes that are available from the command-line. + */ + protected DiagnosticGroups getDiagnosticGroups() { + if (compiler == null) { + return new DiagnosticGroups(); + } + return compiler.getDiagnosticGroups(); + } + + /** No longer does anything. */ + @Deprecated + protected void initOptionsFromFlags(CompilerOptions options) {} + + /** + * A helper function for creating the dependency options object. + */ + static DependencyOptions createDependencyOptions( + boolean manageClosureDependencies, + boolean onlyClosureDependencies, + boolean processCommonJSModules, + List closureEntryPoints) + throws FlagUsageException { + if (onlyClosureDependencies) { + if (closureEntryPoints.isEmpty()) { + throw new FlagUsageException("When only_closure_dependencies is " + + "on, you must specify at least one closure_entry_point"); + } + + return new DependencyOptions() + .setDependencyPruning(true) + .setDependencySorting(true) + .setMoocherDropping(true) + .setEntryPoints(closureEntryPoints); + } else if (processCommonJSModules) { + return new DependencyOptions() + .setDependencyPruning(false) + .setDependencySorting(true) + .setMoocherDropping(false) + .setEntryPoints(closureEntryPoints); + } + else if (manageClosureDependencies || + closureEntryPoints.size() > 0) { + return new DependencyOptions() + .setDependencyPruning(true) + .setDependencySorting(true) + .setMoocherDropping(false) + .setEntryPoints(closureEntryPoints); + } + return null; + } + + /** + * Sets options based on the configurations set flags API. + * Called during the run() run() method. + * If you want to ignore the flags API, or interpret flags your own way, + * then you should override this method. + */ + protected void setRunOptions(CompilerOptions options) + throws FlagUsageException, IOException { + DiagnosticGroups diagnosticGroups = getDiagnosticGroups(); + + if (config.warningGuards != null) { + for (WarningGuardSpec.Entry entry : config.warningGuards.entries) { + diagnosticGroups.setWarningLevel(options, entry.groupName, entry.level); + } + } + + if (!config.warningsWhitelistFile.isEmpty()) { + options.addWarningsGuard( + WhitelistWarningsGuard.fromFile( + new File(config.warningsWhitelistFile))); + } + + createDefineOrTweakReplacements(config.define, options, false); + + options.setTweakProcessing(config.tweakProcessing); + createDefineOrTweakReplacements(config.tweak, options, true); + + DependencyOptions depOptions = createDependencyOptions( + config.manageClosureDependencies, + config.onlyClosureDependencies, + config.processCommonJSModules, + config.closureEntryPoints); + if (depOptions != null) { + options.setDependencyOptions(depOptions); + } + + options.devMode = config.jscompDevMode; + options.setCodingConvention(config.codingConvention); + options.setSummaryDetailLevel(config.summaryDetailLevel); + options.setTrustedStrings(true); + + legacyOutputCharset = options.outputCharset = getLegacyOutputCharset(); + outputCharset2 = getOutputCharset2(); + inputCharset = getInputCharset(); + + if (config.jsOutputFile.length() > 0) { + if (config.skipNormalOutputs) { + throw new FlagUsageException("skip_normal_outputs and js_output_file" + + " cannot be used together."); + } + } + + if (config.skipNormalOutputs && config.printAst) { + throw new FlagUsageException("skip_normal_outputs and print_ast cannot" + + " be used together."); + } + + if (config.skipNormalOutputs && config.printTree) { + throw new FlagUsageException("skip_normal_outputs and print_tree cannot" + + " be used together."); + } + + if (config.createSourceMap.length() > 0) { + options.sourceMapOutputPath = config.createSourceMap; + } + options.sourceMapDetailLevel = config.sourceMapDetailLevel; + options.sourceMapFormat = config.sourceMapFormat; + + if (!config.variableMapInputFile.equals("")) { + options.inputVariableMap = + VariableMap.load(config.variableMapInputFile); + } + + if (!config.propertyMapInputFile.equals("")) { + options.inputPropertyMap = + VariableMap.load(config.propertyMapInputFile); + } + + if (config.languageIn.length() > 0) { + CompilerOptions.LanguageMode languageMode = + CompilerOptions.LanguageMode.fromString(config.languageIn); + if (languageMode != null) { + options.setLanguageIn(languageMode); + } else { + throw new FlagUsageException("Unknown language `" + config.languageIn + + "' specified."); + } + } + + if (!config.outputManifests.isEmpty()) { + Set uniqueNames = Sets.newHashSet(); + for (String filename : config.outputManifests) { + if (!uniqueNames.add(filename)) { + throw new FlagUsageException("output_manifest flags specify " + + "duplicate file names: " + filename); + } + } + } + + if (!config.outputBundles.isEmpty()) { + Set uniqueNames = Sets.newHashSet(); + for (String filename : config.outputBundles) { + if (!uniqueNames.add(filename)) { + throw new FlagUsageException("output_bundle flags specify " + + "duplicate file names: " + filename); + } + } + } + + options.acceptConstKeyword = config.acceptConstKeyword; + options.transformAMDToCJSModules = config.transformAMDToCJSModules; + options.processCommonJSModules = config.processCommonJSModules; + options.commonJSModulePathPrefix = config.commonJSModulePathPrefix; + } + + final protected A getCompiler() { + return compiler; + } + + /** + * Runs the Compiler and calls System.exit() with the exit status of the + * compiler. + */ + final public void run() { + int result = 0; + int runs = 1; + if (config.computePhaseOrdering) { + runs = NUM_RUNS_TO_DETERMINE_OPTIMAL_ORDER; + PhaseOptimizer.randomizeLoops(); + } + try { + for (int i = 0; i < runs && result == 0; i++) { + runTimeStats.recordStartRun(); + result = doRun(); + runTimeStats.recordEndRun(); + } + } catch (AbstractCommandLineRunner.FlagUsageException e) { + System.err.println(e.getMessage()); + result = -1; + } catch (Throwable t) { + t.printStackTrace(); + result = -2; + } + + if (config.computePhaseOrdering) { + runTimeStats.outputBestPhaseOrdering(); + } + + try { + if (jsOutput instanceof Closeable) { + ((Closeable) jsOutput).close(); + } + } catch (IOException e) { + throw Throwables.propagate(e); + } + + if (testMode) { + exitCodeReceiverForTesting.apply(result); + } else { + System.exit(result); + } + } + + /** + * Returns the PrintStream for writing errors associated with this + * AbstractCommandLineRunner. + */ + protected PrintStream getErrorPrintStream() { + return err; + } + + /** + * An exception thrown when command-line flags are used incorrectly. + */ + public static class FlagUsageException extends Exception { + private static final long serialVersionUID = 1L; + + public FlagUsageException(String message) { + super(message); + } + } + + /** + * Creates inputs from a list of files. + * + * Can be overridden by subclasses who want to pull files from different + * places. + * + * @param files A list of filenames + * @param allowStdIn Whether '-' is allowed appear as a filename to represent + * stdin. If true, '-' is only allowed to appear once. + * @return An array of inputs + */ + protected List createInputs(List files, + boolean allowStdIn) throws FlagUsageException, IOException { + List inputs = new ArrayList(files.size()); + boolean usingStdin = false; + for (String filename : files) { + if (!"-".equals(filename)) { + SourceFile newFile = SourceFile.fromFile(filename, inputCharset); + inputs.add(newFile); + } else { + if (!allowStdIn) { + throw new FlagUsageException("Can't specify stdin."); + } + if (usingStdin) { + throw new FlagUsageException("Can't specify stdin twice."); + } + + if (!config.outputManifests.isEmpty()) { + throw new FlagUsageException("Manifest files cannot be generated " + + "when the input is from stdin."); + } + if (!config.outputBundles.isEmpty()) { + throw new FlagUsageException("Bundle files cannot be generated " + + "when the input is from stdin."); + } + inputs.add(SourceFile.fromInputStream("stdin", System.in)); + usingStdin = true; + } + } + return inputs; + } + + /** + * Creates JS source code inputs from a list of files. + */ + private List createSourceInputs(List files) + throws FlagUsageException, IOException { + if (isInTestMode()) { + return inputsSupplierForTesting.get(); + } + if (files.isEmpty()) { + files = Collections.singletonList("-"); + } + try { + return createInputs(files, true); + } catch (FlagUsageException e) { + throw new FlagUsageException("Bad --js flag. " + e.getMessage()); + } + } + + /** + * Creates JS extern inputs from a list of files. + */ + private List createExternInputs(List files) + throws FlagUsageException, IOException { + if (files.isEmpty()) { + return ImmutableList.of(SourceFile.fromCode("/dev/null", "")); + } + try { + return createInputs(files, false); + } catch (FlagUsageException e) { + throw new FlagUsageException("Bad --externs flag. " + e.getMessage()); + } + } + + /** + * Creates module objects from a list of module specifications. + * + * @param specs A list of module specifications, not null or empty. The spec + * format is: name:num-js-files[:[dep,...][:]]. Module + * names must not contain the ':' character. + * @param jsFiles A list of JS file paths, not null + * @return An array of module objects + */ + List createJsModules( + List specs, List jsFiles) + throws FlagUsageException, IOException { + if (isInTestMode()) { + return modulesSupplierForTesting.get(); + } + + Preconditions.checkState(specs != null); + Preconditions.checkState(!specs.isEmpty()); + Preconditions.checkState(jsFiles != null); + + final int totalNumJsFiles = jsFiles.size(); + int nextJsFileIndex = 0; + + Map modulesByName = Maps.newLinkedHashMap(); + for (String spec : specs) { + + // Format is ":[:[,...][:]]". + String[] parts = spec.split(":"); + if (parts.length < 2 || parts.length > 4) { + throw new FlagUsageException("Expected 2-4 colon-delimited parts in " + + "module spec: " + spec); + } + + // Parse module name. + String name = parts[0]; + checkModuleName(name); + if (modulesByName.containsKey(name)) { + throw new FlagUsageException("Duplicate module name: " + name); + } + JSModule module = new JSModule(name); + + // Parse module inputs. + int numJsFiles = -1; + try { + numJsFiles = Integer.parseInt(parts[1]); + } catch (NumberFormatException ignored) { + numJsFiles = -1; + } + + // We will allow modules of zero input. + if (numJsFiles < 0) { + throw new FlagUsageException("Invalid JS file count '" + parts[1] + + "' for module: " + name); + } + if (nextJsFileIndex + numJsFiles > totalNumJsFiles) { + throw new FlagUsageException("Not enough JS files specified. Expected " + + (nextJsFileIndex + numJsFiles - totalNumJsFiles) + + " more in module:" + name); + } + List moduleJsFiles = + jsFiles.subList(nextJsFileIndex, nextJsFileIndex + numJsFiles); + for (SourceFile input : createInputs(moduleJsFiles, false)) { + module.add(input); + } + nextJsFileIndex += numJsFiles; + + if (parts.length > 2) { + // Parse module dependencies. + String depList = parts[2]; + if (depList.length() > 0) { + String[] deps = depList.split(","); + for (String dep : deps) { + JSModule other = modulesByName.get(dep); + if (other == null) { + throw new FlagUsageException("Module '" + name + + "' depends on unknown module '" + dep + + "'. Be sure to list modules in dependency order."); + } + module.addDependency(other); + } + } + } + + modulesByName.put(name, module); + } + + if (nextJsFileIndex < totalNumJsFiles) { + throw new FlagUsageException("Too many JS files specified. Expected " + + nextJsFileIndex + " but found " + totalNumJsFiles); + } + + return Lists.newArrayList(modulesByName.values()); + } + + /** + * Validates the module name. Can be overridden by subclasses. + * @param name The module name + * @throws FlagUsageException if the validation fails + */ + protected void checkModuleName(String name) + throws FlagUsageException { + if (!TokenStream.isJSIdentifier(name)) { + throw new FlagUsageException("Invalid module name: '" + name + "'"); + } + } + + /** + * Parses module wrapper specifications. + * + * @param specs A list of module wrapper specifications, not null. The spec + * format is: name:wrapper. Wrappers. + * @param modules The JS modules whose wrappers are specified + * @return A map from module name to module wrapper. Modules with no wrapper + * will have the empty string as their value in this map. + */ + static Map parseModuleWrappers(List specs, + List modules) throws FlagUsageException { + Preconditions.checkState(specs != null); + + Map wrappers = + Maps.newHashMapWithExpectedSize(modules.size()); + + // Prepopulate the map with module names. + for (JSModule m : modules) { + wrappers.put(m.getName(), ""); + } + + for (String spec : specs) { + + // Format is ":". + int pos = spec.indexOf(':'); + if (pos == -1) { + throw new FlagUsageException("Expected module wrapper to have " + + ": format: " + spec); + } + + // Parse module name. + String name = spec.substring(0, pos); + if (!wrappers.containsKey(name)) { + throw new FlagUsageException("Unknown module: '" + name + "'"); + } + String wrapper = spec.substring(pos + 1); + if (!wrapper.contains("%s")) { + throw new FlagUsageException("No %s placeholder in module wrapper: '" + + wrapper + "'"); + } + wrappers.put(name, wrapper); + } + return wrappers; + } + + private String getModuleOutputFileName(JSModule m) { + return config.moduleOutputPathPrefix + m.getName() + ".js"; + } + + @VisibleForTesting + void writeModuleOutput(Appendable out, JSModule m) + throws FlagUsageException, IOException { + if (parsedModuleWrappers == null) { + parsedModuleWrappers = parseModuleWrappers( + config.moduleWrapper, + Lists.newArrayList( + compiler.getDegenerateModuleGraph().getAllModules())); + } + + String fileName = getModuleOutputFileName(m); + String baseName = new File(fileName).getName(); + writeOutput(out, compiler, compiler.toSource(m), + parsedModuleWrappers.get(m.getName()).replace("%basename%", baseName), + "%s", null); + } + + /** + * Writes code to an output stream, optionally wrapping it in an arbitrary + * wrapper that contains a placeholder where the code should be inserted. + */ + static void writeOutput(Appendable out, Compiler compiler, String code, + String wrapper, String codePlaceholder, + @Nullable Function escaper) + throws IOException { + int pos = wrapper.indexOf(codePlaceholder); + if (pos != -1) { + String prefix = ""; + + if (pos > 0) { + prefix = wrapper.substring(0, pos); + out.append(prefix); + } + + out.append(escaper == null ? code : escaper.apply(code)); + + int suffixStart = pos + codePlaceholder.length(); + if (suffixStart != wrapper.length()) { + // Something after placeholder? + out.append(wrapper.substring(suffixStart)); + } + // Make sure we always end output with a line feed. + out.append('\n'); + + // If we have a source map, adjust its offsets to match + // the code WITHIN the wrapper. + if (compiler != null && compiler.getSourceMap() != null) { + compiler.getSourceMap().setWrapperPrefix(prefix); + } + + } else { + out.append(code); + out.append('\n'); + } + } + + /** + * Creates any directories necessary to write a file that will have a given + * path prefix. + */ + private static void maybeCreateDirsForPath(String pathPrefix) { + if (pathPrefix.length() > 0) { + String dirName = + pathPrefix.charAt(pathPrefix.length() - 1) == File.separatorChar + ? pathPrefix.substring(0, pathPrefix.length() - 1) : new File( + pathPrefix).getParent(); + if (dirName != null) { + new File(dirName).mkdirs(); + } + } + } + + /** + * Parses command-line arguments and runs the compiler. + * + * @return system exit status + */ + protected int doRun() throws FlagUsageException, IOException { + Compiler.setLoggingLevel(Level.parse(config.loggingLevel)); + + List externs = createExterns(); + + compiler = createCompiler(); + B options = createOptions(); + + List modules = null; + Result result = null; + + setRunOptions(options); + + boolean writeOutputToFile = !config.jsOutputFile.isEmpty(); + List outputFileNames = Lists.newArrayList(); + if (writeOutputToFile) { + outputFileNames.add(config.jsOutputFile); + jsOutput = fileNameToLegacyOutputWriter(config.jsOutputFile); + } else if (jsOutput instanceof OutputStream) { + jsOutput = streamToLegacyOutputWriter((OutputStream) jsOutput); + } + + List jsFiles = config.js; + List moduleSpecs = config.module; + + boolean createCommonJsModules = false; + if (options.processCommonJSModules) { + if (moduleSpecs.size() == 1 && "auto".equals(moduleSpecs.get(0))) { + createCommonJsModules = true; + moduleSpecs.remove(0); + } + } + if (!moduleSpecs.isEmpty()) { + modules = createJsModules(moduleSpecs, jsFiles); + for (JSModule m : modules) { + outputFileNames.add(getModuleOutputFileName(m)); + } + + if (config.skipNormalOutputs) { + compiler.initModules(externs, modules, options); + } else { + result = compiler.compileModules(externs, modules, options); + } + } else { + List inputs = createSourceInputs(jsFiles); + if (config.skipNormalOutputs) { + compiler.init(externs, inputs, options); + } else { + result = compiler.compile(externs, inputs, options); + } + } + if (createCommonJsModules) { + // For CommonJS modules construct modules from actual inputs. + modules = Lists.newArrayList(compiler.getDegenerateModuleGraph() + .getAllModules()); + for (JSModule m : modules) { + outputFileNames.add(getModuleOutputFileName(m)); + } + } + + for (String outputFileName : outputFileNames) { + if (compiler.getSourceFileByName(outputFileName) != null) { + compiler.report( + JSError.make(OUTPUT_SAME_AS_INPUT_ERROR, outputFileName)); + return 1; + } + } + + int errCode = processResults(result, modules, options); + // Flush the output if we are writing to a file. + // We can't close yet, because we may need to write phase ordering + // info to it later. + if (jsOutput instanceof Flushable) { + ((Flushable) jsOutput).flush(); + } + return errCode; + } + + /** + * Processes the results of the compile job, and returns an error code. + */ + int processResults(Result result, List modules, B options) + throws FlagUsageException, IOException { + if (config.computePhaseOrdering) { + return 0; + } + + if (config.printPassGraph) { + if (compiler.getRoot() == null) { + return 1; + } else { + jsOutput.append( + DotFormatter.toDot(compiler.getPassConfig().getPassGraph())); + jsOutput.append('\n'); + return 0; + } + } + + if (config.printAst) { + if (compiler.getRoot() == null) { + return 1; + } else { + ControlFlowGraph cfg = compiler.computeCFG(); + DotFormatter.appendDot( + compiler.getRoot().getLastChild(), cfg, jsOutput); + jsOutput.append('\n'); + return 0; + } + } + + if (config.printTree) { + if (compiler.getRoot() == null) { + jsOutput.append("Code contains errors; no tree was generated.\n"); + return 1; + } else { + compiler.getRoot().appendStringTree(jsOutput); + jsOutput.append("\n"); + return 0; + } + } + + rootRelativePathsMap = constructRootRelativePathsMap(); + + if (config.skipNormalOutputs) { + // Output the manifest and bundle files if requested. + outputManifest(); + outputBundle(); + outputModuleGraphJson(); + return 0; + } else if (result.success) { + outputModuleGraphJson(); + if (modules == null) { + outputSingleBinary(); + + // Output the source map if requested. + outputSourceMap(options, config.jsOutputFile); + } else { + outputModuleBinaryAndSourceMaps(modules, options); + } + + // Output the externs if required. + if (options.externExportsPath != null) { + Writer eeOut = + openExternExportsStream(options, config.jsOutputFile); + eeOut.append(result.externExport); + eeOut.close(); + } + + // Output the variable and property name maps if requested. + outputNameMaps(options); + + // Output the manifest and bundle files if requested. + outputManifest(); + outputBundle(); + } + + // return 0 if no errors, the error count otherwise + return Math.min(result.errors.length, 0x7f); + } + + Function getJavascriptEscaper() { + throw new UnsupportedOperationException( + "SourceCodeEscapers is not in the standard release of Guava yet :("); + } + + void outputSingleBinary() throws IOException { + Function escaper = null; + String marker = OUTPUT_MARKER; + if (config.outputWrapper.contains(OUTPUT_MARKER_JS_STRING)) { + marker = OUTPUT_MARKER_JS_STRING; + escaper = getJavascriptEscaper(); + } + + writeOutput( + jsOutput, compiler, compiler.toSource(), config.outputWrapper, + marker, escaper); + } + + private void outputModuleBinaryAndSourceMaps( + List modules, B options) + throws FlagUsageException, IOException { + parsedModuleWrappers = parseModuleWrappers( + config.moduleWrapper, modules); + maybeCreateDirsForPath(config.moduleOutputPathPrefix); + + // If the source map path is in fact a pattern for each + // module, create a stream per-module. Otherwise, create + // a single source map. + Writer mapOut = null; + + if (!shouldGenerateMapPerModule(options)) { + mapOut = fileNameToOutputWriter2(expandSourceMapPath(options, null)); + } + + for (JSModule m : modules) { + if (shouldGenerateMapPerModule(options)) { + mapOut = fileNameToOutputWriter2(expandSourceMapPath(options, m)); + } + + Writer writer = + fileNameToLegacyOutputWriter(getModuleOutputFileName(m)); + + if (options.sourceMapOutputPath != null) { + compiler.getSourceMap().reset(); + } + + writeModuleOutput(writer, m); + + if (options.sourceMapOutputPath != null) { + compiler.getSourceMap().appendTo(mapOut, m.getName()); + } + + writer.close(); + + if (shouldGenerateMapPerModule(options) && mapOut != null) { + mapOut.close(); + mapOut = null; + } + } + + if (mapOut != null) { + mapOut.close(); + } + } + + /** + * Query the flag for the input charset, and return a Charset object + * representing the selection. + * + * @return Charset to use when reading inputs + * @throws FlagUsageException if flag is not a valid Charset name. + */ + private Charset getInputCharset() throws FlagUsageException { + if (!config.charset.isEmpty()) { + if (!Charset.isSupported(config.charset)) { + throw new FlagUsageException(config.charset + + " is not a valid charset name."); + } + return Charset.forName(config.charset); + } + return Charsets.UTF_8; + } + + /** + * Query the flag for the output charset. + * + * Let the outputCharset be the same as the input charset... except if + * we're reading in UTF-8 by default. By tradition, we've always + * output ASCII to avoid various hiccups with different browsers, + * proxies and firewalls. + * + * @return Name of the charset to use when writing outputs. Guaranteed to + * be a supported charset. + * @throws FlagUsageException if flag is not a valid Charset name. + */ + private String getLegacyOutputCharset() throws FlagUsageException { + if (!config.charset.isEmpty()) { + if (!Charset.isSupported(config.charset)) { + throw new FlagUsageException(config.charset + + " is not a valid charset name."); + } + return config.charset; + } + return "US-ASCII"; + } + + /** + * Query the flag for the output charset. Defaults to UTF-8. + * @throws FlagUsageException if flag is not a valid Charset name. + */ + private Charset getOutputCharset2() throws FlagUsageException { + if (!config.charset.isEmpty()) { + if (!Charset.isSupported(config.charset)) { + throw new FlagUsageException(config.charset + + " is not a valid charset name."); + } + return Charset.forName(config.charset); + } + return Charsets.UTF_8; + } + + protected List createExterns() throws FlagUsageException, + IOException { + return isInTestMode() ? externsSupplierForTesting.get() : + createExternInputs(config.externs); + } + + /** + * Returns true if and only if a source map file should be generated for each + * module, as opposed to one unified map. This is specified by having the + * source map pattern include the %outname% variable. + */ + private boolean shouldGenerateMapPerModule(B options) { + return options.sourceMapOutputPath != null + && options.sourceMapOutputPath.contains("%outname%"); + } + + /** + * Returns a stream for outputting the generated externs file. + * + * @param options The options to the Compiler. + * @param path The path of the generated JS source file. + * + * @return The stream or null if no extern-ed exports are being generated. + */ + private Writer openExternExportsStream(B options, + String path) throws IOException { + if (options.externExportsPath == null) { + return null; + } + + String exPath = options.externExportsPath; + + if (!exPath.contains(File.separator)) { + File outputFile = new File(path); + exPath = outputFile.getParent() + File.separatorChar + exPath; + } + + return fileNameToOutputWriter2(exPath); + } + + /** + * Expand a file path specified on the command-line. + * + * Most file paths on the command-line allow an %outname% placeholder. + * The placeholder will expand to a different value depending on + * the current output mode. There are three scenarios: + * + * 1) Single JS output, single extra output: sub in jsOutputPath. + * 2) Multiple JS output, single extra output: sub in the base module name. + * 3) Multiple JS output, multiple extra output: sub in the module output + * file. + * + * Passing a JSModule to this function automatically triggers case #3. + * Otherwise, we'll use strategy #1 or #2 based on the current output mode. + */ + private String expandCommandLinePath( + String path, JSModule forModule) { + String sub; + if (forModule != null) { + sub = config.moduleOutputPathPrefix + forModule.getName() + ".js"; + } else if (!config.module.isEmpty()) { + sub = config.moduleOutputPathPrefix; + } else { + sub = config.jsOutputFile; + } + return path.replace("%outname%", sub); + } + + /** Expansion function for source map. */ + @VisibleForTesting + String expandSourceMapPath(B options, JSModule forModule) { + if (Strings.isNullOrEmpty(options.sourceMapOutputPath)) { + return null; + } + return expandCommandLinePath(options.sourceMapOutputPath, forModule); + } + + /** + * Converts a file name into a Writer taking in account the output charset. + * Returns null if the file name is null. + */ + private Writer fileNameToLegacyOutputWriter(String fileName) + throws IOException { + if (fileName == null) { + return null; + } + if (testMode) { + return new StringWriter(); + } + + return streamToLegacyOutputWriter(filenameToOutputStream(fileName)); + } + + /** + * Converts a file name into a Writer taking in account the output charset. + * Returns null if the file name is null. + */ + private Writer fileNameToOutputWriter2(String fileName) throws IOException { + if (fileName == null) { + return null; + } + if (testMode) { + return new StringWriter(); + } + + return streamToOutputWriter2(filenameToOutputStream(fileName)); + } + + /** + * Converts a file name into a Outputstream. + * Returns null if the file name is null. + */ + protected OutputStream filenameToOutputStream(String fileName) + throws IOException { + if (fileName == null){ + return null; + } + return new FileOutputStream(fileName); + } + + /** + * Create a writer with the legacy output charset. + */ + private Writer streamToLegacyOutputWriter(OutputStream stream) + throws IOException { + if (legacyOutputCharset == null) { + return new BufferedWriter( + new OutputStreamWriter(stream)); + } else { + return new BufferedWriter( + new OutputStreamWriter(stream, legacyOutputCharset)); + } + } + + /** + * Create a writer with the newer output charset. + */ + private Writer streamToOutputWriter2(OutputStream stream) { + if (outputCharset2 == null) { + return new BufferedWriter( + new OutputStreamWriter(stream)); + } else { + return new BufferedWriter( + new OutputStreamWriter(stream, outputCharset2)); + } + } + + /** + * Outputs the source map found in the compiler to the proper path if one + * exists. + * + * @param options The options to the Compiler. + */ + private void outputSourceMap(B options, String associatedName) + throws IOException { + if (Strings.isNullOrEmpty(options.sourceMapOutputPath)) { + return; + } + + String outName = expandSourceMapPath(options, null); + Writer out = fileNameToOutputWriter2(outName); + compiler.getSourceMap().appendTo(out, associatedName); + out.close(); + } + + /** + * Returns the path at which to output map file(s) based on the path at which + * the JS binary will be placed. + * + * @return The path in which to place the generated map file(s). + */ + private String getMapPath(String outputFile) { + String basePath = ""; + + if (outputFile.equals("")) { + // If we have a js_module_binary rule, output the maps + // at modulename_props_map.out, etc. + if (!config.moduleOutputPathPrefix.equals("")) { + basePath = config.moduleOutputPathPrefix; + } else { + basePath = "jscompiler"; + } + } else { + // Get the path of the output file. + File file = new File(outputFile); + + String outputFileName = file.getName(); + + // Strip the .js from the name. + if (outputFileName.endsWith(".js")) { + outputFileName = + outputFileName.substring(0, outputFileName.length() - 3); + } + + basePath = file.getParent() + File.separatorChar + outputFileName; + } + + return basePath; + } + + /** + * Outputs the variable and property name maps for the specified compiler if + * the proper FLAGS are set. + */ + private void outputNameMaps(B options) throws FlagUsageException, + IOException { + + String propertyMapOutputPath = null; + String variableMapOutputPath = null; + String functionInformationMapOutputPath = null; + + // Check the create_name_map_files FLAG. + if (config.createNameMapFiles) { + String basePath = getMapPath(config.jsOutputFile); + + propertyMapOutputPath = basePath + "_props_map.out"; + variableMapOutputPath = basePath + "_vars_map.out"; + functionInformationMapOutputPath = basePath + "_functions_map.out"; + } + + // Check the individual FLAGS. + if (!config.variableMapOutputFile.equals("")) { + if (variableMapOutputPath != null) { + throw new FlagUsageException("The flags variable_map_output_file and " + + "create_name_map_files cannot both be used simultaniously."); + } + + variableMapOutputPath = config.variableMapOutputFile; + } + + if (!config.propertyMapOutputFile.equals("")) { + if (propertyMapOutputPath != null) { + throw new FlagUsageException("The flags property_map_output_file and " + + "create_name_map_files cannot both be used simultaniously."); + } + + propertyMapOutputPath = config.propertyMapOutputFile; + } + + // Output the maps. + if (variableMapOutputPath != null) { + if (compiler.getVariableMap() != null) { + compiler.getVariableMap().save(variableMapOutputPath); + } + } + + if (propertyMapOutputPath != null) { + if (compiler.getPropertyMap() != null) { + compiler.getPropertyMap().save(propertyMapOutputPath); + } + } + + if (functionInformationMapOutputPath != null) { + if (compiler.getFunctionalInformationMap() != null) { + OutputStream file = + filenameToOutputStream(functionInformationMapOutputPath); + CodedOutputStream outputStream = CodedOutputStream.newInstance(file); + compiler.getFunctionalInformationMap().writeTo(outputStream); + outputStream.flush(); + file.flush(); + file.close(); + } + } + } + + /** + * Create a map of constant names to constant values from a textual + * description of the map. + * + * @param definitions A list of overriding definitions for defines in + * the form [=], where is a number, boolean, or + * single-quoted string without single quotes. + */ + @VisibleForTesting + static void createDefineOrTweakReplacements(List definitions, + CompilerOptions options, boolean tweaks) { + // Parse the definitions + for (String override : definitions) { + String[] assignment = override.split("=", 2); + String defName = assignment[0]; + + if (defName.length() > 0) { + String defValue = assignment.length == 1 ? "true" : assignment[1]; + + boolean isTrue = defValue.equals("true"); + boolean isFalse = defValue.equals("false"); + if (isTrue || isFalse) { + if (tweaks) { + options.setTweakToBooleanLiteral(defName, isTrue); + } else { + options.setDefineToBooleanLiteral(defName, isTrue); + } + continue; + } else if (defValue.length() > 1 + && ((defValue.charAt(0) == '\'' && + defValue.charAt(defValue.length() - 1) == '\'') + || (defValue.charAt(0) == '\"' && + defValue.charAt(defValue.length() - 1) == '\"'))) { + // If the value starts and ends with a single quote, + // we assume that it's a string. + String maybeStringVal = + defValue.substring(1, defValue.length() - 1); + if (maybeStringVal.indexOf(defValue.charAt(0)) == -1) { + if (tweaks) { + options.setTweakToStringLiteral(defName, maybeStringVal); + } else { + options.setDefineToStringLiteral(defName, maybeStringVal); + } + continue; + } + } else { + try { + double value = Double.parseDouble(defValue); + if (tweaks) { + options.setTweakToDoubleLiteral(defName, value); + } else { + options.setDefineToDoubleLiteral(defName, value); + } + continue; + } catch (NumberFormatException e) { + // do nothing, it will be caught at the end + } + } + } + + if (tweaks) { + throw new RuntimeException( + "--tweak flag syntax invalid: " + override); + } + throw new RuntimeException( + "--define flag syntax invalid: " + override); + } + } + + /** + * Returns true if and only if a manifest or bundle should be generated + * for each module, as opposed to one unified manifest. + */ + private boolean shouldGenerateOutputPerModule(String output) { + return !config.module.isEmpty() + && output != null && output.contains("%outname%"); + } + + private void outputManifest() throws IOException { + outputManifestOrBundle(config.outputManifests, true); + } + + private void outputBundle() throws IOException { + outputManifestOrBundle(config.outputBundles, false); + } + + /** + * Writes the manifest or bundle of all compiler input files that survived + * manage_closure_dependencies, if requested. + */ + private void outputManifestOrBundle(List outputFiles, + boolean isManifest) throws IOException { + if (outputFiles.isEmpty()) { + return; + } + + for (String output : outputFiles) { + if (output.isEmpty()) { + continue; + } + + if (shouldGenerateOutputPerModule(output)) { + // Generate per-module manifests or bundles + JSModuleGraph graph = compiler.getDegenerateModuleGraph(); + Iterable modules = graph.getAllModules(); + for (JSModule module : modules) { + Writer out = fileNameToOutputWriter2( + expandCommandLinePath(output, module)); + if (isManifest) { + printManifestTo(module.getInputs(), out); + } else { + printBundleTo(module.getInputs(), out); + } + out.close(); + } + } else { + // Generate a single file manifest or bundle. + Writer out = fileNameToOutputWriter2( + expandCommandLinePath(output, null)); + if (config.module.isEmpty()) { + if (isManifest) { + printManifestTo(compiler.getInputsInOrder(), out); + } else { + printBundleTo(compiler.getInputsInOrder(), out); + } + } else { + printModuleGraphManifestOrBundleTo( + compiler.getDegenerateModuleGraph(), out, isManifest); + } + out.close(); + } + } + } + + /** + * Creates a file containing the current module graph in JSON serialization. + */ + private void outputModuleGraphJson() throws IOException { + if (config.outputModuleDependencies != null && + config.outputModuleDependencies != "") { + Writer out = fileNameToOutputWriter2(config.outputModuleDependencies); + printModuleGraphJsonTo(compiler.getDegenerateModuleGraph(), out); + out.close(); + } + } + + /** + * Prints the current module graph as JSON. + */ + @VisibleForTesting + void printModuleGraphJsonTo(JSModuleGraph graph, + Appendable out) throws IOException { + out.append(compiler.getDegenerateModuleGraph().toJson().toString()); + } + + /** + * Prints a set of modules to the manifest or bundle file. + */ + @VisibleForTesting + void printModuleGraphManifestOrBundleTo(JSModuleGraph graph, + Appendable out, boolean isManifest) throws IOException { + Joiner commas = Joiner.on(","); + boolean requiresNewline = false; + for (JSModule module : graph.getAllModules()) { + if (requiresNewline) { + out.append("\n"); + } + + if (isManifest) { + // See CommandLineRunnerTest to see what the format of this + // manifest looks like. + String dependencies = commas.join(module.getSortedDependencyNames()); + out.append( + String.format("{%s%s}\n", + module.getName(), + dependencies.isEmpty() ? "" : ":" + dependencies)); + printManifestTo(module.getInputs(), out); + } else { + printBundleTo(module.getInputs(), out); + } + requiresNewline = true; + } + } + + /** + * Prints a list of input names (using root-relative paths), delimited by + * newlines, to the manifest file. + */ + private void printManifestTo(Iterable inputs, Appendable out) + throws IOException { + for (CompilerInput input : inputs) { + String rootRelativePath = rootRelativePathsMap.get(input.getName()); + String displayName = rootRelativePath != null + ? rootRelativePath + : input.getName(); + out.append(displayName); + out.append("\n"); + } + } + + /** + * Prints all the input contents, starting with a comment that specifies + * the input file name (using root-relative paths) before each file. + */ + private void printBundleTo(Iterable inputs, Appendable out) + throws IOException { + for (CompilerInput input : inputs) { + // Every module has an empty file in it. This makes it easier to implement + // cross-module code motion. + // + // But it also leads to a weird edge case because + // a) If we don't have a module spec, we create a singleton module, and + // b) If we print a bundle file, we copy the original input files. + // + // This means that in the (rare) case where we have no inputs, and no + // module spec, and we're printing a bundle file, we'll have a fake + // input file that shouldn't be copied. So we special-case this, to + // make all the other cases simpler. + if (input.getName().equals( + Compiler.createFillFileName(Compiler.SINGLETON_MODULE_NAME))) { + Preconditions.checkState(1 == Iterables.size(inputs)); + return; + } + + String rootRelativePath = rootRelativePathsMap.get(input.getName()); + String displayName = rootRelativePath != null + ? rootRelativePath + : input.getName(); + File file = new File(input.getName()); + out.append("//"); + out.append(displayName); + out.append("\n"); + Files.copy(file, inputCharset, out); + out.append("\n"); + } + } + + /** + * Construct and return the input root path map. The key is the exec path of + * each input file, and the value is the corresponding root relative path. + */ + private Map constructRootRelativePathsMap() { + Map rootRelativePathsMap = Maps.newLinkedHashMap(); + for (String mapString : config.manifestMaps) { + int colonIndex = mapString.indexOf(':'); + Preconditions.checkState(colonIndex > 0); + String execPath = mapString.substring(0, colonIndex); + String rootRelativePath = mapString.substring(colonIndex + 1); + Preconditions.checkState(rootRelativePath.indexOf(':') == -1); + rootRelativePathsMap.put(execPath, rootRelativePath); + } + return rootRelativePathsMap; + } + + private class RunTimeStats { + private long bestRunTime = Long.MAX_VALUE; + private long worstRunTime = Long.MIN_VALUE; + private long lastStartTime = 0; + private List> loopedPassesInBestRun = null; + + /** + * Record the start of a run. + */ + private void recordStartRun() { + lastStartTime = System.currentTimeMillis(); + PhaseOptimizer.clearLoopsRun(); + } + + /** + * Record the end of a run. + */ + private void recordEndRun() { + long endTime = System.currentTimeMillis(); + long length = endTime - lastStartTime; + worstRunTime = Math.max(length, worstRunTime); + if (length < bestRunTime) { + loopedPassesInBestRun = PhaseOptimizer.getLoopsRun(); + bestRunTime = length; + } + } + + /** + * Print the best phase loop to stderr. + */ + private void outputBestPhaseOrdering() { + try { + jsOutput.append("Best time: " + bestRunTime + "\n"); + jsOutput.append("Worst time: " + worstRunTime + "\n"); + + int i = 1; + for (List loop : loopedPassesInBestRun) { + jsOutput.append( + "\nLoop " + i + ":\n" + Joiner.on("\n").join(loop)+ "\n"); + i++; + } + } catch (IOException e) { + throw new RuntimeException("unexpected exception", e); + } + } + } + + /** + * Configurations for the command line configs. Designed for easy + * building, so that we can decouple the flags-parsing library from + * the actual configuration options. + * + * By design, these configurations must match one-to-one with + * command-line flags. + */ + static class CommandLineConfig { + private boolean printTree = false; + + /** Prints out the parse tree and exits */ + CommandLineConfig setPrintTree(boolean printTree) { + this.printTree = printTree; + return this; + } + + private boolean computePhaseOrdering = false; + + /** + * Runs the compile job many times, then prints out the best phase + * ordering from this run + */ + CommandLineConfig setComputePhaseOrdering(boolean computePhaseOrdering) { + this.computePhaseOrdering = computePhaseOrdering; + return this; + } + + private boolean printAst = false; + + /** + * Prints a dot file describing the internal abstract syntax tree + * and exits + */ + CommandLineConfig setPrintAst(boolean printAst) { + this.printAst = printAst; + return this; + } + + private boolean printPassGraph = false; + + /** Prints a dot file describing the passes that will get run and exits */ + CommandLineConfig setPrintPassGraph(boolean printPassGraph) { + this.printPassGraph = printPassGraph; + return this; + } + + private CompilerOptions.DevMode jscompDevMode = CompilerOptions.DevMode.OFF; + + /** Turns on extra sanity checks */ + CommandLineConfig setJscompDevMode(CompilerOptions.DevMode jscompDevMode) { + this.jscompDevMode = jscompDevMode; + return this; + } + + private String loggingLevel = Level.WARNING.getName(); + + /** + * The logging level (standard java.util.logging.Level + * values) for Compiler progress. Does not control errors or + * warnings for the JavaScript code under compilation + */ + CommandLineConfig setLoggingLevel(String loggingLevel) { + this.loggingLevel = loggingLevel; + return this; + } + + private final List externs = Lists.newArrayList(); + + /** + * The file containing JavaScript externs. You may specify multiple. + */ + CommandLineConfig setExterns(List externs) { + this.externs.clear(); + this.externs.addAll(externs); + return this; + } + + private final List js = Lists.newArrayList(); + + /** + * The JavaScript filename. You may specify multiple. + */ + CommandLineConfig setJs(List js) { + this.js.clear(); + this.js.addAll(js); + return this; + } + + private String jsOutputFile = ""; + + /** + * Primary output filename. If not specified, output is written to stdout + */ + CommandLineConfig setJsOutputFile(String jsOutputFile) { + this.jsOutputFile = jsOutputFile; + return this; + } + + private final List module = Lists.newArrayList(); + + /** + * A JavaScript module specification. The format is + * :[:[,...][:]]]. Module names must be + * unique. Each dep is the name of a module that this module + * depends on. Modules must be listed in dependency order, and JS + * source files must be listed in the corresponding order. Where + * --module flags occur in relation to --js flags is unimportant + */ + CommandLineConfig setModule(List module) { + this.module.clear(); + this.module.addAll(module); + return this; + } + + private String variableMapInputFile = ""; + + /** + * File containing the serialized version of the variable renaming + * map produced by a previous compilation + */ + CommandLineConfig setVariableMapInputFile(String variableMapInputFile) { + this.variableMapInputFile = variableMapInputFile; + return this; + } + + private String propertyMapInputFile = ""; + + /** + * File containing the serialized version of the property renaming + * map produced by a previous compilation + */ + CommandLineConfig setPropertyMapInputFile(String propertyMapInputFile) { + this.propertyMapInputFile = propertyMapInputFile; + return this; + } + + private String variableMapOutputFile = ""; + + /** + * File where the serialized version of the variable renaming map + * produced should be saved + */ + CommandLineConfig setVariableMapOutputFile(String variableMapOutputFile) { + this.variableMapOutputFile = variableMapOutputFile; + return this; + } + + private boolean createNameMapFiles = false; + + /** + * If true, variable renaming and property renaming map + * files will be produced as {binary name}_vars_map.out and + * {binary name}_props_map.out. Note that this flag cannot be used + * in conjunction with either variable_map_output_file or + * property_map_output_file + */ + CommandLineConfig setCreateNameMapFiles(boolean createNameMapFiles) { + this.createNameMapFiles = createNameMapFiles; + return this; + } + + private String propertyMapOutputFile = ""; + + /** + * File where the serialized version of the property renaming map + * produced should be saved + */ + CommandLineConfig setPropertyMapOutputFile(String propertyMapOutputFile) { + this.propertyMapOutputFile = propertyMapOutputFile; + return this; + } + + private CodingConvention codingConvention = CodingConventions.getDefault(); + + /** + * Sets rules and conventions to enforce. + */ + CommandLineConfig setCodingConvention(CodingConvention codingConvention) { + this.codingConvention = codingConvention; + return this; + } + + private int summaryDetailLevel = 1; + + /** + * Controls how detailed the compilation summary is. Values: + * 0 (never print summary), 1 (print summary only if there are + * errors or warnings), 2 (print summary if type checking is on, + * see --check_types), 3 (always print summary). The default level + * is 1 + */ + CommandLineConfig setSummaryDetailLevel(int summaryDetailLevel) { + this.summaryDetailLevel = summaryDetailLevel; + return this; + } + + private String outputWrapper = ""; + + /** + * Interpolate output into this string at the place denoted + * by the marker token %output%, or %output|jsstring% + */ + CommandLineConfig setOutputWrapper(String outputWrapper) { + this.outputWrapper = outputWrapper; + return this; + } + + private final List moduleWrapper = Lists.newArrayList(); + + /** + * An output wrapper for a JavaScript module (optional). See the flag + * description for formatting requirements. + */ + CommandLineConfig setModuleWrapper(List moduleWrapper) { + this.moduleWrapper.clear(); + this.moduleWrapper.addAll(moduleWrapper); + return this; + } + + private String moduleOutputPathPrefix = ""; + + /** + * Prefix for filenames of compiled JS modules. + * .js will be appended to this prefix. Directories + * will be created as needed. Use with --module + */ + CommandLineConfig setModuleOutputPathPrefix(String moduleOutputPathPrefix) { + this.moduleOutputPathPrefix = moduleOutputPathPrefix; + return this; + } + + private String createSourceMap = ""; + + /** + * If specified, a source map file mapping the generated + * source files back to the original source file will be + * output to the specified path. The %outname% placeholder will + * expand to the name of the output file that the source map + * corresponds to. + */ + CommandLineConfig setCreateSourceMap(String createSourceMap) { + this.createSourceMap = createSourceMap; + return this; + } + + private SourceMap.DetailLevel sourceMapDetailLevel = + SourceMap.DetailLevel.ALL; + + /** + * The detail supplied in the source map file, if generated. + */ + CommandLineConfig setSourceMapDetailLevel(SourceMap.DetailLevel level) { + this.sourceMapDetailLevel = level; + return this; + } + + private SourceMap.Format sourceMapFormat = + SourceMap.Format.DEFAULT; + + /** + * The detail supplied in the source map file, if generated. + */ + CommandLineConfig setSourceMapFormat(SourceMap.Format format) { + this.sourceMapFormat = format; + return this; + } + + private WarningGuardSpec warningGuards = null; + + /** + * Add warning guards. + */ + CommandLineConfig setWarningGuardSpec(WarningGuardSpec spec) { + this.warningGuards = spec; + return this; + } + + private final List define = Lists.newArrayList(); + + /** + * Override the value of a variable annotated @define. + * The format is [=], where is the name of a @define + * variable and is a boolean, number, or a single-quoted string + * that contains no single quotes. If [=] is omitted, + * the variable is marked true + */ + CommandLineConfig setDefine(List define) { + this.define.clear(); + this.define.addAll(define); + return this; + } + + private final List tweak = Lists.newArrayList(); + + /** + * Override the default value of a registered tweak. The format is + * [=], where is the ID of a tweak and is a boolean, + * number, or a single-quoted string that contains no single quotes. If + * [=] is omitted, then true is assumed. + */ + CommandLineConfig setTweak(List tweak) { + this.tweak.clear(); + this.tweak.addAll(tweak); + return this; + } + + private TweakProcessing tweakProcessing = TweakProcessing.OFF; + + /** + * Sets the kind of processing to do for goog.tweak functions. + */ + CommandLineConfig setTweakProcessing(TweakProcessing tweakProcessing) { + this.tweakProcessing = tweakProcessing; + return this; + } + + private String charset = ""; + + /** + * Input charset for all files. + */ + CommandLineConfig setCharset(String charset) { + this.charset = charset; + return this; + } + + private boolean manageClosureDependencies = false; + + /** + * Sets whether to sort files by their goog.provide/require deps, + * and prune inputs that are not required. + */ + CommandLineConfig setManageClosureDependencies(boolean newVal) { + this.manageClosureDependencies = newVal; + return this; + } + + private boolean onlyClosureDependencies = false; + + /** + * Sets whether to sort files by their goog.provide/require deps, + * and prune inputs that are not required, and drop all non-closure + * files. + */ + CommandLineConfig setOnlyClosureDependencies(boolean newVal) { + this.onlyClosureDependencies = newVal; + return this; + } + + private List closureEntryPoints = ImmutableList.of(); + + /** + * Set closure entry points, which makes the compiler only include + * those files and sort them in dependency order. + */ + CommandLineConfig setClosureEntryPoints(List entryPoints) { + Preconditions.checkNotNull(entryPoints); + this.closureEntryPoints = entryPoints; + return this; + } + + private List outputManifests = ImmutableList.of(); + + /** + * Sets whether to print output manifest files. + * Filter out empty file names. + */ + CommandLineConfig setOutputManifest(List outputManifests) { + this.outputManifests = Lists.newArrayList(); + for (String manifestName : outputManifests) { + if (!manifestName.isEmpty()) { + this.outputManifests.add(manifestName); + } + } + this.outputManifests = ImmutableList.copyOf(this.outputManifests); + return this; + } + + private String outputModuleDependencies = null; + + /** + * Sets whether a JSON file representing the dependencies between modules + * should be created. + */ + CommandLineConfig setOutputModuleDependencies(String + outputModuleDependencies) { + this.outputModuleDependencies = outputModuleDependencies; + return this; + } + + private List outputBundles = ImmutableList.of(); + + /** + * Sets whether to print output bundle files. + */ + CommandLineConfig setOutputBundle(List outputBundles) { + this.outputBundles = outputBundles; + return this; + } + + private boolean acceptConstKeyword = false; + + /** + * Sets whether to accept usage of 'const' keyword. + */ + CommandLineConfig setAcceptConstKeyword(boolean acceptConstKeyword) { + this.acceptConstKeyword = acceptConstKeyword; + return this; + } + + private String languageIn = ""; + + /** + * Sets whether to accept input files as ECMAScript5 compliant. + * Otherwise, input files are treated as ECMAScript3 compliant. + */ + CommandLineConfig setLanguageIn(String languageIn) { + this.languageIn = languageIn; + return this; + } + + private boolean skipNormalOutputs = false; + + /** + * Sets whether the normal outputs of compilation should be skipped. + */ + CommandLineConfig setSkipNormalOutputs(boolean skipNormalOutputs) { + this.skipNormalOutputs = skipNormalOutputs; + return this; + } + + private List manifestMaps = ImmutableList.of(); + + /** + * Sets the execPath:rootRelativePath mappings + */ + CommandLineConfig setManifestMaps(List manifestMaps) { + this.manifestMaps = manifestMaps; + return this; + } + + + private boolean transformAMDToCJSModules = false; + + /** + * Set whether to transform AMD to CommonJS modules. + */ + CommandLineConfig setTransformAMDToCJSModules( + boolean transformAMDToCJSModules) { + this.transformAMDToCJSModules = transformAMDToCJSModules; + return this; + } + + private boolean processCommonJSModules = false; + + /** + * Sets whether to process CommonJS modules. + */ + CommandLineConfig setProcessCommonJSModules( + boolean processCommonJSModules) { + this.processCommonJSModules = processCommonJSModules; + return this; + } + + + private String commonJSModulePathPrefix = + ProcessCommonJSModules.DEFAULT_FILENAME_PREFIX; + + /** + * Sets the CommonJS module path prefix. + */ + CommandLineConfig setCommonJSModulePathPrefix( + String commonJSModulePathPrefix) { + this.commonJSModulePathPrefix = commonJSModulePathPrefix; + return this; + } + + private String warningsWhitelistFile = ""; + + /** + * Sets a whitelist file that suppresses warnings. + */ + CommandLineConfig setWarningsWhitelistFile(String fileName) { + this.warningsWhitelistFile = fileName; + return this; + } + } + + /** + * A little helper class to make it easier to collect warning types + * from --jscomp_error, --jscomp_warning, and --jscomp_off. + */ + protected static class WarningGuardSpec { + private static class Entry { + private final CheckLevel level; + private final String groupName; + + private Entry(CheckLevel level, String groupName) { + this.level = level; + this.groupName = groupName; + } + } + + // The entries, in the order that they were added. + private final List entries = Lists.newArrayList(); + + protected void add(CheckLevel level, String groupName) { + entries.add(new Entry(level, groupName)); + } + + protected void clear() { + entries.clear(); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AbstractCompiler.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AbstractCompiler.java new file mode 100644 index 0000000..da3cedc --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AbstractCompiler.java @@ -0,0 +1,392 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Supplier; +import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.parsing.Config; +import com.google.javascript.jscomp.type.ReverseAbstractInterpreter; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.head.ErrorReporter; +import com.google.javascript.rhino.head.ast.AstRoot; +import com.google.javascript.rhino.jstype.JSTypeRegistry; + +import java.util.List; +import java.util.Map; + +import javax.annotation.Nullable; + +/** + * An abstract compiler, to help remove the circular dependency of + * passes on JSCompiler. + * + * This is an abstract class, so that we can make the methods package-private. + * + * @author nicksantos@google.com (Nick Santos) + */ +public abstract class AbstractCompiler implements SourceExcerptProvider { + static final DiagnosticType READ_ERROR = DiagnosticType.error( + "JSC_READ_ERROR", "Cannot read: {0}"); + + private LifeCycleStage stage = LifeCycleStage.RAW; + + // TODO(nicksantos): Decide if all of these are really necessary. + // Many of them are just accessors that should be passed to the + // CompilerPass's constructor. + + /** + * Looks up an input (possibly an externs input) by input id. + * May return null. + */ + public abstract CompilerInput getInput(InputId inputId); + + /** + * Looks up a source file by name. May return null. + */ + abstract SourceFile getSourceFileByName(String sourceName); + + /** + * Creates a new externs file. + * @param name A name for the new externs file. + * @throws IllegalArgumentException If the name of the externs file conflicts + * with a pre-existing externs file. + */ + abstract CompilerInput newExternInput(String name); + + /** + * Gets the module graph. May return null if there aren't at least two + * modules. + */ + abstract JSModuleGraph getModuleGraph(); + + /** + * Gets the inputs in the order in which they are being processed. + * Only for use by {@code AbstractCompilerRunner}. + */ + abstract List getInputsInOrder(); + + /** + * Gets a central registry of type information from the compiled JS. + */ + public abstract JSTypeRegistry getTypeRegistry(); + + /** + * Gets a memoized scope creator with type information. + */ + abstract ScopeCreator getTypedScopeCreator(); + + /** + * Gets the top scope. + */ + public abstract Scope getTopScope(); + + /** + * Report an error or warning. + */ + public abstract void report(JSError error); + + /** + * Report an internal error. + */ + abstract void throwInternalError(String msg, Exception cause); + + /** + * Gets the current coding convention. + */ + public abstract CodingConvention getCodingConvention(); + + /** + * Report code changes. + */ + public abstract void reportCodeChange(); + + /** + * Logs a message under a central logger. + */ + abstract void addToDebugLog(String message); + + /** + * Sets the CssRenamingMap. + */ + abstract void setCssRenamingMap(CssRenamingMap map); + + /** + * Gets the CssRenamingMap. + */ + abstract CssRenamingMap getCssRenamingMap(); + + /** + * Gets a suitable SCRIPT node to serve as a parent for code insertion. If + * {@code module} contains any inputs, the returned node will be the SCRIPT + * node corresponding to its first input. If {@code module} is empty, on the + * other hand, then the returned node will be the first SCRIPT node in a + * non-empty module that {@code module} depends on (the deepest one possible). + * + * @param module A module. If null, will return the first SCRIPT node of all + * modules. + * @return A SCRIPT node (never null). + */ + abstract Node getNodeForCodeInsertion(JSModule module); + + /** + * Gets the central registry of type violations. + */ + abstract TypeValidator getTypeValidator(); + + /** + * Parses code for injecting. + */ + abstract Node parseSyntheticCode(String code); + + /** + * Parses code for injecting, and associate it with a given source file. + */ + abstract Node parseSyntheticCode(String filename, String code); + + /** + * Parses code for testing. + */ + abstract Node parseTestCode(String code); + + /** + * Prints a node to source code. + */ + abstract String toSource(Node root); + + /** + * Gets a default error reporter for injecting into Rhino. + */ + abstract ErrorReporter getDefaultErrorReporter(); + + /** + * Get an interpreter for type analysis. + */ + public abstract ReverseAbstractInterpreter getReverseAbstractInterpreter(); + + /** + * @return The current life-cycle stage of the AST we're working on. + */ + LifeCycleStage getLifeCycleStage() { + return stage; + } + + /** + * Generates unique ids. + */ + abstract Supplier getUniqueNameIdSupplier(); + + /** + * @return Whether any errors have been encountered that + * should stop the compilation process. + */ + abstract boolean hasHaltingErrors(); + + /** + * Register a listener for code change events. + */ + abstract void addChangeHandler(CodeChangeHandler handler); + + /** + * Remove a listener for code change events. + */ + abstract void removeChangeHandler(CodeChangeHandler handler); + + /** + * Returns true if compiling in IDE mode. + */ + abstract boolean isIdeMode(); + + /** + * @return Whether the compiler is in ES5Mode. + */ + abstract boolean acceptEcmaScript5(); + + /** + * @return Whether the compiler accepts `const' keyword. + */ + abstract boolean acceptConstKeyword(); + + /** + * Returns the parser configuration. + */ + abstract Config getParserConfig(); + + /** + * Returns true if type checking is enabled. + */ + abstract boolean isTypeCheckingEnabled(); + + /** + * Normalizes the types of AST nodes in the given tree, and + * annotates any nodes to which the coding convention applies so that passes + * can read the annotations instead of using the coding convention. + */ + abstract void prepareAst(Node root); + + /** + * Gets the error manager. + */ + abstract public ErrorManager getErrorManager(); + + /** + * Set the current life-cycle state. + */ + void setLifeCycleStage(LifeCycleStage stage) { + this.stage = stage; + } + + /** + * Are the nodes equal for the purpose of inlining? + * If type aware optimizations are on, type equality is checked. + */ + abstract boolean areNodesEqualForInlining(Node n1, Node n2); + + /** + * Set if RegExp global properties are used. + * @param references Whether there are references to the RegExp global object + * properties. + */ + abstract void setHasRegExpGlobalReferences(boolean references); + + /** + * @return Whether the AST contains references to the RegExp global object + * properties. + */ + abstract boolean hasRegExpGlobalReferences(); + + /** + * @return The error level the given error object will be reported at. + */ + abstract CheckLevel getErrorLevel(JSError error); + + static enum LifeCycleStage { + RAW, + + // See constraints put on the tree by Normalize.java + NORMALIZED, + + // The normalize pass has put constraints on the tree, + // but variables and properties have been renamed so + // coding conventions no longer apply. + NORMALIZED_OBFUSCATED; + + boolean isNormalized() { + return this == NORMALIZED || this == NORMALIZED_OBFUSCATED; + } + + boolean isNormalizedUnobfuscated() { + return this == NORMALIZED; + } + + boolean isNormalizedObfuscated() { + return this == NORMALIZED_OBFUSCATED; + } + } + + /** + * Runs a given compiler-pass by calling its {@code process()} method. + * @param pass The pass to be run. + */ + abstract void process(CompilerPass pass); + + /** + * Returns the root node of the AST, which includes both externs and source. + */ + abstract Node getRoot(); + + // TODO(bashir) It would be good to extract a single dumb data object with + // only getters and setters that keeps all global information we keep for a + // compiler instance. Then move some of the functions of this class there. + + /** + * Updates the list of references for variables in global scope. + * + * @param refMapPatch Maps each variable to all of its references; may contain + * references collected from the whole AST or only a SCRIPT sub-tree. + * @param collectionRoot The root of sub-tree in which reference collection + * has been done. This should either be a SCRIPT node (if collection is + * done on a single file) or it is assumed that collection is on full AST. + */ + abstract void updateGlobalVarReferences(Map + refMapPatch, Node collectionRoot); + + /** + * This can be used to get the list of all references to all global variables + * based on all previous calls to {@code updateGlobalVarReferences}. + * + * @return The reference collection map associated to global scope variable. + */ + abstract GlobalVarReferenceMap getGlobalVarReferences(); + + /** + * @return a CompilerInput that can be modified to add addition extern + * definitions; + */ + abstract CompilerInput getSynthesizedExternsInput(); + + /** + * @return a number in [0,1] range indicating an approximate progress of the + * last compile. Note this should only be used as a hint and no assumptions + * should be made on accuracy, even a completed compile may choose not to set + * this to 1.0 at the end. + */ + public abstract double getProgress(); + + /** + * Gets the last pass name set by setProgress. + */ + abstract String getLastPassName(); + + /** + * Sets the progress percentage as well as the name of the last pass that + * ran (if available). + * @param progress A precentage expressed as a double in the range [0, 1]. + * Use -1 if you just want to set the last pass name. + */ + abstract void setProgress(double progress, @Nullable String lastPassName); + + /** + * The subdir js/ contains libraries of code that we inject + * at compile-time only if requested by this function. + * + * Notice that these libraries will almost always create global symbols. + * + * @param resourceName The name of the library. For example, if "base" is + * is specified, then we load js/base.js + * @return If new code was injected, returns the last expression node of the + * library. If the caller needs to add additional code, they should add + * it as the next sibling of this node. If new code was not injected, + * returns null. + */ + abstract Node ensureLibraryInjected(String resourceName); + + /** + * Stores the "new" Rhino parse tree for a given source file. + * @param sourceName The source file name. + * @param astRoot The "new" Rhino parse tree. + */ + abstract void setOldParseTree(String sourceName, AstRoot astRoot); + + /** + * Gets an old format parse tree for a given source file. + * @param sourceName The source file name to get the tree for. + * @return The "new" Rhino parse tree for the given source file. + */ + abstract AstRoot getOldParseTreeByName(String sourceName); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AbstractMessageFormatter.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AbstractMessageFormatter.java new file mode 100644 index 0000000..018ae86 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AbstractMessageFormatter.java @@ -0,0 +1,87 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableSet; +import com.google.javascript.jscomp.CheckLevel; + +import java.util.Set; + +/** + * Abstract message formatter providing default behavior for implementations + * of {@link MessageFormatter} needing a {@link SourceExcerptProvider}. + * + */ +public abstract class AbstractMessageFormatter implements MessageFormatter { + private final SourceExcerptProvider source; + private boolean colorize; + + public AbstractMessageFormatter(SourceExcerptProvider source) { + this.source = source; + } + + public void setColorize(boolean colorize) { + this.colorize = colorize; + } + + /** + * Get the source excerpt provider. + */ + protected final SourceExcerptProvider getSource() { + return source; + } + + private static final Set SUPPORTED_COLOR_TERMINALS = + ImmutableSet.of("xterm", + "xterm-color", + "xterm-256color", + "screen-bce"); + + static boolean termSupportsColor(String term) { + return SUPPORTED_COLOR_TERMINALS.contains(term); + } + + private static enum Color { + ERROR("\033[31m"), + WARNING("\033[35m"), + RESET("\033[39m"); + + private final String controlCharacter; + + Color(String controlCharacter) { + this.controlCharacter = controlCharacter; + } + + public String getControlCharacter() { + return controlCharacter; + } + } + + String getLevelName(CheckLevel level) { + switch (level) { + case ERROR: return maybeColorize("ERROR", Color.ERROR); + case WARNING: return maybeColorize("WARNING", Color.WARNING); + default: return level.toString(); + } + } + + private String maybeColorize(String text, Color color) { + if (!colorize) return text; + + return color.getControlCharacter() + + text + Color.RESET.getControlCharacter(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AbstractPeepholeOptimization.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AbstractPeepholeOptimization.java new file mode 100644 index 0000000..79dd3c9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AbstractPeepholeOptimization.java @@ -0,0 +1,167 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.javascript.rhino.Node; + +/** + * An abstract class whose implementations run peephole optimizations: + * optimizations that look at a small section of code and either remove + * that code (if it is not needed) or replaces it with smaller code. + * + */ +abstract class AbstractPeepholeOptimization { + + private AbstractCompiler compiler; + + /** + * Given a node to optimize and a traversal, optimize the node. Subclasses + * should override to provide their own peephole optimization. + * + * @param subtree The subtree that will be optimized. + * @return The new version of the subtree (or null if the subtree or one of + * its parents was removed from the AST). If the subtree has not changed, + * this method must return {@code subtree}. + */ + abstract Node optimizeSubtree(Node subtree); + + /** + * Helper method for reporting an error to the compiler when applying a + * peephole optimization. + * + * @param diagnostic The error type + * @param n The node for which the error should be reported + */ + protected void report(DiagnosticType diagnostic, Node n) { + JSError error = + JSError.make(NodeUtil.getSourceName(n), n, diagnostic, n.toString()); + compiler.report(error); + } + + /** + * Helper method for telling the compiler that something has changed. + * Subclasses must call these if they have changed the AST. + */ + protected void reportCodeChange() { + Preconditions.checkNotNull(compiler); + compiler.reportCodeChange(); + } + + /** + * Are the nodes equal for the purpose of inlining? + * If type aware optimizations are on, type equality is checked. + */ + protected boolean areNodesEqualForInlining(Node n1, Node n2) { + /* Our implementation delegates to the compiler. We provide this + * method because we don't want to expose Compiler to PeepholeOptimizations. + */ + Preconditions.checkNotNull(compiler); + return compiler.areNodesEqualForInlining(n1, n2); + } + + /** + * Is the current AST normalized? (e.g. has the Normalize pass been run + * and has the Denormalize pass not yet been run?) + */ + protected boolean isASTNormalized() { + Preconditions.checkNotNull(compiler); + + return compiler.getLifeCycleStage().isNormalized(); + } + + /** + * Informs the optimization that a traversal will begin. + */ + void beginTraversal(AbstractCompiler compiler) { + this.compiler = compiler; + } + + /** + * Informs the optimization that a traversal has completed. + */ + void endTraversal(AbstractCompiler compiler) { + this.compiler = null; + } + + // NodeUtil's mayEffectMutableState and mayHaveSideEffects need access to the + // compiler object, route them through here to give them access. + + /** + * @return Whether the node may create new mutable state, or change existing + * state. + */ + boolean mayEffectMutableState(Node n) { + return NodeUtil.mayEffectMutableState(n, compiler); + } + + /** + * @return Whether the node may have side effects when executed. + */ + boolean mayHaveSideEffects(Node n) { + return NodeUtil.mayHaveSideEffects(n, compiler); + } + + /** + * Returns true if the current node's type implies side effects. + * + * This is a non-recursive version of the may have side effects + * check; used to check wherever the current node's type is one of + * the reason's why a subtree has side effects. + */ + boolean nodeTypeMayHaveSideEffects(Node n) { + return NodeUtil.nodeTypeMayHaveSideEffects(n, compiler); + } + + /** + * @return Whether the source code version is ECMAScript 5 or later. + * Workarounds for quirks in browsers that do not support ES5 can be + * ignored when this is true. + */ + boolean isEcmaScript5OrGreater() { + return compiler != null + && compiler.acceptEcmaScript5(); + } + + /** + * @return the current coding convention. + */ + CodingConvention getCodingConvention() { + // Note: this assumes a thread safe coding convention object. + return compiler.getCodingConvention(); + } + + /** + * Check if the specified node is null or is still in the AST. + */ + @VisibleForTesting + static Node validateResult(Node n) { + done: { + if (n != null && !n.isScript() + && (!n.isBlock() || !n.isSyntheticBlock())) { + for (Node parent : n.getAncestors()) { + if (parent.isScript()) { + break done; + } + } + Preconditions.checkState(false); + } + } + return n; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AliasExternals.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AliasExternals.java new file mode 100644 index 0000000..fc43f34 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AliasExternals.java @@ -0,0 +1,781 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import javax.annotation.Nullable; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Arrays; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +/** + *

AliasExternals provides wrappers and aliases for external globals and + * properties to that they can be referenced by their full name only once + * instead of in all use sites.

+ * + *

The property alias pass creates function wrappers for properties that need + * to be accessed externally. These function wrappers are then used by all + * internal calls to the property, and the names will be compressed during the + * RenamePrototypes step.

+ * + *

Properties that are accessed externally are either system functions + * (i.e. window.document), or used by JavaScript embedded on a page.

+ * + *

Properties that are r-values are changed to use array notation with + * a string that has been defined separately and can be compressed + * i.e. document.window -> document[PROP_window].

+ * + *

Properties that are l-values and can be renamed are renamed to + * SETPROP_prop. I.e. node.innerHTML = '<div>Hello</div>' -> + * SETPROP_innerHTML(node, '<div>hello</div>').

+ * + *

Properties will only be renamed if they are used more than requiredUsage_ + * times, as there is overhead for adding the accessor and mutator functions. + * This is initialized to DEFAULT_REQUIRED_USAGE (=4), but can be + * overridden.

+ * + *

Certain usages (increment, decrement) won't be addressed, as they would + * require a getprop, setprop, and custom logic, and aren't worth + * optimizing.

+ * + *

The global alias pass creates aliases for global variables and functions + * that are declared or need to be used externally. These aliases are then used + * throughout the code, and will be compressed during the RenameVars step.

+ * + *

Globals are aliased by inserting code like "var GLOBAL_window = window;" + * and then replacing all other uses of "window" with "GLOBAL_window."

+ * + *

Globals that are l-values are not aliased.

+ * + */ +class AliasExternals implements CompilerPass { + + /** Number of times a property needs to be accessed in order to alias */ + private static final int DEFAULT_REQUIRED_USAGE = 4; + + /** Number of times a property must be referenced in order to be aliased */ + private int requiredUsage = DEFAULT_REQUIRED_USAGE; + + /** Minimum property size to be worth renaming */ + private static final int MIN_PROP_SIZE = 4; + + /** + * The name of the variable used for the "prototype" string value. This is + * special-cased to make deobfuscated stack traces shorter and more readable + * ("$MyClass$$P$$method$" rather than "$MyClass$$$PROP_prototype$method$"). + * @see NameAnonymousFunctions + */ + static final String PROTOTYPE_PROPERTY_NAME = + getArrayNotationNameFor("prototype"); + + /** Map of all properties that we may be renaming */ + private final Map props = Maps.newHashMap(); + + /** Holds the properties that can be renamed to GETPROP_ */ + private final List accessors = Lists.newArrayList(); + + /** Holds the properties that can be renamed to SETPROP_ */ + private final List mutators = Lists.newArrayList(); + + /** + * Map of node replacements - + * Identity map because Node implements equals() but not hashCode() + */ + private final Map replacementMap = + new IdentityHashMap(); + + /** Map of all globals that we may alias */ + private final Map globals = Maps.newHashMap(); + + /** Reference to JS Compiler */ + private final AbstractCompiler compiler; + + /** Reference to module inputs */ + private final JSModuleGraph moduleGraph; + + /** Root in parse tree for adding generated nodes */ + private Node defaultRoot; + + /** Root in each module for adding generated nodes, if using modules */ + private Map moduleRoots; + + /** + * A set of globals that can not be aliased since they may be undefined or + * can cause errors + */ + private final Set unaliasableGlobals = Sets.newHashSet( + // While "arguments" is declared as a global extern, it really only has + // meaning inside function bodies and should not be aliased at a global + // level. + "arguments", + // Eval should not be aliased, per the ECMA-262 spec section 15.1.2.1 + "eval", + // "NodeFilter" is not defined in IE and throws an error if you try to + // do var foo = NodeFilter. + "NodeFilter", + // Calls to this special function are eliminated by the RenameProperties + // compiler pass. + "JSCompiler_renameProperty"); + + /** Whitelist of aliasable externs. */ + private final Set aliasableGlobals = Sets.newHashSet(); + + /** + * Creates an instance. + * + * @param compiler The Compiler + * @param moduleGraph The graph of input modules. May be null. If given, we'll + * try to push aliased externs into the deepest possible module. + */ + AliasExternals(AbstractCompiler compiler, JSModuleGraph moduleGraph) { + this(compiler, moduleGraph, null, null); + } + + /** + * Creates an instance. + * + * @param compiler The Compiler + * @param moduleGraph The graph of input modules. May be null. If given, we'll + * try to push aliased externs into the deepest possible module. + * @param unaliasableGlobals Comma-separated list of additional globals that + * cannot be aliased since they may be undefined or can cause errors + * (e.g. "foo,bar"). May be null or the empty string. + * @param aliasableGlobals Comma-separated list of globals that + * can be aliased. If provided, only this list of globals can be aliased. + */ + AliasExternals(AbstractCompiler compiler, JSModuleGraph moduleGraph, + @Nullable String unaliasableGlobals, + @Nullable String aliasableGlobals) { + this.compiler = compiler; + this.moduleGraph = moduleGraph; + + if (!Strings.isNullOrEmpty(unaliasableGlobals) && + !Strings.isNullOrEmpty(aliasableGlobals)) { + throw new IllegalArgumentException( + "Cannot pass in both unaliasable and aliasable globals; you must " + + "choose one or the other."); + } + + if (!Strings.isNullOrEmpty(unaliasableGlobals)) { + this.unaliasableGlobals.addAll( + Arrays.asList(unaliasableGlobals.split(","))); + } + + if (!Strings.isNullOrEmpty(aliasableGlobals)) { + this.aliasableGlobals.addAll(Arrays.asList(aliasableGlobals.split(","))); + } + + if (moduleGraph != null) { + moduleRoots = Maps.newHashMap(); + } + } + + /** + * Sets the number of times a property needs to be referenced in order to + * create an alias for it. + * @param usage Number of times + */ + public void setRequiredUsage(int usage) { + this.requiredUsage = usage; + } + + /** + * Do all processing on the root node. + */ + @Override + public void process(Node externs, Node root) { + defaultRoot = root.getFirstChild(); + Preconditions.checkState(defaultRoot.isScript()); + + aliasProperties(externs, root); + aliasGlobals(externs, root); + } + + private void aliasProperties(Node externs, Node root) { + // Get the reserved names, filtered by the whitelist. + NodeTraversal.traverse(compiler, externs, + new GetAliasableNames(aliasableGlobals)); + props.put("prototype", newSymbolForProperty("prototype")); + + // Find the props that can be changed + NodeTraversal.traverse(compiler, root, new PropertyGatherer()); + + // Iterate through the reserved names, decide what to change + // This could have been done during property traversal, but + // This gives opportunity for review & modification if needed + for (Symbol prop : props.values()) { + if (prop.name.length() >= MIN_PROP_SIZE) { + if (prop.accessorCount >= requiredUsage) { + prop.aliasAccessor = true; + } + if (prop.mutatorCount >= requiredUsage) { + prop.aliasMutator = true; + } + } + } + // Change the references to the property gets + for (Node propInfo : accessors) { + replaceAccessor(propInfo); + } + + // Change the references to the property sets + for (Node propInfo : mutators) { + replaceMutator(propInfo); + } + + // And add the accessor and mutator functions, if needed. Property names are + // grouped together so that the CollapseVariableDeclarations pass can put + // them in a single variable declaration statement. + for (Symbol prop : props.values()) { + if (prop.aliasAccessor) { + addAccessorPropName(prop.name, getAddingRoot(prop.deepestModuleAccess)); + } + } + + for (Symbol prop : props.values()) { + if (prop.aliasMutator) { + addMutatorFunction(prop.name, getAddingRoot(prop.deepestModuleMutate)); + } + } + } + + /* + * Replaces a GETPROP with array notation, so that + * it can be optimized. + * I.e. prop.length -> prop[PROP_length] -> prop[a]; + */ + private void replaceAccessor(Node getPropNode) { + /* + * BEFORE + getprop + NODE... + string length + AFTER + getelem + NODE... + name PROP_length + */ + Node propNameNode = getPropNode.getLastChild(); + String propName = propNameNode.getString(); + if (props.get(propName).aliasAccessor) { + Node propSrc = getPropNode.getFirstChild(); + getPropNode.removeChild(propSrc); + + Node newNameNode = + IR.name(getArrayNotationNameFor(propName)); + + Node elemNode = IR.getelem(propSrc, newNameNode); + replaceNode(getPropNode.getParent(), getPropNode, elemNode); + + compiler.reportCodeChange(); + } + } + + /** + * Changes a.prop = b to SETPROP_prop(a, b); + */ + private void replaceMutator(Node getPropNode) { + /* + BEFORE + exprstmt 1 + assign 128 + getprop + NodeTree A + string prop + NODE TREE B + + AFTER + exprstmt 1 + call + name SETPROP_prop + NodeTree A + NODE TREE B + */ + Node propNameNode = getPropNode.getLastChild(); + Node parentNode = getPropNode.getParent(); + + Symbol prop = props.get(propNameNode.getString()); + if (prop.aliasMutator) { + Node propSrc = getPropNode.getFirstChild(); + Node propDest = parentNode.getLastChild(); + + // Remove the orphaned children + getPropNode.removeChild(propSrc); + getPropNode.removeChild(propNameNode); + parentNode.removeChild(propDest); + + // Create the call GETPROP_prop() node, using the old propSrc as the + // one parameter to GETPROP_prop() call. + Node callName = IR.name( + getMutatorFor(propNameNode.getString())); + Node call = IR.call( callName, propSrc, propDest); + call.putBooleanProp(Node.FREE_CALL, true); + + // And replace the assign statement with the new call + replaceNode(parentNode.getParent(), parentNode, call); + + compiler.reportCodeChange(); + } + } + + /** + * Utility function to replace a Node with another node. + * Keeps track of previous replacements so that if you try to replace + * a child of a parent that has changed, it replaces on the new parent + * @param parent Parent of node to be replaced + * @param before Node to be replaced + * @param after Replacement node + */ + private void replaceNode(Node parent, Node before, Node after) { + if (replacementMap.containsKey(parent)) { + parent = replacementMap.get(parent); + } + parent.replaceChild(before, after); + replacementMap.put(before, after); + } + + /** + * Adds a string that can be used to reference properties by array [] + * notation. + * + * PROP_prototype = 'prototype'; + * + * @param propName Name of property + * @param root Root of output tree that function can be added to + */ + private void addAccessorPropName(String propName, Node root) { + /* + * Target: + + var 1 + name PROP_length + string length + */ + Node propValue = IR.string(propName); + Node propNameNode = + IR.name(getArrayNotationNameFor(propName)); + propNameNode.addChildToFront(propValue); + Node var = IR.var(propNameNode); + root.addChildToFront(var); + + compiler.reportCodeChange(); + } + + /** + * Create set property function in JS. Output will be: + * SETPROP_prop(a, b) {a.prop = b;} + * + * @param propName Name of property + * @param root Root of output tree that function can be added to + */ + private void addMutatorFunction(String propName, Node root) { + /* + function SETPROP_prop + name SETPROP_prop + lp + name a + name b + block 1 + return 1 + assign + getprop + name a + string prop + name b + */ + + // Function name node + String functionName = getMutatorFor(propName); + + // Function arguments + String localPropName = getMutatorFor(propName) + "$a"; + String localValueName = getMutatorFor(propName) + "$b"; + // Create the function and append to front of output tree + Node fnNode = IR.function( + IR.name(functionName), + IR.paramList(IR.name(localPropName), IR.name(localValueName)), + IR.block( + IR.returnNode( + IR.assign( + IR.getprop(IR.name(localPropName), IR.string(propName)), + IR.name(localValueName))))); + root.addChildToFront(fnNode); + + compiler.reportCodeChange(); + } + + /** + * Gets a SCRIPT node for code insertion in {@code m} or, if {@code m} is + * empty, in as deep an ancestor module of {@code m} as possible. Returns + * {@code this.defaultRoot} if {@code m} is null. + * + * @param m The module to find a root in (may be null) + * @return A root node + */ + private Node getAddingRoot(JSModule m) { + if (m != null) { + Node root = moduleRoots.get(m); + if (root != null) { + return root; + } + + root = compiler.getNodeForCodeInsertion(m); + if (root != null) { + moduleRoots.put(m, root); + return root; + } + } + + return defaultRoot; + } + + /** + * Gets the aliasable names from externs.js + */ + private class GetAliasableNames extends AbstractPostOrderCallback { + private final Set whitelist; + + public GetAliasableNames(final Set whitelist) { + this.whitelist = whitelist; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.GETPROP: + case Token.GETELEM: + Node dest = n.getFirstChild().getNext(); + if (dest.isString() && + (whitelist.isEmpty() || whitelist.contains(dest.getString()))) { + props.put(dest.getString(), newSymbolForProperty(dest.getString())); + } + } + } + } + + /** + * Gets references to all of the replaceable nodes, as well + * as counting the usage for each property name. + */ + private final class PropertyGatherer extends AbstractPostOrderCallback { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isGetProp()) { + Node propNameNode = n.getLastChild(); + + if (canReplaceWithGetProp(propNameNode, n, parent)) { + String name = propNameNode.getString(); + props.get(name).recordAccessor(t); + accessors.add(n); + } + if (canReplaceWithSetProp(propNameNode, n, parent)) { + String name = propNameNode.getString(); + + props.get(name).recordMutator(t); + mutators.add(n); + } + } + } + + /** + * Logic for when a getprop can be replaced. + * Can't alias a call to eval per ECMA-262 spec section 15.1.2.1 + * Can't be an assign -> no a.b = c; + * Can't be inc or dec -> no a.b++; or a.b--; + * Must be a GETPROP (NODE, A) where A is a reserved name + * @param propNameNode Property name node + * @param getPropNode GETPROP node + * @param parent parent node + * @return True if can be replaced + */ + private boolean canReplaceWithGetProp(Node propNameNode, Node getPropNode, + Node parent) { + boolean isCallTarget = (parent.isCall()) + && (parent.getFirstChild() == getPropNode); + boolean isAssignTarget = NodeUtil.isAssignmentOp(parent) + && (parent.getFirstChild() == getPropNode); + boolean isIncOrDec = (parent.isInc()) || + (parent.isDec()); + return (propNameNode.isString()) && !isAssignTarget + && (!isCallTarget || !"eval".equals(propNameNode.getString())) + && !isIncOrDec + && props.containsKey(propNameNode.getString()); + } + + /** + * Logic for whether a setprop can be replaced. + * + * True if it is target of assign (i.e. foo = A.B), and B is a reserved name + * @param propNameNode Property name node + * @param getPropNode GETPROP node + * @param parent parent node + * @return True if can be replaced + */ + private boolean canReplaceWithSetProp(Node propNameNode, Node getPropNode, + Node parent) { + boolean isAssignTarget = (parent.isAssign()) + && (parent.getFirstChild() == getPropNode); + return (propNameNode.isString()) && isAssignTarget + && props.containsKey(propNameNode.getString()); + } + } + + /** + * Gets the mutator name for a property. + */ + private static String getMutatorFor(String prop) { + return "SETPROP_" + prop; + } + + /** + * Gets the array notation name for a property. + */ + private static String getArrayNotationNameFor(String prop) { + return "$$PROP_" + prop; + } + + private void aliasGlobals(Node externs, Node root) { + // Find all the extern globals that we should alias + NodeTraversal.traverse(compiler, externs, new GetGlobals()); + + // Find all the globals that can be changed + NodeTraversal.traverse(compiler, root, new GlobalGatherer()); + + // Iterate through the used globals, decide what to change. + for (Symbol global : globals.values()) { + if (global.mutatorCount > 0) { + continue; + } + + // We assume that each alias variable will end up compressed to two letter + // names. There is also the overhead of "var xx=;" + int currentBytes = global.name.length() * global.accessorCount; + int aliasedBytes = 8 + global.name.length() + 2 * global.accessorCount; + + if (aliasedBytes < currentBytes) { + global.aliasAccessor = true; + } + } + + // Change the references to the globals + for (Symbol global : globals.values()) { + for (Node globalUse : global.uses) { + replaceGlobalUse(globalUse); + } + if (global.aliasAccessor) { + addGlobalAliasNode(global, + getAddingRoot(global.deepestModuleAccess)); + } + } + } + + /** + * Gets the aliasable names from externs.js + */ + private class GetGlobals extends NodeTraversal.AbstractShallowCallback { + private void getGlobalName(NodeTraversal t, Node dest, Node parent) { + if (dest.isName()) { + + JSDocInfo docInfo = dest.getJSDocInfo() == null ? + parent.getJSDocInfo() : dest.getJSDocInfo(); + boolean aliasable = !unaliasableGlobals.contains(dest.getString()) && + (docInfo == null || !docInfo.isNoAlias()); + + if (aliasable) { + String name = dest.getString(); + Scope.Var var = t.getScope().getVar(name); + + if (var != null && !var.isLocal()) { + globals.put(name, newSymbolForGlobalVar(dest)); + } + } + } + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.FUNCTION: + getGlobalName(t, n.getFirstChild(), n); + break; + case Token.VAR: + for (Node varChild = n.getFirstChild(); + varChild != null; + varChild = varChild.getNext()) { + getGlobalName(t, varChild, n); + } + break; + } + } + } + + /** + * Gets references to all of the replaceable nodes, as well as counting the + * usage for each global. + */ + private final class GlobalGatherer extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isName()) { + String name = n.getString(); + Scope.Var var = t.getScope().getVar(name); + + // It's ok for var to be null since it won't be in any scope if it's + // an extern + if (var != null && var.isLocal()) { + return; + } + + Symbol global = globals.get(name); + if (global != null) { + // If a variable is declared in both externs and normal source, + // don't alias it. + if (n.getParent().isVar() || + n.getParent().isFunction()) { + globals.remove(name); + } + + boolean isFirst = parent.getFirstChild() == n; + // If a global is being assigned to or otherwise modified, then we + // don't want to alias it. + // Using "new" with this global is not a mutator, but it's also + // something that we want to avoid when aliasing, since we may be + // dealing with external objects (e.g. ActiveXObject in MSIE) + if ((NodeUtil.isAssignmentOp(parent) && isFirst) || + (parent.isNew() && isFirst) || + parent.isInc() || + parent.isDec()) { + global.recordMutator(t); + } else { + global.recordAccessor(t); + } + + global.uses.add(n); + } + } + } + } + + /** + * Replace uses of a global with its aliased name. + */ + private void replaceGlobalUse(Node globalUse) { + String globalName = globalUse.getString(); + if (globals.get(globalName).aliasAccessor) { + globalUse.setString("GLOBAL_" + globalName); + + // None of the aliases are marked as @const. + // Because we're reusing the original ref node, + // we need to update it to reflect this. + globalUse.putBooleanProp(Node.IS_CONSTANT_NAME, false); + + compiler.reportCodeChange(); + } + } + + /** + * Adds an alias variable for the global: + * + * var GLOBAL_window = window; + * + * @param global Name of global + * @param root Root of output tree that function can be added to + */ + private void addGlobalAliasNode(Symbol global, Node root) { + /* + * Target: + + var 1 + name GLOBAL_window + name window + */ + + String globalName = global.name; + Node globalValue = IR.name(global.name); + globalValue.putBooleanProp(Node.IS_CONSTANT_NAME, global.isConstant); + + Node globalNameNode = IR.name("GLOBAL_" + globalName); + globalNameNode.addChildToFront(globalValue); + Node var = IR.var(globalNameNode); + root.addChildToFront(var); + + compiler.reportCodeChange(); + } + + private Symbol newSymbolForGlobalVar(Node name) { + return new Symbol( + name.getString(), name.getBooleanProp(Node.IS_CONSTANT_NAME)); + } + + private Symbol newSymbolForProperty(String name) { + return new Symbol(name, false); + } + + /** Struct to hold information about properties & usage */ + private class Symbol { + public final String name; + public int accessorCount = 0; + public int mutatorCount = 0; + public boolean aliasMutator = false; + public boolean aliasAccessor = false; + public final boolean isConstant; + + JSModule deepestModuleAccess = null; + JSModule deepestModuleMutate = null; + + List uses = Lists.newArrayList(); + + private Symbol(String name, boolean isConstant) { + this.name = name; + this.isConstant = isConstant; + } + + void recordAccessor(NodeTraversal t) { + accessorCount++; + if (moduleGraph != null) { + deepestModuleAccess = (deepestModuleAccess == null) ? + t.getModule() : + moduleGraph.getDeepestCommonDependencyInclusive( + t.getModule(), deepestModuleAccess); + } + } + + void recordMutator(NodeTraversal t) { + mutatorCount++; + if (moduleGraph != null) { + deepestModuleMutate = (deepestModuleMutate == null) ? + t.getModule() : + moduleGraph.getDeepestCommonDependencyInclusive( + t.getModule(), deepestModuleMutate); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AliasKeywords.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AliasKeywords.java new file mode 100644 index 0000000..947a1bf --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AliasKeywords.java @@ -0,0 +1,465 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + *

Replaces references to aliasable keyword literals (true, false, + * null) with variables and statements (throw) with functions declared in the + * global scope. When combined with RenameVars, this pass helps to reduce the + * number of bytes taken up by references to these keywords by replacing them + * with references to variables and functions with shorter names.

+ * + */ +class AliasKeywords implements CompilerPass { + /** Callback that finds the nodes that we will alias. */ + private class FindAliasableNodes extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + final int type = n.getType(); + if (isAliasableType(type)) { + visitAliasableNode(n, parent); + } else if (type == Token.NAME) { + visitNameNode(n); + } + } + + /** + * Find the AliasSpecification associated with the node, and tell + * that AliasSpecification about the new node. + */ + private void visitAliasableNode(Node n, Node parent) { + AliasSpecification aliasableNodes = aliasTypes.get(n.getType()); + aliasableNodes.visit(n, parent); + } + + /** + * Sanity check that our aliases are not already defined by + * someone else. + */ + private void visitNameNode(Node n) { + if (isAliasDefinition(n)) { + throw new IllegalStateException( + "Existing alias definition for " + Token.name(n.getType())); + } + } + } + + /** + * An AliasSpecification encapsulates all of the logic to find + * aliasable nodes and alias those nodes, for a given alias name. Subclasses + * fill in template methods, allowing for various kinds of aliasing. + */ + private abstract static class AliasSpecification { + + /** List of nodes to alias (e.g. all 'null' nodes). */ + private final Map nodes = Maps.newHashMap(); + + /** + * Have we declared the alias (e.g. did we inject var + * $$ALIAS_NULL=null; into the parse tree)? + */ + private boolean isAliased = false; + + private String aliasName; + + private int tokenId; + + /** + * @param aliasName name being used as alias + * @param tokenId type of node being replaced with alias + */ + public AliasSpecification(String aliasName, int tokenId) { + this.aliasName = aliasName; + this.tokenId = tokenId; + } + + public void visit(Node n, Node parent) { + nodes.put(n, parent); + } + + /** + * Insert a node that declares our alias into the parse tree, as a + * child of the specified var node. Only do so if we haven't + * already and there are enough references to the aliased node to + * save bytes. + * @return Whether the alias has been inserted. + */ + boolean maybeInsertAliasDeclarationIntoParseTree(Node codeRoot) { + if (nodes.size() >= minOccurrencesRequiredToAlias()) { + insertAliasDeclaration(codeRoot); + isAliased = true; + return true; + } + return false; + } + + /** + * Update all of the nodes with a reference to the corresponding + * alias node. + */ + public void doAlias(AbstractCompiler compiler) { + if (isAliased) { + for (Map.Entry entry : nodes.entrySet()) { + Node n = entry.getKey(); + Node parent = entry.getValue(); + aliasNode(n, parent); + compiler.reportCodeChange(); + } + } + } + + public int getTokenId() { + return tokenId; + } + + public String getAliasName() { + return aliasName; + } + + /** + * Returns the minimum number of nodes that should be present for aliasing + * to take place. + */ + protected abstract int minOccurrencesRequiredToAlias(); + + /** + * Creates a node that defines the alias and attaches it to the parse tree. + * + * @param codeRoot The root of the script. Functions can be attached here, + * e.g., function alias(p){throw p;}. + */ + protected abstract void insertAliasDeclaration(Node codeRoot); + + /** Replaces the node n with its alias. */ + protected abstract void aliasNode(Node n, Node parent); + } + + /** Aliases throw statements with a function call. */ + // TODO(user): Generalize this to work with typeof expressions. + private class ThrowAliasSpecification extends AliasSpecification { + ThrowAliasSpecification(String aliasName) { + super(aliasName, Token.THROW); + } + + @Override + protected void aliasNode(Node throwNode, Node parent) { + Node name = NodeUtil.newName( + compiler.getCodingConvention(), + getAliasName(), throwNode, getAliasName()); + Node aliasCall = IR.call( name, throwNode.removeFirstChild()); + aliasCall.putBooleanProp(Node.FREE_CALL, true); + Node exprResult = IR.exprResult(aliasCall); + parent.replaceChild(throwNode, exprResult); + } + + @Override + /** + * Adds alias function to codeRoot. See {@link #createAliasFunctionNode}). + */ + protected void insertAliasDeclaration(Node codeRoot) { + codeRoot.addChildToFront(createAliasFunctionNode(getAliasName())); + } + + @Override + protected int minOccurrencesRequiredToAlias() { + return MIN_OCCURRENCES_REQUIRED_TO_ALIAS_THROW; + } + } + + /** + * Calculates the minimum number of occurrences of throw needed to alias + * throw. + */ + static int estimateMinOccurrencesRequriedToAlias() { + // Assuming that the alias function name is two bytes in length, two bytes + // will be saved per occurrence of throw: + // throw e;, compared to + // TT(e);. + // However, the alias definition is some length, N, e.g., + // function TT(t){throw t;} + // Hence there must be more than N/2 occurrences of throw to reduce + // the code size. + Node alias = createAliasFunctionNode("TT"); + return InlineCostEstimator.getCost(alias) / 2 + 1; + } + + /** + * Creates a function node that takes a single argument, the object to + * throw. The function throws the object. + */ + private static Node createAliasFunctionNode(String aliasName) { + final String PARAM_NAME = "jscomp_throw_param"; + return IR.function( + IR.name(aliasName), + IR.paramList(IR.name(PARAM_NAME)), + IR.block( + IR.throwNode(IR.name(PARAM_NAME)))); + } + + /** Aliases literal keywords (e.g., null) with variable names. */ + private class KeywordAliasSpecification extends AliasSpecification { + KeywordAliasSpecification(String aliasName, int tokenId) { + super(aliasName, tokenId); + } + + @Override + protected int minOccurrencesRequiredToAlias() { + return MIN_OCCURRENCES_REQUIRED_TO_ALIAS_LITERAL; + } + + @Override + protected void aliasNode(Node n, Node parent) { + Node aliasNode = NodeUtil.newName( + compiler.getCodingConvention(), getAliasName(), n, getAliasName()); + parent.replaceChild(n, aliasNode); + } + + @Override + /** + * Create the alias declaration (e.g. var $$ALIAS_NULL=null;). + */ + protected void insertAliasDeclaration(Node codeRoot) { + Node varNode = new Node(Token.VAR); + Node value = new Node(getTokenId()); + Node name = NodeUtil.newName( + compiler.getCodingConvention(), getAliasName(), + varNode, getAliasName()); + name.addChildToBack(value); + varNode.addChildToBack(name); + codeRoot.addChildrenToFront(varNode); + } + } + + /** Aliases literal keywords (e.g., null) with variable names. */ + private class VoidKeywordAliasSpecification extends AliasSpecification { + VoidKeywordAliasSpecification(String aliasName, int tokenId) { + super(aliasName, tokenId); + } + + @Override + public void visit(Node n, Node parent) { + Node value = n.getFirstChild(); + if (value.isNumber() && value.getDouble() == 0) { + super.visit(n, parent); + } + } + + @Override + protected int minOccurrencesRequiredToAlias() { + return MIN_OCCURRENCES_REQUIRED_TO_ALIAS_LITERAL; + } + + @Override + protected void aliasNode(Node n, Node parent) { + Node aliasNode = NodeUtil.newName( + compiler.getCodingConvention(), getAliasName(), n, getAliasName()); + parent.replaceChild(n, aliasNode); + } + + @Override + /** + * Create the alias declaration (e.g. var $$ALIAS_VOID=void 0;). + */ + protected void insertAliasDeclaration(Node codeRoot) { + Node varNode = new Node(Token.VAR); + Node value = IR.voidNode(IR.number(0)); + Node name = NodeUtil.newName( + compiler.getCodingConvention(), getAliasName(), + varNode, getAliasName()); + name.addChildToBack(value); + varNode.addChildToBack(name); + codeRoot.addChildrenToFront(varNode); + } + } + + + static final String ALIAS_NULL = "JSCompiler_alias_NULL"; + static final String ALIAS_TRUE = "JSCompiler_alias_TRUE"; + static final String ALIAS_FALSE = "JSCompiler_alias_FALSE"; + static final String ALIAS_THROW = "JSCompiler_alias_THROW"; + static final String ALIAS_VOID = "JSCompiler_alias_VOID"; + + /** + * Don't alias a keyword unless it's referenced at least + * MIN_OCCURRENCES_REQUIRED_TO_ALIAS_LITERAL times. Aliasing a keyword has a + * cost (e.g. 'var XX=true;' costs 12 bytes). We make up for this + * cost by replacing references to the keyword with variables that + * have shorter names. If there are only a few references to a + * keyword, the cost outweighs the benefit. It is not possible to + * determine the exact break-even point without compiling twice + * (once with aliasing, another without) and comparing the + * post-gzipped size, so we define a minimum number of references + * required in order to alias. We choose 6 because the alias cost is + * ~7-12 bytes (12 bytes for 'var XX=true;', 7 bytes for a + * subsequent declaration that does not require its own 'var ' or + * semicolon, e.g. var XX=true,XY=null;), but each reference saves + * 2-3 bytes (2 for true and null, 3 for false). Thus, the break + * even point is 3 at best, and 6 at worst. We could use a + * CostEstimator to be precise, but requiring a constant number of + * occurrences is much simpler, and the added precision of a + * CostEstimator would save us <10 bytes for some unlikely edge + * cases (e.g. where false is referenced exactly 5 times, but does + * not get aliased). + */ + static final int MIN_OCCURRENCES_REQUIRED_TO_ALIAS_LITERAL = 6; + + /** + * Don't alias throw statements unless throw is used at least + * MIN_OCCURRENCES_REQUIRED_TO_ALIAS_THROW times. + */ + static final int MIN_OCCURRENCES_REQUIRED_TO_ALIAS_THROW = + estimateMinOccurrencesRequriedToAlias(); + + /** Reference to JS Compiler */ + private final AbstractCompiler compiler; + + /** List of alias specifications, stored in order which transformations + * should be applied. See {@link #createAliasSpecifications}. + */ + private final List aliasSpecifications; + + /** Map from rhino nodes to the corresponding AliasSpecification */ + private final Map aliasTypes; + + /** Set of alias names. */ + private final Set aliasNames; + + AliasKeywords(AbstractCompiler compiler) { + this.compiler = compiler; + aliasSpecifications = createAliasSpecifications(); + aliasTypes = Maps.newLinkedHashMap(); + aliasNames = Sets.newLinkedHashSet(); + for (AliasSpecification specification : aliasSpecifications) { + aliasTypes.put(specification.getTokenId(), specification); + aliasNames.add(specification.getAliasName()); + } + } + + /** + * Do all processing on the root node. + */ + @Override + public void process(Node externs, Node root) { + // Find candidates to alias. + NodeTraversal.traverse(compiler, root, new FindAliasableNodes()); + + if (needsAliases()) { + // Inject alias nodes for null, true, and false into the global scope. + addAliasNodes(compiler.getNodeForCodeInsertion(null)); + + // Update references to null/true/false with references to the aliases. + for (AliasSpecification spec : aliasSpecifications) { + spec.doAlias(compiler); + } + } + } + + private boolean needsAliases() { + for (AliasSpecification spec : aliasSpecifications) { + if (!spec.nodes.isEmpty()) { + return true; + } + } + + return false; + } + + /** + * Inject alias nodes into the global scope. e.g. + * var $$ALIAS_NULL=null,$$ALIAS_TRUE=true,$$ALIAS_FALSE=false;. + */ + private void addAliasNodes(Node codeRoot) { + boolean codeChanged = false; + + for (AliasSpecification spec : aliasSpecifications) { + if (spec.maybeInsertAliasDeclarationIntoParseTree(codeRoot)) { + codeChanged = true; + } + } + + if (codeChanged) { + compiler.reportCodeChange(); + } + } + + /** + * Does the given node define one of our aliases? + */ + private boolean isAliasDefinition(Node n) { + if (!n.isName()) { + return false; + } + + if (!isAliasName(n.getString())) { + // The given Node's string contents is not an alias. Skip it. + return false; + } + + /* + * A definition must have a child node (otherwise it's just a + * reference to the alias). + */ + return n.getFirstChild() != null; + } + + /** + * Is this one of the Rhino token types that we're aliasing? + */ + private boolean isAliasableType(int type) { + return aliasTypes.containsKey(type); + } + + /** + * Is this one of our alias names? + */ + private boolean isAliasName(String name) { + return aliasNames.contains(name); + } + + /** + * Create the AliasSpecifications, one for each type we're aliasing. The + * order of the elements in the list is significant. Transformations should + * be applied in the given order. + */ + private List createAliasSpecifications() { + List l = Lists.newArrayList(); + l.add(new KeywordAliasSpecification(ALIAS_FALSE, Token.FALSE)); + l.add(new KeywordAliasSpecification(ALIAS_NULL, Token.NULL)); + l.add(new KeywordAliasSpecification(ALIAS_TRUE, Token.TRUE)); + l.add(new VoidKeywordAliasSpecification(ALIAS_VOID, Token.VOID)); + // Process throw nodes after literal keyword nodes. This is important when + // a literal keyword is thrown (e.g., throw true;). + // KeywordAliasSpecification needs to know what the parent of the node being + // replaced with an alias is. Because ThrowAliasSpecification replaces the + // parent of the node being aliased, ThrowAliasSpecification invalidates the + // record of the node's parent that KeywordAliasSpecification stores. + l.add(new ThrowAliasSpecification(ALIAS_THROW)); + return l; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AliasStrings.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AliasStrings.java new file mode 100644 index 0000000..437c030 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AliasStrings.java @@ -0,0 +1,456 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.SortedMap; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.CRC32; + +/** + * A {@link Compiler} pass for aliasing strings. String declarations + * contribute to garbage collection, which becomes a problem in large + * applications. Strings that should be aliased occur many times in the code, + * or occur on codepaths that get executed frequently. + * + */ +class AliasStrings extends AbstractPostOrderCallback + implements CompilerPass { + + private static final Logger logger = + Logger.getLogger(AliasStrings.class.getName()); + + /** Prefix for variable names for the aliased strings */ + private static final String STRING_ALIAS_PREFIX = "$$S_"; + + private final AbstractCompiler compiler; + + private final JSModuleGraph moduleGraph; + + // Regular expression matcher for a blacklisting strings in aliasing. + private Matcher blacklist = null; + + /** + * Strings that can be aliased, or null if all strings except 'undefined' + * should be aliased + */ + private final Set aliasableStrings; + + private final boolean outputStringUsage; + + private final SortedMap stringInfoMap = Maps.newTreeMap(); + + private final Set usedHashedAliases = new LinkedHashSet(); + + /** + * Map from module to the node in that module that should parent any string + * variable declarations that have to be moved into that module + */ + private final Map moduleVarParentMap = + new HashMap(); + + /** package private. This value is AND-ed with the hash function to allow + * unit tests to reduce the range of hash values to test collision cases */ + long unitTestHashReductionMask = ~0L; + + /** + * Creates an instance. + * + * @param compiler The compiler + * @param moduleGraph The module graph, or null if there are no modules + * @param strings Set of strings to be aliased. If null, all strings except + * 'undefined' will be aliased. + * @param blacklistRegex The regex to blacklist words in aliasing strings. + * @param outputStringUsage Outputs all strings and the number of times they + * were used in the application to the server log. + */ + AliasStrings(AbstractCompiler compiler, + JSModuleGraph moduleGraph, + Set strings, + String blacklistRegex, + boolean outputStringUsage) { + this.compiler = compiler; + this.moduleGraph = moduleGraph; + this.aliasableStrings = strings; + if (blacklistRegex.length() != 0) { + this.blacklist = Pattern.compile(blacklistRegex).matcher(""); + } else { + this.blacklist = null; + } + this.outputStringUsage = outputStringUsage; + } + + @Override + public void process(Node externs, Node root) { + logger.fine("Aliasing common strings"); + + // Traverse the tree and collect strings + NodeTraversal.traverse(compiler, root, this); + + // 1st edit pass: replace some strings with aliases + replaceStringsWithAliases(); + + // 2nd edit pass: add variable declarations for aliased strings. + addAliasDeclarationNodes(); + + if (outputStringUsage) { + outputStringUsage(); + } + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isString() && + !parent.isGetProp() && + !parent.isRegExp()) { + + String str = n.getString(); + + // "undefined" is special-cased, since it needs to be used when JS code + // is unloading and therefore variable references aren't available. + // This is because of a bug in Firefox. + if ("undefined".equals(str)) { + return; + } + + if (blacklist != null && blacklist.reset(str).find()) { + return; + } + + if (aliasableStrings == null || aliasableStrings.contains(str)) { + StringOccurrence occurrence = new StringOccurrence(n, parent); + StringInfo info = getOrCreateStringInfo(str); + + info.occurrences.add(occurrence); + info.numOccurrences++; + + if (t.inGlobalScope() || isInThrowExpression(n)) { + info.numOccurrencesInfrequentlyExecuted++; + } + + // The current module. + JSModule module = t.getModule(); + if (info.numOccurrences != 1) { + // Check whether the current module depends on the module containing + // the declaration. + if (module != null && + info.moduleToContainDecl != null && + module != info.moduleToContainDecl && + !moduleGraph.dependsOn(module, info.moduleToContainDecl)) { + // We need to declare this string in the deepest module in the + // module dependency graph that both of these modules depend on. + module = moduleGraph.getDeepestCommonDependency( + module, info.moduleToContainDecl); + } else { + // use the previously saved insertion location. + return; + } + } + Node varParent = moduleVarParentMap.get(module); + if (varParent == null) { + varParent = compiler.getNodeForCodeInsertion(module); + moduleVarParentMap.put(module, varParent); + } + info.moduleToContainDecl = module; + info.parentForNewVarDecl = varParent; + info.siblingToInsertVarDeclBefore = varParent.getFirstChild(); + } + } + } + + /** + * Looks up the {@link StringInfo} object for a JavaScript string. Creates + * it if necessary. + */ + private StringInfo getOrCreateStringInfo(String string) { + StringInfo info = stringInfoMap.get(string); + if (info == null) { + info = new StringInfo(stringInfoMap.size()); + stringInfoMap.put(string, info); + } + return info; + } + + /** + * Is the {@link Node} currently within a 'throw' expression? + */ + private static boolean isInThrowExpression(Node n) { + // Look up the traversal stack to find a THROW node + for (Node ancestor : n.getAncestors()) { + switch (ancestor.getType()) { + case Token.THROW: + return true; + case Token.IF: + case Token.WHILE: + case Token.DO: + case Token.FOR: + case Token.SWITCH: + case Token.CASE: + case Token.DEFAULT_CASE: + case Token.BLOCK: + case Token.SCRIPT: + case Token.FUNCTION: + case Token.TRY: + case Token.CATCH: + case Token.RETURN: + case Token.EXPR_RESULT: + // early exit - these nodes types can't be within a THROW + return false; + } + } + return false; + } + + /** + * Replace strings with references to alias variables. + */ + private void replaceStringsWithAliases() { + for (Entry entry : stringInfoMap.entrySet()) { + String literal = entry.getKey(); + StringInfo info = entry.getValue(); + if (shouldReplaceWithAlias(literal, info)) { + for (StringOccurrence occurrence : info.occurrences) { + replaceStringWithAliasName( + occurrence, info.getVariableName(literal), info); + } + } + } + } + + /** + * Creates a var declaration for each aliased string. Var declarations are + * inserted as close to the first use of the string as possible. + */ + private void addAliasDeclarationNodes() { + for (Entry entry : stringInfoMap.entrySet()) { + StringInfo info = entry.getValue(); + if (!info.isAliased) { + continue; + } + String alias = info.getVariableName(entry.getKey()); + Node var = IR.var(IR.name(alias), IR.string(entry.getKey())); + if (info.siblingToInsertVarDeclBefore == null) { + info.parentForNewVarDecl.addChildToFront(var); + } else { + info.parentForNewVarDecl.addChildBefore( + var, info.siblingToInsertVarDeclBefore); + } + compiler.reportCodeChange(); + } + } + + /** + * Dictates the policy for replacing a string with an alias. + * + * @param str The string literal + * @param info Accumulated information about a string + */ + private static boolean shouldReplaceWithAlias(String str, StringInfo info) { + // Optimize for application performance. If there are any uses of the + // string that are not 'infrequent uses', assume they are frequent and + // create an alias. + if (info.numOccurrences > info.numOccurrencesInfrequentlyExecuted) { + return true; + } + + // Optimize for code size. Are aliases smaller than strings? + // + // This logic optimizes for the size of uncompressed code, but it tends to + // get good results for the size of the gzipped code too. + // + // gzip actually prefers that strings are not aliased - it compresses N + // string literals better than 1 string literal and N+1 short variable + // names, provided each string is within 32k of the previous copy. We + // follow the uncompressed logic as insurance against there being multiple + // strings more than 32k apart. + + int sizeOfLiteral = 2 + str.length(); + int sizeOfStrings = info.numOccurrences * sizeOfLiteral; + int sizeOfVariable = 3; + // '6' comes from: 'var =;' in var XXX="..."; + int sizeOfAliases = 6 + sizeOfVariable + sizeOfLiteral // declaration + + info.numOccurrences * sizeOfVariable; // + uses + + return sizeOfAliases < sizeOfStrings; + } + + /** + * Replaces a string literal with a reference to the string's alias variable. + */ + private void replaceStringWithAliasName(StringOccurrence occurrence, + String name, + StringInfo info) { + occurrence.parent.replaceChild(occurrence.node, + IR.name(name)); + info.isAliased = true; + compiler.reportCodeChange(); + } + + /** + * Outputs a log of all strings used more than once in the code. + */ + private void outputStringUsage() { + StringBuilder sb = new StringBuilder("Strings used more than once:\n"); + for (Entry stringInfoEntry : stringInfoMap.entrySet()) { + StringInfo info = stringInfoEntry.getValue(); + if (info.numOccurrences > 1) { + sb.append(info.numOccurrences); + sb.append(": "); + sb.append(stringInfoEntry.getKey()); + sb.append('\n'); + } + } + // TODO(user): Make this save to file OR output to the application + logger.fine(sb.toString()); + } + + // ------------------------------------------------------------------------- + + /** + * A class that holds the location of a single JavaScript string literal + */ + private static final class StringOccurrence { + final Node node; + final Node parent; + + StringOccurrence(Node node, Node parent) { + this.node = node; + this.parent = parent; + } + } + + /** + * A class that holds information about a JavaScript string that might become + * aliased. + */ + private final class StringInfo { + final int id; + + boolean isAliased; // set to 'true' when reference to alias created + + final List occurrences; + int numOccurrences; + int numOccurrencesInfrequentlyExecuted; + + JSModule moduleToContainDecl; + Node parentForNewVarDecl; + Node siblingToInsertVarDeclBefore; + + String aliasName; + + StringInfo(int id) { + this.id = id; + this.occurrences = new ArrayList(); + this.isAliased = false; + } + + /** Returns the JS variable name to be substituted for this string. */ + String getVariableName(String stringLiteral) { + if (aliasName == null) { + aliasName = + encodeStringAsIdentifier(STRING_ALIAS_PREFIX, stringLiteral); + } + return aliasName; + } + + /** + * Returns a legal identifier that uniquely characterizes string 's'. + * + * We want the identifier to be a function of the string value because that + * makes the identifiers stable as the program is changed. + * + * The digits of a good hash function would be adequate, but for short + * strings the following algorithm is easier to work with for unit tests. + * + * ASCII alphanumerics are mapped to themselves. Other characters are + * mapped to $XXX or $XXX_ where XXX is a variable number of hex digits. + * The underscore is inserted as necessary to avoid ambiguity when the + * character following is a hex digit. E.g. '\n1' maps to '$a_1', + * distinguished by the underscore from '\u00A1' which maps to '$a1'. + * + * If the string is short enough, this is sufficient. Longer strings are + * truncated after encoding an initial prefix and appended with a hash + * value. + */ + String encodeStringAsIdentifier(String prefix, String s) { + // Limit to avoid generating very long identifiers + final int MAX_LIMIT = 20; + final int length = s.length(); + final int limit = Math.min(length, MAX_LIMIT); + + StringBuilder sb = new StringBuilder(); + sb.append(prefix); + boolean protectHex = false; + + for (int i = 0; i < limit; i++) { + char ch = s.charAt(i); + + if (protectHex) { + if ((ch >= '0' && ch <= '9') || + (ch >= 'a' && ch <= 'f')) { // toHexString generate lowercase + sb.append('_'); + } + protectHex = false; + } + + if ((ch >= '0' && ch <= '9') || + (ch >= 'A' && ch <= 'Z') || + (ch >= 'a' && ch <= 'z')) { + sb.append(ch); + } else { + sb.append('$'); + sb.append(Integer.toHexString(ch)); + protectHex = true; + } + } + + if (length == limit) { + return sb.toString(); + } + + // The identifier is not unique because we omitted part, so add a + // checksum as a hashcode. + CRC32 crc32 = new CRC32(); + crc32.update(s.getBytes()); + long hash = crc32.getValue() & unitTestHashReductionMask; + sb.append('_'); + sb.append(Long.toHexString(hash)); + String encoded = sb.toString(); + if (!usedHashedAliases.add(encoded)) { + // A collision has been detected (which is very rare). Use the sequence + // id to break the tie. This means that the name is no longer invariant + // across source code changes and recompilations. + encoded += "_" + id; + } + return encoded; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AmbiguateProperties.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AmbiguateProperties.java new file mode 100644 index 0000000..68c3996 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AmbiguateProperties.java @@ -0,0 +1,641 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.TypeValidator.TypeMismatch; +import com.google.javascript.jscomp.graph.AdjacencyGraph; +import com.google.javascript.jscomp.graph.Annotation; +import com.google.javascript.jscomp.graph.GraphColoring; +import com.google.javascript.jscomp.graph.GraphColoring.GreedyGraphColoring; +import com.google.javascript.jscomp.graph.GraphNode; +import com.google.javascript.jscomp.graph.SubGraph; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; + +import java.util.BitSet; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.logging.Logger; + +/** + * Renames unrelated properties to the same name, using type information. + * This allows better compression as more properties can be given short names. + * + *

Properties are considered unrelated if they are never referenced from the + * same type or from a subtype of each others' types, thus this pass is only + * effective if type checking is enabled. + * + * Example: + * + * Foo.fooprop = 0; + * Foo.fooprop2 = 0; + * Bar.barprop = 0; + * + * + * becomes: + * + * + * Foo.a = 0; + * Foo.b = 0; + * Bar.a = 0; + * + * + */ +class AmbiguateProperties implements CompilerPass { + private static final Logger logger = Logger.getLogger( + AmbiguateProperties.class.getName()); + + private final AbstractCompiler compiler; + + private final List stringNodesToRename = Lists.newArrayList(); + private final char[] reservedCharacters; + + /** Map from property name to Property object */ + private final Map propertyMap = Maps.newHashMap(); + + /** Property names that don't get renamed */ + private final Set externedNames = Sets.newHashSet(); + + /** Names to which properties shouldn't be renamed, to avoid name conflicts */ + private final Set quotedNames = Sets.newHashSet(); + + /** Map from original property name to new name. */ + private final Map renamingMap = Maps.newHashMap(); + + /** + * Sorts Property objects by their count, breaking ties alphabetically to + * ensure a deterministic total ordering. + */ + private static final Comparator FREQUENCY_COMPARATOR = + new Comparator() { + @Override + public int compare(Property p1, Property p2) { + if (p1.numOccurrences != p2.numOccurrences) { + return p2.numOccurrences - p1.numOccurrences; + } + return p1.oldName.compareTo(p2.oldName); + } + }; + + /** A map from JSType to a unique representative Integer. */ + private BiMap intForType = HashBiMap.create(); + + /** + * A map from JSType to JSTypeBitSet representing the types related + * to the type. + */ + private Map relatedBitsets = Maps.newHashMap(); + + /** A set of types that invalidate properties from ambiguation. */ + private final Set invalidatingTypes; + + /** + * Prefix of properties to skip renaming. These should be renamed in the + * RenameProperties pass. + */ + static final String SKIP_PREFIX = "JSAbstractCompiler"; + + AmbiguateProperties(AbstractCompiler compiler, + char[] reservedCharacters) { + Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); + this.compiler = compiler; + this.reservedCharacters = reservedCharacters; + + JSTypeRegistry r = compiler.getTypeRegistry(); + invalidatingTypes = Sets.newHashSet( + r.getNativeType(JSTypeNative.ALL_TYPE), + r.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + r.getNativeType(JSTypeNative.NO_TYPE), + r.getNativeType(JSTypeNative.NULL_TYPE), + r.getNativeType(JSTypeNative.VOID_TYPE), + r.getNativeType(JSTypeNative.FUNCTION_FUNCTION_TYPE), + r.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), + r.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE), + r.getNativeType(JSTypeNative.GLOBAL_THIS), + r.getNativeType(JSTypeNative.OBJECT_TYPE), + r.getNativeType(JSTypeNative.OBJECT_PROTOTYPE), + r.getNativeType(JSTypeNative.OBJECT_FUNCTION_TYPE), + r.getNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE), + r.getNativeType(JSTypeNative.UNKNOWN_TYPE)); + + for (TypeMismatch mis : compiler.getTypeValidator().getMismatches()) { + addInvalidatingType(mis.typeA); + addInvalidatingType(mis.typeB); + } + } + + /** + * Invalidates the given type, so that no properties on it will be renamed. + */ + private void addInvalidatingType(JSType type) { + type = type.restrictByNotNullOrUndefined(); + if (type.isUnionType()) { + for (JSType alt : type.toMaybeUnionType().getAlternates()) { + addInvalidatingType(alt); + } + } + + invalidatingTypes.add(type); + ObjectType objType = ObjectType.cast(type); + if (objType != null && objType.isInstanceType()) { + invalidatingTypes.add(objType.getImplicitPrototype()); + } + } + + Map getRenamingMap() { + return renamingMap; + } + + /** Returns an integer that uniquely identifies a JSType. */ + private int getIntForType(JSType type) { + if (intForType.containsKey(type)) { + return intForType.get(type).intValue(); + } + int newInt = intForType.size() + 1; + intForType.put(type, newInt); + return newInt; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, externs, new ProcessExterns()); + NodeTraversal.traverse(compiler, root, new ProcessProperties()); + + Set reservedNames = + new HashSet(externedNames.size() + quotedNames.size()); + reservedNames.addAll(externedNames); + reservedNames.addAll(quotedNames); + + int numRenamedPropertyNames = 0; + int numSkippedPropertyNames = 0; + Set propsByFreq = new TreeSet(FREQUENCY_COMPARATOR); + for (Property p : propertyMap.values()) { + if (!p.skipAmbiguating) { + ++numRenamedPropertyNames; + propsByFreq.add(p); + } else { + ++numSkippedPropertyNames; + reservedNames.add(p.oldName); + } + } + + PropertyGraph graph = new PropertyGraph(Lists.newLinkedList(propsByFreq)); + GraphColoring coloring = + new GreedyGraphColoring(graph, FREQUENCY_COMPARATOR); + int numNewPropertyNames = coloring.color(); + + NameGenerator nameGen = new NameGenerator( + reservedNames, "", reservedCharacters); + Map colorMap = Maps.newHashMap(); + for (int i = 0; i < numNewPropertyNames; ++i) { + colorMap.put(i, nameGen.generateNextName()); + } + for (GraphNode node : graph.getNodes()) { + node.getValue().newName = colorMap.get(node.getAnnotation().hashCode()); + renamingMap.put(node.getValue().oldName, node.getValue().newName); + } + + // Update the string nodes. + for (Node n : stringNodesToRename) { + String oldName = n.getString(); + Property p = propertyMap.get(oldName); + if (p != null && p.newName != null) { + Preconditions.checkState(oldName.equals(p.oldName)); + if (!p.newName.equals(oldName)) { + n.setString(p.newName); + compiler.reportCodeChange(); + } + } + } + + logger.fine("Collapsed " + numRenamedPropertyNames + " properties into " + + numNewPropertyNames + " and skipped renaming " + + numSkippedPropertyNames + " properties."); + } + + private BitSet getRelatedTypesOnNonUnion(JSType type) { + // All of the types we encounter should have been added to the + // relatedBitsets via computeRelatedTypes. + if (relatedBitsets.containsKey(type)) { + return relatedBitsets.get(type); + } else { + throw new RuntimeException("Related types should have been computed for" + + " type: " + type + " but have not been."); + } + } + + /** + * Adds subtypes - and implementors, in the case of interfaces - of the type + * to its JSTypeBitSet of related types. Union types are decomposed into their + * alternative types. + * + *

The 'is related to' relationship is best understood graphically. Draw an + * arrow from each instance type to the prototype of each of its + * subclass. Draw an arrow from each prototype to its instance type. Draw an + * arrow from each interface to its implementors. A type is related to another + * if there is a directed path in the graph from the type to other. Thus, the + * 'is related to' relationship is reflexive and transitive. + * + *

Example with Foo extends Bar which extends Baz and Bar implements I: + *

+   * Foo -> Bar.prototype -> Bar -> Baz.prototype -> Baz
+   *                          ^
+   *                          |
+   *                          I
+   * 
+ * + *

Note that we don't need to correctly handle the relationships between + * functions, because the function type is invalidating (i.e. its properties + * won't be ambiguated). + */ + private void computeRelatedTypes(JSType type) { + if (type.isUnionType()) { + type = type.restrictByNotNullOrUndefined(); + if (type.isUnionType()) { + for (JSType alt : type.toMaybeUnionType().getAlternates()) { + computeRelatedTypes(alt); + } + return; + } + } + + if (relatedBitsets.containsKey(type)) { + // We only need to generate the bit set once. + return; + } + + JSTypeBitSet related = new JSTypeBitSet(intForType.size()); + relatedBitsets.put(type, related); + related.set(getIntForType(type)); + + // A prototype is related to its instance. + if (type.isFunctionPrototypeType()) { + addRelatedInstance(((ObjectType) type).getOwnerFunction(), related); + return; + } + + // An instance is related to its subclasses. + FunctionType constructor = type.toObjectType().getConstructor(); + if (constructor != null && constructor.getSubTypes() != null) { + for (FunctionType subType : constructor.getSubTypes()) { + addRelatedInstance(subType, related); + } + } + + // An interface is related to its implementors. + for (FunctionType implementor : compiler.getTypeRegistry() + .getDirectImplementors(type.toObjectType())) { + addRelatedInstance(implementor, related); + } + } + + /** + * Adds the instance of the given constructor, its implicit prototype and all + * its related types to the given bit set. + */ + private void addRelatedInstance( + FunctionType constructor, JSTypeBitSet related) { + // TODO(user): A constructor which doesn't have an instance type + // (e.g. it's missing the @constructor annotation) should be an invalidating + // type which doesn't reach this code path. + if (constructor.hasInstanceType()) { + ObjectType instanceType = constructor.getInstanceType(); + related.set(getIntForType(instanceType.getImplicitPrototype())); + computeRelatedTypes(instanceType); + related.or(relatedBitsets.get(instanceType)); + } + } + + class PropertyGraph implements AdjacencyGraph { + protected final Map nodes = Maps.newHashMap(); + + PropertyGraph(Collection props) { + for (Property prop : props) { + nodes.put(prop, new PropertyGraphNode(prop)); + } + } + + @Override + public List> getNodes() { + return Lists.>newArrayList(nodes.values()); + } + + @Override + public GraphNode getNode(Property property) { + return nodes.get(property); + } + + @Override + public SubGraph newSubGraph() { + return new PropertySubGraph(); + } + + @Override + public void clearNodeAnnotations() { + for (PropertyGraphNode node : nodes.values()) { + node.setAnnotation(null); + } + } + + @Override + public int getWeight(Property value) { + return value.numOccurrences; + } + } + + /** + * A {@link SubGraph} that represents properties. The related types of + * the properties are used to efficiently calculate adjacency information. + */ + class PropertySubGraph implements SubGraph { + /** Types related to properties referenced in this subgraph. */ + JSTypeBitSet relatedTypes = new JSTypeBitSet(intForType.size()); + + /** + * Returns true if prop is in an independent set from all properties in this + * sub graph. That is, if none of its related types intersects with the + * related types for this sub graph. + */ + @Override + public boolean isIndependentOf(Property prop) { + return !relatedTypes.intersects(prop.relatedTypes); + } + + /** + * Adds the node to the sub graph, adding all its related types to the + * related types for the sub graph. + */ + @Override + public void addNode(Property prop) { + relatedTypes.or(prop.relatedTypes); + } + } + + class PropertyGraphNode implements GraphNode { + Property property; + protected Annotation annotation; + + PropertyGraphNode(Property property) { + this.property = property; + } + + @Override + public Property getValue() { + return property; + } + + @Override + @SuppressWarnings("unchecked") + public A getAnnotation() { + return (A) annotation; + } + + @Override + public void setAnnotation(Annotation data) { + annotation = data; + } + } + + /** A traversal callback that collects externed property names. */ + private class ProcessExterns extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.GETPROP: + Node dest = n.getFirstChild().getNext(); + externedNames.add(dest.getString()); + break; + case Token.OBJECTLIT: + for (Node child = n.getFirstChild(); + child != null; + child = child.getNext()) { + // names: STRING, GET, SET + externedNames.add(child.getString()); + } + break; + } + } + } + + /** Finds all property references, recording the types on which they occur. */ + private class ProcessProperties extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.GETPROP: { + Node propNode = n.getFirstChild().getNext(); + JSType jstype = getJSType(n.getFirstChild()); + maybeMarkCandidate(propNode, jstype, t); + break; + } + case Token.OBJECTLIT: + // The children of an OBJECTLIT node are keys, where the values + // are the children of the keys. + for (Node key = n.getFirstChild(); key != null; + key = key.getNext()) { + // We only want keys that were unquoted. + // Keys are STRING, GET, SET + if (!key.isQuotedString()) { + JSType jstype = getJSType(n.getFirstChild()); + maybeMarkCandidate(key, jstype, t); + } else { + // Ensure that we never rename some other property in a way + // that could conflict with this quoted key. + quotedNames.add(key.getString()); + } + } + break; + case Token.GETELEM: + // If this is a quoted property access (e.g. x['myprop']), we need to + // ensure that we never rename some other property in a way that + // could conflict with this quoted name. + Node child = n.getLastChild(); + if (child.isString()) { + quotedNames.add(child.getString()); + } + break; + } + } + + /** + * If a property node is eligible for renaming, stashes a reference to it + * and increments the property name's access count. + * + * @param n The STRING node for a property + * @param t The traversal + */ + private void maybeMarkCandidate(Node n, JSType type, NodeTraversal t) { + String name = n.getString(); + if (!externedNames.contains(name)) { + stringNodesToRename.add(n); + recordProperty(name, type); + } + } + + private Property recordProperty(String name, JSType type) { + Property prop = getProperty(name); + prop.addType(type); + return prop; + } + } + + /** Returns true if properties on this type should not be renamed. */ + private boolean isInvalidatingType(JSType type) { + if (type.isUnionType()) { + type = type.restrictByNotNullOrUndefined(); + if (type.isUnionType()) { + for (JSType alt : type.toMaybeUnionType().getAlternates()) { + if (isInvalidatingType(alt)) { + return true; + } + } + return false; + } + } + ObjectType objType = ObjectType.cast(type); + return objType == null + || invalidatingTypes.contains(objType) + || !objType.hasReferenceName() + || objType.isUnknownType() + || objType.isEmptyType() /* unresolved types */ + || objType.isEnumType() + || objType.autoboxesTo() != null; + } + + private Property getProperty(String name) { + Property prop = propertyMap.get(name); + if (prop == null) { + prop = new Property(name); + propertyMap.put(name, prop); + } + return prop; + } + + /** + * This method gets the JSType from the Node argument and verifies that it is + * present. + */ + private JSType getJSType(Node n) { + JSType jsType = n.getJSType(); + if (jsType == null) { + // TODO(user): This branch indicates a compiler bug, not worthy of + // halting the compilation but we should log this and analyze to track + // down why it happens. This is not critical and will be resolved over + // time as the type checker is extended. + return compiler.getTypeRegistry().getNativeType( + JSTypeNative.UNKNOWN_TYPE); + } else { + return jsType; + } + } + + /** Encapsulates the information needed for renaming a property. */ + private class Property { + final String oldName; + String newName; + int numOccurrences; + boolean skipAmbiguating; + JSTypeBitSet relatedTypes = new JSTypeBitSet(intForType.size()); + + Property(String name) { + this.oldName = name; + + // Properties with this suffix are handled in RenameProperties. + if (name.startsWith(SKIP_PREFIX)) { + skipAmbiguating = true; + } + } + + /** Add this type to this property, calculating */ + void addType(JSType newType) { + if (skipAmbiguating) { + return; + } + + ++numOccurrences; + + if (newType.isUnionType()) { + newType = newType.restrictByNotNullOrUndefined(); + if (newType.isUnionType()) { + for (JSType alt : newType.toMaybeUnionType().getAlternates()) { + addNonUnionType(alt); + } + return; + } + } + addNonUnionType(newType); + } + + private void addNonUnionType(JSType newType) { + if (skipAmbiguating || isInvalidatingType(newType)) { + skipAmbiguating = true; + return; + } + + if (!relatedTypes.get(getIntForType(newType))) { + computeRelatedTypes(newType); + relatedTypes.or(getRelatedTypesOnNonUnion(newType)); + } + } + } + + // A BitSet that stores type info. Adds pretty-print routines. + private class JSTypeBitSet extends BitSet { + private static final long serialVersionUID = 1L; + + private JSTypeBitSet(int size) { + super(size); + } + + private JSTypeBitSet() { + super(); + } + + /** + * Pretty-printing, for diagnostic purposes. + */ + @Override + public String toString() { + int from = 0; + int current = 0; + List types = Lists.newArrayList(); + while (-1 != (current = nextSetBit(from))) { + types.add(intForType.inverse().get(current).toString()); + from = current + 1; + } + return Joiner.on(" && ").join(types); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AnalyzeNameReferences.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AnalyzeNameReferences.java new file mode 100644 index 0000000..eb9cd11 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AnalyzeNameReferences.java @@ -0,0 +1,145 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NameReferenceGraph.Name; +import com.google.javascript.jscomp.NameReferenceGraph.Reference; +import com.google.javascript.jscomp.graph.FixedPointGraphTraversal; +import com.google.javascript.jscomp.graph.Annotation; +import com.google.javascript.jscomp.graph.GraphNode; +import com.google.javascript.jscomp.graph.FixedPointGraphTraversal.EdgeCallback; +import com.google.javascript.rhino.Node; + +/** + * Analyzes names and references usage by determining: + *

    + *
  1. If the name is reachable from the {@link NameReferenceGraph#MAIN}.
  2. + *
  3. as well as the deepest common module that references it.
  4. + *
+ * + * The two pieces of information will be annotated to {@link NameReferenceGraph} + * by {@link NameInfo} objects. + * + * This is an analysis based on {@link AnalyzeNameReferences} using the more + * accurate graph and will soon replace it. + * + */ +class AnalyzeNameReferences implements CompilerPass { + + private NameReferenceGraph graph; + private final JSModuleGraph moduleGraph; + private final AbstractCompiler compiler; + + AnalyzeNameReferences(AbstractCompiler compiler) { + this.compiler = compiler; + this.moduleGraph = compiler.getModuleGraph(); + } + + @Override + public void process(Node externs, Node root) { + NameReferenceGraphConstruction gc = + new NameReferenceGraphConstruction(compiler); + gc.process(externs, root); + graph = gc.getNameReferenceGraph(); + FixedPointGraphTraversal t = + FixedPointGraphTraversal.newTraversal(new PropagateReferences()); + getInfo(graph.MAIN).markReference(null); + t.computeFixedPoint(graph, Sets.newHashSet(graph.MAIN)); + } + + public NameReferenceGraph getGraph() { + return graph; + } + + private class PropagateReferences implements EdgeCallback { + @Override + public boolean traverseEdge(Name start, Reference edge, Name dest) { + NameInfo startInfo = getInfo(start); + NameInfo destInfo = getInfo(dest); + if (startInfo.isReferenced()) { + JSModule startModule = startInfo.getDeepestCommonModuleRef(); + if (startModule != null && + moduleGraph.dependsOn(startModule, edge.getModule())) { + return destInfo.markReference(startModule); + } else { + return destInfo.markReference(edge.getModule()); + } + } + return false; + } + } + + private NameInfo getInfo(Name symbol) { + GraphNode name = graph.getNode(symbol); + NameInfo info = name.getAnnotation(); + if (info == null) { + info = new NameInfo(); + name.setAnnotation(info); + } + return info; + } + + final class NameInfo implements Annotation { + private boolean referenced = false; + private JSModule deepestCommonModuleRef = null; + + /** Determines whether we've marked a reference to this property name. */ + boolean isReferenced() { + return referenced; + } + + /** + * Returns the deepest common module of all the references to this + * property. + */ + JSModule getDeepestCommonModuleRef() { + return deepestCommonModuleRef; + } + + /** + * Mark a reference in a given module to this property name, and record + * the deepest common module reference. + * @param module The module where it was referenced. + * @return Whether the name info has changed. + */ + boolean markReference(JSModule module) { + boolean hasChanged = false; + if (!referenced) { + referenced = true; + hasChanged = true; + } + + if (moduleGraph != null) { + JSModule originalDeepestCommon = deepestCommonModuleRef; + + if (deepestCommonModuleRef == null) { + deepestCommonModuleRef = module; + } else { + deepestCommonModuleRef = + moduleGraph.getDeepestCommonDependencyInclusive( + deepestCommonModuleRef, module); + } + + if (originalDeepestCommon != deepestCommonModuleRef) { + hasChanged = true; + } + } + return hasChanged; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AnalyzePrototypeProperties.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AnalyzePrototypeProperties.java new file mode 100644 index 0000000..bc570e2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AnalyzePrototypeProperties.java @@ -0,0 +1,831 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.graph.FixedPointGraphTraversal; +import com.google.javascript.jscomp.graph.LinkedDirectedGraph; +import com.google.javascript.jscomp.graph.FixedPointGraphTraversal.EdgeCallback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Deque; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +/** + * Analyzes properties on prototypes. + * + * Uses a reference graph to analyze prototype properties. Each unique property + * name is represented by a node in this graph. An edge from property A to + * property B means that there's a GETPROP access of a property B on some + * object inside of a method named A. + * + * Global functions are also represented by nodes in this graph, with + * similar semantics. + * + */ +class AnalyzePrototypeProperties implements CompilerPass { + + // Constants for symbol types, for easier readability. + private final SymbolType PROPERTY = SymbolType.PROPERTY; + private final SymbolType VAR = SymbolType.VAR; + + private final AbstractCompiler compiler; + private final boolean canModifyExterns; + private final boolean anchorUnusedVars; + private final JSModuleGraph moduleGraph; + private final JSModule firstModule; + + // Properties that are implicitly used as part of the JS language. + private static final Set IMPLICITLY_USED_PROPERTIES = + ImmutableSet.of("length", "toString", "valueOf"); + + // A graph where the nodes are property names or variable names, + // and the edges signify the modules where the property is referenced. + // For example, if we had the code: + // + // Foo.prototype.bar = function(x) { x.baz(); }; // in module 2.; + // + // then this would be represented in the graph by a node representing + // "bar", a node representing "baz", and an edge between them representing + // module #2. + // + // Similarly, if we had: + // + // var scotch = function(f) { return f.age(); }; + // + // then there would be a node for "scotch", a node for "age", and an edge + // from scotch to age. + private final LinkedDirectedGraph symbolGraph = + LinkedDirectedGraph.createWithoutAnnotations(); + + // A dummy node for representing global references. + private final NameInfo globalNode = new NameInfo("[global]"); + + // A dummy node for representing extern references. + private final NameInfo externNode = new NameInfo("[extern]"); + + // A dummy node for representing all anonymous functions with no names. + private final NameInfo anonymousNode = new NameInfo("[anonymous]"); + + // All the real NameInfo for prototype properties, hashed by the name + // of the property that they represent. + private final Map propertyNameInfo = Maps.newHashMap(); + + // All the NameInfo for global functions, hashed by the name of the + // global variable that it's assigned to. + private final Map varNameInfo = Maps.newHashMap(); + + /** + * Creates a new pass for analyzing prototype properties. + * @param compiler The compiler. + * @param moduleGraph The graph for resolving module dependencies. May be + * null if we don't care about module dependencies. + * @param canModifyExterns If true, then we can move prototype + * properties that are declared in the externs file. + * @param anchorUnusedVars If true, then we must mark all vars as referenced, + * even if they are never used. + */ + AnalyzePrototypeProperties(AbstractCompiler compiler, + JSModuleGraph moduleGraph, boolean canModifyExterns, + boolean anchorUnusedVars) { + this.compiler = compiler; + this.moduleGraph = moduleGraph; + this.canModifyExterns = canModifyExterns; + this.anchorUnusedVars = anchorUnusedVars; + + if (moduleGraph != null) { + firstModule = moduleGraph.getRootModule(); + } else { + firstModule = null; + } + + globalNode.markReference(null); + externNode.markReference(null); + symbolGraph.createNode(globalNode); + symbolGraph.createNode(externNode); + + for (String property : IMPLICITLY_USED_PROPERTIES) { + NameInfo nameInfo = getNameInfoForName(property, PROPERTY); + if (moduleGraph == null) { + symbolGraph.connect(externNode, null, nameInfo); + } else { + for (JSModule module : moduleGraph.getAllModules()) { + symbolGraph.connect(externNode, module, nameInfo); + } + } + } + } + + @Override + public void process(Node externRoot, Node root) { + if (!canModifyExterns) { + NodeTraversal.traverse(compiler, externRoot, + new ProcessExternProperties()); + } + + NodeTraversal.traverse(compiler, root, new ProcessProperties()); + + FixedPointGraphTraversal t = + FixedPointGraphTraversal.newTraversal(new PropagateReferences()); + t.computeFixedPoint(symbolGraph, + Sets.newHashSet(externNode, globalNode)); + } + + /** + * Returns information on all prototype properties. + */ + public Collection getAllNameInfo() { + List result = Lists.newArrayList(propertyNameInfo.values()); + result.addAll(varNameInfo.values()); + return result; + } + + /** + * Gets the name info for the property or variable of a given name, + * and creates a new one if necessary. + * + * @param name The name of the symbol. + * @param type The type of symbol. + */ + private NameInfo getNameInfoForName(String name, SymbolType type) { + Map map = type == PROPERTY ? + propertyNameInfo : varNameInfo; + if (map.containsKey(name)) { + return map.get(name); + } else { + NameInfo nameInfo = new NameInfo(name); + map.put(name, nameInfo); + symbolGraph.createNode(nameInfo); + return nameInfo; + } + } + + private class ProcessProperties implements NodeTraversal.ScopedCallback { + // There are two types of context information on this stack: + // 1) Every scope has a NameContext corresponding to its scope. + // Variables are given VAR contexts. + // Prototype properties are given PROPERTY contexts. + // The global scope is given the special [global] context. + // And function expressions that we aren't able to give a reasonable + // name are given a special [anonymous] context. + // 2) Every assignment of a prototype property of a non-function is + // given a name context. These contexts do not have scopes. + private Stack symbolStack = new Stack(); + + @Override + public void enterScope(NodeTraversal t) { + Node n = t.getCurrentNode(); + if (n.isFunction()) { + String propName = getPrototypePropertyNameFromRValue(n); + if (propName != null) { + symbolStack.push( + new NameContext( + getNameInfoForName(propName, PROPERTY), + t.getScope())); + } else if (isGlobalFunctionDeclaration(t, n)) { + Node parent = n.getParent(); + String name = parent.isName() ? + parent.getString() /* VAR */ : + n.getFirstChild().getString() /* named function */; + symbolStack.push( + new NameContext(getNameInfoForName(name, VAR), t.getScope())); + } else { + // NOTE(nicksantos): We use the same anonymous node for all + // functions that do not have reasonable names. I can't remember + // at the moment why we do this. I think it's because anonymous + // nodes can never have in-edges. They're just there as a placeholder + // for scope information, and do not matter in the edge propagation. + symbolStack.push(new NameContext(anonymousNode, t.getScope())); + } + } else { + Preconditions.checkState(t.inGlobalScope()); + symbolStack.push(new NameContext(globalNode, t.getScope())); + } + } + + @Override + public void exitScope(NodeTraversal t) { + symbolStack.pop(); + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + // Process prototype assignments to non-functions. + String propName = processNonFunctionPrototypeAssign(n, parent); + if (propName != null) { + symbolStack.push( + new NameContext( + getNameInfoForName(propName, PROPERTY), null)); + } + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isGetProp()) { + String propName = n.getFirstChild().getNext().getString(); + + if (n.isQualifiedName()) { + if (propName.equals("prototype")) { + if (processPrototypeRef(t, n)) { + return; + } + } else if (compiler.getCodingConvention().isExported(propName)) { + addGlobalUseOfSymbol(propName, t.getModule(), PROPERTY); + return; + } else { + // Do not mark prototype prop assigns as a 'use' in the global scope. + if (n.getParent().isAssign() && n.getNext() != null) { + String rValueName = getPrototypePropertyNameFromRValue(n); + if (rValueName != null) { + return; + } + } + } + } + + addSymbolUse(propName, t.getModule(), PROPERTY); + } else if (n.isObjectLit()) { + // Make sure that we're not handling object literals being + // assigned to a prototype, as in: + // Foo.prototype = {bar: 3, baz: 5}; + String lValueName = NodeUtil.getBestLValueName( + NodeUtil.getBestLValue(n)); + if (lValueName != null && lValueName.endsWith(".prototype")) { + return; + } + + // var x = {a: 1, b: 2} + // should count as a use of property a and b. + for (Node propNameNode = n.getFirstChild(); propNameNode != null; + propNameNode = propNameNode.getNext()) { + // May be STRING, GET, or SET, but NUMBER isn't interesting. + if (!propNameNode.isQuotedString()) { + addSymbolUse(propNameNode.getString(), t.getModule(), PROPERTY); + } + } + } else if (n.isName()) { + String name = n.getString(); + + Var var = t.getScope().getVar(name); + if (var != null) { + // Only process global functions. + if (var.isGlobal()) { + if (var.getInitialValue() != null && + var.getInitialValue().isFunction()) { + if (t.inGlobalScope()) { + if (!processGlobalFunctionDeclaration(t, n, var)) { + addGlobalUseOfSymbol(name, t.getModule(), VAR); + } + } else { + addSymbolUse(name, t.getModule(), VAR); + } + } + + // If it is not a global, it might be accessing a local of the outer + // scope. If that's the case the functions between the variable's + // declaring scope and the variable reference scope cannot be moved. + } else if (var.getScope() != t.getScope()){ + for (int i = symbolStack.size() - 1; i >= 0; i--) { + NameContext context = symbolStack.get(i); + if (context.scope == var.getScope()) { + break; + } + + context.name.readClosureVariables = true; + } + } + } + } + + // Process prototype assignments to non-functions. + if (processNonFunctionPrototypeAssign(n, parent) != null) { + symbolStack.pop(); + } + } + + private void addSymbolUse(String name, JSModule module, SymbolType type) { + NameInfo info = getNameInfoForName(name, type); + NameInfo def = null; + // Skip all anonymous nodes. We care only about symbols with names. + for (int i = symbolStack.size() - 1; i >= 0; i--) { + def = symbolStack.get(i).name; + if (def != anonymousNode) { + break; + } + } + if (!def.equals(info)) { + symbolGraph.connect(def, module, info); + } + } + + /** + * If this is a non-function prototype assign, return the prop name. + * Otherwise, return null. + */ + private String processNonFunctionPrototypeAssign(Node n, Node parent) { + if (isAssignRValue(n, parent) && !n.isFunction()) { + return getPrototypePropertyNameFromRValue(n); + } + return null; + } + + /** + * Determines whether {@code n} is the FUNCTION node in a global function + * declaration. + */ + private boolean isGlobalFunctionDeclaration(NodeTraversal t, Node n) { + // Make sure we're either in the global scope, or the function + // we're looking at is the root of the current local scope. + Scope s = t.getScope(); + if (!(s.isGlobal() || + s.getDepth() == 1 && s.getRootNode() == n)) { + return false; + } + + return NodeUtil.isFunctionDeclaration(n) || + n.isFunction() && n.getParent().isName(); + } + + /** + * Returns true if this is the r-value of an assignment. + */ + private boolean isAssignRValue(Node n, Node parent) { + return parent != null && parent.isAssign() && parent.getFirstChild() != n; + } + + /** + * Returns the name of a prototype property being assigned to this r-value. + * + * Returns null if this is not the R-value of a prototype property, or if + * the R-value is used in multiple expressions (i.e., if there's + * a prototype property assignment in a more complex expression). + */ + private String getPrototypePropertyNameFromRValue(Node rValue) { + Node lValue = NodeUtil.getBestLValue(rValue); + if (lValue == null || + lValue.getParent() == null || + lValue.getParent().getParent() == null || + !(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) || + NodeUtil.isExprAssign(lValue.getParent().getParent()))) { + return null; + } + + String lValueName = + NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue)); + if (lValueName == null) { + return null; + } + int lastDot = lValueName.lastIndexOf('.'); + if (lastDot == -1) { + return null; + } + + String firstPart = lValueName.substring(0, lastDot); + if (!firstPart.endsWith(".prototype")) { + return null; + } + + return lValueName.substring(lastDot + 1); + } + + /** + * Processes a NAME node to see if it's a global function declaration. + * If it is, record it and return true. Otherwise, return false. + */ + private boolean processGlobalFunctionDeclaration(NodeTraversal t, + Node nameNode, Var v) { + Node firstChild = nameNode.getFirstChild(); + Node parent = nameNode.getParent(); + + if (// Check for a named FUNCTION. + isGlobalFunctionDeclaration(t, parent) || + // Check for a VAR declaration. + firstChild != null && + isGlobalFunctionDeclaration(t, firstChild)) { + String name = nameNode.getString(); + getNameInfoForName(name, VAR).getDeclarations().add( + new GlobalFunction(nameNode, v, t.getModule())); + + // If the function name is exported, we should create an edge here + // so that it's never removed. + if (compiler.getCodingConvention().isExported(name) || + anchorUnusedVars) { + addGlobalUseOfSymbol(name, t.getModule(), VAR); + } + + return true; + } + return false; + } + + /** + * Processes the GETPROP of prototype, which can either be under + * another GETPROP (in the case of Foo.prototype.bar), or can be + * under an assignment (in the case of Foo.prototype = ...). + * @return True if a declaration was added. + */ + private boolean processPrototypeRef(NodeTraversal t, Node ref) { + Node root = NodeUtil.getRootOfQualifiedName(ref); + + Node n = ref.getParent(); + switch (n.getType()) { + // Foo.prototype.getBar = function() { ... } + case Token.GETPROP: + Node dest = n.getFirstChild().getNext(); + Node parent = n.getParent(); + Node grandParent = parent.getParent(); + + if (dest.isString() && + NodeUtil.isExprAssign(grandParent) && + NodeUtil.isVarOrSimpleAssignLhs(n, parent)) { + String name = dest.getString(); + Property prop = new AssignmentProperty( + grandParent, + maybeGetVar(t, root), + t.getModule()); + getNameInfoForName(name, PROPERTY).getDeclarations().add(prop); + return true; + } + break; + + // Foo.prototype = { "getBar" : function() { ... } } + case Token.ASSIGN: + Node map = n.getFirstChild().getNext(); + if (map.isObjectLit()) { + for (Node key = map.getFirstChild(); + key != null; key = key.getNext()) { + // May be STRING, GETTER_DEF, or SETTER_DEF, + String name = key.getString(); + Property prop = new LiteralProperty( + key, key.getFirstChild(), map, n, + maybeGetVar(t, root), + t.getModule()); + getNameInfoForName(name, PROPERTY).getDeclarations().add(prop); + } + return true; + } + break; + } + return false; + } + + private Var maybeGetVar(NodeTraversal t, Node maybeName) { + return maybeName.isName() + ? t.getScope().getVar(maybeName.getString()) : null; + } + + private void addGlobalUseOfSymbol(String name, JSModule module, + SymbolType type) { + symbolGraph.connect(globalNode, module, getNameInfoForName(name, type)); + } + } + + private class ProcessExternProperties extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isGetProp()) { + symbolGraph.connect(externNode, firstModule, + getNameInfoForName(n.getLastChild().getString(), PROPERTY)); + } + } + } + + private class PropagateReferences + implements EdgeCallback { + @Override + public boolean traverseEdge(NameInfo start, JSModule edge, NameInfo dest) { + if (start.isReferenced()) { + JSModule startModule = start.getDeepestCommonModuleRef(); + if (startModule != null && + moduleGraph.dependsOn(startModule, edge)) { + return dest.markReference(startModule); + } else { + return dest.markReference(edge); + } + } + return false; + } + } + + // TODO(user): We can use DefinitionsRemover and UseSite here. Then all + // we need to do is call getDefinition() and we'll magically know everything + // about the definition. + + /** + * The declaration of an abstract symbol. + */ + interface Symbol { + /** + * Remove the declaration from the AST. + */ + void remove(); + + /** + * The variable for the root of this symbol. + */ + Var getRootVar(); + + /** + * Returns the module where this appears. + */ + JSModule getModule(); + } + + private enum SymbolType { + PROPERTY, + VAR; + } + + /** + * A function initialized as a VAR statement or a function declaration. + */ + class GlobalFunction implements Symbol { + private final Node nameNode; + private final Var var; + private final JSModule module; + + GlobalFunction(Node nameNode, Var var, JSModule module) { + Node parent = nameNode.getParent(); + Preconditions.checkState( + parent.isVar() || + NodeUtil.isFunctionDeclaration(parent)); + this.nameNode = nameNode; + this.var = var; + this.module = module; + } + + @Override + public Var getRootVar() { + return var; + } + + @Override + public void remove() { + Node parent = nameNode.getParent(); + if (parent.isFunction() || parent.hasOneChild()) { + NodeUtil.removeChild(parent.getParent(), parent); + } else { + Preconditions.checkState(parent.isVar()); + parent.removeChild(nameNode); + } + } + + @Override + public JSModule getModule() { + return module; + } + + public Node getFunctionNode() { + Node parent = nameNode.getParent(); + + if (parent.isFunction()) { + return parent; + } else { + // we are the name of a var node, so the function is name's second child + return nameNode.getChildAtIndex(1); + } + } + } + + /** + * Since there are two ways of assigning properties to prototypes, we hide + * then behind this interface so they can both be removed regardless of type. + */ + interface Property extends Symbol { + + /** Returns the GETPROP node that refers to the prototype. */ + Node getPrototype(); + + /** Returns the value of this property. */ + Node getValue(); + } + + /** + * Properties created via EXPR assignment: + * + *
function Foo() { ... };
+   * Foo.prototype.bar = function() { ... };
+ */ + static class AssignmentProperty implements Property { + private final Node exprNode; + private final Var rootVar; + private final JSModule module; + + /** + * @param node An EXPR node. + */ + AssignmentProperty(Node node, Var rootVar, JSModule module) { + this.exprNode = node; + this.rootVar = rootVar; + this.module = module; + } + + @Override + public Var getRootVar() { + return rootVar; + } + + @Override + public void remove() { + NodeUtil.removeChild(exprNode.getParent(), exprNode); + } + + @Override + public Node getPrototype() { + return getAssignNode().getFirstChild().getFirstChild(); + } + + @Override + public Node getValue() { + return getAssignNode().getLastChild(); + } + + private Node getAssignNode() { + return exprNode.getFirstChild(); + } + + @Override + public JSModule getModule() { + return module; + } + } + + /** + * Properties created via object literals: + * + *
function Foo() { ... };
+   * Foo.prototype = {bar: function() { ... };
+ */ + static class LiteralProperty implements Property { + private final Node key; + private final Node value; + private final Node map; + private final Node assign; + private final Var rootVar; + private final JSModule module; + + LiteralProperty(Node key, Node value, Node map, Node assign, + Var rootVar, JSModule module) { + this.key = key; + this.value = value; + this.map = map; + this.assign = assign; + this.rootVar = rootVar; + this.module = module; + } + + @Override + public Var getRootVar() { + return rootVar; + } + + @Override + public void remove() { + map.removeChild(key); + } + + @Override + public Node getPrototype() { + return assign.getFirstChild(); + } + + @Override + public Node getValue() { + return value; + } + + @Override + public JSModule getModule() { + return module; + } + } + + /** + * The context of the current name. This includes the NameInfo and the scope + * if it is a scope defining name (function). + */ + private class NameContext { + final NameInfo name; + + // If this is a function context, then scope will be the scope of the + // corresponding function. Otherwise, it will be null. + final Scope scope; + + NameContext(NameInfo name, Scope scope) { + this.name = name; + this.scope = scope; + } + } + + /** + * Information on all properties or global variables of a given name. + */ + class NameInfo { + + final String name; + + private boolean referenced = false; + private final Deque declarations = new ArrayDeque(); + private JSModule deepestCommonModuleRef = null; + + // True if this property is a function that reads a variable from an + // outer scope which isn't the global scope. + private boolean readClosureVariables = false; + + /** + * Constructs a new NameInfo. + * @param name The name of the property that this represents. May be null + * to signify dummy nodes in the property graph. + */ + NameInfo(String name) { + this.name = name; + } + + @Override public String toString() { return name; } + + /** Determines whether we've marked a reference to this property name. */ + boolean isReferenced() { + return referenced; + } + + /** Determines whether it reads a closure variable. */ + boolean readsClosureVariables() { + return readClosureVariables; + } + + /** + * Mark a reference in a given module to this property name, and record + * the deepest common module reference. + * @param module The module where it was referenced. + * @return Whether the name info has changed. + */ + boolean markReference(JSModule module) { + boolean hasChanged = false; + if (!referenced) { + referenced = true; + hasChanged = true; + } + + if (moduleGraph != null) { + JSModule originalDeepestCommon = deepestCommonModuleRef; + + if (deepestCommonModuleRef == null) { + deepestCommonModuleRef = module; + } else { + deepestCommonModuleRef = + moduleGraph.getDeepestCommonDependencyInclusive( + deepestCommonModuleRef, module); + } + + if (originalDeepestCommon != deepestCommonModuleRef) { + hasChanged = true; + } + } + + return hasChanged; + } + + /** + * Returns the deepest common module of all the references to this + * property. + */ + JSModule getDeepestCommonModuleRef() { + return deepestCommonModuleRef; + } + + /** + * Returns a mutable collection of all the prototype property declarations + * of this property name. + */ + Deque getDeclarations() { + return declarations; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AnonymousFunctionNamingCallback.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AnonymousFunctionNamingCallback.java new file mode 100644 index 0000000..161ef8d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AnonymousFunctionNamingCallback.java @@ -0,0 +1,137 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + + +/** + * Visitor that performs naming operations on anonymous functions by + * means of the FunctionNamer interface. Anonymous functions are + * named based on context. For example, the anonymous function on the + * RHS based on the property at the LHS of the assignment operator. + * + * goog.string.htmlEscape = function(str) { + * } + * + */ +class AnonymousFunctionNamingCallback + extends AbstractPostOrderCallback { + private final FunctionNamer namer; + + /** + * Interface used by AnonymousFunctionNamingCallback to set the name + * of anonymous functions. + */ + interface FunctionNamer { + /** + * Generates a string representation of a node for use by + * setFunctionName. + */ + String getName(Node node); + + /** + * Sets the name of an anonymous function. + * @param fnNode The function node to update + * @param name The name + */ + void setFunctionName(String name, Node fnNode); + + /** + * Generate a name by "concatenating" the output of multiple calls + * to getName. + */ + String getCombinedName(String lhs, String rhs); + } + + AnonymousFunctionNamingCallback(FunctionNamer namer) { + this.namer = namer; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.FUNCTION: + // this handles functions that are assigned to variables or + // properties + // e.g. goog.string.htmlEscape = function(str) { + // } + + // get the function name and see if it's empty + Node functionNameNode = n.getFirstChild(); + String functionName = functionNameNode.getString(); + if (functionName.length() == 0) { + if (parent.isAssign()) { + // this is an assignment to a property, generally either a + // static function or a prototype function + // e.g. goog.string.htmlEscape = function() { } or + // goog.structs.Map.prototype.getCount = function() { } + Node lhs = parent.getFirstChild(); + String name = namer.getName(lhs); + namer.setFunctionName(name, n); + } else if (parent.isName()) { + // this is an assignment to a variable + // e.g. var handler = function() {} + String name = namer.getName(parent); + namer.setFunctionName(name, n); + } + } + break; + case Token.ASSIGN: + // this handles functions that are assigned to a prototype through + // an object literal + // e.g. BuzzApp.prototype = { + // Start : function() { } + // } + Node lhs = n.getFirstChild(); + Node rhs = lhs.getNext(); + if (rhs.isObjectLit()) { + nameObjectLiteralMethods(rhs, namer.getName(lhs)); + } + } + } + + private void nameObjectLiteralMethods(Node objectLiteral, String context) { + for (Node keyNode = objectLiteral.getFirstChild(); + keyNode != null; + keyNode = keyNode.getNext()) { + Node valueNode = keyNode.getFirstChild(); + + // Object literal keys may be STRING_KEY, GETTER_DEF, SETTER_DEF. + // Get and Set are skipped because they can not be named. + if (keyNode.isStringKey()) { + // concatenate the context and key name to get a new qualified name. + String name = namer.getCombinedName(context, namer.getName(keyNode)); + + int type = valueNode.getType(); + if (type == Token.FUNCTION) { + // set name if function is anonymous + Node functionNameNode = valueNode.getFirstChild(); + String functionName = functionNameNode.getString(); + if (functionName.isEmpty()) { + namer.setFunctionName(name, valueNode); + } + } else if (type == Token.OBJECTLIT) { + // process nested object literal + nameObjectLiteralMethods(valueNode, name); + } + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AnonymousFunctionNamingPolicy.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AnonymousFunctionNamingPolicy.java new file mode 100644 index 0000000..ff8e1b3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AnonymousFunctionNamingPolicy.java @@ -0,0 +1,61 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Strategies for how to do naming of anonymous functions that occur as + * r-values in assignments and variable declarations. + */ +public enum AnonymousFunctionNamingPolicy { + + /** Don't give anonymous functions names */ + OFF(null), + + /** + * Generates names that are based on the left-hand side of the assignment. + * Runs after variable and property renaming, so that the generated names + * will be short and obfuscated. + * @see NameAnonymousFunctions + */ + UNMAPPED(new char[] { NameAnonymousFunctions.DELIMITER }), + + /** + * Generates short unique names and provides a mapping from them back to a + * more meaningful name that's based on the left-hand side of the + * assignment. + * @see NameAnonymousFunctionsMapped + */ + MAPPED(new char[] { NameAnonymousFunctionsMapped.PREFIX }), + ; + + private final char[] reservedCharacters; + + AnonymousFunctionNamingPolicy(char[] reservedCharacters) { + this.reservedCharacters = reservedCharacters; + } + + /** + * Gets characters that are reserved for use in anonymous function names and + * can't be used in variable or property names. + * @return reserved characters or null if no characters are reserved + */ + public char[] getReservedCharacters() { + // TODO(user) - for MAPPED, only the first character is reserved which + // can be used to further optimize + return reservedCharacters; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AstChangeProxy.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AstChangeProxy.java new file mode 100644 index 0000000..5cb90af --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AstChangeProxy.java @@ -0,0 +1,132 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.List; + +/** + * Proxy that provides a high level interface that compiler passes can + * use to replace or remove sections of the AST. + * + */ +class AstChangeProxy { + + /** + * Interface used to notify client code about changes done by + * AstChangeProxy. + */ + interface ChangeListener { + + /** + * Notifies clients about node removals. + */ + void nodeRemoved(Node node); + } + + private final List listeners; + + AstChangeProxy() { + listeners = Lists.newArrayList(); + } + + /** + * Registers a change listener. + */ + final void registerListener(ChangeListener listener) { + listeners.add(listener); + } + + /** + * Unregisters a change listener. + */ + final void unregisterListener(ChangeListener listener) { + listeners.remove(listener); + } + + /** + * Notifies listeners about a removal. + */ + private void notifyOfRemoval(Node node) { + for (ChangeListener listener : listeners) { + listener.nodeRemoved(node); + } + } + + /** + * Removes a node from the parent's child list. + */ + final void removeChild(Node parent, Node node) { + parent.removeChild(node); + + notifyOfRemoval(node); + } + + /** + * Replaces a node from the parent's child list. + */ + final void replaceWith(Node parent, Node node, Node replacement) { + replaceWith(parent, node, Lists.newArrayList(replacement)); + } + + /** + * Replaces a node with the provided list. + */ + final void replaceWith(Node parent, Node node, List replacements) { + Preconditions.checkNotNull(replacements, "\"replacements\" is null."); + + int size = replacements.size(); + + if ((size == 1) && node.isEquivalentTo(replacements.get(0))) { + // trees are equal... don't replace + return; + } + + int parentType = parent.getType(); + + Preconditions.checkState(size == 1 || + parentType == Token.BLOCK || + parentType == Token.SCRIPT || + parentType == Token.LABEL); + + if (parentType == Token.LABEL && size != 1) { + Node block = IR.block(); + for (Node newChild : replacements) { + newChild.copyInformationFrom(node); + Node oldParent = newChild.getParent(); + block.addChildToBack(newChild); + } + + parent.replaceChild(node, block); + + } else { + for (Node newChild : replacements) { + newChild.copyInformationFrom(node); + Node oldParent = newChild.getParent(); + parent.addChildBefore(newChild, node); + } + parent.removeChild(node); + + } + notifyOfRemoval(node); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AstParallelizer.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AstParallelizer.java new file mode 100644 index 0000000..4b7a67c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AstParallelizer.java @@ -0,0 +1,233 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.collect.Lists; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import java.util.List; + +/** + * Breaks up the AST at different levels for parallel analysis and + * optimizations. In all cases, the subtrees are detached from the original + * source tree and are replaced by place-holders for the reverse process. + * Although this class breaks the AST into independent subtrees and make tree + * transformation safe, it is still up to individual passes to preserve proper + * semantics when analyzing the subtrees. + * + */ +class AstParallelizer { + + public static final String TEMP_NAME = "JSC_TMP_PLACE_HOLDER"; + + private final Predicate shouldSplit; + + private final Supplier placeHolderProvider; + + private final List forest; + + private final Node root; + + private final boolean includeRoot; + + // Maps to place holder to the original function. + private final List detachPointList; + + /** + * Constructor. + * + * @param shouldSplit Specify at which node it should split the subtree. + * @param shouldTraverse Specify when to stop looking for subtree to split. + * This is very important for performance as we do not want to + * traverse too much just looking for subtree. + * @param placeHolderProvider Specify what type of node should be place as + * a temporary place holder for where the subtree is detached. + * @param root The AST itself. + * @param includeRoot Should we include the root inside the forest returned + * by {{@link #split()}. + */ + public AstParallelizer( + Predicate shouldSplit, + Predicate shouldTraverse, + Supplier placeHolderProvider, + Node root, + boolean includeRoot) { + this.shouldSplit = shouldSplit; + this.placeHolderProvider = placeHolderProvider; + this.root = root; + this.includeRoot = includeRoot; + this.forest = Lists.newLinkedList(); + this.detachPointList = Lists.newLinkedList(); + } + + public static AstParallelizer createNewFunctionLevelAstParallelizer( + Node root, boolean globalPass) { + + // Split at function level. + Predicate shouldSplit = new Predicate() { + @Override + public boolean apply(Node input) { + return input.isFunction(); + } + }; + + // Always traverse until it finds a split point. + Predicate shouldTraverse = new Predicate() { + @Override + public boolean apply(Node ignored) { + return true; + } + }; + + // Use a function declaration of the same name. + Supplier placeHolders = new Supplier() { + @Override + public Node get() { + return IR.function(IR.name(TEMP_NAME), IR.paramList(), IR.block()); + } + }; + return new AstParallelizer( + shouldSplit, shouldTraverse, placeHolders, root, globalPass); + } + + public static AstParallelizer createNewFileLevelAstParallelizer(Node root) { + + // Split at every node that has a file name prop. + Predicate shouldSplit = new Predicate() { + @Override + public boolean apply(Node input) { + return input.getSourceFileName() != null; + } + }; + + // Use a string as place holder. + Supplier placeHolders = new Supplier() { + @Override + public Node get() { + return NodeUtil.newExpr(IR.string(TEMP_NAME)); + } + }; + + // Only traverse blocks. + Predicate shouldTraverse = new Predicate() { + @Override + public boolean apply(Node n) { + return n.isBlock(); + } + }; + return new AstParallelizer( + shouldSplit, shouldTraverse, placeHolders, root, false); + } + + /** + * Remembers the split point for use in {@link #join()}. + */ + private void recordSplitPoint(Node placeHolder, Node before, Node original) { + detachPointList.add(new DetachPoint(placeHolder, before, original)); + } + + /** + * Splits the AST into subtree at different levels. The subtrees itself are + * usually not valid JavaScript but they are all subtrees of some valid + * JavaScript. + */ + public List split() { + if (includeRoot) { + forest.add(root); + } + split(root); + return forest; + } + + private void split(Node n) { + Node c = n.getFirstChild(); + Node before = null; + while (c != null) { + Node next = c.getNext(); + if (shouldSplit.apply(c)) { + Node placeHolder = placeHolderProvider.get(); + if (before == null) { + forest.add(n.removeFirstChild()); + n.addChildToFront(placeHolder); + } else { + n.addChildAfter(placeHolder, c); + n.removeChildAfter(before); + forest.add(c); + } + recordSplitPoint(placeHolder, before, c); + before = placeHolder; + } else { + split(c); + before = c; + } + c = next; + } + } + + /** + * Reverse the splitting done by {@link #split()}. + */ + public void join() { + // Revert in a reverse order to undo the detachment. + while (!detachPointList.isEmpty()) { + DetachPoint entry = detachPointList.remove(detachPointList.size() - 1); + entry.reattach(); + } + } + + /** + * A class to map the place holder to the original subtree for re-attachment. + * Normally a Map from Node -> Node is sufficient, however, if we also + * remember the node before the place holder, we can avoid using + * {@link Node#replaceChild(Node, Node)} which requires a linear search of + * the before node. Maybe someday we should get a prev pointer for this + * purpose. + */ + private static class DetachPoint { + + // The place holder to remember where the original node was. + private Node placeHolder; + + // The node before the place holder and the original, null if + private Node before; + + // The root of the subtree to be temporary detached. + private Node original; + + private DetachPoint(Node placeHolder, Node before, Node original) { + this.placeHolder = placeHolder; + this.before = before; + this.original = original; + } + + public void reattach() { + // If the place-holder no longer has a parent, this implies the function + // has been removed from the AST. + if (placeHolder.getParent() != null) { + if (before == null) { + placeHolder.getParent().addChildrenToFront(original); + placeHolder.getParent().removeChildAfter(original); + } else { + placeHolder.getParent().addChildAfter(original, before); + placeHolder.getParent().removeChildAfter(original); + } + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AstValidator.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AstValidator.java new file mode 100644 index 0000000..dececfe --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/AstValidator.java @@ -0,0 +1,820 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * This class walks the AST and validates that the structure is correct. + * + * @author johnlenz@google.com (John Lenz) + */ +public class AstValidator implements CompilerPass { + + // Possible enhancements: + // * verify NAME, LABEL_NAME, GETPROP property name and unquoted + // object-literal keys are valid JavaScript identifiers. + // * optionally verify every node has source location information. + // * optionally verify every node has an assigned JSType + // + + public interface ViolationHandler { + void handleViolation(String message, Node n); + } + + private final ViolationHandler violationHandler; + + public AstValidator(ViolationHandler handler) { + this.violationHandler = handler; + } + + public AstValidator() { + this.violationHandler = new ViolationHandler() { + @Override + public void handleViolation(String message, Node n) { + throw new IllegalStateException( + message + " Reference node " + n.toString()); + } + }; + } + + @Override + public void process(Node externs, Node root) { + if (externs != null) { + validateCodeRoot(externs); + } + if (root != null) { + validateCodeRoot(root); + } + } + + public void validateRoot(Node n) { + validateNodeType(Token.BLOCK, n); + validateIsSynthetic(n); + validateChildCount(n, 2); + validateCodeRoot(n.getFirstChild()); + validateCodeRoot(n.getLastChild()); + } + + public void validateCodeRoot(Node n) { + validateNodeType(Token.BLOCK, n); + validateIsSynthetic(n); + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + validateScript(c); + } + } + + public void validateScript(Node n) { + validateNodeType(Token.SCRIPT, n); + validateHasSourceName(n); + validateHasInputId(n); + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + validateStatement(c); + } + } + + public void validateStatement(Node n) { + switch (n.getType()) { + case Token.LABEL: + validateLabel(n); + return; + case Token.BLOCK: + validateBlock(n); + return; + case Token.FUNCTION: + validateFunctionStatement(n); + return; + case Token.WITH: + validateWith(n); + return; + case Token.FOR: + validateFor(n); + return; + case Token.WHILE: + validateWhile(n); + return; + case Token.DO: + validateDo(n); + return; + case Token.SWITCH: + validateSwitch(n); + return; + case Token.IF: + validateIf(n); + return; + case Token.VAR: + validateVar(n); + return; + case Token.EXPR_RESULT: + validateExprStmt(n); + return; + case Token.RETURN: + validateReturn(n); + return; + case Token.THROW: + validateThrow(n); + return; + case Token.TRY: + validateTry(n); + return; + case Token.BREAK: + validateBreak(n); + return; + case Token.CONTINUE: + validateContinue(n); + return; + case Token.EMPTY: + validateChildless(n); + return; + case Token.DEBUGGER: + validateChildless(n); + return; + default: + violation("Expected statement but was " + + Token.name(n.getType()) + ".", n); + } + } + + public void validateExpression(Node n) { + switch (n.getType()) { + // Childless expressions + case Token.FALSE: + case Token.NULL: + case Token.THIS: + case Token.TRUE: + validateChildless(n); + return; + + // General unary ops + case Token.DELPROP: + case Token.POS: + case Token.NEG: + case Token.NOT: + case Token.INC: + case Token.DEC: + case Token.TYPEOF: + case Token.VOID: + case Token.BITNOT: + case Token.CAST: + validateUnaryOp(n); + return; + + // General binary ops + case Token.COMMA: + case Token.OR: + case Token.AND: + case Token.BITOR: + case Token.BITXOR: + case Token.BITAND: + case Token.EQ: + case Token.NE: + case Token.SHEQ: + case Token.SHNE: + case Token.LT: + case Token.GT: + case Token.LE: + case Token.GE: + case Token.INSTANCEOF: + case Token.IN: + case Token.LSH: + case Token.RSH: + case Token.URSH: + case Token.SUB: + case Token.ADD: + case Token.MUL: + case Token.MOD: + case Token.DIV: + validateBinaryOp(n); + return; + + // Assignments + case Token.ASSIGN: + case Token.ASSIGN_BITOR: + case Token.ASSIGN_BITXOR: + case Token.ASSIGN_BITAND: + case Token.ASSIGN_LSH: + case Token.ASSIGN_RSH: + case Token.ASSIGN_URSH: + case Token.ASSIGN_ADD: + case Token.ASSIGN_SUB: + case Token.ASSIGN_MUL: + case Token.ASSIGN_DIV: + case Token.ASSIGN_MOD: + validateAssignmentExpression(n); + return; + + case Token.HOOK: + validateTrinaryOp(n); + return; + + // Node types that require special handling + case Token.STRING: + validateString(n); + return; + + case Token.NUMBER: + validateNumber(n); + return; + + case Token.NAME: + validateName(n); + return; + + case Token.GETELEM: + validateBinaryOp(n); + return; + + case Token.GETPROP: + validateGetProp(n); + return; + + case Token.ARRAYLIT: + validateArrayLit(n); + return; + + case Token.OBJECTLIT: + validateObjectLit(n); + return; + + case Token.REGEXP: + validateRegExpLit(n); + return; + + case Token.CALL: + validateCall(n); + return; + + case Token.NEW: + validateNew(n); + return; + + case Token.FUNCTION: + validateFunctionExpression(n); + return; + + default: + violation("Expected expression but was " + + Token.name(n.getType()), n); + } + } + + private void validateBlock(Node n) { + validateNodeType(Token.BLOCK, n); + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + validateStatement(c); + } + } + + private void validateSyntheticBlock(Node n) { + validateNodeType(Token.BLOCK, n); + validateIsSynthetic(n); + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + validateStatement(c); + } + } + + private void validateIsSynthetic(Node n) { + if (!n.getBooleanProp(Node.SYNTHETIC_BLOCK_PROP)) { + violation("Missing 'synthetic block' annotation.", n); + } + } + + private void validateHasSourceName(Node n) { + String sourceName = n.getSourceFileName(); + if (sourceName == null || sourceName.isEmpty()) { + violation("Missing 'source name' annotation.", n); + } + } + + private void validateHasInputId(Node n) { + InputId inputId = n.getInputId(); + if (inputId == null) { + violation("Missing 'input id' annotation.", n); + } + } + + private void validateLabel(Node n) { + validateNodeType(Token.LABEL, n); + validateChildCount(n, 2); + validateLabelName(n.getFirstChild()); + validateStatement(n.getLastChild()); + } + + private void validateLabelName(Node n) { + validateNodeType(Token.LABEL_NAME, n); + validateNonEmptyString(n); + validateChildCount(n, 0); + } + + private void validateNonEmptyString(Node n) { + validateNonNullString(n); + if (n.getString().isEmpty()) { + violation("Expected non-empty string.", n); + } + } + + private void validateNonNullString(Node n) { + if (n.getString() == null) { + violation("Expected non-null string.", n); + } + } + + private void validateName(Node n) { + validateNodeType(Token.NAME, n); + validateNonEmptyString(n); + validateChildCount(n, 0); + } + + private void validateOptionalName(Node n) { + validateNodeType(Token.NAME, n); + validateNonNullString(n); + validateChildCount(n, 0); + } + + private void validateFunctionStatement(Node n) { + validateNodeType(Token.FUNCTION, n); + validateChildCount(n, 3); + validateName(n.getFirstChild()); + validateParameters(n.getChildAtIndex(1)); + validateBlock(n.getLastChild()); + } + + private void validateFunctionExpression(Node n) { + validateNodeType(Token.FUNCTION, n); + validateChildCount(n, 3); + validateOptionalName(n.getFirstChild()); + validateParameters(n.getChildAtIndex(1)); + validateBlock(n.getLastChild()); + } + + private void validateParameters(Node n) { + validateNodeType(Token.PARAM_LIST, n); + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + validateName(c); + } + } + + private void validateCall(Node n) { + validateNodeType(Token.CALL, n); + validateMinimumChildCount(n, 1); + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + validateExpression(c); + } + } + + private void validateNew(Node n) { + validateNodeType(Token.NEW, n); + validateMinimumChildCount(n, 1); + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + validateExpression(c); + } + } + + private void validateVar(Node n) { + validateNodeType(Token.VAR, n); + this.validateMinimumChildCount(n, 1); + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + // Don't use the validateName here as the NAME is allowed to have + // a child. + validateNodeType(Token.NAME, c); + validateNonEmptyString(c); + validateMaximumChildCount(c, 1); + if (c.hasChildren()) { + validateExpression(c.getFirstChild()); + } + } + } + + private void validateFor(Node n) { + validateNodeType(Token.FOR, n); + validateMinimumChildCount(n, 3); + validateMaximumChildCount(n, 4); + if (NodeUtil.isForIn(n)) { + // FOR-IN + validateChildCount(n, 3); + validateVarOrAssignmentTarget(n.getFirstChild()); + validateExpression(n.getChildAtIndex(1)); + } else { + // FOR + validateChildCount(n, 4); + validateVarOrOptionalExpression(n.getFirstChild()); + validateOptionalExpression(n.getChildAtIndex(1)); + validateOptionalExpression(n.getChildAtIndex(2)); + } + validateBlock(n.getLastChild()); + } + + private void validateVarOrOptionalExpression(Node n) { + if (n.isVar()) { + validateVar(n); + } else { + validateOptionalExpression(n); + } + } + + private void validateVarOrAssignmentTarget(Node n) { + if (n.isVar()) { + // Only one NAME can be declared for FOR-IN expressions. + this.validateChildCount(n, 1); + validateVar(n); + } else { + validateAssignmentTarget(n); + } + } + + private void validateWith(Node n) { + validateNodeType(Token.WITH, n); + validateChildCount(n, 2); + validateExpression(n.getFirstChild()); + validateBlock(n.getLastChild()); + } + + private void validateWhile(Node n) { + validateNodeType(Token.WHILE, n); + validateChildCount(n, 2); + validateExpression(n.getFirstChild()); + validateBlock(n.getLastChild()); + } + + private void validateDo(Node n) { + validateNodeType(Token.DO, n); + validateChildCount(n, 2); + validateBlock(n.getFirstChild()); + validateExpression(n.getLastChild()); + } + + private void validateIf(Node n) { + validateNodeType(Token.IF, n); + validateMinimumChildCount(n, 2); + validateMaximumChildCount(n, 3); + validateExpression(n.getFirstChild()); + validateBlock(n.getChildAtIndex(1)); + if (n.getChildCount() == 3) { + validateBlock(n.getLastChild()); + } + } + + private void validateExprStmt(Node n) { + validateNodeType(Token.EXPR_RESULT, n); + validateChildCount(n, 1); + validateExpression(n.getFirstChild()); + } + + private void validateReturn(Node n) { + validateNodeType(Token.RETURN, n); + validateMaximumChildCount(n, 1); + if (n.hasChildren()) { + validateExpression(n.getFirstChild()); + } + } + + private void validateThrow(Node n) { + validateNodeType(Token.THROW, n); + validateChildCount(n, 1); + validateExpression(n.getFirstChild()); + } + + private void validateBreak(Node n) { + validateNodeType(Token.BREAK, n); + validateMaximumChildCount(n, 1); + if (n.hasChildren()) { + validateLabelName(n.getFirstChild()); + } + } + + private void validateContinue(Node n) { + validateNodeType(Token.CONTINUE, n); + validateMaximumChildCount(n, 1); + if (n.hasChildren()) { + validateLabelName(n.getFirstChild()); + } + } + + private void validateTry(Node n) { + validateNodeType(Token.TRY, n); + validateMinimumChildCount(n, 2); + validateMaximumChildCount(n, 3); + validateBlock(n.getFirstChild()); + + boolean seenCatchOrFinally = false; + + // Validate catch + Node catches = n.getChildAtIndex(1); + validateNodeType(Token.BLOCK, catches); + validateMaximumChildCount(catches, 1); + if (catches.hasChildren()) { + validateCatch(catches.getFirstChild()); + seenCatchOrFinally = true; + } + + // Validate finally + if (n.getChildCount() == 3) { + validateBlock(n.getLastChild()); + seenCatchOrFinally = true; + } + + if (!seenCatchOrFinally) { + violation("Missing catch or finally for try statement.", n); + } + } + + private void validateCatch(Node n) { + validateNodeType(Token.CATCH, n); + validateChildCount(n, 2); + validateName(n.getFirstChild()); + validateBlock(n.getLastChild()); + } + + private void validateSwitch(Node n) { + validateNodeType(Token.SWITCH, n); + validateMinimumChildCount(n, 1); + validateExpression(n.getFirstChild()); + int defaults = 0; + for (Node c = n.getFirstChild().getNext(); c != null; c = c.getNext()) { + validateSwitchMember(n.getLastChild()); + if (c.isDefaultCase()) { + defaults++; + } + } + if (defaults > 1) { + violation("Expected at most 1 'default' in switch but was " + + defaults, n); + } + } + + private void validateSwitchMember(Node n) { + switch (n.getType()) { + case Token.CASE: + validateCase(n); + return; + case Token.DEFAULT_CASE: + validateDefault(n); + return; + default: + violation("Expected switch member but was " + + Token.name(n.getType()), n); + } + } + + private void validateDefault(Node n) { + validateNodeType(Token.DEFAULT_CASE, n); + validateChildCount(n, 1); + validateSyntheticBlock(n.getLastChild()); + } + + private void validateCase(Node n) { + validateNodeType(Token.CASE, n); + validateChildCount(n, 2); + validateExpression(n.getFirstChild()); + validateSyntheticBlock(n.getLastChild()); + } + + private void validateOptionalExpression(Node n) { + if (n.isEmpty()) { + validateChildless(n); + } else { + validateExpression(n); + } + } + + private void validateChildless(Node n) { + validateChildCount(n, 0); + } + + private void validateAssignmentExpression(Node n) { + validateChildCount(n, 2); + validateAssignmentTarget(n.getFirstChild()); + validateExpression(n.getLastChild()); + } + + private void validateAssignmentTarget(Node n) { + switch (n.getType()) { + case Token.NAME: + case Token.GETELEM: + case Token.GETPROP: + validateExpression(n); + return; + default: + violation("Expected assignment target expression but was " + + Token.name(n.getType()), n); + } + } + + private void validateGetProp(Node n) { + validateNodeType(Token.GETPROP, n); + validateChildCount(n, 2); + validateExpression(n.getFirstChild()); + Node prop = n.getLastChild(); + validateNodeType(Token.STRING, prop); + validateNonEmptyString(prop); + } + + private void validateRegExpLit(Node n) { + validateNodeType(Token.REGEXP, n); + validateMinimumChildCount(n, 1); + validateMaximumChildCount(n, 2); + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + validateString(c); + } + } + + private void validateString(Node n) { + validateNodeType(Token.STRING, n); + validateChildCount(n, 0); + try { + // Validate that getString doesn't throw + n.getString(); + } catch (UnsupportedOperationException e) { + violation("Invalid STRING node.", n); + } + } + + private void validateNumber(Node n) { + validateNodeType(Token.NUMBER, n); + validateChildCount(n, 0); + try { + // Validate that getDouble doesn't throw + n.getDouble(); + } catch (UnsupportedOperationException e) { + violation("Invalid NUMBER node.", n); + } + } + + private void validateArrayLit(Node n) { + validateNodeType(Token.ARRAYLIT, n); + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + // EMPTY is allowed to represent empty slots. + validateOptionalExpression(c); + } + } + + private void validateObjectLit(Node n) { + validateNodeType(Token.OBJECTLIT, n); + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + validateObjectLitKey(c); + } + } + + private void validateObjectLitKey(Node n) { + switch (n.getType()) { + case Token.GETTER_DEF: + validateObjectLitGetKey(n); + return; + case Token.SETTER_DEF: + validateObjectLitSetKey(n); + return; + case Token.STRING_KEY: + validateObjectLitStringKey(n); + return; + default: + violation("Expected object literal key expression but was " + + Token.name(n.getType()), n); + } + } + + private void validateObjectLitGetKey(Node n) { + validateNodeType(Token.GETTER_DEF, n); + validateChildCount(n, 1); + validateObjectLiteralKeyName(n); + Node function = n.getFirstChild(); + validateFunctionExpression(function); + // objlit get functions must be nameless, and must have zero parameters. + if (!function.getFirstChild().getString().isEmpty()) { + violation("Expected unnamed function expression.", n); + } + Node functionParams = function.getChildAtIndex(1); + if (functionParams.hasChildren()) { + violation("get methods must not have parameters.", n); + } + } + + private void validateObjectLitSetKey(Node n) { + validateNodeType(Token.SETTER_DEF, n); + validateChildCount(n, 1); + validateObjectLiteralKeyName(n); + Node function = n.getFirstChild(); + validateFunctionExpression(function); + // objlit set functions must be nameless, and must have 1 parameter. + if (!function.getFirstChild().getString().isEmpty()) { + violation("Expected unnamed function expression.", n); + } + Node functionParams = function.getChildAtIndex(1); + if (!functionParams.hasOneChild()) { + violation("set methods must have exactly one parameter.", n); + } + } + + private void validateObjectLitStringKey(Node n) { + validateNodeType(Token.STRING_KEY, n); + validateChildCount(n, 1); + validateObjectLiteralKeyName(n); + validateExpression(n.getFirstChild()); + } + + private void validateObjectLiteralKeyName(Node n) { + if (n.isQuotedString()) { + try { + // Validate that getString doesn't throw + n.getString(); + } catch (UnsupportedOperationException e) { + violation("getString failed for" + Token.name(n.getType()), n); + } + } else { + validateNonEmptyString(n); + } + } + + private void validateUnaryOp(Node n) { + validateChildCount(n, 1); + validateExpression(n.getFirstChild()); + } + + private void validateBinaryOp(Node n) { + validateChildCount(n, 2); + validateExpression(n.getFirstChild()); + validateExpression(n.getLastChild()); + } + + private void validateTrinaryOp(Node n) { + validateChildCount(n, 3); + Node first = n.getFirstChild(); + validateExpression(first); + validateExpression(first.getNext()); + validateExpression(n.getLastChild()); + } + + private void violation(String message, Node n) { + violationHandler.handleViolation(message, n); + } + + private void validateNodeType(int type, Node n) { + if (n.getType() != type) { + violation( + "Expected " + Token.name(type) + " but was " + + Token.name(n.getType()), n); + } + } + + private void validateChildCount(Node n, int i) { + boolean valid = false; + if (i == 0) { + valid = !n.hasChildren(); + } else if (i == 1) { + valid = n.hasOneChild(); + } else { + valid = (n.getChildCount() == i); + } + if (!valid) { + violation( + "Expected " + i + " children, but was " + + n.getChildCount(), n); + } + } + + private void validateMinimumChildCount(Node n, int i) { + boolean valid = false; + if (i == 1) { + valid = n.hasChildren(); + } else if (i == 2) { + valid = n.hasMoreThanOneChild(); + } else { + valid = n.getChildCount() >= i; + } + + if (!valid) { + violation( + "Expected at least " + i + " children, but was " + + n.getChildCount(), n); + } + } + + private void validateMaximumChildCount(Node n, int i) { + boolean valid = false; + if (i == 1) { + valid = !n.hasMoreThanOneChild(); + } else { + valid = n.getChildCount() <= i; + } + if (!valid) { + violation( + "Expected no more than " + i + " children, but was " + + n.getChildCount(), n); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/BasicErrorManager.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/BasicErrorManager.java new file mode 100644 index 0000000..3d19f3c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/BasicErrorManager.java @@ -0,0 +1,191 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.CheckLevel; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.SortedSet; + +/** + *

A basic error manager that sorts all errors and warnings reported to it to + * generate a sorted report when the {@link #generateReport()} method + * is called.

+ * + *

This error manager does not produce any output, but subclasses can + * override the {@link #println(CheckLevel, JSError)} method to generate custom + * output.

+ * + */ +public abstract class BasicErrorManager implements ErrorManager { + private final SortedSet messages = + Sets.newTreeSet(new LeveledJSErrorComparator()); + private int errorCount = 0; + private int warningCount = 0; + private double typedPercent = 0.0; + + @Override + public void report(CheckLevel level, JSError error) { + if (messages.add(new ErrorWithLevel(error, level))) { + if (level == CheckLevel.ERROR) { + errorCount++; + } else if (level == CheckLevel.WARNING) { + warningCount++; + } + } + } + + @Override + public void generateReport() { + for (ErrorWithLevel message : messages) { + println(message.level, message.error); + } + printSummary(); + } + + /** + * Print a message with a trailing new line. This method is called by the + * {@link #generateReport()} method when generating messages. + */ + public abstract void println(CheckLevel level, JSError error); + + /** + * Print the summary of the compilation - number of errors and warnings. + */ + protected abstract void printSummary(); + + @Override + public int getErrorCount() { + return errorCount; + } + + @Override + public int getWarningCount() { + return warningCount; + } + + @Override + public JSError[] getErrors() { + return toArray(CheckLevel.ERROR); + } + + @Override + public JSError[] getWarnings() { + return toArray(CheckLevel.WARNING); + } + + @Override + public void setTypedPercent(double typedPercent) { + this.typedPercent = typedPercent; + } + + @Override + public double getTypedPercent() { + return typedPercent; + } + + private JSError[] toArray(CheckLevel level) { + List errors = new ArrayList(messages.size()); + for (ErrorWithLevel p : messages) { + if (p.level == level) { + errors.add(p.error); + } + } + return errors.toArray(new JSError[errors.size()]); + } + + /** + *

Comparator of {@link JSError} with an associated {@link CheckLevel}. + * The ordering is the standard lexical ordering on the quintuple + * (file name, line number, {@link CheckLevel}, + * character number, description).

+ * + *

Note: this comparator imposes orderings that are inconsistent with + * {@link JSError#equals(Object)}.

+ */ + static final class LeveledJSErrorComparator + implements Comparator { + private static final int P1_LT_P2 = -1; + private static final int P1_GT_P2 = 1; + + @Override + public int compare(ErrorWithLevel p1, ErrorWithLevel p2) { + // null is the smallest value + if (p2 == null) { + if (p1 == null) { + return 0; + } else { + return P1_GT_P2; + } + } + + // check level + if (p1.level != p2.level) { + return p2.level.compareTo(p1.level); + } + + // sourceName comparison + String source1 = p1.error.sourceName; + String source2 = p2.error.sourceName; + if (source1 != null && source2 != null) { + int sourceCompare = source1.compareTo(source2); + if (sourceCompare != 0) { + return sourceCompare; + } + } else if (source1 == null && source2 != null) { + return P1_LT_P2; + } else if (source1 != null && source2 == null) { + return P1_GT_P2; + } + // lineno comparison + int lineno1 = p1.error.lineNumber; + int lineno2 = p2.error.lineNumber; + if (lineno1 != lineno2) { + return lineno1 - lineno2; + } else if (lineno1 < 0 && 0 <= lineno2) { + return P1_LT_P2; + } else if (0 <= lineno1 && lineno2 < 0) { + return P1_GT_P2; + } + // charno comparison + int charno1 = p1.error.getCharno(); + int charno2 = p2.error.getCharno(); + if (charno1 != charno2) { + return charno1 - charno2; + } else if (charno1 < 0 && 0 <= charno2) { + return P1_LT_P2; + } else if (0 <= charno1 && charno2 < 0) { + return P1_GT_P2; + } + // description + return p1.error.description.compareTo(p2.error.description); + } + } + + static class ErrorWithLevel { + final JSError error; + final CheckLevel level; + + ErrorWithLevel(JSError error, CheckLevel level) { + this.error = error; + this.level = level; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ByPathWarningsGuard.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ByPathWarningsGuard.java new file mode 100644 index 0000000..52f0c24 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ByPathWarningsGuard.java @@ -0,0 +1,112 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; + +import java.util.List; + +/** + * An implementation of a {@link WarningsGuard} that can modify the + * {@link CheckLevel} based on the file that caused the warning, and whether + * this file matches a set of paths (specified either as include or exclude + * of path name parts). + * + *

For example: + *

+ * List paths = new ArrayList();
+ * paths.add("foo");
+ * WarningsGuard guard =
+ *     ByPathWarningsGuard.forPath(paths, CheckLevel.ERROR, 1);
+ * 
+ * + * This guard will convert any warning that came from a file that contains "foo" + * in its path to an error. + * + */ +public class ByPathWarningsGuard extends WarningsGuard { + private static final long serialVersionUID = 1L; + + private final List paths; + private final boolean include; + private final int priority; + private CheckLevel level; + + /** + * Constructs a new instance. The priority is determined by the + * {@link CheckLevel}: ERROR have Priority.STRICT, and OFF have priority + * FILTER_BY_PATH. + * + * Use {@link #forPath} or {@link #exceptPath} to actually create a new + * instance. + */ + private ByPathWarningsGuard( + List paths, boolean include, CheckLevel level) { + Preconditions.checkArgument(paths != null); + Preconditions.checkArgument( + level == CheckLevel.OFF || level == CheckLevel.ERROR); + this.paths = paths; + this.include = include; + this.level = level; + this.priority = level == CheckLevel.ERROR ? + WarningsGuard.Priority.STRICT.value : + WarningsGuard.Priority.FILTER_BY_PATH.value; + } + + /** + * @param paths Paths for matching. + * @param level The {@link CheckLevel} to apply on affected files. + * @return a new {@link ByPathWarningsGuard} that would affect any file in the + * given set of paths. + */ + public static ByPathWarningsGuard forPath( + List paths, CheckLevel level) { + return new ByPathWarningsGuard(paths, true, level); + } + + /** + * @param paths Paths for matching. + * @param level The {@link CheckLevel} to apply on affected files. + * @return a new {@link ByPathWarningsGuard} that would affect any file not + * in the given set of paths. + */ + public static ByPathWarningsGuard exceptPath( + List paths, CheckLevel level) { + return new ByPathWarningsGuard(paths, false, level); + } + + @Override + public CheckLevel level(JSError error) { + final String errorPath = error.sourceName; + CheckLevel defaultLevel = error.getDefaultLevel(); + if (defaultLevel != CheckLevel.ERROR && errorPath != null) { + boolean inPath = false; + for (String path : paths) { + inPath |= errorPath.contains(path); + } + if (inPath == include) { + return level; + } + } + return null; + } + + @Override + protected int getPriority() { + return priority; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CallGraph.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CallGraph.java new file mode 100644 index 0000000..81d8dd3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CallGraph.java @@ -0,0 +1,810 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.DefinitionsRemover.Definition; +import com.google.javascript.jscomp.NameReferenceGraph.Name; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.graph.DiGraph; +import com.google.javascript.jscomp.graph.LinkedDirectedGraph; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.Map; + +/** + * A pass the uses a {@link DefinitionProvider} to compute a call graph for an + * AST. + * + *

A {@link CallGraph} connects {@link Function}s to {@link Callsite}s and + * vice versa: each function in the graph links to the callsites it contains and + * each callsite links to the functions it could call. Similarly, each callsite + * links to the function that contains it and each function links to the + * callsites that could call it. + * + *

The callgraph is not precise. That is, a callsite may indicate it can + * call a function when in fact it does not do so in the running program. + * + *

The callgraph is also not complete: in some cases it may be unable to + * determine some targets of a callsite. In this case, + * Callsite.hasUnknownTarget() will return true. + * + *

The CallGraph doesn't (currently) have functions for externally defined + * functions; however, callsites that target externs will have hasExternTarget() + * return true. + * + *

TODO(dcc): Have CallGraph (optionally?) include functions for externs. + * + * @author dcc@google.com (Devin Coughlin) + */ +public class CallGraph implements CompilerPass { + private AbstractCompiler compiler; + + /** + * Maps an AST node (with type Token.CALL or Token.NEW) to a Callsite object. + */ + private Map callsitesByNode; + + /** Maps an AST node (with type Token.FUNCTION) to a Function object. */ + private Map functionsByNode; + + /** + * Will the call graph support looking up the callsites that could call a + * given function? + */ + private boolean computeBackwardGraph; + + /** + * Will the call graph support looking up the functions that a given callsite + * can call? + */ + private boolean computeForwardGraph; + + /** + * If true, then the callgraph will use NameReferenceGraph as a + * definition provider; otherwise, use the faster SimpleDefinitionProvider. + */ + private boolean useNameReferenceGraph = false; + + /** Has the CallGraph already been constructed? */ + private boolean alreadyRun = false; + + /** The name we give the main function. */ + @VisibleForTesting + public static final String MAIN_FUNCTION_NAME = "{main}"; + + /** + * Represents the global function. Calling getBody() on this + * function will yield the global script/block. + * + * TODO(dcc): having a single main function is somewhat misleading. Perhaps + * it might be better to make CallGraph module aware and have one per + * module? + */ + private Function mainFunction; + + /** + * Creates a call graph object supporting the specified lookups. + * + * At least one (and possibly both) of computeForwardGraph and + * computeBackwardGraph must be true. + * + * @param compiler The compiler + * @param computeForwardGraph Should the call graph allow lookup of the target + * functions a given callsite could call? + * @param computeBackwardGraph Should the call graph allow lookup of the + * callsites that could call a given function? + */ + public CallGraph(AbstractCompiler compiler, boolean computeForwardGraph, + boolean computeBackwardGraph) { + Preconditions.checkArgument(computeForwardGraph || computeBackwardGraph); + + this.compiler = compiler; + + this.computeForwardGraph = computeForwardGraph; + this.computeBackwardGraph = computeBackwardGraph; + + callsitesByNode = Maps.newLinkedHashMap(); + functionsByNode = Maps.newLinkedHashMap(); + } + + /** + * Creates a call graph object support both forward and backward lookups. + */ + public CallGraph(AbstractCompiler compiler) { + this(compiler, true, true); + } + + /** + * Builds a call graph for the given externsRoot and jsRoot. + * This method must not be called more than once per CallGraph instance. + */ + @Override + public void process(Node externsRoot, Node jsRoot) { + Preconditions.checkState(alreadyRun == false); + + DefinitionProvider definitionProvider = + constructDefinitionProvider(externsRoot, jsRoot); + + createFunctionsAndCallsites(jsRoot, definitionProvider); + + fillInFunctionInformation(definitionProvider); + + alreadyRun = true; + } + + /** + * Returns the call graph Function object corresponding to the provided + * AST Token.FUNCTION node, or null if no such object exists. + */ + public Function getFunctionForAstNode(Node functionNode) { + Preconditions.checkArgument(functionNode.isFunction()); + + return functionsByNode.get(functionNode); + } + + /** + * Returns a Function object representing the "main" global function. + */ + public Function getMainFunction() { + return mainFunction; + } + + /** + * Returns a collection of all functions (including the main function) + * in the call graph. + */ + public Collection getAllFunctions() { + return functionsByNode.values(); + } + + /** + * Finds a function with the given name. Throws an exception if + * there are no functions or multiple functions with the name. This is + * for testing purposes only. + */ + @VisibleForTesting + public Function getUniqueFunctionWithName(final String desiredName) { + Collection functions = + Collections2.filter(getAllFunctions(), + new Predicate() { + @Override + public boolean apply(Function function) { + + String functionName = function.getName(); + // Anonymous functions will have null names, + // so it is important to handle that correctly here + if (functionName != null && desiredName != null) { + return desiredName.equals(functionName); + } else { + return desiredName == functionName; + } + } + }); + + if (functions.size() == 1) { + return functions.iterator().next(); + } else { + throw new IllegalStateException("Found " + functions.size() + + " functions with name " + desiredName); + } + } + + /** + * Returns the call graph Callsite object corresponding to the provided + * AST Token.CALL or Token.NEW node, or null if no such object exists. + */ + public Callsite getCallsiteForAstNode(Node callsiteNode) { + Preconditions.checkArgument(callsiteNode.isCall() || + callsiteNode.isNew()); + + return callsitesByNode.get(callsiteNode); + } + + /** + * Returns a collection of all callsites in the call graph. + */ + public Collection getAllCallsites() { + return callsitesByNode.values(); + } + + /** + * Creates {@link Function}s and {@link Callsite}s in a single + * AST traversal. + */ + private void createFunctionsAndCallsites(Node jsRoot, + final DefinitionProvider provider) { + // Create fake function representing global execution + mainFunction = createFunction(jsRoot); + + NodeTraversal.traverse(compiler, jsRoot, new AbstractPostOrderCallback() { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + int nodeType = n.getType(); + + if (nodeType == Token.CALL || nodeType == Token.NEW) { + Callsite callsite = createCallsite(n); + + Node containingFunctionNode = t.getScopeRoot(); + + Function containingFunction = + functionsByNode.get(containingFunctionNode); + + if (containingFunction == null) { + containingFunction = createFunction(containingFunctionNode); + } + callsite.containingFunction = containingFunction; + containingFunction.addCallsiteInFunction(callsite); + + connectCallsiteToTargets(callsite, provider); + + } else if (n.isFunction()) { + if (!functionsByNode.containsKey(n)) { + createFunction(n); + } + } + } + }); + } + + /** + * Create a Function object for given an Token.FUNCTION AST node. + * + * This is the bottleneck for Function creation: all Functions should + * be created with this method. + */ + private Function createFunction(Node functionNode) { + Function function = new Function(functionNode); + functionsByNode.put(functionNode, function); + + return function; + } + + private Callsite createCallsite(Node callsiteNode) { + Callsite callsite = new Callsite(callsiteNode); + callsitesByNode.put(callsiteNode, callsite); + + return callsite; + } + + /** + * Maps a Callsite to the Function(s) it could call + * and each Function to the Callsite(s) that could call it. + * + * If the definitionProvider cannot determine the target of the Callsite, + * the Callsite's hasUnknownTarget field is set to true. + * + * If the definitionProvider determines that the target of the Callsite + * could be an extern-defined function, then the Callsite's hasExternTarget + * field is set to true. + * + * @param callsite The callsite for which target functions should be found + * @param definitionProvider The DefinitionProvider used to determine + * targets of callsites. + */ + private void connectCallsiteToTargets(Callsite callsite, + DefinitionProvider definitionProvider) { + Collection definitions = + lookupDefinitionsForTargetsOfCall(callsite.getAstNode(), + definitionProvider); + + if (definitions == null) { + callsite.hasUnknownTarget = true; + } else { + for (Definition definition : definitions) { + if (definition.isExtern()) { + callsite.hasExternTarget = true; + } else { + Node target = definition.getRValue(); + + if (target != null && target.isFunction()) { + Function targetFunction = functionsByNode.get(target); + + if (targetFunction == null) { + targetFunction = createFunction(target); + } + + if (computeForwardGraph) { + callsite.addPossibleTarget(targetFunction); + } + + if (computeBackwardGraph) { + targetFunction.addCallsitePossiblyTargetingFunction(callsite); + } + } else { + callsite.hasUnknownTarget = true; + } + } + } + } + } + + /** + * Fills in function information (such as whether the function is ever + * aliased or whether it is exposed to .call or .apply) using the + * definition provider. + * + * We do this here, rather than when connecting the callgraph, to make sure + * that we have correct information for all functions, rather than just + * functions that are actually called. + */ + private void fillInFunctionInformation(DefinitionProvider provider) { + if (useNameReferenceGraph) { + NameReferenceGraph referenceGraph = (NameReferenceGraph) provider; + + for (Function function : getAllFunctions()) { + if (!function.isMain()) { + String functionName = function.getName(); + + if (functionName != null) { + Name symbol = referenceGraph.getSymbol(functionName); + updateFunctionForName(function, symbol); + } + } + } + } else { + SimpleDefinitionFinder finder = (SimpleDefinitionFinder) provider; + + for (DefinitionSite definitionSite : finder.getDefinitionSites()) { + Definition definition = definitionSite.definition; + + Function function = lookupFunctionForDefinition(definition); + + if (function != null) { + for (UseSite useSite : finder.getUseSites(definition)) { + updateFunctionForUse(function, useSite.node); + } + } + } + } + } + + /** + * Updates {@link Function} information (such as whether is is aliased + * or exposed to .apply or .call from a {@link NameReferenceGraph.Name}. + * + * Note: this method may be called multiple times per Function, each time + * with a different name. + */ + private void updateFunctionForName(Function function, Name name) { + if (name.isAliased()) { + function.isAliased = true; + } + + if (name.exposedToCallOrApply()) { + function.isExposedToCallOrApply = true; + } + } + + /** + * Updates {@link Function} information (such as whether is is aliased + * or exposed to .apply or .call based a site where the function is used. + * + * Note: this method may be called multiple times per Function, each time + * with a different useNode. + */ + private void updateFunctionForUse(Function function, Node useNode) { + Node useParent = useNode.getParent(); + int parentType = useParent.getType(); + + if ((parentType == Token.CALL || parentType == Token.NEW) + && useParent.getFirstChild() == useNode) { + // Regular call sites don't count as aliases + } else if (NodeUtil.isGet(useParent)) { + // GET{PROP,ELEM} don't count as aliases + // but we have to check for using them in .call and .apply. + + if (useParent.isGetProp()) { + Node gramps = useParent.getParent(); + if (NodeUtil.isFunctionObjectApply(gramps) || + NodeUtil.isFunctionObjectCall(gramps)) { + function.isExposedToCallOrApply = true; + } + } + } else { + function.isAliased = true; + } + } + + /** + * Returns a {@link CallGraph.Function} for the passed in {@link Definition} + * or null if the definition isn't for a function. + */ + private Function lookupFunctionForDefinition(Definition definition) { + if (definition != null && !definition.isExtern()) { + Node rValue = definition.getRValue(); + + if (rValue != null && rValue.isFunction()) { + Function function = functionsByNode.get(rValue); + Preconditions.checkNotNull(function); + + return function; + } + } + + return null; + } + + /** + * Constructs and returns a directed graph where the nodes are functions and + * the edges are callsites connecting callers to callees. + * + * It is safe to call this method on both forward and backwardly constructed + * CallGraphs. + */ + public DiGraph getForwardDirectedGraph() { + return constructDirectedGraph(true); + } + + /** + * Constructs and returns a directed graph where the nodes are functions and + * the edges are callsites connecting callees to callers. + * + * It is safe to call this method on both forward and backwardly constructed + * CallGraphs. + */ + public DiGraph getBackwardDirectedGraph() { + return constructDirectedGraph(false); + } + + private static void digraphConnect(DiGraph digraph, + Function caller, + Callsite callsite, + Function callee, + boolean forward) { + + Function source; + Function destination; + + if (forward) { + source = caller; + destination = callee; + } else { + source = callee; + destination = caller; + } + + digraph.connect(source, callsite, destination); + } + + /** + * Constructs a digraph of the call graph. If {@code forward} is true, then + * the edges in the digraph will go from callers to callees, if false then + * the edges will go from callees to callers. + * + * It is safe to run this method on both a forwardly constructed callgraph + * and a backwardly constructed callgraph, regardless of the value of + * {@code forward}. + * + * @param forward If true then the digraph will be a forward digraph. + */ + private DiGraph constructDirectedGraph(boolean forward) { + DiGraphdigraph = + LinkedDirectedGraph.createWithoutAnnotations(); + + // Create nodes in call graph + for (Function function : getAllFunctions()) { + digraph.createNode(function); + } + + if (computeForwardGraph) { + // The CallGraph is a forward graph, so go from callers to callees + for (Function caller : getAllFunctions()) { + for (Callsite callsite : caller.getCallsitesInFunction()) { + for (Function callee : callsite.getPossibleTargets()) { + digraphConnect(digraph, caller, callsite, callee, + forward); + } + } + } + } else { + // The CallGraph is a backward graph, so go from callees to callers + for (Function callee : getAllFunctions()) { + for (Callsite callsite : + callee.getCallsitesPossiblyTargetingFunction()) { + + Function caller = callsite.getContainingFunction(); + digraphConnect(digraph, caller, callsite, callee, + forward); + } + } + } + + return digraph; + } + + /** + * Constructs a DefinitionProvider that can be used to determine the + * targets of callsites. + * + * This construction is the main cost of building the callgraph, so we offer + * the client a choice of NameReferenceGraph, which is slow and hopefully more + * precise, and SimpleDefinitionFinder, which is fast and perhaps not as + * precise. + * + * We use SimpleNameFinder as the default because in practice it does + * not appear to be less precise than NameReferenceGraph and is at least an + * order of magnitude faster on large compiles. + */ + private DefinitionProvider constructDefinitionProvider(Node externsRoot, + Node jsRoot) { + if (useNameReferenceGraph) { + // Name reference graph is very, very slow + NameReferenceGraphConstruction graphConstruction + = new NameReferenceGraphConstruction(compiler); + + graphConstruction.process(externsRoot, jsRoot); + + return graphConstruction.getNameReferenceGraph(); + } else { + SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); + defFinder.process(externsRoot, jsRoot); + return defFinder; + } + } + + /** + * Queries the definition provider for the definitions that could be the + * targets of the given callsite node. + * + * This is complicated by the fact that NameReferenceGraph and + * SimpleDefinitionProvider (the two definition providers we currently + * use) differ on the types of target nodes they will analyze. + */ + private Collection lookupDefinitionsForTargetsOfCall( + Node callsite, DefinitionProvider definitionProvider) { + Preconditions.checkArgument(callsite.isCall() + || callsite.isNew()); + + Node targetExpression = callsite.getFirstChild(); + + // NameReferenceGraph throws an exception unless the node is + // a GETPROP or a NAME + if (!useNameReferenceGraph + || (targetExpression.isGetProp() + || targetExpression.isName())) { + + Collection definitions = + definitionProvider.getDefinitionsReferencedAt(targetExpression); + + if (definitions != null && !definitions.isEmpty()) { + return definitions; + } + } + + return null; + } + + /** + * An inner class that represents functions in the call graph. + * A Function knows how to get its AST node and what Callsites + * it contains. + */ + public class Function { + + private Node astNode; + + private boolean isAliased = false; + + private boolean isExposedToCallOrApply = false; + + private Collection callsitesInFunction; + + private Collection callsitesPossiblyTargetingFunction; + + private Function(Node functionAstNode) { + astNode = functionAstNode; + } + + /** + * Does this function represent the global "main" function? + */ + public boolean isMain() { + return (this == CallGraph.this.mainFunction); + } + + /** + * Returns the underlying AST node for the function. This usually + * has type Token.FUNCTION but in the case of the "main" function + * will have type Token.BLOCK. + */ + public Node getAstNode() { + return astNode; + } + + /** + * Returns the AST node for the body of the function. If this function + * is the main function, it will return the global block. + */ + public Node getBodyNode() { + if (isMain()) { + return astNode; + } else { + return NodeUtil.getFunctionBody(astNode); + } + } + + /** + * Gets the name of this function. Returns null if the function is + * anonymous. + */ + public String getName() { + if (isMain()) { + return MAIN_FUNCTION_NAME; + } else { + return NodeUtil.getFunctionName(astNode); + } + } + + /** + * Returns the callsites in this function. + */ + public Collection getCallsitesInFunction() { + if (callsitesInFunction != null) { + return callsitesInFunction; + } else { + return ImmutableList.of(); + } + } + + private void addCallsiteInFunction(Callsite callsite) { + if (callsitesInFunction == null) { + callsitesInFunction = new LinkedList(); + } + callsitesInFunction.add(callsite); + } + + /** + * Returns a collection of callsites that might call this function. + * + * getCallsitesPossiblyTargetingFunction() is a best effort only: the + * collection may include callsites that do not actually call this function + * and if this function is exported or aliased may be missing actual + * targets. + * + * This method should not be called on a Function from a CallGraph + * that was constructed with {@code computeBackwardGraph} {@code false}. + */ + public Collection getCallsitesPossiblyTargetingFunction() { + if (computeBackwardGraph) { + if (callsitesPossiblyTargetingFunction != null) { + return callsitesPossiblyTargetingFunction; + } else { + return ImmutableList.of(); + } + } else { + throw new UnsupportedOperationException("Cannot call " + + "getCallsitesPossiblyTargetingFunction() on a Function " + + "from a non-backward CallGraph"); + } + } + + private void addCallsitePossiblyTargetingFunction(Callsite callsite) { + Preconditions.checkState(computeBackwardGraph); + if (callsitesPossiblyTargetingFunction == null) { + callsitesPossiblyTargetingFunction = + new LinkedList(); + } + callsitesPossiblyTargetingFunction.add(callsite); + } + + /** + * Returns true if the function is aliased. + */ + public boolean isAliased() { + return isAliased; + } + + /** + * Returns true if the function is ever exposed to ".call" or ".apply". + */ + public boolean isExposedToCallOrApply() { + return isExposedToCallOrApply; + } + } + + /** + * An inner class that represents call sites in the call graph. + * A Callsite knows how to get its AST node, what its containing + * Function is, and what its target Functions are. + */ + public class Callsite { + private Node astNode; + + private boolean hasUnknownTarget = false; + private boolean hasExternTarget = false; + + private Function containingFunction = null; + + private Collection possibleTargets; + + private Callsite(Node callsiteAstNode) { + astNode = callsiteAstNode; + } + + public Node getAstNode() { + return astNode; + } + + public Function getContainingFunction() { + return containingFunction; + } + + /** + * Returns the possible target functions that this callsite could call. + * + * These targets do not include functions defined in externs. If this + * callsite could call an extern function, then hasExternTarget() will + * return true. + * + * getKnownTargets() is a best effort only: the collection may include + * other functions that are not actual targets and (if hasUnknownTargets() + * is true) may be missing actual targets. + * + * This method should not be called on a Callsite from a CallGraph + * that was constructed with {@code computeForwardGraph} {@code false}. + */ + public Collection getPossibleTargets() { + if (computeForwardGraph) { + if (possibleTargets != null) { + return possibleTargets; + } else { + return ImmutableList.of(); + } + } else { + throw new UnsupportedOperationException("Cannot call " + + "getPossibleTargets() on a Callsite from a non-forward " + + "CallGraph"); + } + } + + private void addPossibleTarget(Function target) { + Preconditions.checkState(computeForwardGraph); + + if (possibleTargets == null) { + possibleTargets = new LinkedList(); + } + possibleTargets.add(target); + } + + /** + * If true, then DefinitionProvider used in callgraph construction + * was unable find all target functions of this callsite. + * + * If false, then getKnownTargets() contains all the possible targets of + * this callsite (and, perhaps, additional targets as well). + */ + public boolean hasUnknownTarget() { + return hasUnknownTarget; + } + + /** + * If true, then this callsite could target a function defined in the + * externs. If false, then not. + */ + public boolean hasExternTarget() { + return hasExternTarget; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ChainCalls.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ChainCalls.java new file mode 100644 index 0000000..2a1d792 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ChainCalls.java @@ -0,0 +1,192 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.DefinitionsRemover.Definition; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; +import com.google.javascript.rhino.Node; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * Chain calls to functions that return this. + * + */ +class ChainCalls implements CompilerPass { + private final AbstractCompiler compiler; + private final Set badFunctionNodes = Sets.newHashSet(); + private final Set goodFunctionNodes = Sets.newHashSet(); + private final List callSites = Lists.newArrayList(); + private SimpleDefinitionFinder defFinder; + private GatherFunctions gatherFunctions = new GatherFunctions(); + + ChainCalls(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + defFinder = new SimpleDefinitionFinder(compiler); + defFinder.process(externs, root); + + NodeTraversal.traverse(compiler, root, new GatherCallSites()); + + for (CallSite callSite : callSites) { + callSite.parent.removeChild(callSite.n); + callSite.n.removeChild(callSite.callNode); + callSite.nextGetPropNode.replaceChild(callSite.nextGetPropFirstChildNode, + callSite.callNode); + compiler.reportCodeChange(); + } + } + + /** + * Determines whether a function always returns this. + */ + private class GatherFunctions implements ScopedCallback { + @Override + public void enterScope(NodeTraversal t) { + ControlFlowGraph cfg = t.getControlFlowGraph(); + + for (DiGraphEdge s : cfg.getImplicitReturn().getInEdges()) { + Node exitNode = s.getSource().getValue(); + if (!exitNode.isReturn() || + exitNode.getFirstChild() == null || + !exitNode.getFirstChild().isThis()) { + badFunctionNodes.add(t.getScopeRoot()); + return; + } + } + + goodFunctionNodes.add(t.getScopeRoot()); + } + + @Override + public void exitScope(NodeTraversal t) { + } + + @Override + public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, + Node parent) { + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + } + } + + private class GatherCallSites extends AbstractPostOrderCallback { + /** + * If the function call returns this and the next statement has the same + * target expression, record the call site. + */ + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isExprResult()) { + return; + } + + Node callNode = n.getFirstChild(); + if (!callNode.isCall()) { + return; + } + + Node getPropNode = callNode.getFirstChild(); + if (!getPropNode.isGetProp()) { + return; + } + + Node getPropFirstChildNode = getPropNode.getFirstChild(); + + Collection definitions = + defFinder.getDefinitionsReferencedAt(getPropNode); + if (definitions == null) { + return; + } + for (Definition definition : definitions) { + Node rValue = definition.getRValue(); + if (rValue == null) { + return; + } + if (badFunctionNodes.contains(rValue)) { + return; + } + if (!goodFunctionNodes.contains(rValue)) { + NodeTraversal.traverse(compiler, rValue, gatherFunctions); + if (badFunctionNodes.contains(rValue)) { + return; + } + } + } + + Node nextNode = n.getNext(); + if (nextNode == null || !nextNode.isExprResult()) { + return; + } + + Node nextCallNode = nextNode.getFirstChild(); + if (!nextCallNode.isCall()) { + return; + } + + Node nextGetPropNode = nextCallNode.getFirstChild(); + if (!nextGetPropNode.isGetProp()) { + return; + } + + Node nextGetPropFirstChildNode = nextGetPropNode.getFirstChild(); + if (!compiler.areNodesEqualForInlining( + nextGetPropFirstChildNode, getPropFirstChildNode)) { + return; + } + + if (NodeUtil.mayEffectMutableState(getPropFirstChildNode)) { + return; + } + + // We can't chain immediately as it we wouldn't recognize further + // opportunities to chain. + callSites.add(new CallSite(parent, n, callNode, nextGetPropNode, + nextGetPropFirstChildNode)); + } + } + + /** Records a call site to chain. */ + private static class CallSite { + final Node parent; + final Node n; + final Node callNode; + final Node nextGetPropNode; + final Node nextGetPropFirstChildNode; + + CallSite(Node parent, Node n, Node callNode, Node nextGetPropNode, + Node nextGetPropFirstChildNode) { + this.parent = parent; + this.n = n; + this.callNode = callNode; + this.nextGetPropNode = nextGetPropNode; + this.nextGetPropFirstChildNode = nextGetPropFirstChildNode; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckAccessControls.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckAccessControls.java new file mode 100644 index 0000000..df50c26 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckAccessControls.java @@ -0,0 +1,724 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.JSDocInfo.Visibility; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.StaticSourceFile; + +/** + * A compiler pass that checks that the programmer has obeyed all the access + * control restrictions indicated by JSDoc annotations, like + * {@code @private} and {@code @deprecated}. + * + * Because access control restrictions are attached to type information, + * it's important that TypedScopeCreator, TypeInference, and InferJSDocInfo + * all run before this pass. TypedScopeCreator creates and resolves types, + * TypeInference propagates those types across the AST, and InferJSDocInfo + * propagates JSDoc across the types. + * + * @author nicksantos@google.com (Nick Santos) + */ +class CheckAccessControls implements ScopedCallback, HotSwapCompilerPass { + + static final DiagnosticType DEPRECATED_NAME = DiagnosticType.disabled( + "JSC_DEPRECATED_VAR", + "Variable {0} has been deprecated."); + + static final DiagnosticType DEPRECATED_NAME_REASON = DiagnosticType.disabled( + "JSC_DEPRECATED_VAR_REASON", + "Variable {0} has been deprecated: {1}"); + + static final DiagnosticType DEPRECATED_PROP = DiagnosticType.disabled( + "JSC_DEPRECATED_PROP", + "Property {0} of type {1} has been deprecated."); + + static final DiagnosticType DEPRECATED_PROP_REASON = DiagnosticType.disabled( + "JSC_DEPRECATED_PROP_REASON", + "Property {0} of type {1} has been deprecated: {2}"); + + static final DiagnosticType DEPRECATED_CLASS = DiagnosticType.disabled( + "JSC_DEPRECATED_CLASS", + "Class {0} has been deprecated."); + + static final DiagnosticType DEPRECATED_CLASS_REASON = DiagnosticType.disabled( + "JSC_DEPRECATED_CLASS_REASON", + "Class {0} has been deprecated: {1}"); + + static final DiagnosticType BAD_PRIVATE_GLOBAL_ACCESS = + DiagnosticType.disabled( + "JSC_BAD_PRIVATE_GLOBAL_ACCESS", + "Access to private variable {0} not allowed outside file {1}."); + + static final DiagnosticType BAD_PRIVATE_PROPERTY_ACCESS = + DiagnosticType.disabled( + "JSC_BAD_PRIVATE_PROPERTY_ACCESS", + "Access to private property {0} of {1} not allowed here."); + + static final DiagnosticType BAD_PROTECTED_PROPERTY_ACCESS = + DiagnosticType.disabled( + "JSC_BAD_PROTECTED_PROPERTY_ACCESS", + "Access to protected property {0} of {1} not allowed here."); + + static final DiagnosticType PRIVATE_OVERRIDE = + DiagnosticType.disabled( + "JSC_PRIVATE_OVERRIDE", + "Overriding private property of {0}."); + + static final DiagnosticType EXTEND_FINAL_CLASS = + DiagnosticType.error( + "JSC_EXTEND_FINAL_CLASS", + "{0} is not allowed to extend final class {1}."); + + static final DiagnosticType VISIBILITY_MISMATCH = + DiagnosticType.disabled( + "JSC_VISIBILITY_MISMATCH", + "Overriding {0} property of {1} with {2} property."); + + static final DiagnosticType CONST_PROPERTY_REASSIGNED_VALUE = + DiagnosticType.warning( + "JSC_CONSTANT_PROPERTY_REASSIGNED_VALUE", + "constant property {0} assigned a value more than once"); + + static final DiagnosticType CONST_PROPERTY_DELETED = + DiagnosticType.warning( + "JSC_CONSTANT_PROPERTY_DELETED", + "constant property {0} cannot be deleted"); + + private final AbstractCompiler compiler; + private final TypeValidator validator; + + // State about the current traversal. + private int deprecatedDepth = 0; + private int methodDepth = 0; + private JSType currentClass = null; + + private final Multimap initializedConstantProperties; + + CheckAccessControls(AbstractCompiler compiler) { + this.compiler = compiler; + this.validator = compiler.getTypeValidator(); + this.initializedConstantProperties = HashMultimap.create(); + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + NodeTraversal.traverse(compiler, scriptRoot, this); + } + + @Override + public void enterScope(NodeTraversal t) { + if (!t.inGlobalScope()) { + Node n = t.getScopeRoot(); + Node parent = n.getParent(); + if (isDeprecatedFunction(n, parent)) { + deprecatedDepth++; + } + + if (methodDepth == 0) { + currentClass = getClassOfMethod(n, parent); + } + methodDepth++; + } + } + + @Override + public void exitScope(NodeTraversal t) { + if (!t.inGlobalScope()) { + Node n = t.getScopeRoot(); + Node parent = n.getParent(); + if (isDeprecatedFunction(n, parent)) { + deprecatedDepth--; + } + + methodDepth--; + if (methodDepth == 0) { + currentClass = null; + } + } + } + + /** + * Gets the type of the class that "owns" a method, or null if + * we know that its un-owned. + */ + private JSType getClassOfMethod(Node n, Node parent) { + if (parent.isAssign()) { + Node lValue = parent.getFirstChild(); + if (NodeUtil.isGet(lValue)) { + // We have an assignment of the form "a.b = ...". + JSType lValueType = lValue.getJSType(); + if (lValueType != null && lValueType.isNominalConstructor()) { + // If a.b is a constructor, then everything in this function + // belongs to the "a.b" type. + return (lValueType.toMaybeFunctionType()).getInstanceType(); + } else { + // If a.b is not a constructor, then treat this as a method + // of whatever type is on "a". + return normalizeClassType(lValue.getFirstChild().getJSType()); + } + } else { + // We have an assignment of the form "a = ...", so pull the + // type off the "a". + return normalizeClassType(lValue.getJSType()); + } + } else if (NodeUtil.isFunctionDeclaration(n) || + parent.isName()) { + return normalizeClassType(n.getJSType()); + } + + return null; + } + + /** + * Normalize the type of a constructor, its instance, and its prototype + * all down to the same type (the instance type). + */ + private JSType normalizeClassType(JSType type) { + if (type == null || type.isUnknownType()) { + return type; + } else if (type.isNominalConstructor()) { + return (type.toMaybeFunctionType()).getInstanceType(); + } else if (type.isFunctionPrototypeType()) { + FunctionType owner = ((ObjectType) type).getOwnerFunction(); + if (owner.isConstructor()) { + return owner.getInstanceType(); + } + } + return type; + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.NAME: + checkNameDeprecation(t, n, parent); + checkNameVisibility(t, n, parent); + break; + case Token.GETPROP: + checkPropertyDeprecation(t, n, parent); + checkPropertyVisibility(t, n, parent); + checkConstantProperty(t, n); + break; + case Token.NEW: + checkConstructorDeprecation(t, n, parent); + break; + case Token.FUNCTION: + checkFinalClassOverrides(t, n, parent); + break; + } + } + + /** + * Checks the given NEW node to ensure that access restrictions are obeyed. + */ + private void checkConstructorDeprecation(NodeTraversal t, Node n, + Node parent) { + JSType type = n.getJSType(); + + if (type != null) { + String deprecationInfo = getTypeDeprecationInfo(type); + + if (deprecationInfo != null && + shouldEmitDeprecationWarning(t, n, parent)) { + + if (!deprecationInfo.isEmpty()) { + compiler.report( + t.makeError(n, DEPRECATED_CLASS_REASON, + type.toString(), deprecationInfo)); + } else { + compiler.report( + t.makeError(n, DEPRECATED_CLASS, type.toString())); + } + } + } + } + + /** + * Checks the given NAME node to ensure that access restrictions are obeyed. + */ + private void checkNameDeprecation(NodeTraversal t, Node n, Node parent) { + // Don't bother checking definitions or constructors. + if (parent.isFunction() || parent.isVar() || + parent.isNew()) { + return; + } + + Scope.Var var = t.getScope().getVar(n.getString()); + JSDocInfo docInfo = var == null ? null : var.getJSDocInfo(); + + if (docInfo != null && docInfo.isDeprecated() && + shouldEmitDeprecationWarning(t, n, parent)) { + + if (docInfo.getDeprecationReason() != null) { + compiler.report( + t.makeError(n, DEPRECATED_NAME_REASON, n.getString(), + docInfo.getDeprecationReason())); + } else { + compiler.report( + t.makeError(n, DEPRECATED_NAME, n.getString())); + } + } + } + + /** + * Checks the given GETPROP node to ensure that access restrictions are + * obeyed. + */ + private void checkPropertyDeprecation(NodeTraversal t, Node n, Node parent) { + // Don't bother checking constructors. + if (parent.isNew()) { + return; + } + + ObjectType objectType = + ObjectType.cast(dereference(n.getFirstChild().getJSType())); + String propertyName = n.getLastChild().getString(); + + if (objectType != null) { + String deprecationInfo + = getPropertyDeprecationInfo(objectType, propertyName); + + if (deprecationInfo != null && + shouldEmitDeprecationWarning(t, n, parent)) { + + if (!deprecationInfo.isEmpty()) { + compiler.report( + t.makeError(n, DEPRECATED_PROP_REASON, propertyName, + validator.getReadableJSTypeName(n.getFirstChild(), true), + deprecationInfo)); + } else { + compiler.report( + t.makeError(n, DEPRECATED_PROP, propertyName, + validator.getReadableJSTypeName(n.getFirstChild(), true))); + } + } + } + } + + /** + * Determines whether the given name is visible in the current context. + * @param t The current traversal. + * @param name The name node. + */ + private void checkNameVisibility(NodeTraversal t, Node name, Node parent) { + Var var = t.getScope().getVar(name.getString()); + if (var != null) { + JSDocInfo docInfo = var.getJSDocInfo(); + if (docInfo != null) { + // If a name is private, make sure that we're in the same file. + Visibility visibility = docInfo.getVisibility(); + if (visibility == Visibility.PRIVATE) { + StaticSourceFile varSrc = var.getSourceFile(); + StaticSourceFile refSrc = name.getStaticSourceFile(); + if (varSrc != null && + refSrc != null && + !varSrc.getName().equals(refSrc.getName())) { + if (docInfo.isConstructor() && + isValidPrivateConstructorAccess(parent)) { + return; + } + + compiler.report( + t.makeError(name, BAD_PRIVATE_GLOBAL_ACCESS, + name.getString(), varSrc.getName())); + } + } + } + } + } + + /** + * Checks if a constructor is trying to override a final class. + * @param t The current traversal. + * @param name The name node. + */ + private void checkFinalClassOverrides(NodeTraversal t, Node fn, Node parent) { + JSType type = fn.getJSType().toMaybeFunctionType(); + if (type != null && type.isConstructor()) { + JSType finalParentClass = getFinalParentClass(getClassOfMethod(fn, parent)); + if (finalParentClass != null) { + compiler.report( + t.makeError(fn, EXTEND_FINAL_CLASS, + type.getDisplayName(), finalParentClass.getDisplayName())); + } + } + } + + /** + * Determines whether the given property with @const tag got reassigned + * @param t The current traversal. + * @param getprop The getprop node. + */ + private void checkConstantProperty(NodeTraversal t, + Node getprop) { + // Check whether the property is modified + Node parent = getprop.getParent(); + boolean isDelete = parent.isDelProp(); + if (!(NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == getprop) + && !parent.isInc() && !parent.isDec() + && !isDelete) { + return; + } + + ObjectType objectType = + ObjectType.cast(dereference(getprop.getFirstChild().getJSType())); + String propertyName = getprop.getLastChild().getString(); + + boolean isConstant = isPropertyDeclaredConstant(objectType, propertyName); + + // Check whether constant properties are reassigned + if (isConstant) { + if (isDelete) { + compiler.report( + t.makeError(getprop, CONST_PROPERTY_DELETED, propertyName)); + return; + } + + ObjectType oType = objectType; + while (oType != null) { + if (oType.hasReferenceName()) { + if (initializedConstantProperties.containsEntry( + oType.getReferenceName(), propertyName)) { + compiler.report( + t.makeError(getprop, CONST_PROPERTY_REASSIGNED_VALUE, + propertyName)); + break; + } + } + oType = oType.getImplicitPrototype(); + } + + Preconditions.checkState(objectType.hasReferenceName()); + initializedConstantProperties.put(objectType.getReferenceName(), + propertyName); + + // Add the prototype when we're looking at an instance object + if (objectType.isInstanceType()) { + ObjectType prototype = objectType.getImplicitPrototype(); + if (prototype != null) { + if (prototype.hasProperty(propertyName) + && prototype.hasReferenceName()) { + initializedConstantProperties.put(prototype.getReferenceName(), + propertyName); + } + } + } + } + } + + /** + * Determines whether the given property is visible in the current context. + * @param t The current traversal. + * @param getprop The getprop node. + */ + private void checkPropertyVisibility(NodeTraversal t, + Node getprop, Node parent) { + ObjectType objectType = + ObjectType.cast(dereference(getprop.getFirstChild().getJSType())); + String propertyName = getprop.getLastChild().getString(); + + if (objectType != null) { + // Is this a normal property access, or are we trying to override + // an existing property? + boolean isOverride = parent.getJSDocInfo() != null && + parent.isAssign() && + parent.getFirstChild() == getprop; + + // Find the lowest property defined on a class with visibility + // information. + if (isOverride) { + objectType = objectType.getImplicitPrototype(); + } + JSDocInfo docInfo = null; + for (; objectType != null; + objectType = objectType.getImplicitPrototype()) { + docInfo = objectType.getOwnPropertyJSDocInfo(propertyName); + if (docInfo != null && + docInfo.getVisibility() != Visibility.INHERITED) { + break; + } + } + + if (objectType == null) { + // We couldn't find a visibility modifier; assume it's public. + return; + } + + String referenceSource = getprop.getSourceFileName(); + String definingSource = docInfo.getSourceName(); + boolean sameInput = referenceSource != null + && referenceSource.equals(definingSource); + Visibility visibility = docInfo.getVisibility(); + JSType ownerType = normalizeClassType(objectType); + if (isOverride) { + // Check an ASSIGN statement that's trying to override a property + // on a superclass. + JSDocInfo overridingInfo = parent.getJSDocInfo(); + Visibility overridingVisibility = overridingInfo == null ? + Visibility.INHERITED : overridingInfo.getVisibility(); + + // Check that (a) the property *can* be overridden, and + // (b) that the visibility of the override is the same as the + // visibility of the original property. + if (visibility == Visibility.PRIVATE && !sameInput) { + compiler.report( + t.makeError(getprop, PRIVATE_OVERRIDE, + objectType.toString())); + } else if (overridingVisibility != Visibility.INHERITED && + overridingVisibility != visibility) { + compiler.report( + t.makeError(getprop, VISIBILITY_MISMATCH, + visibility.name(), objectType.toString(), + overridingVisibility.name())); + } + } else { + if (sameInput) { + // private access is always allowed in the same file. + return; + } else if (visibility == Visibility.PRIVATE && + (currentClass == null || !ownerType.isEquivalentTo(currentClass))) { + if (docInfo.isConstructor() && + isValidPrivateConstructorAccess(parent)) { + return; + } + + // private access is not allowed outside the file from a different + // enclosing class. + compiler.report( + t.makeError(getprop, + BAD_PRIVATE_PROPERTY_ACCESS, + propertyName, + validator.getReadableJSTypeName( + getprop.getFirstChild(), true))); + } else if (visibility == Visibility.PROTECTED) { + // There are 3 types of legal accesses of a protected property: + // 1) Accesses in the same file + // 2) Overriding the property in a subclass + // 3) Accessing the property from inside a subclass + // The first two have already been checked for. + if (currentClass == null || !currentClass.isSubtype(ownerType)) { + compiler.report( + t.makeError(getprop, BAD_PROTECTED_PROPERTY_ACCESS, + propertyName, + validator.getReadableJSTypeName( + getprop.getFirstChild(), true))); + } + } + } + } + } + + /** + * Whether the given access of a private constructor is legal. + * + * For example, + * new PrivateCtor_(); // not legal + * PrivateCtor_.newInstance(); // legal + * x instanceof PrivateCtor_ // legal + * + * This is a weird special case, because our visibility system is inherited + * from Java, and JavaScript has no distinction between classes and + * constructors like Java does. + * + * We may want to revisit this if we decide to make the restrictions tighter. + */ + private static boolean isValidPrivateConstructorAccess(Node parent) { + return !parent.isNew(); + } + + /** + * Determines whether a deprecation warning should be emitted. + * @param t The current traversal. + * @param n The node which we are checking. + * @param parent The parent of the node which we are checking. + */ + private boolean shouldEmitDeprecationWarning( + NodeTraversal t, Node n, Node parent) { + // In the global scope, there are only two kinds of accesses that should + // be flagged for warnings: + // 1) Calls of deprecated functions and methods. + // 2) Instantiations of deprecated classes. + // For now, we just let everything else by. + if (t.inGlobalScope()) { + if (!((parent.isCall() && parent.getFirstChild() == n) || + n.isNew())) { + return false; + } + } + + // We can always assign to a deprecated property, to keep it up to date. + if (n.isGetProp() && n == parent.getFirstChild() && + NodeUtil.isAssignmentOp(parent)) { + return false; + } + + return !canAccessDeprecatedTypes(t); + } + + /** + * Returns whether it's currently OK to access deprecated names and + * properties. + * + * There are 3 exceptions when we're allowed to use a deprecated + * type or property: + * 1) When we're in a deprecated function. + * 2) When we're in a deprecated class. + * 3) When we're in a static method of a deprecated class. + */ + private boolean canAccessDeprecatedTypes(NodeTraversal t) { + Node scopeRoot = t.getScopeRoot(); + Node scopeRootParent = scopeRoot.getParent(); + return + // Case #1 + (deprecatedDepth > 0) || + // Case #2 + (getTypeDeprecationInfo(t.getScope().getTypeOfThis()) != null) || + // Case #3 + (scopeRootParent != null && scopeRootParent.isAssign() && + getTypeDeprecationInfo( + getClassOfMethod(scopeRoot, scopeRootParent)) != null); + } + + /** + * Returns whether this is a function node annotated as deprecated. + */ + private static boolean isDeprecatedFunction(Node n, Node parent) { + if (n.isFunction()) { + JSType type = n.getJSType(); + if (type != null) { + return getTypeDeprecationInfo(type) != null; + } + } + + return false; + } + + /** + * Returns the deprecation reason for the type if it is marked + * as being deprecated. Returns empty string if the type is deprecated + * but no reason was given. Returns null if the type is not deprecated. + */ + private static String getTypeDeprecationInfo(JSType type) { + if (type == null) { + return null; + } + + JSDocInfo info = type.getJSDocInfo(); + if (info != null && info.isDeprecated()) { + if (info.getDeprecationReason() != null) { + return info.getDeprecationReason(); + } + return ""; + } + ObjectType objType = ObjectType.cast(type); + if (objType != null) { + ObjectType implicitProto = objType.getImplicitPrototype(); + if (implicitProto != null) { + return getTypeDeprecationInfo(implicitProto); + } + } + return null; + } + + /** + * Returns if a property is declared constant. + */ + private static boolean isPropertyDeclaredConstant( + ObjectType objectType, String prop) { + for (; + // Only objects with reference names can have constant properties. + objectType != null && objectType.hasReferenceName(); + + objectType = objectType.getImplicitPrototype()) { + JSDocInfo docInfo = objectType.getOwnPropertyJSDocInfo(prop); + if (docInfo != null && docInfo.isConstant()) { + return true; + } + } + return false; + } + + /** + * Returns the deprecation reason for the property if it is marked + * as being deprecated. Returns empty string if the property is deprecated + * but no reason was given. Returns null if the property is not deprecated. + */ + private static String getPropertyDeprecationInfo(ObjectType type, + String prop) { + JSDocInfo info = type.getOwnPropertyJSDocInfo(prop); + if (info != null && info.isDeprecated()) { + if (info.getDeprecationReason() != null) { + return info.getDeprecationReason(); + } + + return ""; + } + ObjectType implicitProto = type.getImplicitPrototype(); + if (implicitProto != null) { + return getPropertyDeprecationInfo(implicitProto, prop); + } + return null; + } + + /** + * Dereference a type, autoboxing it and filtering out null. + */ + private static JSType dereference(JSType type) { + return type == null ? null : type.dereference(); + } + + /** + * Returns the super class of the given type that has a constructor. + */ + private JSType getFinalParentClass(JSType type) { + if (type != null) { + ObjectType iproto = ObjectType.cast(type).getImplicitPrototype(); + while (iproto != null && iproto.getConstructor() == null) { + iproto = iproto.getImplicitPrototype(); + } + if (iproto != null) { + Node source = iproto.getConstructor().getSource(); + JSDocInfo jsDoc = source != null ? NodeUtil.getBestJSDocInfo(source) : null; + if (jsDoc != null && jsDoc.isConstant()) { + return iproto; + } + } + } + return null; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckDebuggerStatement.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckDebuggerStatement.java new file mode 100644 index 0000000..ba022da --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckDebuggerStatement.java @@ -0,0 +1,55 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.DiagnosticType; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.Node; + +/** + * {@link CheckDebuggerStatement} checks for the presence of the "debugger" + * statement in JavaScript code. It is appropriate to use this statement while + * developing JavaScript; however, it is generally undesirable to include it in + * production code. + * + * @author bolinfest@google.com (Michael Bolin) + */ +class CheckDebuggerStatement extends AbstractPostOrderCallback + implements CompilerPass { + + static final DiagnosticType DEBUGGER_STATEMENT_PRESENT = + DiagnosticType.disabled("JSC_DEBUGGER_STATEMENT_PRESENT", + "Using the debugger statement can halt your application if the user " + + "has a JavaScript debugger running."); + + private final AbstractCompiler compiler; + + public CheckDebuggerStatement(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isDebugger()) { + t.report(n, DEBUGGER_STATEMENT_PRESENT); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckGlobalNames.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckGlobalNames.java new file mode 100644 index 0000000..d035d9d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckGlobalNames.java @@ -0,0 +1,288 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.GlobalNamespace.Name; +import com.google.javascript.jscomp.GlobalNamespace.Ref; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; + +import java.util.Set; + +/** + * Checks references to undefined properties of global variables. + * + * @author nicksantos@google.com (Nick Santos) + */ +class CheckGlobalNames implements CompilerPass { + + private final AbstractCompiler compiler; + private final CodingConvention convention; + private final CheckLevel level; + + private GlobalNamespace namespace = null; + private final Set objectPrototypeProps = Sets.newHashSet(); + private final Set functionPrototypeProps = Sets.newHashSet(); + + // Warnings + static final DiagnosticType UNDEFINED_NAME_WARNING = DiagnosticType.warning( + "JSC_UNDEFINED_NAME", + "{0} is never defined"); + + static final DiagnosticType NAME_DEFINED_LATE_WARNING = + DiagnosticType.warning( + "JSC_NAME_DEFINED_LATE", + "{0} defined before its owner. {1} is defined at {2}:{3}"); + + static final DiagnosticType STRICT_MODULE_DEP_QNAME = + DiagnosticType.disabled( + "JSC_STRICT_MODULE_DEP_QNAME", + "module {0} cannot reference {2}, defined in " + + "module {1}"); + + /** + * Creates a pass to check global name references at the given warning level. + */ + CheckGlobalNames(AbstractCompiler compiler, CheckLevel level) { + this.compiler = compiler; + this.convention = compiler.getCodingConvention(); + this.level = level; + } + + /** + * Injects a pre-computed global namespace, so that the same namespace + * can be re-used for multiple check passes. Returns this for easy chaining. + */ + CheckGlobalNames injectNamespace(GlobalNamespace namespace) { + Preconditions.checkArgument(namespace.hasExternsRoot()); + this.namespace = namespace; + return this; + } + + @Override + public void process(Node externs, Node root) { + if (namespace == null) { + namespace = new GlobalNamespace(compiler, externs, root); + } + + // Find prototype properties that will affect our analysis. + Preconditions.checkState(namespace.hasExternsRoot()); + findPrototypeProps("Object", objectPrototypeProps); + findPrototypeProps("Function", functionPrototypeProps); + objectPrototypeProps.addAll( + convention.getIndirectlyDeclaredProperties()); + + for (Name name : namespace.getNameForest()) { + // Skip extern names. Externs are often not runnable as real code, + // and will do things like: + // var x; + // x.method; + // which this check forbids. + if (name.inExterns) { + continue; + } + + checkDescendantNames(name, name.globalSets + name.localSets > 0); + } + } + + private void findPrototypeProps(String type, Set props) { + Name slot = namespace.getSlot(type); + if (slot != null) { + for (Ref ref : slot.getRefs()) { + if (ref.type == Ref.Type.PROTOTYPE_GET) { + Node fullName = ref.getNode().getParent().getParent(); + if (fullName.isGetProp()) { + props.add(fullName.getLastChild().getString()); + } + } + } + } + } + + /** + * Checks to make sure all the descendants of a name are defined if they + * are referenced. + * + * @param name A global name. + * @param nameIsDefined If true, {@code name} is defined. Otherwise, it's + * undefined, and any references to descendant names should emit warnings. + */ + private void checkDescendantNames(Name name, boolean nameIsDefined) { + if (name.props != null) { + for (Name prop : name.props) { + // if the ancestor of a property is not defined, then we should emit + // warnings for all references to the property. + boolean propIsDefined = false; + if (nameIsDefined) { + // if the ancestor of a property is defined, then let's check that + // the property is also explicitly defined if it needs to be. + propIsDefined = (!propertyMustBeInitializedByFullName(prop) || + prop.globalSets + prop.localSets > 0); + } + + validateName(prop, propIsDefined); + checkDescendantNames(prop, propIsDefined); + } + } + } + + private void validateName(Name name, boolean isDefined) { + // If the name is not defined, emit warnings for each reference. While + // we're looking through each reference, check all the module dependencies. + Ref declaration = name.getDeclaration(); + Name parent = name.parent; + + JSModuleGraph moduleGraph = compiler.getModuleGraph(); + for (Ref ref : name.getRefs()) { + // Don't worry about global exprs. + boolean isGlobalExpr = ref.getNode().getParent().isExprResult(); + + if (!isDefined && !isTypedef(ref)) { + if (!isGlobalExpr) { + reportRefToUndefinedName(name, ref); + } + } else if (declaration != null && + ref.getModule() != declaration.getModule() && + !moduleGraph.dependsOn( + ref.getModule(), declaration.getModule())) { + reportBadModuleReference(name, ref); + } else { + // Check for late references. + if (ref.scope.isGlobal()) { + // Prototype references are special, because in our reference graph, + // A.prototype counts as a reference to A. + boolean isPrototypeGet = (ref.type == Ref.Type.PROTOTYPE_GET); + Name owner = isPrototypeGet ? name : parent; + boolean singleGlobalParentDecl = + owner != null && + owner.getDeclaration() != null && + owner.localSets == 0; + + if (singleGlobalParentDecl && + owner.getDeclaration().preOrderIndex > ref.preOrderIndex) { + String refName = isPrototypeGet + ? name.getFullName() + ".prototype" + : name.getFullName(); + compiler.report( + JSError.make(ref.source.getName(), ref.node, + NAME_DEFINED_LATE_WARNING, + refName, + owner.getFullName(), + owner.getDeclaration().source.getName(), + String.valueOf(owner.getDeclaration().node.getLineno()))); + } + } + } + } + } + + private boolean isTypedef(Ref ref) { + // If this is an annotated EXPR-GET, don't do anything. + Node parent = ref.node.getParent(); + if (parent.isExprResult()) { + JSDocInfo info = ref.node.getJSDocInfo(); + if (info != null && info.hasTypedefType()) { + return true; + } + } + return false; + } + + private void reportBadModuleReference(Name name, Ref ref) { + compiler.report( + JSError.make(ref.source.getName(), ref.node, STRICT_MODULE_DEP_QNAME, + ref.getModule().getName(), + name.getDeclaration().getModule().getName(), + name.getFullName())); + } + + private void reportRefToUndefinedName(Name name, Ref ref) { + // grab the highest undefined ancestor to output in the warning message. + while (name.parent != null && + name.parent.globalSets + name.parent.localSets == 0) { + name = name.parent; + } + + compiler.report( + JSError.make(ref.getSourceName(), ref.node, level, + UNDEFINED_NAME_WARNING, name.getFullName())); + } + + /** + * Checks whether the given name is a property, and whether that property + * must be initialized with its full qualified name. + */ + private boolean propertyMustBeInitializedByFullName(Name name) { + // If an object or function literal in the global namespace is never + // aliased, then its properties can only come from one of 2 places: + // 1) From its prototype chain, or + // 2) From an assignment to its fully qualified name. + // If we assume #1 is not the case, then #2 implies that its + // properties must all be modeled in the GlobalNamespace as well. + // + // We assume that for global object literals and types (constructors and + // interfaces), we can find all the properties inherited from the prototype + // chain of functions and objects. + if (name.parent == null) { + return false; + } + + boolean parentIsAliased = false; + if (name.parent.aliasingGets > 0) { + for (Ref ref : name.parent.getRefs()) { + if (ref.type == Ref.Type.ALIASING_GET) { + Node aliaser = ref.getNode().getParent(); + + // We don't need to worry about known aliased, because + // they're already covered by the getIndirectlyDeclaredProperties + // call at the top. + boolean isKnownAlias = + aliaser.isCall() && + (convention.getClassesDefinedByCall(aliaser) != null || + convention.getSingletonGetterClassName(aliaser) != null); + if (!isKnownAlias) { + parentIsAliased = true; + } + } + } + } + + if (parentIsAliased) { + return false; + } + + if (objectPrototypeProps.contains(name.getBaseName())) { + return false; + } + + if (name.parent.type == Name.Type.OBJECTLIT) { + return true; + } + + if (name.parent.type == Name.Type.FUNCTION && + name.parent.isDeclaredType() && + !functionPrototypeProps.contains(name.getBaseName())) { + return true; + } + + return false; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckGlobalThis.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckGlobalThis.java new file mode 100644 index 0000000..3d8e0f1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckGlobalThis.java @@ -0,0 +1,201 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * Checks for certain uses of the {@code this} keyword that are considered + * unsafe because they are likely to reference the global {@code this} object + * unintentionally. + * + *

A use of {@code this} is considered unsafe if it's on the left side of an + * assignment or a property access, and not inside one of the following: + *

    + *
  1. a prototype method + *
  2. a function annotated with {@code @constructor} + *
  3. a function annotated with {@code @this}. + *
  4. a function where there's no logical place to put a + * {@code this} annotation. + *
+ * + *

Note that this check does not track assignments of {@code this} to + * variables or objects. The code + *

+ * function evil() {
+ *   var a = this;
+ *   a.useful = undefined;
+ * }
+ * 
+ * will not get flagged, even though it is semantically equivalent to + *
+ * function evil() {
+ *   this.useful = undefined;
+ * }
+ * 
+ * which would get flagged. + * + */ +final class CheckGlobalThis implements Callback { + + static final DiagnosticType GLOBAL_THIS = DiagnosticType.warning( + "JSC_USED_GLOBAL_THIS", + "dangerous use of the global 'this' object"); + + private final AbstractCompiler compiler; + + /** + * If {@code assignLhsChild != null}, then the node being traversed is + * a descendant of the first child of an ASSIGN node. assignLhsChild's + * parent is this ASSIGN node. + */ + private Node assignLhsChild = null; + + CheckGlobalThis(AbstractCompiler compiler) { + this.compiler = compiler; + } + + /** + * Since this pass reports errors only when a global {@code this} keyword + * is encountered, there is no reason to traverse non global contexts. + */ + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + + if (n.isFunction()) { + // Don't traverse functions that are constructors or have the @this + // or @override annotation. + JSDocInfo jsDoc = getFunctionJsDocInfo(n); + if (jsDoc != null && + (jsDoc.isConstructor() || + jsDoc.isInterface() || + jsDoc.hasThisType() || + jsDoc.isOverride())) { + return false; + } + + // Don't traverse functions unless they would normally + // be able to have a @this annotation associated with them. e.g., + // var a = function() { }; // or + // function a() {} // or + // a.x = function() {}; // or + // var a = {x: function() {}}; + int pType = parent.getType(); + if (!(pType == Token.BLOCK || + pType == Token.SCRIPT || + pType == Token.NAME || + pType == Token.ASSIGN || + + // object literal keys + pType == Token.STRING_KEY)) { + return false; + } + + // Don't traverse functions that are getting lent to a prototype. + Node gramps = parent.getParent(); + if (NodeUtil.isObjectLitKey(parent, gramps)) { + JSDocInfo maybeLends = gramps.getJSDocInfo(); + if (maybeLends != null && + maybeLends.getLendsName() != null && + maybeLends.getLendsName().endsWith(".prototype")) { + return false; + } + } + } + + if (parent != null && parent.isAssign()) { + Node lhs = parent.getFirstChild(); + Node rhs = lhs.getNext(); + + if (n == lhs) { + // Always traverse the left side of the assignment. To handle + // nested assignments properly (e.g., (a = this).property = c;), + // assignLhsChild should not be overridden. + if (assignLhsChild == null) { + assignLhsChild = lhs; + } + } else { + // Only traverse the right side if it's not an assignment to a prototype + // property or subproperty. + if (NodeUtil.isGet(lhs)) { + if (lhs.isGetProp() && + lhs.getLastChild().getString().equals("prototype")) { + return false; + } + Node llhs = lhs.getFirstChild(); + if (llhs.isGetProp() && + llhs.getLastChild().getString().equals("prototype")) { + return false; + } + } + } + } + + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isThis() && shouldReportThis(n, parent)) { + compiler.report(t.makeError(n, GLOBAL_THIS)); + } + if (n == assignLhsChild) { + assignLhsChild = null; + } + } + + private boolean shouldReportThis(Node n, Node parent) { + if (assignLhsChild != null) { + // Always report a THIS on the left side of an assign. + return true; + } + + // Also report a THIS with a property access. + return parent != null && NodeUtil.isGet(parent); + } + + /** + * Gets a function's JSDoc information, if it has any. Checks for a few + * patterns (ellipses show where JSDoc would be): + *
+   * ... function() {}
+   * ... x = function() {};
+   * var ... x = function() {};
+   * ... var x = function() {};
+   * 
+ */ + private JSDocInfo getFunctionJsDocInfo(Node n) { + JSDocInfo jsDoc = n.getJSDocInfo(); + Node parent = n.getParent(); + if (jsDoc == null) { + int parentType = parent.getType(); + if (parentType == Token.NAME || parentType == Token.ASSIGN) { + jsDoc = parent.getJSDocInfo(); + if (jsDoc == null && parentType == Token.NAME) { + Node gramps = parent.getParent(); + if (gramps.isVar()) { + jsDoc = gramps.getJSDocInfo(); + } + } + } + } + return jsDoc; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckLevel.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckLevel.java new file mode 100644 index 0000000..c37287d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckLevel.java @@ -0,0 +1,32 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +/** + * Controls checking levels of certain options. For all checks going + * forward, this should be used instead of booleans, so teams and + * individuals can control which checks are off, which produce only warnings, + * and which produce errors, without everyone having to agree. + */ +public enum CheckLevel { + ERROR, + WARNING, + OFF; + + boolean isOn() { + return this != OFF; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckLevelLegacy.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckLevelLegacy.java new file mode 100644 index 0000000..cd544a3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckLevelLegacy.java @@ -0,0 +1,30 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Enum used in flags to control the behavior of JS compiler checks. These + * must be converted to CheckLevel enums before being used to control + * options. Only use this for legacy flags. For new flags, simply use + * {@link CheckLevel}. + */ +public enum CheckLevelLegacy { + LEGACY, + OFF, + WARNING, + ERROR, +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckMissingGetCssName.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckMissingGetCssName.java new file mode 100644 index 0000000..d0abc0d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckMissingGetCssName.java @@ -0,0 +1,122 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.Node; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Ensures string literals matching certain patterns are only used as + * goog.getCssName parameters. + * + * @author mkretzschmar@google.com (Martin Kretzschmar) + */ +class CheckMissingGetCssName + extends AbstractPostOrderCallback implements CompilerPass { + private final AbstractCompiler compiler; + private final CheckLevel level; + private final Matcher blacklist; + + static final String GET_CSS_NAME_FUNCTION = "goog.getCssName"; + static final String GET_UNIQUE_ID_FUNCTION = ".getUniqueId"; + + static final DiagnosticType MISSING_GETCSSNAME = + DiagnosticType.disabled( + "JSC_MISSING_GETCSSNAME", + "missing goog.getCssName around literal ''{0}''"); + + CheckMissingGetCssName(AbstractCompiler compiler, CheckLevel level, + String blacklistRegex) { + this.compiler = compiler; + this.level = level; + this.blacklist = + Pattern.compile("\\b(?:" + blacklistRegex + ")").matcher(""); + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isString() && + !parent.isGetProp() && + !parent.isRegExp()) { + String s = n.getString(); + + for (blacklist.reset(s); blacklist.find();) { + if (insideGetCssNameCall(n, parent)) { + continue; + } + if (insideGetUniqueIdCall(n, parent)) { + continue; + } + if (insideAssignmentToIdConstant(n, parent)) { + continue; + } + compiler.report(t.makeError(n, level, MISSING_GETCSSNAME, + blacklist.group())); + } + } + } + + /** Returns whether the node is an argument of a goog.getCssName call. */ + private boolean insideGetCssNameCall(Node n, Node parent) { + return parent.isCall() && + GET_CSS_NAME_FUNCTION.equals( + parent.getFirstChild().getQualifiedName()); + } + + /** + * Returns whether the node is an argument of a function that returns + * a unique id (the last part of the qualified name matches + * GET_UNIQUE_ID_FUNCTION). + */ + private boolean insideGetUniqueIdCall(Node n, Node parent) { + String name = parent.isCall() ? + parent.getFirstChild().getQualifiedName() : null; + + return name != null && name.endsWith(GET_UNIQUE_ID_FUNCTION); + } + + /** + * Returns whether the node is the right hand side of an assignment or + * initialization of a variable named *_ID of *_ID_. + */ + private boolean insideAssignmentToIdConstant(Node n, Node parent) { + if (parent.isAssign()) { + String qname = parent.getFirstChild().getQualifiedName(); + return qname != null && isIdName(qname); + } else if (parent.isName()) { + Node grandParent = parent.getParent(); + if (grandParent != null && grandParent.isVar()) { + String name = parent.getString(); + return isIdName(name); + } else { + return false; + } + } else { + return false; + } + } + + private boolean isIdName(String name) { + return name.endsWith("ID") || name.endsWith("ID_"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckMissingReturn.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckMissingReturn.java new file mode 100644 index 0000000..8210f98 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckMissingReturn.java @@ -0,0 +1,193 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Predicate; +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.TernaryValue; + +/** + * Checks functions for missing return statements. Return statements are only + * expected for functions with return type information. Functions with empty + * bodies are ignored. + * + */ +class CheckMissingReturn implements ScopedCallback { + + static final DiagnosticType MISSING_RETURN_STATEMENT = + DiagnosticType.warning( + "JSC_MISSING_RETURN_STATEMENT", + "Missing return statement. Function expected to return {0}."); + + private final AbstractCompiler compiler; + private final CheckLevel level; + + private static final Predicate IS_RETURN = new Predicate() { + @Override + public boolean apply(Node input) { + // Check for null because the control flow graph's implicit return node is + // represented by null, so this value might be input. + return input != null && input.isReturn(); + } + }; + + /* Skips all exception edges and impossible edges. */ + private static final Predicate> + GOES_THROUGH_TRUE_CONDITION_PREDICATE = + new Predicate>() { + @Override + public boolean apply(DiGraphEdge input) { + // First skill all exceptions. + Branch branch = input.getValue(); + if (branch == Branch.ON_EX) { + return false; + } else if (branch.isConditional()) { + Node condition = NodeUtil.getConditionExpression( + input.getSource().getValue()); + // TODO(user): We CAN make this bit smarter just looking at + // constants. We DO have a full blown ReverseAbstractInterupter and + // type system that can evaluate some impressions' boolean value but + // for now we will keep this pass lightweight. + if (condition != null) { + TernaryValue val = NodeUtil.getImpureBooleanValue(condition); + if (val != TernaryValue.UNKNOWN) { + return val.toBoolean(true) == (Branch.ON_TRUE == branch); + } + } + } + return true; + } + }; + + /** + * @param level level of severity to report when a missing return statement + * is discovered + */ + CheckMissingReturn(AbstractCompiler compiler, CheckLevel level) { + this.compiler = compiler; + this.level = level; + } + + @Override + public void enterScope(NodeTraversal t) { + JSType returnType = explicitReturnExpected(t.getScopeRoot()); + if (returnType == null) { + return; + } + + if (fastAllPathsReturnCheck(t.getControlFlowGraph())) { + return; + } + + CheckPathsBetweenNodes test = + new CheckPathsBetweenNodes( + t.getControlFlowGraph(), + t.getControlFlowGraph().getEntry(), + t.getControlFlowGraph().getImplicitReturn(), + IS_RETURN, GOES_THROUGH_TRUE_CONDITION_PREDICATE); + + if (!test.allPathsSatisfyPredicate()) { + compiler.report( + t.makeError(t.getScopeRoot(), level, + MISSING_RETURN_STATEMENT, returnType.toString())); + } + } + + /** + * Fast check to see if all execution paths contain a return statement. + * May spuriously report that a return statement is missing. + * + * @return true if all paths return, converse not necessarily true + */ + private static boolean fastAllPathsReturnCheck(ControlFlowGraph cfg) { + for (DiGraphEdge s : cfg.getImplicitReturn().getInEdges()) { + if (!s.getSource().getValue().isReturn()) { + return false; + } + } + return true; + } + + @Override + public void exitScope(NodeTraversal t) { + } + + @Override + public boolean shouldTraverse( + NodeTraversal nodeTraversal, Node n, Node parent) { + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + } + + /** + * Determines if the given scope should explicitly return. All functions + * with non-void or non-unknown return types must have explicit returns. + * @return If a return type is expected, returns it. Otherwise, returns null. + */ + private JSType explicitReturnExpected(Node scope) { + FunctionType scopeType = JSType.toMaybeFunctionType(scope.getJSType()); + + if (scopeType == null) { + return null; + } + + if (isEmptyFunction(scope)) { + return null; + } + + JSType returnType = scopeType.getReturnType(); + + if (returnType == null) { + return null; + } + + if (!isVoidOrUnknown(returnType)) { + return returnType; + } + + return null; + } + + + /** + * @return {@code true} if function represents a JavaScript function + * with an empty body + */ + private static boolean isEmptyFunction(Node function) { + return function.getChildCount() == 3 && + !function.getFirstChild().getNext().getNext().hasChildren(); + } + + /** + * @return {@code true} if returnType is void, unknown, or a union + * containing void or unknown + */ + private boolean isVoidOrUnknown(JSType returnType) { + final JSType voidType = + compiler.getTypeRegistry().getNativeType(JSTypeNative.VOID_TYPE); + return voidType.isSubtype(returnType); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckPathsBetweenNodes.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckPathsBetweenNodes.java new file mode 100644 index 0000000..5b996f9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckPathsBetweenNodes.java @@ -0,0 +1,230 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Predicate; +import com.google.javascript.jscomp.graph.Annotation; +import com.google.javascript.jscomp.graph.DiGraph; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; + +/** + * See constructor, {@link #CheckPathsBetweenNodes(DiGraph, + * DiGraphNode, DiGraphNode, Predicate, Predicate)}, for a + * description of this algorithm. + * + * + * @param The node type. + * @param The edge type. + */ +class CheckPathsBetweenNodes { + + private final Predicate nodePredicate; + private final Predicate> edgePredicate; + private final boolean inclusive; + + // This algorithm works in two stages. First, the depth-first search (DFS) + // tree is calculated with A as the root. During when constructing the DFS + // tree, back edges are recorded. A back edge is a non-tree edge (X -> Y) + // where X is an descendant of Y in the DFS tree. The second step does a + // recursive traversal of the graph. Back edges are ignored during the + // recursive traversal, thus no cycles are encountered. Any recursive branch + // that encounters B without yet satisfying the predicate represents a path + // from the entry node to the exit without any nodes that satisfy the + // predicate. + // + // The implementation of discoverBackEdges follows the DFS-Visit algorithm in + // "Introduction to Algorithms" by Cormen, Leiseron, Rivest, and Stein, 2nd + // ed., on page 541. The calculation of back edges is described on page 546. + + // A non-tree edge in the DFS that connects a node to one of its ancestors. + private static final Annotation BACK_EDGE = new Annotation() {}; + private static final Annotation VISITED_EDGE = new Annotation() {}; + + // Not yet visited. + private static final Annotation WHITE = null; + // Being visited. + private static final Annotation GRAY = new Annotation() {}; + // Finished visiting. + private static final Annotation BLACK = new Annotation() {}; + + private final DiGraph graph; + private final DiGraphNode start; + private final DiGraphNode end; + + /** + * Given a graph G with nodes A and B, this algorithm determines if all paths + * from A to B contain at least one node satisfying a given predicate. + * + * Note that nodePredicate is not necessarily called for all nodes in G nor is + * edgePredicate called for all edges in G. + * + * @param graph Graph G to analyze. + * @param a The node A. + * @param b The node B. + * @param nodePredicate Predicate which at least one node on each path from an + * A node to B (inclusive) must match. + * @param edgePredicate Edges to consider as part of the graph. Edges in + * graph that don't match edgePredicate will be ignored. + * @param inclusive Includes node A and B in the test for the node predicate. + */ + CheckPathsBetweenNodes(DiGraph graph, DiGraphNode a, + DiGraphNode b, Predicate nodePredicate, + Predicate> edgePredicate, boolean inclusive) { + this.graph = graph; + this.start = a; + this.end = b; + this.nodePredicate = nodePredicate; + this.edgePredicate = edgePredicate; + this.inclusive = inclusive; + } + + /** + * Inclusive check. + */ + CheckPathsBetweenNodes(DiGraph graph, DiGraphNode a, + DiGraphNode b, Predicate nodePredicate, + Predicate> edgePredicate) { + this(graph, a, b, nodePredicate, edgePredicate, true); + } + + + /** + * @return true iff all paths contain at least one node that satisfy the + * predicate + */ + public boolean allPathsSatisfyPredicate() { + setUp(); + boolean result = checkAllPathsWithoutBackEdges(start, end); + tearDown(); + return result; + } + + /** + * @return true iff some paths contain at least one node that satisfy the + * predicate + */ + public boolean somePathsSatisfyPredicate() { + setUp(); + boolean result = checkSomePathsWithoutBackEdges(start, end); + tearDown(); + return result; + } + + private void setUp() { + graph.pushNodeAnnotations(); + graph.pushEdgeAnnotations(); + discoverBackEdges(this.start); + } + + private void tearDown() { + graph.popNodeAnnotations(); + graph.popEdgeAnnotations(); + } + + private void discoverBackEdges(DiGraphNode u) { + u.setAnnotation(GRAY); + for (DiGraphEdge e : u.getOutEdges()) { + if (ignoreEdge(e)) { + continue; + } + DiGraphNode v = e.getDestination(); + if (v.getAnnotation() == WHITE) { + discoverBackEdges(v); + } else if (v.getAnnotation() == GRAY) { + e.setAnnotation(BACK_EDGE); + } + } + u.setAnnotation(BLACK); + } + + private boolean ignoreEdge(DiGraphEdge e) { + return !edgePredicate.apply(e); + } + + /** + * Verify that all non-looping paths from {@code a} to {@code b} pass + * through at least one node where {@code nodePredicate} is true. + */ + private boolean checkAllPathsWithoutBackEdges(DiGraphNode a, + DiGraphNode b) { + if (nodePredicate.apply(a.getValue()) && + (inclusive || (a != start && a != end))) { + return true; + } + if (a == b) { + return false; + } + for (DiGraphEdge e : a.getOutEdges()) { + // Once we visited that edge once, we no longer need to + // re-visit it again. + if (e.getAnnotation() == VISITED_EDGE) { + continue; + } + e.setAnnotation(VISITED_EDGE); + + if (ignoreEdge(e)) { + continue; + } + if (e.getAnnotation() == BACK_EDGE) { + continue; + } + + DiGraphNode next = e.getDestination(); + if (!checkAllPathsWithoutBackEdges(next, b)) { + return false; + } + } + return true; + } + + /** + * Verify that some non-looping paths from {@code a} to {@code b} pass + * through at least one node where {@code nodePredicate} is true. + */ + private boolean checkSomePathsWithoutBackEdges(DiGraphNode a, + DiGraphNode b) { + if (nodePredicate.apply(a.getValue()) && + (inclusive || (a != start && a != end))) { + return true; + } + if (a == b) { + return false; + } + for (DiGraphEdge e : a.getOutEdges()) { + // Once we visited that edge once, we no longer need to + // re-visit it again. + if (e.getAnnotation() == VISITED_EDGE) { + continue; + } + e.setAnnotation(VISITED_EDGE); + + if (ignoreEdge(e)) { + continue; + } + if (e.getAnnotation() == BACK_EDGE) { + continue; + } + + DiGraphNode next = e.getDestination(); + if (checkSomePathsWithoutBackEdges(next, b)) { + return true; + } + } + return false; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckProvides.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckProvides.java new file mode 100644 index 0000000..0b42310 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckProvides.java @@ -0,0 +1,133 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.JSDocInfo.Visibility; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Map; + +/** + * Insures '@constructor X' has a 'goog.provide("X")' . + * + */ +class CheckProvides implements HotSwapCompilerPass { + private final AbstractCompiler compiler; + private final CheckLevel checkLevel; + private final CodingConvention codingConvention; + + static final DiagnosticType MISSING_PROVIDE_WARNING = DiagnosticType.disabled( + "JSC_MISSING_PROVIDE", + "missing goog.provide(''{0}'')"); + + CheckProvides(AbstractCompiler compiler, CheckLevel checkLevel) { + this.compiler = compiler; + this.checkLevel = checkLevel; + this.codingConvention = compiler.getCodingConvention(); + } + + @Override + public void process(Node externs, Node root) { + hotSwapScript(root, null); + } + + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + CheckProvidesCallback callback = + new CheckProvidesCallback(codingConvention); + new NodeTraversal(compiler, callback).traverse(scriptRoot); + } + + private class CheckProvidesCallback extends AbstractShallowCallback { + private final Map provides = Maps.newHashMap(); + private final Map ctors = Maps.newHashMap(); + private final CodingConvention convention; + + CheckProvidesCallback(CodingConvention convention){ + this.convention = convention; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.CALL: + String providedClassName = + codingConvention.extractClassNameIfProvide(n, parent); + if (providedClassName != null) { + provides.put(providedClassName, n); + } + break; + case Token.FUNCTION: + visitFunctionNode(n, parent); + break; + case Token.SCRIPT: + visitScriptNode(t, n); + } + } + + private void visitFunctionNode(Node n, Node parent) { + Node name = null; + JSDocInfo info = parent.getJSDocInfo(); + if (info != null && info.isConstructor()) { + name = parent.getFirstChild(); + } else { + // look to the child, maybe it's a named function + info = n.getJSDocInfo(); + if (info != null && info.isConstructor()) { + name = n.getFirstChild(); + } + } + if (name != null && name.isQualifiedName()) { + String qualifiedName = name.getQualifiedName(); + if (!this.convention.isPrivate(qualifiedName)) { + Visibility visibility = info.getVisibility(); + if (!visibility.equals(JSDocInfo.Visibility.PRIVATE)) { + ctors.put(qualifiedName, name); + } + } + } + } + + private void visitScriptNode(NodeTraversal t, Node n) { + for (Map.Entry ctorEntry : ctors.entrySet()) { + String ctor = ctorEntry.getKey(); + int index = -1; + boolean found = false; + do { + index = ctor.indexOf('.', index +1); + String provideKey = index == -1 ? ctor : ctor.substring(0, index); + if (provides.containsKey(provideKey)) { + found = true; + break; + } + } while (index != -1); + + if (!found) { + compiler.report( + t.makeError(ctorEntry.getValue(), checkLevel, + MISSING_PROVIDE_WARNING, ctorEntry.getKey())); + } + } + provides.clear(); + ctors.clear(); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckRegExp.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckRegExp.java new file mode 100644 index 0000000..3ff6cc0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckRegExp.java @@ -0,0 +1,83 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.regex.RegExpTree; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.Node; + +/** + * Look for references to the global RegExp object that would cause + * regular expressions to be unoptimizable, and checks that regular expressions + * are syntactically valid. + * + * @author johnlenz@google.com (John Lenz) + */ +class CheckRegExp extends AbstractPostOrderCallback implements CompilerPass { + + static final DiagnosticType REGEXP_REFERENCE = + DiagnosticType.warning("JSC_REGEXP_REFERENCE", + "References to the global RegExp object prevents " + + "optimization of regular expressions."); + static final DiagnosticType MALFORMED_REGEXP = DiagnosticType.warning( + "JSC_MALFORMED_REGEXP", + "Malformed Regular Expression: {0}"); + + private final AbstractCompiler compiler; + private boolean globalRegExpPropertiesUsed = false; + + public boolean isGlobalRegExpPropertiesUsed() { + return globalRegExpPropertiesUsed; + } + + public CheckRegExp(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (NodeUtil.isReferenceName(n)) { + String name = n.getString(); + if (name.equals("RegExp") && t.getScope().getVar(name) == null) { + int parentType = parent.getType(); + boolean first = (n == parent.getFirstChild()); + if (!((parentType == Token.NEW && first) + || (parentType == Token.CALL && first) + || (parentType == Token.INSTANCEOF && !first))) { + t.report(n, REGEXP_REFERENCE); + globalRegExpPropertiesUsed = true; + } + } + + // Check the syntax of regular expression patterns. + } else if (n.isRegExp()) { + String pattern = n.getFirstChild().getString(); + String flags = n.getChildCount() == 2 + ? n.getLastChild().getString() : ""; + try { + RegExpTree.parseRegExp(pattern, flags); + } catch (IllegalArgumentException ex) { + t.report(n, MALFORMED_REGEXP, ex.getMessage()); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckRequiresForConstructors.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckRequiresForConstructors.java new file mode 100644 index 0000000..097cb28 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckRequiresForConstructors.java @@ -0,0 +1,213 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.List; +import java.util.Set; + + +/** + * This pass walks the AST to create a Collection of 'new' nodes and + * 'goog.require' nodes. It reconciles these Collections, creating a + * warning for each discrepancy. + * + */ +class CheckRequiresForConstructors implements HotSwapCompilerPass { + private final AbstractCompiler compiler; + private final CodingConvention codingConvention; + private final CheckLevel level; + + // Warnings + static final DiagnosticType MISSING_REQUIRE_WARNING = DiagnosticType.disabled( + "JSC_MISSING_REQUIRE_WARNING", + "''{0}'' used but not goog.require''d"); + + CheckRequiresForConstructors(AbstractCompiler compiler, + CheckLevel level) { + this.compiler = compiler; + this.codingConvention = compiler.getCodingConvention(); + this.level = level; + } + + /** + * Uses Collections of new and goog.require nodes to create a compiler warning + * for each new class name without a corresponding goog.require(). + */ + @Override + public void process(Node externs, Node root) { + Callback callback = new CheckRequiresForConstructorsCallback(); + new NodeTraversal(compiler, callback).traverseRoots(externs, root); + } + + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + Callback callback = new CheckRequiresForConstructorsCallback(); + new NodeTraversal(compiler, callback).traverseWithScope(scriptRoot, + SyntacticScopeCreator.generateUntypedTopScope(compiler)); + } + + // Return true if the name is a class name (starts with an uppercase + // character, but is not in all caps). + private static boolean isClassName(String name) { + return (name != null && name.length() > 1 + && Character.isUpperCase(name.charAt(0)) + && !name.equals(name.toUpperCase())); + } + + // Return the shortest prefix of the className that refers to a class, + // or null if no part refers to a class. + private static String getOutermostClassName(String className) { + for (String part : className.split("\\.")) { + if (isClassName(part)) { + return className.substring(0, className.indexOf(part) + + part.length()); + } + } + + return null; + } + + /** + * This class "records" each constructor and goog.require visited and creates + * a warning for each new node without an appropriate goog.require node. + * + */ + private class CheckRequiresForConstructorsCallback implements Callback { + private final List constructors = Lists.newArrayList(); + private final List requires = Lists.newArrayList(); + private final List newNodes = Lists.newArrayList(); + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + return parent == null || !parent.isScript() || + !t.getInput().isExtern(); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + JSDocInfo info; + switch (n.getType()) { + case Token.ASSIGN: + info = (JSDocInfo) n.getProp(Node.JSDOC_INFO_PROP); + if (info != null && info.isConstructor()) { + String qualifiedName = n.getFirstChild().getQualifiedName(); + constructors.add(qualifiedName); + } + break; + case Token.FUNCTION: + if (NodeUtil.isFunctionExpression(n)) { + if (parent.isName()) { + String functionName = parent.getString(); + info = (JSDocInfo) parent.getProp(Node.JSDOC_INFO_PROP); + if (info != null && info.isConstructor()) { + constructors.add(functionName); + } else { + Node gramps = parent.getParent(); + Preconditions.checkState( + gramps != null && gramps.isVar()); + info = (JSDocInfo) gramps.getProp(Node.JSDOC_INFO_PROP); + if (info != null && info.isConstructor()) { + constructors.add(functionName); + } + } + } + } else { + info = (JSDocInfo) n.getProp(Node.JSDOC_INFO_PROP); + if (info != null && info.isConstructor()) { + String functionName = n.getFirstChild().getString(); + constructors.add(functionName); + } + } + break; + case Token.CALL: + visitCallNode(n, parent); + break; + case Token.SCRIPT: + visitScriptNode(t); + break; + case Token.NEW: + visitNewNode(t, n); + } + } + + private void visitScriptNode(NodeTraversal t) { + Set classNames = Sets.newHashSet(); + for (Node node : newNodes) { + String className = node.getFirstChild().getQualifiedName(); + String outermostClassName = getOutermostClassName(className); + boolean notProvidedByConstructors = + (constructors == null || !constructors.contains(className)); + boolean notProvidedByRequires = + (requires == null || (!requires.contains(className) + && !requires.contains(outermostClassName))); + if (notProvidedByConstructors && notProvidedByRequires + && !classNames.contains(className)) { + compiler.report( + t.makeError(node, level, MISSING_REQUIRE_WARNING, className)); + classNames.add(className); + } + } + // for the next script, if there is one, we don't want the new, ctor, and + // require nodes to spill over. + this.newNodes.clear(); + this.requires.clear(); + this.constructors.clear(); + } + + private void visitCallNode(Node n, Node parent) { + String required = codingConvention.extractClassNameIfRequire(n, parent); + if (required != null) { + requires.add(required); + } + } + + private void visitNewNode(NodeTraversal t, Node n) { + Node qNameNode = n.getFirstChild(); + + // If the ctor is something other than a qualified name, ignore it. + if (!qNameNode.isQualifiedName()) { + return; + } + + // Grab the root ctor namespace. + Node nameNode = qNameNode; + for (; nameNode.hasChildren(); nameNode = nameNode.getFirstChild()) {} + + // We only consider programmer-defined constructors that are + // global variables, or are defined on global variables. + if (!nameNode.isName()) { + return; + } + + String name = nameNode.getString(); + Scope.Var var = t.getScope().getVar(name); + if (var == null || var.isLocal() || var.isExtern()) { + return; + } + newNodes.add(n); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckSideEffects.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckSideEffects.java new file mode 100644 index 0000000..7338770 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckSideEffects.java @@ -0,0 +1,196 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.JSDocInfoBuilder; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.List; + +/** + * Checks for non side effecting statements such as + *
+ * var s = "this string is "
+ *         "continued on the next line but you forgot the +";
+ * x == foo();  // should that be '='?
+ * foo();;  // probably just a stray-semicolon. Doesn't hurt to check though
+ * 

+ * and generates warnings. + * + */ +final class CheckSideEffects extends AbstractPostOrderCallback + implements HotSwapCompilerPass { + + static final DiagnosticType USELESS_CODE_ERROR = DiagnosticType.warning( + "JSC_USELESS_CODE", + "Suspicious code. {0}"); + + static final String PROTECTOR_FN = "JSCOMPILER_PRESERVE"; + + private final CheckLevel level; + + private final List problemNodes = Lists.newArrayList(); + + private final AbstractCompiler compiler; + + private final boolean protectSideEffectFreeCode; + + CheckSideEffects(AbstractCompiler compiler, CheckLevel level, + boolean protectSideEffectFreeCode) { + this.compiler = compiler; + this.level = level; + this.protectSideEffectFreeCode = protectSideEffectFreeCode; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + + // Code with hidden side-effect code is common, for example + // accessing "el.offsetWidth" forces a reflow in browsers, to allow this + // will still allowing local dead code removal in general, + // protect the "side-effect free" code in the source. + // + if (protectSideEffectFreeCode) { + protectSideEffects(); + } + } + + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + NodeTraversal.traverse(compiler, scriptRoot, this); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + // VOID nodes appear when there are extra semicolons at the BLOCK level. + // I've been unable to think of any cases where this indicates a bug, + // and apparently some people like keeping these semicolons around, + // so we'll allow it. + if (n.isEmpty() || + n.isComma()) { + return; + } + + if (parent == null) { + return; + } + + // Do not try to remove a block or an expr result. We already handle + // these cases when we visit the child, and the peephole passes will + // fix up the tree in more clever ways when these are removed. + if (n.isExprResult() || n.isBlock()) { + return; + } + + // This no-op statement was there so that JSDoc information could + // be attached to the name. This check should not complain about it. + if (n.isQualifiedName() && n.getJSDocInfo() != null) { + return; + } + + boolean isResultUsed = NodeUtil.isExpressionResultUsed(n); + boolean isSimpleOp = NodeUtil.isSimpleOperator(n); + if (!isResultUsed && + (isSimpleOp || !NodeUtil.mayHaveSideEffects(n, t.getCompiler()))) { + String msg = "This code lacks side-effects. Is there a bug?"; + if (n.isString()) { + msg = "Is there a missing '+' on the previous line?"; + } else if (isSimpleOp) { + msg = "The result of the '" + Token.name(n.getType()).toLowerCase() + + "' operator is not being used."; + } + + t.getCompiler().report( + t.makeError(n, level, USELESS_CODE_ERROR, msg)); + // TODO(johnlenz): determine if it is necessary to + // try to protect side-effect free statements as well. + if (!NodeUtil.isStatement(n)) { + problemNodes.add(n); + } + } + } + + /** + * Protect side-effect free nodes by making them parameters + * to a extern function call. This call will be removed + * after all the optimizations passes have run. + */ + private void protectSideEffects() { + if (!problemNodes.isEmpty()) { + addExtern(); + for (Node n : problemNodes) { + Node name = IR.name(PROTECTOR_FN).srcref(n); + name.putBooleanProp(Node.IS_CONSTANT_NAME, true); + Node replacement = IR.call(name).srcref(n); + replacement.putBooleanProp(Node.FREE_CALL, true); + n.getParent().replaceChild(n, replacement); + replacement.addChildToBack(n); + } + compiler.reportCodeChange(); + } + } + + private void addExtern() { + Node name = IR.name(PROTECTOR_FN); + name.putBooleanProp(Node.IS_CONSTANT_NAME, true); + Node var = IR.var(name); + // Add "@noalias" so we can strip the method when AliasExternals is enabled. + JSDocInfoBuilder builder = new JSDocInfoBuilder(false); + builder.recordNoAlias(); + var.setJSDocInfo(builder.build(var)); + CompilerInput input = compiler.getSynthesizedExternsInput(); + input.getAstRoot(compiler).addChildrenToBack(var); + compiler.reportCodeChange(); + } + + /** + * Remove side-effect sync functions. + */ + static class StripProtection extends AbstractPostOrderCallback implements CompilerPass { + + private final AbstractCompiler compiler; + + StripProtection(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isCall()) { + Node target = n.getFirstChild(); + // TODO(johnlenz): add this to the coding convention + // so we can remove goog.reflect.sinkValue as well. + if (target.isName() && target.getString().equals(PROTECTOR_FN)) { + Node expr = n.getLastChild(); + n.detachChildren(); + parent.replaceChild(n, expr); + } + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckSuspiciousCode.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckSuspiciousCode.java new file mode 100644 index 0000000..115ec12 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckSuspiciousCode.java @@ -0,0 +1,108 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * Checks for common errors, such as misplaced semicolons: + *
+ * if (x); act_now();
+ * 
+ * or comparison against NaN: + *
+ * if (x === NaN) act();
+ * 
+ * and generates warnings. + * + * @author johnlenz@google.com (John Lenz) + */ +final class CheckSuspiciousCode extends AbstractPostOrderCallback { + + static final DiagnosticType SUSPICIOUS_SEMICOLON = DiagnosticType.warning( + "JSC_SUSPICIOUS_SEMICOLON", + "If this if/for/while really shouldn't have a body, use {}"); + + static final DiagnosticType SUSPICIOUS_COMPARISON_WITH_NAN = + DiagnosticType.warning( + "JSC_SUSPICIOUS_NAN", + "Comparison again NaN is always false. Did you mean isNaN()?"); + + CheckSuspiciousCode() { + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + checkMissingSemicolon(t, n); + checkNaN(t, n); + } + + private void checkMissingSemicolon(NodeTraversal t, Node n) { + switch (n.getType()) { + case Token.IF: + Node trueCase = n.getFirstChild().getNext(); + reportIfWasEmpty(t, trueCase); + Node elseCase = trueCase.getNext(); + if (elseCase != null) { + reportIfWasEmpty(t, elseCase); + } + break; + + case Token.WHILE: + case Token.FOR: + reportIfWasEmpty(t, NodeUtil.getLoopCodeBlock(n)); + break; + } + } + + private void reportIfWasEmpty(NodeTraversal t, Node block) { + Preconditions.checkState(block.isBlock()); + + // A semicolon is distinguished from a block without children by + // annotating it with EMPTY_BLOCK. Blocks without children are + // usually intentional, especially with loops. + if (!block.hasChildren() && block.wasEmptyNode()) { + t.getCompiler().report( + t.makeError(block, SUSPICIOUS_SEMICOLON)); + } + } + + private void checkNaN(NodeTraversal t, Node n) { + switch (n.getType()) { + case Token.EQ: + case Token.GE: + case Token.GT: + case Token.LE: + case Token.LT: + case Token.NE: + case Token.SHEQ: + case Token.SHNE: + reportIfNaN(t, n.getFirstChild()); + reportIfNaN(t, n.getLastChild()); + } + } + + private void reportIfNaN(NodeTraversal t, Node n) { + if (NodeUtil.isNaN(n)) { + t.getCompiler().report( + t.makeError(n.getParent(), SUSPICIOUS_COMPARISON_WITH_NAN)); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckUnreachableCode.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckUnreachableCode.java new file mode 100644 index 0000000..b788a3c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CheckUnreachableCode.java @@ -0,0 +1,113 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Predicate; +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.graph.GraphNode; +import com.google.javascript.jscomp.graph.GraphReachability; +import com.google.javascript.jscomp.graph.GraphReachability.EdgeTuple; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.TernaryValue; + +/** + * Use {@link ControlFlowGraph} and {@link GraphReachability} to inform user + * about unreachable code. + * + */ +class CheckUnreachableCode implements ScopedCallback { + + static final DiagnosticType UNREACHABLE_CODE = DiagnosticType.error( + "JSC_UNREACHABLE_CODE", "unreachable code"); + + private final AbstractCompiler compiler; + private final CheckLevel level; + + CheckUnreachableCode(AbstractCompiler compiler, CheckLevel level) { + this.compiler = compiler; + this.level = level; + } + + @Override + public void enterScope(NodeTraversal t) { + initScope(t.getControlFlowGraph()); + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + GraphNode gNode = t.getControlFlowGraph().getNode(n); + if (gNode != null && gNode.getAnnotation() != GraphReachability.REACHABLE) { + + // Only report error when there are some line number informations. + // There are synthetic nodes with no line number informations, nodes + // introduce by other passes (although not likely since this pass should + // be executed early) or some rhino bug. + if (n.getLineno() != -1 && + // Allow spurious semi-colons and spurious breaks. + !n.isEmpty() && !n.isBreak()) { + compiler.report(t.makeError(n, level, UNREACHABLE_CODE)); + // From now on, we are going to assume the user fixed the error and not + // give more warning related to code section reachable from this node. + new GraphReachability( + t.getControlFlowGraph()).recompute(n); + + // Saves time by not traversing children. + return false; + } + } + return true; + } + + private void initScope(ControlFlowGraph controlFlowGraph) { + new GraphReachability( + controlFlowGraph, new ReachablePredicate()).compute( + controlFlowGraph.getEntry().getValue()); + } + + @Override + public void exitScope(NodeTraversal t) { + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + } + + private final class ReachablePredicate implements + Predicate> { + + @Override + public boolean apply(EdgeTuple input) { + Branch branch = input.edge; + if (!branch.isConditional()) { + return true; + } + Node predecessor = input.sourceNode; + Node condition = NodeUtil.getConditionExpression(predecessor); + + // TODO(user): Handle more complicated expression like true == true, + // etc.... + if (condition != null) { + TernaryValue val = NodeUtil.getImpureBooleanValue(condition); + if (val != TernaryValue.UNKNOWN) { + return val.toBoolean(true) == (branch == Branch.ON_TRUE); + } + } + return true; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CleanupPasses.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CleanupPasses.java new file mode 100644 index 0000000..1e77d48 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CleanupPasses.java @@ -0,0 +1,135 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.DefaultPassConfig.HotSwapPassFactory; +import com.google.javascript.jscomp.GlobalVarReferenceMap.GlobalVarRefCleanupPass; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; + +import java.util.List; + + +/** + * Provides passes that should be run before hot-swap/incremental builds. + * + * @author tylerg@google.com (Tyler Goodwin) + */ +class CleanupPasses extends PassConfig { + + private State state; + + public CleanupPasses(CompilerOptions options) { + super(options); + } + + @Override + protected List getChecks() { + List checks = Lists.newArrayList(); + checks.add(fieldCleanupPassFactory); + checks.add(scopeCleanupPassFactory); + checks.add(globalVarRefCleanupPassFactory); + return checks; + } + + @Override + protected State getIntermediateState() { + return state; + } + + @Override + protected List getOptimizations() { + return ImmutableList.of(); + } + + @Override + protected void setIntermediateState(State state) { + this.state = state; + } + + final PassFactory fieldCleanupPassFactory = + new HotSwapPassFactory("FieldCleaupPassFactory", false) { + @Override + protected HotSwapCompilerPass create( + AbstractCompiler compiler) { + return new FieldCleanupPass(compiler); + } + }; + + final PassFactory scopeCleanupPassFactory = + new HotSwapPassFactory("ScopeCleanupPassFactory", false) { + @Override + protected HotSwapCompilerPass create( + AbstractCompiler compiler) { + return new MemoizedScopeCleanupPass(compiler); + } + }; + + final PassFactory globalVarRefCleanupPassFactory = + new HotSwapPassFactory("GlobalVarRefCleanupPassFactory", false) { + @Override + protected HotSwapCompilerPass create( + AbstractCompiler compiler) { + return new GlobalVarRefCleanupPass(compiler); + } + }; + + /** + * A CleanupPass implementation that will remove stored scopes from the + * MemoizedScopeCreator of the compiler instance for a the hot swapped script. + *

+ * This pass will also clear out Source Nodes of Function Types declared on + * Vars tracked by MemoizedScopeCreator + */ + static class MemoizedScopeCleanupPass implements HotSwapCompilerPass { + + private final AbstractCompiler compiler; + + public MemoizedScopeCleanupPass(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + ScopeCreator creator = compiler.getTypedScopeCreator(); + if (creator instanceof MemoizedScopeCreator) { + MemoizedScopeCreator scopeCreator = (MemoizedScopeCreator) creator; + String newSrc = scriptRoot.getSourceFileName(); + for (Var var : scopeCreator.getAllSymbols()) { + JSType type = var.getType(); + if (type != null) { + FunctionType fnType = type.toMaybeFunctionType(); + if (fnType != null + && newSrc.equals(NodeUtil.getSourceName(fnType.getSource()))) { + fnType.setSource(null); + } + } + } + scopeCreator.removeScopesForScript(originalRoot.getSourceFileName()); + } + } + + @Override + public void process(Node externs, Node root) { + // MemoizedScopeCleanupPass should not do work during process. + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ClosureCodeRemoval.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ClosureCodeRemoval.java new file mode 100644 index 0000000..4507c94 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ClosureCodeRemoval.java @@ -0,0 +1,229 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.rhino.Node; +import java.util.List; +import java.util.Set; + +/** + *

Compiler pass that removes Closure-specific code patterns.

+ * + *

Currently does the following:

+ * + *
    + *
  • Instead of setting abstract methods to a function that throws an + * informative error, this pass allows some binary size reduction by + * removing these methods altogether for production builds.
  • + *
  • Remove calls to assertion functions (like goog.asserts.assert). + * If the return value of the assertion function is used, then + * the first argument (the asserted value) will be directly inlined. + * Otherwise, the entire call will be removed. It is well-known that + * this is not provably safe, much like the equivalent assert + * statement in Java.
  • + *
+ * + * @author robbyw@google.com (Robby Walker) + */ +final class ClosureCodeRemoval implements CompilerPass { + + /** Reference to the JS compiler */ + private final AbstractCompiler compiler; + + /** Name used to denote an abstract function */ + static final String ABSTRACT_METHOD_NAME = "goog.abstractMethod"; + + private final boolean removeAbstractMethods; + private final boolean removeAssertionCalls; + + /** + * List of names referenced in successive generations of finding referenced + * nodes. + */ + private final List abstractMethodAssignmentNodes = + Lists.newArrayList(); + + /** + * List of assertion functions. + */ + private final List assertionCalls = Lists.newArrayList(); + + + /** + * Utility class to track a node and its parent. + */ + private class RemovableAssignment { + /** + * The node + */ + final Node node; + + /** + * Its parent + */ + final Node parent; + + /** + * Full chain of ASSIGN ancestors + */ + final List assignAncestors = Lists.newArrayList(); + + /** + * The last ancestor + */ + final Node lastAncestor; + + /** + * Data structure for information about a removable assignment. + * + * @param nameNode The LHS + * @param assignNode The parent ASSIGN node + * @param traversal Access to further levels, assumed to start at 1 + */ + public RemovableAssignment(Node nameNode, Node assignNode, + NodeTraversal traversal) { + this.node = nameNode; + this.parent = assignNode; + + Node ancestor = assignNode; + do { + ancestor = ancestor.getParent(); + assignAncestors.add(ancestor); + } while (ancestor.isAssign() && + ancestor.getFirstChild().isQualifiedName()); + lastAncestor = ancestor.getParent(); + } + + /** + * Remove this node. + */ + public void remove() { + Node rhs = node.getNext(); + Node last = parent; + for (Node ancestor : assignAncestors) { + if (ancestor.isExprResult()) { + lastAncestor.removeChild(ancestor); + } else { + rhs.detachFromParent(); + ancestor.replaceChild(last, rhs); + } + last = ancestor; + } + compiler.reportCodeChange(); + } + } + + /** + * Identifies all assignments of the abstract method to a variable. + */ + private class FindAbstractMethods extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isAssign()) { + Node nameNode = n.getFirstChild(); + Node valueNode = n.getLastChild(); + + if (nameNode.isQualifiedName() && + valueNode.isQualifiedName() && + ABSTRACT_METHOD_NAME.equals(valueNode.getQualifiedName())) { + abstractMethodAssignmentNodes.add(new RemovableAssignment( + n.getFirstChild(), n, t)); + } + } + } + } + + + /** + * Identifies all assertion calls. + */ + private class FindAssertionCalls extends AbstractPostOrderCallback { + Set assertionNames = Sets.newHashSet(); + + FindAssertionCalls() { + for (AssertionFunctionSpec spec : + compiler.getCodingConvention().getAssertionFunctions()) { + assertionNames.add(spec.getFunctionName()); + } + } + + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isCall()) { + String fnName = n.getFirstChild().getQualifiedName(); + if (assertionNames.contains(fnName)) { + assertionCalls.add(n); + } + } + } + } + + + /** + * Creates a Closure code remover. + * + * @param compiler The AbstractCompiler + * @param removeAbstractMethods Remove declarations of abstract methods. + * @param removeAssertionCalls Remove calls to goog.assert functions. + */ + ClosureCodeRemoval(AbstractCompiler compiler, boolean removeAbstractMethods, + boolean removeAssertionCalls) { + this.compiler = compiler; + this.removeAbstractMethods = removeAbstractMethods; + this.removeAssertionCalls = removeAssertionCalls; + } + + @Override + public void process(Node externs, Node root) { + List passes = Lists.newArrayList(); + if (removeAbstractMethods) { + passes.add(new FindAbstractMethods()); + } + if (removeAssertionCalls) { + passes.add(new FindAssertionCalls()); + } + CombinedCompilerPass.traverse(compiler, root, passes); + + for (RemovableAssignment assignment : abstractMethodAssignmentNodes) { + assignment.remove(); + } + + for (Node call : assertionCalls) { + // If the assertion is an expression, just strip the whole thing. + Node parent = call.getParent(); + if (parent.isExprResult()) { + parent.getParent().removeChild(parent); + } else { + // Otherwise, replace the assertion with its first argument, + // which is the return value of the assertion. + Node firstArg = call.getFirstChild().getNext(); + if (firstArg == null) { + parent.replaceChild(call, NodeUtil.newUndefinedNode(call)); + } else { + parent.replaceChild(call, firstArg.detachFromParent()); + } + } + compiler.reportCodeChange(); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ClosureCodingConvention.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ClosureCodingConvention.java new file mode 100644 index 0000000..f78afdb --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ClosureCodingConvention.java @@ -0,0 +1,470 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * This describes the Closure-specific JavaScript coding conventions. + * + */ +public class ClosureCodingConvention extends CodingConventions.Proxy { + + private static final long serialVersionUID = 1L; + + static final DiagnosticType OBJECTLIT_EXPECTED = DiagnosticType.warning( + "JSC_REFLECT_OBJECTLIT_EXPECTED", + "Object literal expected as second argument"); + + private final Set indirectlyDeclaredProperties; + + public ClosureCodingConvention() { + this(CodingConventions.getDefault()); + } + + public ClosureCodingConvention(CodingConvention wrapped) { + super(wrapped); + + Set props = Sets.newHashSet( + "superClass_", + "instance_", + "getInstance"); + props.addAll(wrapped.getIndirectlyDeclaredProperties()); + indirectlyDeclaredProperties = ImmutableSet.copyOf(props); + } + + /** + * Closure's goog.inherits adds a {@code superClass_} property to the + * subclass, and a {@code constructor} property. + */ + @Override + public void applySubclassRelationship(FunctionType parentCtor, + FunctionType childCtor, SubclassType type) { + super.applySubclassRelationship(parentCtor, childCtor, type); + if (type == SubclassType.INHERITS) { + childCtor.defineDeclaredProperty("superClass_", + parentCtor.getPrototype(), childCtor.getSource()); + childCtor.getPrototype().defineDeclaredProperty("constructor", + // Notice that constructor functions do not need to be covariant + // on the superclass. + // So if G extends F, new G() and new F() can accept completely + // different argument types, but G.prototype.constructor needs + // to be covariant on F.prototype.constructor. + // To get around this, we just turn off type-checking on arguments + // and return types of G.prototype.constructor. + childCtor.cloneWithoutArrowType(), + childCtor.getSource()); + } + } + + /** + * {@inheritDoc} + * + *

Understands several different inheritance patterns that occur in + * Google code (various uses of {@code inherits} and {@code mixin}). + */ + @Override + public SubclassRelationship getClassesDefinedByCall(Node callNode) { + SubclassRelationship relationship = + super.getClassesDefinedByCall(callNode); + if (relationship != null) return relationship; + + Node callName = callNode.getFirstChild(); + SubclassType type = typeofClassDefiningName(callName); + if (type != null) { + Node subclass = null; + Node superclass = callNode.getLastChild(); + + // There are six possible syntaxes for a class-defining method: + // SubClass.inherits(SuperClass) + // goog.inherits(SubClass, SuperClass) + // goog$inherits(SubClass, SuperClass) + // SubClass.mixin(SuperClass.prototype) + // goog.mixin(SubClass.prototype, SuperClass.prototype) + // goog$mixin(SubClass.prototype, SuperClass.prototype) + boolean isDeprecatedCall = callNode.getChildCount() == 2 && + callName.isGetProp(); + if (isDeprecatedCall) { + // SubClass.inherits(SuperClass) + subclass = callName.getFirstChild(); + } else if (callNode.getChildCount() == 3) { + // goog.inherits(SubClass, SuperClass) + subclass = callName.getNext(); + } else { + return null; + } + + if (type == SubclassType.MIXIN) { + // Only consider mixins that mix two prototypes as related to + // inheritance. + if (!endsWithPrototype(superclass)) { + return null; + } + if (!isDeprecatedCall) { + if (!endsWithPrototype(subclass)) { + return null; + } + // Strip off the prototype from the name. + subclass = subclass.getFirstChild(); + } + superclass = superclass.getFirstChild(); + } + + // bail out if either of the side of the "inherits" + // isn't a real class name. This prevents us from + // doing something weird in cases like: + // goog.inherits(MySubClass, cond ? SuperClass1 : BaseClass2) + if (subclass != null && + subclass.isUnscopedQualifiedName() && + superclass.isUnscopedQualifiedName()) { + return new SubclassRelationship(type, subclass, superclass); + } + } + + return null; + } + + /** + * Determines whether the given node is a class-defining name, like + * "inherits" or "mixin." + * @return The type of class-defining name, or null. + */ + private SubclassType typeofClassDefiningName(Node callName) { + // Check if the method name matches one of the class-defining methods. + String methodName = null; + if (callName.isGetProp()) { + methodName = callName.getLastChild().getString(); + } else if (callName.isName()) { + String name = callName.getString(); + int dollarIndex = name.lastIndexOf('$'); + if (dollarIndex != -1) { + methodName = name.substring(dollarIndex + 1); + } + } + + if (methodName != null) { + if (methodName.equals("inherits")) { + return SubclassType.INHERITS; + } else if (methodName.equals("mixin")) { + return SubclassType.MIXIN; + } + } + return null; + } + + @Override + public boolean isSuperClassReference(String propertyName) { + return "superClass_".equals(propertyName) || + super.isSuperClassReference(propertyName); + } + + /** + * Given a qualified name node, returns whether "prototype" is at the end. + * For example: + * a.b.c => false + * a.b.c.prototype => true + */ + private boolean endsWithPrototype(Node qualifiedName) { + return qualifiedName.isGetProp() && + qualifiedName.getLastChild().getString().equals("prototype"); + } + + /** + * Extracts X from goog.provide('X'), if the applied Node is goog. + * + * @return The extracted class name, or null. + */ + @Override + public String extractClassNameIfProvide(Node node, Node parent){ + return extractClassNameIfGoog(node, parent, "goog.provide"); + } + + /** + * Extracts X from goog.require('X'), if the applied Node is goog. + * + * @return The extracted class name, or null. + */ + @Override + public String extractClassNameIfRequire(Node node, Node parent){ + return extractClassNameIfGoog(node, parent, "goog.require"); + } + + private static String extractClassNameIfGoog(Node node, Node parent, + String functionName){ + String className = null; + if (NodeUtil.isExprCall(parent)) { + Node callee = node.getFirstChild(); + if (callee != null && callee.isGetProp()) { + String qualifiedName = callee.getQualifiedName(); + if (functionName.equals(qualifiedName)) { + Node target = callee.getNext(); + if (target != null && target.isString()) { + className = target.getString(); + } + } + } + } + return className; + } + + /** + * Use closure's implementation. + * @return closure's function name for exporting properties. + */ + @Override + public String getExportPropertyFunction() { + return "goog.exportProperty"; + } + + /** + * Use closure's implementation. + * @return closure's function name for exporting symbols. + */ + @Override + public String getExportSymbolFunction() { + return "goog.exportSymbol"; + } + + @Override + public List identifyTypeDeclarationCall(Node n) { + Node callName = n.getFirstChild(); + if ("goog.addDependency".equals(callName.getQualifiedName()) && + n.getChildCount() >= 3) { + Node typeArray = callName.getNext().getNext(); + if (typeArray.isArrayLit()) { + List typeNames = Lists.newArrayList(); + for (Node name = typeArray.getFirstChild(); name != null; + name = name.getNext()) { + if (name.isString()) { + typeNames.add(name.getString()); + } + } + return typeNames; + } + } + return super.identifyTypeDeclarationCall(n); + } + + @Override + public String getAbstractMethodName() { + return "goog.abstractMethod"; + } + + @Override + public String getSingletonGetterClassName(Node callNode) { + Node callArg = callNode.getFirstChild(); + String callName = callArg.getQualifiedName(); + + // Use both the original name and the post-CollapseProperties name. + if (!("goog.addSingletonGetter".equals(callName) || + "goog$addSingletonGetter".equals(callName)) || + callNode.getChildCount() != 2) { + return super.getSingletonGetterClassName(callNode); + } + + return callArg.getNext().getQualifiedName(); + } + + @Override + public void applySingletonGetter(FunctionType functionType, + FunctionType getterType, ObjectType objectType) { + super.applySingletonGetter(functionType, getterType, objectType); + functionType.defineDeclaredProperty("getInstance", getterType, + functionType.getSource()); + functionType.defineDeclaredProperty("instance_", objectType, + functionType.getSource()); + } + + @Override + public String getGlobalObject() { + return "goog.global"; + } + + private final Set propertyTestFunctions = ImmutableSet.of( + "goog.isDef", "goog.isNull", "goog.isDefAndNotNull", + "goog.isString", "goog.isNumber", "goog.isBoolean", + "goog.isFunction", "goog.isArray", "goog.isObject"); + + @Override + public boolean isPropertyTestFunction(Node call) { + Preconditions.checkArgument(call.isCall()); + return propertyTestFunctions.contains( + call.getFirstChild().getQualifiedName()) || + super.isPropertyTestFunction(call); + } + + @Override + public ObjectLiteralCast getObjectLiteralCast(Node callNode) { + Preconditions.checkArgument(callNode.isCall()); + ObjectLiteralCast proxyCast = super.getObjectLiteralCast(callNode); + if (proxyCast != null) { + return proxyCast; + } + + Node callName = callNode.getFirstChild(); + if (!"goog.reflect.object".equals(callName.getQualifiedName()) || + callNode.getChildCount() != 3) { + return null; + } + + Node typeNode = callName.getNext(); + if (!typeNode.isQualifiedName()) { + return null; + } + + Node objectNode = typeNode.getNext(); + if (!objectNode.isObjectLit()) { + return new ObjectLiteralCast(null, null, OBJECTLIT_EXPECTED); + } + + return new ObjectLiteralCast( + typeNode.getQualifiedName(), typeNode.getNext(), null); + } + + @Override + public boolean isOptionalParameter(Node parameter) { + return false; + } + + @Override + public boolean isVarArgsParameter(Node parameter) { + return false; + } + + @Override + public boolean isPrivate(String name) { + return false; + } + + @Override + public Collection getAssertionFunctions() { + return ImmutableList.of( + new AssertionFunctionSpec("goog.asserts.assert"), + new AssertionFunctionSpec("goog.asserts.assertNumber", + JSTypeNative.NUMBER_TYPE), + new AssertionFunctionSpec("goog.asserts.assertString", + JSTypeNative.STRING_TYPE), + new AssertionFunctionSpec("goog.asserts.assertFunction", + JSTypeNative.FUNCTION_INSTANCE_TYPE), + new AssertionFunctionSpec("goog.asserts.assertObject", + JSTypeNative.OBJECT_TYPE), + new AssertionFunctionSpec("goog.asserts.assertArray", + JSTypeNative.ARRAY_TYPE), + new AssertInstanceofSpec("goog.asserts.assertInstanceof") + ); + } + + @Override + public Bind describeFunctionBind(Node n, boolean useTypeInfo) { + Bind result = super.describeFunctionBind(n, useTypeInfo); + if (result != null) { + return result; + } + + if (!n.isCall()) { + return null; + } + + Node callTarget = n.getFirstChild(); + String name = callTarget.getQualifiedName(); + if (name != null) { + if (name.equals("goog.bind") + || name.equals("goog$bind")) { + // goog.bind(fn, self, args...); + Node fn = callTarget.getNext(); + if (fn == null) { + return null; + } + Node thisValue = safeNext(fn); + Node parameters = safeNext(thisValue); + return new Bind(fn, thisValue, parameters); + } + + if (name.equals("goog.partial") || name.equals("goog$partial")) { + // goog.partial(fn, args...); + Node fn = callTarget.getNext(); + if (fn == null) { + return null; + } + Node thisValue = null; + Node parameters = safeNext(fn); + return new Bind(fn, thisValue, parameters); + } + } + + return null; + } + + @Override + public Collection getIndirectlyDeclaredProperties() { + return indirectlyDeclaredProperties; + } + + private Node safeNext(Node n) { + if (n != null) { + return n.getNext(); + } + return null; + } + + /** + * A function that will throw an exception when if the value is not + * an instanceof a specific type. + */ + public static class AssertInstanceofSpec extends AssertionFunctionSpec { + public AssertInstanceofSpec(String functionName) { + super(functionName, JSTypeNative.OBJECT_TYPE); + } + + /** + * Returns the type for a type assertion, or null if the function asserts + * that the node must not be null or undefined. + */ + @Override + public JSType getAssertedType(Node call, JSTypeRegistry registry) { + if (call.getChildCount() > 2) { + Node constructor = call.getFirstChild().getNext().getNext(); + if (constructor != null) { + JSType ownerType = constructor.getJSType(); + if (ownerType != null + && ownerType.isFunctionType() + && ownerType.isConstructor()) { + FunctionType functionType = ((FunctionType) ownerType); + return functionType.getInstanceType(); + } + } + } + return super.getAssertedType(call, registry); + } + } + + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ClosureOptimizePrimitives.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ClosureOptimizePrimitives.java new file mode 100644 index 0000000..86f89c3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ClosureOptimizePrimitives.java @@ -0,0 +1,123 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.List; + +/** + *

Compiler pass that converts all calls to: + * goog.object.create(key1, val1, key2, val2, ...) where all of the keys + * are literals into object literals.

+ * + * @author agrieve@google.com (Andrew Grieve) + */ +final class ClosureOptimizePrimitives implements CompilerPass { + + /** Reference to the JS compiler */ + private final AbstractCompiler compiler; + + /** + * Identifies all calls to goog.object.create. + */ + private class FindObjectCreateCalls extends AbstractPostOrderCallback { + List callNodes = Lists.newArrayList(); + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isCall()) { + String fnName = n.getFirstChild().getQualifiedName(); + if ("goog$object$create".equals(fnName) || + "goog.object.create".equals(fnName)) { + callNodes.add(n); + } + } + } + } + + /** + * @param compiler The AbstractCompiler + */ + ClosureOptimizePrimitives(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + FindObjectCreateCalls pass = new FindObjectCreateCalls(); + NodeTraversal.traverse(compiler, root, pass); + processObjectCreateCalls(pass.callNodes); + } + + /** + * Converts all of the given call nodes to object literals that are safe to + * do so. + */ + private void processObjectCreateCalls(List callNodes) { + for (Node callNode : callNodes) { + Node curParam = callNode.getFirstChild().getNext(); + if (canOptimizeObjectCreate(curParam)) { + Node objNode = IR.objectlit().srcref(callNode); + while (curParam != null) { + Node keyNode = curParam; + Node valueNode = curParam.getNext(); + curParam = valueNode.getNext(); + + callNode.removeChild(keyNode); + callNode.removeChild(valueNode); + + if (!keyNode.isString()) { + keyNode = IR.string(NodeUtil.getStringValue(keyNode)) + .srcref(keyNode); + } + keyNode.setType(Token.STRING_KEY); + keyNode.setQuotedString(); + objNode.addChildToBack(IR.propdef(keyNode, valueNode)); + } + callNode.getParent().replaceChild(callNode, objNode); + compiler.reportCodeChange(); + } + } + } + + /** + * Returns whether the given call to goog.object.create can be converted to an + * object literal. + */ + private boolean canOptimizeObjectCreate(Node firstParam) { + Node curParam = firstParam; + while (curParam != null) { + // All keys must be strings or numbers. + if (!curParam.isString() && !curParam.isNumber()) { + return false; + } + curParam = curParam.getNext(); + + // Check for an odd number of parameters. + if (curParam == null) { + return false; + } + curParam = curParam.getNext(); + } + return true; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ClosureRewriteClass.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ClosureRewriteClass.java new file mode 100644 index 0000000..4c1fcea --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ClosureRewriteClass.java @@ -0,0 +1,403 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.List; + +/** + * Rewrites "goog.defineClass" into a form that is suitable for + * type checking and dead code elimination. + * + * @author johnlenz@google.com (John Lenz) + */ +class ClosureRewriteClass extends AbstractPostOrderCallback + implements HotSwapCompilerPass { + + // Errors + static final DiagnosticType GOOG_CLASS_TARGET_INVALID = DiagnosticType.error( + "JSC_GOOG_CLASS_TARGET_INVALID", + "Unsupported class definition expression."); + + static final DiagnosticType GOOG_CLASS_SUPER_CLASS_NOT_VALID = DiagnosticType.error( + "JSC_GOOG_CLASS_SUPER_CLASS_NOT_VALID", + "The super class must be null or a valid name reference"); + + static final DiagnosticType GOOG_CLASS_DESCRIPTOR_NOT_VALID = DiagnosticType.error( + "JSC_GOOG_CLASS_DESCRIPTOR_NOT_VALID", + "The class descriptor must be an object literal"); + + static final DiagnosticType GOOG_CLASS_CONSTRUCTOR_MISING = DiagnosticType.error( + "JSC_GOOG_CLASS_CONSTRUCTOR_MISING", + "The constructor expression is missing for the class descriptor"); + + static final DiagnosticType GOOG_CLASS_STATICS_NOT_VALID = DiagnosticType.error( + "JSC_GOOG_CLASS_STATICS_NOT_VALID", + "The class statics descriptor must be an object or function literal"); + + static final DiagnosticType GOOG_CLASS_UNEXPECTED_PARAMS = DiagnosticType.error( + "JSC_GOOG_CLASS_UNEXPECTED_PARAMS", + "The class definition has too many arguments."); + + private final AbstractCompiler compiler; + + public ClosureRewriteClass(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + this.compiler.process(this); + + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isCall() && isGoogDefineClass(n)) { + if (!validateUsage(n)) { + compiler.report(JSError.make(n, GOOG_CLASS_TARGET_INVALID)); + } + } + maybeRewriteClassDefinition(n); + } + + private boolean validateUsage(Node n) { + // There are only three valid usage patterns for of goog.defineClass + // var ClassName = googDefineClass + // namespace.ClassName = googDefineClass + // and within an objectlit, used by the goog.defineClass. + Node parent = n.getParent(); + switch (parent.getType()) { + case Token.NAME: + return true; + case Token.ASSIGN: + return n == parent.getLastChild() && parent.getParent().isExprResult(); + case Token.STRING_KEY: + return isContainedInGoogDefineClass(parent); + } + return false; + } + + private boolean isContainedInGoogDefineClass(Node n) { + while (n != null) { + n = n.getParent(); + if (n.isCall()) { + if (isGoogDefineClass(n)) { + return true; + } + } else if (!n.isObjectLit() && !n.isStringKey()) { + break; + } + } + return false; + } + + private void maybeRewriteClassDefinition(Node n) { + if (n.isVar()) { + Node target = n.getFirstChild(); + Node value = target.getFirstChild(); + maybeRewriteClassDefinition(n, target, value); + } else if (NodeUtil.isExprAssign(n)) { + Node assign = n.getFirstChild(); + Node target = assign.getFirstChild(); + Node value = assign.getLastChild(); + maybeRewriteClassDefinition(n, target, value); + } + } + + private void maybeRewriteClassDefinition( + Node n, Node target, Node value) { + if (isGoogDefineClass(value)) { + if (!target.isQualifiedName()) { + compiler.report(JSError.make(n, GOOG_CLASS_TARGET_INVALID)); + } + ClassDefinition def = extractClassDefinition(target, value); + if (def != null) { + value.detachFromParent(); + target.detachFromParent(); + rewriteGoogDefineClass(n, def); + } + } + } + + private static class MemberDefinition { + final JSDocInfo info; + final Node name; + final Node value; + + MemberDefinition(JSDocInfo info, Node name, Node value) { + this.info = info; + this.name = name; + this.value = value; + } + } + + private final class ClassDefinition { + final Node name; + final Node superClass; + final MemberDefinition constructor; + final List staticProps; + final List props; + final Node classModifier; + + ClassDefinition( + Node name, + Node superClass, + MemberDefinition constructor, + List staticProps, + List props, + Node classModifier) { + this.name = name; + this.superClass = superClass; + this.constructor = constructor; + this.staticProps = staticProps; + this.props = props; + this.classModifier = classModifier; + } + } + + /** + * Validates the class definition and if valid, destructively extracts + * the class definition from the AST. + */ + private ClassDefinition extractClassDefinition( + Node targetName, Node callNode) { + + // name = goog.defineClass(superClass, {...}, [modifier, ...]) + Node superClass = NodeUtil.getArgumentForCallOrNew(callNode, 0); + if (superClass == null || + (!superClass.isNull() && !superClass.isQualifiedName())) { + compiler.report(JSError.make(callNode, GOOG_CLASS_SUPER_CLASS_NOT_VALID)); + return null; + } + if (NodeUtil.isNullOrUndefined(superClass)) { + superClass = null; + } + + Node description = NodeUtil.getArgumentForCallOrNew(callNode, 1); + if (description == null + || !description.isObjectLit() + || !validateObjLit(description)) { + // report bad class definition + compiler.report(JSError.make(callNode, GOOG_CLASS_DESCRIPTOR_NOT_VALID)); + return null; + } + + int paramCount = callNode.getChildCount() -1; + if (paramCount > 2) { + compiler.report(JSError.make(callNode, GOOG_CLASS_UNEXPECTED_PARAMS)); + return null; + } + + Node constructor = extractProperty(description, "constructor"); + if (constructor == null) { + // report missing constructor + compiler.report(JSError.make(description, GOOG_CLASS_CONSTRUCTOR_MISING)); + return null; + } + JSDocInfo info = NodeUtil.getBestJSDocInfo(constructor); + + Node classModifier = null; + Node statics = null; + Node staticsProp = extractProperty(description, "statics"); + if (staticsProp != null) { + if (staticsProp.isObjectLit() && validateObjLit(staticsProp)) { + statics = staticsProp; + } else if (staticsProp.isFunction()) { + classModifier = staticsProp; + } else { + compiler.report( + JSError.make(staticsProp, GOOG_CLASS_STATICS_NOT_VALID)); + return null; + } + } + + if (statics == null) { + statics = IR.objectlit(); + } + + // Ok, now rip apart the definition into its component pieces. + // Remove the "special" property key nodes. + maybeDetach(constructor.getParent()); + maybeDetach(statics.getParent()); + if (classModifier != null) { + maybeDetach(classModifier.getParent()); + } + ClassDefinition def = new ClassDefinition( + targetName, + maybeDetach(superClass), + new MemberDefinition(info, null, maybeDetach(constructor)), + objectLitToList(maybeDetach(statics)), + objectLitToList(description), + maybeDetach(classModifier)); + return def; + } + + private Node maybeDetach(Node node) { + if (node != null && node.getParent() != null) { + node.detachFromParent(); + } + return node; + } + + // Only unquoted plain properties are currently supported. + private boolean validateObjLit(Node objlit) { + for (Node key : objlit.children()) { + if (!key.isStringKey() || key.isQuotedString()) { + return false; + } + } + return true; + } + + /** + * @return The first property in the objlit that matches the key. + */ + private Node extractProperty(Node objlit, String keyName) { + for (Node keyNode : objlit.children()) { + if (keyNode.getString().equals(keyName)) { + return keyNode.isStringKey() ? keyNode.getFirstChild() : null; + } + } + return null; + } + + private List objectLitToList( + Node objlit) { + List result = Lists.newArrayList(); + for (Node keyNode : objlit.children()) { + result.add( + new MemberDefinition( + NodeUtil.getBestJSDocInfo(keyNode), + keyNode, + keyNode.removeFirstChild())); + } + objlit.detachChildren(); + return result; + } + + private void rewriteGoogDefineClass(Node exprRoot, ClassDefinition cls) { + + // For simplicity add everything into a block, before adding it to the AST. + Node block = IR.block(); + + if (exprRoot.isVar()) { + // example: var ctr = function(){} + block.addChildToBack( + IR.var( + cls.name.cloneTree(), cls.constructor.value) + .srcref(exprRoot).setJSDocInfo(cls.constructor.info)); + } else { + // example: ns.ctr = function(){} + block.addChildToBack( + fixupSrcref(IR.exprResult( + IR.assign( + cls.name.cloneTree(), cls.constructor.value) + .srcref(exprRoot).setJSDocInfo(cls.constructor.info) + .srcref(exprRoot))).setJSDocInfo(cls.constructor.info)); + } + + if (cls.superClass != null) { + // example: goog.inherits(ctr, superClass) + block.addChildToBack( + fixupSrcref(IR.exprResult( + IR.call( + NodeUtil.newQualifiedNameNode( + compiler.getCodingConvention(), "goog.inherits") + .srcrefTree(cls.superClass), + cls.name.cloneTree(), + cls.superClass.cloneTree()).srcref(cls.superClass)))); + } + + for (MemberDefinition def : cls.staticProps) { + // example: ctr.prop = value + block.addChildToBack( + fixupSrcref(IR.exprResult( + fixupSrcref(IR.assign( + IR.getprop(cls.name.cloneTree(), + IR.string(def.name.getString()).srcref(def.name)) + .srcref(def.name), + def.value)).setJSDocInfo(def.info)))); + // Handle inner class definitions. + maybeRewriteClassDefinition(block.getLastChild()); + } + + for (MemberDefinition def : cls.props) { + // example: ctr.prototype.prop = value + block.addChildToBack( + fixupSrcref(IR.exprResult( + fixupSrcref(IR.assign( + IR.getprop( + fixupSrcref(IR.getprop(cls.name.cloneTree(), + IR.string("prototype").srcref(def.name))), + IR.string(def.name.getString()).srcref(def.name)) + .srcref(def.name), + def.value)).setJSDocInfo(def.info)))); + // Handle inner class definitions. + maybeRewriteClassDefinition(block.getLastChild()); + } + + if (cls.classModifier != null) { + // example: modifier(ctr) + block.addChildToBack( + IR.exprResult( + fixupFreeCall( + IR.call( + cls.classModifier, + cls.name.cloneTree()) + .srcref(cls.classModifier))) + .srcref(cls.classModifier)); + } + + exprRoot.getParent().replaceChild(exprRoot, block); + compiler.reportCodeChange(); + } + + private Node fixupSrcref(Node node) { + node.srcref(node.getFirstChild()); + return node; + } + + private Node fixupFreeCall(Node call) { + Preconditions.checkState(call.isCall()); + call.putBooleanProp(Node.FREE_CALL, true); + return call; + } + + /** + * @return Whether the call represents a class definition. + */ + private boolean isGoogDefineClass(Node value) { + if (value != null && value.isCall()) { + String targetName = value.getFirstChild().getQualifiedName(); + return ("goog.defineClass".equals(targetName) || + "goog.labs.classdef.defineClass".equals(targetName)); + } + return false; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CoalesceVariableNames.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CoalesceVariableNames.java new file mode 100644 index 0000000..11bb585 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CoalesceVariableNames.java @@ -0,0 +1,444 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.ControlFlowGraph.AbstractCfgNodeTraversalCallback; +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.DataFlowAnalysis.FlowState; +import com.google.javascript.jscomp.LiveVariablesAnalysis.LiveVariableLattice; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; +import com.google.javascript.jscomp.graph.GraphColoring; +import com.google.javascript.jscomp.graph.GraphColoring.GreedyGraphColoring; +import com.google.javascript.jscomp.graph.GraphNode; +import com.google.javascript.jscomp.graph.LinkedUndirectedGraph; +import com.google.javascript.jscomp.graph.UndiGraph; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import java.util.Comparator; +import java.util.Deque; +import java.util.Iterator; +import java.util.Set; + +/** + * Reuse variable names if possible. + * + *

For example, from var x = 1; print(x); var y = 2; print(y); + * to var x = 1; print(x); x = 2; print(x). The benefits are + * slightly shorter code because of the removed var declaration, + * less unique variables in hope for better renaming, and finally better gzip + * compression. + * + *

The pass operates similar to a typical register allocator found in an + * optimizing compiler by first computing live ranges with + * {@link LiveVariablesAnalysis} and a variable interference graph. Then it uses + * graph coloring in {@link GraphColoring} to determine which two variables can + * be merge together safely. + * + */ +class CoalesceVariableNames extends AbstractPostOrderCallback implements + CompilerPass, ScopedCallback { + + private final AbstractCompiler compiler; + private final Deque> colorings; + private final boolean usePseudoNames; + + private static final Comparator coloringTieBreaker = + new Comparator() { + @Override + public int compare(Var v1, Var v2) { + return v1.index - v2.index; + } + }; + + /** + * @param usePseudoNames For debug purposes, when merging variable foo and bar + * to foo, rename both variable to foo_bar. + */ + CoalesceVariableNames(AbstractCompiler compiler, boolean usePseudoNames) { + Preconditions.checkState(!compiler.getLifeCycleStage().isNormalized()); + + this.compiler = compiler; + colorings = Lists.newLinkedList(); + this.usePseudoNames = usePseudoNames; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + } + + private static boolean shouldOptimizeScope(Scope scope) { + // TODO(user): We CAN do this in the global scope, just need to be + // careful when something is exported. Liveness uses bit-vector for live + // sets so I don't see compilation time will be a problem for running this + // pass in the global scope. + if (scope.isGlobal()) { + return false; + } + + if (LiveVariablesAnalysis.MAX_VARIABLES_TO_ANALYZE < + scope.getVarCount()) { + return false; + } + + return true; + } + + @Override + public void enterScope(NodeTraversal t) { + Scope scope = t.getScope(); + if (!shouldOptimizeScope(scope)) { + return; + } + + ControlFlowGraph cfg = t.getControlFlowGraph(); + LiveVariablesAnalysis liveness = + new LiveVariablesAnalysis(cfg, scope, compiler); + // If the function has exactly 2 params, mark them as escaped. This is + // a work-around for an IE bug where it throws an exception if you + // write to the parameters of the callback in a sort(). See: + // http://code.google.com/p/closure-compiler/issues/detail?id=58 + if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) { + liveness.markAllParametersEscaped(); + } + liveness.analyze(); + + UndiGraph interferenceGraph = + computeVariableNamesInterferenceGraph( + t, cfg, liveness.getEscapedLocals()); + + GraphColoring coloring = + new GreedyGraphColoring(interferenceGraph, + coloringTieBreaker); + + coloring.color(); + colorings.push(coloring); + } + + @Override + public void exitScope(NodeTraversal t) { + if (!shouldOptimizeScope(t.getScope())) { + return; + } + colorings.pop(); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (colorings.isEmpty() || !n.isName() || + parent.isFunction()) { + // Don't rename named functions. + return; + } + Var var = t.getScope().getVar(n.getString()); + GraphNode vNode = colorings.peek().getGraph().getNode(var); + if (vNode == null) { + // This is not a local. + return; + } + Var coalescedVar = colorings.peek().getPartitionSuperNode(var); + + if (!usePseudoNames) { + if (vNode.getValue().equals(coalescedVar)) { + // The coalesced name is itself, nothing to do. + return; + } + + // Rename. + n.setString(coalescedVar.name); + compiler.reportCodeChange(); + + if (parent.isVar()) { + removeVarDeclaration(n); + } + } else { + // This code block is slow but since usePseudoName is for debugging, + // we should not sacrifice performance for non-debugging compilation to + // make this fast. + String pseudoName = null; + Set allMergedNames = Sets.newTreeSet(); + for (Iterator i = t.getScope().getVars(); i.hasNext();) { + Var iVar = i.next(); + + // Look for all the variables that can be merged (in the graph by now) + // and it is merged with the current coalescedVar. + if (colorings.peek().getGraph().getNode(iVar) != null && + coalescedVar.equals(colorings.peek().getPartitionSuperNode(iVar))) { + allMergedNames.add(iVar.name); + } + } + + // Keep its original name. + if (allMergedNames.size() == 1) { + return; + } + + pseudoName = Joiner.on("_").join(allMergedNames); + + while (t.getScope().isDeclared(pseudoName, true)) { + pseudoName += "$"; + } + + n.setString(pseudoName); + compiler.reportCodeChange(); + + if (!vNode.getValue().equals(coalescedVar) && parent.isVar()) { + removeVarDeclaration(n); + } + } + } + + private UndiGraph computeVariableNamesInterferenceGraph( + NodeTraversal t, ControlFlowGraph cfg, Set escaped) { + UndiGraph interferenceGraph = + LinkedUndirectedGraph.create(); + Scope scope = t.getScope(); + + // First create a node for each non-escaped variable. + for (Iterator i = scope.getVars(); i.hasNext();) { + Var v = i.next(); + if (!escaped.contains(v)) { + + // TODO(user): In theory, we CAN coalesce function names just like + // any variables. Our Liveness analysis captures this just like it as + // described in the specification. However, we saw some zipped and + // and unzipped size increase after this. We are not totally sure why + // that is but, for now, we will respect the dead functions and not play + // around with it. + if (!v.getParentNode().isFunction()) { + interferenceGraph.createNode(v); + } + } + } + + // Go through each variable and try to connect them. + for (Iterator i1 = scope.getVars(); i1.hasNext();) { + Var v1 = i1.next(); + + NEXT_VAR_PAIR: + for (Iterator i2 = scope.getVars(); i2.hasNext();) { + Var v2 = i2.next(); + + // Skip duplicate pairs. + if (v1.index >= v2.index) { + continue; + } + + if (!interferenceGraph.hasNode(v1) || + !interferenceGraph.hasNode(v2)) { + // Skip nodes that were not added. They are globals and escaped + // locals. Also avoid merging a variable with itself. + continue NEXT_VAR_PAIR; + } + + if (v1.getParentNode().isParamList() && + v2.getParentNode().isParamList()) { + interferenceGraph.connectIfNotFound(v1, null, v2); + continue NEXT_VAR_PAIR; + } + + // Go through every CFG node in the program and look at + // this variable pair. If they are both live at the same + // time, add an edge between them and continue to the next pair. + NEXT_CROSS_CFG_NODE: + for (DiGraphNode cfgNode : cfg.getDirectedGraphNodes()) { + if (cfg.isImplicitReturn(cfgNode)) { + continue NEXT_CROSS_CFG_NODE; + } + + FlowState state = cfgNode.getAnnotation(); + // Check the live states and add edge when possible. + if ((state.getIn().isLive(v1) && state.getIn().isLive(v2)) || + (state.getOut().isLive(v1) && state.getOut().isLive(v2))) { + interferenceGraph.connectIfNotFound(v1, null, v2); + continue NEXT_VAR_PAIR; + } + } + + // v1 and v2 might not have an edge between them! woohoo. there's + // one last sanity check that we have to do: we have to check + // if there's a collision *within* the cfg node. + NEXT_INTRA_CFG_NODE: + for (DiGraphNode cfgNode : cfg.getDirectedGraphNodes()) { + if (cfg.isImplicitReturn(cfgNode)) { + continue NEXT_INTRA_CFG_NODE; + } + + FlowState state = cfgNode.getAnnotation(); + boolean v1OutLive = state.getOut().isLive(v1); + boolean v2OutLive = state.getOut().isLive(v2); + CombinedLiveRangeChecker checker = new CombinedLiveRangeChecker( + new LiveRangeChecker(v1, v2OutLive ? null : v2), + new LiveRangeChecker(v2, v1OutLive ? null : v1)); + NodeTraversal.traverse( + compiler, + cfgNode.getValue(), + checker); + if (checker.connectIfCrossed(interferenceGraph)) { + continue NEXT_VAR_PAIR; + } + } + } + } + return interferenceGraph; + } + + /** + * A simple wrapper calls to call two AbstractCfgNodeTraversalCallback + * callback during the same traversal. Both traversals must have the same + * "shouldTraverse" conditions. + */ + private static class CombinedLiveRangeChecker + extends AbstractCfgNodeTraversalCallback { + + private final LiveRangeChecker callback1; + private final LiveRangeChecker callback2; + + CombinedLiveRangeChecker( + LiveRangeChecker callback1, + LiveRangeChecker callback2) { + this.callback1 = callback1; + this.callback2 = callback2; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (LiveRangeChecker.shouldVisit(n)) { + callback1.visit(t, n, parent); + callback2.visit(t, n, parent); + } + } + + boolean connectIfCrossed(UndiGraph interferenceGraph) { + if (callback1.crossed || callback2.crossed) { + Var v1 = callback1.getDef(); + Var v2 = callback2.getDef(); + interferenceGraph.connectIfNotFound(v1, null, v2); + return true; + } + return false; + } + } + + /** + * Tries to remove variable declaration if the variable has been coalesced + * with another variable that has already been declared. + */ + private void removeVarDeclaration(Node name) { + Node var = name.getParent(); + Node parent = var.getParent(); + + // Special case when we are in FOR-IN loop. + if (NodeUtil.isForIn(parent)) { + var.removeChild(name); + parent.replaceChild(var, name); + } else if (var.hasOneChild()) { + // The removal is easy when there is only one variable in the VAR node. + if (name.hasChildren()) { + Node value = name.removeFirstChild(); + var.removeChild(name); + Node assign = IR.assign(name, value).srcref(name); + + // We don't need to wrapped it with EXPR node if it is within a FOR. + if (!parent.isFor()) { + assign = NodeUtil.newExpr(assign); + } + parent.replaceChild(var, assign); + + } else { + // In a FOR( ; ; ) node, we must replace it with an EMPTY or else it + // becomes a FOR-IN node. + NodeUtil.removeChild(parent, var); + } + } else { + if (!name.hasChildren()) { + var.removeChild(name); + } + // We are going to leave duplicated declaration otherwise. + } + } + + private static class LiveRangeChecker + extends AbstractCfgNodeTraversalCallback { + boolean defFound = false; + boolean crossed = false; + private final Var def; + private final Var use; + + public LiveRangeChecker(Var def, Var use) { + this.def = def; + this.use = use; + } + + Var getDef() { + return def; + } + + /** + * @return Whether any LiveRangeChecker would be interested in the node. + */ + public static boolean shouldVisit(Node n) { + return (n.isName() + || (n.hasChildren() && n.getFirstChild().isName())); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!defFound && isAssignTo(def, n, parent)) { + defFound = true; + } + + if (defFound && (use == null || isReadFrom(use, n))) { + crossed = true; + } + } + + private static boolean isAssignTo(Var var, Node n, Node parent) { + if (n.isName() && var.getName().equals(n.getString()) && + parent != null) { + if (parent.isParamList()) { + // In a function declaration, the formal parameters are assigned. + return true; + } else if (parent.isVar()) { + // If this is a VAR declaration, if the name node has a child, we are + // assigning to that name. + return n.hasChildren(); + } + return false; // Definitely a read. + } else { + // Lastly, any assignmentOP is also an assign. + Node name = n.getFirstChild(); + return name != null && name.isName() && + var.getName().equals(name.getString()) && + NodeUtil.isAssignmentOp(n); + } + } + + private static boolean isReadFrom(Var var, Node name) { + return name != null && name.isName() && + var.getName().equals(name.getString()) && + !NodeUtil.isVarOrSimpleAssignLhs(name, name.getParent()); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodeChangeHandler.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodeChangeHandler.java new file mode 100644 index 0000000..e113498 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodeChangeHandler.java @@ -0,0 +1,61 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * A simple listener for code change events. + * @author nicksantos@google.com (Nick Santos) + */ +interface CodeChangeHandler { + + // TODO(nicksantos): Add more to this interface, for more fine-grained + // change reporting. + + /** Report a change to the AST. */ + void reportChange(); + + /** + * A trivial change handler that just records whether the code + * has changed since the last reset. + */ + static final class RecentChange implements CodeChangeHandler { + private boolean hasChanged = false; + + @Override + public void reportChange() { + hasChanged = true; + } + + boolean hasCodeChanged() { + return hasChanged; + } + + void reset() { + hasChanged = false; + } + } + + /** + * A change handler that throws an exception if any changes are made. + */ + static final class ForbiddenChange implements CodeChangeHandler { + @Override + public void reportChange() { + throw new IllegalStateException("Code changes forbidden"); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodeConsumer.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodeConsumer.java new file mode 100644 index 0000000..551a0d9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodeConsumer.java @@ -0,0 +1,313 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + + +/** + * Abstracted consumer of the CodeGenerator output. + * + * @see CodeGenerator + * @see CodePrinter + * @see InlineCostEstimator + */ +abstract class CodeConsumer { + boolean statementNeedsEnded = false; + boolean statementStarted = false; + boolean sawFunction = false; + + /** + * Starts the source mapping for the given + * node at the current position. + */ + void startSourceMapping(Node node) { + } + + /** + * Finishes the source mapping for the given + * node at the current position. + */ + void endSourceMapping(Node node) { + } + + /** + * Provides a means of interrupting the CodeGenerator. Derived classes should + * return false to stop further processing. + */ + boolean continueProcessing() { + return true; + } + + /** + * Retrieve the last character of the last string sent to append. + */ + abstract char getLastChar(); + + void addIdentifier(String identifier) { + add(identifier); + } + + /** + * Appends a string to the code, keeping track of the current line length. + * + * NOTE: the string must be a complete token--partial strings or + * partial regexes will run the risk of being split across lines. + * + * Do not directly append newlines with this method. Instead use + * {@link #startNewLine}. + */ + abstract void append(String str); + + void appendBlockStart() { + append("{"); + } + + void appendBlockEnd() { + append("}"); + } + + void startNewLine() { + } + + void maybeLineBreak() { + maybeCutLine(); + } + + void maybeCutLine() { + } + + void endLine() { + } + + void notePreferredLineBreak() { + } + + void beginBlock() { + if (statementNeedsEnded) { + append(";"); + maybeLineBreak(); + } + appendBlockStart(); + + endLine(); + statementNeedsEnded = false; + } + + void endBlock() { + endBlock(false); + } + + void endBlock(boolean shouldEndLine) { + appendBlockEnd(); + if (shouldEndLine) { + endLine(); + } + statementNeedsEnded = false; + } + + void listSeparator() { + add(","); + maybeLineBreak(); + } + + /** + * Indicates the end of a statement and a ';' may need to be added. + * But we don't add it now, in case we're at the end of a block (in which + * case we don't have to add the ';'). + * See maybeEndStatement() + */ + void endStatement() { + endStatement(false); + } + + void endStatement(boolean needSemiColon) { + if (needSemiColon) { + append(";"); + maybeLineBreak(); + statementNeedsEnded = false; + } else if (statementStarted) { + statementNeedsEnded = true; + } + } + + /** + * This is to be called when we're in a statement. If the prev statement + * needs to be ended, add a ';'. + */ + void maybeEndStatement() { + // Add a ';' if we need to. + if (statementNeedsEnded) { + append(";"); + maybeLineBreak(); + endLine(); + statementNeedsEnded = false; + } + statementStarted = true; + } + + void endFunction() { + endFunction(false); + } + + void endFunction(boolean statementContext) { + sawFunction = true; + if (statementContext) { + endLine(); + } + } + + void beginCaseBody() { + append(":"); + } + + void endCaseBody() { + } + + void add(String newcode) { + maybeEndStatement(); + + if (newcode.length() == 0) { + return; + } + + char c = newcode.charAt(0); + if ((isWordChar(c) || c == '\\') && + isWordChar(getLastChar())) { + // need space to separate. This is not pretty printing. + // For example: "return foo;" + append(" "); + } else if (c == '/' && getLastChar() == '/') { + // Do not allow a forward slash to appear after a DIV. + // For example, + // REGEXP DIV REGEXP + // is valid and should print like + // / // / / + append(" "); + } + + append(newcode); + } + + void appendOp(String op, boolean binOp) { + append(op); + } + + void addOp(String op, boolean binOp) { + maybeEndStatement(); + + char first = op.charAt(0); + char prev = getLastChar(); + + if ((first == '+' || first == '-') && prev == first) { + // This is not pretty printing. This is to prevent misparsing of + // things like "x + ++y" or "x++ + ++y" + append(" "); + } else if (Character.isLetter(first) && + isWordChar(prev)) { + // Make sure there is a space after e.g. instanceof , typeof + append(" "); + } else if (prev == '-' && first == '>') { + // Make sure that we don't emit --> + append(" "); + } + + // Allow formatting around the operator. + appendOp(op, binOp); + + // Line breaking after an operator is always safe. Line breaking before an + // operator on the other hand is not. We only line break after a bin op + // because it looks strange. + if (binOp) { + maybeCutLine(); + } + } + + void addNumber(double x) { + // This is not pretty printing. This is to prevent misparsing of x- -4 as + // x--4 (which is a syntax error). + char prev = getLastChar(); + boolean negativeZero = isNegativeZero(x); + if ((x < 0 || negativeZero) && prev == '-') { + add(" "); + } + + if (negativeZero) { + addConstant("-0"); + } else if ((long) x == x) { + long value = (long) x; + long mantissa = value; + int exp = 0; + if (Math.abs(x) >= 100) { + while (mantissa / 10 * Math.pow(10, exp + 1) == value) { + mantissa /= 10; + exp++; + } + } + if (exp > 2) { + addConstant(Long.toString(mantissa) + "E" + Integer.toString(exp)); + } else { + long valueAbs = Math.abs(value); + if (Long.toHexString(valueAbs).length() + 2 < + Long.toString(valueAbs).length()) { + addConstant((value < 0 ? "-" : "") + "0x" + + Long.toHexString(valueAbs)); + } else { + addConstant(Long.toString(value)); + } + } + } else { + addConstant(String.valueOf(x).replace(".0E", "E")); + } + } + + void addConstant(String newcode) { + add(newcode); + } + + static boolean isNegativeZero(double x) { + return x == 0.0 && Math.copySign(1, x) == -1.0; + } + + static boolean isWordChar(char ch) { + return (ch == '_' || + ch == '$' || + Character.isLetterOrDigit(ch)); + } + + /** + * If the body of a for loop or the then clause of an if statement has + * a single statement, should it be wrapped in a block? Doing so can + * help when pretty-printing the code, and permits putting a debugging + * breakpoint on the statement inside the condition. + * + * @return {@boolean true} if such expressions should be wrapped + */ + boolean shouldPreserveExtraBlocks() { + return false; + } + + /** + * @return Whether the a line break can be added after the specified BLOCK. + */ + boolean breakAfterBlockFor(Node n, boolean statementContext) { + return statementContext; + } + + /** Called when we're at the end of a file. */ + void endFile() {} +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodeGenerator.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodeGenerator.java new file mode 100644 index 0000000..2270c17 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodeGenerator.java @@ -0,0 +1,1287 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.TokenStream; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.util.Map; + +/** + * CodeGenerator generates codes from a parse tree, sending it to the specified + * CodeConsumer. + * + */ +class CodeGenerator { + private static final String LT_ESCAPED = "\\x3c"; + private static final String GT_ESCAPED = "\\x3e"; + + // A memoizer for formatting strings as JS strings. + private final Map ESCAPED_JS_STRINGS = Maps.newHashMap(); + + private static final char[] HEX_CHARS + = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + private final CodeConsumer cc; + + private final CharsetEncoder outputCharsetEncoder; + + private final boolean preferSingleQuotes; + private final boolean trustedStrings; + + private CodeGenerator(CodeConsumer consumer) { + cc = consumer; + outputCharsetEncoder = null; + preferSingleQuotes = false; + trustedStrings = true; + } + + static CodeGenerator forCostEstimation(CodeConsumer consumer) { + return new CodeGenerator(consumer); + } + + CodeGenerator( + CodeConsumer consumer, + CompilerOptions options) { + cc = consumer; + + Charset outputCharset = options.getOutputCharset(); + if (outputCharset == null || outputCharset == Charsets.US_ASCII) { + // If we want our default (pretending to be UTF-8, but escaping anything + // outside of straight ASCII), then don't use the encoder, but + // just special-case the code. This keeps the normal path through + // the code identical to how it's been for years. + this.outputCharsetEncoder = null; + } else { + this.outputCharsetEncoder = outputCharset.newEncoder(); + } + this.preferSingleQuotes = options.preferSingleQuotes; + this.trustedStrings = options.trustedStrings; + } + + /** + * Insert a ECMASCRIPT 5 strict annotation. + */ + public void tagAsStrict() { + add("'use strict';"); + } + + void add(String str) { + cc.add(str); + } + + private void addIdentifier(String identifier) { + cc.addIdentifier(identifierEscape(identifier)); + } + + void add(Node n) { + add(n, Context.OTHER); + } + + void add(Node n, Context context) { + if (!cc.continueProcessing()) { + return; + } + + int type = n.getType(); + String opstr = NodeUtil.opToStr(type); + int childCount = n.getChildCount(); + Node first = n.getFirstChild(); + Node last = n.getLastChild(); + + // Handle all binary operators + if (opstr != null && first != last) { + Preconditions.checkState( + childCount == 2, + "Bad binary operator \"%s\": expected 2 arguments but got %s", + opstr, childCount); + int p = NodeUtil.precedence(type); + + // For right-hand-side of operations, only pass context if it's + // the IN_FOR_INIT_CLAUSE one. + Context rhsContext = getContextForNoInOperator(context); + + // Handle associativity. + // e.g. if the parse tree is a * (b * c), + // we can simply generate a * b * c. + if (last.getType() == type && + NodeUtil.isAssociative(type)) { + addExpr(first, p, context); + cc.addOp(opstr, true); + addExpr(last, p, rhsContext); + } else if (NodeUtil.isAssignmentOp(n) && NodeUtil.isAssignmentOp(last)) { + // Assignments are the only right-associative binary operators + addExpr(first, p, context); + cc.addOp(opstr, true); + addExpr(last, p, rhsContext); + } else { + unrollBinaryOperator(n, type, opstr, context, rhsContext, p, p + 1); + } + return; + } + + cc.startSourceMapping(n); + + switch (type) { + case Token.TRY: { + Preconditions.checkState(first.getNext().isBlock() && + !first.getNext().hasMoreThanOneChild()); + Preconditions.checkState(childCount >= 2 && childCount <= 3); + + add("try"); + add(first, Context.PRESERVE_BLOCK); + + // second child contains the catch block, or nothing if there + // isn't a catch block + Node catchblock = first.getNext().getFirstChild(); + if (catchblock != null) { + add(catchblock); + } + + if (childCount == 3) { + add("finally"); + add(last, Context.PRESERVE_BLOCK); + } + break; + } + + case Token.CATCH: + Preconditions.checkState(childCount == 2); + add("catch("); + add(first); + add(")"); + add(last, Context.PRESERVE_BLOCK); + break; + + case Token.THROW: + Preconditions.checkState(childCount == 1); + add("throw"); + add(first); + + // Must have a ';' after a throw statement, otherwise safari can't + // parse this. + cc.endStatement(true); + break; + + case Token.RETURN: + add("return"); + if (childCount == 1) { + add(first); + } else { + Preconditions.checkState(childCount == 0); + } + cc.endStatement(); + break; + + case Token.VAR: + if (first != null) { + add("var "); + addList(first, false, getContextForNoInOperator(context)); + } + break; + + case Token.LABEL_NAME: + Preconditions.checkState(!n.getString().isEmpty()); + addIdentifier(n.getString()); + break; + + case Token.NAME: + if (first == null || first.isEmpty()) { + addIdentifier(n.getString()); + } else { + Preconditions.checkState(childCount == 1); + addIdentifier(n.getString()); + cc.addOp("=", true); + if (first.isComma()) { + addExpr(first, NodeUtil.precedence(Token.ASSIGN), Context.OTHER); + } else { + // Add expression, consider nearby code at lowest level of + // precedence. + addExpr(first, 0, getContextForNoInOperator(context)); + } + } + break; + + case Token.ARRAYLIT: + add("["); + addArrayList(first); + add("]"); + break; + + case Token.PARAM_LIST: + add("("); + addList(first); + add(")"); + break; + + case Token.COMMA: + Preconditions.checkState(childCount == 2); + unrollBinaryOperator(n, Token.COMMA, ",", context, Context.OTHER, 0, 0); + break; + + case Token.NUMBER: + Preconditions.checkState(childCount == 0); + cc.addNumber(n.getDouble()); + break; + + case Token.TYPEOF: + case Token.VOID: + case Token.NOT: + case Token.BITNOT: + case Token.POS: { + // All of these unary operators are right-associative + Preconditions.checkState(childCount == 1); + cc.addOp(NodeUtil.opToStrNoFail(type), false); + addExpr(first, NodeUtil.precedence(type), Context.OTHER); + break; + } + + case Token.NEG: { + Preconditions.checkState(childCount == 1); + + // It's important to our sanity checker that the code + // we print produces the same AST as the code we parse back. + // NEG is a weird case because Rhino parses "- -2" as "2". + if (n.getFirstChild().isNumber()) { + cc.addNumber(-n.getFirstChild().getDouble()); + } else { + cc.addOp(NodeUtil.opToStrNoFail(type), false); + addExpr(first, NodeUtil.precedence(type), Context.OTHER); + } + + break; + } + + case Token.HOOK: { + Preconditions.checkState(childCount == 3); + int p = NodeUtil.precedence(type); + addExpr(first, p + 1, context); + cc.addOp("?", true); + addExpr(first.getNext(), 1, Context.OTHER); + cc.addOp(":", true); + addExpr(last, 1, Context.OTHER); + break; + } + + case Token.REGEXP: + if (!first.isString() || + !last.isString()) { + throw new Error("Expected children to be strings"); + } + + String regexp = regexpEscape(first.getString(), outputCharsetEncoder); + + // I only use one .add because whitespace matters + if (childCount == 2) { + add(regexp + last.getString()); + } else { + Preconditions.checkState(childCount == 1); + add(regexp); + } + break; + + case Token.FUNCTION: + if (n.getClass() != Node.class) { + throw new Error("Unexpected Node subclass."); + } + Preconditions.checkState(childCount == 3); + boolean funcNeedsParens = (context == Context.START_OF_EXPR); + if (funcNeedsParens) { + add("("); + } + + add("function"); + add(first); + + add(first.getNext()); + add(last, Context.PRESERVE_BLOCK); + cc.endFunction(context == Context.STATEMENT); + + if (funcNeedsParens) { + add(")"); + } + break; + + case Token.GETTER_DEF: + case Token.SETTER_DEF: + Preconditions.checkState(n.getParent().isObjectLit()); + Preconditions.checkState(childCount == 1); + Preconditions.checkState(first.isFunction()); + + // Get methods are unnamed + Preconditions.checkState(first.getFirstChild().getString().isEmpty()); + if (type == Token.GETTER_DEF) { + // Get methods have no parameters. + Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); + add("get "); + } else { + // Set methods have one parameter. + Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); + add("set "); + } + + // The name is on the GET or SET node. + String name = n.getString(); + Node fn = first; + Node parameters = fn.getChildAtIndex(1); + Node body = fn.getLastChild(); + + // Add the property name. + if (!n.isQuotedString() && + TokenStream.isJSIdentifier(name) && + // do not encode literally any non-literal characters that were + // Unicode escaped. + NodeUtil.isLatin(name)) { + add(name); + } else { + // Determine if the string is a simple number. + double d = getSimpleNumber(name); + if (!Double.isNaN(d)) { + cc.addNumber(d); + } else { + addJsString(n); + } + } + + add(parameters); + add(body, Context.PRESERVE_BLOCK); + break; + + case Token.SCRIPT: + case Token.BLOCK: { + if (n.getClass() != Node.class) { + throw new Error("Unexpected Node subclass."); + } + boolean preserveBlock = context == Context.PRESERVE_BLOCK; + if (preserveBlock) { + cc.beginBlock(); + } + + boolean preferLineBreaks = + type == Token.SCRIPT || + (type == Token.BLOCK && + !preserveBlock && + n.getParent() != null && + n.getParent().isScript()); + for (Node c = first; c != null; c = c.getNext()) { + add(c, Context.STATEMENT); + + // VAR doesn't include ';' since it gets used in expressions + if (c.isVar()) { + cc.endStatement(); + } + + if (c.isFunction()) { + cc.maybeLineBreak(); + } + + // Prefer to break lines in between top-level statements + // because top-level statements are more homogeneous. + if (preferLineBreaks) { + cc.notePreferredLineBreak(); + } + } + if (preserveBlock) { + cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); + } + break; + } + + case Token.FOR: + if (childCount == 4) { + add("for("); + if (first.isVar()) { + add(first, Context.IN_FOR_INIT_CLAUSE); + } else { + addExpr(first, 0, Context.IN_FOR_INIT_CLAUSE); + } + add(";"); + add(first.getNext()); + add(";"); + add(first.getNext().getNext()); + add(")"); + addNonEmptyStatement( + last, getContextForNonEmptyExpression(context), false); + } else { + Preconditions.checkState(childCount == 3); + add("for("); + add(first); + add("in"); + add(first.getNext()); + add(")"); + addNonEmptyStatement( + last, getContextForNonEmptyExpression(context), false); + } + break; + + case Token.DO: + Preconditions.checkState(childCount == 2); + add("do"); + addNonEmptyStatement(first, Context.OTHER, false); + add("while("); + add(last); + add(")"); + cc.endStatement(); + break; + + case Token.WHILE: + Preconditions.checkState(childCount == 2); + add("while("); + add(first); + add(")"); + addNonEmptyStatement( + last, getContextForNonEmptyExpression(context), false); + break; + + case Token.EMPTY: + Preconditions.checkState(childCount == 0); + break; + + case Token.GETPROP: { + Preconditions.checkState( + childCount == 2, + "Bad GETPROP: expected 2 children, but got %s", childCount); + Preconditions.checkState( + last.isString(), + "Bad GETPROP: RHS should be STRING"); + boolean needsParens = (first.isNumber()); + if (needsParens) { + add("("); + } + addExpr(first, NodeUtil.precedence(type), context); + if (needsParens) { + add(")"); + } + add("."); + addIdentifier(last.getString()); + break; + } + + case Token.GETELEM: + Preconditions.checkState( + childCount == 2, + "Bad GETELEM: expected 2 children but got %s", childCount); + addExpr(first, NodeUtil.precedence(type), context); + add("["); + add(first.getNext()); + add("]"); + break; + + case Token.WITH: + Preconditions.checkState(childCount == 2); + add("with("); + add(first); + add(")"); + addNonEmptyStatement( + last, getContextForNonEmptyExpression(context), false); + break; + + case Token.INC: + case Token.DEC: { + Preconditions.checkState(childCount == 1); + String o = type == Token.INC ? "++" : "--"; + int postProp = n.getIntProp(Node.INCRDECR_PROP); + // A non-zero post-prop value indicates a post inc/dec, default of zero + // is a pre-inc/dec. + if (postProp != 0) { + addExpr(first, NodeUtil.precedence(type), context); + cc.addOp(o, false); + } else { + cc.addOp(o, false); + add(first); + } + break; + } + + case Token.CALL: + // We have two special cases here: + // 1) If the left hand side of the call is a direct reference to eval, + // then it must have a DIRECT_EVAL annotation. If it does not, then + // that means it was originally an indirect call to eval, and that + // indirectness must be preserved. + // 2) If the left hand side of the call is a property reference, + // then the call must not a FREE_CALL annotation. If it does, then + // that means it was originally an call without an explicit this and + // that must be preserved. + if (isIndirectEval(first) + || n.getBooleanProp(Node.FREE_CALL) && NodeUtil.isGet(first)) { + add("(0,"); + addExpr(first, NodeUtil.precedence(Token.COMMA), Context.OTHER); + add(")"); + } else { + addExpr(first, NodeUtil.precedence(type), context); + } + add("("); + addList(first.getNext()); + add(")"); + break; + + case Token.IF: + boolean hasElse = childCount == 3; + boolean ambiguousElseClause = + context == Context.BEFORE_DANGLING_ELSE && !hasElse; + if (ambiguousElseClause) { + cc.beginBlock(); + } + + add("if("); + add(first); + add(")"); + + if (hasElse) { + addNonEmptyStatement( + first.getNext(), Context.BEFORE_DANGLING_ELSE, false); + add("else"); + addNonEmptyStatement( + last, getContextForNonEmptyExpression(context), false); + } else { + addNonEmptyStatement(first.getNext(), Context.OTHER, false); + Preconditions.checkState(childCount == 2); + } + + if (ambiguousElseClause) { + cc.endBlock(); + } + break; + + case Token.NULL: + Preconditions.checkState(childCount == 0); + cc.addConstant("null"); + break; + + case Token.THIS: + Preconditions.checkState(childCount == 0); + add("this"); + break; + + case Token.FALSE: + Preconditions.checkState(childCount == 0); + cc.addConstant("false"); + break; + + case Token.TRUE: + Preconditions.checkState(childCount == 0); + cc.addConstant("true"); + break; + + case Token.CONTINUE: + Preconditions.checkState(childCount <= 1); + add("continue"); + if (childCount == 1) { + if (!first.isLabelName()) { + throw new Error("Unexpected token type. Should be LABEL_NAME."); + } + add(" "); + add(first); + } + cc.endStatement(); + break; + + case Token.DEBUGGER: + Preconditions.checkState(childCount == 0); + add("debugger"); + cc.endStatement(); + break; + + case Token.BREAK: + Preconditions.checkState(childCount <= 1); + add("break"); + if (childCount == 1) { + if (!first.isLabelName()) { + throw new Error("Unexpected token type. Should be LABEL_NAME."); + } + add(" "); + add(first); + } + cc.endStatement(); + break; + + case Token.EXPR_RESULT: + Preconditions.checkState(childCount == 1); + add(first, Context.START_OF_EXPR); + cc.endStatement(); + break; + + case Token.NEW: + add("new "); + int precedence = NodeUtil.precedence(type); + + // If the first child contains a CALL, then claim higher precedence + // to force parentheses. Otherwise, when parsed, NEW will bind to the + // first viable parentheses (don't traverse into functions). + if (NodeUtil.containsType( + first, Token.CALL, NodeUtil.MATCH_NOT_FUNCTION)) { + precedence = NodeUtil.precedence(first.getType()) + 1; + } + addExpr(first, precedence, Context.OTHER); + + // '()' is optional when no arguments are present + Node next = first.getNext(); + if (next != null) { + add("("); + addList(next); + add(")"); + } + break; + + case Token.STRING_KEY: + Preconditions.checkState( + childCount == 1, "Object lit key must have 1 child"); + addJsString(n); + break; + + case Token.STRING: + Preconditions.checkState( + childCount == 0, "A string may not have children"); + addJsString(n); + break; + + case Token.DELPROP: + Preconditions.checkState(childCount == 1); + add("delete "); + add(first); + break; + + case Token.OBJECTLIT: { + boolean needsParens = (context == Context.START_OF_EXPR); + if (needsParens) { + add("("); + } + add("{"); + for (Node c = first; c != null; c = c.getNext()) { + if (c != first) { + cc.listSeparator(); + } + + if (c.isGetterDef() || c.isSetterDef()) { + add(c); + } else { + Preconditions.checkState(c.isStringKey()); + String key = c.getString(); + // Object literal property names don't have to be quoted if they + // are not JavaScript keywords + if (!c.isQuotedString() && + !TokenStream.isKeyword(key) && + TokenStream.isJSIdentifier(key) && + // do not encode literally any non-literal characters that + // were Unicode escaped. + NodeUtil.isLatin(key)) { + add(key); + } else { + // Determine if the string is a simple number. + double d = getSimpleNumber(key); + if (!Double.isNaN(d)) { + cc.addNumber(d); + } else { + addExpr(c, 1, Context.OTHER); + } + } + add(":"); + addExpr(c.getFirstChild(), 1, Context.OTHER); + } + } + add("}"); + if (needsParens) { + add(")"); + } + break; + } + + case Token.SWITCH: + add("switch("); + add(first); + add(")"); + cc.beginBlock(); + addAllSiblings(first.getNext()); + cc.endBlock(context == Context.STATEMENT); + break; + + case Token.CASE: + Preconditions.checkState(childCount == 2); + add("case "); + add(first); + addCaseBody(last); + break; + + case Token.DEFAULT_CASE: + Preconditions.checkState(childCount == 1); + add("default"); + addCaseBody(first); + break; + + case Token.LABEL: + Preconditions.checkState(childCount == 2); + if (!first.isLabelName()) { + throw new Error("Unexpected token type. Should be LABEL_NAME."); + } + add(first); + add(":"); + addNonEmptyStatement( + last, getContextForNonEmptyExpression(context), true); + break; + + case Token.CAST: + add("("); + add(first); + add(")"); + break; + + default: + throw new Error("Unknown type " + type + "\n" + n.toStringTree()); + } + + cc.endSourceMapping(n); + } + + /** + * We could use addList recursively here, but sometimes we produce + * very deeply nested operators and run out of stack space, so we + * just unroll the recursion when possible. + * + * We assume nodes are left-recursive. + */ + private void unrollBinaryOperator( + Node n, int op, String opStr, Context context, + Context rhsContext, int leftPrecedence, int rightPrecedence) { + Node firstNonOperator = n.getFirstChild(); + while (firstNonOperator.getType() == op) { + firstNonOperator = firstNonOperator.getFirstChild(); + } + + addExpr(firstNonOperator, leftPrecedence, context); + + Node current = firstNonOperator; + do { + current = current.getParent(); + cc.addOp(opStr, true); + addExpr(current.getFirstChild().getNext(), rightPrecedence, rhsContext); + } while (current != n); + } + + static boolean isSimpleNumber(String s) { + int len = s.length(); + for (int index = 0; index < len; index++) { + char c = s.charAt(index); + if (c < '0' || c > '9') { + return false; + } + } + return len > 0 && s.charAt(0) != '0'; + } + + static double getSimpleNumber(String s) { + if (isSimpleNumber(s)) { + try { + long l = Long.parseLong(s); + if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) { + return l; + } + } catch (NumberFormatException e) { + // The number was too long to parse. Fall through to NaN. + } + } + return Double.NaN; + } + + /** + * @return Whether the name is an indirect eval. + */ + private boolean isIndirectEval(Node n) { + return n.isName() && "eval".equals(n.getString()) && + !n.getBooleanProp(Node.DIRECT_EVAL); + } + + /** + * Adds a block or expression, substituting a VOID with an empty statement. + * This is used for "for (...);" and "if (...);" type statements. + * + * @param n The node to print. + * @param context The context to determine how the node should be printed. + */ + private void addNonEmptyStatement( + Node n, Context context, boolean allowNonBlockChild) { + Node nodeToProcess = n; + + if (!allowNonBlockChild && !n.isBlock()) { + throw new Error("Missing BLOCK child."); + } + + // Strip unneeded blocks, that is blocks with <2 children unless + // the CodePrinter specifically wants to keep them. + if (n.isBlock()) { + int count = getNonEmptyChildCount(n, 2); + if (count == 0) { + if (cc.shouldPreserveExtraBlocks()) { + cc.beginBlock(); + cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); + } else { + cc.endStatement(true); + } + return; + } + + if (count == 1) { + // Hack around a couple of browser bugs: + // Safari needs a block around function declarations. + // IE6/7 needs a block around DOs. + Node firstAndOnlyChild = getFirstNonEmptyChild(n); + boolean alwaysWrapInBlock = cc.shouldPreserveExtraBlocks(); + if (alwaysWrapInBlock || isOneExactlyFunctionOrDo(firstAndOnlyChild)) { + cc.beginBlock(); + add(firstAndOnlyChild, Context.STATEMENT); + cc.maybeLineBreak(); + cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); + return; + } else { + // Continue with the only child. + nodeToProcess = firstAndOnlyChild; + } + } + + if (count > 1) { + context = Context.PRESERVE_BLOCK; + } + } + + if (nodeToProcess.isEmpty()) { + cc.endStatement(true); + } else { + add(nodeToProcess, context); + + // VAR doesn't include ';' since it gets used in expressions - so any + // VAR in a statement context needs a call to endStatement() here. + if (nodeToProcess.isVar()) { + cc.endStatement(); + } + } + } + + /** + * @return Whether the Node is a DO or FUNCTION (with or without + * labels). + */ + private boolean isOneExactlyFunctionOrDo(Node n) { + if (n.isLabel()) { + Node labeledStatement = n.getLastChild(); + if (!labeledStatement.isBlock()) { + return isOneExactlyFunctionOrDo(labeledStatement); + } else { + // For labels with block children, we need to ensure that a + // labeled FUNCTION or DO isn't generated when extraneous BLOCKs + // are skipped. + if (getNonEmptyChildCount(n, 2) == 1) { + return isOneExactlyFunctionOrDo(getFirstNonEmptyChild(n)); + } else { + // Either a empty statement or an block with more than one child, + // way it isn't a FUNCTION or DO. + return false; + } + } + } else { + return (n.isFunction() || n.isDo()); + } + } + + private void addExpr(Node n, int minPrecedence, Context context) { + if ((NodeUtil.precedence(n.getType()) < minPrecedence) || + ((context == Context.IN_FOR_INIT_CLAUSE) && n.isIn())){ + add("("); + add(n, Context.OTHER); + add(")"); + } else { + add(n, context); + } + } + + void addList(Node firstInList) { + addList(firstInList, true, Context.OTHER); + } + + void addList(Node firstInList, boolean isArrayOrFunctionArgument) { + addList(firstInList, isArrayOrFunctionArgument, Context.OTHER); + } + + void addList(Node firstInList, boolean isArrayOrFunctionArgument, + Context lhsContext) { + for (Node n = firstInList; n != null; n = n.getNext()) { + boolean isFirst = n == firstInList; + if (isFirst) { + addExpr(n, isArrayOrFunctionArgument ? 1 : 0, lhsContext); + } else { + cc.listSeparator(); + addExpr(n, isArrayOrFunctionArgument ? 1 : 0, Context.OTHER); + } + } + } + + /** + * This function adds a comma-separated list as is specified by an ARRAYLIT + * node with the associated skipIndexes array. This is a space optimization + * since we avoid creating a whole Node object for each empty array literal + * slot. + * @param firstInList The first in the node list (chained through the next + * property). + */ + void addArrayList(Node firstInList) { + boolean lastWasEmpty = false; + for (Node n = firstInList; n != null; n = n.getNext()) { + if (n != firstInList) { + cc.listSeparator(); + } + addExpr(n, 1, Context.OTHER); + lastWasEmpty = n.isEmpty(); + } + + if (lastWasEmpty) { + cc.listSeparator(); + } + } + + void addCaseBody(Node caseBody) { + cc.beginCaseBody(); + add(caseBody); + cc.endCaseBody(); + } + + void addAllSiblings(Node n) { + for (Node c = n; c != null; c = c.getNext()) { + add(c); + } + } + + /** Outputs a JS string, using the optimal (single/double) quote character */ + private void addJsString(Node n) { + String s = n.getString(); + boolean useSlashV = n.getBooleanProp(Node.SLASH_V); + if (useSlashV) { + add(jsString(n.getString(), useSlashV)); + } else { + String cached = ESCAPED_JS_STRINGS.get(s); + if (cached == null) { + cached = jsString(n.getString(), useSlashV); + ESCAPED_JS_STRINGS.put(s, cached); + } + add(cached); + } + } + + private String jsString(String s, boolean useSlashV) { + int singleq = 0, doubleq = 0; + + // could count the quotes and pick the optimal quote character + for (int i = 0; i < s.length(); i++) { + switch (s.charAt(i)) { + case '"': doubleq++; break; + case '\'': singleq++; break; + } + } + + String doublequote, singlequote; + char quote; + if (preferSingleQuotes ? + (singleq <= doubleq) : (singleq < doubleq)) { + // more double quotes so enclose in single quotes. + quote = '\''; + doublequote = "\""; + singlequote = "\\\'"; + } else { + // more single quotes so escape the doubles + quote = '\"'; + doublequote = "\\\""; + singlequote = "\'"; + } + + return strEscape(s, quote, doublequote, singlequote, "\\\\", + outputCharsetEncoder, useSlashV, false); + } + + /** Escapes regular expression */ + String regexpEscape(String s, CharsetEncoder outputCharsetEncoder) { + return strEscape(s, '/', "\"", "'", "\\", outputCharsetEncoder, false, true); + } + + /** + * Escapes the given string to a double quoted (") JavaScript/JSON string + */ + String escapeToDoubleQuotedJsString(String s) { + return strEscape(s, '"', "\\\"", "\'", "\\\\", null, false, false); + } + + /* If the user doesn't want to specify an output charset encoder, assume + they want Latin/ASCII characters only. + */ + String regexpEscape(String s) { + return regexpEscape(s, null); + } + + /** Helper to escape JavaScript string as well as regular expression */ + private String strEscape( + String s, + char quote, + String doublequoteEscape, + String singlequoteEscape, + String backslashEscape, + CharsetEncoder outputCharsetEncoder, + boolean useSlashV, + boolean isRegexp) { + StringBuilder sb = new StringBuilder(s.length() + 2); + sb.append(quote); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + switch (c) { + case '\0': sb.append("\\x00"); break; + case '\u000B': + if (useSlashV) { + sb.append("\\v"); + } else { + sb.append("\\x0B"); + } + break; + // From the SingleEscapeCharacter grammar production. + case '\b': sb.append("\\b"); break; + case '\f': sb.append("\\f"); break; + case '\n': sb.append("\\n"); break; + case '\r': sb.append("\\r"); break; + case '\t': sb.append("\\t"); break; + case '\\': sb.append(backslashEscape); break; + case '\"': sb.append(doublequoteEscape); break; + case '\'': sb.append(singlequoteEscape); break; + + // From LineTerminators (ES5 Section 7.3, Table 3) + case '\u2028': sb.append("\\u2028"); break; + case '\u2029': sb.append("\\u2029"); break; + + case '=': + // '=' is a syntactically signficant regexp character. + if (trustedStrings || isRegexp) { + sb.append(c); + } else { + sb.append("\\x3d"); + } + break; + + case '&': + if (trustedStrings || isRegexp) { + sb.append(c); + } else { + sb.append("\\x26"); + } + break; + + case '>': + if (!trustedStrings && !isRegexp) { + sb.append(GT_ESCAPED); + break; + } + + // Break --> into --\> or ]]> into ]]\> + // + // This is just to prevent developers from shooting themselves in the + // foot, and does not provide the level of security that you get + // with trustedString == false. + if (i >= 2 && + ((s.charAt(i - 1) == '-' && s.charAt(i - 2) == '-') || + (s.charAt(i - 1) == ']' && s.charAt(i - 2) == ']'))) { + sb.append(GT_ESCAPED); + } else { + sb.append(c); + } + break; + case '<': + if (!trustedStrings && !isRegexp) { + sb.append(LT_ESCAPED); + break; + } + + // Break 0x1f && c < 0x7f) { + sb.append(c); + } else { + // Other characters can be misinterpreted by some JS parsers, + // or perhaps mangled by proxies along the way, + // so we play it safe and Unicode escape them. + appendHexJavaScriptRepresentation(sb, c); + } + } + } + } + sb.append(quote); + return sb.toString(); + } + + static String identifierEscape(String s) { + // First check if escaping is needed at all -- in most cases it isn't. + if (NodeUtil.isLatin(s)) { + return s; + } + + // Now going through the string to escape non-Latin characters if needed. + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + // Identifiers should always go to Latin1/ ASCII characters because + // different browser's rules for valid identifier characters are + // crazy. + if (c > 0x1F && c < 0x7F) { + sb.append(c); + } else { + appendHexJavaScriptRepresentation(sb, c); + } + } + return sb.toString(); + } + /** + * @param maxCount The maximum number of children to look for. + * @return The number of children of this node that are non empty up to + * maxCount. + */ + private static int getNonEmptyChildCount(Node n, int maxCount) { + int i = 0; + Node c = n.getFirstChild(); + for (; c != null && i < maxCount; c = c.getNext()) { + if (c.isBlock()) { + i += getNonEmptyChildCount(c, maxCount-i); + } else if (!c.isEmpty()) { + i++; + } + } + return i; + } + + /** Gets the first non-empty child of the given node. */ + private static Node getFirstNonEmptyChild(Node n) { + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + if (c.isBlock()) { + Node result = getFirstNonEmptyChild(c); + if (result != null) { + return result; + } + } else if (!c.isEmpty()) { + return c; + } + } + return null; + } + + // Information on the current context. Used for disambiguating special cases. + // For example, a "{" could indicate the start of an object literal or a + // block, depending on the current context. + enum Context { + STATEMENT, + BEFORE_DANGLING_ELSE, // a hack to resolve the else-clause ambiguity + START_OF_EXPR, + PRESERVE_BLOCK, + // Are we inside the init clause of a for loop? If so, the containing + // expression can't contain an in operator. Pass this context flag down + // until we reach expressions which no longer have the limitation. + IN_FOR_INIT_CLAUSE, + OTHER + } + + private Context getContextForNonEmptyExpression(Context currentContext) { + return currentContext == Context.BEFORE_DANGLING_ELSE ? + Context.BEFORE_DANGLING_ELSE : Context.OTHER; + } + + /** + * If we're in a IN_FOR_INIT_CLAUSE, we can't permit in operators in the + * expression. Pass on the IN_FOR_INIT_CLAUSE flag through subexpressions. + */ + private Context getContextForNoInOperator(Context context) { + return (context == Context.IN_FOR_INIT_CLAUSE + ? Context.IN_FOR_INIT_CLAUSE : Context.OTHER); + } + + /** + * @see #appendHexJavaScriptRepresentation(int, Appendable) + */ + private static void appendHexJavaScriptRepresentation( + StringBuilder sb, char c) { + try { + appendHexJavaScriptRepresentation(c, sb); + } catch (IOException ex) { + // StringBuilder does not throw IOException. + throw new RuntimeException(ex); + } + } + + /** + * Returns a JavaScript representation of the character in a hex escaped + * format. + * + * @param codePoint The code point to append. + * @param out The buffer to which the hex representation should be appended. + */ + private static void appendHexJavaScriptRepresentation( + int codePoint, Appendable out) + throws IOException { + if (Character.isSupplementaryCodePoint(codePoint)) { + // Handle supplementary Unicode values which are not representable in + // JavaScript. We deal with these by escaping them as two 4B sequences + // so that they will round-trip properly when sent from Java to JavaScript + // and back. + char[] surrogates = Character.toChars(codePoint); + appendHexJavaScriptRepresentation(surrogates[0], out); + appendHexJavaScriptRepresentation(surrogates[1], out); + return; + } + out.append("\\u") + .append(HEX_CHARS[(codePoint >>> 12) & 0xf]) + .append(HEX_CHARS[(codePoint >>> 8) & 0xf]) + .append(HEX_CHARS[(codePoint >>> 4) & 0xf]) + .append(HEX_CHARS[codePoint & 0xf]); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodePrinter.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodePrinter.java new file mode 100644 index 0000000..fd77e51 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodePrinter.java @@ -0,0 +1,683 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Throwables; +import com.google.debugging.sourcemap.FilePosition; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; + +/** + * CodePrinter prints out JS code in either pretty format or compact format. + * + * @see CodeGenerator + */ +class CodePrinter { + // The number of characters after which we insert a line break in the code + static final int DEFAULT_LINE_LENGTH_THRESHOLD = 500; + + + // There are two separate CodeConsumers, one for pretty-printing and + // another for compact printing. + + // There are two implementations because the CompactCodePrinter + // potentially has a very different implementation to the pretty + // version. + + private abstract static class MappedCodePrinter extends CodeConsumer { + final private Deque mappings; + final private List allMappings; + final private boolean createSrcMap; + final private SourceMap.DetailLevel sourceMapDetailLevel; + protected final StringBuilder code = new StringBuilder(1024); + protected final int lineLengthThreshold; + protected int lineLength = 0; + protected int lineIndex = 0; + + MappedCodePrinter( + int lineLengthThreshold, + boolean createSrcMap, + SourceMap.DetailLevel sourceMapDetailLevel) { + Preconditions.checkState(sourceMapDetailLevel != null); + this.lineLengthThreshold = lineLengthThreshold <= 0 ? Integer.MAX_VALUE : + lineLengthThreshold; + this.createSrcMap = createSrcMap; + this.sourceMapDetailLevel = sourceMapDetailLevel; + this.mappings = createSrcMap ? new ArrayDeque() : null; + this.allMappings = createSrcMap ? new ArrayList() : null; + } + + /** + * Maintains a mapping from a given node to the position + * in the source code at which its generated form was + * placed. This position is relative only to the current + * run of the CodeConsumer and will be normalized + * later on by the SourceMap. + * + * @see SourceMap + */ + private static class Mapping { + Node node; + FilePosition start; + FilePosition end; + } + + /** + * Starts the source mapping for the given + * node at the current position. + */ + @Override + void startSourceMapping(Node node) { + Preconditions.checkState(sourceMapDetailLevel != null); + Preconditions.checkState(node != null); + if (createSrcMap + && node.getSourceFileName() != null + && node.getLineno() > 0 + && sourceMapDetailLevel.apply(node)) { + int line = getCurrentLineIndex(); + int index = getCurrentCharIndex(); + Preconditions.checkState(line >= 0); + Mapping mapping = new Mapping(); + mapping.node = node; + mapping.start = new FilePosition(line, index); + mappings.push(mapping); + allMappings.add(mapping); + } + } + + /** + * Finishes the source mapping for the given + * node at the current position. + */ + @Override + void endSourceMapping(Node node) { + if (createSrcMap && !mappings.isEmpty() && mappings.peek().node == node) { + Mapping mapping = mappings.pop(); + int line = getCurrentLineIndex(); + int index = getCurrentCharIndex(); + Preconditions.checkState(line >= 0); + mapping.end = new FilePosition(line, index); + } + } + + /** + * Generates the source map from the given code consumer, + * appending the information it saved to the SourceMap + * object given. + */ + void generateSourceMap(SourceMap map){ + if (createSrcMap) { + for (Mapping mapping : allMappings) { + map.addMapping(mapping.node, mapping.start, mapping.end); + } + } + } + + /** + * Reports to the code consumer that the given line has been cut at the + * given position, i.e. a \n has been inserted there. Or that a cut has + * been undone, i.e. a previously inserted \n has been removed. + * All mappings in the source maps after that position will be renormalized + * as needed. + */ + void reportLineCut(int lineIndex, int charIndex, boolean insertion) { + if (createSrcMap) { + for (Mapping mapping : allMappings) { + mapping.start = convertPosition(mapping.start, lineIndex, charIndex, + insertion); + + if (mapping.end != null) { + mapping.end = convertPosition(mapping.end, lineIndex, charIndex, + insertion); + } + } + } + } + + /** + * Converts the given position by normalizing it against the insertion + * or removal of a newline at the given line and character position. + * + * @param position The existing position before the newline was inserted. + * @param lineIndex The index of the line at which the newline was inserted. + * @param characterPosition The position on the line at which the newline + * was inserted. + * @param insertion True if a newline was inserted, false if a newline was + * removed. + * + * @return The normalized position. + * @throws IllegalStateException if an attempt to reverse a line cut is + * made on a previous line rather than the current line. + */ + private FilePosition convertPosition(FilePosition position, int lineIndex, + int characterPosition, boolean insertion) { + int originalLine = position.getLine(); + int originalChar = position.getColumn(); + if (insertion) { + if (originalLine == lineIndex && originalChar >= characterPosition) { + // If the position falls on the line itself, then normalize it + // if it falls at or after the place the newline was inserted. + return new FilePosition( + originalLine + 1, originalChar - characterPosition); + } else { + return position; + } + } else { + if (originalLine == lineIndex) { + return new FilePosition( + originalLine - 1, originalChar + characterPosition); + } else if (originalLine > lineIndex) { + // Not supported, can only undo a cut on the most recent line. To + // do this on a previous lines would require reevaluating the cut + // positions on all subsequent lines. + throw new IllegalStateException( + "Cannot undo line cut on a previous line."); + } else { + return position; + } + } + } + + public String getCode() { + return code.toString(); + } + + @Override + char getLastChar() { + return (code.length() > 0) ? code.charAt(code.length() - 1) : '\0'; + } + + protected final int getCurrentCharIndex() { + return lineLength; + } + + protected final int getCurrentLineIndex() { + return lineIndex; + } + } + + static class PrettyCodePrinter + extends MappedCodePrinter { + // The number of characters after which we insert a line break in the code + static final String INDENT = " "; + + private int indent = 0; + + /** + * @param lineLengthThreshold The length of a line after which we force + * a newline when possible. + * @param createSourceMap Whether to generate source map data. + * @param sourceMapDetailLevel A filter to control which nodes get mapped + * into the source map. + */ + private PrettyCodePrinter( + int lineLengthThreshold, + boolean createSourceMap, + SourceMap.DetailLevel sourceMapDetailLevel) { + super(lineLengthThreshold, createSourceMap, sourceMapDetailLevel); + } + + /** + * Appends a string to the code, keeping track of the current line length. + */ + @Override + void append(String str) { + // For pretty printing: indent at the beginning of the line + if (lineLength == 0) { + for (int i = 0; i < indent; i++) { + code.append(INDENT); + lineLength += INDENT.length(); + } + } + code.append(str); + lineLength += str.length(); + } + + /** + * Adds a newline to the code, resetting the line length and handling + * indenting for pretty printing. + */ + @Override + void startNewLine() { + if (lineLength > 0) { + code.append('\n'); + lineIndex++; + lineLength = 0; + } + } + + @Override + void maybeLineBreak() { + maybeCutLine(); + } + + /** + * This may start a new line if the current line is longer than the line + * length threshold. + */ + @Override + void maybeCutLine() { + if (lineLength > lineLengthThreshold) { + startNewLine(); + } + } + + @Override + void endLine() { + startNewLine(); + } + + @Override + void appendBlockStart() { + append(" {"); + indent++; + } + + @Override + void appendBlockEnd() { + endLine(); + indent--; + append("}"); + } + + @Override + void listSeparator() { + add(", "); + maybeLineBreak(); + } + + @Override + void endFunction(boolean statementContext) { + super.endFunction(statementContext); + if (statementContext) { + startNewLine(); + } + } + + @Override + void beginCaseBody() { + super.beginCaseBody(); + indent++; + endLine(); + } + + @Override + void endCaseBody() { + super.endCaseBody(); + indent--; + endStatement(); + } + + @Override + void appendOp(String op, boolean binOp) { + if (binOp) { + if (getLastChar() != ' ' && op.charAt(0) != ',') { + append(" "); + } + append(op); + append(" "); + } else { + append(op); + } + } + + /** + * If the body of a for loop or the then clause of an if statement has + * a single statement, should it be wrapped in a block? + * {@inheritDoc} + */ + @Override + boolean shouldPreserveExtraBlocks() { + // When pretty-printing, always place the statement in its own block + // so it is printed on a separate line. This allows breakpoints to be + // placed on the statement. + return true; + } + + /** + * @return The TRY node for the specified CATCH node. + */ + private Node getTryForCatch(Node n) { + return n.getParent().getParent(); + } + + /** + * @return Whether the a line break should be added after the specified + * BLOCK. + */ + @Override + boolean breakAfterBlockFor(Node n, boolean isStatementContext) { + Preconditions.checkState(n.isBlock()); + Node parent = n.getParent(); + if (parent != null) { + int type = parent.getType(); + switch (type) { + case Token.DO: + // Don't break before 'while' in DO-WHILE statements. + return false; + case Token.FUNCTION: + // FUNCTIONs are handled separately, don't break here. + return false; + case Token.TRY: + // Don't break before catch + return n != parent.getFirstChild(); + case Token.CATCH: + // Don't break before finally + return !NodeUtil.hasFinally(getTryForCatch(parent)); + case Token.IF: + // Don't break before else + return n == parent.getLastChild(); + } + } + return true; + } + + @Override + void endFile() { + maybeEndStatement(); + } + } + + + static class CompactCodePrinter + extends MappedCodePrinter { + + // The CompactCodePrinter tries to emit just enough newlines to stop there + // being lines longer than the threshold. Since the output is going to be + // gzipped, it makes sense to try to make the newlines appear in similar + // contexts so that gzip can encode them for 'free'. + // + // This version tries to break the lines at 'preferred' places, which are + // between the top-level forms. This works because top-level forms tend to + // be more uniform than arbitrary legal contexts. Better compression would + // probably require explicit modeling of the gzip algorithm. + + private final boolean lineBreak; + private final boolean preferLineBreakAtEndOfFile; + private int lineStartPosition = 0; + private int preferredBreakPosition = 0; + private int prevCutPosition = 0; + private int prevLineStartPosition = 0; + + /** + * @param lineBreak break the lines a bit more aggressively + * @param lineLengthThreshold The length of a line after which we force + * a newline when possible. + * @param createSrcMap Whether to gather source position + * mapping information when printing. + * @param sourceMapDetailLevel A filter to control which nodes get mapped into + * the source map. + */ + private CompactCodePrinter(boolean lineBreak, + boolean preferLineBreakAtEndOfFile, int lineLengthThreshold, + boolean createSrcMap, SourceMap.DetailLevel sourceMapDetailLevel) { + super(lineLengthThreshold, createSrcMap, sourceMapDetailLevel); + this.lineBreak = lineBreak; + this.preferLineBreakAtEndOfFile = preferLineBreakAtEndOfFile; + } + + /** + * Appends a string to the code, keeping track of the current line length. + */ + @Override + void append(String str) { + code.append(str); + lineLength += str.length(); + } + + /** + * Adds a newline to the code, resetting the line length. + */ + @Override + void startNewLine() { + if (lineLength > 0) { + prevCutPosition = code.length(); + prevLineStartPosition = lineStartPosition; + code.append('\n'); + lineLength = 0; + lineIndex++; + lineStartPosition = code.length(); + } + } + + @Override + void maybeLineBreak() { + if (lineBreak) { + if (sawFunction) { + startNewLine(); + sawFunction = false; + } + } + + // Since we are at a legal line break, can we upgrade the + // preferred break position? We prefer to break after a + // semicolon rather than before it. + int len = code.length(); + if (preferredBreakPosition == len - 1) { + char ch = code.charAt(len - 1); + if (ch == ';') { + preferredBreakPosition = len; + } + } + maybeCutLine(); + } + + /** + * This may start a new line if the current line is longer than the line + * length threshold. + */ + @Override + void maybeCutLine() { + if (lineLength > lineLengthThreshold) { + // Use the preferred position provided it will break the line. + if (preferredBreakPosition > lineStartPosition && + preferredBreakPosition < lineStartPosition + lineLength) { + int position = preferredBreakPosition; + code.insert(position, '\n'); + prevCutPosition = position; + reportLineCut(lineIndex, position - lineStartPosition, true); + lineIndex++; + lineLength -= (position - lineStartPosition); + lineStartPosition = position + 1; + } else { + startNewLine(); + } + } + } + + @Override + void notePreferredLineBreak() { + preferredBreakPosition = code.length(); + } + + @Override + void endFile() { + super.endFile(); + if (!preferLineBreakAtEndOfFile) { + return; + } + if (lineLength > lineLengthThreshold / 2) { + // Add an extra break at end of file. + append(";"); + startNewLine(); + } else if (prevCutPosition > 0) { + // Shift the previous break to end of file by replacing it with a + // and adding a new break at end of file. Adding the space + // handles cases like instanceof\nfoo. (it would be nice to avoid this) + code.setCharAt(prevCutPosition, ' '); + lineStartPosition = prevLineStartPosition; + lineLength = code.length() - lineStartPosition; + reportLineCut(lineIndex, prevCutPosition + 1, false); + lineIndex--; + prevCutPosition = 0; + prevLineStartPosition = 0; + append(";"); + startNewLine(); + } else { + // A small file with no line breaks. We do nothing in this case to + // avoid excessive line breaks. It's not ideal if a lot of these pile + // up, but that is reasonably unlikely. + } + } + + } + + static class Builder { + private final Node root; + private CompilerOptions options = new CompilerOptions(); + private boolean outputTypes = false; + private SourceMap sourceMap = null; + private boolean tagAsStrict; + + /** + * Sets the root node from which to generate the source code. + * @param node The root node. + */ + Builder(Node node) { + root = node; + } + + /** + * Sets the output options from compiler options. + */ + Builder setCompilerOptions(CompilerOptions options) { + try { + this.options = (CompilerOptions) options.clone(); + } catch (CloneNotSupportedException e) { + throw Throwables.propagate(e); + } + return this; + } + + /** + * Sets whether pretty printing should be used. + * @param prettyPrint If true, pretty printing will be used. + */ + Builder setPrettyPrint(boolean prettyPrint) { + options.prettyPrint = prettyPrint; + return this; + } + + /** + * Sets whether line breaking should be done automatically. + * @param lineBreak If true, line breaking is done automatically. + */ + Builder setLineBreak(boolean lineBreak) { + options.lineBreak = lineBreak; + return this; + } + + /** + * Sets whether to output closure-style type annotations. + * @param outputTypes If true, outputs closure-style type annotations. + */ + Builder setOutputTypes(boolean outputTypes) { + this.outputTypes = outputTypes; + return this; + } + + /** + * Sets the source map to which to write the metadata about + * the generated source code. + * + * @param sourceMap The source map. + */ + Builder setSourceMap(SourceMap sourceMap) { + this.sourceMap = sourceMap; + return this; + } + + /** + * Set whether the output should be tags as ECMASCRIPT 5 Strict. + */ + Builder setTagAsStrict(boolean tagAsStrict) { + this.tagAsStrict = tagAsStrict; + return this; + } + + /** + * Generates the source code and returns it. + */ + String build() { + if (root == null) { + throw new IllegalStateException( + "Cannot build without root node being specified"); + } + + Format outputFormat = outputTypes + ? Format.TYPED + : options.prettyPrint + ? Format.PRETTY + : Format.COMPACT; + + return toSource(root, outputFormat, options, sourceMap, tagAsStrict); + } + } + + enum Format { + COMPACT, + PRETTY, + TYPED + } + + /** + * Converts a tree to JS code + */ + private static String toSource(Node root, Format outputFormat, + CompilerOptions options, SourceMap sourceMap, boolean tagAsStrict) { + Preconditions.checkState(options.sourceMapDetailLevel != null); + + boolean createSourceMap = (sourceMap != null); + MappedCodePrinter mcp = + outputFormat == Format.COMPACT + ? new CompactCodePrinter( + options.lineBreak, + options.preferLineBreakAtEndOfFile, + options.lineLengthThreshold, + createSourceMap, + options.sourceMapDetailLevel) + : new PrettyCodePrinter( + options.lineLengthThreshold, + createSourceMap, + options.sourceMapDetailLevel); + CodeGenerator cg = + outputFormat == Format.TYPED + ? new TypedCodeGenerator(mcp, options) + : new CodeGenerator(mcp, options); + + if (tagAsStrict) { + cg.tagAsStrict(); + } + + cg.add(root); + mcp.endFile(); + + String code = mcp.getCode(); + + if (createSourceMap) { + mcp.generateSourceMap(sourceMap); + } + + return code; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodingConvention.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodingConvention.java new file mode 100644 index 0000000..5fef156 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodingConvention.java @@ -0,0 +1,414 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.StaticScope; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * CodingConvention defines a set of hooks to customize the behavior of the + * Compiler for a specific team/company. + * + */ +public interface CodingConvention extends Serializable { + + /** + * This checks whether a given variable name, such as a name in all-caps + * should be treated as if it had the @const annotation. + * + * @param variableName potentially constant variable name + * @return {@code true} if the name should be treated as a constant. + */ + public boolean isConstant(String variableName); + + /** + * This checks whether a given key of an object literal, such as a + * name in all-caps should be treated as if it had the @const + * annotation. + */ + public boolean isConstantKey(String keyName); + + /** + * This checks that a given {@code key} may be used as a key for an enum. + * + * @param key the potential key to an enum + * @return {@code true} if the {@code key} may be used as an enum key, + * {@code false} otherwise + */ + public boolean isValidEnumKey(String key); + + /** + * This checks whether a given parameter name should be treated as an + * optional parameter as far as type checking or function call arg count + * checking is concerned. Note that an optional function parameter may be + * declared as a simple type and is automatically converted to a union of the + * declared type and Undefined. + * + * @param parameter The parameter's node. + * @return {@code true} if the parameter should be treated as an optional + * parameter. + */ + public boolean isOptionalParameter(Node parameter); + + /** + * This checks whether a given parameter should be treated as a marker + * for a variable argument list function. A VarArgs parameter must be the + * last parameter in a function declaration. + * + * @param parameter The parameter's node. + * @return {@code true} if the parameter should be treated as a variable + * length parameter. + */ + public boolean isVarArgsParameter(Node parameter); + + /** + * Checks whether a global variable or function name should be treated as + * exported, or externally referenceable. + * + * @param name A global variable or function name. + * @param local {@code true} if the name is a local variable. + * @return {@code true} if the name should be considered exported. + */ + public boolean isExported(String name, boolean local); + + /** + * Should be isExported(name, true) || isExported(name, false); + */ + public boolean isExported(String name); + + /** + * Checks whether a name should be considered private. Private global + * variables and functions can only be referenced within the source file in + * which they are declared. Private properties and methods should only be + * accessed by the class that defines them. + * + * @param name The name of a global variable or function, or a method or + * property. + * @return {@code true} if the name should be considered private. + */ + public boolean isPrivate(String name); + + /** + * Checks if the given method defines a subclass relationship, + * and if it does, returns information on that relationship. By default, + * always returns null. Meant to be overridden by subclasses. + * + * @param callNode A CALL node. + */ + public SubclassRelationship getClassesDefinedByCall(Node callNode); + + /** + * Returns true if passed a string referring to the superclass. The string + * will usually be from the string node at the right of a GETPROP, e.g. + * this.superClass_. + */ + public boolean isSuperClassReference(String propertyName); + + /** + * Convenience method for determining provided dependencies amongst different + * JS scripts. + */ + public String extractClassNameIfProvide(Node node, Node parent); + + /** + * Convenience method for determining required dependencies amongst different + * JS scripts. + */ + public String extractClassNameIfRequire(Node node, Node parent); + + /** + * Function name used when exporting properties. + * Signature: fn(object, publicName, symbol). + * @return function name. + */ + public String getExportPropertyFunction(); + + /** + * Function name used when exporting symbols. + * Signature: fn(publicPath, object). + * @return function name. + */ + public String getExportSymbolFunction(); + + /** + * Checks if the given CALL node is forward-declaring any types, + * and returns the name of the types if it is. + */ + public List identifyTypeDeclarationCall(Node n); + + /** + * In many JS libraries, the function that produces inheritance also + * adds properties to the superclass and/or subclass. + */ + public void applySubclassRelationship(FunctionType parentCtor, + FunctionType childCtor, SubclassType type); + + /** + * Function name for abstract methods. An abstract method can be assigned to + * an interface method instead of an function expression in order to avoid + * linter warnings produced by assigning a function without a return value + * where a return value is expected. + * @return function name. + */ + public String getAbstractMethodName(); + + /** + * Checks if the given method defines a singleton getter, and if it does, + * returns the name of the class with the singleton getter. By default, always + * returns null. Meant to be overridden by subclasses. + * + * addSingletonGetter needs a coding convention because in the general case, + * it can't be inlined. The function inliner sees that it creates an alias + * to the given class in an inner closure, and bails out. + * + * @param callNode A CALL node. + */ + public String getSingletonGetterClassName(Node callNode); + + /** + * In many JS libraries, the function that adds a singleton getter to a class + * adds properties to the class. + */ + public void applySingletonGetter(FunctionType functionType, + FunctionType getterType, ObjectType objectType); + + /** + * @return Whether the function is inlinable by convention. + */ + public boolean isInlinableFunction(Node n); + + /** + * @return the delegate relationship created by the call or null. + */ + public DelegateRelationship getDelegateRelationship(Node callNode); + + /** + * In many JS libraries, the function that creates a delegate relationship + * also adds properties to the delegator and delegate base. + */ + public void applyDelegateRelationship( + ObjectType delegateSuperclass, ObjectType delegateBase, + ObjectType delegator, FunctionType delegateProxy, + FunctionType findDelegate); + + /** + * @return the name of the delegate superclass. + */ + public String getDelegateSuperclassName(); + + /** + * Checks for function calls that set the calling conventions on delegate + * methods. + */ + public void checkForCallingConventionDefiningCalls( + Node n, Map delegateCallingConventions); + + /** + * Defines the delegate proxy prototype properties. Their types depend on + * properties of the delegate base methods. + * + * @param delegateProxyPrototypes List of delegate proxy prototypes. + */ + public void defineDelegateProxyPrototypeProperties( + JSTypeRegistry registry, StaticScope scope, + List delegateProxyPrototypes, + Map delegateCallingConventions); + + /** + * Gets the name of the global object. + */ + public String getGlobalObject(); + + /** + * A Bind instance or null. + */ + public Bind describeFunctionBind(Node n); + + /** + * A Bind instance or null. + * @param useTypeInfo If we believe type information is reliable enough + * to use to figure out what the bind function is. + */ + public Bind describeFunctionBind(Node n, boolean useTypeInfo); + + public static class Bind { + // The target of the bind action + final Node target; + // The node representing the "this" value, maybe null + final Node thisValue; + // The head of a Node list representing the parameters + final Node parameters; + + public Bind(Node target, Node thisValue, Node parameters) { + this.target = target; + this.thisValue = thisValue; + this.parameters = parameters; + } + + /** + * The number of parameters bound (not including the 'this' value). + */ + int getBoundParameterCount() { + if (parameters == null) { + return 0; + } + Node paramParent = parameters.getParent(); + return paramParent.getChildCount() - + paramParent.getIndexOfChild(parameters); + } + } + + /** + * Whether this CALL function is testing for the existence of a property. + */ + public boolean isPropertyTestFunction(Node call); + + /** + * Whether this GETPROP node is an alias for an object prototype. + */ + public boolean isPrototypeAlias(Node getProp); + + /** + * Checks if the given method performs a object literal cast, and if it does, + * returns information on the cast. By default, always returns null. Meant + * to be overridden by subclasses. + * + * @param callNode A CALL node. + */ + public ObjectLiteralCast getObjectLiteralCast(Node callNode); + + /** + * Gets a collection of all properties that are defined indirectly on global + * objects. (For example, Closure defines superClass_ in the goog.inherits + * call). + */ + public Collection getIndirectlyDeclaredProperties(); + + /** + * Returns the set of AssertionFunction. + */ + public Collection getAssertionFunctions(); + + static enum SubclassType { + INHERITS, + MIXIN + } + + static class SubclassRelationship { + final SubclassType type; + final String subclassName; + final String superclassName; + + public SubclassRelationship(SubclassType type, + Node subclassNode, Node superclassNode) { + this.type = type; + this.subclassName = subclassNode.getQualifiedName(); + this.superclassName = superclassNode.getQualifiedName(); + } + } + + /** + * Delegates provides a mechanism and structure for identifying where classes + * can call out to optional code to augment their functionality. The optional + * code is isolated from the base code through the use of a subclass in the + * optional code derived from the delegate class in the base code. + */ + static class DelegateRelationship { + /** The subclass in the base code. */ + final String delegateBase; + + /** The class in the base code. */ + final String delegator; + + DelegateRelationship(String delegateBase, String delegator) { + this.delegateBase = delegateBase; + this.delegator = delegator; + } + } + + /** + * An object literal cast provides a mechanism to cast object literals to + * other types without a warning. + */ + static class ObjectLiteralCast { + /** Type to cast to. */ + final String typeName; + + /** Object to cast. */ + final Node objectNode; + + /** Error message */ + final DiagnosticType diagnosticType; + + ObjectLiteralCast(String typeName, Node objectNode, + DiagnosticType diagnosticType) { + this.typeName = typeName; + this.objectNode = objectNode; + this.diagnosticType = diagnosticType; + } + } + + /** + * A function that will throw an exception when either: + * -One or more of its parameters evaluate to false. + * -One or more of its parameters are not of a certain type. + */ + public class AssertionFunctionSpec { + protected final String functionName; + protected final JSTypeNative assertedType; + + public AssertionFunctionSpec(String functionName) { + this(functionName, null); + } + + public AssertionFunctionSpec(String functionName, + JSTypeNative assertedType) { + this.functionName = functionName; + this.assertedType = assertedType; + } + + /** Returns the name of the function. */ + public String getFunctionName() { + return functionName; + } + + /** + * Returns the parameter of the assertion function that is being checked. + * @param firstParam The first parameter of the function call. + */ + public Node getAssertedParam(Node firstParam) { + return firstParam; + } + + /** + * Returns the type for a type assertion, or null if the function asserts + * that the node must not be null or undefined. + */ + public JSType getAssertedType(Node call, JSTypeRegistry registry) { + return assertedType != null ? registry.getNativeType(assertedType) : null; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodingConventions.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodingConventions.java new file mode 100644 index 0000000..88e2040 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CodingConventions.java @@ -0,0 +1,477 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.StaticScope; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Helper classes for dealing with coding conventions. + */ +public class CodingConventions { + + private CodingConventions() {} + + /** Gets the default coding convention. */ + public static CodingConvention getDefault() { + return new DefaultCodingConvention(); + } + + /** + * A convention that wraps another. + * + * When you want to support a new library, you should subclass this + * delegate, and override the methods that you want to customize. + * + * This way, a person using jQuery and Closure Library can create a new + * coding convention by creating a jQueryCodingConvention that delegates + * to a ClosureCodingConvention that delegates to a DefaultCodingConvention. + */ + public static class Proxy implements CodingConvention { + + protected final CodingConvention nextConvention; + + protected Proxy(CodingConvention convention) { + this.nextConvention = convention; + } + + @Override + public boolean isConstant(String variableName) { + return nextConvention.isConstant(variableName); + } + + @Override public boolean isConstantKey(String keyName) { + return nextConvention.isConstantKey(keyName); + } + + @Override + public boolean isValidEnumKey(String key) { + return nextConvention.isValidEnumKey(key); + } + + @Override + public boolean isOptionalParameter(Node parameter) { + return nextConvention.isOptionalParameter(parameter); + } + + @Override + public boolean isVarArgsParameter(Node parameter) { + return nextConvention.isVarArgsParameter(parameter); + } + + @Override + public boolean isExported(String name, boolean local) { + return nextConvention.isExported(name, local); + } + + + @Override + public final boolean isExported(String name) { + return isExported(name, false) || isExported(name, true); + } + + @Override + public boolean isPrivate(String name) { + return nextConvention.isPrivate(name); + } + + @Override + public SubclassRelationship getClassesDefinedByCall(Node callNode) { + return nextConvention.getClassesDefinedByCall(callNode); + } + + @Override + public boolean isSuperClassReference(String propertyName) { + return nextConvention.isSuperClassReference(propertyName); + } + + @Override + public String extractClassNameIfProvide(Node node, Node parent) { + return nextConvention.extractClassNameIfProvide(node, parent); + } + + @Override + public String extractClassNameIfRequire(Node node, Node parent) { + return nextConvention.extractClassNameIfRequire(node, parent); + } + + @Override + public String getExportPropertyFunction() { + return nextConvention.getExportPropertyFunction(); + } + + @Override + public String getExportSymbolFunction() { + return nextConvention.getExportSymbolFunction(); + } + + @Override + public List identifyTypeDeclarationCall(Node n) { + return nextConvention.identifyTypeDeclarationCall(n); + } + + @Override + public void applySubclassRelationship(FunctionType parentCtor, + FunctionType childCtor, SubclassType type) { + nextConvention.applySubclassRelationship( + parentCtor, childCtor, type); + } + + @Override + public String getAbstractMethodName() { + return nextConvention.getAbstractMethodName(); + } + + @Override + public String getSingletonGetterClassName(Node callNode) { + return nextConvention.getSingletonGetterClassName(callNode); + } + + @Override + public void applySingletonGetter(FunctionType functionType, + FunctionType getterType, ObjectType objectType) { + nextConvention.applySingletonGetter( + functionType, getterType, objectType); + } + + @Override + public boolean isInlinableFunction(Node n) { + return nextConvention.isInlinableFunction(n); + } + + @Override + public DelegateRelationship getDelegateRelationship(Node callNode) { + return nextConvention.getDelegateRelationship(callNode); + } + + @Override + public void applyDelegateRelationship( + ObjectType delegateSuperclass, ObjectType delegateBase, + ObjectType delegator, FunctionType delegateProxy, + FunctionType findDelegate) { + nextConvention.applyDelegateRelationship( + delegateSuperclass, delegateBase, delegator, + delegateProxy, findDelegate); + } + + @Override + public String getDelegateSuperclassName() { + return nextConvention.getDelegateSuperclassName(); + } + + @Override + public void checkForCallingConventionDefiningCalls( + Node n, Map delegateCallingConventions) { + nextConvention.checkForCallingConventionDefiningCalls( + n, delegateCallingConventions); + } + + @Override + public void defineDelegateProxyPrototypeProperties( + JSTypeRegistry registry, StaticScope scope, + List delegateProxyPrototypes, + Map delegateCallingConventions) { + nextConvention.defineDelegateProxyPrototypeProperties( + registry, scope, delegateProxyPrototypes, delegateCallingConventions); + } + + @Override + public String getGlobalObject() { + return nextConvention.getGlobalObject(); + } + + @Override + public Collection getAssertionFunctions() { + return nextConvention.getAssertionFunctions(); + } + + @Override + public Bind describeFunctionBind(Node n) { + return describeFunctionBind(n, false); + } + + @Override + public Bind describeFunctionBind(Node n, boolean useTypeInfo) { + return nextConvention.describeFunctionBind(n, useTypeInfo); + } + + @Override + public boolean isPropertyTestFunction(Node call) { + return nextConvention.isPropertyTestFunction(call); + } + + @Override + public boolean isPrototypeAlias(Node getProp) { + return false; + } + + @Override + public ObjectLiteralCast getObjectLiteralCast(Node callNode) { + return nextConvention.getObjectLiteralCast(callNode); + } + + @Override + public Collection getIndirectlyDeclaredProperties() { + return nextConvention.getIndirectlyDeclaredProperties(); + } + } + + + /** + * The default coding convention. + * Should be at the bottom of all proxy chains. + */ + private static class DefaultCodingConvention implements CodingConvention { + + private static final long serialVersionUID = 1L; + + @Override + public boolean isConstant(String variableName) { + return false; + } + + @Override + public boolean isConstantKey(String variableName) { + return false; + } + + @Override + public boolean isValidEnumKey(String key) { + return key != null && key.length() > 0; + } + + @Override + public boolean isOptionalParameter(Node parameter) { + // be as lax as possible, but this must be mutually exclusive from + // var_args parameters. + return !isVarArgsParameter(parameter); + } + + @Override + public boolean isVarArgsParameter(Node parameter) { + // be as lax as possible + return parameter.getParent().getLastChild() == parameter; + } + + @Override + public boolean isExported(String name, boolean local) { + return local && name.startsWith("$super"); + } + + @Override + public boolean isExported(String name) { + return isExported(name, false) || isExported(name, true); + } + + @Override + public boolean isPrivate(String name) { + return false; + } + + @Override + public SubclassRelationship getClassesDefinedByCall(Node callNode) { + return null; + } + + @Override + public boolean isSuperClassReference(String propertyName) { + return false; + } + + @Override + public String extractClassNameIfProvide(Node node, Node parent) { + String message = "only implemented in GoogleCodingConvention"; + throw new UnsupportedOperationException(message); + } + + @Override + public String extractClassNameIfRequire(Node node, Node parent) { + String message = "only implemented in GoogleCodingConvention"; + throw new UnsupportedOperationException(message); + } + + @Override + public String getExportPropertyFunction() { + return null; + } + + @Override + public String getExportSymbolFunction() { + return null; + } + + @Override + public List identifyTypeDeclarationCall(Node n) { + return null; + } + + @Override + public void applySubclassRelationship(FunctionType parentCtor, + FunctionType childCtor, SubclassType type) { + // do nothing + } + + @Override + public String getAbstractMethodName() { + return null; + } + + @Override + public String getSingletonGetterClassName(Node callNode) { + return null; + } + + @Override + public void applySingletonGetter(FunctionType functionType, + FunctionType getterType, ObjectType objectType) { + // do nothing. + } + + @Override + public boolean isInlinableFunction(Node n) { + Preconditions.checkState(n.isFunction()); + return true; + } + + @Override + public DelegateRelationship getDelegateRelationship(Node callNode) { + return null; + } + + @Override + public void applyDelegateRelationship( + ObjectType delegateSuperclass, ObjectType delegateBase, + ObjectType delegator, FunctionType delegateProxy, + FunctionType findDelegate) { + // do nothing. + } + + @Override + public String getDelegateSuperclassName() { + return null; + } + + @Override + public void checkForCallingConventionDefiningCalls(Node n, + Map delegateCallingConventions) { + // do nothing. + } + + @Override + public void defineDelegateProxyPrototypeProperties( + JSTypeRegistry registry, StaticScope scope, + List delegateProxyPrototypes, + Map delegateCallingConventions) { + // do nothing. + } + + @Override + public String getGlobalObject() { + return "window"; + } + + @Override + public boolean isPropertyTestFunction(Node call) { + return false; + } + + @Override + public boolean isPrototypeAlias(Node getProp) { + return false; + } + + @Override + public ObjectLiteralCast getObjectLiteralCast(Node callNode) { + return null; + } + + @Override + public Collection getAssertionFunctions() { + return Collections.emptySet(); + } + + @Override + public Bind describeFunctionBind(Node n) { + return describeFunctionBind(n, false); + } + + @Override + public Bind describeFunctionBind(Node n, boolean useTypeInfo) { + if (!n.isCall()) { + return null; + } + + Node callTarget = n.getFirstChild(); + String name = callTarget.getQualifiedName(); + if (name != null) { + if (name.equals("Function.prototype.bind.call")) { + // goog.bind(fn, self, args...); + Node fn = callTarget.getNext(); + if (fn == null) { + return null; + } + Node thisValue = safeNext(fn); + Node parameters = safeNext(thisValue); + return new Bind(fn, thisValue, parameters); + } + } + + if (callTarget.isGetProp() + && callTarget.getLastChild().getString().equals("bind")) { + Node maybeFn = callTarget.getFirstChild(); + JSType maybeFnType = maybeFn.getJSType(); + FunctionType fnType = null; + if (useTypeInfo && maybeFnType != null) { + fnType = maybeFnType.restrictByNotNullOrUndefined() + .toMaybeFunctionType(); + } + + if (fnType != null || maybeFn.isFunction()) { + // (function(){}).bind(self, args...); + Node thisValue = callTarget.getNext(); + Node parameters = safeNext(thisValue); + return new Bind(maybeFn, thisValue, parameters); + } + } + + return null; + } + + @Override + public Collection getIndirectlyDeclaredProperties() { + return ImmutableList.of(); + } + + private Node safeNext(Node n) { + if (n != null) { + return n.getNext(); + } + return null; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CollapseAnonymousFunctions.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CollapseAnonymousFunctions.java new file mode 100644 index 0000000..9ed82e2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CollapseAnonymousFunctions.java @@ -0,0 +1,119 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.Node; + +/** + * Collapses anonymous function expressions into named function declarations, + * i.e. the following: + * + *

+ * var f = function()
+ * 
+ *
+ * becomes:
+ *
+ * 
function f()
+ * + * This reduces the generated code size but changes the semantics because f + * will be defined before its definition is reached. + * + */ +class CollapseAnonymousFunctions implements CompilerPass { + private final AbstractCompiler compiler; + + public CollapseAnonymousFunctions(AbstractCompiler compiler) { + Preconditions.checkArgument(compiler.getLifeCycleStage().isNormalized()); + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, new Callback()); + } + + private class Callback extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isVar()) { + return; + } + + // It is only safe to collapse anonymous functions that appear + // at top-level blocks. In other cases the difference between + // variable and function declarations can lead to problems or + // expose subtle bugs in browser implementation as function + // definitions are added to scopes before the start of execution. + + Node grandparent = parent.getParent(); + if (!(parent.isScript() || + grandparent != null && + grandparent.isFunction() && + parent.isBlock())) { + return; + } + + // Need to store the next name in case the current name is removed from + // the linked list. + Preconditions.checkState(n.hasOneChild()); + Node name = n.getFirstChild(); + Node value = name.getFirstChild(); + if (value != null && + value.isFunction() && + !isRecursiveFunction(value)) { + Node fnName = value.getFirstChild(); + fnName.setString(name.getString()); + NodeUtil.copyNameAnnotations(name, fnName); + name.removeChild(value); + parent.replaceChild(n, value); + + // Renormalize the code. + if (!t.inGlobalScope() && + NodeUtil.isHoistedFunctionDeclaration(value)) { + parent.addChildToFront(value.detachFromParent()); + } + + compiler.reportCodeChange(); + } + } + + private boolean isRecursiveFunction(Node function) { + Node name = function.getFirstChild(); + if (name.getString().isEmpty()) { + return false; + } + Node args = name.getNext(); + Node body = args.getNext(); + return containsName(body, name.getString()); + } + + private boolean containsName(Node n, String name) { + if (n.isName() && n.getString().equals(name)) { + return true; + } + + for (Node child : n.children()) { + if (containsName(child, name)) { + return true; + } + } + return false; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CollapseProperties.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CollapseProperties.java new file mode 100644 index 0000000..3dc9204 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CollapseProperties.java @@ -0,0 +1,970 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.GlobalNamespace.Name; +import com.google.javascript.jscomp.GlobalNamespace.Ref; +import com.google.javascript.jscomp.GlobalNamespace.Ref.Type; +import com.google.javascript.jscomp.ReferenceCollectingCallback; +import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; +import com.google.javascript.jscomp.Scope; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.TokenStream; +import com.google.javascript.rhino.jstype.JSType; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Flattens global objects/namespaces by replacing each '.' with '$' in + * their names. This reduces the number of property lookups the browser has + * to do and allows the {@link RenameVars} pass to shorten namespaced names. + * For example, goog.events.handleEvent() -> goog$events$handleEvent() -> Za(). + * + *

If a global object's name is assigned to more than once, or if a property + * is added to the global object in a complex expression, then none of its + * properties will be collapsed (for safety/correctness). + * + *

If, after a global object is declared, it is never referenced except when + * its properties are read or set, then the object will be removed after its + * properties have been collapsed. + * + *

Uninitialized variable stubs are created at a global object's declaration + * site for any of its properties that are added late in a local scope. + * + *

If, after an object is declared, it is referenced directly in a way that + * might create an alias for it, then none of its properties will be collapsed. + * This behavior is a safeguard to prevent the values associated with the + * flattened names from getting out of sync with the object's actual property + * values. For example, in the following case, an alias a$b, if created, could + * easily keep the value 0 even after a.b became 5: + * a = {b: 0}; c = a; c.b = 5; . + * + *

This pass doesn't flatten property accesses of the form: a[b]. + * + *

For lots of examples, see the unit test. + * + */ +class CollapseProperties implements CompilerPass { + + // Warnings + static final DiagnosticType UNSAFE_NAMESPACE_WARNING = + DiagnosticType.warning( + "JSC_UNSAFE_NAMESPACE", + "incomplete alias created for namespace {0}"); + + static final DiagnosticType NAMESPACE_REDEFINED_WARNING = + DiagnosticType.warning( + "JSC_NAMESPACE_REDEFINED", + "namespace {0} should not be redefined"); + + static final DiagnosticType UNSAFE_THIS = DiagnosticType.warning( + "JSC_UNSAFE_THIS", + "dangerous use of 'this' in static method {0}"); + + private AbstractCompiler compiler; + + /** Global namespace tree */ + private List globalNames; + + /** Maps names (e.g. "a.b.c") to nodes in the global namespace tree */ + private Map nameMap; + + private final boolean collapsePropertiesOnExternTypes; + private final boolean inlineAliases; + + /** + * Creates an instance. + * + * @param compiler The JSCompiler, for reporting code changes + * @param collapsePropertiesOnExternTypes if true, will rename user-defined + * static properties on externed typed. E.g. String.foo. + * @param inlineAliases Whether we're allowed to inline local aliases of + * namespaces, etc. + */ + CollapseProperties(AbstractCompiler compiler, + boolean collapsePropertiesOnExternTypes, boolean inlineAliases) { + this.compiler = compiler; + this.collapsePropertiesOnExternTypes = collapsePropertiesOnExternTypes; + this.inlineAliases = inlineAliases; + } + + @Override + public void process(Node externs, Node root) { + GlobalNamespace namespace; + if (collapsePropertiesOnExternTypes) { + namespace = new GlobalNamespace(compiler, externs, root); + } else { + namespace = new GlobalNamespace(compiler, root); + } + + if (inlineAliases) { + inlineAliases(namespace); + } + nameMap = namespace.getNameIndex(); + globalNames = namespace.getNameForest(); + checkNamespaces(); + + for (Name n : globalNames) { + flattenReferencesToCollapsibleDescendantNames(n, n.getBaseName()); + } + + // We collapse property definitions after collapsing property references + // because this step can alter the parse tree above property references, + // invalidating the node ancestry stored with each reference. + for (Name n : globalNames) { + collapseDeclarationOfNameAndDescendants(n, n.getBaseName()); + } + } + + /** + * For each qualified name N in the global scope, we check if: + * (a) No ancestor of N is ever aliased or assigned an unknown value type. + * (If N = "a.b.c", "a" and "a.b" are never aliased). + * (b) N has exactly one write, and it lives in the global scope. + * (c) N is aliased in a local scope. + * + * If (a) is true, then GlobalNamespace must know all the writes to N. + * If (a) and (b) are true, then N cannot change during the execution of + * a local scope. + * If (a) and (b) and (c) are true, then the alias can be inlined if the + * alias obeys the usual rules for how we decide whether a variable is + * inlineable. + * @see InlineVariables + */ + private void inlineAliases(GlobalNamespace namespace) { + // Invariant: All the names in the worklist meet condition (a). + Deque workList = new ArrayDeque(namespace.getNameForest()); + while (!workList.isEmpty()) { + Name name = workList.pop(); + + // Don't attempt to inline a getter or setter property as a variable. + if (name.type == Name.Type.GET || name.type == Name.Type.SET) { + continue; + } + + if (name.globalSets == 1 && name.localSets == 0 && + name.aliasingGets > 0) { + // {@code name} meets condition (b). Find all of its local aliases + // and try to inline them. + List refs = Lists.newArrayList(name.getRefs()); + for (Ref ref : refs) { + if (ref.type == Type.ALIASING_GET && ref.scope.isLocal()) { + // {@code name} meets condition (c). Try to inline it. + if (inlineAliasIfPossible(ref, namespace)) { + name.removeRef(ref); + } + } + } + } + + // Check if {@code name} has any aliases left after the + // local-alias-inlining above. + if ((name.type == Name.Type.OBJECTLIT || + name.type == Name.Type.FUNCTION) && + name.aliasingGets == 0 && name.props != null) { + // All of {@code name}'s children meet condition (a), so they can be + // added to the worklist. + workList.addAll(name.props); + } + } + } + + private boolean inlineAliasIfPossible(Ref alias, GlobalNamespace namespace) { + // Ensure that the alias is assigned to a local variable at that + // variable's declaration. If the alias's parent is a NAME, + // then the NAME must be the child of a VAR node, and we must + // be in a VAR assignment. + Node aliasParent = alias.node.getParent(); + if (aliasParent.isName()) { + // Ensure that the local variable is well defined and never reassigned. + Scope scope = alias.scope; + Var aliasVar = scope.getVar(aliasParent.getString()); + ReferenceCollectingCallback collector = + new ReferenceCollectingCallback(compiler, + ReferenceCollectingCallback.DO_NOTHING_BEHAVIOR, + Predicates.equalTo(aliasVar)); + (new NodeTraversal(compiler, collector)).traverseAtScope(scope); + + ReferenceCollection aliasRefs = collector.getReferences(aliasVar); + if (aliasRefs.isWellDefined() + && aliasRefs.firstReferenceIsAssigningDeclaration() + && aliasRefs.isAssignedOnceInLifetime()) { + // The alias is well-formed, so do the inlining now. + int size = aliasRefs.references.size(); + Set newNodes = Sets.newHashSetWithExpectedSize(size - 1); + for (int i = 1; i < size; i++) { + ReferenceCollectingCallback.Reference aliasRef = + aliasRefs.references.get(i); + + Node newNode = alias.node.cloneTree(); + aliasRef.getParent().replaceChild(aliasRef.getNode(), newNode); + newNodes.add(newNode); + } + + // just set the original alias to null. + aliasParent.replaceChild(alias.node, IR.nullNode()); + compiler.reportCodeChange(); + + // Inlining the variable may have introduced new references + // to descendants of {@code name}. So those need to be collected now. + namespace.scanNewNodes(alias.scope, newNodes); + return true; + } + } + + return false; + } + + /** + * Runs through all namespaces (prefixes of classes and enums), and checks if + * any of them have been used in an unsafe way. + */ + private void checkNamespaces() { + for (Name name : nameMap.values()) { + if (name.isNamespace() && + (name.aliasingGets > 0 || name.localSets + name.globalSets > 1 || + name.deleteProps > 0)) { + boolean initialized = name.getDeclaration() != null; + for (Ref ref : name.getRefs()) { + if (ref == name.getDeclaration()) { + continue; + } + + if (ref.type == Ref.Type.DELETE_PROP) { + if (initialized) { + warnAboutNamespaceRedefinition(name, ref); + } + } else if ( + ref.type == Ref.Type.SET_FROM_GLOBAL || + ref.type == Ref.Type.SET_FROM_LOCAL) { + if (initialized) { + warnAboutNamespaceRedefinition(name, ref); + } + + initialized = true; + } else if (ref.type == Ref.Type.ALIASING_GET) { + warnAboutNamespaceAliasing(name, ref); + } + } + } + } + } + + /** + * Reports a warning because a namespace was aliased. + * + * @param nameObj A namespace that is being aliased + * @param ref The reference that forced the alias + */ + private void warnAboutNamespaceAliasing(Name nameObj, Ref ref) { + compiler.report( + JSError.make(ref.getSourceName(), ref.node, + UNSAFE_NAMESPACE_WARNING, nameObj.getFullName())); + } + + /** + * Reports a warning because a namespace was redefined. + * + * @param nameObj A namespace that is being redefined + * @param ref The reference that set the namespace + */ + private void warnAboutNamespaceRedefinition(Name nameObj, Ref ref) { + compiler.report( + JSError.make(ref.getSourceName(), ref.node, + NAMESPACE_REDEFINED_WARNING, nameObj.getFullName())); + } + + /** + * Flattens all references to collapsible properties of a global name except + * their initial definitions. Recurses on subnames. + * + * @param n An object representing a global name + * @param alias The flattened name for {@code n} + */ + private void flattenReferencesToCollapsibleDescendantNames( + Name n, String alias) { + if (n.props == null) return; + + for (Name p : n.props) { + String propAlias = appendPropForAlias(alias, p.getBaseName()); + + if (p.canCollapse()) { + flattenReferencesTo(p, propAlias); + } else if (p.isSimpleStubDeclaration()) { + flattenSimpleStubDeclaration(p, propAlias); + } + + flattenReferencesToCollapsibleDescendantNames(p, propAlias); + } + } + + + /** + * Flattens a stub declaration. + * This is mostly a hack to support legacy users. + */ + private void flattenSimpleStubDeclaration(Name name, String alias) { + Ref ref = Iterables.getOnlyElement(name.getRefs()); + Node nameNode = NodeUtil.newName( + compiler.getCodingConvention(), alias, ref.node, + name.getFullName()); + Node varNode = IR.var(nameNode).copyInformationFrom(nameNode); + + Preconditions.checkState( + ref.node.getParent().isExprResult()); + Node parent = ref.node.getParent(); + Node gramps = parent.getParent(); + gramps.replaceChild(parent, varNode); + compiler.reportCodeChange(); + } + + + /** + * Flattens all references to a collapsible property of a global name except + * its initial definition. + * + * @param n A global property name (e.g. "a.b" or "a.b.c.d") + * @param alias The flattened name (e.g. "a$b" or "a$b$c$d") + */ + private void flattenReferencesTo(Name n, String alias) { + String originalName = n.getFullName(); + for (Ref r : n.getRefs()) { + if (r == n.getDeclaration()) { + // Declarations are handled separately. + continue; + } + + Node rParent = r.node.getParent(); + + // There are two cases when we shouldn't flatten a reference: + // 1) Object literal keys, because duplicate keys show up as refs. + // 2) References inside a complex assign. (a = x.y = 0). These are + // called TWIN references, because they show up twice in the + // reference list. Only collapse the set, not the alias. + if (!NodeUtil.isObjectLitKey(r.node, rParent) && + (r.getTwin() == null || r.isSet())) { + flattenNameRef(alias, r.node, rParent, originalName); + } + } + + // Flatten all occurrences of a name as a prefix of its subnames. For + // example, if {@code n} corresponds to the name "a.b", then "a.b" will be + // replaced with "a$b" in all occurrences of "a.b.c", "a.b.c.d", etc. + if (n.props != null) { + for (Name p : n.props) { + flattenPrefixes(alias, p, 1); + } + } + } + + /** + * Flattens all occurrences of a name as a prefix of subnames beginning + * with a particular subname. + * + * @param n A global property name (e.g. "a.b.c.d") + * @param alias A flattened prefix name (e.g. "a$b") + * @param depth The difference in depth between the property name and + * the prefix name (e.g. 2) + */ + private void flattenPrefixes(String alias, Name n, int depth) { + // Only flatten the prefix of a name declaration if the name being + // initialized is fully qualified (i.e. not an object literal key). + String originalName = n.getFullName(); + Ref decl = n.getDeclaration(); + if (decl != null && decl.node != null && + decl.node.isGetProp()) { + flattenNameRefAtDepth(alias, decl.node, depth, originalName); + } + + for (Ref r : n.getRefs()) { + if (r == decl) { + // Declarations are handled separately. + continue; + } + + // References inside a complex assign (a = x.y = 0) + // have twins. We should only flatten one of the twins. + if (r.getTwin() == null || r.isSet()) { + flattenNameRefAtDepth(alias, r.node, depth, originalName); + } + } + + if (n.props != null) { + for (Name p : n.props) { + flattenPrefixes(alias, p, depth + 1); + } + } + } + + /** + * Flattens a particular prefix of a single name reference. + * + * @param alias A flattened prefix name (e.g. "a$b") + * @param n The node corresponding to a subproperty name (e.g. "a.b.c.d") + * @param depth The difference in depth between the property name and + * the prefix name (e.g. 2) + * @param originalName String version of the property name. + */ + private void flattenNameRefAtDepth(String alias, Node n, int depth, + String originalName) { + // This method has to work for both GETPROP chains and, in rare cases, + // OBJLIT keys, possibly nested. That's why we check for children before + // proceeding. In the OBJLIT case, we don't need to do anything. + int nType = n.getType(); + boolean isQName = nType == Token.NAME || nType == Token.GETPROP; + boolean isObjKey = NodeUtil.isObjectLitKey(n, n.getParent()); + Preconditions.checkState(isObjKey || isQName); + if (isQName) { + for (int i = 1; i < depth && n.hasChildren(); i++) { + n = n.getFirstChild(); + } + if (n.hasChildren()) { + flattenNameRef(alias, n.getFirstChild(), n, originalName); + } + } + } + + /** + * Replaces a GETPROP a.b.c with a NAME a$b$c. + * + * @param alias A flattened prefix name (e.g. "a$b") + * @param n The GETPROP node corresponding to the original name (e.g. "a.b") + * @param parent {@code n}'s parent + * @param originalName String version of the property name. + */ + private void flattenNameRef(String alias, Node n, Node parent, + String originalName) { + // BEFORE: + // getprop + // getprop + // name a + // string b + // string c + // AFTER: + // name a$b$c + Node ref = NodeUtil.newName( + compiler.getCodingConvention(), alias, n, originalName); + NodeUtil.copyNameAnnotations(n.getLastChild(), ref); + if (parent.isCall() && n == parent.getFirstChild()) { + // The node was a call target, we are deliberately flatten these as + // we node the "this" isn't provided by the namespace. Mark it as such: + parent.putBooleanProp(Node.FREE_CALL, true); + } + + JSType type = n.getJSType(); + if (type != null) { + ref.setJSType(type); + } + parent.replaceChild(n, ref); + compiler.reportCodeChange(); + } + + /** + * Collapses definitions of the collapsible properties of a global name. + * Recurses on subnames that also represent JavaScript objects with + * collapsible properties. + * + * @param n A node representing a global name + * @param alias The flattened name for {@code n} + */ + private void collapseDeclarationOfNameAndDescendants(Name n, String alias) { + boolean canCollapseChildNames = n.canCollapseUnannotatedChildNames(); + + // Handle this name first so that nested object literals get unrolled. + if (n.canCollapse()) { + updateObjLitOrFunctionDeclaration(n, alias, canCollapseChildNames); + } + + if (n.props != null) { + for (Name p : n.props) { + // Recurse first so that saved node ancestries are intact when needed. + collapseDeclarationOfNameAndDescendants( + p, appendPropForAlias(alias, p.getBaseName())); + + if (!p.inExterns && canCollapseChildNames && + p.getDeclaration() != null && + p.canCollapse() && + p.getDeclaration().node != null && + p.getDeclaration().node.getParent() != null && + p.getDeclaration().node.getParent().isAssign()) { + updateSimpleDeclaration( + appendPropForAlias(alias, p.getBaseName()), p, p.getDeclaration()); + } + } + } + } + + /** + * Updates the initial assignment to a collapsible property at global scope + * by changing it to a variable declaration (e.g. a.b = 1 -> var a$b = 1). + * The property's value may either be a primitive or an object literal or + * function whose properties aren't collapsible. + * + * @param alias The flattened property name (e.g. "a$b") + * @param refName The name for the reference being updated. + * @param ref An object containing information about the assignment getting + * updated + */ + private void updateSimpleDeclaration(String alias, Name refName, Ref ref) { + Node rvalue = ref.node.getNext(); + Node parent = ref.node.getParent(); + Node gramps = parent.getParent(); + Node greatGramps = gramps.getParent(); + Node greatGreatGramps = greatGramps.getParent(); + + if (rvalue != null && rvalue.isFunction()) { + checkForHosedThisReferences(rvalue, refName.docInfo, refName); + } + + // Create the new alias node. + Node nameNode = NodeUtil.newName( + compiler.getCodingConvention(), alias, gramps.getFirstChild(), + refName.getFullName()); + NodeUtil.copyNameAnnotations(ref.node.getLastChild(), nameNode); + + if (gramps.isExprResult()) { + // BEFORE: a.b.c = ...; + // exprstmt + // assign + // getprop + // getprop + // name a + // string b + // string c + // NODE + // AFTER: var a$b$c = ...; + // var + // name a$b$c + // NODE + + // Remove the r-value (NODE). + parent.removeChild(rvalue); + nameNode.addChildToFront(rvalue); + + Node varNode = IR.var(nameNode); + greatGramps.replaceChild(gramps, varNode); + } else { + // This must be a complex assignment. + Preconditions.checkNotNull(ref.getTwin()); + + // BEFORE: + // ... (x.y = 3); + // + // AFTER: + // var x$y; + // ... (x$y = 3); + + Node current = gramps; + Node currentParent = gramps.getParent(); + for (; !currentParent.isScript() && + !currentParent.isBlock(); + current = currentParent, + currentParent = currentParent.getParent()) {} + + // Create a stub variable declaration right + // before the current statement. + Node stubVar = IR.var(nameNode.cloneTree()) + .copyInformationFrom(nameNode); + currentParent.addChildBefore(stubVar, current); + + parent.replaceChild(ref.node, nameNode); + } + + compiler.reportCodeChange(); + } + + /** + * Updates the first initialization (a.k.a "declaration") of a global name. + * This involves flattening the global name (if it's not just a global + * variable name already), collapsing object literal keys into global + * variables, declaring stub global variables for properties added later + * in a local scope. + * + * It may seem odd that this function also takes care of declaring stubs + * for direct children. The ultimate goal of this function is to eliminate + * the global name entirely (when possible), so that "middlemen" namespaces + * disappear, and to do that we need to make sure that all the direct children + * will be collapsed as well. + * + * @param n An object representing a global name (e.g. "a", "a.b.c") + * @param alias The flattened name for {@code n} (e.g. "a", "a$b$c") + * @param canCollapseChildNames Whether it's possible to collapse children of + * this name. (This is mostly passed for convenience; it's equivalent to + * n.canCollapseChildNames()). + */ + private void updateObjLitOrFunctionDeclaration( + Name n, String alias, boolean canCollapseChildNames) { + Ref decl = n.getDeclaration(); + if (decl == null) { + // Some names do not have declarations, because they + // are only defined in local scopes. + return; + } + + if (decl.getTwin() != null) { + // Twin declarations will get handled when normal references + // are handled. + return; + } + + switch (decl.node.getParent().getType()) { + case Token.ASSIGN: + updateObjLitOrFunctionDeclarationAtAssignNode( + n, alias, canCollapseChildNames); + break; + case Token.VAR: + updateObjLitOrFunctionDeclarationAtVarNode(n, canCollapseChildNames); + break; + case Token.FUNCTION: + updateFunctionDeclarationAtFunctionNode(n, canCollapseChildNames); + break; + } + } + + /** + * Updates the first initialization (a.k.a "declaration") of a global name + * that occurs at an ASSIGN node. See comment for + * {@link #updateObjLitOrFunctionDeclaration}. + * + * @param n An object representing a global name (e.g. "a", "a.b.c") + * @param alias The flattened name for {@code n} (e.g. "a", "a$b$c") + */ + private void updateObjLitOrFunctionDeclarationAtAssignNode( + Name n, String alias, boolean canCollapseChildNames) { + // NOTE: It's important that we don't add additional nodes + // (e.g. a var node before the exprstmt) because the exprstmt might be + // the child of an if statement that's not inside a block). + + Ref ref = n.getDeclaration(); + Node rvalue = ref.node.getNext(); + Node varNode = new Node(Token.VAR); + Node varParent = ref.node.getAncestor(3); + Node gramps = ref.node.getAncestor(2); + boolean isObjLit = rvalue.isObjectLit(); + boolean insertedVarNode = false; + + if (isObjLit && n.canEliminate()) { + // Eliminate the object literal altogether. + varParent.replaceChild(gramps, varNode); + ref.node = null; + insertedVarNode = true; + + } else if (!n.isSimpleName()) { + // Create a VAR node to declare the name. + if (rvalue.isFunction()) { + checkForHosedThisReferences(rvalue, n.docInfo, n); + } + + ref.node.getParent().removeChild(rvalue); + + Node nameNode = NodeUtil.newName( + compiler.getCodingConvention(), + alias, ref.node.getAncestor(2), n.getFullName()); + + JSDocInfo info = ref.node.getParent().getJSDocInfo(); + if (ref.node.getLastChild().getBooleanProp(Node.IS_CONSTANT_NAME) || + (info != null && info.isConstant())) { + nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); + } + + if (info != null) { + varNode.setJSDocInfo(info); + } + varNode.addChildToBack(nameNode); + nameNode.addChildToFront(rvalue); + varParent.replaceChild(gramps, varNode); + + // Update the node ancestry stored in the reference. + ref.node = nameNode; + insertedVarNode = true; + } + + if (canCollapseChildNames) { + if (isObjLit) { + declareVarsForObjLitValues( + n, alias, rvalue, + varNode, varParent.getChildBefore(varNode), varParent); + } + + addStubsForUndeclaredProperties(n, alias, varParent, varNode); + } + + if (insertedVarNode) { + if (!varNode.hasChildren()) { + varParent.removeChild(varNode); + } + compiler.reportCodeChange(); + } + } + + /** + * Warns about any references to "this" in the given FUNCTION. The function + * is getting collapsed, so the references will change. + */ + private void checkForHosedThisReferences(Node function, JSDocInfo docInfo, + final Name name) { + // A function is getting collapsed. Make sure that if it refers to + // "this", it must be a constructor or documented with @this. + if (docInfo == null || + (!docInfo.isConstructor() && !docInfo.hasThisType())) { + NodeTraversal.traverse(compiler, function.getLastChild(), + new NodeTraversal.AbstractShallowCallback() { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isThis()) { + compiler.report( + JSError.make(name.getDeclaration().getSourceName(), n, + UNSAFE_THIS, name.getFullName())); + } + } + }); + } + } + + /** + * Updates the first initialization (a.k.a "declaration") of a global name + * that occurs at a VAR node. See comment for + * {@link #updateObjLitOrFunctionDeclaration}. + * + * @param n An object representing a global name (e.g. "a") + */ + private void updateObjLitOrFunctionDeclarationAtVarNode( + Name n, boolean canCollapseChildNames) { + if (!canCollapseChildNames) { + return; + } + + Ref ref = n.getDeclaration(); + String name = ref.node.getString(); + Node rvalue = ref.node.getFirstChild(); + Node varNode = ref.node.getParent(); + Node gramps = varNode.getParent(); + + boolean isObjLit = rvalue.isObjectLit(); + int numChanges = 0; + + if (isObjLit) { + numChanges += declareVarsForObjLitValues( + n, name, rvalue, varNode, gramps.getChildBefore(varNode), + gramps); + } + + numChanges += addStubsForUndeclaredProperties(n, name, gramps, varNode); + + if (isObjLit && n.canEliminate()) { + varNode.removeChild(ref.node); + if (!varNode.hasChildren()) { + gramps.removeChild(varNode); + } + numChanges++; + + // Clear out the object reference, since we've eliminated it from the + // parse tree. + ref.node = null; + } + + if (numChanges > 0) { + compiler.reportCodeChange(); + } + } + + /** + * Updates the first initialization (a.k.a "declaration") of a global name + * that occurs at a FUNCTION node. See comment for + * {@link #updateObjLitOrFunctionDeclaration}. + * + * @param n An object representing a global name (e.g. "a") + */ + private void updateFunctionDeclarationAtFunctionNode( + Name n, boolean canCollapseChildNames) { + if (!canCollapseChildNames) { + return; + } + + Ref ref = n.getDeclaration(); + String fnName = ref.node.getString(); + addStubsForUndeclaredProperties( + n, fnName, ref.node.getAncestor(2), ref.node.getParent()); + } + + /** + * Declares global variables to serve as aliases for the values in an object + * literal, optionally removing all of the object literal's keys and values. + * + * @param alias The object literal's flattened name (e.g. "a$b$c") + * @param objlit The OBJLIT node + * @param varNode The VAR node to which new global variables should be added + * as children + * @param nameToAddAfter The child of {@code varNode} after which new + * variables should be added (may be null) + * @param varParent {@code varNode}'s parent + * @return The number of variables added + */ + private int declareVarsForObjLitValues( + Name objlitName, String alias, Node objlit, Node varNode, + Node nameToAddAfter, Node varParent) { + int numVars = 0; + int arbitraryNameCounter = 0; + boolean discardKeys = !objlitName.shouldKeepKeys(); + + for (Node key = objlit.getFirstChild(), nextKey; key != null; + key = nextKey) { + Node value = key.getFirstChild(); + nextKey = key.getNext(); + + // A get or a set can not be rewritten as a VAR. + if (key.isGetterDef() || key.isSetterDef()) { + continue; + } + + // We generate arbitrary names for keys that aren't valid JavaScript + // identifiers, since those keys are never referenced. (If they were, + // this object literal's child names wouldn't be collapsible.) The only + // reason that we don't eliminate them entirely is the off chance that + // their values are expressions that have side effects. + boolean isJsIdentifier = !key.isNumber() && + TokenStream.isJSIdentifier(key.getString()); + String propName = isJsIdentifier ? + key.getString() : String.valueOf(++arbitraryNameCounter); + + // If the name cannot be collapsed, skip it. + String qName = objlitName.getFullName() + '.' + propName; + Name p = nameMap.get(qName); + if (p != null && !p.canCollapse()) { + continue; + } + + String propAlias = appendPropForAlias(alias, propName); + Node refNode = null; + if (discardKeys) { + objlit.removeChild(key); + value.detachFromParent(); + } else { + // Substitute a reference for the value. + refNode = IR.name(propAlias); + if (key.getBooleanProp(Node.IS_CONSTANT_NAME)) { + refNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); + } + + key.replaceChild(value, refNode); + } + + // Declare the collapsed name as a variable with the original value. + Node nameNode = IR.name(propAlias); + nameNode.addChildToFront(value); + if (key.getBooleanProp(Node.IS_CONSTANT_NAME)) { + nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); + } + Node newVar = IR.var(nameNode) + .copyInformationFromForTree(key); + if (nameToAddAfter != null) { + varParent.addChildAfter(newVar, nameToAddAfter); + } else { + varParent.addChildBefore(newVar, varNode); + } + compiler.reportCodeChange(); + nameToAddAfter = newVar; + + // Update the global name's node ancestry if it hasn't already been + // done. (Duplicate keys in an object literal can bring us here twice + // for the same global name.) + if (isJsIdentifier && p != null) { + if (!discardKeys) { + Ref newAlias = + p.getDeclaration().cloneAndReclassify(Ref.Type.ALIASING_GET); + newAlias.node = refNode; + p.addRef(newAlias); + } + + p.getDeclaration().node = nameNode; + + if (value.isFunction()) { + checkForHosedThisReferences(value, value.getJSDocInfo(), p); + } + } + + numVars++; + } + return numVars; + } + + /** + * Adds global variable "stubs" for any properties of a global name that are + * only set in a local scope or read but never set. + * + * @param n An object representing a global name (e.g. "a", "a.b.c") + * @param alias The flattened name of the object whose properties we are + * adding stubs for (e.g. "a$b$c") + * @param parent The node to which new global variables should be added + * as children + * @param addAfter The child of after which new + * variables should be added (may be null) + * @return The number of variables added + */ + private int addStubsForUndeclaredProperties( + Name n, String alias, Node parent, Node addAfter) { + Preconditions.checkState(n.canCollapseUnannotatedChildNames()); + Preconditions.checkArgument(NodeUtil.isStatementBlock(parent)); + Preconditions.checkNotNull(addAfter); + int numStubs = 0; + if (n.props != null) { + for (Name p : n.props) { + if (p.needsToBeStubbed()) { + String propAlias = appendPropForAlias(alias, p.getBaseName()); + Node nameNode = IR.name(propAlias); + Node newVar = IR.var(nameNode) + .copyInformationFromForTree(addAfter); + parent.addChildAfter(newVar, addAfter); + addAfter = newVar; + numStubs++; + compiler.reportCodeChange(); + + // Determine if this is a constant var by checking the first + // reference to it. Don't check the declaration, as it might be null. + if (p.getRefs().get(0).node.getLastChild().getBooleanProp( + Node.IS_CONSTANT_NAME)) { + nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); + } + } + } + } + return numStubs; + } + + private static String appendPropForAlias(String root, String prop) { + if (prop.indexOf('$') != -1) { + // Encode '$' in a property as '$0'. Because '0' cannot be the + // start of an identifier, this will never conflict with our + // encoding from '.' -> '$'. + prop = prop.replace("$", "$0"); + } + return root + '$' + prop; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CollapseVariableDeclarations.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CollapseVariableDeclarations.java new file mode 100644 index 0000000..e0466ea --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CollapseVariableDeclarations.java @@ -0,0 +1,250 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.List; +import java.util.Set; + +/** + * Collapses multiple variable declarations into a single one. i.e the + * following: + * + *

+ * var a;
+ * var b = 1;
+ * var c = 2;
+ * 
+ * + * becomes: + * + *
var a, b = 1, c = 2;
+ * + * This reduces the generated code size. More optimizations are possible: + *
  • Group all variable declarations inside a function into one such variable. + * declaration block.
  • + *
  • Re-use variables instead of declaring a new one if they are used for + * only part of a function.
  • + * + * Similarly, also collapses assigns like: + * + *
    + * a = true;
    + * b = true;
    + * var c = true;
    + * 
    + * + * becomes: + * + *
    var c = b = a = true;
    + * + */ +class CollapseVariableDeclarations implements CompilerPass { + /** Reference to JS Compiler */ + private final AbstractCompiler compiler; + + /** Encapsulation of information about a variable declaration collapse */ + private static class Collapse { + /** + * Variable declaration that any following var nodes should be + * collapsed into + */ + final Node startNode; + + /** + * Last node (non-inclusive) of the chain of nodes to collapse. + */ + final Node endNode; + + /** Parent of the nodes to the collapse */ + final Node parent; + + Collapse(Node startNode, Node endNode, Node parent) { + this.startNode = startNode; + this.endNode = endNode; + this.parent = parent; + } + } + + /** + * Collapses to do in this pass. + */ + private final List collapses = Lists.newArrayList(); + + /** + * Nodes we've already looked at for collapsing, so that we don't look at them + * again (we look ahead when examining what nodes can be collapsed, and the + * node traversal may give them to us again) + */ + private final Set nodesToCollapse = Sets.newHashSet(); + + CollapseVariableDeclarations(AbstractCompiler compiler) { + Preconditions.checkState(!compiler.getLifeCycleStage().isNormalized()); + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + collapses.clear(); + nodesToCollapse.clear(); + + NodeTraversal.traverse(compiler, root, new GatherCollapses()); + + if (!collapses.isEmpty()) { + applyCollapses(); + compiler.reportCodeChange(); + } + } + + /** + * Gathers all of the variable declarations / assignments that should be + * collapsed into one. + * + * We do not do the collapsing as we go since node traversal would be affected + * by the changes we are making to the parse tree. + */ + private class GatherCollapses extends AbstractPostOrderCallback { + + // If a VAR is declared like + // var x; + // then we should not create new VAR nodes for it later in the tree. + // This is a workaround for a bug in Firefox. + private final Set blacklistedVars = Sets.newHashSet(); + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isVar()) { + blacklistStubVars(t, n); + } + + // Only care about var nodes + if (!n.isVar() && !canBeRedeclared(n, t.getScope())) return; + + // If we've already looked at this node, skip it + if (nodesToCollapse.contains(n)) return; + + // Adjacent VAR children of an IF node are the if and else parts and can't + // be collapsed + if (parent.isIf()) return; + + Node varNode = n; + + boolean hasVar = n.isVar(); + + // Find variable declarations that follow this one (if any) + n = n.getNext(); + + boolean hasNodesToCollapse = false; + + while (n != null && + (n.isVar() || canBeRedeclared(n, t.getScope()))) { + + if (n.isVar()) { + blacklistStubVars(t, n); + hasVar = true; + } + + nodesToCollapse.add(n); + hasNodesToCollapse = true; + + n = n.getNext(); + } + + if (hasNodesToCollapse && hasVar) { + nodesToCollapse.add(varNode); + collapses.add(new Collapse(varNode, n, parent)); + } + } + + private void blacklistStubVars(NodeTraversal t, Node varNode) { + for (Node child = varNode.getFirstChild(); + child != null; child = child.getNext()) { + if (child.getFirstChild() == null) { + blacklistedVars.add(t.getScope().getVar(child.getString())); + } + } + } + + private boolean canBeRedeclared(Node n, Scope s) { + if (!NodeUtil.isExprAssign(n)) { + return false; + } + Node assign = n.getFirstChild(); + Node lhs = assign.getFirstChild(); + + if (!lhs.isName()) { + return false; + } + + Var var = s.getVar(lhs.getString()); + return var != null + && var.getScope() == s + && !isNamedParameter(var) + && !blacklistedVars.contains(var); + } + } + + private boolean isNamedParameter(Var v) { + return v.getParentNode().isParamList(); + } + + private void applyCollapses() { + for (Collapse collapse : collapses) { + + Node var = new Node(Token.VAR); + var.copyInformationFrom(collapse.startNode); + collapse.parent.addChildBefore(var, collapse.startNode); + + boolean redeclaration = false; + for (Node n = collapse.startNode; n != collapse.endNode;) { + Node next = n.getNext(); + + Preconditions.checkState(var.getNext() == n); + collapse.parent.removeChildAfter(var); + + if (n.isVar()) { + while(n.hasChildren()) { + var.addChildToBack(n.removeFirstChild()); + } + } else { + Node assign = n.getFirstChild(); + Node lhs = assign.getFirstChild(); + Preconditions.checkState(lhs.isName()); + Node rhs = assign.getLastChild(); + lhs.addChildToBack(rhs.detachFromParent()); + var.addChildToBack(lhs.detachFromParent()); + redeclaration = true; + } + n = next; + } + + if (redeclaration) { + JSDocInfo info = new JSDocInfo(); + info.addSuppression("duplicate"); + var.setJSDocInfo(info); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CombinedCompilerPass.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CombinedCompilerPass.java new file mode 100644 index 0000000..28b29e5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CombinedCompilerPass.java @@ -0,0 +1,195 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.rhino.Node; + +import java.util.List; + +/** + *

    A compiler pass combining multiple {@link Callback} + * and {@link ScopedCallback} objects. This pass can be used to separate + * logically different verifications without incurring any additional traversal + * and CFG generation costs.

    + * + *

    Due to this compiler pass' nature, none of the callbacks may mutate + * the parse tree.

    + * + *

    TODO(user): + * This combined pass is currently limited in the type of callbacks it can + * combine due to the difficulty of handling NodeTraversal's methods that + * initiate more recursion (e.g., {@link NodeTraversal#traverse(Node)} and + * {@link NodeTraversal#traverseInnerNode(Node, Node, Scope)}). The + * {@link NodeTraversal} object passed to the individual callbacks should + * be instrumented to emulate the correct behavior. For instance, + * one could create a {@link NodeTraversal} whose + * {@link NodeTraversal#traverseInnerNode(Node, Node, Scope)} ties + * back into this compiler pass to give it context about what combined + * passes are doing.

    + * + */ +final class CombinedCompilerPass implements HotSwapCompilerPass, + ScopedCallback { + + /** The callbacks that this pass combines. */ + private final CallbackWrapper[] callbacks; + private final AbstractCompiler compiler; + + /** + * Creates a combined compiler pass. + * @param compiler the compiler + */ + CombinedCompilerPass( + AbstractCompiler compiler, Callback... callbacks) { + this(compiler, Lists.newArrayList(callbacks)); + } + + CombinedCompilerPass( + AbstractCompiler compiler, List callbacks) { + this.compiler = compiler; + this.callbacks = new CallbackWrapper[callbacks.size()]; + for (int i = 0; i < callbacks.size(); i++) { + this.callbacks[i] = new CallbackWrapper(callbacks.get(i)); + } + } + + static void traverse(AbstractCompiler compiler, Node root, + List callbacks) { + if (callbacks.size() == 1) { + NodeTraversal.traverse(compiler, root, callbacks.get(0)); + } else { + (new CombinedCompilerPass(compiler, callbacks)).process(null, root); + } + } + + /** + * Maintains information about a callback in order to simulate it being the + * exclusive client of the shared {@link NodeTraversal}. In particular, this + * class simulates abbreviating the traversal when the wrapped callback + * returns false for + * {@link Callback#shouldTraverse(NodeTraversal, Node, Node)}. + * The callback becomes inactive (i.e., traversal messages are not sent to it) + * until the main traversal revisits the node during the post-order visit. + * + */ + private static class CallbackWrapper { + /** The callback being wrapped. Never null. */ + private final Callback callback; + /** + * if (callback instanceof ScopedCallback), then scopedCallback points + * to an instance of ScopedCallback, otherwise scopedCallback points to null + */ + private final ScopedCallback scopedCallback; + + /** + * The node that {@link Callback#shouldTraverse(NodeTraversal, Node, Node)} + * returned false for. The wrapped callback doesn't receive messages until + * after this node is revisited in the post-order traversal. + */ + private Node waiting = null; + + private CallbackWrapper(Callback callback) { + this.callback = callback; + if (callback instanceof ScopedCallback) { + scopedCallback = (ScopedCallback) callback; + } else { + scopedCallback = null; + } + } + + /** + * Visits the node unless the wrapped callback is inactive. Activates the + * callback if appropriate. + */ + void visitOrMaybeActivate(NodeTraversal t, Node n, Node parent) { + if (isActive()) { + callback.visit(t, n, parent); + } else if (waiting == n) { + waiting = null; + } + } + + void shouldTraverseIfActive(NodeTraversal t, Node n, Node parent) { + if (isActive() && !callback.shouldTraverse(t, n, parent)) { + waiting = n; + } + } + + void enterScopeIfActive(NodeTraversal t) { + if (isActive() && scopedCallback != null) { + scopedCallback.enterScope(t); + } + } + + void exitScopeIfActive(NodeTraversal t) { + if (isActive() && scopedCallback != null) { + scopedCallback.exitScope(t); + } + } + + boolean isActive() { + return waiting == null; + } + } + + @Override + public final void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + NodeTraversal.traverse(compiler, scriptRoot, this); + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + for (CallbackWrapper callback : callbacks) { + callback.shouldTraverseIfActive(t, n, parent); + } + // Note that this method could return false if all callbacks are inactive. + // This apparent optimization would make this method more expensive + // in the typical case where not all nodes are inactive. It is + // very unlikely that many all callbacks would be inactive at the same + // time (indeed, there are several checking passes that never return false). + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + for (CallbackWrapper callback : callbacks) { + callback.visitOrMaybeActivate(t, n, parent); + } + } + + @Override + public void enterScope(NodeTraversal t) { + for (CallbackWrapper callback : callbacks) { + callback.enterScopeIfActive(t); + } + } + + @Override + public void exitScope(NodeTraversal t) { + for (CallbackWrapper callback : callbacks) { + callback.exitScopeIfActive(t); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CommandLineRunner.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CommandLineRunner.java new file mode 100644 index 0000000..2d3b139 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CommandLineRunner.java @@ -0,0 +1,942 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.common.io.Files; +import com.google.common.io.LimitInputStream; + +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.Option; +import org.kohsuke.args4j.OptionDef; +import org.kohsuke.args4j.spi.OptionHandler; +import org.kohsuke.args4j.spi.Parameters; +import org.kohsuke.args4j.spi.Setter; +import org.kohsuke.args4j.spi.StringOptionHandler; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.nio.charset.Charset; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * CommandLineRunner translates flags into Java API calls on the Compiler. + * + * This class may be extended and used to create other Java classes + * that behave the same as running the Compiler from the command line. If you + * want to run the compiler in-process in Java, you should look at this class + * for hints on what API calls to make, but you should not use this class + * directly. + * + * Example: + *
    + * class MyCommandLineRunner extends CommandLineRunner {
    + *   MyCommandLineRunner(String[] args) {
    + *     super(args);
    + *   }
    + *
    + *   {@code @Override} protected CompilerOptions createOptions() {
    + *     CompilerOptions options = super.createOptions();
    + *     addMyCrazyCompilerPassThatOutputsAnExtraFile(options);
    + *     return options;
    + *   }
    + *
    + *   public static void main(String[] args) {
    + *     MyCommandLineRunner runner = new MyCommandLineRunner(args);
    + *     if (runner.shouldRunCompiler()) {
    + *       runner.run();
    + *     } else {
    + *       System.exit(-1);
    + *     }
    + *   }
    + * }
    + * 
    + * + * This class is totally not thread-safe. + * + * @author bolinfest@google.com (Michael Bolin) + */ +public class CommandLineRunner extends + AbstractCommandLineRunner { + // I don't really care about unchecked warnings in this class. + @SuppressWarnings("unchecked") + private static class Flags { + private static final WarningGuardSpec warningGuardSpec = + new WarningGuardSpec(); + + @Option(name = "--help", + handler = BooleanOptionHandler.class, + usage = "Displays this message") + private boolean display_help = false; + + @Option(name = "--print_tree", + handler = BooleanOptionHandler.class, + usage = "Prints out the parse tree and exits") + private boolean print_tree = false; + + @Option(name = "--print_ast", + handler = BooleanOptionHandler.class, + usage = "Prints a dot file describing the internal abstract syntax" + + " tree and exits") + private boolean print_ast = false; + + @Option(name = "--print_pass_graph", + handler = BooleanOptionHandler.class, + usage = "Prints a dot file describing the passes that will get run" + + " and exits") + private boolean print_pass_graph = false; + + // Turn on (very slow) extra sanity checks for use when modifying the + // compiler. + @Option(name = "--jscomp_dev_mode", + // hidden, no usage + aliases = {"--dev_mode"}) + private CompilerOptions.DevMode jscomp_dev_mode = + CompilerOptions.DevMode.OFF; + + @Option(name = "--logging_level", + usage = "The logging level (standard java.util.logging.Level" + + " values) for Compiler progress. Does not control errors or" + + " warnings for the JavaScript code under compilation") + private String logging_level = Level.WARNING.getName(); + + @Option(name = "--externs", + usage = "The file containing JavaScript externs. You may specify" + + " multiple") + private List externs = Lists.newArrayList(); + + @Option(name = "--js", + usage = "The JavaScript filename. You may specify multiple") + private List js = Lists.newArrayList(); + + @Option(name = "--js_output_file", + usage = "Primary output filename. If not specified, output is " + + "written to stdout") + private String js_output_file = ""; + + @Option(name = "--module", + usage = "A JavaScript module specification. The format is " + + ":[:[,...][:]]]. Module names must be " + + "unique. Each dep is the name of a module that this module " + + "depends on. Modules must be listed in dependency order, and JS " + + "source files must be listed in the corresponding order. Where " + + "--module flags occur in relation to --js flags is unimportant. " + + "Provide the value 'auto' to trigger module creation from CommonJS" + + "modules.") + private List module = Lists.newArrayList(); + + @Option(name = "--variable_map_input_file", + usage = "File containing the serialized version of the variable " + + "renaming map produced by a previous compilation") + private String variable_map_input_file = ""; + + @Option(name = "--property_map_input_file", + usage = "File containing the serialized version of the property " + + "renaming map produced by a previous compilation") + private String property_map_input_file = ""; + + @Option(name = "--variable_map_output_file", + usage = "File where the serialized version of the variable " + + "renaming map produced should be saved") + private String variable_map_output_file = ""; + + @Option(name = "--create_name_map_files", + handler = BooleanOptionHandler.class, + usage = "If true, variable renaming and property renaming map " + + "files will be produced as {binary name}_vars_map.out and " + + "{binary name}_props_map.out. Note that this flag cannot be used " + + "in conjunction with either variable_map_output_file or " + + "property_map_output_file") + private boolean create_name_map_files = false; + + @Option(name = "--property_map_output_file", + usage = "File where the serialized version of the property " + + "renaming map produced should be saved") + private String property_map_output_file = ""; + + @Option(name = "--third_party", + handler = BooleanOptionHandler.class, + usage = "Check source validity but do not enforce Closure style " + + "rules and conventions") + private boolean third_party = false; + + + @Option(name = "--summary_detail_level", + usage = "Controls how detailed the compilation summary is. Values:" + + " 0 (never print summary), 1 (print summary only if there are " + + "errors or warnings), 2 (print summary if the 'checkTypes' " + + "diagnostic group is enabled, see --jscomp_warning), " + + "3 (always print summary). The default level is 1") + private int summary_detail_level = 1; + + @Option(name = "--output_wrapper", + usage = "Interpolate output into this string at the place denoted" + + " by the marker token %output%. Use marker token %output|jsstring%" + + " to do js string escaping on the output.") + private String output_wrapper = ""; + + @Option(name = "--module_wrapper", + usage = "An output wrapper for a JavaScript module (optional). " + + "The format is :. The module name must correspond " + + "with a module specified using --module. The wrapper must " + + "contain %s as the code placeholder. The %basename% placeholder can " + + "also be used to substitute the base name of the module output file.") + private List module_wrapper = Lists.newArrayList(); + + @Option(name = "--module_output_path_prefix", + usage = "Prefix for filenames of compiled JS modules. " + + ".js will be appended to this prefix. Directories " + + "will be created as needed. Use with --module") + private String module_output_path_prefix = "./"; + + @Option(name = "--create_source_map", + usage = "If specified, a source map file mapping the generated " + + "source files back to the original source file will be " + + "output to the specified path. The %outname% placeholder will " + + "expand to the name of the output file that the source map " + + "corresponds to.") + private String create_source_map = ""; + + @Option(name = "--source_map_format", + usage = "The source map format to produce. " + + "Options: V1, V2, V3, DEFAULT. DEFAULT produces V2.") + private SourceMap.Format source_map_format = SourceMap.Format.DEFAULT; + + // Used to define the flag, values are stored by the handler. + @SuppressWarnings("unused") + @Option(name = "--jscomp_error", + handler = WarningGuardErrorOptionHandler.class, + usage = "Make the named class of warnings an error. Options:" + + DiagnosticGroups.DIAGNOSTIC_GROUP_NAMES) + private List jscomp_error = Lists.newArrayList(); + + // Used to define the flag, values are stored by the handler. + @SuppressWarnings("unused") + @Option(name = "--jscomp_warning", + handler = WarningGuardWarningOptionHandler.class, + usage = "Make the named class of warnings a normal warning. " + + "Options:" + DiagnosticGroups.DIAGNOSTIC_GROUP_NAMES) + private List jscomp_warning = Lists.newArrayList(); + + // Used to define the flag, values are stored by the handler. + @SuppressWarnings("unused") + @Option(name = "--jscomp_off", + handler = WarningGuardOffOptionHandler.class, + usage = "Turn off the named class of warnings. Options:" + + DiagnosticGroups.DIAGNOSTIC_GROUP_NAMES) + private List jscomp_off = Lists.newArrayList(); + + @Option(name = "--define", + aliases = {"--D", "-D"}, + usage = "Override the value of a variable annotated @define. " + + "The format is [=], where is the name of a @define " + + "variable and is a boolean, number, or a single-quoted string " + + "that contains no single quotes. If [=] is omitted, " + + "the variable is marked true") + private List define = Lists.newArrayList(); + + @Option(name = "--charset", + usage = "Input and output charset for all files. By default, we " + + "accept UTF-8 as input and output US_ASCII") + private String charset = ""; + + @Option(name = "--compilation_level", + usage = "Specifies the compilation level to use. Options: " + + "WHITESPACE_ONLY, SIMPLE_OPTIMIZATIONS, ADVANCED_OPTIMIZATIONS") + private CompilationLevel compilation_level = + CompilationLevel.SIMPLE_OPTIMIZATIONS; + + @Option(name = "--use_types_for_optimization", + usage = "Experimental: perform additional optimizations " + + "based on available information. Inaccurate type annotations " + + "may result in incorrect results.") + private boolean use_types_for_optimization = false; + + @Option(name = "--warning_level", + usage = "Specifies the warning level to use. Options: " + + "QUIET, DEFAULT, VERBOSE") + private WarningLevel warning_level = WarningLevel.DEFAULT; + + @Option(name = "--use_only_custom_externs", + handler = BooleanOptionHandler.class, + usage = "Specifies whether the default externs should be excluded") + private boolean use_only_custom_externs = false; + + @Option(name = "--debug", + handler = BooleanOptionHandler.class, + usage = "Enable debugging options") + private boolean debug = false; + + @Option(name = "--generate_exports", + handler = BooleanOptionHandler.class, + usage = "Generates export code for those marked with @export") + private boolean generate_exports = false; + + @Option(name = "--formatting", + usage = "Specifies which formatting options, if any, should be " + + "applied to the output JS. Options: " + + "PRETTY_PRINT, PRINT_INPUT_DELIMITER, SINGLE_QUOTES") + private List formatting = Lists.newArrayList(); + + @Option(name = "--process_common_js_modules", + usage = "Process CommonJS modules to a concatenable form.") + private boolean process_common_js_modules = false; + + @Option(name = "--common_js_module_path_prefix", + usage = "Path prefix to be removed from CommonJS module names.") + private String common_js_path_prefix = + ProcessCommonJSModules.DEFAULT_FILENAME_PREFIX; + + @Option(name = "--common_js_entry_module", + usage = "Root of your common JS dependency hierarchy. "+ + "Your main script.") + private String common_js_entry_module; + + @Option(name = "--transform_amd_modules", + usage = "Transform AMD to CommonJS modules.") + private boolean transform_amd_modules = false; + + @Option(name = "--process_closure_primitives", + handler = BooleanOptionHandler.class, + usage = "Processes built-ins from the Closure library, such as " + + "goog.require(), goog.provide(), and goog.exportSymbol()") + private boolean process_closure_primitives = true; + + @Option(name = "--manage_closure_dependencies", + handler = BooleanOptionHandler.class, + usage = "Automatically sort dependencies so that a file that " + + "goog.provides symbol X will always come before a file that " + + "goog.requires symbol X. If an input provides symbols, and " + + "those symbols are never required, then that input will not " + + "be included in the compilation.") + private boolean manage_closure_dependencies = false; + + @Option(name = "--only_closure_dependencies", + handler = BooleanOptionHandler.class, + usage = "Only include files in the transitive dependency of the " + + "entry points (specified by closure_entry_point). Files that do " + + "not provide dependencies will be removed. This supersedes" + + "manage_closure_dependencies") + private boolean only_closure_dependencies = false; + + @Option(name = "--closure_entry_point", + usage = "Entry points to the program. Must be goog.provide'd " + + "symbols. Any goog.provide'd symbols that are not a transitive " + + "dependency of the entry points will be removed. Files without " + + "goog.provides, and their dependencies, will always be left in. " + + "If any entry points are specified, then the " + + "manage_closure_dependencies option will be set to true and " + + "all files will be sorted in dependency order.") + private List closure_entry_point = Lists.newArrayList(); + + @Option(name = "--process_jquery_primitives", + handler = BooleanOptionHandler.class, + usage = "Processes built-ins from the Jquery library, such as " + + "jQuery.fn and jQuery.extend()") + private boolean process_jquery_primitives = false; + + @Option(name = "--output_manifest", + usage = "Prints out a list of all the files in the compilation. " + + "If --manage_closure_dependencies is on, this will not include " + + "files that got dropped because they were not required. " + + "The %outname% placeholder expands to the JS output file. " + + "If you're using modularization, using %outname% will create " + + "a manifest for each module.") + private String output_manifest = ""; + + @Option(name = "--output_module_dependencies", + usage = "Prints out a JSON file of dependencies between modules.") + private String output_module_dependencies = ""; + + @Option(name = "--accept_const_keyword", + usage = "Allows usage of const keyword.") + private boolean accept_const_keyword = false; + + @Option(name = "--language_in", + usage = "Sets what language spec that input sources conform. " + + "Options: ECMASCRIPT3 (default), ECMASCRIPT5, ECMASCRIPT5_STRICT") + private String language_in = "ECMASCRIPT3"; + + @Option(name = "--version", + handler = BooleanOptionHandler.class, + usage = "Prints the compiler version to stderr.") + private boolean version = false; + + @Option(name = "--translations_file", + usage = "Source of translated messages. Currently only supports XTB.") + private String translationsFile = ""; + + @Option(name = "--translations_project", + usage = "Scopes all translations to the specified project." + + "When specified, we will use different message ids so that messages " + + "in different projects can have different translations.") + private String translationsProject = null; + + @Option(name = "--flagfile", + usage = "A file containing additional command-line options.") + private String flag_file = ""; + + @Option(name = "--warnings_whitelist_file", + usage = "A file containing warnings to suppress. Each line should be " + + "of the form\n" + + ":? ") + private String warnings_whitelist_file = ""; + + @Argument + private List arguments = Lists.newArrayList(); + + /** + * Users may specify JS inputs via the legacy {@code --js} option, as well + * as via additional arguments to the Closure Compiler. For example, it is + * convenient to leverage the additional arguments feature when using the + * Closure Compiler in combination with {@code find} and {@code xargs}: + *
    +     * find MY_JS_SRC_DIR -name '*.js' \
    +     *     | xargs java -jar compiler.jar --manage_closure_dependencies
    +     * 
    + * The {@code find} command will produce a list of '*.js' source files in + * the {@code MY_JS_SRC_DIR} directory while {@code xargs} will convert them + * to a single, space-delimited set of arguments that are appended to the + * {@code java} command to run the Compiler. + *

    + * Note that it is important to use the + * {@code --manage_closure_dependencies} option in this case because the + * order produced by {@code find} is unlikely to be sorted correctly with + * respect to {@code goog.provide()} and {@code goog.requires()}. + */ + List getJsFiles() { + List allJsInputs = Lists.newArrayListWithCapacity( + js.size() + arguments.size()); + allJsInputs.addAll(js); + allJsInputs.addAll(arguments); + return allJsInputs; + } + + // Our own option parser to be backwards-compatible. + // It needs to be public because of the crazy reflection that args4j does. + public static class BooleanOptionHandler extends OptionHandler { + private static final Set TRUES = + Sets.newHashSet("true", "on", "yes", "1"); + private static final Set FALSES = + Sets.newHashSet("false", "off", "no", "0"); + + public BooleanOptionHandler( + CmdLineParser parser, OptionDef option, + Setter setter) { + super(parser, option, setter); + } + + @Override + public int parseArguments(Parameters params) throws CmdLineException { + String param = null; + try { + param = params.getParameter(0); + } catch (CmdLineException e) {} + + if (param == null) { + setter.addValue(true); + return 0; + } else { + String lowerParam = param.toLowerCase(); + if (TRUES.contains(lowerParam)) { + setter.addValue(true); + } else if (FALSES.contains(lowerParam)) { + setter.addValue(false); + } else { + setter.addValue(true); + return 0; + } + return 1; + } + } + + @Override + public String getDefaultMetaVariable() { + return null; + } + } + + // Our own parser for warning guards that preserves the original order + // of the flags. + public static class WarningGuardErrorOptionHandler + extends StringOptionHandler { + public WarningGuardErrorOptionHandler( + CmdLineParser parser, OptionDef option, + Setter setter) { + super(parser, option, new WarningGuardSetter(setter, CheckLevel.ERROR)); + } + } + + public static class WarningGuardWarningOptionHandler + extends StringOptionHandler { + public WarningGuardWarningOptionHandler( + CmdLineParser parser, OptionDef option, + Setter setter) { + super(parser, option, + new WarningGuardSetter(setter, CheckLevel.WARNING)); + } + } + + public static class WarningGuardOffOptionHandler + extends StringOptionHandler { + public WarningGuardOffOptionHandler( + CmdLineParser parser, OptionDef option, + Setter setter) { + super(parser, option, new WarningGuardSetter(setter, CheckLevel.OFF)); + } + } + + private static class WarningGuardSetter implements Setter { + private final Setter proxy; + private final CheckLevel level; + + private WarningGuardSetter( + Setter proxy, CheckLevel level) { + this.proxy = proxy; + this.level = level; + } + + @Override public boolean isMultiValued() { + return proxy.isMultiValued(); + } + + @Override public Class getType() { + return (Class) proxy.getType(); + } + + @Override public void addValue(String value) throws CmdLineException { + proxy.addValue(value); + warningGuardSpec.add(level, value); + } + } + } + + /** + * Set of options that can be used with the --formatting flag. + */ + private static enum FormattingOption { + PRETTY_PRINT, + PRINT_INPUT_DELIMITER, + SINGLE_QUOTES + ; + + private void applyToOptions(CompilerOptions options) { + switch (this) { + case PRETTY_PRINT: + options.prettyPrint = true; + break; + case PRINT_INPUT_DELIMITER: + options.printInputDelimiter = true; + break; + case SINGLE_QUOTES: + options.setPreferSingleQuotes(true); + break; + default: + throw new RuntimeException("Unknown formatting option: " + this); + } + } + } + + private final Flags flags = new Flags(); + + private boolean isConfigValid = false; + + /** + * Create a new command-line runner. You should only need to call + * the constructor if you're extending this class. Otherwise, the main + * method should instantiate it. + */ + protected CommandLineRunner(String[] args) { + super(); + initConfigFromFlags(args, System.err); + } + + protected CommandLineRunner(String[] args, PrintStream out, PrintStream err) { + super(out, err); + initConfigFromFlags(args, err); + } + + /** + * Split strings into tokens delimited by whitespace, but treat quoted + * strings as single tokens. Non-whitespace characters adjacent to quoted + * strings will be returned as part of the token. For example, the string + * {@code "--js='/home/my project/app.js'"} would be returned as a single + * token. + * + * @param lines strings to tokenize + * @return a list of tokens + */ + private List tokenizeKeepingQuotedStrings(List lines) { + List tokens = Lists.newArrayList(); + Pattern tokenPattern = + Pattern.compile("(?:[^ \t\f\\x0B'\"]|(?:'[^']*'|\"[^\"]*\"))+"); + + for (String line : lines) { + Matcher matcher = tokenPattern.matcher(line); + while (matcher.find()) { + tokens.add(matcher.group(0)); + } + } + return tokens; + } + + private List processArgs(String[] args) { + // Args4j has a different format that the old command-line parser. + // So we use some voodoo to get the args into the format that args4j + // expects. + Pattern argPattern = Pattern.compile("(--[a-zA-Z_]+)=(.*)"); + Pattern quotesPattern = Pattern.compile("^['\"](.*)['\"]$"); + List processedArgs = Lists.newArrayList(); + + for (String arg : args) { + Matcher matcher = argPattern.matcher(arg); + if (matcher.matches()) { + processedArgs.add(matcher.group(1)); + + String value = matcher.group(2); + Matcher quotesMatcher = quotesPattern.matcher(value); + if (quotesMatcher.matches()) { + processedArgs.add(quotesMatcher.group(1)); + } else { + processedArgs.add(value); + } + } else { + processedArgs.add(arg); + } + } + + return processedArgs; + } + + private void processFlagFile(PrintStream err) + throws CmdLineException, IOException { + File flagFileInput = new File(flags.flag_file); + List argsInFile = tokenizeKeepingQuotedStrings( + Files.readLines(flagFileInput, Charset.defaultCharset())); + + flags.flag_file = ""; + List processedFileArgs + = processArgs(argsInFile.toArray(new String[] {})); + CmdLineParser parserFileArgs = new CmdLineParser(flags); + Flags.warningGuardSpec.clear(); + parserFileArgs.parseArgument(processedFileArgs.toArray(new String[] {})); + + // Currently we are not supporting this (prevent direct/indirect loops) + if (!flags.flag_file.equals("")) { + err.println("ERROR - Arguments in the file cannot contain " + + "--flagfile option."); + isConfigValid = false; + } + } + + private void initConfigFromFlags(String[] args, PrintStream err) { + + List processedArgs = processArgs(args); + + CmdLineParser parser = new CmdLineParser(flags); + Flags.warningGuardSpec.clear(); + isConfigValid = true; + try { + parser.parseArgument(processedArgs.toArray(new String[] {})); + // For contains --flagfile flag + if (!flags.flag_file.equals("")) { + processFlagFile(err); + } + } catch (CmdLineException e) { + err.println(e.getMessage()); + isConfigValid = false; + } catch (IOException ioErr) { + err.println("ERROR - " + flags.flag_file + " read error."); + isConfigValid = false; + } + + if (flags.version) { + err.println( + "Closure Compiler (http://code.google.com/closure/compiler)\n" + + "Version: " + Compiler.getReleaseVersion() + "\n" + + "Built on: " + Compiler.getReleaseDate()); + err.flush(); + } + + if (flags.process_common_js_modules) { + flags.process_closure_primitives = true; + flags.manage_closure_dependencies = true; + if (flags.common_js_entry_module == null) { + err.println("Please specify --common_js_entry_module."); + err.flush(); + isConfigValid = false; + } + flags.closure_entry_point = Lists.newArrayList( + ProcessCommonJSModules.toModuleName(flags.common_js_entry_module)); + } + + if (!isConfigValid || flags.display_help) { + isConfigValid = false; + parser.printUsage(err); + } else { + CodingConvention conv; + if (flags.third_party) { + conv = CodingConventions.getDefault(); + } else if (flags.process_jquery_primitives) { + conv = new JqueryCodingConvention(); + } else { + conv = new ClosureCodingConvention(); + } + + getCommandLineConfig() + .setPrintTree(flags.print_tree) + .setPrintAst(flags.print_ast) + .setPrintPassGraph(flags.print_pass_graph) + .setJscompDevMode(flags.jscomp_dev_mode) + .setLoggingLevel(flags.logging_level) + .setExterns(flags.externs) + .setJs(flags.getJsFiles()) + .setJsOutputFile(flags.js_output_file) + .setModule(flags.module) + .setVariableMapInputFile(flags.variable_map_input_file) + .setPropertyMapInputFile(flags.property_map_input_file) + .setVariableMapOutputFile(flags.variable_map_output_file) + .setCreateNameMapFiles(flags.create_name_map_files) + .setPropertyMapOutputFile(flags.property_map_output_file) + .setCodingConvention(conv) + .setSummaryDetailLevel(flags.summary_detail_level) + .setOutputWrapper(flags.output_wrapper) + .setModuleWrapper(flags.module_wrapper) + .setModuleOutputPathPrefix(flags.module_output_path_prefix) + .setCreateSourceMap(flags.create_source_map) + .setSourceMapFormat(flags.source_map_format) + .setWarningGuardSpec(Flags.warningGuardSpec) + .setDefine(flags.define) + .setCharset(flags.charset) + .setManageClosureDependencies(flags.manage_closure_dependencies) + .setOnlyClosureDependencies(flags.only_closure_dependencies) + .setClosureEntryPoints(flags.closure_entry_point) + .setOutputManifest(ImmutableList.of(flags.output_manifest)) + .setOutputModuleDependencies(flags.output_module_dependencies) + .setAcceptConstKeyword(flags.accept_const_keyword) + .setLanguageIn(flags.language_in) + .setProcessCommonJSModules(flags.process_common_js_modules) + .setCommonJSModulePathPrefix(flags.common_js_path_prefix) + .setTransformAMDToCJSModules(flags.transform_amd_modules) + .setWarningsWhitelistFile(flags.warnings_whitelist_file); + } + } + + @Override + protected CompilerOptions createOptions() { + CompilerOptions options = new CompilerOptions(); + if (flags.process_jquery_primitives) { + options.setCodingConvention(new JqueryCodingConvention()); + } else { + options.setCodingConvention(new ClosureCodingConvention()); + } + + CompilationLevel level = flags.compilation_level; + level.setOptionsForCompilationLevel(options); + + if (flags.debug) { + level.setDebugOptionsForCompilationLevel(options); + } + + if (flags.use_types_for_optimization) { + level.setTypeBasedOptimizationOptions(options); + } + + if (flags.generate_exports) { + options.setGenerateExports(flags.generate_exports); + } + + WarningLevel wLevel = flags.warning_level; + wLevel.setOptionsForWarningLevel(options); + for (FormattingOption formattingOption : flags.formatting) { + formattingOption.applyToOptions(options); + } + + options.closurePass = flags.process_closure_primitives; + + options.jqueryPass = flags.process_jquery_primitives && + CompilationLevel.ADVANCED_OPTIMIZATIONS == level; + + if (!flags.translationsFile.isEmpty()) { + try { + options.messageBundle = new XtbMessageBundle( + new FileInputStream(flags.translationsFile), + flags.translationsProject); + } catch (IOException e) { + throw new RuntimeException("Reading XTB file", e); + } + } else if (CompilationLevel.ADVANCED_OPTIMIZATIONS == level) { + // In SIMPLE or WHITESPACE mode, if the user hasn't specified a + // translations file, they might reasonably try to write their own + // implementation of goog.getMsg that makes the substitution at + // run-time. + // + // In ADVANCED mode, goog.getMsg is going to be renamed anyway, + // so we might as well inline it. + options.messageBundle = new EmptyMessageBundle(); + } + + return options; + } + + @Override + protected Compiler createCompiler() { + return new Compiler(getErrorPrintStream()); + } + + @Override + protected List createExterns() throws FlagUsageException, + IOException { + List externs = super.createExterns(); + if (flags.use_only_custom_externs || isInTestMode()) { + return externs; + } else { + List defaultExterns = getDefaultExterns(); + defaultExterns.addAll(externs); + return defaultExterns; + } + } + + // The externs expected in externs.zip, in sorted order. + private static final List DEFAULT_EXTERNS_NAMES = ImmutableList.of( + // JS externs + "es3.js", + "es5.js", + + // Event APIs + "w3c_event.js", + "w3c_event3.js", + "gecko_event.js", + "ie_event.js", + "webkit_event.js", + + // DOM apis + "w3c_dom1.js", + "w3c_dom2.js", + "w3c_dom3.js", + "gecko_dom.js", + "ie_dom.js", + "webkit_dom.js", + + // CSS apis + "w3c_css.js", + "gecko_css.js", + "ie_css.js", + "webkit_css.js", + + // Top-level namespaces + "google.js", + + "deprecated.js", + "fileapi.js", + "flash.js", + "gears_symbols.js", + "gears_types.js", + "gecko_xml.js", + "html5.js", + "ie_vml.js", + "iphone.js", + "webstorage.js", + "w3c_anim_timing.js", + "w3c_css3d.js", + "w3c_elementtraversal.js", + "w3c_geolocation.js", + "w3c_indexeddb.js", + "w3c_navigation_timing.js", + "w3c_range.js", + "w3c_selectors.js", + "w3c_xml.js", + "window.js", + "webkit_notifications.js", + "webgl.js"); + + /** + * @return a mutable list + * @throws IOException + */ + public static List getDefaultExterns() throws IOException { + InputStream input = CommandLineRunner.class.getResourceAsStream( + "/externs.zip"); + if (input == null) { + // In some environments, the externs.zip is relative to this class. + input = CommandLineRunner.class.getResourceAsStream("externs.zip"); + } + Preconditions.checkNotNull(input); + + ZipInputStream zip = new ZipInputStream(input); + Map externsMap = Maps.newHashMap(); + for (ZipEntry entry = null; (entry = zip.getNextEntry()) != null; ) { + BufferedInputStream entryStream = new BufferedInputStream( + new LimitInputStream(zip, entry.getSize())); + externsMap.put(entry.getName(), + SourceFile.fromInputStream( + // Give the files an odd prefix, so that they do not conflict + // with the user's files. + "externs.zip//" + entry.getName(), + entryStream)); + } + + Preconditions.checkState( + externsMap.keySet().equals(Sets.newHashSet(DEFAULT_EXTERNS_NAMES)), + "Externs zip must match our hard-coded list of externs."); + + // Order matters, so the resources must be added to the result list + // in the expected order. + List externs = Lists.newArrayList(); + for (String key : DEFAULT_EXTERNS_NAMES) { + externs.add(externsMap.get(key)); + } + + return externs; + } + + /** + * @return Whether the configuration is valid. + */ + public boolean shouldRunCompiler() { + return this.isConfigValid; + } + + /** + * Runs the Compiler. Exits cleanly in the event of an error. + */ + public static void main(String[] args) { + CommandLineRunner runner = new CommandLineRunner(args); + if (runner.shouldRunCompiler()) { + runner.run(); + } else { + System.exit(-1); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CompilationLevel.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CompilationLevel.java new file mode 100644 index 0000000..e6d5ef7 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CompilationLevel.java @@ -0,0 +1,214 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.CompilerOptions.Reach; + +/** + * A CompilationLevel represents the level of optimization that should be + * applied when compiling JavaScript code. + * + * @author bolinfest@google.com (Michael Bolin) + */ +public enum CompilationLevel { + + /** + * WHITESPACE_ONLY removes comments and extra whitespace in the input JS. + */ + WHITESPACE_ONLY, + + /** + * SIMPLE_OPTIMIZATIONS performs transformations to the input JS that do not + * require any changes to JS that depend on the input JS. For example, + * function arguments are renamed (which should not matter to code that + * depends on the input JS), but functions themselves are not renamed (which + * would otherwise require external code to change to use the renamed function + * names). + */ + SIMPLE_OPTIMIZATIONS, + + /** + * ADVANCED_OPTIMIZATIONS aggressively reduces code size by renaming function + * names and variables, removing code which is never called, etc. + */ + ADVANCED_OPTIMIZATIONS, + ; + + private CompilationLevel() {} + + public void setOptionsForCompilationLevel(CompilerOptions options) { + switch (this) { + case WHITESPACE_ONLY: + applyBasicCompilationOptions(options); + break; + case SIMPLE_OPTIMIZATIONS: + applySafeCompilationOptions(options); + break; + case ADVANCED_OPTIMIZATIONS: + applyFullCompilationOptions(options); + break; + default: + throw new RuntimeException("Unknown compilation level."); + } + } + + public void setDebugOptionsForCompilationLevel(CompilerOptions options) { + options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.UNMAPPED; + options.generatePseudoNames = true; + options.removeClosureAsserts = false; + // Don't shadow variables as it is too confusing. + options.shadowVariables = false; + } + + /** + * Gets options that only strip whitespace and comments. + * @param options The CompilerOptions object to set the options on. + */ + private static void applyBasicCompilationOptions(CompilerOptions options) { + options.skipAllCompilerPasses(); + + // Allows annotations that are not standard. + options.setWarningLevel(DiagnosticGroups.NON_STANDARD_JSDOC, + CheckLevel.OFF); + } + + /** + * Add options that are safe. Safe means options that won't break the + * JavaScript code even if no symbols are exported and no coding convention + * is used. + * @param options The CompilerOptions object to set the options on. + */ + private static void applySafeCompilationOptions(CompilerOptions options) { + // ReplaceIdGenerators is on by default, but should run in simple mode. + options.replaceIdGenerators = false; + + // Does not call applyBasicCompilationOptions(options) because the call to + // skipAllCompilerPasses() cannot be easily undone. + options.dependencyOptions.setDependencySorting(true); + options.closurePass = true; + options.setRenamingPolicy( + VariableRenamingPolicy.LOCAL, PropertyRenamingPolicy.OFF); + options.shadowVariables = true; + options.setInlineVariables(Reach.LOCAL_ONLY); + options.flowSensitiveInlineVariables = true; + options.setInlineFunctions(Reach.LOCAL_ONLY); + options.checkGlobalThisLevel = CheckLevel.OFF; + options.foldConstants = true; + options.coalesceVariableNames = true; + options.deadAssignmentElimination = true; + options.collapseVariableDeclarations = true; + options.convertToDottedProperties = true; + options.labelRenaming = true; + options.removeDeadCode = true; + options.optimizeArgumentsArray = true; + options.setRemoveUnusedVariables(Reach.LOCAL_ONLY); + options.collapseObjectLiterals = true; + options.protectHiddenSideEffects = true; + + // Allows annotations that are not standard. + options.setWarningLevel(DiagnosticGroups.NON_STANDARD_JSDOC, + CheckLevel.OFF); + } + + /** + * Add the options that will work only if the user exported all the symbols + * correctly. + * @param options The CompilerOptions object to set the options on. + */ + private static void applyFullCompilationOptions(CompilerOptions options) { + // Do not call applySafeCompilationOptions(options) because the call can + // create possible conflicts between multiple diagnostic groups. + + // All the safe optimizations. + options.dependencyOptions.setDependencySorting(true); + options.closurePass = true; + options.foldConstants = true; + options.coalesceVariableNames = true; + options.deadAssignmentElimination = true; + options.extractPrototypeMemberDeclarations = true; + options.collapseVariableDeclarations = true; + options.convertToDottedProperties = true; + options.rewriteFunctionExpressions = true; + options.labelRenaming = true; + options.removeDeadCode = true; + options.optimizeArgumentsArray = true; + options.collapseObjectLiterals = true; + options.protectHiddenSideEffects = true; + + // All the advance optimizations. + options.removeClosureAsserts = true; + options.aliasKeywords = true; + options.reserveRawExports = true; + options.setRenamingPolicy( + VariableRenamingPolicy.ALL, PropertyRenamingPolicy.ALL_UNQUOTED); + options.shadowVariables = true; + options.removeUnusedPrototypeProperties = true; + options.removeUnusedPrototypePropertiesInExterns = true; + options.collapseAnonymousFunctions = true; + options.collapseProperties = true; + options.checkGlobalThisLevel = CheckLevel.WARNING; + options.rewriteFunctionExpressions = true; + options.smartNameRemoval = true; + options.inlineConstantVars = true; + options.setInlineFunctions(Reach.ALL); + options.inlineGetters = true; + options.setInlineVariables(Reach.ALL); + options.flowSensitiveInlineVariables = true; + options.computeFunctionSideEffects = true; + + // Remove unused vars also removes unused functions. + options.setRemoveUnusedVariables(Reach.ALL); + + // Move code around based on the defined modules. + options.crossModuleCodeMotion = true; + options.crossModuleMethodMotion = true; + + // Call optimizations + options.devirtualizePrototypeMethods = true; + options.optimizeParameters = true; + options.optimizeReturns = true; + options.optimizeCalls = true; + + // Kindly tell the user that they have JsDocs that we don't understand. + options.setWarningLevel(DiagnosticGroups.NON_STANDARD_JSDOC, + CheckLevel.WARNING); + } + + /** + * Enable additional optimizations that use type information. + * @param options The CompilerOptions object to set the options on. + */ + public void setTypeBasedOptimizationOptions(CompilerOptions options) { + switch (this) { + case ADVANCED_OPTIMIZATIONS: + options.inferTypes = true; + options.disambiguateProperties = true; + options.ambiguateProperties = true; + options.inlineProperties = true; + // TODO(johnlenz) :removeUnusedClassProperties isn't strictly a + // type based pass, but add it here for now because I may have to + // make it into one. + options.removeUnusedClassProperties = true; + break; + case SIMPLE_OPTIMIZATIONS: + // TODO(johnlenz): enable peephole type based optimization. + break; + case WHITESPACE_ONLY: + break; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Compiler.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Compiler.java new file mode 100644 index 0000000..5f6b2c5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Compiler.java @@ -0,0 +1,2552 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.base.Throwables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.io.CharStreams; +import com.google.javascript.jscomp.CompilerOptions.DevMode; +import com.google.javascript.jscomp.CompilerOptions.LanguageMode; +import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.deps.SortedDependencies; +import com.google.javascript.jscomp.deps.SortedDependencies.CircularDependencyException; +import com.google.javascript.jscomp.deps.SortedDependencies.MissingProvideException; +import com.google.javascript.jscomp.parsing.Config; +import com.google.javascript.jscomp.parsing.ParserRunner; +import com.google.javascript.jscomp.type.ChainableReverseAbstractInterpreter; +import com.google.javascript.jscomp.type.ClosureReverseAbstractInterpreter; +import com.google.javascript.jscomp.type.ReverseAbstractInterpreter; +import com.google.javascript.jscomp.type.SemanticReverseAbstractInterpreter; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.head.ErrorReporter; +import com.google.javascript.rhino.head.ast.AstRoot; +import com.google.javascript.rhino.jstype.JSTypeRegistry; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; + +/** + * Compiler (and the other classes in this package) does the following: + *

      + *
    • parses JS code + *
    • checks for undefined variables + *
    • performs optimizations such as constant folding and constants inlining + *
    • renames variables (to short names) + *
    • outputs compact JavaScript code + *
    + * + * External variables are declared in 'externs' files. For instance, the file + * may include definitions for global javascript/browser objects such as + * window, document. + * + */ +public class Compiler extends AbstractCompiler { + static final String SINGLETON_MODULE_NAME = "[singleton]"; + + static final DiagnosticType MODULE_DEPENDENCY_ERROR = + DiagnosticType.error("JSC_MODULE_DEPENDENCY_ERROR", + "Bad dependency: {0} -> {1}. " + + "Modules must be listed in dependency order."); + + static final DiagnosticType MISSING_ENTRY_ERROR = DiagnosticType.error( + "JSC_MISSING_ENTRY_ERROR", + "required entry point \"{0}\" never provided"); + + private static final String CONFIG_RESOURCE = + "com.google.javascript.jscomp.parsing.ParserConfig"; + + CompilerOptions options = null; + + private PassConfig passes = null; + + // The externs inputs + private List externs; + + // The JS source modules + private List modules; + + // The graph of the JS source modules. Must be null if there are less than + // 2 modules, because we use this as a signal for which passes to run. + private JSModuleGraph moduleGraph; + + // The JS source inputs + private List inputs; + + // error manager to which error management is delegated + private ErrorManager errorManager; + + // Warnings guard for filtering warnings. + private WarningsGuard warningsGuard; + + // Compile-time injected libraries. The node points to the last node of + // the library, so code can be inserted after. + private final Map injectedLibraries = Maps.newLinkedHashMap(); + + // Parse tree root nodes + Node externsRoot; + Node jsRoot; + Node externAndJsRoot; + + private Map inputsById; + + /** The source code map */ + private SourceMap sourceMap; + + /** The externs created from the exports. */ + private String externExports = null; + + /** + * Ids for function inlining so that each declared name remains + * unique. + */ + private int uniqueNameId = 0; + + /** + * Whether to assume there are references to the RegExp Global object + * properties. + */ + private boolean hasRegExpGlobalReferences = true; + + /** The function information map */ + private FunctionInformationMap functionInformationMap; + + /** Debugging information */ + private final StringBuilder debugLog = new StringBuilder(); + + /** Detects Google-specific coding conventions. */ + CodingConvention defaultCodingConvention = new ClosureCodingConvention(); + + private JSTypeRegistry typeRegistry; + private Config parserConfig = null; + + private ReverseAbstractInterpreter abstractInterpreter; + private TypeValidator typeValidator; + + public PerformanceTracker tracker; + + // The oldErrorReporter exists so we can get errors from the JSTypeRegistry. + private final com.google.javascript.rhino.ErrorReporter oldErrorReporter = + RhinoErrorReporter.forOldRhino(this); + + // This error reporter gets the messages from the current Rhino parser. + private final ErrorReporter defaultErrorReporter = + RhinoErrorReporter.forNewRhino(this); + + /** Error strings used for reporting JSErrors */ + public static final DiagnosticType OPTIMIZE_LOOP_ERROR = DiagnosticType.error( + "JSC_OPTIMIZE_LOOP_ERROR", + "Exceeded max number of optimization iterations: {0}"); + public static final DiagnosticType MOTION_ITERATIONS_ERROR = + DiagnosticType.error("JSC_OPTIMIZE_LOOP_ERROR", + "Exceeded max number of code motion iterations: {0}"); + + // We use many recursive algorithms that use O(d) memory in the depth + // of the tree. + private static final long COMPILER_STACK_SIZE = (1 << 21); // About 2MB + + /** + * Under JRE 1.6, the JS Compiler overflows the stack when running on some + * large or complex JS code. When threads are available, we run all compile + * jobs on a separate thread with a larger stack. + * + * That way, we don't have to increase the stack size for *every* thread + * (which is what -Xss does). + */ + private static final ExecutorService compilerExecutor = + Executors.newCachedThreadPool(new ThreadFactory() { + @Override public Thread newThread(Runnable r) { + return new Thread(null, r, "jscompiler", COMPILER_STACK_SIZE); + } + }); + + /** + * Use a dedicated compiler thread per Compiler instance. + */ + private Thread compilerThread = null; + + /** Whether to use threads. */ + private boolean useThreads = true; + + + /** + * Logger for the whole com.google.javascript.jscomp domain - + * setting configuration for this logger affects all loggers + * in other classes within the compiler. + */ + private static final Logger logger = + Logger.getLogger("com.google.javascript.jscomp"); + + private final PrintStream outStream; + + private GlobalVarReferenceMap globalRefMap = null; + + private volatile double progress = 0.0; + private String lastPassName; + + /** + * Creates a Compiler that reports errors and warnings to its logger. + */ + public Compiler() { + this((PrintStream) null); + } + + /** + * Creates n Compiler that reports errors and warnings to an output + * stream. + */ + public Compiler(PrintStream stream) { + addChangeHandler(recentChange); + outStream = stream; + } + + /** + * Creates a Compiler that uses a custom error manager. + */ + public Compiler(ErrorManager errorManager) { + this(); + setErrorManager(errorManager); + } + + /** + * Sets the error manager. + * + * @param errorManager the error manager, it cannot be {@code null} + */ + public void setErrorManager(ErrorManager errorManager) { + Preconditions.checkNotNull( + errorManager, "the error manager cannot be null"); + this.errorManager = errorManager; + } + + /** + * Creates a message formatter instance corresponding to the value of + * {@link CompilerOptions}. + */ + private MessageFormatter createMessageFormatter() { + boolean colorize = options.shouldColorizeErrorOutput(); + return options.errorFormat.toFormatter(this, colorize); + } + + /** + * Initialize the compiler options. Only necessary if you're not doing + * a normal compile() job. + */ + public void initOptions(CompilerOptions options) { + this.options = options; + if (errorManager == null) { + if (outStream == null) { + setErrorManager( + new LoggerErrorManager(createMessageFormatter(), logger)); + } else { + PrintStreamErrorManager printer = + new PrintStreamErrorManager(createMessageFormatter(), outStream); + printer.setSummaryDetailLevel(options.summaryDetailLevel); + setErrorManager(printer); + } + } + + // DiagnosticGroups override the plain checkTypes option. + if (options.enables(DiagnosticGroups.CHECK_TYPES)) { + options.checkTypes = true; + } else if (options.disables(DiagnosticGroups.CHECK_TYPES)) { + options.checkTypes = false; + } else if (!options.checkTypes) { + // If DiagnosticGroups did not override the plain checkTypes + // option, and checkTypes is enabled, then turn off the + // parser type warnings. + options.setWarningLevel( + DiagnosticGroup.forType( + RhinoErrorReporter.TYPE_PARSE_ERROR), + CheckLevel.OFF); + } + + if (options.checkGlobalThisLevel.isOn() && + !options.disables(DiagnosticGroups.GLOBAL_THIS)) { + options.setWarningLevel( + DiagnosticGroups.GLOBAL_THIS, + options.checkGlobalThisLevel); + } + + if (options.getLanguageIn() == LanguageMode.ECMASCRIPT5_STRICT) { + options.setWarningLevel( + DiagnosticGroups.ES5_STRICT, + CheckLevel.ERROR); + } + + // Initialize the warnings guard. + List guards = Lists.newArrayList(); + guards.add( + new SuppressDocWarningsGuard( + getDiagnosticGroups().getRegisteredGroups())); + guards.add(options.getWarningsGuard()); + + ComposeWarningsGuard composedGuards = new ComposeWarningsGuard(guards); + + // All passes must run the variable check. This synthesizes + // variables later so that the compiler doesn't crash. It also + // checks the externs file for validity. If you don't want to warn + // about missing variable declarations, we shut that specific + // error off. + if (!options.checkSymbols && + !composedGuards.enables(DiagnosticGroups.CHECK_VARIABLES)) { + composedGuards.addGuard(new DiagnosticGroupWarningsGuard( + DiagnosticGroups.CHECK_VARIABLES, CheckLevel.OFF)); + } + + this.warningsGuard = composedGuards; + } + + /** + * Initializes the instance state needed for a compile job. + * @deprecated Convert your arrays to lists and use the list-based API. + */ + @Deprecated + public void init(JSSourceFile[] externs, JSSourceFile[] inputs, + CompilerOptions options) { + init(Lists.newArrayList(externs), + Lists.newArrayList(inputs), options); + } + + /** + * Initializes the instance state needed for a compile job. + */ + public void init( + List externs, + List inputs, + CompilerOptions options) { + JSModule module = new JSModule(SINGLETON_MODULE_NAME); + for (SourceFile input : inputs) { + module.add(input); + } + + initModules(externs, Lists.newArrayList(module), options); + } + + /** + * Initializes the instance state needed for a compile job if the sources + * are in modules. + * @deprecated Convert your arrays to lists and use the list-based API. + */ + @Deprecated + public void init(JSSourceFile[] externs, JSModule[] modules, + CompilerOptions options) { + initModules(Lists.newArrayList(externs), + Lists.newArrayList(modules), options); + } + + /** + * Initializes the instance state needed for a compile job if the sources + * are in modules. + */ + public void initModules( + List externs, List modules, CompilerOptions options) { + initOptions(options); + + checkFirstModule(modules); + fillEmptyModules(modules); + + this.externs = makeCompilerInput(externs, true); + + // Generate the module graph, and report any errors in the module + // specification as errors. + this.modules = modules; + if (modules.size() > 1) { + try { + this.moduleGraph = new JSModuleGraph(modules); + } catch (JSModuleGraph.ModuleDependenceException e) { + // problems with the module format. Report as an error. The + // message gives all details. + report(JSError.make(MODULE_DEPENDENCY_ERROR, + e.getModule().getName(), e.getDependentModule().getName())); + return; + } + } else { + this.moduleGraph = null; + } + + this.inputs = getAllInputsFromModules(modules); + initBasedOnOptions(); + + initInputsByIdMap(); + } + + /** + * Do any initialization that is dependent on the compiler options. + */ + private void initBasedOnOptions() { + // Create the source map if necessary. + if (options.sourceMapOutputPath != null) { + sourceMap = options.sourceMapFormat.getInstance(); + sourceMap.setPrefixMappings(options.sourceMapLocationMappings); + } + } + + private List makeCompilerInput( + List files, boolean isExtern) { + List inputs = Lists.newArrayList(); + for (T file : files) { + inputs.add(new CompilerInput(file, isExtern)); + } + return inputs; + } + + private static final DiagnosticType EMPTY_MODULE_LIST_ERROR = + DiagnosticType.error("JSC_EMPTY_MODULE_LIST_ERROR", + "At least one module must be provided"); + + private static final DiagnosticType EMPTY_ROOT_MODULE_ERROR = + DiagnosticType.error("JSC_EMPTY_ROOT_MODULE_ERROR", + "Root module '{0}' must contain at least one source code input"); + + /** + * Verifies that at least one module has been provided and that the first one + * has at least one source code input. + */ + private void checkFirstModule(List modules) { + if (modules.isEmpty()) { + report(JSError.make(EMPTY_MODULE_LIST_ERROR)); + } else if (modules.get(0).getInputs().isEmpty() && modules.size() > 1) { + // The root module may only be empty if there is exactly 1 module. + report(JSError.make(EMPTY_ROOT_MODULE_ERROR, + modules.get(0).getName())); + } + } + + /** + * Empty modules get an empty "fill" file, so that we can move code into + * an empty module. + */ + static String createFillFileName(String moduleName) { + return "[" + moduleName + "]"; + } + + /** + * Fill any empty modules with a place holder file. It makes any cross module + * motion easier. + */ + private static void fillEmptyModules(List modules) { + for (JSModule module : modules) { + if (module.getInputs().isEmpty()) { + module.add(SourceFile.fromCode( + createFillFileName(module.getName()), "")); + } + } + } + + /** + * Rebuilds the internal list of inputs by iterating over all modules. + * This is necessary if inputs have been added to or removed from a module + * after the {@link #init(List, List, CompilerOptions)} call. + */ + public void rebuildInputsFromModules() { + inputs = getAllInputsFromModules(modules); + initInputsByIdMap(); + } + + /** + * Builds a single list of all module inputs. Verifies that it contains no + * duplicates. + */ + private static List getAllInputsFromModules( + List modules) { + List inputs = Lists.newArrayList(); + Map inputMap = Maps.newHashMap(); + for (JSModule module : modules) { + for (CompilerInput input : module.getInputs()) { + String inputName = input.getName(); + + // NOTE(nicksantos): If an input is in more than one module, + // it will show up twice in the inputs list, and then we + // will get an error down the line. + inputs.add(input); + inputMap.put(inputName, module); + } + } + return inputs; + } + + static final DiagnosticType DUPLICATE_INPUT = + DiagnosticType.error("JSC_DUPLICATE_INPUT", "Duplicate input: {0}"); + static final DiagnosticType DUPLICATE_EXTERN_INPUT = + DiagnosticType.error("JSC_DUPLICATE_EXTERN_INPUT", + "Duplicate extern input: {0}"); + + /** + * Creates a map to make looking up an input by name fast. Also checks for + * duplicate inputs. + */ + void initInputsByIdMap() { + inputsById = new HashMap(); + for (CompilerInput input : externs) { + InputId id = input.getInputId(); + CompilerInput previous = putCompilerInput(id, input); + if (previous != null) { + report(JSError.make(DUPLICATE_EXTERN_INPUT, input.getName())); + } + } + for (CompilerInput input : inputs) { + InputId id = input.getInputId(); + CompilerInput previous = putCompilerInput(id, input); + if (previous != null) { + report(JSError.make(DUPLICATE_INPUT, input.getName())); + } + } + } + + public Result compile( + SourceFile extern, SourceFile input, CompilerOptions options) { + return compile(Lists.newArrayList(extern), Lists.newArrayList(input), options); + } + + /** + * @deprecated Convert your arrays to lists and use the list-based API. + */ + @Deprecated + public Result compile( + SourceFile extern, JSSourceFile[] input, CompilerOptions options) { + return compile(Lists.newArrayList(extern), Lists.newArrayList(input), options); + } + + /** + * @deprecated Convert your arrays to lists and use the list-based + * compileModules method. + */ + @Deprecated + public Result compile( + JSSourceFile extern, JSModule[] modules, CompilerOptions options) { + return compileModules( + Lists.newArrayList(extern), Lists.newArrayList(modules), options); + } + + /** + * Compiles a list of inputs. + * @deprecated Convert your arrays to lists and use the list-based compile + * method. + */ + @Deprecated + public Result compile(JSSourceFile[] externs, + JSSourceFile[] inputs, + CompilerOptions options) { + return compile(Lists.newArrayList(externs), + Lists.newArrayList(inputs), + options); + } + + /** + * Compiles a list of inputs. + */ + public Result compile( + List externs, List inputs, CompilerOptions options) { + // The compile method should only be called once. + Preconditions.checkState(jsRoot == null); + + try { + init(externs, inputs, options); + if (hasErrors()) { + return getResult(); + } + return compile(); + } finally { + Tracer t = newTracer("generateReport"); + errorManager.generateReport(); + stopTracer(t, "generateReport"); + } + } + + /** + * Compiles a list of modules. + * @deprecated Convert your arrays to lists and use the list-based + * compileModules method. + */ + @Deprecated + public Result compile(JSSourceFile[] externs, + JSModule[] modules, + CompilerOptions options) { + return compileModules(Lists.newArrayList(externs), + Lists.newArrayList(modules), + options); + } + + /** + * Compiles a list of modules. + */ + public Result compileModules(List externs, + List modules, CompilerOptions options) { + // The compile method should only be called once. + Preconditions.checkState(jsRoot == null); + + try { + initModules(externs, modules, options); + if (hasErrors()) { + return getResult(); + } + return compile(); + } finally { + Tracer t = newTracer("generateReport"); + errorManager.generateReport(); + stopTracer(t, "generateReport"); + } + } + + private Result compile() { + return runInCompilerThread(new Callable() { + @Override + public Result call() throws Exception { + compileInternal(); + return getResult(); + } + }); + } + + /** + * Disable threads. This is for clients that run on AppEngine and + * don't have threads. + */ + public void disableThreads() { + useThreads = false; + } + + @SuppressWarnings("unchecked") + T runInCompilerThread(final Callable callable) { + final boolean dumpTraceReport = options != null && options.tracer.isOn(); + T result = null; + final Throwable[] exception = new Throwable[1]; + Callable bootCompilerThread = new Callable() { + @Override + public T call() { + try { + compilerThread = Thread.currentThread(); + if (dumpTraceReport) { + Tracer.initCurrentThreadTrace(); + } + return callable.call(); + } catch (Throwable e) { + exception[0] = e; + } finally { + compilerThread = null; + if (dumpTraceReport) { + Tracer.logAndClearCurrentThreadTrace(); + tracker.outputTracerReport(outStream == null ? + System.out : outStream); + } + } + return null; + } + }; + + Preconditions.checkState( + compilerThread == null || compilerThread == Thread.currentThread(), + "Please do not share the Compiler across threads"); + + // If the compiler thread is available, use it. + if (useThreads && compilerThread == null) { + try { + result = compilerExecutor.submit(bootCompilerThread).get(); + } catch (InterruptedException e) { + throw Throwables.propagate(e); + } catch (ExecutionException e) { + throw Throwables.propagate(e); + } + } else { + try { + result = callable.call(); + } catch (Exception e) { + exception[0] = e; + } + } + + // Pass on any exception caught by the runnable object. + if (exception[0] != null) { + throw new RuntimeException(exception[0]); + } + + return result; + } + + private void compileInternal() { + setProgress(0.0, null); + parse(); + // 15 percent of the work is assumed to be for parsing (based on some + // minimal analysis on big JS projects, of course this depends on options) + setProgress(0.15, "parse"); + if (hasErrors()) { + return; + } + + if (!precheck()) { + return; + } + + if (options.nameAnonymousFunctionsOnly) { + // TODO(nicksantos): Move this into an instrument() phase maybe? + check(); + return; + } + + if (!options.skipAllPasses) { + check(); + if (hasErrors()) { + return; + } + + if (options.isExternExportsEnabled() + || options.externExportsPath != null) { + externExports(); + } + + // IDE-mode is defined to stop here, before the heavy rewriting begins. + if (!options.ideMode) { + optimize(); + } + } + + if (options.recordFunctionInformation) { + recordFunctionInformation(); + } + + if (options.devMode == DevMode.START_AND_END) { + runSanityCheck(); + } + setProgress(1.0, "recordFunctionInformation"); + } + + public void parse() { + parseInputs(); + } + + PassConfig getPassConfig() { + if (passes == null) { + passes = createPassConfigInternal(); + } + return passes; + } + + /** + * Create the passes object. Clients should use setPassConfig instead of + * overriding this. + */ + PassConfig createPassConfigInternal() { + return new DefaultPassConfig(options); + } + + /** + * @param passes The PassConfig to use with this Compiler. + * @throws NullPointerException if passes is null + * @throws IllegalStateException if this.passes has already been assigned + */ + public void setPassConfig(PassConfig passes) { + // Important to check for null because if setPassConfig(null) is + // called before this.passes is set, getPassConfig() will create a + // new PassConfig object and use that, which is probably not what + // the client wanted since he or she probably meant to use their + // own PassConfig object. + Preconditions.checkNotNull(passes); + + if (this.passes != null) { + throw new IllegalStateException("this.passes has already been assigned"); + } + this.passes = passes; + } + + /** + * Carry out any special checks or procedures that need to be done before + * proceeding with rest of the compilation process. + * + * @return true, to continue with compilation + */ + boolean precheck() { + return true; + } + + public void check() { + runCustomPasses(CustomPassExecutionTime.BEFORE_CHECKS); + + // We are currently only interested in check-passes for progress reporting + // as it is used for IDEs, that's why the maximum progress is set to 1.0. + PhaseOptimizer phaseOptimizer = new PhaseOptimizer(this, tracker, + new PhaseOptimizer.ProgressRange(getProgress(), 1.0)); + if (options.devMode == DevMode.EVERY_PASS) { + phaseOptimizer.setSanityCheck(sanityCheck); + } + phaseOptimizer.consume(getPassConfig().getChecks()); + phaseOptimizer.process(externsRoot, jsRoot); + if (hasErrors()) { + return; + } + + // TODO(nicksantos): clean this up. The flow here is too hard to follow. + if (options.nameAnonymousFunctionsOnly) { + return; + } + + if (options.removeTryCatchFinally) { + removeTryCatchFinally(); + } + + if (options.getTweakProcessing().shouldStrip() || + !options.stripTypes.isEmpty() || + !options.stripNameSuffixes.isEmpty() || + !options.stripTypePrefixes.isEmpty() || + !options.stripNamePrefixes.isEmpty()) { + stripCode(options.stripTypes, options.stripNameSuffixes, + options.stripTypePrefixes, options.stripNamePrefixes); + } + + runCustomPasses(CustomPassExecutionTime.BEFORE_OPTIMIZATIONS); + } + + private void externExports() { + logger.fine("Creating extern file for exports"); + startPass("externExports"); + + ExternExportsPass pass = new ExternExportsPass(this); + process(pass); + + externExports = pass.getGeneratedExterns(); + + endPass(); + } + + @Override + void process(CompilerPass p) { + p.process(externsRoot, jsRoot); + } + + private final PassFactory sanityCheck = + new PassFactory("sanityCheck", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new SanityCheck(compiler); + } + }; + + private void maybeSanityCheck() { + if (options.devMode == DevMode.EVERY_PASS) { + runSanityCheck(); + } + } + + private void runSanityCheck() { + sanityCheck.create(this).process(externsRoot, jsRoot); + } + + /** + * Removes try/catch/finally statements for easier debugging. + */ + void removeTryCatchFinally() { + logger.fine("Remove try/catch/finally"); + startPass("removeTryCatchFinally"); + RemoveTryCatch r = new RemoveTryCatch(this); + process(r); + endPass(); + } + + /** + * Strips code for smaller compiled code. This is useful for removing debug + * statements to prevent leaking them publicly. + */ + void stripCode(Set stripTypes, Set stripNameSuffixes, + Set stripTypePrefixes, Set stripNamePrefixes) { + logger.fine("Strip code"); + startPass("stripCode"); + StripCode r = new StripCode(this, stripTypes, stripNameSuffixes, + stripTypePrefixes, stripNamePrefixes); + if (options.getTweakProcessing().shouldStrip()) { + r.enableTweakStripping(); + } + process(r); + endPass(); + } + + /** + * Runs custom passes that are designated to run at a particular time. + */ + private void runCustomPasses(CustomPassExecutionTime executionTime) { + if (options.customPasses != null) { + Tracer t = newTracer("runCustomPasses"); + try { + for (CompilerPass p : options.customPasses.get(executionTime)) { + process(p); + } + } finally { + stopTracer(t, "runCustomPasses"); + } + } + } + + private Tracer currentTracer = null; + private String currentPassName = null; + + /** + * Marks the beginning of a pass. + */ + void startPass(String passName) { + Preconditions.checkState(currentTracer == null); + currentPassName = passName; + currentTracer = newTracer(passName); + } + + /** + * Marks the end of a pass. + */ + void endPass() { + Preconditions.checkState(currentTracer != null, + "Tracer should not be null at the end of a pass."); + stopTracer(currentTracer, currentPassName); + String passToCheck = currentPassName; + currentPassName = null; + currentTracer = null; + + maybeSanityCheck(); + } + + /** + * Returns a new tracer for the given pass name. + */ + Tracer newTracer(String passName) { + String comment = passName + + (recentChange.hasCodeChanged() ? " on recently changed AST" : ""); + if (options.tracer.isOn()) { + tracker.recordPassStart(passName); + } + return new Tracer("Compiler", comment); + } + + void stopTracer(Tracer t, String passName) { + long result = t.stop(); + if (options.tracer.isOn()) { + tracker.recordPassStop(passName, result); + } + } + + /** + * Returns the result of the compilation. + */ + public Result getResult() { + PassConfig.State state = getPassConfig().getIntermediateState(); + return new Result(getErrors(), getWarnings(), debugLog.toString(), + state.variableMap, state.propertyMap, + state.anonymousFunctionNameMap, state.stringMap, functionInformationMap, + sourceMap, externExports, state.cssNames, state.idGeneratorMap); + } + + /** + * Returns an array constructed from errors + temporary warnings. + */ + public JSError[] getMessages() { + return getErrors(); + } + + /** + * Returns the array of errors (never null). + */ + public JSError[] getErrors() { + return errorManager.getErrors(); + } + + /** + * Returns the array of warnings (never null). + */ + public JSError[] getWarnings() { + return errorManager.getWarnings(); + } + + @Override + public Node getRoot() { + return externAndJsRoot; + } + + /** + * Creates a new id for making unique names. + */ + private int nextUniqueNameId() { + return uniqueNameId++; + } + + /** + * Resets the unique name id counter + */ + @VisibleForTesting + void resetUniqueNameId() { + uniqueNameId = 0; + } + + @Override + Supplier getUniqueNameIdSupplier() { + final Compiler self = this; + return new Supplier() { + @Override + public String get() { + return String.valueOf(self.nextUniqueNameId()); + } + }; + } + + @Override + boolean areNodesEqualForInlining(Node n1, Node n2) { + if (options.ambiguateProperties || + options.disambiguateProperties) { + // The type based optimizations require that type information is preserved + // during other optimizations. + return n1.isEquivalentToTyped(n2); + } else { + return n1.isEquivalentTo(n2); + } + } + + //------------------------------------------------------------------------ + // Inputs + //------------------------------------------------------------------------ + + // TODO(nicksantos): Decide which parts of these belong in an AbstractCompiler + // interface, and which ones should always be injected. + + @Override + public CompilerInput getInput(InputId id) { + return inputsById.get(id); + } + + /** + * Removes an input file from AST. + * @param id The id of the input to be removed. + */ + protected void removeExternInput(InputId id) { + CompilerInput input = getInput(id); + if (input == null) { + return; + } + Preconditions.checkState(input.isExtern(), "Not an extern input: %s", input.getName()); + inputsById.remove(id); + externs.remove(input); + Node root = input.getAstRoot(this); + if (root != null) { + root.detachFromParent(); + } + } + + @Override + public CompilerInput newExternInput(String name) { + SourceAst ast = new SyntheticAst(name); + if (inputsById.containsKey(ast.getInputId())) { + throw new IllegalArgumentException("Conflicting externs name: " + name); + } + CompilerInput input = new CompilerInput(ast, true); + putCompilerInput(input.getInputId(), input); + externsRoot.addChildToFront(ast.getAstRoot(this)); + externs.add(0, input); + return input; + } + + private CompilerInput putCompilerInput(InputId id, CompilerInput input) { + input.setCompiler(this); + return inputsById.put(id, input); + } + + /** Add a source input dynamically. Intended for incremental compilation. */ + void addIncrementalSourceAst(JsAst ast) { + InputId id = ast.getInputId(); + Preconditions.checkState(getInput(id) == null, "Duplicate input %s", id.getIdName()); + putCompilerInput(id, new CompilerInput(ast)); + } + + /** + * Replace a source input dynamically. Intended for incremental + * re-compilation. + * + * If the new source input doesn't parse, then keep the old input + * in the AST and return false. + * + * @return Whether the new AST was attached successfully. + */ + boolean replaceIncrementalSourceAst(JsAst ast) { + CompilerInput oldInput = getInput(ast.getInputId()); + Preconditions.checkNotNull(oldInput, "No input to replace: %s", ast.getInputId().getIdName()); + Node newRoot = ast.getAstRoot(this); + if (newRoot == null) { + return false; + } + + Node oldRoot = oldInput.getAstRoot(this); + if (oldRoot != null) { + oldRoot.getParent().replaceChild(oldRoot, newRoot); + } else { + getRoot().getLastChild().addChildToBack(newRoot); + } + + CompilerInput newInput = new CompilerInput(ast); + putCompilerInput(ast.getInputId(), newInput); + + JSModule module = oldInput.getModule(); + if (module != null) { + module.addAfter(newInput, oldInput); + module.remove(oldInput); + } + + // Verify the input id is set properly. + Preconditions.checkState( + newInput.getInputId().equals(oldInput.getInputId())); + InputId inputIdOnAst = newInput.getAstRoot(this).getInputId(); + Preconditions.checkState(newInput.getInputId().equals(inputIdOnAst)); + + inputs.remove(oldInput); + return true; + } + + /** + * Add a new source input dynamically. Intended for incremental compilation. + *

    + * If the new source input doesn't parse, it will not be added, and a false + * will be returned. + * + * @param ast the JS Source to add. + * @return true if the source was added successfully, false otherwise. + * @throws IllegalStateException if an input for this ast already exists. + */ + boolean addNewSourceAst(JsAst ast) { + CompilerInput oldInput = getInput(ast.getInputId()); + if (oldInput != null) { + throw new IllegalStateException( + "Input already exists: " + ast.getInputId().getIdName()); + } + Node newRoot = ast.getAstRoot(this); + if (newRoot == null) { + return false; + } + + getRoot().getLastChild().addChildToBack(newRoot); + + CompilerInput newInput = new CompilerInput(ast); + + // TODO(tylerg): handle this for multiple modules at some point. + if (moduleGraph == null && !modules.isEmpty()) { + // singleton module + modules.get(0).add(newInput); + } + + putCompilerInput(ast.getInputId(), newInput); + + return true; + } + + @Override + JSModuleGraph getModuleGraph() { + return moduleGraph; + } + + /** + * Gets a module graph. This will always return a module graph, even + * in the degenerate case when there's only one module. + */ + JSModuleGraph getDegenerateModuleGraph() { + return moduleGraph == null ? new JSModuleGraph(modules) : moduleGraph; + } + + @Override + public JSTypeRegistry getTypeRegistry() { + if (typeRegistry == null) { + typeRegistry = new JSTypeRegistry(oldErrorReporter, options.looseTypes); + } + return typeRegistry; + } + + @Override + public MemoizedScopeCreator getTypedScopeCreator() { + return getPassConfig().getTypedScopeCreator(); + } + + @SuppressWarnings("unchecked") + DefaultPassConfig ensureDefaultPassConfig() { + PassConfig passes = getPassConfig().getBasePassConfig(); + Preconditions.checkState(passes instanceof DefaultPassConfig, + "PassConfigs must eventually delegate to the DefaultPassConfig"); + return (DefaultPassConfig) passes; + } + + public SymbolTable buildKnownSymbolTable() { + SymbolTable symbolTable = new SymbolTable(getTypeRegistry()); + + MemoizedScopeCreator typedScopeCreator = getTypedScopeCreator(); + if (typedScopeCreator != null) { + symbolTable.addScopes(typedScopeCreator.getAllMemoizedScopes()); + symbolTable.addSymbolsFrom(typedScopeCreator); + } else { + symbolTable.findScopes(this, externsRoot, jsRoot); + } + + GlobalNamespace globalNamespace = + ensureDefaultPassConfig().getGlobalNamespace(); + if (globalNamespace != null) { + symbolTable.addSymbolsFrom(globalNamespace); + } + + ReferenceCollectingCallback refCollector = + new ReferenceCollectingCallback( + this, ReferenceCollectingCallback.DO_NOTHING_BEHAVIOR); + NodeTraversal.traverse(this, getRoot(), refCollector); + symbolTable.addSymbolsFrom(refCollector); + + PreprocessorSymbolTable preprocessorSymbolTable = + ensureDefaultPassConfig().getPreprocessorSymbolTable(); + if (preprocessorSymbolTable != null) { + symbolTable.addSymbolsFrom(preprocessorSymbolTable); + } + + symbolTable.fillNamespaceReferences(); + symbolTable.fillPropertyScopes(); + symbolTable.fillThisReferences(this, externsRoot, jsRoot); + symbolTable.fillPropertySymbols(this, externsRoot, jsRoot); + symbolTable.fillJSDocInfo(this, externsRoot, jsRoot); + + return symbolTable; + } + + @Override + public Scope getTopScope() { + return getPassConfig().getTopScope(); + } + + @Override + public ReverseAbstractInterpreter getReverseAbstractInterpreter() { + if (abstractInterpreter == null) { + ChainableReverseAbstractInterpreter interpreter = + new SemanticReverseAbstractInterpreter( + getCodingConvention(), getTypeRegistry()); + if (options.closurePass) { + interpreter = new ClosureReverseAbstractInterpreter( + getCodingConvention(), getTypeRegistry()) + .append(interpreter).getFirst(); + } + abstractInterpreter = interpreter; + } + return abstractInterpreter; + } + + @Override + TypeValidator getTypeValidator() { + if (typeValidator == null) { + typeValidator = new TypeValidator(this); + } + return typeValidator; + } + + //------------------------------------------------------------------------ + // Parsing + //------------------------------------------------------------------------ + + /** + * Parses the externs and main inputs. + * + * @return A synthetic root node whose two children are the externs root + * and the main root + */ + Node parseInputs() { + boolean devMode = options.devMode != DevMode.OFF; + + // If old roots exist (we are parsing a second time), detach each of the + // individual file parse trees. + if (externsRoot != null) { + externsRoot.detachChildren(); + } + if (jsRoot != null) { + jsRoot.detachChildren(); + } + + // Parse main JS sources. + jsRoot = IR.block(); + jsRoot.setIsSyntheticBlock(true); + + externsRoot = IR.block(); + externsRoot.setIsSyntheticBlock(true); + + externAndJsRoot = IR.block(externsRoot, jsRoot); + externAndJsRoot.setIsSyntheticBlock(true); + + if (options.tracer.isOn()) { + tracker = new PerformanceTracker(jsRoot, options.tracer); + addChangeHandler(tracker.getCodeChangeHandler()); + } + + Tracer tracer = newTracer("parseInputs"); + + try { + // Parse externs sources. + for (CompilerInput input : externs) { + Node n = input.getAstRoot(this); + if (hasErrors()) { + return null; + } + externsRoot.addChildToBack(n); + } + + // Modules inferred in ProcessCommonJS pass. + if (options.transformAMDToCJSModules || options.processCommonJSModules) { + processAMDAndCommonJSModules(); + } + + hoistExterns(externsRoot); + + // Check if the sources need to be re-ordered. + boolean staleInputs = false; + if (options.dependencyOptions.needsManagement()) { + for (CompilerInput input : inputs) { + // Forward-declare all the provided types, so that they + // are not flagged even if they are dropped from the process. + for (String provide : input.getProvides()) { + getTypeRegistry().forwardDeclareType(provide); + } + } + + try { + inputs = + (moduleGraph == null ? new JSModuleGraph(modules) : moduleGraph) + .manageDependencies(options.dependencyOptions, inputs); + staleInputs = true; + } catch (CircularDependencyException e) { + report(JSError.make( + JSModule.CIRCULAR_DEPENDENCY_ERROR, e.getMessage())); + + // If in IDE mode, we ignore the error and keep going. + if (hasErrors()) { + return null; + } + } catch (MissingProvideException e) { + report(JSError.make( + MISSING_ENTRY_ERROR, e.getMessage())); + + // If in IDE mode, we ignore the error and keep going. + if (hasErrors()) { + return null; + } + } + } + + hoistNoCompileFiles(); + + if (staleInputs) { + repartitionInputs(); + } + + // Build the AST. + for (CompilerInput input : inputs) { + Node n = input.getAstRoot(this); + if (n == null) { + continue; + } + + if (devMode) { + runSanityCheck(); + if (hasErrors()) { + return null; + } + } + + if (options.sourceMapOutputPath != null || + options.nameReferenceReportPath != null) { + + // Annotate the nodes in the tree with information from the + // input file. This information is used to construct the SourceMap. + SourceInformationAnnotator sia = + new SourceInformationAnnotator( + input.getName(), options.devMode != DevMode.OFF); + NodeTraversal.traverse(this, n, sia); + } + + jsRoot.addChildToBack(n); + } + + if (hasErrors()) { + return null; + } + return externAndJsRoot; + } finally { + stopTracer(tracer, "parseInputs"); + } + } + + /** + * Hoists inputs with the @externs annotation into the externs list. + */ + private void hoistExterns(Node externsRoot) { + boolean staleInputs = false; + for (CompilerInput input : inputs) { + if (options.dependencyOptions.needsManagement()) { + // If we're doing scanning dependency info anyway, use that + // information to skip sources that obviously aren't externs. + if (!input.getProvides().isEmpty() || !input.getRequires().isEmpty()) { + continue; + } + } + + Node n = input.getAstRoot(this); + + // Inputs can have a null AST on a parse error. + if (n == null) { + continue; + } + + JSDocInfo info = n.getJSDocInfo(); + if (info != null && info.isExterns()) { + // If the input file is explicitly marked as an externs file, then + // assume the programmer made a mistake and throw it into + // the externs pile anyways. + externsRoot.addChildToBack(n); + input.setIsExtern(true); + + input.getModule().remove(input); + + externs.add(input); + staleInputs = true; + } + } + + if (staleInputs) { + repartitionInputs(); + } + } + + /** + * Hoists inputs with the @nocompiler annotation out of the inputs. + */ + private void hoistNoCompileFiles() { + boolean staleInputs = false; + for (CompilerInput input : inputs) { + Node n = input.getAstRoot(this); + + // Inputs can have a null AST on a parse error. + if (n == null) { + continue; + } + + JSDocInfo info = n.getJSDocInfo(); + if (info != null && info.isNoCompile()) { + input.getModule().remove(input); + staleInputs = true; + } + } + + if (staleInputs) { + repartitionInputs(); + } + } + + private void repartitionInputs() { + fillEmptyModules(modules); + rebuildInputsFromModules(); + } + + /** + * Transforms AMD and CJS modules to something closure compiler can + * process and creates JSModules and the corresponding dependency tree + * on the way. + */ + void processAMDAndCommonJSModules() { + Map modulesByName = Maps.newLinkedHashMap(); + Map modulesByInput = Maps.newLinkedHashMap(); + // TODO(nicksantos): Refactor module dependency resolution to work nicely + // with multiple ways to express dependencies. Directly support JSModules + // that are equivalent to a signal file and which express their deps + // directly in the source. + for (CompilerInput input : inputs) { + input.setCompiler(this); + Node root = input.getAstRoot(this); + if (root == null) { + continue; + } + if (options.transformAMDToCJSModules) { + new TransformAMDToCJSModule(this).process(null, root); + } + if (options.processCommonJSModules) { + ProcessCommonJSModules cjs = new ProcessCommonJSModules(this, + options.commonJSModulePathPrefix); + cjs.process(null, root); + JSModule m = cjs.getModule(); + if (m != null) { + modulesByName.put(m.getName(), m); + modulesByInput.put(input, m); + } + } + } + if (options.processCommonJSModules) { + List modules = Lists.newArrayList(modulesByName.values()); + if (!modules.isEmpty()) { + this.modules = modules; + this.moduleGraph = new JSModuleGraph(this.modules); + } + for (JSModule module : modules) { + for (CompilerInput input : module.getInputs()) { + for (String require : input.getRequires()) { + JSModule dependency = modulesByName.get(require); + if (dependency == null) { + report(JSError.make(MISSING_ENTRY_ERROR, require)); + } else { + module.addDependency(dependency); + } + } + } + } + try { + modules = Lists.newArrayList(); + for (CompilerInput input : this.moduleGraph.manageDependencies( + options.dependencyOptions, inputs)) { + modules.add(modulesByInput.get(input)); + } + JSModule root = new JSModule("root"); + for (JSModule m : modules) { + m.addDependency(root); + } + modules.add(0, root); + SortedDependencies sorter = + new SortedDependencies(modules); + modules = sorter.getDependenciesOf(modules, true); + this.modules = modules; + + this.moduleGraph = new JSModuleGraph(modules); + } catch (Exception e) { + Throwables.propagate(e); + } + } + } + + public Node parse(SourceFile file) { + initCompilerOptionsIfTesting(); + addToDebugLog("Parsing: " + file.getName()); + return new JsAst(file).getAstRoot(this); + } + + private int syntheticCodeId = 0; + + @Override + Node parseSyntheticCode(String js) { + CompilerInput input = new CompilerInput( + SourceFile.fromCode(" [synthetic:" + (++syntheticCodeId) + "] ", js)); + putCompilerInput(input.getInputId(), input); + return input.getAstRoot(this); + } + + /** + * Allow subclasses to override the default CompileOptions object. + */ + protected CompilerOptions newCompilerOptions() { + return new CompilerOptions(); + } + + void initCompilerOptionsIfTesting() { + if (options == null) { + // initialization for tests that don't initialize the compiler + // by the normal mechanisms. + initOptions(newCompilerOptions()); + } + } + + @Override + Node parseSyntheticCode(String fileName, String js) { + initCompilerOptionsIfTesting(); + return parse(SourceFile.fromCode(fileName, js)); + } + + @Override + Node parseTestCode(String js) { + initCompilerOptionsIfTesting(); + CompilerInput input = new CompilerInput( + SourceFile.fromCode("[testcode]", js)); + if (inputsById == null) { + inputsById = Maps.newHashMap(); + } + putCompilerInput(input.getInputId(), input); + return input.getAstRoot(this); + } + + @Override + ErrorReporter getDefaultErrorReporter() { + return defaultErrorReporter; + } + + //------------------------------------------------------------------------ + // Convert back to source code + //------------------------------------------------------------------------ + + /** + * Converts the main parse tree back to JS code. + */ + public String toSource() { + return runInCompilerThread(new Callable() { + @Override + public String call() throws Exception { + Tracer tracer = newTracer("toSource"); + try { + CodeBuilder cb = new CodeBuilder(); + if (jsRoot != null) { + int i = 0; + for (Node scriptNode = jsRoot.getFirstChild(); + scriptNode != null; + scriptNode = scriptNode.getNext()) { + toSource(cb, i++, scriptNode); + } + } + return cb.toString(); + } finally { + stopTracer(tracer, "toSource"); + } + } + }); + } + + /** + * Converts the parse tree for each input back to JS code. + */ + public String[] toSourceArray() { + return runInCompilerThread(new Callable() { + @Override + public String[] call() throws Exception { + Tracer tracer = newTracer("toSourceArray"); + try { + int numInputs = inputs.size(); + String[] sources = new String[numInputs]; + CodeBuilder cb = new CodeBuilder(); + for (int i = 0; i < numInputs; i++) { + Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); + cb.reset(); + toSource(cb, i, scriptNode); + sources[i] = cb.toString(); + } + return sources; + } finally { + stopTracer(tracer, "toSourceArray"); + } + } + }); + } + + /** + * Converts the parse tree for a module back to JS code. + */ + public String toSource(final JSModule module) { + return runInCompilerThread(new Callable() { + @Override + public String call() throws Exception { + List inputs = module.getInputs(); + int numInputs = inputs.size(); + if (numInputs == 0) { + return ""; + } + CodeBuilder cb = new CodeBuilder(); + for (int i = 0; i < numInputs; i++) { + Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); + if (scriptNode == null) { + throw new IllegalArgumentException( + "Bad module: " + module.getName()); + } + toSource(cb, i, scriptNode); + } + return cb.toString(); + } + }); + } + + + /** + * Converts the parse tree for each input in a module back to JS code. + */ + public String[] toSourceArray(final JSModule module) { + return runInCompilerThread(new Callable() { + @Override + public String[] call() throws Exception { + List inputs = module.getInputs(); + int numInputs = inputs.size(); + if (numInputs == 0) { + return new String[0]; + } + + String[] sources = new String[numInputs]; + CodeBuilder cb = new CodeBuilder(); + for (int i = 0; i < numInputs; i++) { + Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); + if (scriptNode == null) { + throw new IllegalArgumentException( + "Bad module input: " + inputs.get(i).getName()); + } + + cb.reset(); + toSource(cb, i, scriptNode); + sources[i] = cb.toString(); + } + return sources; + } + }); + } + + /** + * Writes out JS code from a root node. If printing input delimiters, this + * method will attach a comment to the start of the text indicating which + * input the output derived from. If there were any preserve annotations + * within the root's source, they will also be printed in a block comment + * at the beginning of the output. + */ + public void toSource(final CodeBuilder cb, + final int inputSeqNum, + final Node root) { + runInCompilerThread(new Callable() { + @Override + public Void call() throws Exception { + if (options.printInputDelimiter) { + if ((cb.getLength() > 0) && !cb.endsWith("\n")) { + cb.append("\n"); // Make sure that the label starts on a new line + } + Preconditions.checkState(root.isScript()); + + String delimiter = options.inputDelimiter; + + String inputName = root.getInputId().getIdName(); + String sourceName = root.getSourceFileName(); + Preconditions.checkState(sourceName != null); + Preconditions.checkState(!sourceName.isEmpty()); + + delimiter = delimiter + .replaceAll("%name%", Matcher.quoteReplacement(inputName)) + .replaceAll("%num%", String.valueOf(inputSeqNum)); + + cb.append(delimiter) + .append("\n"); + } + if (root.getJSDocInfo() != null && + root.getJSDocInfo().getLicense() != null) { + cb.append("/*\n") + .append(root.getJSDocInfo().getLicense()) + .append("*/\n"); + } + + // If there is a valid source map, then indicate to it that the current + // root node's mappings are offset by the given string builder buffer. + if (options.sourceMapOutputPath != null) { + sourceMap.setStartingPosition( + cb.getLineIndex(), cb.getColumnIndex()); + } + + // if LanguageMode is ECMASCRIPT5_STRICT, only print 'use strict' + // for the first input file + String code = toSource(root, sourceMap, inputSeqNum == 0); + if (!code.isEmpty()) { + cb.append(code); + + // In order to avoid parse ambiguity when files are concatenated + // together, all files should end in a semi-colon. Do a quick + // heuristic check if there's an obvious semi-colon already there. + int length = code.length(); + char lastChar = code.charAt(length - 1); + char secondLastChar = length >= 2 ? + code.charAt(length - 2) : '\0'; + boolean hasSemiColon = lastChar == ';' || + (lastChar == '\n' && secondLastChar == ';'); + if (!hasSemiColon) { + cb.append(";"); + } + } + return null; + } + }); + } + + /** + * Generates JavaScript source code for an AST, doesn't generate source + * map info. + */ + @Override + String toSource(Node n) { + initCompilerOptionsIfTesting(); + return toSource(n, null, true); + } + + /** + * Generates JavaScript source code for an AST. + */ + private String toSource(Node n, SourceMap sourceMap, boolean firstOutput) { + CodePrinter.Builder builder = new CodePrinter.Builder(n); + builder.setCompilerOptions(options); + builder.setSourceMap(sourceMap); + builder.setTagAsStrict(firstOutput && + options.getLanguageOut() == LanguageMode.ECMASCRIPT5_STRICT); + return builder.build(); + } + + /** + * Stores a buffer of text to which more can be appended. This is just like a + * StringBuilder except that we also track the number of lines. + */ + public static class CodeBuilder { + private final StringBuilder sb = new StringBuilder(); + private int lineCount = 0; + private int colCount = 0; + + /** Removes all text, but leaves the line count unchanged. */ + void reset() { + sb.setLength(0); + } + + /** Appends the given string to the text buffer. */ + CodeBuilder append(String str) { + sb.append(str); + + // Adjust the line and column information for the new text. + int index = -1; + int lastIndex = index; + while ((index = str.indexOf('\n', index + 1)) >= 0) { + ++lineCount; + lastIndex = index; + } + + if (lastIndex == -1) { + // No new lines, append the new characters added. + colCount += str.length(); + } else { + colCount = str.length() - (lastIndex + 1); + } + + return this; + } + + /** Returns all text in the text buffer. */ + @Override + public String toString() { + return sb.toString(); + } + + /** Returns the length of the text buffer. */ + public int getLength() { + return sb.length(); + } + + /** Returns the (zero-based) index of the last line in the text buffer. */ + int getLineIndex() { + return lineCount; + } + + /** Returns the (zero-based) index of the last column in the text buffer. */ + int getColumnIndex() { + return colCount; + } + + /** Determines whether the text ends with the given suffix. */ + boolean endsWith(String suffix) { + return (sb.length() > suffix.length()) + && suffix.equals(sb.substring(sb.length() - suffix.length())); + } + } + + //------------------------------------------------------------------------ + // Optimizations + //------------------------------------------------------------------------ + + public void optimize() { + // Ideally, this pass should be the first pass run, however: + // 1) VariableReferenceCheck reports unexpected warnings if Normalize + // is done first. + // 2) ReplaceMessages, stripCode, and potentially custom passes rely on + // unmodified local names. + normalize(); + + PhaseOptimizer phaseOptimizer = new PhaseOptimizer(this, tracker, null); + if (options.devMode == DevMode.EVERY_PASS) { + phaseOptimizer.setSanityCheck(sanityCheck); + } + phaseOptimizer.consume(getPassConfig().getOptimizations()); + phaseOptimizer.process(externsRoot, jsRoot); + } + + @Override + void setCssRenamingMap(CssRenamingMap map) { + options.cssRenamingMap = map; + } + + @Override + CssRenamingMap getCssRenamingMap() { + return options.cssRenamingMap; + } + + /** + * Reprocesses the current defines over the AST. This is used by GwtCompiler + * to generate N outputs for different targets from the same (checked) AST. + * For each target, we apply the target-specific defines by calling + * {@code processDefines} and then {@code optimize} to optimize the AST + * specifically for that target. + */ + public void processDefines() { + (new DefaultPassConfig(options)).processDefines.create(this) + .process(externsRoot, jsRoot); + } + + boolean isInliningForbidden() { + return options.propertyRenaming == PropertyRenamingPolicy.HEURISTIC || + options.propertyRenaming == + PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC; + } + + /** Control Flow Analysis. */ + ControlFlowGraph computeCFG() { + logger.fine("Computing Control Flow Graph"); + Tracer tracer = newTracer("computeCFG"); + ControlFlowAnalysis cfa = new ControlFlowAnalysis(this, true, false); + process(cfa); + stopTracer(tracer, "computeCFG"); + return cfa.getCfg(); + } + + public void normalize() { + logger.fine("Normalizing"); + startPass("normalize"); + process(new Normalize(this, false)); + endPass(); + } + + @Override + void prepareAst(Node root) { + CompilerPass pass = new PrepareAst(this); + pass.process(null, root); + } + + void recordFunctionInformation() { + logger.fine("Recording function information"); + startPass("recordFunctionInformation"); + RecordFunctionInformation recordFunctionInfoPass = + new RecordFunctionInformation( + this, getPassConfig().getIntermediateState().functionNames); + process(recordFunctionInfoPass); + functionInformationMap = recordFunctionInfoPass.getMap(); + endPass(); + } + + protected final CodeChangeHandler.RecentChange recentChange = + new CodeChangeHandler.RecentChange(); + private final List codeChangeHandlers = + Lists.newArrayList(); + + /** Name of the synthetic input that holds synthesized externs. */ + static final String SYNTHETIC_EXTERNS = "{SyntheticVarsDeclar}"; + + private CompilerInput synthesizedExternsInput = null; + + @Override + void addChangeHandler(CodeChangeHandler handler) { + codeChangeHandlers.add(handler); + } + + @Override + void removeChangeHandler(CodeChangeHandler handler) { + codeChangeHandlers.remove(handler); + } + + /** + * All passes should call reportCodeChange() when they alter + * the JS tree structure. This is verified by CompilerTestCase. + * This allows us to optimize to a fixed point. + */ + @Override + public void reportCodeChange() { + for (CodeChangeHandler handler : codeChangeHandlers) { + handler.reportChange(); + } + } + + @Override + public CodingConvention getCodingConvention() { + CodingConvention convention = options.getCodingConvention(); + convention = convention != null ? convention : defaultCodingConvention; + return convention; + } + + @Override + public boolean isIdeMode() { + return options.ideMode; + } + + @Override + public boolean acceptEcmaScript5() { + switch (options.getLanguageIn()) { + case ECMASCRIPT5: + case ECMASCRIPT5_STRICT: + return true; + case ECMASCRIPT3: + return false; + } + throw new IllegalStateException("unexpected language mode"); + } + + public LanguageMode languageMode() { + return options.getLanguageIn(); + } + + @Override + public boolean acceptConstKeyword() { + return options.acceptConstKeyword; + } + + @Override + Config getParserConfig() { + if (parserConfig == null) { + Config.LanguageMode mode; + switch (options.getLanguageIn()) { + case ECMASCRIPT3: + mode = Config.LanguageMode.ECMASCRIPT3; + break; + case ECMASCRIPT5: + mode = Config.LanguageMode.ECMASCRIPT5; + break; + case ECMASCRIPT5_STRICT: + mode = Config.LanguageMode.ECMASCRIPT5_STRICT; + break; + default: + throw new IllegalStateException("unexpected language mode"); + } + + parserConfig = ParserRunner.createConfig( + isIdeMode(), + mode, + acceptConstKeyword(), + options.extraAnnotationNames); + } + return parserConfig; + } + + @Override + public boolean isTypeCheckingEnabled() { + return options.checkTypes; + } + + + //------------------------------------------------------------------------ + // Error reporting + //------------------------------------------------------------------------ + + /** + * The warning classes that are available from the command-line, and + * are suppressible by the {@code @suppress} annotation. + */ + protected DiagnosticGroups getDiagnosticGroups() { + return new DiagnosticGroups(); + } + + @Override + public void report(JSError error) { + CheckLevel level = error.getDefaultLevel(); + if (warningsGuard != null) { + CheckLevel newLevel = warningsGuard.level(error); + if (newLevel != null) { + level = newLevel; + } + } + + if (level.isOn()) { + if (getOptions().errorHandler != null) { + getOptions().errorHandler.report(level, error); + } + errorManager.report(level, error); + } + } + + @Override + public CheckLevel getErrorLevel(JSError error) { + Preconditions.checkNotNull(options); + return warningsGuard.level(error); + } + + /** + * Report an internal error. + */ + @Override + void throwInternalError(String message, Exception cause) { + String finalMessage = + "INTERNAL COMPILER ERROR.\n" + + "Please report this problem.\n" + message; + + RuntimeException e = new RuntimeException(finalMessage, cause); + if (cause != null) { + e.setStackTrace(cause.getStackTrace()); + } + throw e; + } + + + /** + * Gets the number of errors. + */ + public int getErrorCount() { + return errorManager.getErrorCount(); + } + + /** + * Gets the number of warnings. + */ + public int getWarningCount() { + return errorManager.getWarningCount(); + } + + @Override + boolean hasHaltingErrors() { + return !isIdeMode() && getErrorCount() > 0; + } + + /** + * Consults the {@link ErrorManager} to see if we've encountered errors + * that should halt compilation.

    + * + * If {@link CompilerOptions#ideMode} is {@code true}, this function + * always returns {@code false} without consulting the error manager. The + * error manager will continue to be told about new errors and warnings, but + * the compiler will complete compilation of all inputs.

    + */ + public boolean hasErrors() { + return hasHaltingErrors(); + } + + /** Called from the compiler passes, adds debug info */ + @Override + void addToDebugLog(String str) { + debugLog.append(str); + debugLog.append('\n'); + logger.fine(str); + } + + @Override + SourceFile getSourceFileByName(String sourceName) { + // Here we assume that the source name is the input name, this + // is try of JavaScript parsed from source. + if (sourceName != null) { + CompilerInput input = inputsById.get(new InputId(sourceName)); + if (input != null) { + return input.getSourceFile(); + } + } + return null; + } + + @Override + public String getSourceLine(String sourceName, int lineNumber) { + if (lineNumber < 1) { + return null; + } + SourceFile input = getSourceFileByName(sourceName); + if (input != null) { + return input.getLine(lineNumber); + } + return null; + } + + @Override + public Region getSourceRegion(String sourceName, int lineNumber) { + if (lineNumber < 1) { + return null; + } + SourceFile input = getSourceFileByName(sourceName); + if (input != null) { + return input.getRegion(lineNumber); + } + return null; + } + + //------------------------------------------------------------------------ + // Package-private helpers + //------------------------------------------------------------------------ + + @Override + Node getNodeForCodeInsertion(JSModule module) { + if (module == null) { + if (inputs.isEmpty()) { + throw new IllegalStateException("No inputs"); + } + + return inputs.get(0).getAstRoot(this); + } + + List moduleInputs = module.getInputs(); + if (moduleInputs.size() > 0) { + return moduleInputs.get(0).getAstRoot(this); + } + throw new IllegalStateException("Root module has no inputs"); + } + + public SourceMap getSourceMap() { + return sourceMap; + } + + VariableMap getVariableMap() { + return getPassConfig().getIntermediateState().variableMap; + } + + VariableMap getPropertyMap() { + return getPassConfig().getIntermediateState().propertyMap; + } + + CompilerOptions getOptions() { + return options; + } + + FunctionInformationMap getFunctionalInformationMap() { + return functionInformationMap; + } + + /** + * Sets the logging level for the com.google.javascript.jscomp package. + */ + public static void setLoggingLevel(Level level) { + logger.setLevel(level); + } + + /** Gets the DOT graph of the AST generated at the end of compilation. */ + public String getAstDotGraph() throws IOException { + if (jsRoot != null) { + ControlFlowAnalysis cfa = new ControlFlowAnalysis(this, true, false); + cfa.process(null, jsRoot); + return DotFormatter.toDot(jsRoot, cfa.getCfg()); + } else { + return ""; + } + } + + @Override + public ErrorManager getErrorManager() { + if (options == null) { + initOptions(newCompilerOptions()); + } + return errorManager; + } + + @Override + List getInputsInOrder() { + return Collections.unmodifiableList(inputs); + } + + /** + * Returns an unmodifiable view of the compiler inputs indexed by id. + */ + public Map getInputsById() { + return Collections.unmodifiableMap(inputsById); + } + + /** + * Gets the externs in the order in which they are being processed. + */ + List getExternsInOrder() { + return Collections.unmodifiableList(externs); + } + + /** + * Stores the internal compiler state just before optimization is performed. + * This can be saved and restored in order to efficiently optimize multiple + * different output targets without having to perform checking multiple times. + * + * NOTE: This does not include all parts of the compiler's internal state. In + * particular, SourceFiles and CompilerOptions are not recorded. In + * order to recreate a Compiler instance from scratch, you would need to + * call {@code init} with the same arguments as in the initial creation before + * restoring intermediate state. + */ + public static class IntermediateState implements Serializable { + private static final long serialVersionUID = 1L; + + Node externsRoot; + private Node jsRoot; + private List externs; + private List inputs; + private List modules; + private PassConfig.State passConfigState; + private JSTypeRegistry typeRegistry; + private AbstractCompiler.LifeCycleStage lifeCycleStage; + private Map injectedLibraries; + + private IntermediateState() {} + } + + /** + * Returns the current internal state, excluding the input files and modules. + */ + public IntermediateState getState() { + IntermediateState state = new IntermediateState(); + state.externsRoot = externsRoot; + state.jsRoot = jsRoot; + state.externs = externs; + state.inputs = inputs; + state.modules = modules; + state.passConfigState = getPassConfig().getIntermediateState(); + state.typeRegistry = typeRegistry; + state.lifeCycleStage = getLifeCycleStage(); + state.injectedLibraries = Maps.newLinkedHashMap(injectedLibraries); + + return state; + } + + /** + * Sets the internal state to the capture given. Note that this assumes that + * the input files are already set up. + */ + public void setState(IntermediateState state) { + externsRoot = state.externsRoot; + jsRoot = state.jsRoot; + externs = state.externs; + inputs = state.inputs; + modules = state.modules; + passes = createPassConfigInternal(); + getPassConfig().setIntermediateState(state.passConfigState); + typeRegistry = state.typeRegistry; + setLifeCycleStage(state.lifeCycleStage); + + injectedLibraries.clear(); + injectedLibraries.putAll(state.injectedLibraries); + } + + @VisibleForTesting + List getInputsForTesting() { + return inputs; + } + + @VisibleForTesting + List getExternsForTesting() { + return externs; + } + + @Override + boolean hasRegExpGlobalReferences() { + return hasRegExpGlobalReferences; + } + + @Override + void setHasRegExpGlobalReferences(boolean references) { + hasRegExpGlobalReferences = references; + } + + @Override + void updateGlobalVarReferences(Map refMapPatch, + Node collectionRoot) { + Preconditions.checkState(collectionRoot.isScript() + || collectionRoot.isBlock()); + if (globalRefMap == null) { + globalRefMap = new GlobalVarReferenceMap(getInputsInOrder(), + getExternsInOrder()); + } + globalRefMap.updateGlobalVarReferences(refMapPatch, collectionRoot); + } + + @Override + GlobalVarReferenceMap getGlobalVarReferences() { + return globalRefMap; + } + + @Override + CompilerInput getSynthesizedExternsInput() { + if (synthesizedExternsInput == null) { + synthesizedExternsInput = newExternInput(SYNTHETIC_EXTERNS); + } + return synthesizedExternsInput; + } + + @Override + public double getProgress() { + return progress; + } + + @Override + String getLastPassName() { + return lastPassName; + } + + @Override + void setProgress(double newProgress, String passName) { + this.lastPassName = passName; + if (newProgress > 1.0) { + progress = 1.0; + } else { + progress = newProgress; + } + } + + /** + * Replaces one file in a hot-swap mode. The given JsAst should be made + * from a new version of a file that already was present in the last compile + * call. If the file is new, this will silently ignored. + * + * @param ast the ast of the file that is being replaced + */ + public void replaceScript(JsAst ast) { + CompilerInput input = this.getInput(ast.getInputId()); + if (!replaceIncrementalSourceAst(ast)) { + return; + } + Node originalRoot = input.getAstRoot(this); + + processNewScript(ast, originalRoot); + } + + /** + * Adds a new Script AST to the compile state. If a script for the same file + * already exists the script will not be added, instead a call to + * #replaceScript should be used. + * + * @param ast the ast of the new file + */ + public void addNewScript(JsAst ast) { + if (!addNewSourceAst(ast)) { + return; + } + Node emptyScript = new Node(Token.SCRIPT); + InputId inputId = ast.getInputId(); + emptyScript.setInputId(inputId); + emptyScript.setStaticSourceFile( + SourceFile.fromCode(inputId.getIdName(), "")); + + processNewScript(ast, emptyScript); + } + + private void processNewScript(JsAst ast, Node originalRoot) { + Node js = ast.getAstRoot(this); + Preconditions.checkNotNull(js); + + runHotSwap(originalRoot, js, this.getCleanupPassConfig()); + // NOTE: If hot swap passes that use GlobalNamespace are added, we will need + // to revisit this approach to clearing GlobalNamespaces + runHotSwapPass(null, null, ensureDefaultPassConfig().garbageCollectChecks); + + this.getTypeRegistry().clearNamedTypes(); + this.removeSyntheticVarsInput(); + + runHotSwap(originalRoot, js, this.ensureDefaultPassConfig()); + } + + /** + * Execute the passes from a PassConfig instance over a single replaced file. + */ + private void runHotSwap( + Node originalRoot, Node js, PassConfig passConfig) { + for (PassFactory passFactory : passConfig.getChecks()) { + runHotSwapPass(originalRoot, js, passFactory); + } + } + + private void runHotSwapPass( + Node originalRoot, Node js, PassFactory passFactory) { + HotSwapCompilerPass pass = passFactory.getHotSwapPass(this); + if (pass != null) { + logger.info("Performing HotSwap for pass " + passFactory.getName()); + pass.hotSwapScript(js, originalRoot); + } + } + + private PassConfig getCleanupPassConfig() { + return new CleanupPasses(getOptions()); + } + + private void removeSyntheticVarsInput() { + String sourceName = Compiler.SYNTHETIC_EXTERNS; + removeExternInput(new InputId(sourceName)); + } + + @Override + Node ensureLibraryInjected(String resourceName) { + if (injectedLibraries.containsKey(resourceName)) { + return null; + } + + // All libraries depend on js/base.js + boolean isBase = "base".equals(resourceName); + if (!isBase) { + ensureLibraryInjected("base"); + } + + Node firstChild = loadLibraryCode(resourceName).removeChildren(); + Node lastChild = firstChild.getLastSibling(); + + Node parent = getNodeForCodeInsertion(null); + if (isBase) { + parent.addChildrenToFront(firstChild); + } else { + parent.addChildrenAfter( + firstChild, injectedLibraries.get("base")); + } + reportCodeChange(); + + injectedLibraries.put(resourceName, lastChild); + return lastChild; + } + + /** Load a library as a resource */ + @VisibleForTesting + Node loadLibraryCode(String resourceName) { + String originalCode; + try { + originalCode = CharStreams.toString(new InputStreamReader( + Compiler.class.getResourceAsStream( + String.format("js/%s.js", resourceName)), + Charsets.UTF_8)); + } catch (IOException e) { + throw new RuntimeException(e); + } + + return Normalize.parseAndNormalizeSyntheticCode( + this, originalCode, + String.format("jscomp_%s_", resourceName)); + } + + /** Returns the compiler version baked into the jar. */ + public static String getReleaseVersion() { + ResourceBundle config = ResourceBundle.getBundle(CONFIG_RESOURCE); + return config.getString("compiler.version"); + } + + /** Returns the compiler date baked into the jar. */ + public static String getReleaseDate() { + ResourceBundle config = ResourceBundle.getBundle(CONFIG_RESOURCE); + return config.getString("compiler.date"); + } + + /** + * {@inheritDoc} + */ + @Override + public void setOldParseTree(String sourceName, AstRoot oldAst) { + } + + /** + * {@inheritDoc} + */ + @Override + public AstRoot getOldParseTreeByName(String sourceName) { + return null; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CompilerInput.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CompilerInput.java new file mode 100644 index 0000000..4259a88 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CompilerInput.java @@ -0,0 +1,363 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.deps.DependencyInfo; +import com.google.javascript.jscomp.deps.JsFileParser; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.Node; +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * A class for the internal representation of an input to the compiler. + * Wraps a {@link SourceAst} and maintain state such as module for the input and + * whether the input is an extern. Also calculates provided and required types. + * + */ +public class CompilerInput + implements SourceAst, DependencyInfo { + + private static final long serialVersionUID = 1L; + + // Info about where the file lives. + private JSModule module; + final private InputId id; + + // The AST. + private final SourceAst ast; + + // Provided and required symbols. + private final Set provides = Sets.newHashSet(); + private final Set requires = Sets.newHashSet(); + private boolean generatedDependencyInfoFromSource = false; + + // An AbstractCompiler for doing parsing. + // We do not want to persist this across serialized state. + private transient AbstractCompiler compiler; + + public CompilerInput(SourceAst ast) { + this(ast, ast.getSourceFile().getName(), false); + } + + public CompilerInput(SourceAst ast, boolean isExtern) { + this(ast, ast.getInputId(), isExtern); + } + + public CompilerInput(SourceAst ast, String inputId, boolean isExtern) { + this(ast, new InputId(inputId), isExtern); + } + + public CompilerInput(SourceAst ast, InputId inputId, boolean isExtern) { + this.ast = ast; + this.id = inputId; + + // TODO(nicksantos): Add a precondition check here. People are passing + // in null, but they should not be. + if (ast != null && ast.getSourceFile() != null) { + ast.getSourceFile().setIsExtern(isExtern); + } + } + + public CompilerInput(SourceFile file) { + this(file, false); + } + + public CompilerInput(SourceFile file, boolean isExtern) { + this(new JsAst(file), isExtern); + } + + /** Returns a name for this input. Must be unique across all inputs. */ + @Override + public InputId getInputId() { + return id; + } + + /** Returns a name for this input. Must be unique across all inputs. */ + @Override + public String getName() { + return id.getIdName(); + } + + public SourceAst getAst() { + return ast; + } + + /** Gets the path relative to closure-base, if one is available. */ + @Override + public String getPathRelativeToClosureBase() { + // TODO(nicksantos): Implement me. + throw new UnsupportedOperationException(); + } + + @Override + public Node getAstRoot(AbstractCompiler compiler) { + Node root = ast.getAstRoot(compiler); + // The root maybe null if the AST can not be created. + if (root != null) { + Preconditions.checkState(root.isScript()); + Preconditions.checkNotNull(root.getInputId()); + } + return root; + } + + @Override + public void clearAst() { + ast.clearAst(); + } + + @Override + public SourceFile getSourceFile() { + return ast.getSourceFile(); + } + + @Override + public void setSourceFile(SourceFile file) { + ast.setSourceFile(file); + } + + /** Returns the SourceAst object on which this input is based. */ + public SourceAst getSourceAst() { + return ast; + } + + /** Sets an abstract compiler for doing parsing. */ + public void setCompiler(AbstractCompiler compiler) { + this.compiler = compiler; + } + + private void checkErrorManager() { + Preconditions.checkNotNull(compiler, + "Expected setCompiler to be called first: " + this); + Preconditions.checkNotNull(compiler.getErrorManager(), + "Expected compiler to call an error manager: " + this); + } + + /** Gets a list of types depended on by this input. */ + @Override + public Collection getRequires() { + checkErrorManager(); + try { + regenerateDependencyInfoIfNecessary(); + return Collections.unmodifiableSet(requires); + } catch (IOException e) { + compiler.getErrorManager().report(CheckLevel.ERROR, + JSError.make(AbstractCompiler.READ_ERROR, getName())); + return ImmutableList.of(); + } + } + + /** Gets a list of types provided by this input. */ + @Override + public Collection getProvides() { + checkErrorManager(); + try { + regenerateDependencyInfoIfNecessary(); + return Collections.unmodifiableSet(provides); + } catch (IOException e) { + compiler.getErrorManager().report(CheckLevel.ERROR, + JSError.make(AbstractCompiler.READ_ERROR, getName())); + return ImmutableList.of(); + } + } + + // TODO(nicksantos): Remove addProvide/addRequire/removeRequire once + // there is better support for discovering non-closure dependencies. + void addProvide(String provide) { + getProvides(); + provides.add(provide); + } + + void addRequire(String require) { + getRequires(); + requires.add(require); + } + + public void removeRequire(String require) { + getRequires(); + requires.remove(require); + } + + /** + * Regenerates the provides/requires if we need to do so. + */ + private void regenerateDependencyInfoIfNecessary() throws IOException { + // If the code is NOT a JsAst, then it was not originally JS code. + // Look at the Ast for dependency info. + if (!(ast instanceof JsAst)) { + Preconditions.checkNotNull(compiler, + "Expected setCompiler to be called first"); + DepsFinder finder = new DepsFinder(); + Node root = getAstRoot(compiler); + if (root == null) { + return; + } + + finder.visitTree(getAstRoot(compiler)); + + // TODO(nicksantos|user): This caching behavior is a bit + // odd, and only works if you assume the exact call flow that + // clients are currently using. In that flow, they call + // getProvides(), then remove the goog.provide calls from the + // AST, and then call getProvides() again. + // + // This won't work for any other call flow, or any sort of incremental + // compilation scheme. The API needs to be fixed so callers aren't + // doing weird things like this, and then we should get rid of the + // multiple-scan strategy. + + provides.addAll(finder.provides); + requires.addAll(finder.requires); + } else { + // Otherwise, look at the source code. + if (!generatedDependencyInfoFromSource) { + // Note: it's OK to use getName() instead of + // getPathRelativeToClosureBase() here because we're not using + // this to generate deps files. (We're only using it for + // symbol dependencies.) + DependencyInfo info = + (new JsFileParser(compiler.getErrorManager())) + .setIncludeGoogBase(true) + .parseFile(getName(), getName(), getCode()); + + provides.addAll(info.getProvides()); + requires.addAll(info.getRequires()); + + generatedDependencyInfoFromSource = true; + } + } + } + + private static class DepsFinder { + private final List provides = Lists.newArrayList(); + private final List requires = Lists.newArrayList(); + private final CodingConvention codingConvention = + new ClosureCodingConvention(); + + void visitTree(Node n) { + visitSubtree(n, null); + } + + void visitSubtree(Node n, Node parent) { + if (n.isCall()) { + String require = + codingConvention.extractClassNameIfRequire(n, parent); + if (require != null) { + requires.add(require); + } + + String provide = + codingConvention.extractClassNameIfProvide(n, parent); + if (provide != null) { + provides.add(provide); + } + return; + } else if (parent != null && + !parent.isExprResult() && + !parent.isScript()) { + return; + } + + for (Node child = n.getFirstChild(); + child != null; child = child.getNext()) { + visitSubtree(child, n); + } + } + } + + /** + * Gets the source line for the indicated line number. + * + * @param lineNumber the line number, 1 being the first line of the file. + * @return The line indicated. Does not include the newline at the end + * of the file. Returns {@code null} if it does not exist, + * or if there was an IO exception. + */ + public String getLine(int lineNumber) { + return getSourceFile().getLine(lineNumber); + } + + /** + * Get a region around the indicated line number. The exact definition of a + * region is implementation specific, but it must contain the line indicated + * by the line number. A region must not start or end by a carriage return. + * + * @param lineNumber the line number, 1 being the first line of the file. + * @return The line indicated. Returns {@code null} if it does not exist, + * or if there was an IO exception. + */ + public Region getRegion(int lineNumber) { + return getSourceFile().getRegion(lineNumber); + } + + public String getCode() throws IOException { + return getSourceFile().getCode(); + } + + /** Returns the module to which the input belongs. */ + public JSModule getModule() { + return module; + } + + /** Sets the module to which the input belongs. */ + public void setModule(JSModule module) { + // An input may only belong to one module. + Preconditions.checkArgument( + module == null || this.module == null || this.module == module); + this.module = module; + } + + /** Overrides the module to which the input belongs. */ + void overrideModule(JSModule module) { + this.module = module; + } + + public boolean isExtern() { + if (ast == null || ast.getSourceFile() == null) { + return false; + } + return ast.getSourceFile().isExtern(); + } + + void setIsExtern(boolean isExtern) { + if (ast == null || ast.getSourceFile() == null) { + return; + } + ast.getSourceFile().setIsExtern(isExtern); + } + + public int getLineOffset(int lineno) { + return ast.getSourceFile().getLineOffset(lineno); + } + + /** @return The number of lines in this input. */ + public int getNumLines() { + return ast.getSourceFile().getNumLines(); + } + + @Override + public String toString() { + return getName(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CompilerOptions.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CompilerOptions.java new file mode 100644 index 0000000..c4ba579 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CompilerOptions.java @@ -0,0 +1,2188 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.SourcePosition; + +import java.io.Serializable; +import java.nio.charset.Charset; +import java.text.ParseException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Compiler options + * @author nicksantos@google.com (Nick Santos) + */ +public class CompilerOptions implements Serializable, Cloneable { + + // Unused. For people using reflection to circumvent access control. + @SuppressWarnings("unused") + private boolean manageClosureDependencies = false; + + // A common enum for compiler passes that can run either globally or locally. + public enum Reach { + ALL, + LOCAL_ONLY, + NONE + } + + // TODO(nicksantos): All public properties of this class should be made + // package-private, and have a public setter. + + private static final long serialVersionUID = 7L; + + /** + * The JavaScript language version accepted. + */ + private LanguageMode languageIn; + + /** + * The JavaScript language version that should be produced. + * Currently, this is always the same as {@link #languageIn}. + */ + private LanguageMode languageOut; + + /** + * Whether the compiler accepts the `const' keyword. + */ + boolean acceptConstKeyword; + + /** + * Whether the compiler should assume that a function's "this" value + * never needs coercion (for example in non-strict "null" or "undefined" will + * be coerced to the global "this" and primitives to objects). + */ + private boolean assumeStrictThis; + + /** + * Configures the compiler for use as an IDE backend. In this mode: + *

      + *
    • No optimization passes will run.
    • + *
    • The last time custom passes are invoked is + * {@link CustomPassExecutionTime#BEFORE_OPTIMIZATIONS}
    • + *
    • The compiler will always try to process all inputs fully, even + * if it encounters errors.
    • + *
    • The compiler may record more information than is strictly + * needed for codegen.
    • + *
    + */ + public boolean ideMode; + + boolean saveDataStructures = false; + + /** + * Even if checkTypes is disabled, clients might want to still infer types. + * This is mostly used when ideMode is enabled. + */ + boolean inferTypes; + + /** + * Configures the compiler to skip as many passes as possible. + */ + boolean skipAllPasses; + + /** + * If true, name anonymous functions only. All others passes will be skipped. + */ + boolean nameAnonymousFunctionsOnly; + + /** + * Configures the compiler to run expensive sanity checks after + * every pass. Only intended for internal development. + */ + DevMode devMode; + + //-------------------------------- + // Input Options + //-------------------------------- + + DependencyOptions dependencyOptions = new DependencyOptions(); + + /** Returns localized replacement for MSG_* variables */ + // Transient so that clients don't have to implement Serializable. + public transient MessageBundle messageBundle = null; + + //-------------------------------- + // Checks + //-------------------------------- + + /** Checks that all symbols are defined */ + public boolean checkSymbols; + + public CheckLevel aggressiveVarCheck; + + /** Checks for suspicious variable definitions and undefined variables */ + public void setAggressiveVarCheck(CheckLevel level) { + this.aggressiveVarCheck = level; + } + + /** Checks for suspicious statements that have no effect */ + public boolean checkSuspiciousCode; + + /** Checks for invalid control structures */ + public boolean checkControlStructures; + + /** Checks types on expressions */ + public boolean checkTypes; + + boolean tightenTypes; + + /** Tightens types based on a global analysis. Experimental. */ + public void setTightenTypes(boolean tighten) { + tightenTypes = tighten; + } + + public CheckLevel reportMissingOverride; + + /** + * Flags a warning if a property is missing the @override annotation, but it + * overrides a base class property. + */ + public void setReportMissingOverride(CheckLevel level) { + reportMissingOverride = level; + } + + CheckLevel reportUnknownTypes; + + /** Flags a warning for every node whose type could not be determined. */ + public void setReportUnknownTypes(CheckLevel level) { + reportUnknownTypes = level; + } + + /** Checks for missing goog.require() calls **/ + public CheckLevel checkRequires; + + public void setCheckRequires(CheckLevel level) { + checkRequires = level; + } + + public CheckLevel checkProvides; + + /** Checks for missing goog.provides() calls **/ + public void setCheckProvides(CheckLevel level) { + checkProvides = level; + } + + public CheckLevel checkGlobalNamesLevel; + + /** + * Checks the integrity of references to qualified global names. + * (e.g. "a.b") + */ + public void setCheckGlobalNamesLevel(CheckLevel level) { + checkGlobalNamesLevel = level; + } + + public CheckLevel brokenClosureRequiresLevel; + + /** Sets the check level for bad Closure require calls. */ + public void setBrokenClosureRequiresLevel(CheckLevel level) { + brokenClosureRequiresLevel = level; + } + + public CheckLevel checkGlobalThisLevel; + + /** + * Checks for certain uses of the {@code this} keyword that are considered + * unsafe because they are likely to reference the global {@code this} + * object unintentionally. + * + * If this is off, but collapseProperties is on, then the compiler will + * usually ignore you and run this check anyways. + */ + public void setCheckGlobalThisLevel(CheckLevel level) { + this.checkGlobalThisLevel = level; + } + + public CheckLevel checkMissingGetCssNameLevel; + + /** + * Checks that certain string literals only appear in strings used as + * goog.getCssName arguments. + */ + public void setCheckMissingGetCssNameLevel(CheckLevel level) { + this.checkMissingGetCssNameLevel = level; + } + + /** + * Regex of string literals that may only appear in goog.getCssName arguments. + */ + public String checkMissingGetCssNameBlacklist; + + /** Checks that the syntactic restrictions of Caja are met. */ + boolean checkCaja; + + public void setCheckCaja(boolean check) { + checkCaja = check; + } + + /** + * A set of extra annotation names which are accepted and silently ignored + * when encountered in a source file. Defaults to null which has the same + * effect as specifying an empty set. + */ + Set extraAnnotationNames; + + //-------------------------------- + // Optimizations + //-------------------------------- + + /** Folds constants (e.g. (2 + 3) to 5) */ + public boolean foldConstants; + + /** Remove assignments to values that can not be referenced */ + public boolean deadAssignmentElimination; + + /** Inlines constants (symbols that are all CAPS) */ + public boolean inlineConstantVars; + + /** Inlines global functions */ + public boolean inlineFunctions; + + /** Inlines functions defined in local scopes */ + public boolean inlineLocalFunctions; + + /** Inlines properties */ + boolean inlineProperties; + + /** Move code to a deeper module */ + public boolean crossModuleCodeMotion; + + /** Merge two variables together as one. */ + public boolean coalesceVariableNames; + + /** Move methods to a deeper module */ + public boolean crossModuleMethodMotion; + + /** Inlines trivial getters */ + public boolean inlineGetters; + + /** Inlines variables */ + public boolean inlineVariables; + + /** Inlines variables */ + boolean inlineLocalVariables; + + // TODO(user): This is temporary. Once flow sensitive inlining is stable + // Remove this. + public boolean flowSensitiveInlineVariables; + + /** Removes code associated with unused global names */ + public boolean smartNameRemoval; + + /** Removes code that will never execute */ + public boolean removeDeadCode; + + public CheckLevel checkUnreachableCode; + + /** Checks for unreachable code */ + public void setCheckUnreachableCode(CheckLevel level) { + this.checkUnreachableCode = level; + } + + public CheckLevel checkMissingReturn; + + /** Checks for missing return statements */ + public void setCheckMissingReturn(CheckLevel level) { + this.checkMissingReturn = level; + } + + /** Extracts common prototype member declarations */ + public boolean extractPrototypeMemberDeclarations; + + /** Removes unused member prototypes */ + public boolean removeUnusedPrototypeProperties; + + /** Tells AnalyzePrototypeProperties it can remove externed props. */ + public boolean removeUnusedPrototypePropertiesInExterns; + + /** Removes unused member properties */ + public boolean removeUnusedClassProperties; + + /** Removes unused variables */ + public boolean removeUnusedVars; + + /** Removes unused variables in local scope. */ + public boolean removeUnusedLocalVars; + + /** Adds variable aliases for externals to reduce code size */ + public boolean aliasExternals; + + String aliasableGlobals; + + /** + * A comma separated white-list of global names. When {@link #aliasExternals} + * is enable, if set to a non-empty string, only externals with these names + * will be considered for aliasing. + */ + public void setAliasableGlobals(String names) { + aliasableGlobals = names; + } + + String unaliasableGlobals; + + /** + * A comma separated white-list of global names. When {@link #aliasExternals} + * is enable, these global names will not be aliased. + */ + public void setUnaliasableGlobals(String names) { + unaliasableGlobals = names; + } + + /** Collapses multiple variable declarations into one */ + public boolean collapseVariableDeclarations; + + /** Group multiple variable declarations into one */ + boolean groupVariableDeclarations; + + /** + * Collapses anonymous function declarations into named function + * declarations + */ + public boolean collapseAnonymousFunctions; + + /** + * If set to a non-empty set, those strings literals will be aliased to a + * single global instance per string, to avoid creating more objects than + * necessary. + */ + public Set aliasableStrings; + + /** + * A blacklist in the form of a regular expression to block strings that + * contains certain words from being aliased. + * If the value is the empty string, no words are blacklisted. + */ + public String aliasStringsBlacklist; + + /** + * Aliases all string literals to global instances, to avoid creating more + * objects than necessary (if true, overrides any set of strings passed in + * to aliasableStrings) + */ + public boolean aliasAllStrings; + + /** Print string usage as part of the compilation log. */ + boolean outputJsStringUsage; + + /** Converts quoted property accesses to dot syntax (a['b'] -> a.b) */ + public boolean convertToDottedProperties; + + /** Reduces the size of common function expressions. */ + public boolean rewriteFunctionExpressions; + + /** + * Remove unused and constant parameters. + */ + public boolean optimizeParameters; + + /** + * Remove unused return values. + */ + public boolean optimizeReturns; + + /** + * Remove unused parameters from call sites. + */ + public boolean optimizeCalls; + + /** + * Provide formal names for elements of arguments array. + */ + public boolean optimizeArgumentsArray; + + /** Chains calls to functions that return this. */ + boolean chainCalls; + + //-------------------------------- + // Renaming + //-------------------------------- + + /** Controls which variables get renamed. */ + public VariableRenamingPolicy variableRenaming; + + /** Controls which properties get renamed. */ + public PropertyRenamingPolicy propertyRenaming; + + /** Should we use affinity information when generating property names. */ + boolean propertyAffinity; + + /** Controls label renaming. */ + public boolean labelRenaming; + + /** Reserve property names on the global this object. */ + public boolean reserveRawExports; + + /** Should shadow variable names in outer scope. */ + boolean shadowVariables; + + /** + * Generate pseudo names for variables and properties for debugging purposes. + */ + public boolean generatePseudoNames; + + /** Specifies a prefix for all globals */ + public String renamePrefix; + + /** + * Specifies the name of an object that will be used to store all non-extern + * globals. + */ + public String renamePrefixNamespace; + + /** Aliases true, false, and null to variables with shorter names. */ + public boolean aliasKeywords; + + /** Flattens multi-level property names (e.g. a$b = x) */ + public boolean collapseProperties; + + /** Split object literals into individual variables when possible. */ + boolean collapseObjectLiterals; + + public void setCollapseObjectLiterals(boolean enabled) { + collapseObjectLiterals = enabled; + } + + /** Flattens multi-level property names on extern types (e.g. String$f = x) */ + boolean collapsePropertiesOnExternTypes; + + /** + * Devirtualize prototype method by rewriting them to be static calls that + * take the this pointer as their first argument + */ + public boolean devirtualizePrototypeMethods; + + /** + * Use @nosideeffects annotations, function bodies and name graph + * to determine if calls have side effects. Requires --check_types. + */ + public boolean computeFunctionSideEffects; + + /** + * Where to save debug report for compute function side effects. + */ + String debugFunctionSideEffectsPath; + + /** + * Rename properties to disambiguate between unrelated fields based on + * type information. + */ + public boolean disambiguateProperties; + + /** Rename unrelated properties to the same name to reduce code size. */ + public boolean ambiguateProperties; + + /** Give anonymous functions names for easier debugging */ + public AnonymousFunctionNamingPolicy anonymousFunctionNaming; + + /** Input anonymous function renaming map. */ + VariableMap inputAnonymousFunctionNamingMap; + + /** Input variable renaming map. */ + VariableMap inputVariableMap; + + /** Input property renaming map. */ + VariableMap inputPropertyMap; + + /** Whether to export test functions. */ + public boolean exportTestFunctions; + + boolean specializeInitialModule; + + /** Specialize the initial module at the cost of later modules */ + public void setSpecializeInitialModule(boolean enabled) { + specializeInitialModule = enabled; + } + + //-------------------------------- + // Special-purpose alterations + //-------------------------------- + + /** + * Replace UI strings with chrome.i18n.getMessage calls. + * Used by Chrome extensions/apps. + */ + boolean replaceMessagesWithChromeI18n; + String tcProjectId; + + public void setReplaceMessagesWithChromeI18n( + boolean replaceMessagesWithChromeI18n, + String tcProjectId) { + if (replaceMessagesWithChromeI18n && + messageBundle != null && + !(messageBundle instanceof EmptyMessageBundle)) { + throw new RuntimeException("When replacing messages with " + + "chrome.i18n.getMessage, a message bundle should not be specified."); + } + + this.replaceMessagesWithChromeI18n = replaceMessagesWithChromeI18n; + this.tcProjectId = tcProjectId; + } + + /** Inserts run-time type assertions for debugging. */ + boolean runtimeTypeCheck; + + /** + * A JS function to be used for logging run-time type assertion + * failures. It will be passed the warning as a string and the + * faulty expression as arguments. + */ + String runtimeTypeCheckLogFunction; + + /** A CodingConvention to use during the compile. */ + private CodingConvention codingConvention; + + boolean ignoreCajaProperties; + + /** Add code to skip properties that Caja adds to Object.prototype */ + public void setIgnoreCajaProperties(boolean enabled) { + ignoreCajaProperties = enabled; + } + + public String syntheticBlockStartMarker; + + public String syntheticBlockEndMarker; + + /** Compiling locale */ + public String locale; + + /** Sets the special "COMPILED" value to true */ + public boolean markAsCompiled; + + /** Removes try...catch...finally blocks for easier debugging */ + public boolean removeTryCatchFinally; + + /** Processes goog.provide() and goog.require() calls */ + public boolean closurePass; + + /** Processes jQuery aliases */ + public boolean jqueryPass; + + /** Remove goog.abstractMethod assignments. */ + boolean removeAbstractMethods; + + /** Remove goog.asserts calls. */ + boolean removeClosureAsserts; + + /** Gather CSS names (requires closurePass) */ + public boolean gatherCssNames; + + /** Names of types to strip */ + public Set stripTypes; + + /** Name suffixes that determine which variables and properties to strip */ + public Set stripNameSuffixes; + + /** Name prefixes that determine which variables and properties to strip */ + public Set stripNamePrefixes; + + /** Qualified type name prefixes that determine which types to strip */ + public Set stripTypePrefixes; + + /** Custom passes */ + public transient + Multimap customPasses; + + /** Mark no side effect calls */ + public boolean markNoSideEffectCalls; + + /** Replacements for @defines. Will be Boolean, Numbers, or Strings */ + private Map defineReplacements; + + /** What kind of processing to do for goog.tweak functions. */ + private TweakProcessing tweakProcessing; + + /** Replacements for tweaks. Will be Boolean, Numbers, or Strings */ + private Map tweakReplacements; + + /** Move top-level function declarations to the top */ + public boolean moveFunctionDeclarations; + + /** Instrumentation template to use with #recordFunctionInformation */ + public String instrumentationTemplate; + + String appNameStr; + + /** + * App identifier string for use by the instrumentation template's + * app_name_setter. @see #instrumentationTemplate + */ + public void setAppNameStr(String appNameStr) { + this.appNameStr = appNameStr; + } + + /** Record function information */ + public boolean recordFunctionInformation; + + public boolean generateExports; + + /** Map used in the renaming of CSS class names. */ + public CssRenamingMap cssRenamingMap; + + /** Whitelist used in the renaming of CSS class names. */ + Set cssRenamingWhitelist; + + /** Process instances of goog.testing.ObjectPropertyString. */ + boolean processObjectPropertyString; + + /** Replace id generators */ + boolean replaceIdGenerators = true; // true by default for legacy reasons. + + /** Id generators to replace. */ + Set idGenerators; + + /** + * A previous map of ids (serialized to a string by a previous compile). + * This will be used as a hint during the ReplaceIdGenerators pass, which + * will attempt to reuse the same ids. + */ + String idGeneratorsMapSerialized; + + /** Configuration strings */ + List replaceStringsFunctionDescriptions; + + String replaceStringsPlaceholderToken; + // A list of strings that should not be used as replacements + Set replaceStringsReservedStrings; + // A previous map of replacements to strings. + VariableMap replaceStringsInputMap; + + /** List of properties that we report invalidation errors for. */ + Map propertyInvalidationErrors; + + /** Transform AMD to CommonJS modules. */ + boolean transformAMDToCJSModules = false; + + /** Rewrite CommonJS modules so that they can be concatenated together. */ + boolean processCommonJSModules = false; + + /** CommonJS module prefix. */ + String commonJSModulePathPrefix = + ProcessCommonJSModules.DEFAULT_FILENAME_PREFIX; + + + //-------------------------------- + // Output options + //-------------------------------- + + /** Output in pretty indented format */ + public boolean prettyPrint; + + /** Line break the output a bit more aggressively */ + public boolean lineBreak; + + /** Prefer line breaks at end of file */ + public boolean preferLineBreakAtEndOfFile; + + /** Prints a separator comment before each JS script */ + public boolean printInputDelimiter; + + /** The string to use as the separator for printInputDelimiter */ + public String inputDelimiter = "// Input %num%"; + + boolean preferSingleQuotes; + + /** + * Normally, when there are an equal number of single and double quotes + * in a string, the compiler will use double quotes. Set this to true + * to prefer single quotes. + */ + public void setPreferSingleQuotes(boolean enabled) { + this.preferSingleQuotes = enabled; + } + + boolean trustedStrings; + + /** + * Some people want to put arbitrary user input into strings, which are then + * run through the compiler. These scripts are then put into HTML. + * By default, we assume strings are untrusted. If the compiler is run + * from the command-line, we assume that strings are trusted. + */ + public void setTrustedStrings(boolean yes) { + trustedStrings = yes; + } + + String reportPath; + + /** Where to save a report of global name usage */ + public void setReportPath(String reportPath) { + this.reportPath = reportPath; + } + + TracerMode tracer; + + public TracerMode getTracerMode() { + return tracer; + } + + public void setTracerMode(TracerMode mode) { + tracer = mode; + } + + private boolean colorizeErrorOutput; + + public ErrorFormat errorFormat; + + private ComposeWarningsGuard warningsGuard = new ComposeWarningsGuard(); + + int summaryDetailLevel = 1; + + int lineLengthThreshold = CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD; + + //-------------------------------- + // Special Output Options + //-------------------------------- + + /** + * Whether the exports should be made available via {@link Result} after + * compilation. This is implicitly true if {@link #externExportsPath} is set. + */ + private boolean externExports; + + /** The output path for the created externs file. */ + String externExportsPath; + + String nameReferenceReportPath; + + /** Where to save a cross-reference report from the name reference graph */ + public void setNameReferenceReportPath(String filePath) { + nameReferenceReportPath = filePath; + } + + String nameReferenceGraphPath; + + /** Where to save the name reference graph */ + public void setNameReferenceGraphPath(String filePath) { + nameReferenceGraphPath = filePath; + } + + //-------------------------------- + // Debugging Options + //-------------------------------- + + /** The output path for the source map. */ + public String sourceMapOutputPath; + + /** The detail level for the generated source map. */ + public SourceMap.DetailLevel sourceMapDetailLevel = + SourceMap.DetailLevel.SYMBOLS; + + /** The source map file format */ + public SourceMap.Format sourceMapFormat = + SourceMap.Format.DEFAULT; + + public List sourceMapLocationMappings = + Collections.emptyList(); + + /** + * Charset to use when generating code. If null, then output ASCII. + * This needs to be a string because CompilerOptions is serializable. + */ + String outputCharset; + + /** + * Whether the named objects types included 'undefined' by default. + */ + boolean looseTypes; + + /** + * When set, assume that apparently side-effect free code is meaningful. + */ + boolean protectHiddenSideEffects; + + /** + * When enabled, assume that apparently side-effect free code is meaningful. + */ + public void setProtectHiddenSideEffects(boolean enable) { + this.protectHiddenSideEffects = enable; + } + + /** + * Data holder Alias Transformation information accumulated during a compile. + */ + private transient AliasTransformationHandler aliasHandler; + + /** + * Handler for compiler warnings and errors. + */ + transient ErrorHandler errorHandler; + + /** + * Initializes compiler options. All options are disabled by default. + * + * Command-line frontends to the compiler should set these properties + * like a builder. + */ + public CompilerOptions() { + // Accepted language + languageIn = LanguageMode.ECMASCRIPT3; + + // Language variation + acceptConstKeyword = false; + + // Checks + skipAllPasses = false; + nameAnonymousFunctionsOnly = false; + devMode = DevMode.OFF; + checkSymbols = false; + aggressiveVarCheck = CheckLevel.OFF; + checkSuspiciousCode = false; + checkControlStructures = false; + checkTypes = false; + tightenTypes = false; + reportMissingOverride = CheckLevel.OFF; + reportUnknownTypes = CheckLevel.OFF; + checkRequires = CheckLevel.OFF; + checkProvides = CheckLevel.OFF; + checkGlobalNamesLevel = CheckLevel.OFF; + brokenClosureRequiresLevel = CheckLevel.ERROR; + checkGlobalThisLevel = CheckLevel.OFF; + checkUnreachableCode = CheckLevel.OFF; + checkMissingReturn = CheckLevel.OFF; + checkMissingGetCssNameLevel = CheckLevel.OFF; + checkMissingGetCssNameBlacklist = null; + checkCaja = false; + computeFunctionSideEffects = false; + chainCalls = false; + extraAnnotationNames = null; + + // Optimizations + foldConstants = false; + coalesceVariableNames = false; + deadAssignmentElimination = false; + inlineConstantVars = false; + inlineFunctions = false; + inlineLocalFunctions = false; + assumeStrictThis = false; + inlineProperties = false; + crossModuleCodeMotion = false; + crossModuleMethodMotion = false; + inlineGetters = false; + inlineVariables = false; + inlineLocalVariables = false; + smartNameRemoval = false; + removeDeadCode = false; + extractPrototypeMemberDeclarations = false; + removeUnusedPrototypeProperties = false; + removeUnusedPrototypePropertiesInExterns = false; + removeUnusedClassProperties = false; + removeUnusedVars = false; + removeUnusedLocalVars = false; + aliasExternals = false; + collapseVariableDeclarations = false; + groupVariableDeclarations = false; + collapseAnonymousFunctions = false; + aliasableStrings = Collections.emptySet(); + aliasStringsBlacklist = ""; + aliasAllStrings = false; + outputJsStringUsage = false; + convertToDottedProperties = false; + rewriteFunctionExpressions = false; + optimizeParameters = false; + optimizeReturns = false; + + // Renaming + variableRenaming = VariableRenamingPolicy.OFF; + propertyRenaming = PropertyRenamingPolicy.OFF; + propertyAffinity = false; + labelRenaming = false; + generatePseudoNames = false; + shadowVariables = false; + renamePrefix = null; + aliasKeywords = false; + collapseProperties = false; + collapsePropertiesOnExternTypes = false; + collapseObjectLiterals = false; + devirtualizePrototypeMethods = false; + disambiguateProperties = false; + ambiguateProperties = false; + anonymousFunctionNaming = AnonymousFunctionNamingPolicy.OFF; + exportTestFunctions = false; + + // Alterations + runtimeTypeCheck = false; + runtimeTypeCheckLogFunction = null; + ignoreCajaProperties = false; + syntheticBlockStartMarker = null; + syntheticBlockEndMarker = null; + locale = null; + markAsCompiled = false; + removeTryCatchFinally = false; + closurePass = false; + jqueryPass = false; + removeAbstractMethods = true; + removeClosureAsserts = false; + stripTypes = Collections.emptySet(); + stripNameSuffixes = Collections.emptySet(); + stripNamePrefixes = Collections.emptySet(); + stripTypePrefixes = Collections.emptySet(); + customPasses = null; + markNoSideEffectCalls = false; + defineReplacements = Maps.newHashMap(); + tweakProcessing = TweakProcessing.OFF; + tweakReplacements = Maps.newHashMap(); + moveFunctionDeclarations = false; + instrumentationTemplate = null; + appNameStr = ""; + recordFunctionInformation = false; + generateExports = false; + cssRenamingMap = null; + cssRenamingWhitelist = null; + processObjectPropertyString = false; + idGenerators = Collections.emptySet(); + replaceStringsFunctionDescriptions = Collections.emptyList(); + replaceStringsPlaceholderToken = ""; + replaceStringsReservedStrings = Collections.emptySet(); + propertyInvalidationErrors = Maps.newHashMap(); + + // Output + printInputDelimiter = false; + prettyPrint = false; + lineBreak = false; + preferLineBreakAtEndOfFile = false; + reportPath = null; + tracer = TracerMode.OFF; + colorizeErrorOutput = false; + errorFormat = ErrorFormat.SINGLELINE; + debugFunctionSideEffectsPath = null; + externExports = false; + nameReferenceReportPath = null; + nameReferenceGraphPath = null; + + // Debugging + aliasHandler = NULL_ALIAS_TRANSFORMATION_HANDLER; + errorHandler = null; + } + + /** + * @return Whether to attempt to remove unused class properties + */ + public boolean isRemoveUnusedClassProperties() { + return removeUnusedClassProperties; + } + + /** + * @param removeUnusedClassProperties Whether to attempt to remove + * unused class properties + */ + public void setRemoveUnusedClassProperties(boolean removeUnusedClassProperties) { + this.removeUnusedClassProperties = removeUnusedClassProperties; + } + + /** + * Returns the map of define replacements. + */ + public Map getDefineReplacements() { + return getReplacementsHelper(defineReplacements); + } + + /** + * Returns the map of tweak replacements. + */ + public Map getTweakReplacements() { + return getReplacementsHelper(tweakReplacements); + } + + /** + * Creates a map of String->Node from a map of String->Number/String/Boolean. + */ + private static Map getReplacementsHelper( + Map source) { + Map map = Maps.newHashMap(); + for (Map.Entry entry : source.entrySet()) { + String name = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof Boolean) { + map.put(name, NodeUtil.booleanNode(((Boolean) value).booleanValue())); + } else if (value instanceof Integer) { + map.put(name, IR.number(((Integer) value).intValue())); + } else if (value instanceof Double) { + map.put(name, IR.number(((Double) value).doubleValue())); + } else { + Preconditions.checkState(value instanceof String); + map.put(name, IR.string((String) value)); + } + } + return map; + } + + /** + * Sets the value of the {@code @define} variable in JS + * to a boolean literal. + */ + public void setDefineToBooleanLiteral(String defineName, boolean value) { + defineReplacements.put(defineName, new Boolean(value)); + } + + /** + * Sets the value of the {@code @define} variable in JS to a + * String literal. + */ + public void setDefineToStringLiteral(String defineName, String value) { + defineReplacements.put(defineName, value); + } + + /** + * Sets the value of the {@code @define} variable in JS to a + * number literal. + */ + public void setDefineToNumberLiteral(String defineName, int value) { + defineReplacements.put(defineName, new Integer(value)); + } + + /** + * Sets the value of the {@code @define} variable in JS to a + * number literal. + */ + public void setDefineToDoubleLiteral(String defineName, double value) { + defineReplacements.put(defineName, new Double(value)); + } + + /** + * Sets the value of the tweak in JS + * to a boolean literal. + */ + public void setTweakToBooleanLiteral(String tweakId, boolean value) { + tweakReplacements.put(tweakId, new Boolean(value)); + } + + /** + * Sets the value of the tweak in JS to a + * String literal. + */ + public void setTweakToStringLiteral(String tweakId, String value) { + tweakReplacements.put(tweakId, value); + } + + /** + * Sets the value of the tweak in JS to a + * number literal. + */ + public void setTweakToNumberLiteral(String tweakId, int value) { + tweakReplacements.put(tweakId, new Integer(value)); + } + + /** + * Sets the value of the tweak in JS to a + * number literal. + */ + public void setTweakToDoubleLiteral(String tweakId, double value) { + tweakReplacements.put(tweakId, new Double(value)); + } + + /** + * Skip all possible passes, to make the compiler as fast as possible. + */ + public void skipAllCompilerPasses() { + skipAllPasses = true; + } + + /** + * Whether the warnings guard in this Options object enables the given + * group of warnings. + */ + boolean enables(DiagnosticGroup type) { + return warningsGuard.enables(type); + } + + /** + * Whether the warnings guard in this Options object disables the given + * group of warnings. + */ + boolean disables(DiagnosticGroup type) { + return warningsGuard.disables(type); + } + + /** + * Configure the given type of warning to the given level. + */ + public void setWarningLevel(DiagnosticGroup type, CheckLevel level) { + addWarningsGuard(new DiagnosticGroupWarningsGuard(type, level)); + } + + WarningsGuard getWarningsGuard() { + return warningsGuard; + } + + /** + * Reset the warnings guard. + */ + public void resetWarningsGuard() { + warningsGuard = new ComposeWarningsGuard(); + } + + /** + * The emergency fail safe removes all strict and ERROR-escalating + * warnings guards. + */ + void useEmergencyFailSafe() { + warningsGuard = warningsGuard.makeEmergencyFailSafeGuard(); + } + + /** + * Add a guard to the set of warnings guards. + */ + public void addWarningsGuard(WarningsGuard guard) { + warningsGuard.addGuard(guard); + } + + /** + * Sets the variable and property renaming policies for the compiler, + * in a way that clears warnings about the renaming policy being + * uninitialized from flags. + */ + public void setRenamingPolicy(VariableRenamingPolicy newVariablePolicy, + PropertyRenamingPolicy newPropertyPolicy) { + this.variableRenaming = newVariablePolicy; + this.propertyRenaming = newPropertyPolicy; + } + + public void setPropertyAffinity(boolean useAffinity) { + this.propertyAffinity = useAffinity; + } + + /** Should shadow outer scope variable name during renaming. */ + public void setShadowVariables(boolean shadow) { + this.shadowVariables = shadow; + } + + /** + * If true, flattens multi-level property names on extern types + * (e.g. String$f = x). This should only be used with the typed version of + * the externs files. + */ + public void setCollapsePropertiesOnExternTypes(boolean collapse) { + collapsePropertiesOnExternTypes = collapse; + } + + /** + * If true, process goog.testing.ObjectPropertyString instances. + */ + public void setProcessObjectPropertyString(boolean process) { + processObjectPropertyString = process; + } + + /** + * @param replaceIdGenerators the replaceIdGenerators to set + */ + public void setReplaceIdGenerators(boolean replaceIdGenerators) { + this.replaceIdGenerators = replaceIdGenerators; + } + + /** + * Sets the id generators to replace. + */ + public void setIdGenerators(Set idGenerators) { + this.idGenerators = Sets.newHashSet(idGenerators); + } + + /** + * A previous map of ids (serialized to a string by a previous compile). + * This will be used as a hint during the ReplaceIdGenerators pass, which + * will attempt to reuse the same ids. + */ + public void setIdGeneratorsMap(String previousMappings) { + this.idGeneratorsMapSerialized = previousMappings; + } + + /** + * Set the function inlining policy for the compiler. + */ + public void setInlineFunctions(Reach reach) { + switch (reach) { + case ALL: + this.inlineFunctions = true; + this.inlineLocalFunctions = true; + break; + case LOCAL_ONLY: + this.inlineFunctions = false; + this.inlineLocalFunctions = true; + break; + case NONE: + this.inlineFunctions = false; + this.inlineLocalFunctions = false; + break; + default: + throw new IllegalStateException("unexpected"); + } + } + + /** + * Set the variable inlining policy for the compiler. + */ + public void setInlineVariables(Reach reach) { + switch (reach) { + case ALL: + this.inlineVariables = true; + this.inlineLocalVariables = true; + break; + case LOCAL_ONLY: + this.inlineVariables = false; + this.inlineLocalVariables = true; + break; + case NONE: + this.inlineVariables = false; + this.inlineLocalVariables = false; + break; + default: + throw new IllegalStateException("unexpected"); + } + } + + /** + * Set the function inlining policy for the compiler. + */ + public void setInlineProperties(boolean enable) { + inlineProperties = enable; + } + + /** + * Set the variable removal policy for the compiler. + */ + @Deprecated + public void setRemoveUnusedVariable(Reach reach) { + setRemoveUnusedVariables(reach); + } + + /** + * Set the variable removal policy for the compiler. + */ + public void setRemoveUnusedVariables(Reach reach) { + switch (reach) { + case ALL: + this.removeUnusedVars = true; + this.removeUnusedLocalVars = true; + break; + case LOCAL_ONLY: + this.removeUnusedVars = false; + this.removeUnusedLocalVars = true; + break; + case NONE: + this.removeUnusedVars = false; + this.removeUnusedLocalVars = false; + break; + default: + throw new IllegalStateException("unexpected"); + } + } + + /** + * Sets the functions whose debug strings to replace. + */ + public void setReplaceStringsConfiguration( + String placeholderToken, List functionDescriptors) { + this.replaceStringsPlaceholderToken = placeholderToken; + this.replaceStringsFunctionDescriptions = + Lists.newArrayList(functionDescriptors); + } + + @Deprecated + public void setRewriteNewDateGoogNow(boolean rewrite) { + } + + public void setRemoveAbstractMethods(boolean remove) { + this.removeAbstractMethods = remove; + } + + public void setRemoveClosureAsserts(boolean remove) { + this.removeClosureAsserts = remove; + } + + /** + * If true, name anonymous functions only. All other passes will be skipped. + */ + public void setNameAnonymousFunctionsOnly(boolean value) { + this.nameAnonymousFunctionsOnly = value; + } + + public void setColorizeErrorOutput(boolean colorizeErrorOutput) { + this.colorizeErrorOutput = colorizeErrorOutput; + } + + public boolean shouldColorizeErrorOutput() { + return colorizeErrorOutput; + } + + /** + * If true, chain calls to functions that return this. + */ + public void setChainCalls(boolean value) { + this.chainCalls = value; + } + + /** + * If true, accept `const' keyword. + */ + public void setAcceptConstKeyword(boolean value) { + this.acceptConstKeyword = value; + } + + /** + * Enable run-time type checking, which adds JS type assertions for debugging. + * + * @param logFunction A JS function to be used for logging run-time type + * assertion failures. + */ + public void enableRuntimeTypeCheck(String logFunction) { + this.runtimeTypeCheck = true; + this.runtimeTypeCheckLogFunction = logFunction; + } + + public void disableRuntimeTypeCheck() { + this.runtimeTypeCheck = false; + } + + public void setGenerateExports(boolean generateExports) { + this.generateExports = generateExports; + } + + public void setCodingConvention(CodingConvention codingConvention) { + this.codingConvention = codingConvention; + } + + public CodingConvention getCodingConvention() { + return codingConvention; + } + + /** + * Sets dependency options. See the DependencyOptions class for more info. + * This supersedes manageClosureDependencies. + */ + public void setDependencyOptions(DependencyOptions options) { + Preconditions.checkNotNull(options); + this.dependencyOptions = options; + } + + /** + * Sort inputs by their goog.provide/goog.require calls, and prune inputs + * whose symbols are not required. + */ + public void setManageClosureDependencies(boolean newVal) { + dependencyOptions.setDependencySorting( + newVal || dependencyOptions.shouldSortDependencies()); + dependencyOptions.setDependencyPruning( + newVal || dependencyOptions.shouldPruneDependencies()); + dependencyOptions.setMoocherDropping(false); + manageClosureDependencies = newVal; + } + + /** + * Sort inputs by their goog.provide/goog.require calls. + * + * @param entryPoints Entry points to the program. Must be goog.provide'd + * symbols. Any goog.provide'd symbols that are not a transitive + * dependency of the entry points will be deleted. + * Files without goog.provides, and their dependencies, + * will always be left in. + */ + public void setManageClosureDependencies(List entryPoints) { + Preconditions.checkNotNull(entryPoints); + setManageClosureDependencies(true); + dependencyOptions.setEntryPoints(entryPoints); + } + + /** + * Controls how detailed the compilation summary is. Values: + * 0 (never print summary), 1 (print summary only if there are + * errors or warnings), 2 (print summary if type checking is on, + * see --check_types), 3 (always print summary). The default level + * is 1 + */ + public void setSummaryDetailLevel(int summaryDetailLevel) { + this.summaryDetailLevel = summaryDetailLevel; + } + + /** + * @deprecated replaced by {@link #setExternExports} + */ + @Deprecated + public void enableExternExports(boolean enabled) { + this.externExports = enabled; + } + + public void setExtraAnnotationNames(Set extraAnnotationNames) { + this.extraAnnotationNames = Sets.newHashSet(extraAnnotationNames); + } + + public boolean isExternExportsEnabled() { + return externExports; + } + + /** + * Sets the output charset by name. + */ + public void setOutputCharset(String charsetName) { + this.outputCharset = charsetName; + } + + /** + * Gets the output charset as a rich object. + */ + Charset getOutputCharset() { + return outputCharset == null ? null : Charset.forName(outputCharset); + } + + /** + * Sets how goog.tweak calls are processed. + */ + public void setTweakProcessing(TweakProcessing tweakProcessing) { + this.tweakProcessing = tweakProcessing; + } + + public TweakProcessing getTweakProcessing() { + return tweakProcessing; + } + + /** + * Sets how goog.tweak calls are processed. + */ + public void setLanguageIn(LanguageMode languageIn) { + this.languageIn = languageIn; + this.languageOut = languageIn; + } + + public LanguageMode getLanguageIn() { + return languageIn; + } + + public LanguageMode getLanguageOut() { + return languageOut; + } + + /** + * Whether to include "undefined" in the default types. + * For example: + * "{Object}" is normally "Object|null" becomes "Object|null|undefined" + * "{?string}" is normally "string|null" becomes "string|null|undefined" + * In either case "!" annotated types excluded both null and undefined. + */ + public void setLooseTypes(boolean looseTypes) { + this.looseTypes = looseTypes; + } + + @Override + public Object clone() throws CloneNotSupportedException { + CompilerOptions clone = (CompilerOptions) super.clone(); + // TODO(bolinfest): Add relevant custom cloning. + return clone; + } + + public void setAliasTransformationHandler( + AliasTransformationHandler changes) { + this.aliasHandler = changes; + } + + public AliasTransformationHandler getAliasTransformationHandler() { + return this.aliasHandler; + } + + /** + * Set a custom handler for warnings and errors. + * + * This is mostly used for piping the warnings and errors to + * a file behind the scenes. + * + * If you want to filter warnings and errors, you should use a WarningsGuard. + * + * If you want to change how warnings and errors are reported to the user, + * you should set a ErrorManager on the Compiler. An ErrorManager is + * intended to summarize the errors for a single compile job. + */ + public void setErrorHandler(ErrorHandler handler) { + this.errorHandler = handler; + } + + /** + * If true, enables type inference. If checkTypes is enabled, this flag has + * no effect. + */ + public void setInferTypes(boolean enable) { + inferTypes = enable; + } + + /** + * Gets the inferTypes flag. Note that if checkTypes is enabled, this flag + * is ignored when configuring the compiler. + */ + public boolean getInferTypes() { + return inferTypes; + } + + /** + * @return Whether assumeStrictThis is set. + */ + public boolean assumeStrictThis() { + return assumeStrictThis; + } + + /** + * If true, enables enables additional optimizations. + */ + public void setAssumeStrictThis(boolean enable) { + this.assumeStrictThis = enable; + } + + /** + * Sets the list of properties that we report property invalidation errors + * for. + */ + public void setPropertyInvalidationErrors( + Map propertyInvalidationErrors) { + this.propertyInvalidationErrors = + Maps.newHashMap(propertyInvalidationErrors); + } + + public void setLanguageOut(LanguageMode languageOut) { + this.languageOut = languageOut; + } + + public void setIdeMode(boolean ideMode) { + this.ideMode = ideMode; + } + + /** + * Whether to keep internal data structures around after we're + * finished compiling. We do this by default when IDE mode is on. + */ + public void setSaveDataStructures(boolean save) { + this.saveDataStructures = save; + } + + public void setSkipAllPasses(boolean skipAllPasses) { + this.skipAllPasses = skipAllPasses; + } + + public void setDevMode(DevMode devMode) { + this.devMode = devMode; + } + + public void setMessageBundle(MessageBundle messageBundle) { + this.messageBundle = messageBundle; + } + + public void setCheckSymbols(boolean checkSymbols) { + this.checkSymbols = checkSymbols; + } + + public void setCheckSuspiciousCode(boolean checkSuspiciousCode) { + this.checkSuspiciousCode = checkSuspiciousCode; + } + + public void setCheckControlStructures(boolean checkControlStructures) { + this.checkControlStructures = checkControlStructures; + } + + public void setCheckTypes(boolean checkTypes) { + this.checkTypes = checkTypes; + } + + public void setCheckMissingGetCssNameBlacklist(String blackList) { + this.checkMissingGetCssNameBlacklist = blackList; + } + + public void setFoldConstants(boolean foldConstants) { + this.foldConstants = foldConstants; + } + + public void setDeadAssignmentElimination(boolean deadAssignmentElimination) { + this.deadAssignmentElimination = deadAssignmentElimination; + } + + public void setInlineConstantVars(boolean inlineConstantVars) { + this.inlineConstantVars = inlineConstantVars; + } + + public void setInlineFunctions(boolean inlineFunctions) { + this.inlineFunctions = inlineFunctions; + } + + public void setInlineLocalFunctions(boolean inlineLocalFunctions) { + this.inlineLocalFunctions = inlineLocalFunctions; + } + + public void setCrossModuleCodeMotion(boolean crossModuleCodeMotion) { + this.crossModuleCodeMotion = crossModuleCodeMotion; + } + + public void setCoalesceVariableNames(boolean coalesceVariableNames) { + this.coalesceVariableNames = coalesceVariableNames; + } + + public void setCrossModuleMethodMotion(boolean crossModuleMethodMotion) { + this.crossModuleMethodMotion = crossModuleMethodMotion; + } + + public void setInlineGetters(boolean inlineGetters) { + this.inlineGetters = inlineGetters; + } + + public void setInlineVariables(boolean inlineVariables) { + this.inlineVariables = inlineVariables; + } + + public void setInlineLocalVariables(boolean inlineLocalVariables) { + this.inlineLocalVariables = inlineLocalVariables; + } + + public void setFlowSensitiveInlineVariables(boolean enabled) { + this.flowSensitiveInlineVariables = enabled; + } + + public void setSmartNameRemoval(boolean smartNameRemoval) { + this.smartNameRemoval = smartNameRemoval; + } + + public void setRemoveDeadCode(boolean removeDeadCode) { + this.removeDeadCode = removeDeadCode; + } + + public void setExtractPrototypeMemberDeclarations(boolean enabled) { + this.extractPrototypeMemberDeclarations = enabled; + } + + public void setRemoveUnusedPrototypeProperties(boolean enabled) { + this.removeUnusedPrototypeProperties = enabled; + } + + public void setRemoveUnusedPrototypePropertiesInExterns( + boolean enabled) { + this.removeUnusedPrototypePropertiesInExterns = enabled; + } + + public void setRemoveUnusedVars(boolean removeUnusedVars) { + this.removeUnusedVars = removeUnusedVars; + } + + public void setRemoveUnusedLocalVars(boolean removeUnusedLocalVars) { + this.removeUnusedLocalVars = removeUnusedLocalVars; + } + + public void setAliasExternals(boolean aliasExternals) { + this.aliasExternals = aliasExternals; + } + + public void setCollapseVariableDeclarations(boolean enabled) { + this.collapseVariableDeclarations = enabled; + } + + public void setGroupVariableDeclarations(boolean enabled) { + this.groupVariableDeclarations = enabled; + } + + public void setCollapseAnonymousFunctions(boolean enabled) { + this.collapseAnonymousFunctions = enabled; + } + + public void setAliasableStrings(Set aliasableStrings) { + this.aliasableStrings = aliasableStrings; + } + + public void setAliasStringsBlacklist(String aliasStringsBlacklist) { + this.aliasStringsBlacklist = aliasStringsBlacklist; + } + + public void setAliasAllStrings(boolean aliasAllStrings) { + this.aliasAllStrings = aliasAllStrings; + } + + public void setOutputJsStringUsage(boolean outputJsStringUsage) { + this.outputJsStringUsage = outputJsStringUsage; + } + + public void setConvertToDottedProperties(boolean convertToDottedProperties) { + this.convertToDottedProperties = convertToDottedProperties; + } + + public void setRewriteFunctionExpressions(boolean rewriteFunctionExpressions) { + this.rewriteFunctionExpressions = rewriteFunctionExpressions; + } + + public void setOptimizeParameters(boolean optimizeParameters) { + this.optimizeParameters = optimizeParameters; + } + + public void setOptimizeReturns(boolean optimizeReturns) { + this.optimizeReturns = optimizeReturns; + } + + public void setOptimizeCalls(boolean optimizeCalls) { + this.optimizeCalls = optimizeCalls; + } + + public void setOptimizeArgumentsArray(boolean optimizeArgumentsArray) { + this.optimizeArgumentsArray = optimizeArgumentsArray; + } + + public void setVariableRenaming(VariableRenamingPolicy variableRenaming) { + this.variableRenaming = variableRenaming; + } + + public void setPropertyRenaming(PropertyRenamingPolicy propertyRenaming) { + this.propertyRenaming = propertyRenaming; + } + + public void setLabelRenaming(boolean labelRenaming) { + this.labelRenaming = labelRenaming; + } + + public void setReserveRawExports(boolean reserveRawExports) { + this.reserveRawExports = reserveRawExports; + } + + public void setGeneratePseudoNames(boolean generatePseudoNames) { + this.generatePseudoNames = generatePseudoNames; + } + + public void setRenamePrefix(String renamePrefix) { + this.renamePrefix = renamePrefix; + } + + public void setRenamePrefixNamespace(String renamePrefixNamespace) { + this.renamePrefixNamespace = renamePrefixNamespace; + } + + public void setAliasKeywords(boolean aliasKeywords) { + this.aliasKeywords = aliasKeywords; + } + + public void setCollapseProperties(boolean collapseProperties) { + this.collapseProperties = collapseProperties; + } + + public void setDevirtualizePrototypeMethods(boolean devirtualizePrototypeMethods) { + this.devirtualizePrototypeMethods = devirtualizePrototypeMethods; + } + + public void setComputeFunctionSideEffects(boolean computeFunctionSideEffects) { + this.computeFunctionSideEffects = computeFunctionSideEffects; + } + + public void setDebugFunctionSideEffectsPath(String debugFunctionSideEffectsPath) { + this.debugFunctionSideEffectsPath = debugFunctionSideEffectsPath; + } + + public void setDisambiguateProperties(boolean disambiguateProperties) { + this.disambiguateProperties = disambiguateProperties; + } + + public void setAmbiguateProperties(boolean ambiguateProperties) { + this.ambiguateProperties = ambiguateProperties; + } + + public void setAnonymousFunctionNaming( + AnonymousFunctionNamingPolicy anonymousFunctionNaming) { + this.anonymousFunctionNaming = anonymousFunctionNaming; + } + + public void setInputAnonymousFunctionNamingMap(VariableMap inputMap) { + this.inputAnonymousFunctionNamingMap = inputMap; + } + + @Deprecated + public void setInputVariableMapSerialized(byte[] inputVariableMapSerialized) + throws ParseException { + this.inputVariableMap = VariableMap.fromBytes(inputVariableMapSerialized); + } + + public void setInputVariableMap(VariableMap inputVariableMap) { + this.inputVariableMap = inputVariableMap; + } + + @Deprecated + public void setInputPropertyMapSerialized(byte[] inputPropertyMapSerialized) + throws ParseException { + this.inputPropertyMap = VariableMap.fromBytes(inputPropertyMapSerialized); + } + + public void setInputPropertyMap(VariableMap inputPropertyMap) { + this.inputPropertyMap = inputPropertyMap; + } + + public void setExportTestFunctions(boolean exportTestFunctions) { + this.exportTestFunctions = exportTestFunctions; + } + + public void setRuntimeTypeCheck(boolean runtimeTypeCheck) { + this.runtimeTypeCheck = runtimeTypeCheck; + } + + public void setRuntimeTypeCheckLogFunction(String runtimeTypeCheckLogFunction) { + this.runtimeTypeCheckLogFunction = runtimeTypeCheckLogFunction; + } + + public void setSyntheticBlockStartMarker(String syntheticBlockStartMarker) { + this.syntheticBlockStartMarker = syntheticBlockStartMarker; + } + + public void setSyntheticBlockEndMarker(String syntheticBlockEndMarker) { + this.syntheticBlockEndMarker = syntheticBlockEndMarker; + } + + public void setLocale(String locale) { + this.locale = locale; + } + + public void setMarkAsCompiled(boolean markAsCompiled) { + this.markAsCompiled = markAsCompiled; + } + + public void setRemoveTryCatchFinally(boolean removeTryCatchFinally) { + this.removeTryCatchFinally = removeTryCatchFinally; + } + + public void setClosurePass(boolean closurePass) { + this.closurePass = closurePass; + } + + public void setGatherCssNames(boolean gatherCssNames) { + this.gatherCssNames = gatherCssNames; + } + + public void setStripTypes(Set stripTypes) { + this.stripTypes = stripTypes; + } + + public void setStripNameSuffixes(Set stripNameSuffixes) { + this.stripNameSuffixes = stripNameSuffixes; + } + + public void setStripNamePrefixes(Set stripNamePrefixes) { + this.stripNamePrefixes = stripNamePrefixes; + } + + public void setStripTypePrefixes(Set stripTypePrefixes) { + this.stripTypePrefixes = stripTypePrefixes; + } + + public void setCustomPasses(Multimap customPasses) { + this.customPasses = customPasses; + } + + public void setMarkNoSideEffectCalls(boolean markNoSideEffectCalls) { + this.markNoSideEffectCalls = markNoSideEffectCalls; + } + + public void setDefineReplacements(Map defineReplacements) { + this.defineReplacements = defineReplacements; + } + + public void setTweakReplacements(Map tweakReplacements) { + this.tweakReplacements = tweakReplacements; + } + + public void setMoveFunctionDeclarations(boolean moveFunctionDeclarations) { + this.moveFunctionDeclarations = moveFunctionDeclarations; + } + + public void setInstrumentationTemplate(String instrumentationTemplate) { + this.instrumentationTemplate = instrumentationTemplate; + } + + public void setRecordFunctionInformation(boolean recordFunctionInformation) { + this.recordFunctionInformation = recordFunctionInformation; + } + + public void setCssRenamingMap(CssRenamingMap cssRenamingMap) { + this.cssRenamingMap = cssRenamingMap; + } + + public void setCssRenamingWhitelist(Set whitelist) { + this.cssRenamingWhitelist = whitelist; + } + + public void setReplaceStringsFunctionDescriptions(List replaceStringsFunctionDescriptions) { + this.replaceStringsFunctionDescriptions = replaceStringsFunctionDescriptions; + } + + public void setReplaceStringsPlaceholderToken(String replaceStringsPlaceholderToken) { + this.replaceStringsPlaceholderToken = replaceStringsPlaceholderToken; + } + + public void setReplaceStringsReservedStrings(Set replaceStringsReservedStrings) { + this.replaceStringsReservedStrings = replaceStringsReservedStrings; + } + + public void setReplaceStringsInputMap(VariableMap serializedMap) { + this.replaceStringsInputMap = serializedMap; + } + + public void setPrettyPrint(boolean prettyPrint) { + this.prettyPrint = prettyPrint; + } + + public void setLineBreak(boolean lineBreak) { + this.lineBreak = lineBreak; + } + + public void setPreferLineBreakAtEndOfFile(boolean lineBreakAtEnd) { + this.preferLineBreakAtEndOfFile = lineBreakAtEnd; + } + + public void setPrintInputDelimiter(boolean printInputDelimiter) { + this.printInputDelimiter = printInputDelimiter; + } + + public void setInputDelimiter(String inputDelimiter) { + this.inputDelimiter = inputDelimiter; + } + + public void setTracer(TracerMode tracer) { + this.tracer = tracer; + } + + public void setErrorFormat(ErrorFormat errorFormat) { + this.errorFormat = errorFormat; + } + + public void setWarningsGuard(ComposeWarningsGuard warningsGuard) { + this.warningsGuard = warningsGuard; + } + + public void setLineLengthThreshold(int lineLengthThreshold) { + this.lineLengthThreshold = lineLengthThreshold; + } + + public void setExternExports(boolean externExports) { + this.externExports = externExports; + } + + public void setExternExportsPath(String externExportsPath) { + this.externExportsPath = externExportsPath; + } + + public void setSourceMapOutputPath(String sourceMapOutputPath) { + this.sourceMapOutputPath = sourceMapOutputPath; + } + + public void setSourceMapDetailLevel(SourceMap.DetailLevel sourceMapDetailLevel) { + this.sourceMapDetailLevel = sourceMapDetailLevel; + } + + public void setSourceMapFormat(SourceMap.Format sourceMapFormat) { + this.sourceMapFormat = sourceMapFormat; + } + + public void setSourceMapLocationMappings(List sourceMapLocationMappings) { + this.sourceMapLocationMappings = sourceMapLocationMappings; + } + + /** + * Activates transformation of AMD to CommonJS modules. + */ + public void setTransformAMDToCJSModules(boolean transformAMDToCJSModules) { + this.transformAMDToCJSModules = transformAMDToCJSModules; + } + + /** + * Rewrites CommonJS modules so that modules can be concatenated together, + * by renaming all globals to avoid conflicting with other modules. + */ + public void setProcessCommonJSModules(boolean processCommonJSModules) { + this.processCommonJSModules = processCommonJSModules; + } + + /** + * Sets a path prefix for CommonJS modules. + */ + public void setCommonJSModulePathPrefix(String commonJSModulePathPrefix) { + this.commonJSModulePathPrefix = commonJSModulePathPrefix; + } + + + ////////////////////////////////////////////////////////////////////////////// + // Enums + + /** When to do the extra sanity checks */ + public static enum LanguageMode { + /** + * Traditional JavaScript + */ + ECMASCRIPT3, + + /** + * Shiny new JavaScript + */ + ECMASCRIPT5, + + /** + * Nitpicky, shiny new JavaScript + */ + ECMASCRIPT5_STRICT; + + public static LanguageMode fromString(String value) { + if (value.equals("ECMASCRIPT5_STRICT") || + value.equals("ES5_STRICT")) { + return CompilerOptions.LanguageMode.ECMASCRIPT5_STRICT; + } else if (value.equals("ECMASCRIPT5") || + value.equals("ES5")) { + return CompilerOptions.LanguageMode.ECMASCRIPT5; + } else if (value.equals("ECMASCRIPT3") || + value.equals("ES3")) { + return CompilerOptions.LanguageMode.ECMASCRIPT3; + } + return null; + } + } + + /** When to do the extra sanity checks */ + static enum DevMode { + /** + * Don't do any extra sanity checks. + */ + OFF, + + /** + * After the initial parse + */ + START, + + /** + * At the start and at the end of all optimizations. + */ + START_AND_END, + + /** + * After every pass + */ + EVERY_PASS + } + + public static enum TracerMode { + ALL, // Collect all timing and size metrics. + RAW_SIZE, // Collect all timing and size metrics, except gzipped size. + TIMING_ONLY, // Collect timing metrics only. + OFF; // Collect no timing and size metrics. + + boolean isOn() { + return this != OFF; + } + } + + public static enum TweakProcessing { + OFF, // Do not run the ProcessTweaks pass. + CHECK, // Run the pass, but do not strip out the calls. + STRIP; // Strip out all calls to goog.tweak.*. + + public boolean isOn() { + return this != OFF; + } + + public boolean shouldStrip() { + return this == STRIP; + } + } + + /** + * A Role Specific Interface for JS Compiler that represents a data holder + * object which is used to store goog.scope alias code changes to code made + * during a compile. There is no guarantee that individual alias changes are + * invoked in the order they occur during compilation, so implementations + * should not assume any relevance to the order changes arrive. + *

    + * Calls to the mutators are expected to resolve very quickly, so + * implementations should not perform expensive operations in the mutator + * methods. + * + * @author tylerg@google.com (Tyler Goodwin) + */ + public interface AliasTransformationHandler { + + /** + * Builds an AliasTransformation implementation and returns it to the + * caller. + *

    + * Callers are allowed to request multiple AliasTransformation instances for + * the same file, though it is expected that the first and last char values + * for multiple instances will not overlap. + *

    + * This method is expected to have a side-effect of storing off the created + * AliasTransformation, which guarantees that invokers of this interface + * cannot leak AliasTransformation to this implementation that the + * implementor did not create + * + * @param sourceFile the source file the aliases re contained in. + * @param position the region of the source file associated with the + * goog.scope call. The item of the SourcePosition is the returned + * AliasTransformation + */ + public AliasTransformation logAliasTransformation( + String sourceFile, SourcePosition position); + } + + /** + * A Role Specific Interface for the JS Compiler to report aliases used to + * change the code during a compile. + *

    + * While aliases defined by goog.scope are expected to by only 1 per file, and + * the only top-level structure in the file, this is not enforced. + */ + public interface AliasTransformation { + + /** + * Adds an alias definition to the AliasTransformation instance. + *

    + * Last definition for a given alias is kept if an alias is inserted + * multiple times (since this is generally the behavior in JavaScript code). + * + * @param alias the name of the alias. + * @param definition the definition of the alias. + */ + void addAlias(String alias, String definition); + } + + /** + * A Null implementation of the CodeChanges interface which performs all + * operations as a No-Op + */ + static final AliasTransformationHandler NULL_ALIAS_TRANSFORMATION_HANDLER = + new NullAliasTransformationHandler(); + + private static class NullAliasTransformationHandler + implements AliasTransformationHandler, Serializable { + private static final long serialVersionUID = 0L; + + private static final AliasTransformation NULL_ALIAS_TRANSFORMATION = + new NullAliasTransformation(); + + @Override + public AliasTransformation logAliasTransformation( + String sourceFile, SourcePosition position) { + position.setItem(NULL_ALIAS_TRANSFORMATION); + return NULL_ALIAS_TRANSFORMATION; + } + + private static class NullAliasTransformation + implements AliasTransformation, Serializable { + private static final long serialVersionUID = 0L; + + @Override + public void addAlias(String alias, String definition) { + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CompilerPass.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CompilerPass.java new file mode 100644 index 0000000..bfb8cda --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CompilerPass.java @@ -0,0 +1,41 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + *

    Interface for classes that can compile JS.

    + * + *

    Class has single function "process", which is passed + * the root node of the parsed JS tree, as well as the + * root node of the external JS tree (used to provide a public API + * and prevent renaming of system functions).

    + * + *

    Use this class to support testing with BaseCompilerTest

    + * + */ +public interface CompilerPass { + + /** + * Process the JS with root node root. + * Can modify the contents of each Node tree + * @param externs Top of external JS tree + * @param root Top of JS tree + */ + void process(Node externs, Node root); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ComposeWarningsGuard.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ComposeWarningsGuard.java new file mode 100644 index 0000000..d7bb699 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ComposeWarningsGuard.java @@ -0,0 +1,182 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.CheckLevel; + +import java.io.Serializable; +import java.util.*; +import java.util.Map; +import java.util.TreeSet; + +/** + * WarningsGuard that represents just a chain of other guards. For example we + * could have following chain + * 1) all warnings outside of /foo/ should be suppressed + * 2) errors with key JSC_BAR should be marked as warning + * 3) the rest should be reported as error + * + * This class is designed for such behavior. + * + * @author anatol@google.com (Anatol Pomazau) + */ +public class ComposeWarningsGuard extends WarningsGuard { + + private static final long serialVersionUID = 1L; + + // The order that the guards were added in. + private final Map orderOfAddition = Maps.newHashMap(); + private int numberOfAdds = 0; + + private final Comparator guardComparator = + new GuardComparator(orderOfAddition); + private boolean demoteErrors = false; + + private static class GuardComparator + implements Comparator, Serializable { + private static final long serialVersionUID = 1L; + + private final Map orderOfAddition; + private GuardComparator(Map orderOfAddition) { + this.orderOfAddition = orderOfAddition; + } + + @Override + public int compare(WarningsGuard a, WarningsGuard b) { + int priorityDiff = a.getPriority() - b.getPriority(); + if (priorityDiff != 0) { + return priorityDiff; + } + + // If the warnings guards have the same priority, the one that + // was added last wins. + return orderOfAddition.get(b).intValue() - + orderOfAddition.get(a).intValue(); + } + } + + // The order that the guards are applied in. + private final TreeSet guards = + new TreeSet(guardComparator); + + public ComposeWarningsGuard(List guards) { + addGuards(guards); + } + + public ComposeWarningsGuard(WarningsGuard... guards) { + this(Lists.newArrayList(guards)); + } + + void addGuard(WarningsGuard guard) { + if (guard instanceof ComposeWarningsGuard) { + ComposeWarningsGuard composeGuard = (ComposeWarningsGuard) guard; + if (composeGuard.demoteErrors) { + this.demoteErrors = composeGuard.demoteErrors; + } + + // Reverse the guards, so that they have the same order in the result. + addGuards(Lists.newArrayList(composeGuard.guards.descendingSet())); + } else { + numberOfAdds++; + orderOfAddition.put(guard, numberOfAdds); + guards.remove(guard); + guards.add(guard); + } + } + + private void addGuards(Iterable guards) { + for (WarningsGuard guard : guards) { + addGuard(guard); + } + } + + @Override + public CheckLevel level(JSError error) { + for (WarningsGuard guard : guards) { + CheckLevel newLevel = guard.level(error); + if (newLevel != null) { + if (demoteErrors && newLevel == CheckLevel.ERROR) { + return CheckLevel.WARNING; + } + return newLevel; + } + } + return null; + } + + @Override + public boolean disables(DiagnosticGroup group) { + nextSingleton: + for (DiagnosticType type : group.getTypes()) { + DiagnosticGroup singleton = DiagnosticGroup.forType(type); + + for (WarningsGuard guard : guards) { + if (guard.disables(singleton)) { + continue nextSingleton; + } else if (guard.enables(singleton)) { + return false; + } + } + + return false; + } + + return true; + } + + /** + * Determines whether this guard will "elevate" the status of any disabled + * diagnostic type in the group to a warning or an error. + */ + @Override + public boolean enables(DiagnosticGroup group) { + for (WarningsGuard guard : guards) { + if (guard.enables(group)) { + return true; + } else if (guard.disables(group)) { + return false; + } + } + + return false; + } + + List getGuards() { + return Collections.unmodifiableList(Lists.newArrayList(guards)); + } + + /** + * Make a warnings guard that's the same as this one but with + * all escalating guards turned down. + */ + ComposeWarningsGuard makeEmergencyFailSafeGuard() { + ComposeWarningsGuard safeGuard = new ComposeWarningsGuard(); + safeGuard.demoteErrors = true; + for (WarningsGuard guard : guards.descendingSet()) { + safeGuard.addGuard(guard); + } + return safeGuard; + } + + @Override + public String toString() { + return Joiner.on(", ").join(guards); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ConcreteType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ConcreteType.java new file mode 100644 index 0000000..7bf7593 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ConcreteType.java @@ -0,0 +1,718 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.graph.LatticeElement; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.StaticScope; +import com.google.javascript.rhino.jstype.StaticSlot; +import com.google.javascript.rhino.jstype.UnknownType; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * Represents a reference type for which the exact definition in the source is + * known. Unlike a {@code JSType} reference type, a concrete instance type of A + * indicates that an instance of A -- not a subclass of A -- is a possible + * value. Other concrete types are functions (whose definitions are known), + * arrays containing concrete types, and unions of concrete types. + * + * These types are computed by {@code TightenTypes}. + * + */ +abstract class ConcreteType implements LatticeElement { + /** Static instance of the empty set of concrete types. */ + static final ConcreteType NONE = new ConcreteNoneType(); + + /** Static instance of the set of all concrete types. */ + static final ConcreteType ALL = new ConcreteAll(); + + /** Constant empty list of function types. */ + private static final List NO_FUNCTIONS = + Lists.newArrayList(); + + /** Constant empty list of instance types. */ + private static final List NO_INSTANCES = + Lists.newArrayList(); + + /** Constant empty list of slots. */ + private static final List> NO_SLOTS = + Lists.>newArrayList(); + + protected static ConcreteType createForTypes(Collection types) { + if (types == null || types.size() == 0) { + return NONE; + } else if (types.size() == 1) { + return types.iterator().next(); + } else { + return new ConcreteUnionType(Sets.newHashSet(types)); + } + } + + /** Indicates whether this is an empty set of types. */ + boolean isNone() { return false; } + + /** Indicates whether this type is a function. */ + boolean isFunction() { return false; } + + /** + * Indicates whether this type is an instance of some type (or a prototype + * instance of a type). + * */ + boolean isInstance() { return false; } + + /** Indicates whether this type is a union of concrete types. */ + boolean isUnion() { return false; } + + /** Indicates whether this type is the set of all types. */ + boolean isAll() { return false; } + + /** Indicates whether this represents exactly one type. */ + boolean isSingleton() { return !isNone() && !isUnion() && !isAll(); } + + /** Returns this as a function, if it is one, or null, if not. */ + ConcreteFunctionType toFunction() { return null; } + + /** Returns this as an instance, if it is one, or null, if not. */ + ConcreteInstanceType toInstance() { return null; } + + /** Returns this as a union, if it is one, or null, if not. */ + ConcreteUnionType toUnion() { return null; } + + /** Returns the scope for the type, or null if not applicable. */ + StaticScope getScope() { return null; } + + /** Returns the union of this type with the given one. */ + ConcreteType unionWith(ConcreteType other) { + Preconditions.checkState(this.isSingleton()); // Sets must override. + if (!other.isSingleton()) { + return other.unionWith(this); + } else if (equals(other)) { + return this; + } else { + return new ConcreteUnionType(this, other); + } + } + + /** Returns the intersection of this type with the given one. */ + ConcreteType intersectWith(ConcreteType other) { + if (!other.isSingleton()) { + return other.intersectWith(this); + } else if (equals(other)) { + return this; + } else { + return NONE; + } + } + + /** + * Calls {@code filter()} on each type, adding it to the returned list if it + * is not null. + */ + private List getMatchingTypes(TypeFilter filter) { + C type = null; + if (isUnion()) { + List list = Lists.newArrayList(); + for (ConcreteType alt : toUnion().getAlternatives()) { + if ((type = filter.filter(alt)) != null) { + list.add(type); + } + } + return list; + } else if ((type = filter.filter(this)) != null) { + List list = Lists.newArrayList(); + list.add(type); + return list; + } else { + return filter.emptyList; + } + } + + /** + * Provides one function to filter an input, either returning the filtered + * version of the input, or null if the input does not have a corresponding + * output. + */ + abstract class TypeFilter { + /** The empty list for a caller to use if there are no non-null outputs. */ + final List emptyList; + + TypeFilter(List emptyList) { + this.emptyList = emptyList; + } + + abstract protected C filter(ConcreteType type); + } + + /** Returns all function types in this set. */ + List getFunctions() { + return getMatchingTypes(new TypeFilter(NO_FUNCTIONS) { + @Override public ConcreteFunctionType filter(ConcreteType type) { + return type.isFunction() ? type.toFunction() : null; + } + }); + } + + /** Returns all instance types in this set. */ + List getInstances() { + return getMatchingTypes(new TypeFilter(NO_INSTANCES) { + @Override public ConcreteInstanceType filter(ConcreteType type) { + return type.isInstance() ? type.toInstance() : null; + } + }); + } + + /** Returns the (non-null) instance types of all functions in this set. */ + List getFunctionInstanceTypes() { + return getMatchingTypes(new TypeFilter(NO_INSTANCES) { + @Override public ConcreteInstanceType filter(ConcreteType type) { + if (type.isFunction()) { + return type.toFunction().getInstanceType(); + } + return null; + } + }); + } + + /** Returns all (non-null) function prototype types in this set. */ + List getPrototypeTypes() { + return getMatchingTypes(new TypeFilter(NO_INSTANCES) { + @Override public ConcreteInstanceType filter(ConcreteType type) { + if (type.isInstance() + && type.toInstance().isFunctionPrototype()) { + return type.toInstance(); + } + return null; + } + }); + } + + /** Returns the (non-null) superclasses of all functions in this set. */ + List getSuperclassTypes() { + return getMatchingTypes(new TypeFilter(NO_FUNCTIONS) { + @Override public ConcreteFunctionType filter(ConcreteType type) { + return type.isFunction() + && type.toFunction().getSuperclassType() != null + ? type.toFunction().getSuperclassType() : null; + } + }); + } + + /** Returns the (non-null) index-th parameters of functions in this set. */ + List> getParameterSlots(final int index) { + return getMatchingTypes(new TypeFilter>(NO_SLOTS) { + @Override public StaticSlot filter(ConcreteType type) { + return type.isFunction() + && toFunction().getParameterSlot(index) != null + ? toFunction().getParameterSlot(index) : null; + } + }); + } + + /** + * Returns the (non-null) slots for properties with the given name in all + * instance types in this set. + */ + List> getPropertySlots(final String name) { + return getMatchingTypes(new TypeFilter>(NO_SLOTS) { + @Override public StaticSlot filter(ConcreteType type) { + StaticSlot slot = null; + if (type.isInstance()) { + slot = type.toInstance().getPropertySlot(name); + } + return slot; + } + }); + } + + /** + * Returns the concrete type for the given property from the given type. + * If the given type is a union type, returns the union of types for the slots + * of the property. + */ + ConcreteType getPropertyType(final String name) { + ConcreteType ret = NONE; + for (StaticSlot slot : getPropertySlots(name)) { + ret = ret.unionWith(slot.getType()); + } + return ret; + } + + /** Implements the empty set of types. */ + private static class ConcreteNoneType extends ConcreteType { + @Override boolean isNone() { return true; } + + @Override ConcreteType unionWith(ConcreteType other) { return other; } + + @Override ConcreteType intersectWith(ConcreteType other) { return NONE; } + + @Override public String toString() { return "()"; } + } + + /** + * Represents a specific function in the source code. Note that we assume the + * factory creates only a single instance of this class for a given + * declaration, so we do not need to override {@code Object.equals}. + * + * {@code bodyScope} contains a slot for each local variable in the function + * body's scope as well as special slots to keep track of whether the + * function is called, the this type, and the return type. + */ + static class ConcreteFunctionType extends ConcreteType { + /** Name used for the call slot (see {@code getCallSlot}). */ + static final String CALL_SLOT_NAME = ":call"; + + /** Name used for the this slot (see {@code getThisSlot}). */ + static final String THIS_SLOT_NAME = ":this"; + + /** Name used for the return slot (see {@code getReturnSlot}). */ + static final String RETURN_SLOT_NAME = ":return"; + + private final Factory factory; + private final Node declaration; + private final StaticScope parentScope; + private StaticScope bodyScope; + private ConcreteInstanceType instanceType; + private ConcreteInstanceType prototypeType; + + ConcreteFunctionType(Factory factory, + Node declaration, + StaticScope parentScope) { + this.factory = factory; + this.declaration = declaration; + this.parentScope = parentScope; + + Preconditions.checkArgument(declaration.isFunction()); + Preconditions.checkArgument(declaration.getJSType() != null); + Preconditions.checkArgument(declaration.getJSType().isFunctionType()); + } + + @Override boolean isFunction() { return true; } + + @Override ConcreteFunctionType toFunction() { return this; } + + /** + * Returns the slot representing that a call to it occurred. This is + * assigned a type if the function is called. This ensures that the body of + * the function is processed even if it has no arguments or if the arguments + * do not take any concrete types as arguments. + */ + StaticSlot getCallSlot() { + return getScope().getOwnSlot(CALL_SLOT_NAME); + } + + /** Returns the slot representing the value of 'this' in the body. */ + StaticSlot getThisSlot() { + return getScope().getOwnSlot(THIS_SLOT_NAME); + } + + /** Returns the slot representing the values returned. */ + StaticSlot getReturnSlot() { + return getScope().getOwnSlot(RETURN_SLOT_NAME); + } + + /** Returns the slot representing the index-th parameter. */ + StaticSlot getParameterSlot(int index) { + return getScope().getOwnSlot(getParameterName(index)); + } + + /** Returns the name for the index-th parameter within the function. */ + private String getParameterName(int index) { + int count = 0; + for (Node n = getFirstParameter(); n != null; n = n.getNext()) { + if (count++ == index) { + return n.getString(); + } + } + return null; + } + + /** Returns the node containing the first parameter's name. */ + private Node getFirstParameter() { + return declaration.getFirstChild().getNext().getFirstChild(); + } + + /** Returns the JSType of this function. */ + public FunctionType getJSType() { + return JSType.toMaybeFunctionType(declaration.getJSType()); + } + + /** + * Returns the concrete type representing instances of this type or null if + * it has none. + */ + ConcreteInstanceType getInstanceType() { + if (instanceType == null) { + if (getJSType().isConstructor()) { + instanceType = + factory.createConcreteInstance(getJSType().getInstanceType()); + } + } + return instanceType; + } + + /** Returns the concrete type representing the prototype of this type. */ + ConcreteInstanceType getPrototypeType() { + if (prototypeType == null) { + prototypeType = + factory.createConcreteInstance(getJSType().getPrototype()); + } + return prototypeType; + } + + /** Returns the type of the superclass (or null if none exists). */ + ConcreteFunctionType getSuperclassType() { + FunctionType superConstructor = getJSType().getSuperClassConstructor(); + return (superConstructor != null) + ? factory.getConcreteFunction(superConstructor) : null; + } + + /** Returns the scope for the body of this function. */ + @Override StaticScope getScope() { + if (bodyScope == null) { + bodyScope = factory.createFunctionScope(declaration, parentScope); + } + return bodyScope; + } + + /** + * Informally, a function is represented by + * {@code function (params): returnType} where the {@code params} is a comma + * separated list of types, the first one being a special + * {@code this:T} if the function expects a known type for {@code this}. + */ + @Override public String toString() { + StringBuilder b = new StringBuilder(32); + b.append("function ("); + boolean hasKnownTypeOfThis = !getThisSlot().getType().isNone(); + if (hasKnownTypeOfThis) { + b.append("this:"); + b.append(getThisSlot().getType().toString()); + } + + Node n = getFirstParameter(); + if (hasKnownTypeOfThis && n != null) { + b.append(", "); + } + for (int i = 0; n != null; ++i, n = n.getNext()) { + String paramName = n.getString(); + StaticSlot var = getScope().getOwnSlot(paramName); + b.append(var.getType()); + getParameterSlot(i).getType(); + if (n.getNext() != null) { + b.append(", "); + } + } + + b.append(")"); + if (getReturnSlot().getType() != null) { + b.append(": "); + b.append(getReturnSlot().getType().toString()); + } + return b.toString(); + } + } + + /** + * Represents a specific constructor in the source code. Note that we assume + * the factory creates only a single instance of this class for a given + * declaration, so we do not need to override {@code Object.equals}. + * + * The {@code StaticScope} contains a slot for each property defined on the + * instance type and the scope parent chain follows the prototype chain + * hierarchy. + */ + static class ConcreteInstanceType extends ConcreteType { + /** Factory for creating types and scopes. */ + private final Factory factory; + + /** Stores the normal type information for this instance. */ + public final ObjectType instanceType; + + /** The type information for the implicit prototype of this type, if any. */ + private ConcreteInstanceType prototype; + + /** + * A scope containing the properties of this instance, created on demand. + * Its parent scope corresponds to the scope of the implicit prototype. + */ + private StaticScope scope; + + ConcreteInstanceType(Factory factory, ObjectType instanceType) { + this.factory = factory; + this.instanceType = instanceType; + + Preconditions.checkArgument(!(instanceType instanceof UnknownType)); + } + + @Override boolean isInstance() { return true; } + + @Override ConcreteInstanceType toInstance() { return this; } + + /** Determines whether this is a function prototype type. */ + boolean isFunctionPrototype() { + return instanceType.isFunctionPrototypeType(); + } + + /** Returns the slot representing the property with the given name. */ + StaticSlot getPropertySlot(String propName) { + return getScope().getSlot(propName); + } + + /** + * Returns the closest instance type in the prototype chain that contains + * the given property. + */ + ConcreteInstanceType getInstanceTypeWithProperty(String propName) { + if (getScope().getOwnSlot(propName) != null) { + // Normalize the instance type into the prototype, to be as + // consistent as possible with non-type tightened behavior. + // + // TODO(nicksantos|user): There's a larger issue here. + // When JSCompiler infers property types on instance types, + // that means that someone is just assigning a property + // without declaring it. In this case, we can't meaningfully + // tell when the property is being pulled off the subtype + // vs. when it's being pulled off the supertype. So we should + // probably invalidate properties of this sort. + if (instanceType.getConstructor() != null) { + return getConstructorType().getPrototypeType(); + } + return this; + } else if (getImplicitPrototype() != null) { + return getImplicitPrototype().getInstanceTypeWithProperty(propName); + } else { + return null; + } + } + + /** Returns the type representing the implicit prototype. */ + ConcreteInstanceType getImplicitPrototype() { + if ((prototype == null) + && (instanceType.getImplicitPrototype() != null)) { + ObjectType proto = instanceType.getImplicitPrototype(); + if ((proto != instanceType) && !(proto instanceof UnknownType)) { + prototype = factory.createConcreteInstance(proto); + } + } + return prototype; + } + + /** Returns the type of the constructor or null if this has none. */ + ConcreteFunctionType getConstructorType() { + if (instanceType.isFunctionPrototypeType()) { + return factory.getConcreteFunction(instanceType.getOwnerFunction()); + } else { + FunctionType constructor = instanceType.getConstructor(); + return (constructor != null) + ? factory.getConcreteFunction(constructor) : null; + } + } + + /** Returns the scope of this type in the prototype chain. */ + @Override StaticScope getScope() { + if (scope == null) { + scope = factory.createInstanceScope(instanceType); + } + return scope; + } + + @Override public String toString() { return instanceType.toString(); } + } + + /** + * Represents a finite set of possible alternatives for this type. Note that + * we make no effort to merge different array types into one array type, so + * clients should not assume there is only one array in a set. + */ + static class ConcreteUnionType extends ConcreteType { + private final Set alternatives; + + ConcreteUnionType(ConcreteType... alternatives) { + this(Sets.newHashSet(alternatives)); + } + + ConcreteUnionType(Set alternatives) { + Preconditions.checkArgument(alternatives.size() > 1); + this.alternatives = alternatives; + } + + @Override boolean isUnion() { return true; } + + @Override ConcreteUnionType toUnion() { return this; } + + @Override ConcreteType unionWith(ConcreteType other) { + if (other.isSingleton()) { + if (alternatives.contains(other)) { + return this; + } else { + Set alts = Sets.newHashSet(alternatives); + alts.add(other); + return new ConcreteUnionType(alts); + } + } else if (other.isUnion()) { + ConcreteUnionType otherUnion = other.toUnion(); + if (alternatives.containsAll(otherUnion.alternatives)) { + return this; + } else if (otherUnion.alternatives.containsAll(alternatives)) { + return otherUnion; + } else { + Set alts = Sets.newHashSet(alternatives); + alts.addAll(otherUnion.alternatives); + return new ConcreteUnionType(alts); + } + } else { + Preconditions.checkArgument(other.isNone() || other.isAll()); + return other.unionWith(this); + } + } + + @Override ConcreteType intersectWith(ConcreteType other) { + if (other.isSingleton()) { + if (alternatives.contains(other)) { + return other; + } else { + return NONE; + } + } else if (other.isUnion()) { + Set types = Sets.newHashSet(alternatives); + types.retainAll(other.toUnion().alternatives); + return createForTypes(types); + } else { + Preconditions.checkArgument(other.isNone() || other.isAll()); + return other.intersectWith(this); + } + } + + /** Returns all of the types in this set of alternatives. */ + Set getAlternatives() { return alternatives; } + + @Override public boolean equals(Object obj) { + return (obj instanceof ConcreteUnionType) + && alternatives.equals(((ConcreteUnionType) obj).alternatives); + } + + @Override public int hashCode() { + return alternatives.hashCode() ^ 0x5f6e7d8c; + } + + @Override public String toString() { + List names = Lists.newArrayList(); + for (ConcreteType type : alternatives) { + names.add(type.toString()); + } + Collections.sort(names); + + return "(" + Joiner.on(",").join(names) + ")"; + } + } + + /** Implements the set of all concrete types. */ + private static class ConcreteAll extends ConcreteType { + @Override boolean isAll() { return true; } + + @Override ConcreteType unionWith(ConcreteType other) { return this; } + + @Override ConcreteType intersectWith(ConcreteType other) { return other; } + + @Override public String toString() { return "*"; } + } + + /** + * Represents an opaque singleton type that is different from any other. + * This is used by DisambiguateProperties to rename GETPROP nodes that are + * never reached in the TightenTypes flow analysis. This helps subsequent + * passes remove unreferenced properties and functions. ID passed to the + * constructor should be unique per-instance as it is used for generating + * nice, unique, names in {@code toString()}. + */ + static class ConcreteUniqueType extends ConcreteType { + private final int id; + + ConcreteUniqueType(int id) { + this.id = id; + + Preconditions.checkArgument(id >= 0); + } + + @Override public boolean equals(Object o) { + return (o instanceof ConcreteUniqueType) + && (id == ((ConcreteUniqueType) o).id); + } + + @Override public int hashCode() { + return ConcreteUniqueType.class.hashCode() ^ id; + } + + @Override public String toString() { return "Unique$" + id; } + } + + /** + * Factory for function and instance (singleton) types and scopes. It is + * important that both function and instance types are singletons because + * callers may try to create the same one multiple times, and if multiple + * exist, they will not necessarily all receive the same type information. + */ + interface Factory { + /** Returns the singleton concrete type for the given function. */ + ConcreteFunctionType createConcreteFunction( + Node declaration, StaticScope parent); + + /** Returns the singleton concrete type for the given instance type. */ + ConcreteInstanceType createConcreteInstance(ObjectType instanceType); + + /** + * Returns the already created concrete function type for the given type or + * null if none exists. + */ + ConcreteFunctionType getConcreteFunction(FunctionType function); + + /** + * Returns the already created concrete instance type for the given type or + * null if none exists. + */ + ConcreteInstanceType getConcreteInstance(ObjectType instance); + + /** + * Returns a (nested) scope for the given function. This will include + * slots for $call, $return, each parameter, and the slots declared in the + * body of the function. + */ + StaticScope createFunctionScope( + Node declaration, StaticScope parent); + + /** + * Returns a scope for the given instance type, nested inside the given + * scope of the prototype. This will include slots for each of the + * properties on our type. + */ + StaticScope createInstanceScope(ObjectType instanceType); + + /** Returns the type registry used by this factory. */ + JSTypeRegistry getTypeRegistry(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ConstCheck.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ConstCheck.java new file mode 100644 index 0000000..fa8a3ce --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ConstCheck.java @@ -0,0 +1,133 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.*; + +/** + * Verifies that constants are only assigned a value once. + * e.g. var XX = 5; + * XX = 3; // error! + * XX++; // error! + * + */ +class ConstCheck extends AbstractPostOrderCallback + implements CompilerPass { + + static final DiagnosticType CONST_REASSIGNED_VALUE_ERROR = + DiagnosticType.error( + "JSC_CONSTANT_REASSIGNED_VALUE_ERROR", + "constant {0} assigned a value more than once"); + + private final AbstractCompiler compiler; + private final Set initializedConstants; + + /** + * Creates an instance. + */ + public ConstCheck(AbstractCompiler compiler) { + this.compiler = compiler; + this.initializedConstants = new HashSet(); + } + + @Override + public void process(Node externs, Node root) { + Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.NAME: + if (parent != null && + parent.isVar() && + n.hasChildren()) { + String name = n.getString(); + Scope.Var var = t.getScope().getVar(name); + if (isConstant(var)) { + if (initializedConstants.contains(var)) { + reportError(t, n, name); + } else { + initializedConstants.add(var); + } + } + } + break; + + case Token.ASSIGN: + case Token.ASSIGN_BITOR: + case Token.ASSIGN_BITXOR: + case Token.ASSIGN_BITAND: + case Token.ASSIGN_LSH: + case Token.ASSIGN_RSH: + case Token.ASSIGN_URSH: + case Token.ASSIGN_ADD: + case Token.ASSIGN_SUB: + case Token.ASSIGN_MUL: + case Token.ASSIGN_DIV: + case Token.ASSIGN_MOD: { + Node lhs = n.getFirstChild(); + if (lhs.isName()) { + String name = lhs.getString(); + Scope.Var var = t.getScope().getVar(name); + if (isConstant(var)) { + if (initializedConstants.contains(var)) { + reportError(t, n, name); + } else { + initializedConstants.add(var); + } + } + } + break; + } + + case Token.INC: + case Token.DEC: { + Node lhs = n.getFirstChild(); + if (lhs.isName()) { + String name = lhs.getString(); + Scope.Var var = t.getScope().getVar(name); + if (isConstant(var)) { + reportError(t, n, name); + } + } + break; + } + } + } + + /** + * Gets whether a variable is a constant initialized to a literal value at + * the point where it is declared. + */ + private boolean isConstant(Scope.Var var) { + return var != null && var.isConst(); + } + + /** + * Reports a reassigned constant error. + */ + void reportError(NodeTraversal t, Node n, String name) { + compiler.report(t.makeError(n, CONST_REASSIGNED_VALUE_ERROR, name)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ControlFlowAnalysis.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ControlFlowAnalysis.java new file mode 100644 index 0000000..980deff --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ControlFlowAnalysis.java @@ -0,0 +1,1058 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.ArrayDeque; +import java.util.Comparator; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; + +/** + * This is a compiler pass that computes a control flow graph. + * + */ +final class ControlFlowAnalysis implements Callback, CompilerPass { + + /** + * Based roughly on the first few pages of + * + * "Declarative Intraprocedural Flow Analysis of Java Source Code by + * Nilsson-Nyman, Hedin, Magnusson & Ekman", + * + * this pass computes the control flow graph from the AST. However, a full + * attribute grammar is not necessary. We will compute the flow edges with a + * single post order traversal. The "follow()" of a given node will be + * computed recursively in a demand driven fashion. + * + * As of this moment, we are not performing any inter-procedural analysis + * within our framework. + */ + + private final AbstractCompiler compiler; + + private ControlFlowGraph cfg; + + private Map astPosition; + + // TODO(nicksantos): should these be node annotations? + private Map, Integer> nodePriorities; + + // We order CFG nodes by by looking at the AST positions. + // CFG nodes that come first lexically should be visited first, because + // they will often be executed first in the source program. + private final Comparator> priorityComparator = + new Comparator>() { + @Override + public int compare( + DiGraphNode a, DiGraphNode b) { + return astPosition.get(a.getValue()) - astPosition.get(b.getValue()); + } + }; + + private int astPositionCounter; + private int priorityCounter; + + private final boolean shouldTraverseFunctions; + private final boolean edgeAnnotations; + + // We need to store where we started, in case we aren't doing a flow analysis + // for the whole scope. This happens, for example, when running type inference + // on only the externs. + private Node root; + + /* + * This stack captures the structure of nested TRY blocks. The top of the + * stack is the inner most TRY block. A FUNCTION node in this stack implies + * that the handler is determined by the caller of the function at runtime. + */ + private final Deque exceptionHandler = new ArrayDeque(); + + /* + * This map is used to handle the follow of FINALLY. For example: + * + * while(x) { + * try { + * try { + * break; + * } catch (a) { + * } finally { + * foo(); + * } + * fooFollow(); + * } catch (b) { + * } finally { + * bar(); + * } + * barFollow(); + * } + * END(); + * + * In this case finallyMap will contain a map from: + * first FINALLY -> bar() + * second FINALLY -> END() + * + * When we are connecting foo() and bar() to to their respective follow, we + * must also look up this map and connect: + * foo() -> bar() + * bar() -> END + */ + private final Multimap finallyMap = HashMultimap.create(); + + /** + * Constructor. + * + * @param compiler Compiler instance. + * @param shouldTraverseFunctions Whether functions should be traversed (true + * by default). + * @param edgeAnnotations Whether to allow edge annotations. By default, + * only node annotations are allowed. + */ + ControlFlowAnalysis(AbstractCompiler compiler, + boolean shouldTraverseFunctions, boolean edgeAnnotations) { + this.compiler = compiler; + this.shouldTraverseFunctions = shouldTraverseFunctions; + this.edgeAnnotations = edgeAnnotations; + } + + ControlFlowGraph getCfg() { + return cfg; + } + + @Override + public void process(Node externs, Node root) { + this.root = root; + astPositionCounter = 0; + astPosition = Maps.newHashMap(); + nodePriorities = Maps.newHashMap(); + cfg = new AstControlFlowGraph(computeFallThrough(root), nodePriorities, + edgeAnnotations); + NodeTraversal.traverse(compiler, root, this); + astPosition.put(null, ++astPositionCounter); // the implicit return is last. + + // Now, generate the priority of nodes by doing a depth-first + // search on the CFG. + priorityCounter = 0; + DiGraphNode entry = cfg.getEntry(); + prioritizeFromEntryNode(entry); + + if (shouldTraverseFunctions) { + // If we're traversing inner functions, we need to rank the + // priority of them too. + for (DiGraphNode candidate : cfg.getDirectedGraphNodes()) { + Node value = candidate.getValue(); + if (value != null && value.isFunction()) { + Preconditions.checkState( + !nodePriorities.containsKey(candidate) || candidate == entry); + prioritizeFromEntryNode(candidate); + } + } + } + + // At this point, all reachable nodes have been given a priority, but + // unreachable nodes have not been given a priority. Put them last. + // Presumably, it doesn't really matter what priority they get, since + // this shouldn't happen in real code. + for (DiGraphNode candidate : cfg.getDirectedGraphNodes()) { + if (!nodePriorities.containsKey(candidate)) { + nodePriorities.put(candidate, ++priorityCounter); + } + } + + // Again, the implicit return node is always last. + nodePriorities.put(cfg.getImplicitReturn(), ++priorityCounter); + } + + /** + * Given an entry node, find all the nodes reachable from that node + * and prioritize them. + */ + private void prioritizeFromEntryNode(DiGraphNode entry) { + PriorityQueue> worklist = + new PriorityQueue>(10, priorityComparator); + worklist.add(entry); + + while (!worklist.isEmpty()) { + DiGraphNode current = worklist.remove(); + if (nodePriorities.containsKey(current)) { + continue; + } + + nodePriorities.put(current, ++priorityCounter); + + List> successors = + cfg.getDirectedSuccNodes(current); + for (DiGraphNode candidate : successors) { + worklist.add(candidate); + } + } + } + + @Override + public boolean shouldTraverse( + NodeTraversal nodeTraversal, Node n, Node parent) { + astPosition.put(n, astPositionCounter++); + + switch (n.getType()) { + case Token.FUNCTION: + if (shouldTraverseFunctions || n == cfg.getEntry().getValue()) { + exceptionHandler.push(n); + return true; + } + return false; + case Token.TRY: + exceptionHandler.push(n); + return true; + } + + /* + * We are going to stop the traversal depending on what the node's parent + * is. + * + * We are only interested in adding edges between nodes that change control + * flow. The most obvious ones are loops and IF-ELSE's. A statement + * transfers control to its next sibling. + * + * In case of an expression tree, there is no control flow within the tree + * even when there are short circuited operators and conditionals. When we + * are doing data flow analysis, we will simply synthesize lattices up the + * expression tree by finding the meet at each expression node. + * + * For example: within a Token.SWITCH, the expression in question does not + * change the control flow and need not to be considered. + */ + if (parent != null) { + switch (parent.getType()) { + case Token.FOR: + // Only traverse the body of the for loop. + return n == parent.getLastChild(); + + // Skip the conditions. + case Token.IF: + case Token.WHILE: + case Token.WITH: + return n != parent.getFirstChild(); + case Token.DO: + return n != parent.getFirstChild().getNext(); + // Only traverse the body of the cases + case Token.SWITCH: + case Token.CASE: + case Token.CATCH: + case Token.LABEL: + return n != parent.getFirstChild(); + case Token.FUNCTION: + return n == parent.getFirstChild().getNext().getNext(); + case Token.CONTINUE: + case Token.BREAK: + case Token.EXPR_RESULT: + case Token.VAR: + case Token.RETURN: + case Token.THROW: + return false; + case Token.TRY: + /* Just before we are about to visit the second child of the TRY node, + * we know that we will be visiting either the CATCH or the FINALLY. + * In other words, we know that the post order traversal of the TRY + * block has been finished, no more exceptions can be caught by the + * handler at this TRY block and should be taken out of the stack. + */ + if (n == parent.getFirstChild().getNext()) { + Preconditions.checkState(exceptionHandler.peek() == parent); + exceptionHandler.pop(); + } + } + } + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.IF: + handleIf(n); + return; + case Token.WHILE: + handleWhile(n); + return; + case Token.DO: + handleDo(n); + return; + case Token.FOR: + handleFor(n); + return; + case Token.SWITCH: + handleSwitch(n); + return; + case Token.CASE: + handleCase(n); + return; + case Token.DEFAULT_CASE: + handleDefault(n); + return; + case Token.BLOCK: + case Token.SCRIPT: + handleStmtList(n); + return; + case Token.FUNCTION: + handleFunction(n); + return; + case Token.EXPR_RESULT: + handleExpr(n); + return; + case Token.THROW: + handleThrow(n); + return; + case Token.TRY: + handleTry(n); + return; + case Token.CATCH: + handleCatch(n); + return; + case Token.BREAK: + handleBreak(n); + return; + case Token.CONTINUE: + handleContinue(n); + return; + case Token.RETURN: + handleReturn(n); + return; + case Token.WITH: + handleWith(n); + return; + case Token.LABEL: + return; + default: + handleStmt(n); + return; + } + } + + private void handleIf(Node node) { + Node thenBlock = node.getFirstChild().getNext(); + Node elseBlock = thenBlock.getNext(); + createEdge(node, Branch.ON_TRUE, computeFallThrough(thenBlock)); + + if (elseBlock == null) { + createEdge(node, Branch.ON_FALSE, + computeFollowNode(node, this)); // not taken branch + } else { + createEdge(node, Branch.ON_FALSE, computeFallThrough(elseBlock)); + } + connectToPossibleExceptionHandler( + node, NodeUtil.getConditionExpression(node)); + } + + private void handleWhile(Node node) { + // Control goes to the first statement if the condition evaluates to true. + createEdge(node, Branch.ON_TRUE, + computeFallThrough(node.getFirstChild().getNext())); + + // Control goes to the follow() if the condition evaluates to false. + createEdge(node, Branch.ON_FALSE, + computeFollowNode(node, this)); + connectToPossibleExceptionHandler( + node, NodeUtil.getConditionExpression(node)); + } + + private void handleDo(Node node) { + // The first edge can be the initial iteration as well as the iterations + // after. + createEdge(node, Branch.ON_TRUE, computeFallThrough(node.getFirstChild())); + // The edge that leaves the do loop if the condition fails. + createEdge(node, Branch.ON_FALSE, + computeFollowNode(node, this)); + connectToPossibleExceptionHandler( + node, NodeUtil.getConditionExpression(node)); + } + + private void handleFor(Node forNode) { + if (forNode.getChildCount() == 4) { + // We have for (init; cond; iter) { body } + Node init = forNode.getFirstChild(); + Node cond = init.getNext(); + Node iter = cond.getNext(); + Node body = iter.getNext(); + // After initialization, we transfer to the FOR which is in charge of + // checking the condition (for the first time). + createEdge(init, Branch.UNCOND, forNode); + // The edge that transfer control to the beginning of the loop body. + createEdge(forNode, Branch.ON_TRUE, computeFallThrough(body)); + // The edge to end of the loop. + createEdge(forNode, Branch.ON_FALSE, + computeFollowNode(forNode, this)); + // The end of the body will have a unconditional branch to our iter + // (handled by calling computeFollowNode of the last instruction of the + // body. Our iter will jump to the forNode again to another condition + // check. + createEdge(iter, Branch.UNCOND, forNode); + connectToPossibleExceptionHandler(init, init); + connectToPossibleExceptionHandler(forNode, cond); + connectToPossibleExceptionHandler(iter, iter); + } else { + // We have for (item in collection) { body } + Node item = forNode.getFirstChild(); + Node collection = item.getNext(); + Node body = collection.getNext(); + // The collection behaves like init. + createEdge(collection, Branch.UNCOND, forNode); + // The edge that transfer control to the beginning of the loop body. + createEdge(forNode, Branch.ON_TRUE, computeFallThrough(body)); + // The edge to end of the loop. + createEdge(forNode, Branch.ON_FALSE, + computeFollowNode(forNode, this)); + connectToPossibleExceptionHandler(forNode, collection); + } + } + + private void handleSwitch(Node node) { + // Transfer to the first non-DEFAULT CASE. if there are none, transfer + // to the DEFAULT or the EMPTY node. + Node next = getNextSiblingOfType( + node.getFirstChild().getNext(), Token.CASE, Token.EMPTY); + if (next != null) { // Has at least one CASE or EMPTY + createEdge(node, Branch.UNCOND, next); + } else { // Has no CASE but possibly a DEFAULT + if (node.getFirstChild().getNext() != null) { + createEdge(node, Branch.UNCOND, node.getFirstChild().getNext()); + } else { // No CASE, no DEFAULT + createEdge(node, Branch.UNCOND, computeFollowNode(node, this)); + } + } + connectToPossibleExceptionHandler(node, node.getFirstChild()); + } + + private void handleCase(Node node) { + // Case is a bit tricky....First it goes into the body if condition is true. + createEdge(node, Branch.ON_TRUE, + node.getFirstChild().getNext()); + // Look for the next CASE, skipping over DEFAULT. + Node next = getNextSiblingOfType(node.getNext(), Token.CASE); + if (next != null) { // Found a CASE + Preconditions.checkState(next.isCase()); + createEdge(node, Branch.ON_FALSE, next); + } else { // No more CASE found, go back and search for a DEFAULT. + Node parent = node.getParent(); + Node deflt = getNextSiblingOfType( + parent.getFirstChild().getNext(), Token.DEFAULT_CASE); + if (deflt != null) { // Has a DEFAULT + createEdge(node, Branch.ON_FALSE, deflt); + } else { // No DEFAULT found, go to the follow of the SWITCH. + createEdge(node, Branch.ON_FALSE, computeFollowNode(node, this)); + } + } + connectToPossibleExceptionHandler(node, node.getFirstChild()); + } + + private void handleDefault(Node node) { + // Directly goes to the body. It should not transfer to the next case. + createEdge(node, Branch.UNCOND, node.getFirstChild()); + } + + private void handleWith(Node node) { + // Directly goes to the body. It should not transfer to the next case. + createEdge(node, Branch.UNCOND, node.getLastChild()); + connectToPossibleExceptionHandler(node, node.getFirstChild()); + } + + private void handleStmtList(Node node) { + Node parent = node.getParent(); + // Special case, don't add a block of empty CATCH block to the graph. + if (node.isBlock() && parent != null && + parent.isTry() && + NodeUtil.getCatchBlock(parent) == node && + !NodeUtil.hasCatchHandler(node)) { + return; + } + + // A block transfer control to its first child if it is not empty. + Node child = node.getFirstChild(); + + // Function declarations are skipped since control doesn't go into that + // function (unless it is called) + while (child != null && child.isFunction()) { + child = child.getNext(); + } + + if (child != null) { + createEdge(node, Branch.UNCOND, computeFallThrough(child)); + } else { + createEdge(node, Branch.UNCOND, computeFollowNode(node, this)); + } + + // Synthetic blocks + if (parent != null) { + switch (parent.getType()) { + case Token.DEFAULT_CASE: + case Token.CASE: + case Token.TRY: + break; + default: + if (node.isBlock() && node.isSyntheticBlock()) { + createEdge(node, Branch.SYN_BLOCK, computeFollowNode(node, this)); + } + break; + } + } + } + + private void handleFunction(Node node) { + // A block transfer control to its first child if it is not empty. + Preconditions.checkState(node.getChildCount() >= 3); + createEdge(node, Branch.UNCOND, + computeFallThrough(node.getFirstChild().getNext().getNext())); + Preconditions.checkState(exceptionHandler.peek() == node); + exceptionHandler.pop(); + } + + private void handleExpr(Node node) { + createEdge(node, Branch.UNCOND, computeFollowNode(node, this)); + connectToPossibleExceptionHandler(node, node); + } + + private void handleThrow(Node node) { + connectToPossibleExceptionHandler(node, node); + } + + private void handleTry(Node node) { + createEdge(node, Branch.UNCOND, node.getFirstChild()); + } + + private void handleCatch(Node node) { + createEdge(node, Branch.UNCOND, node.getLastChild()); + } + + private void handleBreak(Node node) { + String label = null; + // See if it is a break with label. + if (node.hasChildren()) { + label = node.getFirstChild().getString(); + } + Node cur; + Node previous = null; + Node lastJump; + Node parent = node.getParent(); + /* + * Continuously look up the ancestor tree for the BREAK target or the target + * with the corresponding label and connect to it. If along the path we + * discover a FINALLY, we will connect the BREAK to that FINALLY. From then + * on, we will just record the control flow changes in the finallyMap. This + * is due to the fact that we need to connect any node that leaves its own + * FINALLY block to the outer FINALLY or the BREAK's target but those nodes + * are not known yet due to the way we traverse the nodes. + */ + for (cur = node, lastJump = node; + !isBreakTarget(cur, label); + cur = parent, parent = parent.getParent()) { + if (cur.isTry() && NodeUtil.hasFinally(cur) + && cur.getLastChild() != previous) { + if (lastJump == node) { + createEdge(lastJump, Branch.UNCOND, computeFallThrough( + cur.getLastChild())); + } else { + finallyMap.put(lastJump, computeFallThrough(cur.getLastChild())); + } + lastJump = cur; + } + if (parent == null) { + if (compiler.isIdeMode()) { + // In IDE mode, we expect that the data flow graph may + // not be well-formed. + return; + } else { + throw new IllegalStateException("Cannot find break target."); + } + } + previous = cur; + } + if (lastJump == node) { + createEdge(lastJump, Branch.UNCOND, computeFollowNode(cur, this)); + } else { + finallyMap.put(lastJump, computeFollowNode(cur, this)); + } + } + + private void handleContinue(Node node) { + String label = null; + if (node.hasChildren()) { + label = node.getFirstChild().getString(); + } + Node cur; + Node previous = null; + Node lastJump; + + // Similar to handBreak's logic with a few minor variation. + Node parent = node.getParent(); + for (cur = node, lastJump = node; + !isContinueTarget(cur, parent, label); + cur = parent, parent = parent.getParent()) { + if (cur.isTry() && NodeUtil.hasFinally(cur) + && cur.getLastChild() != previous) { + if (lastJump == node) { + createEdge(lastJump, Branch.UNCOND, cur.getLastChild()); + } else { + finallyMap.put(lastJump, computeFallThrough(cur.getLastChild())); + } + lastJump = cur; + } + Preconditions.checkState(parent != null, "Cannot find continue target."); + previous = cur; + } + Node iter = cur; + if (cur.getChildCount() == 4) { + iter = cur.getFirstChild().getNext().getNext(); + } + + if (lastJump == node) { + createEdge(node, Branch.UNCOND, iter); + } else { + finallyMap.put(lastJump, iter); + } + } + + private void handleReturn(Node node) { + Node lastJump = null; + for (Iterator iter = exceptionHandler.iterator(); iter.hasNext();) { + Node curHandler = iter.next(); + if (curHandler.isFunction()) { + break; + } + if (NodeUtil.hasFinally(curHandler)) { + if (lastJump == null) { + createEdge(node, Branch.UNCOND, curHandler.getLastChild()); + } else { + finallyMap.put(lastJump, + computeFallThrough(curHandler.getLastChild())); + } + lastJump = curHandler; + } + } + + if (node.hasChildren()) { + connectToPossibleExceptionHandler(node, node.getFirstChild()); + } + + if (lastJump == null) { + createEdge(node, Branch.UNCOND, null); + } else { + finallyMap.put(lastJump, null); + } + } + + private void handleStmt(Node node) { + // Simply transfer to the next line. + createEdge(node, Branch.UNCOND, computeFollowNode(node, this)); + connectToPossibleExceptionHandler(node, node); + } + + static Node computeFollowNode(Node node, ControlFlowAnalysis cfa) { + return computeFollowNode(node, node, cfa); + } + + static Node computeFollowNode(Node node) { + return computeFollowNode(node, node, null); + } + + /** + * Computes the follow() node of a given node and its parent. There is a side + * effect when calling this function. If this function computed an edge that + * exists a FINALLY, it'll attempt to connect the fromNode to the outer + * FINALLY according to the finallyMap. + * + * @param fromNode The original source node since {@code node} is changed + * during recursion. + * @param node The node that follow() should compute. + */ + private static Node computeFollowNode( + Node fromNode, Node node, ControlFlowAnalysis cfa) { + /* + * This is the case where: + * + * 1. Parent is null implies that we are transferring control to the end of + * the script. + * + * 2. Parent is a function implies that we are transferring control back to + * the caller of the function. + * + * 3. If the node is a return statement, we should also transfer control + * back to the caller of the function. + * + * 4. If the node is root then we have reached the end of what we have been + * asked to traverse. + * + * In all cases we should transfer control to a "symbolic return" node. + * This will make life easier for DFAs. + */ + Node parent = node.getParent(); + if (parent == null || parent.isFunction() || + (cfa != null && node == cfa.root)) { + return null; + } + + // If we are just before a IF/WHILE/DO/FOR: + switch (parent.getType()) { + // The follow() of any of the path from IF would be what follows IF. + case Token.IF: + return computeFollowNode(fromNode, parent, cfa); + case Token.CASE: + case Token.DEFAULT_CASE: + // After the body of a CASE, the control goes to the body of the next + // case, without having to go to the case condition. + if (parent.getNext() != null) { + if (parent.getNext().isCase()) { + return parent.getNext().getFirstChild().getNext(); + } else if (parent.getNext().isDefaultCase()) { + return parent.getNext().getFirstChild(); + } else { + Preconditions.checkState(false, "Not reachable"); + } + } else { + return computeFollowNode(fromNode, parent, cfa); + } + break; + case Token.FOR: + if (NodeUtil.isForIn(parent)) { + return parent; + } else { + return parent.getFirstChild().getNext().getNext(); + } + case Token.WHILE: + case Token.DO: + return parent; + case Token.TRY: + // If we are coming out of the TRY block... + if (parent.getFirstChild() == node) { + if (NodeUtil.hasFinally(parent)) { // and have FINALLY block. + return computeFallThrough(parent.getLastChild()); + } else { // and have no FINALLY. + return computeFollowNode(fromNode, parent, cfa); + } + // CATCH block. + } else if (NodeUtil.getCatchBlock(parent) == node){ + if (NodeUtil.hasFinally(parent)) { // and have FINALLY block. + return computeFallThrough(node.getNext()); + } else { + return computeFollowNode(fromNode, parent, cfa); + } + // If we are coming out of the FINALLY block... + } else if (parent.getLastChild() == node){ + if (cfa != null) { + for (Node finallyNode : cfa.finallyMap.get(parent)) { + cfa.createEdge(fromNode, Branch.ON_EX, finallyNode); + } + } + return computeFollowNode(fromNode, parent, cfa); + } + } + + // Now that we are done with the special cases follow should be its + // immediate sibling, unless its sibling is a function + Node nextSibling = node.getNext(); + + // Skip function declarations because control doesn't get pass into it. + while (nextSibling != null && nextSibling.isFunction()) { + nextSibling = nextSibling.getNext(); + } + + if (nextSibling != null) { + return computeFallThrough(nextSibling); + } else { + // If there are no more siblings, control is transferred up the AST. + return computeFollowNode(fromNode, parent, cfa); + } + } + + /** + * Computes the destination node of n when we want to fallthrough into the + * subtree of n. We don't always create a CFG edge into n itself because of + * DOs and FORs. + */ + static Node computeFallThrough(Node n) { + switch (n.getType()) { + case Token.DO: + return computeFallThrough(n.getFirstChild()); + case Token.FOR: + if (NodeUtil.isForIn(n)) { + return n.getFirstChild().getNext(); + } + return computeFallThrough(n.getFirstChild()); + case Token.LABEL: + return computeFallThrough(n.getLastChild()); + default: + return n; + } + } + + /** + * Connects the two nodes in the control flow graph. + * + * @param fromNode Source. + * @param toNode Destination. + */ + private void createEdge(Node fromNode, ControlFlowGraph.Branch branch, + Node toNode) { + cfg.createNode(fromNode); + cfg.createNode(toNode); + cfg.connectIfNotFound(fromNode, branch, toNode); + } + + /** + * Connects cfgNode to the proper CATCH block if target subtree might throw + * an exception. If there are FINALLY blocks reached before a CATCH, it will + * make the corresponding entry in finallyMap. + */ + private void connectToPossibleExceptionHandler(Node cfgNode, Node target) { + if (mayThrowException(target) && !exceptionHandler.isEmpty()) { + Node lastJump = cfgNode; + for (Node handler : exceptionHandler) { + if (handler.isFunction()) { + return; + } + Preconditions.checkState(handler.isTry()); + Node catchBlock = NodeUtil.getCatchBlock(handler); + + if (!NodeUtil.hasCatchHandler(catchBlock)) { // No catch but a FINALLY. + if (lastJump == cfgNode) { + createEdge(cfgNode, Branch.ON_EX, handler.getLastChild()); + } else { + finallyMap.put(lastJump, handler.getLastChild()); + } + } else { // Has a catch. + if (lastJump == cfgNode) { + createEdge(cfgNode, Branch.ON_EX, catchBlock); + return; + } else { + finallyMap.put(lastJump, catchBlock); + } + } + lastJump = handler; + } + } + } + + /** + * Get the next sibling (including itself) of one of the given types. + */ + private static Node getNextSiblingOfType(Node first, int ... types) { + for (Node c = first; c != null; c = c.getNext()) { + for (int type : types) { + if (c.getType() == type) { + return c; + } + } + } + return null; + } + + /** + * Checks if target is actually the break target of labeled continue. The + * label can be null if it is an unlabeled break. + */ + public static boolean isBreakTarget(Node target, String label) { + return isBreakStructure(target, label != null) && + matchLabel(target.getParent(), label); + } + + /** + * Checks if target is actually the continue target of labeled continue. The + * label can be null if it is an unlabeled continue. + */ + private static boolean isContinueTarget( + Node target, Node parent, String label) { + return isContinueStructure(target) && matchLabel(parent, label); + } + /** + * Check if label is actually referencing the target control structure. If + * label is null, it always returns true. + */ + private static boolean matchLabel(Node target, String label) { + if (label == null) { + return true; + } + while (target.isLabel()) { + if (target.getFirstChild().getString().equals(label)) { + return true; + } + target = target.getParent(); + } + return false; + } + + /** + * Determines if the subtree might throw an exception. + */ + public static boolean mayThrowException(Node n) { + switch (n.getType()) { + case Token.CALL: + case Token.GETPROP: + case Token.GETELEM: + case Token.THROW: + case Token.NEW: + case Token.ASSIGN: + case Token.INC: + case Token.DEC: + case Token.INSTANCEOF: + return true; + case Token.FUNCTION: + return false; + } + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + if (!ControlFlowGraph.isEnteringNewCfgNode(c) && mayThrowException(c)) { + return true; + } + } + return false; + } + + /** + * Determines whether the given node can be terminated with a BREAK node. + */ + static boolean isBreakStructure(Node n, boolean labeled) { + switch (n.getType()) { + case Token.FOR: + case Token.DO: + case Token.WHILE: + case Token.SWITCH: + return true; + case Token.BLOCK: + case Token.IF: + case Token.TRY: + return labeled; + default: + return false; + } + } + + /** + * Determines whether the given node can be advanced with a CONTINUE node. + */ + static boolean isContinueStructure(Node n) { + switch (n.getType()) { + case Token.FOR: + case Token.DO: + case Token.WHILE: + return true; + default: + return false; + } + } + + /** + * Get the TRY block with a CATCH that would be run if n throws an exception. + * @return The CATCH node or null if it there isn't a CATCH before the + * the function terminates. + */ + static Node getExceptionHandler(Node n) { + for (Node cur = n; + !cur.isScript() && !cur.isFunction(); + cur = cur.getParent()) { + Node catchNode = getCatchHandlerForBlock(cur); + if (catchNode != null) { + return catchNode; + } + } + return null; + } + + /** + * Locate the catch BLOCK given the first block in a TRY. + * @return The CATCH node or null there is no catch handler. + */ + static Node getCatchHandlerForBlock(Node block) { + if (block.isBlock() && + block.getParent().isTry() && + block.getParent().getFirstChild() == block) { + for (Node s = block.getNext(); s != null; s = s.getNext()) { + if (NodeUtil.hasCatchHandler(s)) { + return s.getFirstChild(); + } + } + } + return null; + } + + /** + * A {@link ControlFlowGraph} which provides a node comparator based on the + * pre-order traversal of the AST. + */ + private static class AstControlFlowGraph extends ControlFlowGraph { + private final Map, Integer> priorities; + + /** + * Constructor. + * @param entry The entry node. + * @param priorities The map from nodes to position in the AST (to be + * filled by the {@link ControlFlowAnalysis#shouldTraverse}). + */ + private AstControlFlowGraph(Node entry, + Map, Integer> priorities, + boolean edgeAnnotations) { + super(entry, + true /* node annotations */, edgeAnnotations); + this.priorities = priorities; + } + + @Override + /** + * Returns a node comparator based on the pre-order traversal of the AST. + * @param isForward x 'before' y in the pre-order traversal implies + * x 'less than' y (if true) and x 'greater than' y (if false). + */ + public Comparator> getOptionalNodeComparator( + boolean isForward) { + if (isForward) { + return new Comparator>() { + @Override + public int compare( + DiGraphNode n1, DiGraphNode n2) { + return getPosition(n1) - getPosition(n2); + } + }; + } else { + return new Comparator>() { + @Override + public int compare( + DiGraphNode n1, DiGraphNode n2) { + return getPosition(n2) - getPosition(n1); + } + }; + } + } + + /** + * Gets the pre-order traversal position of the given node. + * @return An arbitrary counter used for comparing positions. + */ + private int getPosition(DiGraphNode n) { + Integer priority = priorities.get(n); + Preconditions.checkNotNull(priority); + return priority; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ControlFlowGraph.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ControlFlowGraph.java new file mode 100644 index 0000000..c0764c0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ControlFlowGraph.java @@ -0,0 +1,206 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.jscomp.graph.LinkedDirectedGraph; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Comparator; + +/** + * Control flow graph. + * + * + * @param The instruction type of the control flow graph. + */ +class ControlFlowGraph extends + LinkedDirectedGraph { + + /** + * A special node marked by the node value key null to a singleton + * "return" when control is transferred outside of the current control flow + * graph. + */ + private final DiGraphNode implicitReturn; + + private final DiGraphNode entry; + + /** + * Constructor. + */ + ControlFlowGraph( + N entry, boolean nodeAnnotations, boolean edgeAnnotations) { + super(nodeAnnotations, edgeAnnotations); + implicitReturn = createDirectedGraphNode(null); + this.entry = createDirectedGraphNode(entry); + } + + /** + * Gets the implicit return node. + * + * @return Return node. + */ + public DiGraphNode getImplicitReturn() { + return implicitReturn; + } + + /** + * Gets the entry point of the control flow graph. In general, this should be + * the beginning of the global script or beginning of a function. + * + * @return The entry point. + */ + public DiGraphNode getEntry() { + return entry; + } + + /** + * Checks whether node is the implicit return. + * + * @param node Node. + * @return True if the node is the implicit return. + */ + public boolean isImplicitReturn( + DiGraphNode node) { + return node == implicitReturn; + } + + /** + * Connects the node to the explicit return. + * + * @param srcValue Node. + * @param edgeValue Edge. + */ + public void connectToImplicitReturn(N srcValue, Branch edgeValue) { + super.connect(srcValue, edgeValue, null); + } + + /** + * Gets a comparator for the nodes. The default implementation returns + * {@code null}. See {@link ControlFlowGraph#getOptionalNodeComparator}. + * @param isForward Whether the comparator sorts the nodes in the direction of + * the flow. + * @return a comparator or null (in particular, if not overridden) + */ + public Comparator> getOptionalNodeComparator( + boolean isForward) { + return null; + } + + /** + * The edge object for the control flow graph. + */ + public static enum Branch { + /** Edge is taken if the condition is true. */ + ON_TRUE, + /** Edge is taken if the condition is false. */ + ON_FALSE, + /** Unconditional branch. */ + UNCOND, + /** + * Exception-handling code paths. + * Conflates two kind of control flow passing: + * - An exception is thrown, and falls into a catch or finally block + * - During exception handling, a finally block finishes and control + * passes to the next finally block. + * In theory, we need 2 different edge types. In practice, we + * can just treat them as "the edges we can't really optimize". + */ + ON_EX, + /** Possible folded-away template */ + SYN_BLOCK; + + public boolean isConditional() { + return this == ON_TRUE || this == ON_FALSE; + } + } + + /** + * Abstract callback to visit a control flow graph node without going into + * subtrees of the node that are also represented by other + * control flow graph nodes. + * + *

    For example, traversing an IF node as root will visit the two subtrees + * pointed by the {@link ControlFlowGraph.Branch#ON_TRUE} and + * {@link ControlFlowGraph.Branch#ON_FALSE} edges. + */ + public abstract static class AbstractCfgNodeTraversalCallback implements + Callback { + @Override + public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, + Node parent) { + if (parent == null) { + return true; + } + return !isEnteringNewCfgNode(n); + } + } + + /** + * @return True if n should be represented by a new CFG node in the control + * flow graph. + */ + public static boolean isEnteringNewCfgNode(Node n) { + Node parent = n.getParent(); + switch (parent.getType()) { + case Token.BLOCK: + case Token.SCRIPT: + case Token.TRY: + return true; + case Token.FUNCTION: + // A function node represents the start of a function where the name + // bleeds into the local scope and parameters are assigned + // to the formal argument names. The node includes the name of the + // function and the LP list since we assume the whole set up process + // is atomic without change in control flow. The next change of + // control is going into the function's body, represented by the second + // child. + return n != parent.getFirstChild().getNext(); + case Token.WHILE: + case Token.DO: + case Token.IF: + // These control structures are represented by a node that holds the + // condition. Each of them is a branch node based on its condition. + return NodeUtil.getConditionExpression(parent) != n; + + case Token.FOR: + // The FOR(;;) node differs from other control structures in that + // it has an initialization and an increment statement. Those + // two statements have corresponding CFG nodes to represent them. + // The FOR node only represents the condition check for each iteration. + // That way the following: + // for(var x = 0; x < 10; x++) { } has a graph that is isomorphic to + // var x = 0; while(x<10) { x++; } + if (NodeUtil.isForIn(parent)) { + // TODO(user): Investigate how we should handle the case where + // we have a very complex expression inside the FOR-IN header. + return n != parent.getFirstChild(); + } else { + return NodeUtil.getConditionExpression(parent) != n; + } + case Token.SWITCH: + case Token.CASE: + case Token.CATCH: + case Token.WITH: + return n != parent.getFirstChild(); + default: + return false; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ControlStructureCheck.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ControlStructureCheck.java new file mode 100644 index 0000000..22b4d34 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ControlStructureCheck.java @@ -0,0 +1,76 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * Check for usage of 'with'. + * + */ +class ControlStructureCheck implements HotSwapCompilerPass { + + private final AbstractCompiler compiler; + + static final DiagnosticType USE_OF_WITH = DiagnosticType.warning( + "JSC_USE_OF_WITH", + "The use of the 'with' structure should be avoided."); + + ControlStructureCheck(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + check(root); + } + + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + check(scriptRoot); + } + + /** + * Reports errors for any invalid use of control structures. + * + * @param node Current node to check. + */ + private void check(Node node) { + switch (node.getType()) { + case Token.WITH: + JSDocInfo info = node.getJSDocInfo(); + boolean allowWith = + info != null && info.getSuppressions().contains("with"); + if (!allowWith) { + report(node, USE_OF_WITH); + } + break; + } + + for (Node bChild = node.getFirstChild(); bChild != null;) { + Node next = bChild.getNext(); + check(bChild); + bChild = next; + } + } + + private void report(Node n, DiagnosticType error) { + compiler.report(JSError.make(n.getSourceFileName(), n, error)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ConvertToDottedProperties.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ConvertToDottedProperties.java new file mode 100644 index 0000000..7a13c9e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ConvertToDottedProperties.java @@ -0,0 +1,69 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * Converts property accesses from quoted string syntax to dot syntax, where + * possible. Dot syntax is more compact and avoids an object allocation in + * IE 6. + * + */ +class ConvertToDottedProperties extends AbstractPostOrderCallback + implements CompilerPass { + + private final AbstractCompiler compiler; + + ConvertToDottedProperties(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.GETTER_DEF: + case Token.SETTER_DEF: + case Token.STRING_KEY: + if (NodeUtil.isValidPropertyName(n.getString())) { + n.putBooleanProp(Node.QUOTED_PROP, false); + compiler.reportCodeChange(); + } + break; + + case Token.GETELEM: + Node left = n.getFirstChild(); + Node right = left.getNext(); + if (right.isString() && + NodeUtil.isValidPropertyName(right.getString())) { + n.removeChild(left); + n.removeChild(right); + parent.replaceChild(n, IR.getprop(left, right)); + compiler.reportCodeChange(); + } + break; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CreateSyntheticBlocks.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CreateSyntheticBlocks.java new file mode 100644 index 0000000..3be8a6b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CreateSyntheticBlocks.java @@ -0,0 +1,217 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.List; + +import javax.annotation.Nullable; + +/** + * Creates synthetic blocks to optimizations from moving code + * past markers in the source. + * + */ +class CreateSyntheticBlocks implements CompilerPass { + static final DiagnosticType UNMATCHED_START_MARKER = DiagnosticType.warning( + "JSC_UNMATCHED_START_MARKER", "Unmatched {0}"); + + static final DiagnosticType UNMATCHED_END_MARKER = DiagnosticType.warning( + "JSC_UNMATCHED_END_MARKER", "Unmatched {1} - {0} not in the same block"); + + static final DiagnosticType INVALID_MARKER_USAGE = DiagnosticType.warning( + "JSC_INVALID_MARKER_USAGE", "Marker {0} can only be used in a simple " + + "call expression"); + + private final AbstractCompiler compiler; + + /** Name of the start marker. */ + private final String startMarkerName; + + /** Name of the end marker. */ + private final String endMarkerName; + + /** + * Markers can be nested. + */ + private final Deque markerStack = new ArrayDeque(); + + private final List validMarkers = Lists.newArrayList(); + + private class Marker { + final Node startMarker; + final Node endMarker; + public Marker(Node startMarker, Node endMarker) { + this.startMarker = startMarker; + this.endMarker = endMarker; + } + } + + public CreateSyntheticBlocks(AbstractCompiler compiler, + String startMarkerName, String endMarkerName) { + this.compiler = compiler; + this.startMarkerName = startMarkerName; + this.endMarkerName = endMarkerName; + } + + @Override + public void process(Node externs, Node root) { + // Find and validate the markers. + NodeTraversal.traverse(compiler, root, new Callback()); + + // Complain about any unmatched markers. + for (Node node : markerStack) { + compiler.report( + JSError.make(NodeUtil.getSourceName(node), + node, + UNMATCHED_START_MARKER, startMarkerName)); + } + + // Add the block for the valid marker sets. + for (Marker marker : validMarkers) { + addBlocks(marker); + } + } + + /** + * @param marker The marker to add synthetic blocks for. + */ + private void addBlocks(Marker marker) { + // Add block around the template section so that it looks like this: + // BLOCK (synthetic) + // START + // BLOCK (synthetic) + // BODY + // END + // This prevents the start or end markers from mingling with the code + // in the block body. + + + Node originalParent = marker.endMarker.getParent(); + Node outerBlock = IR.block(); + outerBlock.setIsSyntheticBlock(true); + originalParent.addChildBefore(outerBlock, marker.startMarker); + + Node innerBlock = IR.block(); + innerBlock.setIsSyntheticBlock(true); + // Move everything after the start Node up to the end Node into the inner + // block. + moveSiblingExclusive(originalParent, innerBlock, + marker.startMarker, + marker.endMarker); + + // Add the start node. + outerBlock.addChildToBack(originalParent.removeChildAfter(outerBlock)); + // Add the inner block + outerBlock.addChildToBack(innerBlock); + // and finally the end node. + outerBlock.addChildToBack(originalParent.removeChildAfter(outerBlock)); + + compiler.reportCodeChange(); + } + + /** + * Move the Nodes between start and end from the source block to the + * destination block. If start is null, move the first child of the block. + * If end is null, move the last child of the block. + */ + private void moveSiblingExclusive( + Node src, Node dest, @Nullable Node start, @Nullable Node end) { + while (childAfter(src, start) != end) { + Node child = removeChildAfter(src, start); + dest.addChildToBack(child); + } + } + + /** + * Like Node.getNext, that null is used to signal the child before the + * block. + */ + private Node childAfter(Node parent, @Nullable Node siblingBefore) { + if (siblingBefore == null) { + return parent.getFirstChild(); + } else { + return siblingBefore.getNext(); + } + } + + /** + * Like removeChildAfter, the firstChild is removed + */ + private Node removeChildAfter(Node parent, @Nullable Node siblingBefore) { + if (siblingBefore == null) { + return parent.removeFirstChild(); + } else { + return parent.removeChildAfter(siblingBefore); + } + } + + private class Callback extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isCall() || !n.getFirstChild().isName()) { + return; + } + + Node callTarget = n.getFirstChild(); + String callName = callTarget.getString(); + + if (startMarkerName.equals(callName)) { + if (!parent.isExprResult()) { + compiler.report( + t.makeError(n, INVALID_MARKER_USAGE, startMarkerName)); + return; + } + markerStack.push(parent); + return; + } + + if (!endMarkerName.equals(callName)) { + return; + } + + Node endMarkerNode = parent; + if (!endMarkerNode.isExprResult()) { + compiler.report( + t.makeError(n, INVALID_MARKER_USAGE, endMarkerName)); + return; + } + + if (markerStack.isEmpty()) { + compiler.report(t.makeError(n, UNMATCHED_END_MARKER, + startMarkerName, endMarkerName)); + return; + } + + Node startMarkerNode = markerStack.pop(); + if (endMarkerNode.getParent() != startMarkerNode.getParent()) { + // The end marker isn't in the same block as the start marker. + compiler.report(t.makeError(n, UNMATCHED_END_MARKER, + startMarkerName, endMarkerName)); + return; + } + + // This is a valid marker set add it to the list of markers to process. + validMarkers.add(new Marker(startMarkerNode, endMarkerNode)); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CrossModuleCodeMotion.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CrossModuleCodeMotion.java new file mode 100755 index 0000000..7412a61 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CrossModuleCodeMotion.java @@ -0,0 +1,427 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.CodingConvention.SubclassRelationship; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +/** + * A {@link Compiler} pass for moving code to a deeper module if possible. + * - currently it only moves functions + variables + * + */ +class CrossModuleCodeMotion extends AbstractPostOrderCallback + implements CompilerPass { + + private static final Logger logger = + Logger.getLogger(CrossModuleCodeMotion.class.getName()); + + private final AbstractCompiler compiler; + private final JSModuleGraph graph; + + /** + * Map from module to the node in that module that should parent any string + * variable declarations that have to be moved into that module + */ + private final Map moduleVarParentMap = + new HashMap(); + + /* + * NOTE - I made this a LinkedHashMap to make testing easier. With a regular + * HashMap, the variables may not output in a consistent order + */ + private final Map namedInfo = + new LinkedHashMap(); + + /** + * Creates an instance. + * + * @param compiler The compiler + */ + CrossModuleCodeMotion(AbstractCompiler compiler, JSModuleGraph graph) { + this.compiler = compiler; + this.graph = graph; + } + + @Override + public void process(Node externs, Node root) { + logger.fine("Moving functions + variable into deeper modules"); + + // If there are <2 modules, then we will never move anything, so we're done + if (graph != null && graph.getModuleCount() > 1) { + + // Traverse the tree and find the modules where a var is declared + used + NodeTraversal.traverse(compiler, root, this); + + // Move the functions + variables to a deeper module [if possible] + moveCode(); + } + } + + /** move the code accordingly */ + private void moveCode() { + for (NamedInfo info : namedInfo.values()) { + JSModule deepestDependency = info.deepestModule; + + // Only move if all are true: + // a) allowMove is true + // b) it was used + declared somewhere [if not, then it will be removed + // as dead or invalid code elsewhere] + // c) the new dependency depends on the declModule + if (info.allowMove && deepestDependency != null) { + Iterator it = info.declarationIterator(); + JSModuleGraph moduleGraph = compiler.getModuleGraph(); + while (it.hasNext()) { + Declaration decl = it.next(); + if (decl.module != null && + moduleGraph.dependsOn(deepestDependency, + decl.module)) { + + // Find the appropriate spot to move it to + Node destParent = moduleVarParentMap.get(deepestDependency); + if (destParent == null) { + destParent = compiler.getNodeForCodeInsertion(deepestDependency); + moduleVarParentMap.put(deepestDependency, destParent); + } + + // VAR Nodes are normalized to have only one child. + Node declParent = decl.node.getParent(); + Preconditions.checkState( + !declParent.isVar() || declParent.hasOneChild(), + "AST not normalized."); + + // Remove it + declParent.detachFromParent(); + + // Add it to the new spot + destParent.addChildToFront(declParent); + + compiler.reportCodeChange(); + } + } + } + } + } + + /** useful information for each variable candidate */ + private class NamedInfo { + boolean allowMove = true; + + // The deepest module where the variable is used. Starts at null + private JSModule deepestModule = null; + + // The module where declarations appear + private JSModule declModule = null; + + // information on the spot where the item was declared + private final Deque declarations = + new ArrayDeque(); + + // Add a Module where it is used + void addUsedModule(JSModule m) { + // If we are not allowed to move it, all bets are off + if (!allowMove) { + return; + } + + // If we have no deepest module yet, set this one + if (deepestModule == null) { + deepestModule = m; + } else { + // Find the deepest common dependency + deepestModule = + graph.getDeepestCommonDependencyInclusive(m, deepestModule); + } + } + + /** + * Add a declaration for this name. + * @return Whether this is a valid declaration. If this returns false, + * this should be added as a reference. + */ + boolean addDeclaration(Declaration d) { + // all declarations must appear in the same module. + if (declModule != null && d.module != declModule) { + return false; + } + declarations.push(d); + declModule = d.module; + return true; + } + + /** + * Returns an iterator over the declarations, in the order that they were + * declared. + */ + Iterator declarationIterator() { + return declarations.iterator(); + } + } + + private class Declaration { + final JSModule module; + final Node node; + + Declaration(JSModule module, Node node, Node parent, Node gramps) { + this.module = module; + this.node = node; + } + } + + /** + * return true if the node has any form of conditional in its ancestry + * TODO(nicksantos) keep track of the conditionals in the ancestry, so + * that we don't have to recrawl it. + */ + private boolean hasConditionalAncestor(Node n) { + for (Node ancestor : n.getAncestors()) { + switch (ancestor.getType()) { + case Token.DO: + case Token.FOR: + case Token.HOOK: + case Token.IF: + case Token.SWITCH: + case Token.WHILE: + case Token.FUNCTION: + return true; + } + } + return false; + } + + /** + * get the information on a variable + */ + private NamedInfo getNamedInfo(Var v) { + NamedInfo info = namedInfo.get(v); + if (info == null) { + info = new NamedInfo(); + namedInfo.put(v, info); + } + return info; + } + + /** + * Process the references to named variables + */ + private void processReference(NodeTraversal t, NamedInfo info, String name) { + // A name is recursively defined if: + // 1: It is calling itself. + // 2: One of its property calls itself. + // Recursive definition should not block movement. + + boolean recursive = false; + Node rootNode = t.getScope().getRootNode(); + if (rootNode.isFunction()) { + + // CASE #1: + String scopeFuncName = rootNode.getFirstChild().getString(); + Node scopeFuncParent = rootNode.getParent(); + if (scopeFuncName.equals(name)) { + recursive = true; + } else if (scopeFuncParent.isName() && + scopeFuncParent.getString().equals(name)) { + recursive = true; + } else { + // CASE #2: + // Suppose name is Foo, we keep look up the scope stack to look for + // a scope with "Foo.prototype.bar = function() { ..... " + for (Scope s = t.getScope(); + s.getParent() != null; s = s.getParent()) { + Node curRoot = s.getRootNode(); + if (curRoot.getParent().isAssign()) { + Node owner = curRoot.getParent().getFirstChild(); + while (owner.isGetProp()) { + owner = owner.getFirstChild(); + } + if (owner.isName() && + owner.getString().equals(name)) { + recursive = true; + break; + } + } + } + } + } + + if (!recursive) { + info.addUsedModule(t.getModule()); + } + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isName()) { + return; + } + + // Skip empty and exported names + String name = n.getString(); + if (name.isEmpty() || compiler.getCodingConvention().isExported(name)) { + return; + } + + // If the JSCompiler can't find a Var for this string, then all + // bets are off. This sometimes occurs with closures. Alternately, we skip + // non-global variables + Var v = t.getScope().getVar(name); + if (v == null || !v.isGlobal()) { + return; + } + + NamedInfo info = getNamedInfo(v); + if (info.allowMove) { + if (maybeProcessDeclaration(t, n, parent, info)) { + // Check to see if the declaration is conditional starting at the + // grandparent of the name node. Since a function declaration + // is considered conditional (the function might not be called) + // we would need to skip the parent in this check as the name could + // just be a function itself. + if (hasConditionalAncestor(parent.getParent())) { + info.allowMove = false; + } + } else { + // Otherwise, it's a reference + processReference(t, info, name); + } + } + } + + /** + * Determines whether the given NAME node belongs to a declaration that + * can be moved across modules. If it is, registers it properly. + * + * There are four types of movable declarations: + * 1) var NAME = [movable object]; + * 2) function NAME() {} + * 3) NAME = [movable object]; + * NAME.prop = [movable object]; + * NAME.prop.prop2 = [movable object]; + * etc. + * 4) Class-defining function calls, like "inherits" and "mixin". + * NAME.inherits([some other name]); + * where "movable object" is a literal or a function. + */ + private boolean maybeProcessDeclaration(NodeTraversal t, Node name, + Node parent, NamedInfo info) { + Node gramps = parent.getParent(); + switch (parent.getType()) { + case Token.VAR: + if (canMoveValue(name.getFirstChild())) { + return info.addDeclaration( + new Declaration(t.getModule(), name, parent, gramps)); + } + return false; + + case Token.FUNCTION: + if (NodeUtil.isFunctionDeclaration(parent)) { + return info.addDeclaration( + new Declaration(t.getModule(), name, parent, gramps)); + } + return false; + + case Token.ASSIGN: + case Token.GETPROP: + Node child = name; + + // Look for assignment expressions where the name is the root + // of a qualified name on the left hand side of the assignment. + for (Node current : name.getAncestors()) { + if (current.isGetProp()) { + // fallthrough + } else if (current.isAssign() && + current.getFirstChild() == child) { + Node currentParent = current.getParent(); + if (currentParent.isExprResult() && + canMoveValue(current.getLastChild())) { + return info.addDeclaration( + new Declaration(t.getModule(), current, currentParent, + currentParent.getParent())); + } + } else { + return false; + } + + child = current; + } + return false; + + case Token.CALL: + if (NodeUtil.isExprCall(gramps)) { + SubclassRelationship relationship = + compiler.getCodingConvention().getClassesDefinedByCall(parent); + if (relationship != null && + name.getString().equals(relationship.subclassName)) { + return info.addDeclaration( + new Declaration(t.getModule(), parent, gramps, + gramps.getParent())); + } + } + return false; + + default: + return false; + } + } + + /** + * Determines whether the given value is eligible to be moved across modules. + */ + private boolean canMoveValue(Node n) { + // the value is only movable if it's + // a) nothing, + // b) a constant literal, + // c) a function, or + // d) an array/object literal of movable values. + // e) a function stub generated by CrossModuleMethodMotion. + if (n == null || NodeUtil.isLiteralValue(n, true) || + n.isFunction()) { + return true; + } else if (n.isCall()) { + Node functionName = n.getFirstChild(); + return functionName.isName() && + (functionName.getString().equals( + CrossModuleMethodMotion.STUB_METHOD_NAME) || + functionName.getString().equals( + CrossModuleMethodMotion.UNSTUB_METHOD_NAME)); + } else if (n.isArrayLit() || n.isObjectLit()) { + boolean isObjectLit = n.isObjectLit(); + for (Node child = n.getFirstChild(); child != null; + child = child.getNext()) { + if (!canMoveValue(isObjectLit ? child.getFirstChild() : child)) { + return false; + } + } + + return true; + } + + return false; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CrossModuleMethodMotion.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CrossModuleMethodMotion.java new file mode 100644 index 0000000..810f309 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CrossModuleMethodMotion.java @@ -0,0 +1,220 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.AnalyzePrototypeProperties.NameInfo; +import com.google.javascript.jscomp.AnalyzePrototypeProperties.Property; +import com.google.javascript.jscomp.AnalyzePrototypeProperties.Symbol; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; + +/** + * Move prototype methods into later modules. + * + * @author nicksantos@google.com (Nick Santos) + */ +class CrossModuleMethodMotion implements CompilerPass { + + // Internal errors + static final DiagnosticType NULL_COMMON_MODULE_ERROR = DiagnosticType.error( + "JSC_INTERNAL_ERROR_MODULE_DEPEND", + "null deepest common module"); + + private final AbstractCompiler compiler; + private final IdGenerator idGenerator; + private final AnalyzePrototypeProperties analyzer; + private final JSModuleGraph moduleGraph; + + static final String STUB_METHOD_NAME = "JSCompiler_stubMethod"; + static final String UNSTUB_METHOD_NAME = "JSCompiler_unstubMethod"; + + // Visible for testing + static final String STUB_DECLARATIONS = + "var JSCompiler_stubMap = [];" + + "function JSCompiler_stubMethod(JSCompiler_stubMethod_id) {" + + " return function() {" + + " return JSCompiler_stubMap[JSCompiler_stubMethod_id].apply(" + + " this, arguments);" + + " };" + + "}" + + "function JSCompiler_unstubMethod(" + + " JSCompiler_unstubMethod_id, JSCompiler_unstubMethod_body) {" + + " return JSCompiler_stubMap[JSCompiler_unstubMethod_id] = " + + " JSCompiler_unstubMethod_body;" + + "}"; + + /** + * Creates a new pass for moving prototype properties. + * @param compiler The compiler. + * @param idGenerator An id generator for method stubs. + * @param canModifyExterns If true, then we can move prototype + * properties that are declared in the externs file. + */ + CrossModuleMethodMotion(AbstractCompiler compiler, IdGenerator idGenerator, + boolean canModifyExterns) { + this.compiler = compiler; + this.idGenerator = idGenerator; + this.moduleGraph = compiler.getModuleGraph(); + this.analyzer = new AnalyzePrototypeProperties(compiler, moduleGraph, + canModifyExterns, false); + } + + @Override + public void process(Node externRoot, Node root) { + // If there are < 2 modules, then we will never move anything, + // so we're done. + if (moduleGraph != null && moduleGraph.getModuleCount() > 1) { + analyzer.process(externRoot, root); + moveMethods(analyzer.getAllNameInfo()); + } + } + + /** + * Move methods deeper in the module graph when possible. + */ + private void moveMethods(Collection allNameInfo) { + boolean hasStubDeclaration = idGenerator.hasGeneratedAnyIds(); + for (NameInfo nameInfo : allNameInfo) { + if (!nameInfo.isReferenced()) { + // The code below can't do anything with unreferenced name + // infos. They should be skipped to avoid NPE since their + // deepestCommonModuleRef is null. + continue; + } + + if (nameInfo.readsClosureVariables()) { + continue; + } + + JSModule deepestCommonModuleRef = nameInfo.getDeepestCommonModuleRef(); + if (deepestCommonModuleRef == null) { + compiler.report(JSError.make(NULL_COMMON_MODULE_ERROR)); + continue; + } + + Iterator declarations = + nameInfo.getDeclarations().descendingIterator(); + while (declarations.hasNext()) { + Symbol symbol = declarations.next(); + if (!(symbol instanceof Property)) { + continue; + } + Property prop = (Property) symbol; + + // We should only move a property across modules if: + // 1) We can move it deeper in the module graph, and + // 2) it's a function, and + // 3) it is not a GETTER_DEF or a SETTER_DEF, and + // 4) the class is available in the global scope. + // + // #1 should be obvious. #2 is more subtle. It's possible + // to copy off of a prototype, as in the code: + // for (var k in Foo.prototype) { + // doSomethingWith(Foo.prototype[k]); + // } + // This is a common way to implement pseudo-multiple inheritance in JS. + // + // So if we move a prototype method into a deeper module, we must + // replace it with a stub function so that it preserves its original + // behavior. + if (prop.getRootVar() == null || !prop.getRootVar().isGlobal()) { + continue; + } + + Node value = prop.getValue(); + if (moduleGraph.dependsOn(deepestCommonModuleRef, prop.getModule()) && + value.isFunction()) { + Node valueParent = value.getParent(); + if (valueParent.isGetterDef() + || valueParent.isSetterDef()) { + // TODO(johnlenz): a GET or SET can't be deferred like a normal + // FUNCTION property definition as a mix-in would get the result + // of a GET instead of the function itself. + continue; + } + Node proto = prop.getPrototype(); + int stubId = idGenerator.newId(); + + // example: JSCompiler_stubMethod(id); + Node stubCall = IR.call( + IR.name(STUB_METHOD_NAME), + IR.number(stubId)) + .copyInformationFromForTree(value); + stubCall.putBooleanProp(Node.FREE_CALL, true); + + // stub out the method in the original module + // A.prototype.b = JSCompiler_stubMethod(id); + valueParent.replaceChild(value, stubCall); + + // unstub the function body in the deeper module + Node unstubParent = compiler.getNodeForCodeInsertion( + deepestCommonModuleRef); + Node unstubCall = IR.call( + IR.name(UNSTUB_METHOD_NAME), + IR.number(stubId), + value); + unstubCall.putBooleanProp(Node.FREE_CALL, true); + unstubParent.addChildToFront( + // A.prototype.b = JSCompiler_unstubMethod(id, body); + IR.exprResult( + IR.assign( + IR.getprop( + proto.cloneTree(), + IR.string(nameInfo.name)), + unstubCall)) + .copyInformationFromForTree(value)); + + compiler.reportCodeChange(); + } + } + } + + if (!hasStubDeclaration && idGenerator.hasGeneratedAnyIds()) { + // Declare stub functions in the top-most module. + Node declarations = compiler.parseSyntheticCode(STUB_DECLARATIONS); + compiler.getNodeForCodeInsertion(null).addChildrenToFront( + declarations.removeChildren()); + } + } + + static class IdGenerator implements Serializable { + private static final long serialVersionUID = 0L; + + /** + * Ids for cross-module method stubbing, so that each method has + * a unique id. + */ + private int currentId = 0; + + /** + * Returns whether we've generated any new ids. + */ + boolean hasGeneratedAnyIds() { + return currentId != 0; + } + + /** + * Creates a new id for stubbing a method. + */ + int newId() { + return currentId++; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CssRenamingMap.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CssRenamingMap.java new file mode 100644 index 0000000..64833c3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CssRenamingMap.java @@ -0,0 +1,51 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Interface used by {@link ReplaceCssNames} to substitute CSS class names. + */ +public interface CssRenamingMap { + public static enum Style { + BY_WHOLE, + BY_PART, + } + + String get(String value); + + Style getStyle(); + + public static abstract class ByPart implements CssRenamingMap { + @Override + abstract public String get(String value); + + @Override + public Style getStyle() { + return Style.BY_PART; + } + } + + public static abstract class ByWhole implements CssRenamingMap { + @Override + abstract public String get(String value); + + @Override + public Style getStyle() { + return Style.BY_WHOLE; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CustomPassExecutionTime.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CustomPassExecutionTime.java new file mode 100644 index 0000000..ac3642a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/CustomPassExecutionTime.java @@ -0,0 +1,28 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Custom pass type. Controls the compilation stage at which each + * custom pass executes. + */ +public enum CustomPassExecutionTime { + BEFORE_CHECKS, + BEFORE_OPTIMIZATIONS, + BEFORE_OPTIMIZATION_LOOP, + AFTER_OPTIMIZATION_LOOP, +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DataFlowAnalysis.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DataFlowAnalysis.java new file mode 100644 index 0000000..5b39646 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DataFlowAnalysis.java @@ -0,0 +1,579 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.graph.Annotation; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; +import com.google.javascript.jscomp.graph.LatticeElement; +import com.google.javascript.rhino.Node; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * A framework to help writing static program analysis. A subclass of + * this framework should specify how a single node changes the state + * of a program. This class finds a safe estimate (a fixed-point) for + * the whole program. The proven facts about the program will be + * annotated with + * {@link com.google.javascript.jscomp.graph.GraphNode#setAnnotation} to the + * given control flow graph's nodes in form of {@link LatticeElement} + * after calling {@link #analyze()}. + * + *

    As a guideline, the following is a list of behaviors that any analysis + * can take: + *

      + *
    1. Flow Direction: Is the analysis a forward or backward analysis? + *
    2. Lattice Elements: How does the analysis represent the state of the + * program at any given point? + *
    3. JOIN Operation: Given two incoming paths and a lattice state value, what + * can the compiler conclude at the join point? + *
    4. Flow Equations: How does an instruction modify the state of program in + * terms of lattice values? + *
    5. Initial Entry Value: What can the compiler assume at the beginning of the + * program? + *
    6. Initial Estimate: What can the compiler assume at each point of the + * program? (What is the BOTTOM value of the lattice) By definition this lattice + * JOIN {@code x} for any {@code x} must also be {@code x}. + *
    + * To make these behaviors known to the framework, the following steps must be + * taken. + *
      + *
    1. Flow Direction: Implement {@link #isForward()}. + *
    2. Lattice Elements: Implement {@link LatticeElement}. + *
    3. JOIN Operation: Implement + * {@link JoinOp#apply}. + *
    4. Flow Equations: Implement + * {@link #flowThrough(Object, LatticeElement)}. + *
    5. Initial Entry Value: Implement {@link #createEntryLattice()}. + *
    6. Initial Estimate: Implement {@link #createInitialEstimateLattice()}. + *
    + * + *

    Upon execution of the {@link #analyze()} method, nodes of the input + * control flow graph will be annotated with a {@link FlowState} object that + * represents maximum fixed point solution. Any previous annotations at the + * nodes of the control flow graph will be lost. + * + * + * @param The control flow graph's node value type. + * @param Lattice element type. + */ +abstract class DataFlowAnalysis { + + private final ControlFlowGraph cfg; + final JoinOp joinOp; + protected final Set> orderedWorkSet; + + /* + * Feel free to increase this to a reasonable number if you are finding that + * more and more passes need more than 200000 steps before finding a + * fixed-point. If you just have a special case, consider calling + * {@link #analyse(int)} instead. + */ + public static final int MAX_STEPS = 200000; + + /** + * Constructs a data flow analysis. + * + *

    Typical usage + *

    +   * DataFlowAnalysis dfa = ...
    +   * dfa.analyze();
    +   * 
    + * + * {@link #analyze()} annotates the result to the control flow graph by + * means of {@link DiGraphNode#setAnnotation} without any + * modification of the graph itself. Additional calls to {@link #analyze()} + * recomputes the analysis which can be useful if the control flow graph + * has been modified. + * + * @param targetCfg The control flow graph object that this object performs + * on. Modification of the graph requires a separate call to + * {@link #analyze()}. + * + * @see #analyze() + */ + DataFlowAnalysis(ControlFlowGraph targetCfg, JoinOp joinOp) { + this.cfg = targetCfg; + this.joinOp = joinOp; + Comparator> nodeComparator = + cfg.getOptionalNodeComparator(isForward()); + if (nodeComparator != null) { + this.orderedWorkSet = Sets.newTreeSet(nodeComparator); + } else { + this.orderedWorkSet = Sets.newLinkedHashSet(); + } + } + + /** + * Returns the control flow graph that this analysis was performed on. + * Modifications can be done on this graph, however, the only time that the + * annotations are correct is after {@link #analyze()} is called and before + * the graph has been modified. + */ + final ControlFlowGraph getCfg() { + return cfg; + } + + /** + * Returns the lattice element at the exit point. + */ + L getExitLatticeElement() { + DiGraphNode node = getCfg().getImplicitReturn(); + FlowState state = node.getAnnotation(); + return state.getIn(); + } + + @SuppressWarnings("unchecked") + protected L join(L latticeA, L latticeB) { + return joinOp.apply(Lists.newArrayList(latticeA, latticeB)); + } + + /** + * Checks whether the analysis is a forward flow analysis or backward flow + * analysis. + * + * @return {@code true} if it is a forward analysis. + */ + abstract boolean isForward(); + + /** + * Computes the output state for a given node and input state. + * + * @param node The node. + * @param input Input lattice that should be read-only. + * @return Output lattice. + */ + abstract L flowThrough(N node, L input); + + /** + * Finds a fixed-point solution using at most {@link #MAX_STEPS} + * iterations. + * + * @see #analyze(int) + */ + final void analyze() { + analyze(MAX_STEPS); + } + + /** + * Finds a fixed-point solution. The function has the side effect of replacing + * the existing node annotations with the computed solutions using {@link + * com.google.javascript.jscomp.graph.GraphNode#setAnnotation(Annotation)}. + * + *

    Initially, each node's input and output flow state contains the value + * given by {@link #createInitialEstimateLattice()} (with the exception of the + * entry node of the graph which takes on the {@link #createEntryLattice()} + * value. Each node will use the output state of its predecessor and compute a + * output state according to the instruction. At that time, any nodes that + * depends on the node's newly modified output value will need to recompute + * their output state again. Each step will perform a computation at one node + * until no extra computation will modify any existing output state anymore. + * + * @param maxSteps Max number of iterations before the method stops and throw + * a {@link MaxIterationsExceededException}. This will prevent the + * analysis from going into a infinite loop. + */ + final void analyze(int maxSteps) { + initialize(); + int step = 0; + while (!orderedWorkSet.isEmpty()) { + if (step > maxSteps) { + throw new MaxIterationsExceededException( + "Analysis did not terminate after " + maxSteps + " iterations"); + } + DiGraphNode curNode = orderedWorkSet.iterator().next(); + orderedWorkSet.remove(curNode); + joinInputs(curNode); + if (flow(curNode)) { + // If there is a change in the current node, we want to grab the list + // of nodes that this node affects. + List> nextNodes = isForward() ? + cfg.getDirectedSuccNodes(curNode) : + cfg.getDirectedPredNodes(curNode); + for (DiGraphNode nextNode : nextNodes) { + if (nextNode != cfg.getImplicitReturn()) { + orderedWorkSet.add(nextNode); + } + } + } + step++; + } + if (isForward()) { + joinInputs(getCfg().getImplicitReturn()); + } + } + + /** + * Gets the state of the initial estimation at each node. + * + * @return Initial state. + */ + abstract L createInitialEstimateLattice(); + + /** + * Gets the incoming state of the entry node. + * + * @return Entry state. + */ + abstract L createEntryLattice(); + + /** + * Initializes the work list and the control flow graph. + */ + protected void initialize() { + // TODO(user): Calling clear doesn't deallocate the memory in a + // LinkedHashSet. Consider creating a new work set if we plan to repeatedly + // call analyze. + orderedWorkSet.clear(); + for (DiGraphNode node : cfg.getDirectedGraphNodes()) { + node.setAnnotation(new FlowState(createInitialEstimateLattice(), + createInitialEstimateLattice())); + if (node != cfg.getImplicitReturn()) { + orderedWorkSet.add(node); + } + } + } + + /** + * Performs a single flow through a node. + * + * @return {@code true} if the flow state differs from the previous state. + */ + protected boolean flow(DiGraphNode node) { + FlowState state = node.getAnnotation(); + if (isForward()) { + L outBefore = state.out; + state.out = flowThrough(node.getValue(), state.in); + return !outBefore.equals(state.out); + } else { + L inBefore = state.in; + state.in = flowThrough(node.getValue(), state.out); + return !inBefore.equals(state.in); + } + } + + /** + * Computes the new flow state at a given node's entry by merging the + * output (input) lattice of the node's predecessor (successor). + * + * @param node Node to compute new join. + */ + protected void joinInputs(DiGraphNode node) { + FlowState state = node.getAnnotation(); + if (isForward()) { + if (cfg.getEntry() == node) { + state.setIn(createEntryLattice()); + } else { + List> inNodes = cfg.getDirectedPredNodes(node); + if (inNodes.size() == 1) { + FlowState inNodeState = inNodes.get(0).getAnnotation(); + state.setIn(inNodeState.getOut()); + } else if (inNodes.size() > 1) { + List values = new ArrayList(inNodes.size()); + for (DiGraphNode currentNode : inNodes) { + FlowState currentNodeState = currentNode.getAnnotation(); + values.add(currentNodeState.getOut()); + } + state.setIn(joinOp.apply(values)); + } + } + } else { + List> inNodes = cfg.getDirectedSuccNodes(node); + if (inNodes.size() == 1) { + DiGraphNode inNode = inNodes.get(0); + if (inNode == cfg.getImplicitReturn()) { + state.setOut(createEntryLattice()); + } else { + FlowState inNodeState = inNode.getAnnotation(); + state.setOut(inNodeState.getIn()); + } + } else if (inNodes.size() > 1) { + List values = new ArrayList(inNodes.size()); + for (DiGraphNode currentNode : inNodes) { + FlowState currentNodeState = currentNode.getAnnotation(); + values.add(currentNodeState.getIn()); + } + state.setOut(joinOp.apply(values)); + } + } + } + + /** + * The in and out states of a node. + * + * @param Input and output lattice element type. + */ + static class FlowState implements Annotation { + private L in; + private L out; + + /** + * Private constructor. No other classes should create new states. + * + * @param inState Input. + * @param outState Output. + */ + private FlowState(L inState, L outState) { + Preconditions.checkNotNull(inState); + Preconditions.checkNotNull(outState); + this.in = inState; + this.out = outState; + } + + L getIn() { + return in; + } + + void setIn(L in) { + Preconditions.checkNotNull(in); + this.in = in; + } + + L getOut() { + return out; + } + + void setOut(L out) { + Preconditions.checkNotNull(out); + this.out = out; + } + + @Override + public String toString() { + return String.format("IN: %s OUT: %s", in, out); + } + + @Override + public int hashCode() { + return Objects.hashCode(in, out); + } + } + + /** + * The exception to be thrown if the analysis has been running for a long + * number of iterations. Chances are the analysis is not monotonic, a + * fixed-point cannot be found and it is currently stuck in an infinite loop. + */ + static class MaxIterationsExceededException extends RuntimeException { + private static final long serialVersionUID = 1L; + MaxIterationsExceededException(String msg) { + super(msg); + } + } + + abstract static class BranchedForwardDataFlowAnalysis + extends DataFlowAnalysis { + + @Override + protected void initialize() { + orderedWorkSet.clear(); + for (DiGraphNode node : getCfg().getDirectedGraphNodes()) { + int outEdgeCount = getCfg().getOutEdges(node.getValue()).size(); + List outLattices = Lists.newArrayList(); + for (int i = 0; i < outEdgeCount; i++) { + outLattices.add(createInitialEstimateLattice()); + } + node.setAnnotation(new BranchedFlowState( + createInitialEstimateLattice(), outLattices)); + if (node != getCfg().getImplicitReturn()) { + orderedWorkSet.add(node); + } + } + } + + BranchedForwardDataFlowAnalysis(ControlFlowGraph targetCfg, + JoinOp joinOp) { + super(targetCfg, joinOp); + } + + /** + * Returns the lattice element at the exit point. Needs to be overridden + * because we use a BranchedFlowState instead of a FlowState; ugh. + */ + @Override + L getExitLatticeElement() { + DiGraphNode node = getCfg().getImplicitReturn(); + BranchedFlowState state = node.getAnnotation(); + return state.getIn(); + } + + @Override + final boolean isForward() { + return true; + } + + /** + * The branched flow function maps a single lattice to a list of output + * lattices. + * + *

    Each outgoing edge of a node will have a corresponding output lattice + * in the ordered returned by + * {@link com.google.javascript.jscomp.graph.DiGraph#getOutEdges(Object)} + * in the returned list. + * + * @return A list of output values depending on the edge's branch type. + */ + abstract List branchedFlowThrough(N node, L input); + + @Override + protected final boolean flow(DiGraphNode node) { + BranchedFlowState state = node.getAnnotation(); + List outBefore = state.out; + state.out = branchedFlowThrough(node.getValue(), state.in); + Preconditions.checkState(outBefore.size() == state.out.size()); + for (int i = 0; i < outBefore.size(); i++) { + if (!outBefore.get(i).equals(state.out.get(i))) { + return true; + } + } + return false; + } + + @Override + protected void joinInputs(DiGraphNode node) { + BranchedFlowState state = node.getAnnotation(); + List> predNodes = + getCfg().getDirectedPredNodes(node); + List values = new ArrayList(predNodes.size()); + + for (DiGraphNode predNode : predNodes) { + BranchedFlowState predNodeState = predNode.getAnnotation(); + + L in = predNodeState.out.get( + getCfg().getDirectedSuccNodes(predNode).indexOf(node)); + + values.add(in); + } + if (getCfg().getEntry() == node) { + state.setIn(createEntryLattice()); + } else if (!values.isEmpty()) { + state.setIn(joinOp.apply(values)); + } + } + } + + /** + * The in and out states of a node. + * + * @param Input and output lattice element type. + */ + static class BranchedFlowState + implements Annotation { + private L in; + private List out; + + /** + * Private constructor. No other classes should create new states. + * + * @param inState Input. + * @param outState Output. + */ + private BranchedFlowState(L inState, List outState) { + Preconditions.checkNotNull(inState); + Preconditions.checkNotNull(outState); + this.in = inState; + this.out = outState; + } + + L getIn() { + return in; + } + + void setIn(L in) { + Preconditions.checkNotNull(in); + this.in = in; + } + + List getOut() { + return out; + } + + void setOut(List out) { + Preconditions.checkNotNull(out); + for (L item : out) { + Preconditions.checkNotNull(item); + } + this.out = out; + } + + @Override + public String toString() { + return String.format("IN: %s OUT: %s", in, out); + } + + @Override + public int hashCode() { + return Objects.hashCode(in, out); + } + } + + /** + * Compute set of escaped variables. When a variable is escaped in a + * dataflow analysis, it can be reference outside of the code that we are + * analyzing. A variable is escaped if any of the following is true: + * + *

      + *
    1. It is defined as the exception name in CATCH clause so it became a + * variable local not to our definition of scope.
    2. + *
    3. Exported variables as they can be needed after the script terminates. + *
    4. + *
    5. Names of named functions because in JavaScript, function foo(){} + * does not kill foo in the dataflow.
    6. + */ + static void computeEscaped(final Scope jsScope, final Set escaped, + AbstractCompiler compiler) { + // TODO(user): Very good place to store this information somewhere. + AbstractPostOrderCallback finder = new AbstractPostOrderCallback() { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (jsScope == t.getScope() || !n.isName() + || parent.isFunction()) { + return; + } + String name = n.getString(); + Var var = t.getScope().getVar(name); + if (var != null && var.scope == jsScope) { + escaped.add(jsScope.getVar(name)); + } + } + }; + + NodeTraversal t = new NodeTraversal(compiler, finder); + t.traverseAtScope(jsScope); + + // 1: Remove the exception name in CATCH which technically isn't local to + // begin with. + for (Iterator i = jsScope.getVars(); i.hasNext();) { + Var var = i.next(); + if (var.getParentNode().isCatch() || + compiler.getCodingConvention().isExported(var.getName())) { + escaped.add(var); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DeadAssignmentsElimination.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DeadAssignmentsElimination.java new file mode 100644 index 0000000..9d1b56c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DeadAssignmentsElimination.java @@ -0,0 +1,438 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.DataFlowAnalysis.FlowState; +import com.google.javascript.jscomp.LiveVariablesAnalysis.LiveVariableLattice; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * Removes local variable assignments that are useless based on information from + * {@link LiveVariablesAnalysis}. If there is an assignment to variable + * {@code x} and {@code x} is dead after this assignment, we know that the + * current content of {@code x} will not be read and this assignment is useless. + * + */ +class DeadAssignmentsElimination extends AbstractPostOrderCallback implements + CompilerPass, ScopedCallback { + + private final AbstractCompiler compiler; + private LiveVariablesAnalysis liveness; + + // Matches all assignment operators and increment/decrement operators. + // Does *not* match VAR initialization, since RemoveUnusedVariables + // will already remove variables that are initialized but unused. + private static final Predicate matchRemovableAssigns = + new Predicate() { + @Override + public boolean apply(Node n) { + return (NodeUtil.isAssignmentOp(n) && + n.getFirstChild().isName()) || + n.isInc() || n.isDec(); + } + }; + + public DeadAssignmentsElimination(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + Preconditions.checkNotNull(externs); + Preconditions.checkNotNull(root); + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void enterScope(NodeTraversal t) { + Scope scope = t.getScope(); + // Global scope _SHOULD_ work, however, liveness won't finish without + // -Xmx1024 in closure. We might have to look at coding conventions for + // exported variables as well. + if (scope.isGlobal()) { + return; + } + + if (LiveVariablesAnalysis.MAX_VARIABLES_TO_ANALYZE < + t.getScope().getVarCount()) { + return; + } + + // We are not going to do any dead assignment elimination in when there is + // at least one inner function because in most browsers, when there is a + // closure, ALL the variables are saved (escaped). + Node fnBlock = t.getScopeRoot().getLastChild(); + if (NodeUtil.containsFunction(fnBlock)) { + return; + } + + // We don't do any dead assignment elimination if there are no assigns + // to eliminate. :) + if (!NodeUtil.has(fnBlock, matchRemovableAssigns, + Predicates.alwaysTrue())) { + return; + } + + // Computes liveness information first. + ControlFlowGraph cfg = t.getControlFlowGraph(); + liveness = new LiveVariablesAnalysis(cfg, scope, compiler); + liveness.analyze(); + tryRemoveDeadAssignments(t, cfg); + } + + @Override + public void exitScope(NodeTraversal t) { + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + } + + /** + * Try to remove useless assignments from a control flow graph that has been + * annotated with liveness information. + * + * @param t The node traversal. + * @param cfg The control flow graph of the program annotated with liveness + * information. + */ + private void tryRemoveDeadAssignments(NodeTraversal t, + ControlFlowGraph cfg) { + Iterable> nodes = cfg.getDirectedGraphNodes(); + + for (DiGraphNode cfgNode : nodes) { + FlowState state = + cfgNode.getAnnotation(); + Node n = cfgNode.getValue(); + if (n == null) { + continue; + } + switch (n.getType()) { + case Token.IF: + case Token.WHILE: + case Token.DO: + tryRemoveAssignment(t, NodeUtil.getConditionExpression(n), state); + continue; + case Token.FOR: + if (!NodeUtil.isForIn(n)) { + tryRemoveAssignment( + t, NodeUtil.getConditionExpression(n), state); + } + continue; + case Token.SWITCH: + case Token.CASE: + case Token.RETURN: + if (n.hasChildren()) { + tryRemoveAssignment(t, n.getFirstChild(), state); + } + continue; + // TODO(user): case Token.VAR: Remove var a=1;a=2;..... + } + + tryRemoveAssignment(t, n, state); + } + } + + private void tryRemoveAssignment(NodeTraversal t, Node n, + FlowState state) { + tryRemoveAssignment(t, n, n, state); + } + + /** + * Determines if any local variables are dead after the instruction {@code n} + * and are assigned within the subtree of {@code n}. Removes those assignments + * if there are any. + * + * @param n Target instruction. + * @param exprRoot The CFG node where the liveness information in state is + * still correct. + * @param state The liveness information at {@code n}. + */ + private void tryRemoveAssignment(NodeTraversal t, Node n, Node exprRoot, + FlowState state) { + + Node parent = n.getParent(); + + if (NodeUtil.isAssignmentOp(n) || + n.isInc() || n.isDec()) { + + Node lhs = n.getFirstChild(); + Node rhs = lhs.getNext(); + + // Recurse first. Example: dead_x = dead_y = 1; We try to clean up dead_y + // first. + if (rhs != null) { + tryRemoveAssignment(t, rhs, exprRoot, state); + rhs = lhs.getNext(); + } + + Scope scope = t.getScope(); + if (!lhs.isName()) { + return; // Not a local variable assignment. + } + String name = lhs.getString(); + if (!scope.isDeclared(name, false)) { + return; + } + Var var = scope.getVar(name); + + if (liveness.getEscapedLocals().contains(var)) { + return; // Local variable that might be escaped due to closures. + } + + // If we have an identity assignment such as a=a, always remove it + // regardless of what the liveness results because it + // does not change the result afterward. + if (rhs != null && + rhs.isName() && + rhs.getString().equals(var.name) && + n.isAssign()) { + n.removeChild(rhs); + n.getParent().replaceChild(n, rhs); + compiler.reportCodeChange(); + return; + } + + if (state.getOut().isLive(var)) { + return; // Variable not dead. + } + + if (state.getIn().isLive(var) && + isVariableStillLiveWithinExpression(n, exprRoot, var.name)) { + // The variable is killed here but it is also live before it. + // This is possible if we have say: + // if (X = a && a = C) {..} ; .......; a = S; + // In this case we are safe to remove "a = C" because it is dead. + // However if we have: + // if (a = C && X = a) {..} ; .......; a = S; + // removing "a = C" is NOT correct, although the live set at the node + // is exactly the same. + // TODO(user): We need more fine grain CFA or we need to keep track + // of GEN sets when we recurse here. + return; + } + + if (n.isAssign()) { + n.removeChild(rhs); + n.getParent().replaceChild(n, rhs); + } else if (NodeUtil.isAssignmentOp(n)) { + n.removeChild(rhs); + n.removeChild(lhs); + Node op = new Node(NodeUtil.getOpFromAssignmentOp(n), lhs, rhs); + parent.replaceChild(n, op); + } else if (n.isInc() || n.isDec()) { + if (parent.isExprResult()) { + parent.replaceChild(n, + IR.voidNode(IR.number(0).srcref(n))); + } else if(n.isComma() && n != parent.getLastChild()) { + parent.removeChild(n); + } else if (parent.isFor() && !NodeUtil.isForIn(parent) && + NodeUtil.getConditionExpression(parent) != n) { + parent.replaceChild(n, IR.empty()); + } else { + // Cannot replace x = a++ with x = a because that's not valid + // when a is not a number. + return; + } + } else { + // Not reachable. + Preconditions.checkState(false, "Unknown statement"); + } + + compiler.reportCodeChange(); + return; + + } else { + for (Node c = n.getFirstChild(); c != null;) { + Node next = c.getNext(); + if (!ControlFlowGraph.isEnteringNewCfgNode(c)) { + tryRemoveAssignment(t, c, exprRoot, state); + } + c = next; + } + return; + } + } + + /** + * Given a variable, node n in the tree and a sub-tree denoted by exprRoot as + * the root, this function returns true if there exists a read of that + * variable before a write to that variable that is on the right side of n. + * + * For example, suppose the node is x = 1: + * + * y = 1, x = 1; // false, there is no reads at all. + * y = 1, x = 1, print(x) // true, there is a read right of n. + * y = 1, x = 1, x = 2, print(x) // false, there is a read right of n but + * // it is after a write. + * + * @param n The current node we should look at. + * @param exprRoot The node + */ + private boolean isVariableStillLiveWithinExpression( + Node n, Node exprRoot, String variable) { + while (n != exprRoot) { + VariableLiveness state = VariableLiveness.MAYBE_LIVE; + switch (n.getParent().getType()) { + case Token.OR: + case Token.AND: + // If the currently node is the first child of + // AND/OR, be conservative only consider the READs + // of the second operand. + if (n.getNext() != null) { + state = isVariableReadBeforeKill( + n.getNext(), variable); + if (state == VariableLiveness.KILL) { + state = VariableLiveness.MAYBE_LIVE; + } + } + break; + + case Token.HOOK: + // If current node is the condition, check each following + // branch, otherwise it is a conditional branch and the + // other branch can be ignored. + if (n.getNext() != null && n.getNext().getNext() != null) { + state = checkHookBranchReadBeforeKill( + n.getNext(), n.getNext().getNext(), variable); + } + break; + + default: + for(Node sibling = n.getNext(); sibling != null; + sibling = sibling.getNext()) { + state = isVariableReadBeforeKill(sibling, variable); + if (state != VariableLiveness.MAYBE_LIVE) { + break; + } + } + } + + // If we see a READ or KILL there is no need to continue. + if (state == VariableLiveness.READ) { + return true; + } else if (state == VariableLiveness.KILL) { + return false; + } + n = n.getParent(); + } + return false; + } + + // The current liveness of the variable + private enum VariableLiveness { + MAYBE_LIVE, // May be still live in the current expression tree. + READ, // Known there is a read left of it. + KILL, // Known there is a write before any read. + } + + /** + * Give an expression and a variable. It returns READ, if the first + * reference of that variable is a read. It returns KILL, if the first + * reference of that variable is an assignment. It returns MAY_LIVE otherwise. + */ + private VariableLiveness isVariableReadBeforeKill( + Node n, String variable) { + if (ControlFlowGraph.isEnteringNewCfgNode(n)) { // Not a FUNCTION + return VariableLiveness.MAYBE_LIVE; + } + + if (n.isName() && variable.equals(n.getString())) { + if (NodeUtil.isVarOrSimpleAssignLhs(n, n.getParent())) { + Preconditions.checkState(n.getParent().isAssign()); + // The expression to which the assignment is made is evaluated before + // the RHS is evaluated (normal left to right evaluation) but the KILL + // occurs after the RHS is evaluated. + Node rhs = n.getNext(); + VariableLiveness state = isVariableReadBeforeKill(rhs, variable); + if (state == VariableLiveness.READ) { + return state; + } + return VariableLiveness.KILL; + } else { + return VariableLiveness.READ; + } + } + + switch (n.getType()) { + // Conditionals + case Token.OR: + case Token.AND: + VariableLiveness v1 = isVariableReadBeforeKill( + n.getFirstChild(), variable); + VariableLiveness v2 = isVariableReadBeforeKill( + n.getLastChild(), variable); + // With a AND/OR the first branch always runs, but the second is + // may not. + if (v1 != VariableLiveness.MAYBE_LIVE) { + return v1; + } else if (v2 == VariableLiveness.READ) { + return VariableLiveness.READ; + } else { + return VariableLiveness.MAYBE_LIVE; + } + case Token.HOOK: + VariableLiveness first = isVariableReadBeforeKill( + n.getFirstChild(), variable); + if (first != VariableLiveness.MAYBE_LIVE) { + return first; + } + return checkHookBranchReadBeforeKill( + n.getFirstChild().getNext(), n.getLastChild(), variable); + + default: + // Expressions are evaluated left-right, depth first. + for (Node child = n.getFirstChild(); + child != null; child = child.getNext()) { + VariableLiveness state = isVariableReadBeforeKill(child, variable); + if (state != VariableLiveness.MAYBE_LIVE) { + return state; + } + } + } + + return VariableLiveness.MAYBE_LIVE; + } + + private VariableLiveness checkHookBranchReadBeforeKill( + Node trueCase, Node falseCase, String variable) { + VariableLiveness v1 = isVariableReadBeforeKill( + trueCase, variable); + VariableLiveness v2 = isVariableReadBeforeKill( + falseCase, variable); + // With a hook it is unknown which branch will run, so + // we must be conservative. A read by either is a READ, and + // a KILL is only considered if both KILL. + if (v1 == VariableLiveness.READ || v2 == VariableLiveness.READ) { + return VariableLiveness.READ; + } else if (v1 == VariableLiveness.KILL && v2 == VariableLiveness.KILL) { + return VariableLiveness.KILL; + } else { + return VariableLiveness.MAYBE_LIVE; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DefaultPassConfig.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DefaultPassConfig.java new file mode 100644 index 0000000..a10c4dd --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DefaultPassConfig.java @@ -0,0 +1,2320 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.common.io.Files; +import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage; +import com.google.javascript.jscomp.CompilerOptions.LanguageMode; +import com.google.javascript.jscomp.ExtractPrototypeMemberDeclarations.Pattern; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.jscomp.parsing.ParserRunner; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Pass factories and meta-data for native JSCompiler passes. + * + * @author nicksantos@google.com (Nick Santos) + */ +// TODO(nicksantos): This needs state for a variety of reasons. Some of it +// is to satisfy the existing API. Some of it is because passes really do +// need to share state in non-trivial ways. This should be audited and +// cleaned up. +public class DefaultPassConfig extends PassConfig { + + /* For the --mark-as-compiled pass */ + private static final String COMPILED_CONSTANT_NAME = "COMPILED"; + + /* Constant name for Closure's locale */ + private static final String CLOSURE_LOCALE_CONSTANT_NAME = "goog.LOCALE"; + + // Compiler errors when invalid combinations of passes are run. + static final DiagnosticType TIGHTEN_TYPES_WITHOUT_TYPE_CHECK = + DiagnosticType.error("JSC_TIGHTEN_TYPES_WITHOUT_TYPE_CHECK", + "TightenTypes requires type checking. Please use --check_types."); + + static final DiagnosticType CANNOT_USE_PROTOTYPE_AND_VAR = + DiagnosticType.error("JSC_CANNOT_USE_PROTOTYPE_AND_VAR", + "Rename prototypes and inline variables cannot be used together"); + + // Miscellaneous errors. + static final DiagnosticType REPORT_PATH_IO_ERROR = + DiagnosticType.error("JSC_REPORT_PATH_IO_ERROR", + "Error writing compiler report to {0}"); + + private static final DiagnosticType NAME_REF_GRAPH_FILE_ERROR = + DiagnosticType.error("JSC_NAME_REF_GRAPH_FILE_ERROR", + "Error \"{1}\" writing name reference graph to \"{0}\"."); + + private static final DiagnosticType NAME_REF_REPORT_FILE_ERROR = + DiagnosticType.error("JSC_NAME_REF_REPORT_FILE_ERROR", + "Error \"{1}\" writing name reference report to \"{0}\"."); + + private static final java.util.regex.Pattern GLOBAL_SYMBOL_NAMESPACE_PATTERN = + java.util.regex.Pattern.compile("^[a-zA-Z0-9$_]+$"); + + /** + * A global namespace to share across checking passes. + */ + private GlobalNamespace namespaceForChecks = null; + + /** + * A symbol table for registering references that get removed during + * preprocessing. + */ + private PreprocessorSymbolTable preprocessorSymbolTable = null; + + /** + * A type-tightener to share across optimization passes. + */ + private TightenTypes tightenTypes = null; + + /** Names exported by goog.exportSymbol. */ + private Set exportedNames = null; + + /** + * Ids for cross-module method stubbing, so that each method has + * a unique id. + */ + private CrossModuleMethodMotion.IdGenerator crossModuleIdGenerator = + new CrossModuleMethodMotion.IdGenerator(); + + /** + * Keys are arguments passed to getCssName() found during compilation; values + * are the number of times the key appeared as an argument to getCssName(). + */ + private Map cssNames = null; + + /** The variable renaming map */ + private VariableMap variableMap = null; + + /** The property renaming map */ + private VariableMap propertyMap = null; + + /** The naming map for anonymous functions */ + private VariableMap anonymousFunctionNameMap = null; + + /** Fully qualified function names and globally unique ids */ + private FunctionNames functionNames = null; + + /** String replacement map */ + private VariableMap stringMap = null; + + /** Id generator map */ + private String idGeneratorMap = null; + + public DefaultPassConfig(CompilerOptions options) { + super(options); + } + + @Override + protected State getIntermediateState() { + return new State( + cssNames == null ? null : Maps.newHashMap(cssNames), + exportedNames == null ? null : + Collections.unmodifiableSet(exportedNames), + crossModuleIdGenerator, variableMap, propertyMap, + anonymousFunctionNameMap, stringMap, functionNames, idGeneratorMap); + } + + @Override + protected void setIntermediateState(State state) { + this.cssNames = state.cssNames == null ? null : + Maps.newHashMap(state.cssNames); + this.exportedNames = state.exportedNames == null ? null : + Sets.newHashSet(state.exportedNames); + this.crossModuleIdGenerator = state.crossModuleIdGenerator; + this.variableMap = state.variableMap; + this.propertyMap = state.propertyMap; + this.anonymousFunctionNameMap = state.anonymousFunctionNameMap; + this.stringMap = state.stringMap; + this.functionNames = state.functionNames; + this.idGeneratorMap = state.idGeneratorMap; + } + + GlobalNamespace getGlobalNamespace() { + return namespaceForChecks; + } + + PreprocessorSymbolTable getPreprocessorSymbolTable() { + return preprocessorSymbolTable; + } + + void maybeInitializePreprocessorSymbolTable(AbstractCompiler compiler) { + if (options.ideMode) { + Node root = compiler.getRoot(); + if (preprocessorSymbolTable == null || + preprocessorSymbolTable.getRootNode() != root) { + preprocessorSymbolTable = new PreprocessorSymbolTable(root); + } + } + } + + @Override + protected List getChecks() { + List checks = Lists.newArrayList(); + + checks.add(createEmptyPass("beforeStandardChecks")); + + if (options.closurePass) { + checks.add(closureGoogScopeAliases); + checks.add(closureRewriteGoogClass); + } + + if (options.nameAnonymousFunctionsOnly) { + if (options.anonymousFunctionNaming == + AnonymousFunctionNamingPolicy.MAPPED) { + checks.add(nameMappedAnonymousFunctions); + } else if (options.anonymousFunctionNaming == + AnonymousFunctionNamingPolicy.UNMAPPED) { + checks.add(nameUnmappedAnonymousFunctions); + } + return checks; + } + + if (options.jqueryPass) { + checks.add(jqueryAliases); + } + + checks.add(checkSideEffects); + + if (options.checkSuspiciousCode || + options.enables(DiagnosticGroups.GLOBAL_THIS) || + options.enables(DiagnosticGroups.DEBUGGER_STATEMENT_PRESENT)) { + checks.add(suspiciousCode); + } + + if (options.checkControlStructures + || options.enables(DiagnosticGroups.ES5_STRICT)) { + checks.add(checkControlStructures); + } + + if (options.checkRequires.isOn()) { + checks.add(checkRequires); + } + + if (options.checkProvides.isOn()) { + checks.add(checkProvides); + } + + // The following passes are more like "preprocessor" passes. + // It's important that they run before most checking passes. + // Perhaps this method should be renamed? + if (options.generateExports) { + checks.add(generateExports); + } + + if (options.exportTestFunctions) { + checks.add(exportTestFunctions); + } + + if (options.closurePass) { + checks.add(closurePrimitives); + } + + if (options.closurePass && options.checkMissingGetCssNameLevel.isOn()) { + checks.add(closureCheckGetCssName); + } + + if (options.syntheticBlockStartMarker != null) { + // This pass must run before the first fold constants pass. + checks.add(createSyntheticBlocks); + } + + checks.add(checkVars); + if (options.computeFunctionSideEffects) { + checks.add(checkRegExp); + } + + if (options.aggressiveVarCheck.isOn()) { + checks.add(checkVariableReferences); + } + + // This pass should run before types are assigned. + if (options.processObjectPropertyString) { + checks.add(objectPropertyStringPreprocess); + } + + if (options.checkTypes || options.inferTypes) { + checks.add(resolveTypes); + checks.add(inferTypes); + if (options.checkTypes) { + checks.add(checkTypes); + } else { + checks.add(inferJsDocInfo); + } + + // We assume that only IDE-mode clients will try to query the + // typed scope creator after the compile job. + if (!options.ideMode && !options.saveDataStructures) { + checks.add(clearTypedScopePass); + } + } + + if (options.checkUnreachableCode.isOn() || + (options.checkTypes && options.checkMissingReturn.isOn())) { + checks.add(checkControlFlow); + } + + // CheckAccessControls only works if check types is on. + if (options.checkTypes && + (options.enables(DiagnosticGroups.ACCESS_CONTROLS) + || options.enables(DiagnosticGroups.CONSTANT_PROPERTY))) { + checks.add(checkAccessControls); + } + + if (options.checkGlobalNamesLevel.isOn()) { + checks.add(checkGlobalNames); + } + + if (options.enables(DiagnosticGroups.ES5_STRICT) || options.checkCaja) { + checks.add(checkStrictMode); + } + + // Replace 'goog.getCssName' before processing defines but after the + // other checks have been done. + if (options.closurePass) { + checks.add(closureReplaceGetCssName); + } + + // i18n + // If you want to customize the compiler to use a different i18n pass, + // you can create a PassConfig that calls replacePassFactory + // to replace this. + if (options.replaceMessagesWithChromeI18n) { + checks.add(replaceMessagesForChrome); + } else if (options.messageBundle != null) { + checks.add(replaceMessages); + } + + if (options.getTweakProcessing().isOn()) { + checks.add(processTweaks); + } + + // Defines in code always need to be processed. + checks.add(processDefines); + + if (options.instrumentationTemplate != null || + options.recordFunctionInformation) { + checks.add(computeFunctionNames); + } + + if (options.nameReferenceGraphPath != null && + !options.nameReferenceGraphPath.isEmpty()) { + checks.add(printNameReferenceGraph); + } + + if (options.nameReferenceReportPath != null && + !options.nameReferenceReportPath.isEmpty()) { + checks.add(printNameReferenceReport); + } + + checks.add(createEmptyPass("afterStandardChecks")); + + assertAllOneTimePasses(checks); + return checks; + } + + @Override + protected List getOptimizations() { + List passes = Lists.newArrayList(); + passes.add(garbageCollectChecks); + + // TODO(nicksantos): The order of these passes makes no sense, and needs + // to be re-arranged. + + if (options.runtimeTypeCheck) { + passes.add(runtimeTypeCheck); + } + + passes.add(createEmptyPass("beforeStandardOptimizations")); + + if (options.replaceIdGenerators) { + passes.add(replaceIdGenerators); + } + + // Optimizes references to the arguments variable. + if (options.optimizeArgumentsArray) { + passes.add(optimizeArgumentsArray); + } + + // Abstract method removal works best on minimally modified code, and also + // only needs to run once. + if (options.closurePass && + (options.removeAbstractMethods || options.removeClosureAsserts)) { + passes.add(closureCodeRemoval); + } + + // Collapsing properties can undo constant inlining, so we do this before + // the main optimization loop. + if (options.collapseProperties) { + passes.add(collapseProperties); + } + + // ReplaceStrings runs after CollapseProperties in order to simplify + // pulling in values of constants defined in enums structures. + if (!options.replaceStringsFunctionDescriptions.isEmpty()) { + passes.add(replaceStrings); + } + + // Tighten types based on actual usage. + if (options.tightenTypes) { + passes.add(tightenTypesBuilder); + } + + // Property disambiguation should only run once and needs to be done + // soon after type checking, both so that it can make use of type + // information and so that other passes can take advantage of the renamed + // properties. + if (options.disambiguateProperties) { + passes.add(disambiguateProperties); + } + + if (options.computeFunctionSideEffects) { + passes.add(markPureFunctions); + } else if (options.markNoSideEffectCalls) { + // TODO(user) The properties that this pass adds to CALL and NEW + // AST nodes increase the AST's in-memory size. Given that we are + // already running close to our memory limits, we could run into + // trouble if we end up using the @nosideeffects annotation a lot + // or compute @nosideeffects annotations by looking at function + // bodies. It should be easy to propagate @nosideeffects + // annotations as part of passes that depend on this property and + // store the result outside the AST (which would allow garbage + // collection once the pass is done). + passes.add(markNoSideEffectCalls); + } + + if (options.chainCalls) { + passes.add(chainCalls); + } + + // Constant checking must be done after property collapsing because + // property collapsing can introduce new constants (e.g. enum values). + // TODO(johnlenz): make checkConsts namespace aware so it can be run + // as during the checks phase. + passes.add(checkConsts); + + // The Caja library adds properties to Object.prototype, which breaks + // most for-in loops. This adds a check to each loop that skips + // any property matching /___$/. + if (options.ignoreCajaProperties) { + passes.add(ignoreCajaProperties); + } + + assertAllOneTimePasses(passes); + + if (options.smartNameRemoval || options.reportPath != null) { + passes.addAll(getCodeRemovingPasses()); + passes.add(smartNamePass); + } + + // This needs to come after the inline constants pass, which is run within + // the code removing passes. + if (options.closurePass) { + passes.add(closureOptimizePrimitives); + } + + // TODO(user): This forces a first crack at crossModuleCodeMotion + // before devirtualization. Once certain functions are devirtualized, + // it confuses crossModuleCodeMotion ability to recognized that + // it is recursive. + + // TODO(user): This is meant for a temporary quick win. + // In the future, we might want to improve our analysis in + // CrossModuleCodeMotion so we don't need to do this. + if (options.crossModuleCodeMotion) { + passes.add(crossModuleCodeMotion); + } + + // Method devirtualization benefits from property disambiguation so + // it should run after that pass but before passes that do + // optimizations based on global names (like cross module code motion + // and inline functions). Smart Name Removal does better if run before + // this pass. + if (options.devirtualizePrototypeMethods) { + passes.add(devirtualizePrototypeMethods); + } + + if (options.customPasses != null) { + passes.add(getCustomPasses( + CustomPassExecutionTime.BEFORE_OPTIMIZATION_LOOP)); + } + + passes.add(createEmptyPass("beforeMainOptimizations")); + + passes.addAll(getMainOptimizationLoop()); + + if (options.specializeInitialModule) { + // When specializing the initial module, we want our fixups to be + // as lean as possible, so we run the entire optimization loop to a + // fixed point before specializing, then specialize, and then run the + // main optimization loop again. + + if (options.crossModuleCodeMotion) { + passes.add(crossModuleCodeMotion); + } + + if (options.crossModuleMethodMotion) { + passes.add(crossModuleMethodMotion); + } + + passes.add(specializeInitialModule); + passes.addAll(getMainOptimizationLoop()); + } + + passes.add(createEmptyPass("beforeModuleMotion")); + + if (options.crossModuleCodeMotion) { + passes.add(crossModuleCodeMotion); + } + + if (options.crossModuleMethodMotion) { + passes.add(crossModuleMethodMotion); + } + + passes.add(createEmptyPass("afterModuleMotion")); + + // Some optimizations belong outside the loop because running them more + // than once would either have no benefit or be incorrect. + if (options.customPasses != null) { + passes.add(getCustomPasses( + CustomPassExecutionTime.AFTER_OPTIMIZATION_LOOP)); + } + + if (options.flowSensitiveInlineVariables) { + passes.add(flowSensitiveInlineVariables); + + // After inlining some of the variable uses, some variables are unused. + // Re-run remove unused vars to clean it up. + if (options.removeUnusedVars || options.removeUnusedLocalVars) { + passes.add(removeUnusedVars); + } + } + + // Running this pass again is required to have goog.events compile down to + // nothing when compiled on its own. + if (options.smartNameRemoval) { + passes.add(smartNamePass2); + } + + if (options.collapseAnonymousFunctions) { + passes.add(collapseAnonymousFunctions); + } + + // Move functions before extracting prototype member declarations. + if (options.moveFunctionDeclarations || + // renamePrefixNamescape relies on moveFunctionDeclarations + // to preserve semantics. + options.renamePrefixNamespace != null) { + passes.add(moveFunctionDeclarations); + } + + if (options.anonymousFunctionNaming == + AnonymousFunctionNamingPolicy.MAPPED) { + passes.add(nameMappedAnonymousFunctions); + } + + // The mapped name anonymous function pass makes use of information that + // the extract prototype member declarations pass removes so the former + // happens before the latter. + // + // Extracting prototype properties screws up the heuristic renaming + // policies, so never run it when those policies are requested. + if (options.extractPrototypeMemberDeclarations && + (options.propertyRenaming != PropertyRenamingPolicy.HEURISTIC && + options.propertyRenaming != + PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC)) { + passes.add(extractPrototypeMemberDeclarations); + } + + if (options.ambiguateProperties && + (options.propertyRenaming == PropertyRenamingPolicy.ALL_UNQUOTED)) { + passes.add(ambiguateProperties); + } + + if (options.propertyRenaming != PropertyRenamingPolicy.OFF) { + passes.add(renameProperties); + } + + // Reserve global names added to the "windows" object. + if (options.reserveRawExports) { + passes.add(gatherRawExports); + } + + // This comes after property renaming because quoted property names must + // not be renamed. + if (options.convertToDottedProperties) { + passes.add(convertToDottedProperties); + } + + // Property renaming must happen before this pass runs since this + // pass may convert dotted properties into quoted properties. It + // is beneficial to run before alias strings, alias keywords and + // variable renaming. + if (options.rewriteFunctionExpressions) { + passes.add(rewriteFunctionExpressions); + } + + // This comes after converting quoted property accesses to dotted property + // accesses in order to avoid aliasing property names. + if (!options.aliasableStrings.isEmpty() || options.aliasAllStrings) { + passes.add(aliasStrings); + } + + if (options.aliasExternals) { + passes.add(aliasExternals); + } + + if (options.aliasKeywords) { + passes.add(aliasKeywords); + } + + // Passes after this point can no longer depend on normalized AST + // assumptions. + passes.add(markUnnormalized); + + if (options.coalesceVariableNames) { + passes.add(coalesceVariableNames); + + // coalesceVariables creates identity assignments and more redundant code + // that can be removed, rerun the peephole optimizations to clean them + // up. + if (options.foldConstants) { + passes.add(peepholeOptimizations); + } + } + + if (options.collapseVariableDeclarations) { + passes.add(exploitAssign); + passes.add(collapseVariableDeclarations); + } + + // This pass works best after collapseVariableDeclarations. + passes.add(denormalize); + + if (options.instrumentationTemplate != null) { + passes.add(instrumentFunctions); + } + + if (options.variableRenaming != VariableRenamingPolicy.ALL) { + // If we're leaving some (or all) variables with their old names, + // then we need to undo any of the markers we added for distinguishing + // local variables ("$$1"). + passes.add(invertContextualRenaming); + } + + if (options.variableRenaming != VariableRenamingPolicy.OFF) { + passes.add(renameVars); + } + + if (options.groupVariableDeclarations) { + passes.add(groupVariableDeclarations); + } + + // This pass should run after names stop changing. + if (options.processObjectPropertyString) { + passes.add(objectPropertyStringPostprocess); + } + + if (options.labelRenaming) { + passes.add(renameLabels); + } + + if (options.foldConstants) { + passes.add(latePeepholeOptimizations); + } + + if (options.anonymousFunctionNaming == + AnonymousFunctionNamingPolicy.UNMAPPED) { + passes.add(nameUnmappedAnonymousFunctions); + } + + passes.add(stripSideEffectProtection); + + if (options.renamePrefixNamespace != null) { + if (!GLOBAL_SYMBOL_NAMESPACE_PATTERN.matcher( + options.renamePrefixNamespace).matches()) { + throw new IllegalArgumentException( + "Illegal character in renamePrefixNamespace name: " + + options.renamePrefixNamespace); + } + passes.add(rescopeGlobalSymbols); + } + + // Safety checks + passes.add(sanityCheckAst); + passes.add(sanityCheckVars); + + return passes; + } + + /** Creates the passes for the main optimization loop. */ + private List getMainOptimizationLoop() { + List passes = Lists.newArrayList(); + if (options.inlineGetters) { + passes.add(inlineSimpleMethods); + } + + passes.addAll(getCodeRemovingPasses()); + + if (options.inlineFunctions || options.inlineLocalFunctions) { + passes.add(inlineFunctions); + } + + if (options.inlineProperties) { + passes.add(inlineProperties); + } + + boolean runOptimizeCalls = options.optimizeCalls + || options.optimizeParameters + || options.optimizeReturns; + + if (options.removeUnusedVars || options.removeUnusedLocalVars) { + if (options.deadAssignmentElimination) { + passes.add(deadAssignmentsElimination); + } + if (!runOptimizeCalls) { + passes.add(removeUnusedVars); + } + } + if (runOptimizeCalls) { + passes.add(optimizeCallsAndRemoveUnusedVars); + } + assertAllLoopablePasses(passes); + return passes; + } + + /** Creates several passes aimed at removing code. */ + private List getCodeRemovingPasses() { + List passes = Lists.newArrayList(); + if (options.collapseObjectLiterals && !isInliningForbidden()) { + passes.add(collapseObjectLiterals); + } + + if (options.inlineVariables || options.inlineLocalVariables) { + passes.add(inlineVariables); + } else if (options.inlineConstantVars) { + passes.add(inlineConstants); + } + + if (options.foldConstants) { + // These used to be one pass. + passes.add(minimizeExitPoints); + passes.add(peepholeOptimizations); + } + + if (options.removeDeadCode) { + passes.add(removeUnreachableCode); + } + + if (options.removeUnusedPrototypeProperties) { + passes.add(removeUnusedPrototypeProperties); + } + + if (options.removeUnusedClassProperties && !isInliningForbidden()) { + passes.add(removeUnusedClassProperties); + } + + assertAllLoopablePasses(passes); + return passes; + } + + /** + * Checks for code that is probably wrong (such as stray expressions). + */ + final HotSwapPassFactory checkSideEffects = + new HotSwapPassFactory("checkSideEffects", true) { + @Override + protected HotSwapCompilerPass create(final AbstractCompiler compiler) { + // The current approach to protecting "hidden" side-effects is to + // wrap them in a function call that is stripped later, this shouldn't + // be done in IDE mode where AST changes may be unexpected. + boolean protectHiddenSideEffects = + options.protectHiddenSideEffects && !options.ideMode; + return new CheckSideEffects(compiler, + options.checkSuspiciousCode ? CheckLevel.WARNING : CheckLevel.OFF, + protectHiddenSideEffects); + } + }; + + /** + * Checks for code that is probably wrong (such as stray expressions). + */ + final PassFactory stripSideEffectProtection = + new PassFactory("stripSideEffectProtection", true) { + @Override + protected CompilerPass create(final AbstractCompiler + compiler) { + return new CheckSideEffects.StripProtection(compiler); + } + }; + + /** + * Checks for code that is probably wrong (such as stray expressions). + */ + final HotSwapPassFactory suspiciousCode = + new HotSwapPassFactory("suspiciousCode", true) { + @Override + protected HotSwapCompilerPass create(final AbstractCompiler compiler) { + List sharedCallbacks = Lists.newArrayList(); + if (options.checkSuspiciousCode) { + sharedCallbacks.add(new CheckSuspiciousCode()); + } + + if (options.enables(DiagnosticGroups.GLOBAL_THIS)) { + sharedCallbacks.add(new CheckGlobalThis(compiler)); + } + + if (options.enables(DiagnosticGroups.DEBUGGER_STATEMENT_PRESENT)) { + sharedCallbacks.add(new CheckDebuggerStatement(compiler)); + } + + return combineChecks(compiler, sharedCallbacks); + } + + }; + + /** Verify that all the passes are one-time passes. */ + private void assertAllOneTimePasses(List passes) { + for (PassFactory pass : passes) { + Preconditions.checkState(pass.isOneTimePass()); + } + } + + /** Verify that all the passes are multi-run passes. */ + private void assertAllLoopablePasses(List passes) { + for (PassFactory pass : passes) { + Preconditions.checkState(!pass.isOneTimePass()); + } + } + + /** Checks for validity of the control structures. */ + final HotSwapPassFactory checkControlStructures = + new HotSwapPassFactory("checkControlStructures", true) { + @Override + protected HotSwapCompilerPass create(AbstractCompiler compiler) { + return new ControlStructureCheck(compiler); + } + }; + + /** Checks that all constructed classes are goog.require()d. */ + final HotSwapPassFactory checkRequires = + new HotSwapPassFactory("checkRequires", true) { + @Override + protected HotSwapCompilerPass create(AbstractCompiler compiler) { + return new CheckRequiresForConstructors(compiler, options.checkRequires); + } + }; + + /** Makes sure @constructor is paired with goog.provides(). */ + final HotSwapPassFactory checkProvides = + new HotSwapPassFactory("checkProvides", true) { + @Override + protected HotSwapCompilerPass create(AbstractCompiler compiler) { + return new CheckProvides(compiler, options.checkProvides); + } + }; + + private static final DiagnosticType GENERATE_EXPORTS_ERROR = + DiagnosticType.error( + "JSC_GENERATE_EXPORTS_ERROR", + "Exports can only be generated if export symbol/property " + + "functions are set."); + + /** Generates exports for @export annotations. */ + final PassFactory generateExports = new PassFactory("generateExports", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + CodingConvention convention = compiler.getCodingConvention(); + if (convention.getExportSymbolFunction() != null && + convention.getExportPropertyFunction() != null) { + return new GenerateExports(compiler, + convention.getExportSymbolFunction(), + convention.getExportPropertyFunction()); + } else { + return new ErrorPass(compiler, GENERATE_EXPORTS_ERROR); + } + } + }; + + /** Generates exports for functions associated with JsUnit. */ + final PassFactory exportTestFunctions = + new PassFactory("exportTestFunctions", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + CodingConvention convention = compiler.getCodingConvention(); + if (convention.getExportSymbolFunction() != null) { + return new ExportTestFunctions(compiler, + convention.getExportSymbolFunction(), + convention.getExportPropertyFunction()); + } else { + return new ErrorPass(compiler, GENERATE_EXPORTS_ERROR); + } + } + }; + + /** Raw exports processing pass. */ + final PassFactory gatherRawExports = + new PassFactory("gatherRawExports", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + final GatherRawExports pass = new GatherRawExports( + compiler); + + return new CompilerPass() { + @Override + public void process(Node externs, Node root) { + pass.process(externs, root); + if (exportedNames == null) { + exportedNames = Sets.newHashSet(); + } + exportedNames.addAll(pass.getExportedVariableNames()); + } + }; + } + }; + + /** Closure pre-processing pass. */ + @SuppressWarnings("deprecation") + final HotSwapPassFactory closurePrimitives = + new HotSwapPassFactory("closurePrimitives", true) { + @Override + protected HotSwapCompilerPass create(AbstractCompiler compiler) { + maybeInitializePreprocessorSymbolTable(compiler); + final ProcessClosurePrimitives pass = new ProcessClosurePrimitives( + compiler, + preprocessorSymbolTable, + options.brokenClosureRequiresLevel); + + return new HotSwapCompilerPass() { + @Override + public void process(Node externs, Node root) { + pass.process(externs, root); + exportedNames = pass.getExportedVariableNames(); + } + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + pass.hotSwapScript(scriptRoot, originalRoot); + } + }; + } + }; + + /** Expand jQuery Primitives and Aliases pass. */ + final PassFactory jqueryAliases = new PassFactory("jqueryAliases", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new ExpandJqueryAliases(compiler); + } + }; + + /** + * The default i18n pass. + * A lot of the options are not configurable, because ReplaceMessages + * has a lot of legacy logic. + */ + final PassFactory replaceMessages = new PassFactory("replaceMessages", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new ReplaceMessages(compiler, + options.messageBundle, + /* warn about message dupes */ + true, + /* allow messages with goog.getMsg */ + JsMessage.Style.getFromParams(true, false), + /* if we can't find a translation, don't worry about it. */ + false); + } + }; + + final PassFactory replaceMessagesForChrome = + new PassFactory("replaceMessages", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new ReplaceMessagesForChrome(compiler, + new GoogleJsMessageIdGenerator(options.tcProjectId), + /* warn about message dupes */ + true, + /* allow messages with goog.getMsg */ + JsMessage.Style.getFromParams(true, false)); + } + }; + + /** Applies aliases and inlines goog.scope. */ + final HotSwapPassFactory closureGoogScopeAliases = + new HotSwapPassFactory("closureGoogScopeAliases", true) { + @Override + protected HotSwapCompilerPass create(AbstractCompiler compiler) { + maybeInitializePreprocessorSymbolTable(compiler); + return new ScopedAliases( + compiler, + preprocessorSymbolTable, + options.getAliasTransformationHandler()); + } + }; + + /** Rewrites goog.class */ + final HotSwapPassFactory closureRewriteGoogClass = + new HotSwapPassFactory("closureRewriteGoogClass", true) { + @Override + protected HotSwapCompilerPass create(AbstractCompiler compiler) { + return new ClosureRewriteClass(compiler); + } + }; + + /** Checks that CSS class names are wrapped in goog.getCssName */ + final PassFactory closureCheckGetCssName = + new PassFactory("closureCheckGetCssName", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + String blacklist = options.checkMissingGetCssNameBlacklist; + Preconditions.checkState(blacklist != null && !blacklist.isEmpty(), + "Not checking use of goog.getCssName because of empty blacklist."); + return new CheckMissingGetCssName( + compiler, options.checkMissingGetCssNameLevel, blacklist); + } + }; + + /** + * Processes goog.getCssName. The cssRenamingMap is used to lookup + * replacement values for the classnames. If null, the raw class names are + * inlined. + */ + final PassFactory closureReplaceGetCssName = + new PassFactory("closureReplaceGetCssName", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new CompilerPass() { + @Override + public void process(Node externs, Node jsRoot) { + Map newCssNames = null; + if (options.gatherCssNames) { + newCssNames = Maps.newHashMap(); + } + ReplaceCssNames pass = new ReplaceCssNames( + compiler, + newCssNames, + options.cssRenamingWhitelist); + pass.process(externs, jsRoot); + cssNames = newCssNames; + } + }; + } + }; + + /** + * Creates synthetic blocks to prevent FoldConstants from moving code + * past markers in the source. + */ + final PassFactory createSyntheticBlocks = + new PassFactory("createSyntheticBlocks", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new CreateSyntheticBlocks(compiler, + options.syntheticBlockStartMarker, + options.syntheticBlockEndMarker); + } + }; + + /** Various peephole optimizations. */ + final PassFactory peepholeOptimizations = + new PassFactory("peepholeOptimizations", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + final boolean late = false; + return new PeepholeOptimizationsPass(compiler, + new PeepholeSubstituteAlternateSyntax(late), + new PeepholeReplaceKnownMethods(late), + new PeepholeRemoveDeadCode(), + new PeepholeFoldConstants(late), + new PeepholeCollectPropertyAssignments()); + } + }; + + /** Same as peepholeOptimizations but aggressively merges code together */ + final PassFactory latePeepholeOptimizations = + new PassFactory("latePeepholeOptimizations", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + final boolean late = true; + return new PeepholeOptimizationsPass(compiler, + new StatementFusion(), + new PeepholeRemoveDeadCode(), + new PeepholeSubstituteAlternateSyntax(late), + new PeepholeReplaceKnownMethods(late), + new PeepholeFoldConstants(late), + new ReorderConstantExpression()); + } + }; + + /** Checks that all variables are defined. */ + final HotSwapPassFactory checkVars = + new HotSwapPassFactory("checkVars", true) { + @Override + protected HotSwapCompilerPass create(AbstractCompiler compiler) { + return new VarCheck(compiler); + } + }; + + /** Checks for RegExp references. */ + final PassFactory checkRegExp = + new PassFactory("checkRegExp", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + final CheckRegExp pass = new CheckRegExp(compiler); + + return new CompilerPass() { + @Override + public void process(Node externs, Node root) { + pass.process(externs, root); + compiler.setHasRegExpGlobalReferences( + pass.isGlobalRegExpPropertiesUsed()); + } + }; + } + }; + + /** Checks that references to variables look reasonable. */ + final HotSwapPassFactory checkVariableReferences = + new HotSwapPassFactory("checkVariableReferences", true) { + @Override + protected HotSwapCompilerPass create(AbstractCompiler compiler) { + return new VariableReferenceCheck( + compiler, options.aggressiveVarCheck); + } + }; + + /** Pre-process goog.testing.ObjectPropertyString. */ + final PassFactory objectPropertyStringPreprocess = + new PassFactory("ObjectPropertyStringPreprocess", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new ObjectPropertyStringPreprocess(compiler); + } + }; + + /** Creates a typed scope and adds types to the type registry. */ + final HotSwapPassFactory resolveTypes = + new HotSwapPassFactory("resolveTypes", true) { + @Override + protected HotSwapCompilerPass create(AbstractCompiler compiler) { + return new GlobalTypeResolver(compiler); + } + }; + + /** Clears the typed scope when we're done. */ + final PassFactory clearTypedScopePass = + new PassFactory("clearTypedScopePass", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new ClearTypedScope(); + } + }; + + /** Runs type inference. */ + final HotSwapPassFactory inferTypes = + new HotSwapPassFactory("inferTypes", true) { + @Override + protected HotSwapCompilerPass create(final AbstractCompiler compiler) { + return new HotSwapCompilerPass() { + @Override + public void process(Node externs, Node root) { + Preconditions.checkNotNull(topScope); + Preconditions.checkNotNull(getTypedScopeCreator()); + + makeTypeInference(compiler).process(externs, root); + } + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + makeTypeInference(compiler).inferAllScopes(scriptRoot); + } + }; + } + }; + + final HotSwapPassFactory inferJsDocInfo = + new HotSwapPassFactory("inferJsDocInfo", true) { + @Override + protected HotSwapCompilerPass create(final AbstractCompiler compiler) { + return new HotSwapCompilerPass() { + @Override + public void process(Node externs, Node root) { + Preconditions.checkNotNull(topScope); + Preconditions.checkNotNull(getTypedScopeCreator()); + + makeInferJsDocInfo(compiler).process(externs, root); + } + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + makeInferJsDocInfo(compiler).hotSwapScript(scriptRoot, originalRoot); + } + }; + } +}; + + /** Checks type usage */ + final HotSwapPassFactory checkTypes = + new HotSwapPassFactory("checkTypes", true) { + @Override + protected HotSwapCompilerPass create(final AbstractCompiler compiler) { + return new HotSwapCompilerPass() { + @Override + public void process(Node externs, Node root) { + Preconditions.checkNotNull(topScope); + Preconditions.checkNotNull(getTypedScopeCreator()); + + TypeCheck check = makeTypeCheck(compiler); + check.process(externs, root); + compiler.getErrorManager().setTypedPercent(check.getTypedPercent()); + } + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + makeTypeCheck(compiler).check(scriptRoot, false); + } + }; + } + }; + + /** + * Checks possible execution paths of the program for problems: missing return + * statements and dead code. + */ + final HotSwapPassFactory checkControlFlow = + new HotSwapPassFactory("checkControlFlow", true) { + @Override + protected HotSwapCompilerPass create(AbstractCompiler compiler) { + List callbacks = Lists.newArrayList(); + if (options.checkUnreachableCode.isOn()) { + callbacks.add( + new CheckUnreachableCode(compiler, options.checkUnreachableCode)); + } + if (options.checkMissingReturn.isOn() && options.checkTypes) { + callbacks.add( + new CheckMissingReturn(compiler, options.checkMissingReturn)); + } + return combineChecks(compiler, callbacks); + } + }; + + /** Checks access controls. Depends on type-inference. */ + final HotSwapPassFactory checkAccessControls = + new HotSwapPassFactory("checkAccessControls", true) { + @Override + protected HotSwapCompilerPass create(AbstractCompiler compiler) { + return new CheckAccessControls(compiler); + } + }; + + /** Executes the given callbacks with a {@link CombinedCompilerPass}. */ + private static HotSwapCompilerPass combineChecks(AbstractCompiler compiler, + List callbacks) { + Preconditions.checkArgument(callbacks.size() > 0); + Callback[] array = callbacks.toArray(new Callback[callbacks.size()]); + return new CombinedCompilerPass(compiler, array); + } + + /** A compiler pass that resolves types in the global scope. */ + class GlobalTypeResolver implements HotSwapCompilerPass { + private final AbstractCompiler compiler; + + GlobalTypeResolver(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + if (topScope == null) { + regenerateGlobalTypedScope(compiler, root.getParent()); + } else { + compiler.getTypeRegistry().resolveTypesInScope(topScope); + } + } + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + patchGlobalTypedScope(compiler, scriptRoot); + } + } + + /** A compiler pass that clears the global scope. */ + class ClearTypedScope implements CompilerPass { + @Override + public void process(Node externs, Node root) { + clearTypedScope(); + } + } + + /** Checks global name usage. */ + final PassFactory checkGlobalNames = + new PassFactory("checkGlobalNames", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new CompilerPass() { + @Override + public void process(Node externs, Node jsRoot) { + // Create a global namespace for analysis by check passes. + // Note that this class does all heavy computation lazily, + // so it's OK to create it here. + namespaceForChecks = new GlobalNamespace(compiler, externs, jsRoot); + new CheckGlobalNames(compiler, options.checkGlobalNamesLevel) + .injectNamespace(namespaceForChecks).process(externs, jsRoot); + } + }; + } + }; + + /** Checks that the code is ES5 or Caja compliant. */ + final PassFactory checkStrictMode = + new PassFactory("checkStrictMode", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new StrictModeCheck(compiler, + !options.checkSymbols, // don't check variables twice + !options.checkCaja); // disable eval check if not Caja + } + }; + + /** Process goog.tweak.getTweak() calls. */ + final PassFactory processTweaks = new PassFactory("processTweaks", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new CompilerPass() { + @Override + public void process(Node externs, Node jsRoot) { + new ProcessTweaks(compiler, + options.getTweakProcessing().shouldStrip(), + options.getTweakReplacements()).process(externs, jsRoot); + } + }; + } + }; + + /** Override @define-annotated constants. */ + final PassFactory processDefines = new PassFactory("processDefines", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new CompilerPass() { + @Override + public void process(Node externs, Node jsRoot) { + Map replacements = getAdditionalReplacements(options); + replacements.putAll(options.getDefineReplacements()); + + new ProcessDefines(compiler, replacements) + .injectNamespace(namespaceForChecks).process(externs, jsRoot); + } + }; + } + }; + + /** Release references to data that is only needed during checks. */ + final PassFactory garbageCollectChecks = + new HotSwapPassFactory("garbageCollectChecks", true) { + @Override + protected HotSwapCompilerPass create(final AbstractCompiler compiler) { + return new HotSwapCompilerPass() { + @Override + public void process(Node externs, Node jsRoot) { + // Kill the global namespace so that it can be garbage collected + // after all passes are through with it. + namespaceForChecks = null; + } + + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + process(null, null); + } + }; + } + }; + + /** Checks that all constants are not modified */ + final PassFactory checkConsts = new PassFactory("checkConsts", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new ConstCheck(compiler); + } + }; + + /** Computes the names of functions for later analysis. */ + final PassFactory computeFunctionNames = + new PassFactory("computeFunctionNames", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return ((functionNames = new FunctionNames(compiler))); + } + }; + + /** Skips Caja-private properties in for-in loops */ + final PassFactory ignoreCajaProperties = + new PassFactory("ignoreCajaProperties", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new IgnoreCajaProperties(compiler); + } + }; + + /** Inserts run-time type assertions for debugging. */ + final PassFactory runtimeTypeCheck = + new PassFactory("runtimeTypeCheck", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new RuntimeTypeCheck(compiler, + options.runtimeTypeCheckLogFunction); + } + }; + + /** Generates unique ids. */ + final PassFactory replaceIdGenerators = + new PassFactory("replaceIdGenerators", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new CompilerPass() { + @Override public void process(Node externs, Node root) { + ReplaceIdGenerators pass = + new ReplaceIdGenerators( + compiler, options.idGenerators, options.generatePseudoNames, + options.idGeneratorsMapSerialized); + pass.process(externs, root); + idGeneratorMap = pass.getSerializedIdMappings(); + } + }; + } + }; + + /** Replace strings. */ + final PassFactory replaceStrings = new PassFactory("replaceStrings", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new CompilerPass() { + @Override public void process(Node externs, Node root) { + ReplaceStrings pass = new ReplaceStrings( + compiler, + options.replaceStringsPlaceholderToken, + options.replaceStringsFunctionDescriptions, + options.replaceStringsReservedStrings, + options.replaceStringsInputMap); + pass.process(externs, root); + stringMap = pass.getStringMap(); + } + }; + } + }; + + /** Optimizes the "arguments" array. */ + final PassFactory optimizeArgumentsArray = + new PassFactory("optimizeArgumentsArray", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new OptimizeArgumentsArray(compiler); + } + }; + + /** Remove variables set to goog.abstractMethod. */ + final PassFactory closureCodeRemoval = + new PassFactory("closureCodeRemoval", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new ClosureCodeRemoval(compiler, options.removeAbstractMethods, + options.removeClosureAsserts); + } + }; + + /** Special case optimizations for closure functions. */ + final PassFactory closureOptimizePrimitives = + new PassFactory("closureOptimizePrimitives", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new ClosureOptimizePrimitives(compiler); + } + }; + + /** Puts global symbols into a single object. */ + final PassFactory rescopeGlobalSymbols = + new PassFactory("rescopeGlobalSymbols", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new RescopeGlobalSymbols(compiler, options.renamePrefixNamespace); + } + }; + + /** Collapses names in the global scope. */ + final PassFactory collapseProperties = + new PassFactory("collapseProperties", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new CollapseProperties( + compiler, options.collapsePropertiesOnExternTypes, + !isInliningForbidden()); + } + }; + + /** Rewrite properties as variables. */ + final PassFactory collapseObjectLiterals = + new PassFactory("collapseObjectLiterals", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new InlineObjectLiterals( + compiler, compiler.getUniqueNameIdSupplier()); + } + }; + + /** + * Try to infer the actual types, which may be narrower + * than the declared types. + */ + final PassFactory tightenTypesBuilder = + new PassFactory("tightenTypes", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + if (!options.checkTypes) { + return new ErrorPass(compiler, TIGHTEN_TYPES_WITHOUT_TYPE_CHECK); + } + tightenTypes = new TightenTypes(compiler); + return tightenTypes; + } + }; + + /** Devirtualize property names based on type information. */ + final PassFactory disambiguateProperties = + new PassFactory("disambiguateProperties", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + if (tightenTypes == null) { + return DisambiguateProperties.forJSTypeSystem(compiler, + options.propertyInvalidationErrors); + } else { + return DisambiguateProperties.forConcreteTypeSystem( + compiler, tightenTypes, options.propertyInvalidationErrors); + } + } + }; + + /** + * Chain calls to functions that return this. + */ + final PassFactory chainCalls = new PassFactory("chainCalls", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new ChainCalls(compiler); + } + }; + + /** + * Rewrite instance methods as static methods, to make them easier + * to inline. + */ + final PassFactory devirtualizePrototypeMethods = + new PassFactory("devirtualizePrototypeMethods", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new DevirtualizePrototypeMethods(compiler); + } + }; + + /** + * Optimizes unused function arguments, unused return values, and inlines + * constant parameters. Also runs RemoveUnusedVars. + */ + final PassFactory optimizeCallsAndRemoveUnusedVars = + new PassFactory("optimizeCalls_and_removeUnusedVars", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + OptimizeCalls passes = new OptimizeCalls(compiler); + if (options.optimizeReturns) { + // Remove unused return values. + passes.addPass(new OptimizeReturns(compiler)); + } + + if (options.optimizeParameters) { + // Remove all parameters that are constants or unused. + passes.addPass(new OptimizeParameters(compiler)); + } + + if (options.optimizeCalls) { + boolean removeOnlyLocals = options.removeUnusedLocalVars + && !options.removeUnusedVars; + boolean preserveAnonymousFunctionNames = + options.anonymousFunctionNaming != + AnonymousFunctionNamingPolicy.OFF; + passes.addPass( + new RemoveUnusedVars(compiler, !removeOnlyLocals, + preserveAnonymousFunctionNames, true)); + } + return passes; + } + }; + + /** + * Look for function calls that are pure, and annotate them + * that way. + */ + final PassFactory markPureFunctions = + new PassFactory("markPureFunctions", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new PureFunctionIdentifier.Driver( + compiler, options.debugFunctionSideEffectsPath, false); + } + }; + + /** + * Look for function calls that have no side effects, and annotate them + * that way. + */ + final PassFactory markNoSideEffectCalls = + new PassFactory("markNoSideEffectCalls", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new MarkNoSideEffectCalls(compiler); + } + }; + + /** Inlines variables heuristically. */ + final PassFactory inlineVariables = + new PassFactory("inlineVariables", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + if (isInliningForbidden()) { + // In old renaming schemes, inlining a variable can change whether + // or not a property is renamed. This is bad, and those old renaming + // schemes need to die. + return new ErrorPass(compiler, CANNOT_USE_PROTOTYPE_AND_VAR); + } else { + InlineVariables.Mode mode; + if (options.inlineVariables) { + mode = InlineVariables.Mode.ALL; + } else if (options.inlineLocalVariables) { + mode = InlineVariables.Mode.LOCALS_ONLY; + } else { + throw new IllegalStateException("No variable inlining option set."); + } + + return new InlineVariables(compiler, mode, true); + } + } + }; + + /** Inlines variables that are marked as constants. */ + final PassFactory inlineConstants = + new PassFactory("inlineConstants", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new InlineVariables( + compiler, InlineVariables.Mode.CONSTANTS_ONLY, true); + } + }; + + /** + * Perform local control flow optimizations. + */ + final PassFactory minimizeExitPoints = + new PassFactory("minimizeExitPoints", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new MinimizeExitPoints(compiler); + } + }; + + /** + * Use data flow analysis to remove dead branches. + */ + final PassFactory removeUnreachableCode = + new PassFactory("removeUnreachableCode", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new UnreachableCodeElimination(compiler, true); + } + }; + + /** + * Remove prototype properties that do not appear to be used. + */ + final PassFactory removeUnusedPrototypeProperties = + new PassFactory("removeUnusedPrototypeProperties", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new RemoveUnusedPrototypeProperties( + compiler, options.removeUnusedPrototypePropertiesInExterns, + !options.removeUnusedVars); + } + }; + + /** + * Remove prototype properties that do not appear to be used. + */ + final PassFactory removeUnusedClassProperties = + new PassFactory("removeUnusedClassProperties", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new RemoveUnusedClassProperties(compiler); + } + }; + + /** + * Process smart name processing - removes unused classes and does referencing + * starting with minimum set of names. + */ + final PassFactory smartNamePass = new PassFactory("smartNamePass", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new CompilerPass() { + @Override + public void process(Node externs, Node root) { + NameAnalyzer na = new NameAnalyzer(compiler, false); + na.process(externs, root); + + String reportPath = options.reportPath; + if (reportPath != null) { + try { + Files.write(na.getHtmlReport(), new File(reportPath), + Charsets.UTF_8); + } catch (IOException e) { + compiler.report(JSError.make(REPORT_PATH_IO_ERROR, reportPath)); + } + } + + if (options.smartNameRemoval) { + na.removeUnreferenced(); + } + } + }; + } + }; + + /** + * Process smart name processing - removes unused classes and does referencing + * starting with minimum set of names. + */ + final PassFactory smartNamePass2 = new PassFactory("smartNamePass", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new CompilerPass() { + @Override + public void process(Node externs, Node root) { + NameAnalyzer na = new NameAnalyzer(compiler, false); + na.process(externs, root); + na.removeUnreferenced(); + } + }; + } + }; + + /** Inlines simple methods, like getters */ + final PassFactory inlineSimpleMethods = + new PassFactory("inlineSimpleMethods", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new InlineSimpleMethods(compiler); + } + }; + + /** Kills dead assignments. */ + final PassFactory deadAssignmentsElimination = + new PassFactory("deadAssignmentsElimination", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new DeadAssignmentsElimination(compiler); + } + }; + + /** Inlines function calls. */ + final PassFactory inlineFunctions = + new PassFactory("inlineFunctions", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + boolean enableBlockInlining = !isInliningForbidden(); + return new InlineFunctions( + compiler, + compiler.getUniqueNameIdSupplier(), + options.inlineFunctions, + options.inlineLocalFunctions, + enableBlockInlining, + options.assumeStrictThis() + || options.getLanguageIn() == LanguageMode.ECMASCRIPT5_STRICT, + true /* assumeMinimumCapture */); + } + }; + + /** Inlines constant properties. */ + final PassFactory inlineProperties = + new PassFactory("inlineProperties", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new InlineProperties(compiler); + } + }; + + /** Removes variables that are never used. */ + final PassFactory removeUnusedVars = + new PassFactory("removeUnusedVars", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + boolean removeOnlyLocals = options.removeUnusedLocalVars + && !options.removeUnusedVars; + boolean preserveAnonymousFunctionNames = + options.anonymousFunctionNaming != AnonymousFunctionNamingPolicy.OFF; + return new RemoveUnusedVars( + compiler, + !removeOnlyLocals, + preserveAnonymousFunctionNames, + false); + } + }; + + /** + * Move global symbols to a deeper common module + */ + final PassFactory crossModuleCodeMotion = + new PassFactory("crossModuleCodeMotion", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new CrossModuleCodeMotion(compiler, compiler.getModuleGraph()); + } + }; + + /** + * Move methods to a deeper common module + */ + final PassFactory crossModuleMethodMotion = + new PassFactory("crossModuleMethodMotion", false) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new CrossModuleMethodMotion( + compiler, crossModuleIdGenerator, + // Only move properties in externs if we're not treating + // them as exports. + options.removeUnusedPrototypePropertiesInExterns); + } + }; + + /** + * Specialize the initial module at the cost of later modules + */ + final PassFactory specializeInitialModule = + new PassFactory("specializeInitialModule", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new SpecializeModule(compiler, devirtualizePrototypeMethods, + inlineFunctions, removeUnusedPrototypeProperties); + } + }; + + /** A data-flow based variable inliner. */ + final PassFactory flowSensitiveInlineVariables = + new PassFactory("flowSensitiveInlineVariables", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new FlowSensitiveInlineVariables(compiler); + } + }; + + /** Uses register-allocation algorithms to use fewer variables. */ + final PassFactory coalesceVariableNames = + new PassFactory("coalesceVariableNames", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new CoalesceVariableNames(compiler, options.generatePseudoNames); + } + }; + + /** + * Some simple, local collapses (e.g., {@code var x; var y;} becomes + * {@code var x,y;}. + */ + final PassFactory exploitAssign = new PassFactory("exploitAssign", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new PeepholeOptimizationsPass(compiler, + new ExploitAssigns()); + } + }; + + /** + * Some simple, local collapses (e.g., {@code var x; var y;} becomes + * {@code var x,y;}. + */ + final PassFactory collapseVariableDeclarations = + new PassFactory("collapseVariableDeclarations", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new CollapseVariableDeclarations(compiler); + } + }; + + /** + * Simple global collapses of variable declarations. + */ + final PassFactory groupVariableDeclarations = + new PassFactory("groupVariableDeclarations", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new GroupVariableDeclarations(compiler); + } + }; + + /** + * Extracts common sub-expressions. + */ + final PassFactory extractPrototypeMemberDeclarations = + new PassFactory("extractPrototypeMemberDeclarations", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new ExtractPrototypeMemberDeclarations( + compiler, Pattern.USE_GLOBAL_TEMP); + } + }; + + /** Rewrites common function definitions to be more compact. */ + final PassFactory rewriteFunctionExpressions = + new PassFactory("rewriteFunctionExpressions", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new FunctionRewriter(compiler); + } + }; + + /** Collapses functions to not use the VAR keyword. */ + final PassFactory collapseAnonymousFunctions = + new PassFactory("collapseAnonymousFunctions", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new CollapseAnonymousFunctions(compiler); + } + }; + + /** Moves function declarations to the top, to simulate actual hoisting. */ + final PassFactory moveFunctionDeclarations = + new PassFactory("moveFunctionDeclarations", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new MoveFunctionDeclarations(compiler); + } + }; + + final PassFactory nameUnmappedAnonymousFunctions = + new PassFactory("nameAnonymousFunctions", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new NameAnonymousFunctions(compiler); + } + }; + + final PassFactory nameMappedAnonymousFunctions = + new PassFactory("nameAnonymousFunctions", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new CompilerPass() { + @Override public void process(Node externs, Node root) { + NameAnonymousFunctionsMapped naf = + new NameAnonymousFunctionsMapped( + compiler, options.inputAnonymousFunctionNamingMap); + naf.process(externs, root); + anonymousFunctionNameMap = naf.getFunctionMap(); + } + }; + } + }; + + /** Alias external symbols. */ + final PassFactory aliasExternals = new PassFactory("aliasExternals", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new AliasExternals(compiler, compiler.getModuleGraph(), + options.unaliasableGlobals, options.aliasableGlobals); + } + }; + + /** + * Alias string literals with global variables, to avoid creating lots of + * transient objects. + */ + final PassFactory aliasStrings = new PassFactory("aliasStrings", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new AliasStrings( + compiler, + compiler.getModuleGraph(), + options.aliasAllStrings ? null : options.aliasableStrings, + options.aliasStringsBlacklist, + options.outputJsStringUsage); + } + }; + + /** Aliases common keywords (true, false) */ + final PassFactory aliasKeywords = new PassFactory("aliasKeywords", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new AliasKeywords(compiler); + } + }; + + /** Handling for the ObjectPropertyString primitive. */ + final PassFactory objectPropertyStringPostprocess = + new PassFactory("ObjectPropertyStringPostprocess", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new ObjectPropertyStringPostprocess(compiler); + } + }; + + /** + * Renames properties so that the two properties that never appear on + * the same object get the same name. + */ + final PassFactory ambiguateProperties = + new PassFactory("ambiguateProperties", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new AmbiguateProperties( + compiler, options.anonymousFunctionNaming.getReservedCharacters()); + } + }; + + /** + * Mark the point at which the normalized AST assumptions no longer hold. + */ + final PassFactory markUnnormalized = + new PassFactory("markUnnormalized", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new CompilerPass() { + @Override public void process(Node externs, Node root) { + compiler.setLifeCycleStage(LifeCycleStage.RAW); + } + }; + } + }; + + /** Denormalize the AST for code generation. */ + final PassFactory denormalize = new PassFactory("denormalize", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new Denormalize(compiler); + } + }; + + /** Inverting name normalization. */ + final PassFactory invertContextualRenaming = + new PassFactory("invertContextualRenaming", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return MakeDeclaredNamesUnique.getContextualRenameInverter(compiler); + } + }; + + /** + * Renames properties. + */ + final PassFactory renameProperties = + new PassFactory("renameProperties", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + final VariableMap prevPropertyMap = options.inputPropertyMap; + return new CompilerPass() { + @Override public void process(Node externs, Node root) { + propertyMap = runPropertyRenaming( + compiler, prevPropertyMap, externs, root); + } + }; + } + }; + + private VariableMap runPropertyRenaming( + AbstractCompiler compiler, VariableMap prevPropertyMap, + Node externs, Node root) { + char[] reservedChars = + options.anonymousFunctionNaming.getReservedCharacters(); + switch (options.propertyRenaming) { + case HEURISTIC: + RenamePrototypes rproto = new RenamePrototypes(compiler, false, + reservedChars, prevPropertyMap); + rproto.process(externs, root); + return rproto.getPropertyMap(); + + case AGGRESSIVE_HEURISTIC: + RenamePrototypes rproto2 = new RenamePrototypes(compiler, true, + reservedChars, prevPropertyMap); + rproto2.process(externs, root); + return rproto2.getPropertyMap(); + + case ALL_UNQUOTED: + RenameProperties rprop = new RenameProperties( + compiler, options.propertyAffinity, options.generatePseudoNames, + prevPropertyMap, reservedChars); + rprop.process(externs, root); + return rprop.getPropertyMap(); + + default: + throw new IllegalStateException( + "Unrecognized property renaming policy"); + } + } + + /** Renames variables. */ + final PassFactory renameVars = new PassFactory("renameVars", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + final VariableMap prevVariableMap = options.inputVariableMap; + return new CompilerPass() { + @Override public void process(Node externs, Node root) { + variableMap = runVariableRenaming( + compiler, prevVariableMap, externs, root); + } + }; + } + }; + + private VariableMap runVariableRenaming( + AbstractCompiler compiler, VariableMap prevVariableMap, + Node externs, Node root) { + char[] reservedChars = + options.anonymousFunctionNaming.getReservedCharacters(); + boolean preserveAnonymousFunctionNames = + options.anonymousFunctionNaming != AnonymousFunctionNamingPolicy.OFF; + Set reservedNames = Sets.newHashSet(); + if (exportedNames != null) { + reservedNames.addAll(exportedNames); + } + reservedNames.addAll(ParserRunner.getReservedVars()); + RenameVars rn = new RenameVars( + compiler, + options.renamePrefix, + options.variableRenaming == VariableRenamingPolicy.LOCAL, + preserveAnonymousFunctionNames, + options.generatePseudoNames, + options.shadowVariables, + prevVariableMap, + reservedChars, + reservedNames); + rn.process(externs, root); + return rn.getVariableMap(); + } + + /** Renames labels */ + final PassFactory renameLabels = new PassFactory("renameLabels", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new RenameLabels(compiler); + } + }; + + /** Convert bracket access to dot access */ + final PassFactory convertToDottedProperties = + new PassFactory("convertToDottedProperties", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new ConvertToDottedProperties(compiler); + } + }; + + /** Checks that all variables are defined. */ + final PassFactory sanityCheckAst = new PassFactory("sanityCheckAst", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new AstValidator(); + } + }; + + /** Checks that all variables are defined. */ + final PassFactory sanityCheckVars = new PassFactory("sanityCheckVars", true) { + @Override + protected CompilerPass create(AbstractCompiler compiler) { + return new VarCheck(compiler, true); + } + }; + + /** Adds instrumentations according to an instrumentation template. */ + final PassFactory instrumentFunctions = + new PassFactory("instrumentFunctions", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new CompilerPass() { + @Override public void process(Node externs, Node root) { + try { + FileReader templateFile = + new FileReader(options.instrumentationTemplate); + (new InstrumentFunctions( + compiler, functionNames, + options.instrumentationTemplate, + options.appNameStr, + templateFile)).process(externs, root); + } catch (IOException e) { + compiler.report( + JSError.make(AbstractCompiler.READ_ERROR, + options.instrumentationTemplate)); + } + } + }; + } + }; + + /** + * Create a no-op pass that can only run once. Used to break up loops. + */ + static PassFactory createEmptyPass(String name) { + return new PassFactory(name, true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return runInSerial(); + } + }; + } + + /** + * Runs custom passes that are designated to run at a particular time. + */ + private PassFactory getCustomPasses( + final CustomPassExecutionTime executionTime) { + return new PassFactory("runCustomPasses", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return runInSerial(options.customPasses.get(executionTime)); + } + }; + } + + /** + * All inlining is forbidden in heuristic renaming mode, because inlining + * will ruin the invariants that it depends on. + */ + private boolean isInliningForbidden() { + return options.propertyRenaming == PropertyRenamingPolicy.HEURISTIC || + options.propertyRenaming == + PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC; + } + + /** Create a compiler pass that runs the given passes in serial. */ + private static CompilerPass runInSerial(final CompilerPass ... passes) { + return runInSerial(Lists.newArrayList(passes)); + } + + /** Create a compiler pass that runs the given passes in serial. */ + private static CompilerPass runInSerial( + final Collection passes) { + return new CompilerPass() { + @Override public void process(Node externs, Node root) { + for (CompilerPass pass : passes) { + pass.process(externs, root); + } + } + }; + } + + @VisibleForTesting + static Map getAdditionalReplacements( + CompilerOptions options) { + Map additionalReplacements = Maps.newHashMap(); + + if (options.markAsCompiled || options.closurePass) { + additionalReplacements.put(COMPILED_CONSTANT_NAME, IR.trueNode()); + } + + if (options.closurePass && options.locale != null) { + additionalReplacements.put(CLOSURE_LOCALE_CONSTANT_NAME, + IR.string(options.locale)); + } + + return additionalReplacements; + } + + final PassFactory printNameReferenceGraph = + new PassFactory("printNameReferenceGraph", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new CompilerPass() { + @Override + public void process(Node externs, Node jsRoot) { + NameReferenceGraphConstruction gc = + new NameReferenceGraphConstruction(compiler); + gc.process(externs, jsRoot); + String graphFileName = options.nameReferenceGraphPath; + try { + Files.write(DotFormatter.toDot(gc.getNameReferenceGraph()), + new File(graphFileName), + Charsets.UTF_8); + } catch (IOException e) { + compiler.report( + JSError.make( + NAME_REF_GRAPH_FILE_ERROR, e.getMessage(), graphFileName)); + } + } + }; + } + }; + + final PassFactory printNameReferenceReport = + new PassFactory("printNameReferenceReport", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new CompilerPass() { + @Override + public void process(Node externs, Node jsRoot) { + NameReferenceGraphConstruction gc = + new NameReferenceGraphConstruction(compiler); + String reportFileName = options.nameReferenceReportPath; + try { + NameReferenceGraphReport report = + new NameReferenceGraphReport(gc.getNameReferenceGraph()); + Files.write(report.getHtmlReport(), + new File(reportFileName), + Charsets.UTF_8); + } catch (IOException e) { + compiler.report( + JSError.make( + NAME_REF_REPORT_FILE_ERROR, + e.getMessage(), + reportFileName)); + } + } + }; + } + }; + + /** + * A pass-factory that is good for {@code HotSwapCompilerPass} passes. + */ + abstract static class HotSwapPassFactory extends PassFactory { + + HotSwapPassFactory(String name, boolean isOneTimePass) { + super(name, isOneTimePass); + } + + @Override + protected abstract HotSwapCompilerPass create(AbstractCompiler compiler); + + @Override + HotSwapCompilerPass getHotSwapPass(AbstractCompiler compiler) { + return this.create(compiler); + } + } + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DefinitionProvider.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DefinitionProvider.java new file mode 100644 index 0000000..6e5ea57 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DefinitionProvider.java @@ -0,0 +1,38 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.DefinitionsRemover.Definition; +import com.google.javascript.rhino.Node; + +import java.util.Collection; + +/** + * Maps variable uses sites to variable definition sites. + * + */ +interface DefinitionProvider { + /** + * Returns a collection of definitions that characterize the + * possible values of a variable or property. If information is + * unavailable or incomplete, return null. This function should + * never return an empty collection. + * + * @return non-empty definition collection, or null. + */ + Collection getDefinitionsReferencedAt(Node useSite); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DefinitionSite.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DefinitionSite.java new file mode 100644 index 0000000..7b88a96 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DefinitionSite.java @@ -0,0 +1,47 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.DefinitionsRemover.Definition; +import com.google.javascript.rhino.Node; + +/** + * Information about the context in which a Definition appears. + * Includes the definition node, and context in which the definition + * occurs - including the definition module. + * + */ + +class DefinitionSite { + final Node node; + final Definition definition; + final JSModule module; + final boolean inGlobalScope; + final boolean inExterns; + + DefinitionSite(Node node, + Definition definition, + JSModule module, + boolean inGlobalScope, + boolean inExterns) { + this.node = node; + this.definition = definition; + this.module = module; + this.inGlobalScope = inGlobalScope; + this.inExterns = inExterns; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DefinitionsRemover.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DefinitionsRemover.java new file mode 100644 index 0000000..adf49a7 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DefinitionsRemover.java @@ -0,0 +1,401 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Set; + +/** + * Models an assignment that defines a variable and the removal of it. + * + */ +class DefinitionsRemover { + + /** + * @return an {@link Definition} object if the node contains a definition or + * {@code null} otherwise. + */ + static Definition getDefinition(Node n, boolean isExtern) { + // TODO(user): Since we have parent pointers handy. A lot of constructors + // can be simplified. + + // This logic must match #isDefinitionNode + Node parent = n.getParent(); + if (parent == null) { + return null; + } + + if (NodeUtil.isVarDeclaration(n) && n.hasChildren()) { + return new VarDefinition(n, isExtern); + } else if (parent.isFunction() && parent.getFirstChild() == n) { + if (!NodeUtil.isFunctionExpression(parent)) { + return new NamedFunctionDefinition(parent, isExtern); + } else if (!n.getString().equals("")) { + return new FunctionExpressionDefinition(parent, isExtern); + } + } else if (parent.isAssign() && parent.getFirstChild() == n) { + return new AssignmentDefinition(parent, isExtern); + } else if (NodeUtil.isObjectLitKey(n, parent)) { + return new ObjectLiteralPropertyDefinition(parent, n, n.getFirstChild(), + isExtern); + } else if (parent.isParamList()) { + Node function = parent.getParent(); + return new FunctionArgumentDefinition(function, n, isExtern); + } + return null; + } + + /** + * @return Whether a definition object can be created. + */ + static boolean isDefinitionNode(Node n) { + // This logic must match #getDefinition + Node parent = n.getParent(); + if (parent == null) { + return false; + } + + if (NodeUtil.isVarDeclaration(n) && n.hasChildren()) { + return true; + } else if (parent.isFunction() && parent.getFirstChild() == n) { + if (!NodeUtil.isFunctionExpression(parent)) { + return true; + } else if (!n.getString().equals("")) { + return true; + } + } else if (parent.isAssign() && parent.getFirstChild() == n) { + return true; + } else if (NodeUtil.isObjectLitKey(n, parent)) { + return true; + } else if (parent.isParamList()) { + return true; + } + return false; + } + + + static abstract class Definition { + + private final boolean isExtern; + + Definition(boolean isExtern) { + this.isExtern = isExtern; + } + + /** + * Removes this definition from the AST if it is not an extern. + * + * This method should not be called on a definition for which isExtern() + * is true. + */ + public void remove() { + if (!isExtern) { + performRemove(); + } else { + throw new IllegalStateException("Attempt to remove() an extern" + + " definition."); + } + } + + /** + * Subclasses should override to remove the definition from the AST. + */ + protected abstract void performRemove(); + + /** + * Variable or property name represented by this definition. + * For example, in the case of assignments this method would + * return the NAME, GETPROP or GETELEM expression that acts as the + * assignment left hand side. + * + * @return the L-Value associated with this definition. + * The node's type is always NAME, GETPROP or GETELEM. + */ + public abstract Node getLValue(); + + /** + * Value expression that acts as the right hand side of the + * definition statement. + */ + public abstract Node getRValue(); + + /** + * Returns true if the definition is an extern. + */ + public boolean isExtern() { + return isExtern; + } + } + + /** + * Represents an name-only external definition. The definition's + * RHS is missing. + */ + abstract static class IncompleteDefinition extends Definition { + private static final Set ALLOWED_TYPES = + ImmutableSet.of(Token.NAME, Token.GETPROP, Token.GETELEM); + private final Node lValue; + + IncompleteDefinition(Node lValue, boolean inExterns) { + super(inExterns); + Preconditions.checkNotNull(lValue); + Preconditions.checkArgument(ALLOWED_TYPES.contains(lValue.getType()), + "Unexpected lValue type %s", Token.name(lValue.getType())); + this.lValue = lValue; + } + + @Override + public Node getLValue() { + return lValue; + } + + @Override + public Node getRValue() { + return null; + } + } + + /** + * Represents an unknown definition. + */ + static final class UnknownDefinition extends IncompleteDefinition { + UnknownDefinition(Node lValue, boolean inExterns) { + super(lValue, inExterns); + } + + @Override + public void performRemove() { + throw new IllegalArgumentException("Can't remove an UnknownDefinition"); + } + } + + /** + * Represents an name-only external definition. The definition's + * RHS is missing. + */ + static final class ExternalNameOnlyDefinition extends IncompleteDefinition { + + ExternalNameOnlyDefinition(Node lValue) { + super(lValue, true); + } + + @Override + public void performRemove() { + throw new IllegalArgumentException( + "Can't remove external name-only definition"); + } + } + + /** + * Represents a function formal parameter. The definition's RHS is missing. + */ + static final class FunctionArgumentDefinition extends IncompleteDefinition { + FunctionArgumentDefinition(Node function, + Node argumentName, + boolean inExterns) { + super(argumentName, inExterns); + Preconditions.checkArgument(function.isFunction()); + Preconditions.checkArgument(argumentName.isName()); + } + + @Override + public void performRemove() { + throw new IllegalArgumentException( + "Can't remove a FunctionArgumentDefinition"); + } + } + + /** + * Represents a function declaration or function expression. + */ + abstract static class FunctionDefinition extends Definition { + + protected final Node function; + + FunctionDefinition(Node node, boolean inExterns) { + super(inExterns); + Preconditions.checkArgument(node.isFunction()); + function = node; + } + + @Override + public Node getLValue() { + return function.getFirstChild(); + } + + @Override + public Node getRValue() { + return function; + } + } + + /** + * Represents a function declaration without assignment node such as + * {@code function foo()}. + */ + static final class NamedFunctionDefinition extends FunctionDefinition { + NamedFunctionDefinition(Node node, boolean inExterns) { + super(node, inExterns); + } + + @Override + public void performRemove() { + function.detachFromParent(); + } + } + + /** + * Represents a function expression that acts as a RHS. The defined + * name is only reachable from within the function. + */ + static final class FunctionExpressionDefinition extends FunctionDefinition { + FunctionExpressionDefinition(Node node, boolean inExterns) { + super(node, inExterns); + Preconditions.checkArgument( + NodeUtil.isFunctionExpression(node)); + } + + @Override + public void performRemove() { + // replace internal name with "" + function.replaceChild(function.getFirstChild(), IR.name("")); + } + } + + /** + * Represents a declaration within an assignment. + */ + static final class AssignmentDefinition extends Definition { + private final Node assignment; + + AssignmentDefinition(Node node, boolean inExterns) { + super(inExterns); + Preconditions.checkArgument(node.isAssign()); + assignment = node; + } + + @Override + public void performRemove() { + // A simple assignment. foo = bar() -> bar(); + Node parent = assignment.getParent(); + Node last = assignment.getLastChild(); + assignment.removeChild(last); + parent.replaceChild(assignment, last); + } + + @Override + public Node getLValue() { + return assignment.getFirstChild(); + } + + @Override + public Node getRValue() { + return assignment.getLastChild(); + } + } + + /** + * Represents member declarations using a object literal. + * Example: var x = { e : function() { } }; + */ + static final class ObjectLiteralPropertyDefinition extends Definition { + + private final Node literal; + private final Node name; + private final Node value; + + ObjectLiteralPropertyDefinition(Node lit, Node name, Node value, + boolean isExtern) { + super(isExtern); + + this.literal = lit; + this.name = name; + this.value = value; + } + + @Override + public void performRemove() { + literal.removeChild(name); + } + + @Override + public Node getLValue() { + // TODO(user) revisit: object literal definitions are an example + // of definitions whose LHS doesn't correspond to a node that + // exists in the AST. We will have to change the return type of + // getLValue sooner or later in order to provide this added + // flexibility. + + switch (name.getType()) { + case Token.SETTER_DEF: + case Token.GETTER_DEF: + case Token.STRING_KEY: + // TODO(johnlenz): return a GETELEM for quoted strings. + return IR.getprop( + IR.objectlit(), + IR.string(name.getString())); + default: + throw new IllegalStateException("unexpected"); + } + } + + @Override + public Node getRValue() { + return value; + } + } + + /** + * Represents a VAR declaration with an assignment. + */ + static final class VarDefinition extends Definition { + private final Node name; + VarDefinition(Node node, boolean inExterns) { + super(inExterns); + Preconditions.checkArgument(NodeUtil.isVarDeclaration(node)); + Preconditions.checkArgument(node.hasChildren(), + "VAR Declaration of %sshould be assigned a value.", node.getString()); + name = node; + } + + @Override + public void performRemove() { + Node var = name.getParent(); + Preconditions.checkState(var.getFirstChild() == var.getLastChild(), + "AST should be normalized first"); + Node parent = var.getParent(); + Node rValue = name.removeFirstChild(); + Preconditions.checkState(!parent.isFor()); + parent.replaceChild(var, NodeUtil.newExpr(rValue)); + } + + @Override + public Node getLValue() { + return name; + } + + @Override + public Node getRValue() { + return name.getFirstChild(); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Denormalize.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Denormalize.java new file mode 100644 index 0000000..ca4c0f4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Denormalize.java @@ -0,0 +1,151 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * The goal with this pass is to reverse the simplifications done in the + * normalization pass that are not handled by other passes (such as + * CollapseVariableDeclarations) to avoid making the resulting code larger. + * + * Currently this pass only does one thing pushing statements into for-loop + * initializer. This: + * var a = 0; for(;a<0;a++) {} + * becomes: + * for(var a = 0;a<0;a++) {} + * + * @author johnlenz@google.com (johnlenz) + */ +class Denormalize implements CompilerPass, Callback { + + private final AbstractCompiler compiler; + + Denormalize(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + maybeCollapseIntoForStatements(n, parent); + } + + /** + * Collapse VARs and EXPR_RESULT node into FOR loop initializers where + * possible. + */ + private void maybeCollapseIntoForStatements(Node n, Node parent) { + // Only SCRIPT, BLOCK, and LABELs can have FORs that can be collapsed into. + // LABELs are not supported here. + if (parent == null || !NodeUtil.isStatementBlock(parent)) { + return; + } + + // Is the current node something that can be in a for loop initializer? + if (!n.isExprResult() && !n.isVar()) { + return; + } + + // Is the next statement a valid FOR? + Node nextSibling = n.getNext(); + if (nextSibling == null) { + return; + } else if (NodeUtil.isForIn(nextSibling)) { + Node forNode = nextSibling; + Node forVar = forNode.getFirstChild(); + if (forVar.isName() + && n.isVar() && n.hasOneChild()) { + Node name = n.getFirstChild(); + if (!name.hasChildren() + && forVar.getString().equals(name.getString())) { + // OK, the names match, and the var declaration does not have an + // initializer. Move it into the loop. + parent.removeChild(n); + forNode.replaceChild(forVar, n); + compiler.reportCodeChange(); + } + } + } else if (nextSibling.isFor() + && nextSibling.getFirstChild().isEmpty()) { + + // Does the current node contain an in operator? If so, embedding + // the expression in a for loop can cause some JavaScript parsers (such + // as the PlayStation 3's browser based on Access's NetFront + // browser) to fail to parse the code. + // See bug 1778863 for details. + if (NodeUtil.containsType(n, Token.IN)) { + return; + } + + // Move the current node into the FOR loop initializer. + Node forNode = nextSibling; + Node oldInitializer = forNode.getFirstChild(); + parent.removeChild(n); + + Node newInitializer; + if (n.isVar()) { + newInitializer = n; + } else { + // Extract the expression from EXPR_RESULT node. + Preconditions.checkState(n.hasOneChild()); + newInitializer = n.getFirstChild(); + n.removeChild(newInitializer); + } + + forNode.replaceChild(oldInitializer, newInitializer); + + compiler.reportCodeChange(); + } + } + + static class StripConstantAnnotations + extends AbstractPostOrderCallback + implements CompilerPass { + private AbstractCompiler compiler; + + StripConstantAnnotations(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node js) { + NodeTraversal.traverse(compiler, externs, this); + NodeTraversal.traverse(compiler, js, this); + } + + @Override + public void visit(NodeTraversal t, Node node, Node parent) { + if (node.isName() || node.isString() || node.isStringKey()) { + node.removeProp(Node.IS_CONSTANT_NAME); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DependencyOptions.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DependencyOptions.java new file mode 100644 index 0000000..06ab0eb --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DependencyOptions.java @@ -0,0 +1,138 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Sets; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Set; + +/** + * Options for how to manage dependencies between input files. + * + * Dependency information is usually pulled out from the JS code by + * looking for primitive dependency functions (like Closure Library's + * goog.provide/goog.require). Analysis of this dependency information is + * controlled by {@code CodingConvention}, which lets you define those + * dependency primitives. + * + * This options class determines how we use that dependency information + * to change how code is built. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class DependencyOptions implements Serializable { + private static final long serialVersionUID = 1L; + + private boolean sortDependencies = false; + private boolean pruneDependencies = false; + private boolean dropMoochers = false; + private final Set entryPoints = Sets.newHashSet(); + + /** + * Enables or disables dependency sorting mode. + * + * If true, we will sort the input files based on dependency information + * in them. Otherwise, we will use the order of files specified + * on the command-line. + * @return this for easy building. + */ + public DependencyOptions setDependencySorting(boolean enabled) { + this.sortDependencies = enabled; + return this; + } + + /** + * Enables or disables dependency pruning mode. + * + * In dependency pruning mode, we will look for all files that provide a + * symbol. Unless that file is a transitive dependency of a file that + * we're using, we will remove it from the compilation job. + * + * This does not affect how we handle files that do not provide symbols. + * See setMoocherDropping for information on how these are handled. + * + * @return this for easy chaining. + */ + public DependencyOptions setDependencyPruning(boolean enabled) { + this.pruneDependencies = enabled; + return this; + } + + /** + * Enables or disables moocher dropping mode. + * + * A 'moocher' is a file that does not provide any symbols (though they + * may require symbols). This is usually because they don't want to + * tie themselves to a particular dependency system (e.g., Closure's + * goog.provide, CommonJS modules). So they rely on other people to + * manage dependencies on them. + * + * If true, we drop these files when we prune dependencies. + * If false, we always keep these files an anything they depend on. + * The default is false. + * + * Notice that this option only makes sense if dependency pruning is on, + * and a set of entry points is specified. + * + * @return this for easy chaining. + */ + public DependencyOptions setMoocherDropping(boolean enabled) { + this.dropMoochers = enabled; + return this; + } + + /** + * Adds a collection of symbols to always keep. + * + * In dependency pruning mode, we will automatically keep all the + * transitive dependencies of these symbols. + * + * The syntactic form of a symbol depends on the type of dependency + * primitives we're using. For example, goog.provide('foo.bar') + * provides the symbol 'foo.bar'. + * + * @return this for easy chaining. + */ + public DependencyOptions setEntryPoints(Collection symbols) { + entryPoints.clear(); + entryPoints.addAll(symbols); + return this; + } + + /** Returns whether re-ordering of files is needed. */ + boolean needsManagement() { + return sortDependencies || pruneDependencies; + } + + boolean shouldSortDependencies() { + return sortDependencies; + } + + boolean shouldPruneDependencies() { + return pruneDependencies; + } + + boolean shouldDropMoochers() { + return pruneDependencies && dropMoochers; + } + + Collection getEntryPoints() { + return entryPoints; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DevirtualizePrototypeMethods.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DevirtualizePrototypeMethods.java new file mode 100644 index 0000000..de2b0d4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DevirtualizePrototypeMethods.java @@ -0,0 +1,458 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.DefinitionsRemover.Definition; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; + +import java.util.Collection; +import java.util.List; + +/** + * Rewrites prototyped methods calls as static calls that take "this" + * as their first argument. This transformation simplifies the call + * graph so smart name removal, cross module code motion and other + * passes can do more. + * + *

      This pass should only be used in production code if property + * and variable renaming are turned on. Resulting code may also + * benefit from --collapse_anonymous_functions and + * --collapse_variable_declarations + * + *

      This pass only rewrites functions that are part of an objects + * prototype. Functions that access the "arguments" variable + * arguments object are not eligible for this optimization. + * + *

      For example: + *

      + *     A.prototype.accumulate = function(value) {
      + *       this.total += value; return this.total
      + *     }
      + *     var total = a.accumulate(2)
      + * 
      + * + *

      will be rewritten as: + * + *

      + *     var accumulate = function(self, value) {
      + *       self.total += value; return self.total
      + *     }
      + *     var total = accumulate(a, 2)
      + * 
      + * + */ +class DevirtualizePrototypeMethods + implements OptimizeCalls.CallGraphCompilerPass, + SpecializationAwareCompilerPass { + private final AbstractCompiler compiler; + private SpecializeModule.SpecializationState specializationState; + + DevirtualizePrototypeMethods(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void enableSpecialization(SpecializeModule.SpecializationState state) { + this.specializationState = state; + } + + @Override + public void process(Node externs, Node root) { + SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); + defFinder.process(externs, root); + process(externs, root, defFinder); + } + + @Override + public void process( + Node externs, Node root, SimpleDefinitionFinder definitions) { + for (DefinitionSite defSite : definitions.getDefinitionSites()) { + rewriteDefinitionIfEligible(defSite, definitions); + } + } + + /** + * Determines if the name node acts as the function name in a call expression. + */ + private static boolean isCall(UseSite site) { + Node node = site.node; + Node parent = node.getParent(); + return (parent.getFirstChild() == node) && parent.isCall(); + } + + /** + * Determines if the current node is a function prototype definition. + */ + private static boolean isPrototypeMethodDefinition(Node node) { + Node parent = node.getParent(); + if (parent == null) { + return false; + } + Node gramp = parent.getParent(); + if (gramp == null) { + return false; + } + + if (node.isGetProp()) { + if (parent.getFirstChild() != node) { + return false; + } + + if (!NodeUtil.isExprAssign(gramp)) { + return false; + } + + Node functionNode = parent.getLastChild(); + if ((functionNode == null) || !functionNode.isFunction()) { + return false; + } + + Node nameNode = node.getFirstChild(); + return nameNode.isGetProp() && + nameNode.getLastChild().getString().equals("prototype"); + } else if (node.isStringKey()) { + Preconditions.checkState(parent.isObjectLit()); + + if (!gramp.isAssign()) { + return false; + } + + if (gramp.getLastChild() != parent) { + return false; + } + + Node greatGramp = gramp.getParent(); + if (greatGramp == null || !greatGramp.isExprResult()) { + return false; + } + + Node functionNode = node.getFirstChild(); + if ((functionNode == null) || !functionNode.isFunction()) { + return false; + } + + Node target = gramp.getFirstChild(); + return target.isGetProp() && + target.getLastChild().getString().equals("prototype"); + } else { + return false; + } + } + + private String getMethodName(Node node) { + if (node.isGetProp()) { + return node.getLastChild().getString(); + } else if (node.isStringKey()) { + return node.getString(); + } else { + throw new IllegalStateException("unexpected"); + } + } + + /** + * @returns The new name for a rewritten method. + */ + private String getRewrittenMethodName(String originalMethodName) { + return "JSCompiler_StaticMethods_" + originalMethodName; + } + + /** + * Rewrites method definition and call sites if the method is + * defined in the global scope exactly once. + * + * Definition and use site information is provided by the + * {@link SimpleDefinitionFinder} passed in as an argument. + * + * @param defSite definition site to process. + * @param defFinder structure that hold Node -> Definition and + * Definition -> [UseSite] maps. + */ + private void rewriteDefinitionIfEligible(DefinitionSite defSite, + SimpleDefinitionFinder defFinder) { + if (defSite.inExterns || + !defSite.inGlobalScope || + !isEligibleDefinition(defFinder, defSite)) { + return; + } + + Node node = defSite.node; + if (!isPrototypeMethodDefinition(node)) { + return; + } + + for (Node ancestor = node.getParent(); + ancestor != null; + ancestor = ancestor.getParent()) { + if (NodeUtil.isControlStructure(ancestor)) { + return; + } + } + + // TODO(user) The code only works if there is a single definition + // associated with a property name. Once this pass starts using + // the NameReferenceGraph to disambiguate call sites, it will be + // necessary to consider type information when generating static + // method names and/or append unique ids to duplicate static + // method names. + // Whatever scheme we use should not break stable renaming. + String newMethodName = getRewrittenMethodName( + getMethodName(node)); + rewriteDefinition(node, newMethodName); + rewriteCallSites(defFinder, defSite.definition, newMethodName); + } + + /** + * Determines if a method definition is eligible for rewrite as a + * global function. In order to be eligible for rewrite, the + * definition must: + * + * - Refer to a function that takes a fixed number of arguments. + * - Function must not be exported. + * - Function must be used at least once. + * - Property is never accessed outside a function call context. + * - The definition under consideration must be the only possible + * choice at each call site. + * - Definition must happen in a module loaded before the first use. + */ + private boolean isEligibleDefinition(SimpleDefinitionFinder defFinder, + DefinitionSite definitionSite) { + + Definition definition = definitionSite.definition; + JSModule definitionModule = definitionSite.module; + + // Only functions may be rewritten. + // Functions that access "arguments" are not eligible since + // rewrite changes the structure of this object. + Node rValue = definition.getRValue(); + if (rValue == null || + !rValue.isFunction() || + NodeUtil.isVarArgsFunction(rValue)) { + return false; + } + + // Exporting a method prevents rewrite. + Node lValue = definition.getLValue(); + if ((lValue == null) || + !lValue.isGetProp()) { + return false; + } + CodingConvention codingConvention = compiler.getCodingConvention(); + if (codingConvention.isExported(lValue.getLastChild().getString())) { + return false; + } + + Collection useSites = defFinder.getUseSites(definition); + + // Rewriting unused methods is not sound. + if (useSites.isEmpty()) { + return false; + } + + JSModuleGraph moduleGraph = compiler.getModuleGraph(); + + for (UseSite site : useSites) { + // Accessing the property directly prevents rewrite. + if (!isCall(site)) { + return false; + } + + Node nameNode = site.node; + + // Don't rewrite methods called in functions that can't be specialized + // if we are specializing + if (specializationState != null && + !specializationState.canFixupSpecializedFunctionContainingNode( + nameNode)) { + return false; + } + + // Multiple definitions prevent rewrite. + Collection singleSiteDefinitions = + defFinder.getDefinitionsReferencedAt(nameNode); + if (singleSiteDefinitions.size() > 1) { + return false; + } + Preconditions.checkState(!singleSiteDefinitions.isEmpty()); + Preconditions.checkState(singleSiteDefinitions.contains(definition)); + + // Accessing the property in a module loaded before the + // definition module prevents rewrite; accessing a variable + // before definition results in a parse error. + JSModule callModule = site.module; + if ((definitionModule != callModule) && + ((callModule == null) || + !moduleGraph.dependsOn(callModule, definitionModule))) { + return false; + } + } + + return true; + } + + /** + * Rewrites object method call sites as calls to global functions + * that take "this" as their first argument. + * + * Before: + * o.foo(a, b, c) + * + * After: + * foo(o, a, b, c) + */ + private void rewriteCallSites(SimpleDefinitionFinder defFinder, + Definition definition, + String newMethodName) { + Collection useSites = defFinder.getUseSites(definition); + for (UseSite site : useSites) { + Node node = site.node; + Node parent = node.getParent(); + + Node objectNode = node.getFirstChild(); + node.removeChild(objectNode); + parent.replaceChild(node, objectNode); + parent.addChildToFront(IR.name(newMethodName).srcref(node)); + Preconditions.checkState(parent.isCall()); + parent.putBooleanProp(Node.FREE_CALL, true); + compiler.reportCodeChange(); + + if (specializationState != null) { + specializationState.reportSpecializedFunctionContainingNode(parent); + } + } + } + + /** + * Rewrites method definitions as global functions that take "this" + * as their first argument. + * + * Before: + * a.prototype.b = function(a, b, c) {...} + * + * After: + * var b = function(self, a, b, c) {...} + */ + private void rewriteDefinition(Node node, String newMethodName) { + boolean isObjLitDefKey = node.isStringKey(); + + Node parent = node.getParent(); + + Node refNode = isObjLitDefKey ? node : parent.getFirstChild(); + Node newNameNode = IR.name(newMethodName).copyInformationFrom(refNode); + Node newVarNode = IR.var(newNameNode).copyInformationFrom(refNode); + + Node functionNode; + if (!isObjLitDefKey) { + Preconditions.checkState(parent.isAssign()); + functionNode = parent.getLastChild(); + Node expr = parent.getParent(); + Node block = expr.getParent(); + parent.removeChild(functionNode); + newNameNode.addChildToFront(functionNode); + block.replaceChild(expr, newVarNode); + + if (specializationState != null) { + specializationState.reportRemovedFunction(functionNode, block); + } + } else { + Preconditions.checkState(parent.isObjectLit()); + functionNode = node.getFirstChild(); + Node assign = parent.getParent(); + Node expr = assign.getParent(); + Node block = expr.getParent(); + + node.removeChild(functionNode); + parent.removeChild(node); + newNameNode.addChildToFront(functionNode); + block.addChildAfter(newVarNode, expr); + + if (specializationState != null) { + specializationState.reportRemovedFunction(functionNode, block); + } + } + + // add extra argument + String self = newMethodName + "$self"; + Node argList = functionNode.getFirstChild().getNext(); + argList.addChildToFront(IR.name(self) + .copyInformationFrom(functionNode)); + + // rewrite body + Node body = functionNode.getLastChild(); + replaceReferencesToThis(body, self); + + // fix type + fixFunctionType(functionNode); + + compiler.reportCodeChange(); + } + + /** + * Creates a new JSType based on the original function type by + * adding the original this pointer type to the beginning of the + * argument type list and replacing the this pointer type with + * NO_TYPE. + */ + private void fixFunctionType(Node functionNode) { + FunctionType type = JSType.toMaybeFunctionType(functionNode.getJSType()); + if (type != null) { + JSTypeRegistry typeRegistry = compiler.getTypeRegistry(); + + List parameterTypes = Lists.newArrayList(); + parameterTypes.add(type.getTypeOfThis()); + + for (Node param : type.getParameters()) { + parameterTypes.add(param.getJSType()); + } + + ObjectType thisType = + typeRegistry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); + JSType returnType = type.getReturnType(); + + JSType newType = typeRegistry.createFunctionType( + thisType, returnType, parameterTypes); + functionNode.setJSType(newType); + } + } + + /** + * Replaces references to "this" with references to name. Do not + * traverse function boundaries. + */ + private void replaceReferencesToThis(Node node, String name) { + if (node.isFunction()) { + return; + } + + for (Node child : node.children()) { + if (child.isThis()) { + Node newName = IR.name(name); + newName.setJSType(child.getJSType()); + node.replaceChild(child, newName); + } else { + replaceReferencesToThis(child, name); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DiagnosticGroup.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DiagnosticGroup.java new file mode 100644 index 0000000..c03fac3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DiagnosticGroup.java @@ -0,0 +1,136 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; + +/** + * Group a set of related diagnostic types together, so that they can + * be toggled on and off as one unit. + * @author nicksantos@google.com (Nick Santos) + */ +public class DiagnosticGroup implements Serializable { + private static final long serialVersionUID = 1; + + // The set of types represented by this group, hashed by key. + private final Set types; + + // A human-readable name for the group. + private final String name; + + /** + * Create a group that matches all errors of the given types. + */ + DiagnosticGroup(String name, DiagnosticType ...types) { + this.name = name; + this.types = ImmutableSet.copyOf(Arrays.asList(types)); + } + + /** + * Create a group that matches all errors of the given types. + */ + public DiagnosticGroup(DiagnosticType ...types) { + this(null, types); + } + + /** + * Create a diagnostic group with no name that only matches the given type. + */ + private DiagnosticGroup(DiagnosticType type) { + this.name = null; + this.types = ImmutableSet.of(type); + } + + // DiagnosticGroups with only a single DiagnosticType. + private static final Map singletons = + Maps.newHashMap(); + + /** Create a diagnostic group that matches only the given type. */ + public static DiagnosticGroup forType(DiagnosticType type) { + if (!singletons.containsKey(type)) { + singletons.put(type, new DiagnosticGroup(type)); + } + return singletons.get(type); + } + + /** + * Create a composite group. + */ + public DiagnosticGroup(DiagnosticGroup ...groups) { + this(null, groups); + } + + /** + * Create a composite group. + */ + public DiagnosticGroup(String name, DiagnosticGroup ...groups) { + Set set = Sets.newHashSet(); + + for (DiagnosticGroup group : groups) { + set.addAll(group.types); + } + + this.name = name; + this.types = ImmutableSet.copyOf(set); + } + + /** + * Returns whether the given error's type matches a type + * in this group. + */ + public boolean matches(JSError error) { + return matches(error.getType()); + } + + /** + * Returns whether the given type matches a type in this group. + */ + public boolean matches(DiagnosticType type) { + return types.contains(type); + } + + /** + * Returns whether all of the types in the given group are in this group. + */ + boolean isSubGroup(DiagnosticGroup group) { + for (DiagnosticType type : group.types) { + if (!matches(type)) { + return false; + } + } + return true; + } + + /** + * Returns an iterable over all the types in this group. + */ + public Iterable getTypes() { + return types; + } + + @Override + public String toString() { + return name == null ? super.toString() : "DiagnosticGroup<" + name + ">"; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DiagnosticGroupWarningsGuard.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DiagnosticGroupWarningsGuard.java new file mode 100644 index 0000000..a4d762e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DiagnosticGroupWarningsGuard.java @@ -0,0 +1,65 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.CheckLevel; + + +/** + * Sets the level for a particular DiagnosticGroup. + * @author nicksantos@google.com (Nick Santos) + */ +public class DiagnosticGroupWarningsGuard extends WarningsGuard { + private static final long serialVersionUID = 1L; + + final DiagnosticGroup group; + final CheckLevel level; + + public DiagnosticGroupWarningsGuard( + DiagnosticGroup group, CheckLevel level) { + this.group = group; + this.level = level; + } + + @Override + public CheckLevel level(JSError error) { + return group.matches(error) ? level : null; + } + + @Override + public boolean disables(DiagnosticGroup otherGroup) { + return !level.isOn() && group.isSubGroup(otherGroup); + } + + @Override + public boolean enables(DiagnosticGroup otherGroup) { + if (level.isOn()) { + for (DiagnosticType type : otherGroup.getTypes()) { + if (group.matches(type)) { + return true; + } + } + } + + return false; + } + + @Override + public String toString() { + return group + "(" + level + ")"; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DiagnosticGroups.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DiagnosticGroups.java new file mode 100644 index 0000000..2eeac0b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DiagnosticGroups.java @@ -0,0 +1,256 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +import java.util.Map; + +/** + * Named groups of DiagnosticTypes exposed by Compiler. + * @author nicksantos@google.com (Nick Santos) + */ +public class DiagnosticGroups { + static final DiagnosticType UNUSED = + DiagnosticType.warning("JSC_UNUSED", "{0}"); + + public DiagnosticGroups() {} + + private static final Map groupsByName = + Maps.newHashMap(); + + static DiagnosticGroup registerDeprecatedGroup(String name) { + return registerGroup(name, new DiagnosticGroup(name, UNUSED)); + } + + static DiagnosticGroup registerGroup(String name, + DiagnosticGroup group) { + groupsByName.put(name, group); + return group; + } + + static DiagnosticGroup registerGroup(String name, + DiagnosticType ... types) { + DiagnosticGroup group = new DiagnosticGroup(name, types); + groupsByName.put(name, group); + return group; + } + + static DiagnosticGroup registerGroup(String name, + DiagnosticGroup ... groups) { + DiagnosticGroup group = new DiagnosticGroup(name, groups); + groupsByName.put(name, group); + return group; + } + + /** Get the registered diagnostic groups, indexed by name. */ + protected Map getRegisteredGroups() { + return ImmutableMap.copyOf(groupsByName); + } + + /** Find the diagnostic group registered under the given name. */ + public DiagnosticGroup forName(String name) { + return groupsByName.get(name); + } + + // A bit of a hack to display the available groups on the command-line. + // New groups should be added to this list if they are public and should + // be listed on the command-line as an available option. + // + // If a group is suppressible on a per-file basis, it should be added + // to parser/ParserConfig.properties + static final String DIAGNOSTIC_GROUP_NAMES = + "accessControls, ambiguousFunctionDecl, cast, checkRegExp, " + + "checkTypes, checkVars, const, constantProperty, deprecated, " + + "duplicateMessage, " + + "es5Strict, externsValidation, fileoverviewTags, globalThis, " + + "internetExplorerChecks, invalidCasts, misplacedTypeAnnotation, " + + "missingProperties, " + + "nonStandardJsDocs, suspiciousCode, strictModuleDepCheck, " + + "typeInvalidation, " + + "undefinedNames, undefinedVars, unknownDefines, uselessCode, " + + "visibility"; + + public static final DiagnosticGroup GLOBAL_THIS = + DiagnosticGroups.registerGroup("globalThis", + CheckGlobalThis.GLOBAL_THIS); + + public static final DiagnosticGroup DEPRECATED = + DiagnosticGroups.registerGroup("deprecated", + CheckAccessControls.DEPRECATED_NAME, + CheckAccessControls.DEPRECATED_NAME_REASON, + CheckAccessControls.DEPRECATED_PROP, + CheckAccessControls.DEPRECATED_PROP_REASON, + CheckAccessControls.DEPRECATED_CLASS, + CheckAccessControls.DEPRECATED_CLASS_REASON); + + public static final DiagnosticGroup VISIBILITY = + DiagnosticGroups.registerGroup("visibility", + CheckAccessControls.BAD_PRIVATE_GLOBAL_ACCESS, + CheckAccessControls.BAD_PRIVATE_PROPERTY_ACCESS, + CheckAccessControls.BAD_PROTECTED_PROPERTY_ACCESS, + CheckAccessControls.PRIVATE_OVERRIDE, + CheckAccessControls.VISIBILITY_MISMATCH); + + public static final DiagnosticGroup CONSTANT_PROPERTY = + DiagnosticGroups.registerGroup("constantProperty", + CheckAccessControls.CONST_PROPERTY_DELETED, + CheckAccessControls.CONST_PROPERTY_REASSIGNED_VALUE); + + public static final DiagnosticGroup NON_STANDARD_JSDOC = + DiagnosticGroups.registerGroup("nonStandardJsDocs", + RhinoErrorReporter.BAD_JSDOC_ANNOTATION); + + public static final DiagnosticGroup ACCESS_CONTROLS = + DiagnosticGroups.registerGroup("accessControls", + DEPRECATED, VISIBILITY); + + public static final DiagnosticGroup INVALID_CASTS = + DiagnosticGroups.registerGroup("invalidCasts", + TypeValidator.INVALID_CAST); + + public static final DiagnosticGroup FILEOVERVIEW_JSDOC = + DiagnosticGroups.registerDeprecatedGroup("fileoverviewTags"); + + public static final DiagnosticGroup STRICT_MODULE_DEP_CHECK = + DiagnosticGroups.registerGroup("strictModuleDepCheck", + VarCheck.STRICT_MODULE_DEP_ERROR, + CheckGlobalNames.STRICT_MODULE_DEP_QNAME); + + public static final DiagnosticGroup VIOLATED_MODULE_DEP = + DiagnosticGroups.registerGroup("violatedModuleDep", + VarCheck.VIOLATED_MODULE_DEP_ERROR); + + public static final DiagnosticGroup EXTERNS_VALIDATION = + DiagnosticGroups.registerGroup("externsValidation", + VarCheck.NAME_REFERENCE_IN_EXTERNS_ERROR, + VarCheck.UNDEFINED_EXTERN_VAR_ERROR); + + public static final DiagnosticGroup AMBIGUOUS_FUNCTION_DECL = + DiagnosticGroups.registerGroup("ambiguousFunctionDecl", + VariableReferenceCheck.AMBIGUOUS_FUNCTION_DECL); + + public static final DiagnosticGroup UNKNOWN_DEFINES = + DiagnosticGroups.registerGroup("unknownDefines", + ProcessDefines.UNKNOWN_DEFINE_WARNING); + + public static final DiagnosticGroup TWEAKS = + DiagnosticGroups.registerGroup("tweakValidation", + ProcessTweaks.INVALID_TWEAK_DEFAULT_VALUE_WARNING, + ProcessTweaks.TWEAK_WRONG_GETTER_TYPE_WARNING, + ProcessTweaks.UNKNOWN_TWEAK_WARNING); + + public static final DiagnosticGroup MISSING_PROPERTIES = + DiagnosticGroups.registerGroup("missingProperties", + TypeCheck.INEXISTENT_PROPERTY); + + public static final DiagnosticGroup INTERNET_EXPLORER_CHECKS = + DiagnosticGroups.registerGroup("internetExplorerChecks", + RhinoErrorReporter.TRAILING_COMMA); + + public static final DiagnosticGroup UNDEFINED_VARIABLES = + DiagnosticGroups.registerGroup("undefinedVars", + VarCheck.UNDEFINED_VAR_ERROR); + + public static final DiagnosticGroup UNDEFINED_NAMES = + DiagnosticGroups.registerGroup("undefinedNames", + CheckGlobalNames.UNDEFINED_NAME_WARNING); + + public static final DiagnosticGroup DEBUGGER_STATEMENT_PRESENT = + DiagnosticGroups.registerGroup("checkDebuggerStatement", + CheckDebuggerStatement.DEBUGGER_STATEMENT_PRESENT); + + public static final DiagnosticGroup CHECK_REGEXP = + DiagnosticGroups.registerGroup("checkRegExp", + CheckRegExp.REGEXP_REFERENCE, + CheckRegExp.MALFORMED_REGEXP); + + public static final DiagnosticGroup CHECK_TYPES = + DiagnosticGroups.registerGroup("checkTypes", + TypeValidator.ALL_DIAGNOSTICS, + TypeCheck.ALL_DIAGNOSTICS); + + public static final DiagnosticGroup CHECK_VARIABLES = + DiagnosticGroups.registerGroup("checkVars", + VarCheck.UNDEFINED_VAR_ERROR, + SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR); + + public static final DiagnosticGroup CHECK_USELESS_CODE = + DiagnosticGroups.registerGroup("uselessCode", + CheckSideEffects.USELESS_CODE_ERROR, + CheckUnreachableCode.UNREACHABLE_CODE); + + public static final DiagnosticGroup CONST = + DiagnosticGroups.registerGroup("const", + CheckAccessControls.CONST_PROPERTY_DELETED, + CheckAccessControls.CONST_PROPERTY_REASSIGNED_VALUE, + ConstCheck.CONST_REASSIGNED_VALUE_ERROR); + + public static final DiagnosticGroup TYPE_INVALIDATION = + DiagnosticGroups.registerGroup("typeInvalidation", + DisambiguateProperties.Warnings.INVALIDATION); + + public static final DiagnosticGroup DUPLICATE_VARS = + DiagnosticGroups.registerGroup("duplicate", + SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR, + TypeValidator.DUP_VAR_DECLARATION); + + public static final DiagnosticGroup ES5_STRICT = + DiagnosticGroups.registerGroup("es5Strict", + ControlStructureCheck.USE_OF_WITH, + StrictModeCheck.UNKNOWN_VARIABLE, + StrictModeCheck.EVAL_DECLARATION, + StrictModeCheck.EVAL_ASSIGNMENT, + StrictModeCheck.ARGUMENTS_DECLARATION, + StrictModeCheck.ARGUMENTS_ASSIGNMENT, + StrictModeCheck.DELETE_VARIABLE, + StrictModeCheck.DUPLICATE_OBJECT_KEY, + StrictModeCheck.BAD_FUNCTION_DECLARATION); + + public static final DiagnosticGroup CHECK_PROVIDES = + DiagnosticGroups.registerGroup("checkProvides", + CheckProvides.MISSING_PROVIDE_WARNING); + + public static final DiagnosticGroup DUPLICATE_MESSAGE = + DiagnosticGroups.registerGroup("duplicateMessage", + JsMessageVisitor.MESSAGE_DUPLICATE_KEY); + + public static final DiagnosticGroup MISPLACED_TYPE_ANNOTATION = + DiagnosticGroups.registerGroup("misplacedTypeAnnotation", + RhinoErrorReporter.MISPLACED_TYPE_ANNOTATION); + + public static final DiagnosticGroup CAST = + DiagnosticGroups.registerGroup("cast", + TypeValidator.INVALID_CAST); + + public static final DiagnosticGroup SUSPICIOUS_CODE = + DiagnosticGroups.registerGroup("suspiciousCode", + CheckSuspiciousCode.SUSPICIOUS_SEMICOLON, + CheckSuspiciousCode.SUSPICIOUS_COMPARISON_WITH_NAN); + + /** + * Adds warning levels by name. + */ + void setWarningLevel(CompilerOptions options, + String name, CheckLevel level) { + DiagnosticGroup group = forName(name); + Preconditions.checkNotNull(group, "No warning class for name: %s", name); + options.setWarningLevel(group, level); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DiagnosticType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DiagnosticType.java new file mode 100644 index 0000000..18b2f8e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DiagnosticType.java @@ -0,0 +1,133 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.CheckLevel; + +import java.io.Serializable; +import java.text.MessageFormat; + +/** + * The type of a compile or analysis error. + * + */ +public class DiagnosticType + implements Comparable, Serializable { + private static final long serialVersionUID = 1; + + /** + * The error type. Used as the BugPattern and BugInstance types by + * BugBot's XML + */ + public final String key; + + /** The default way to format errors */ + public final MessageFormat format; + + /** Default level */ + public final CheckLevel defaultLevel; + + /** Reporting level, initially the defaultLevel but may be changed. */ + public CheckLevel level; + + /** + * Create a DiagnosticType at level CheckLevel.ERROR + * + * @param name An identifier + * @param descriptionFormat A format string + * @return A new DiagnosticType + */ + public static DiagnosticType error(String name, String descriptionFormat) { + return make(name, CheckLevel.ERROR, descriptionFormat); + } + + /** + * Create a DiagnosticType at level CheckLevel.WARNING + * + * @param name An identifier + * @param descriptionFormat A format string + * @return A new DiagnosticType + */ + public static DiagnosticType warning(String name, String descriptionFormat) { + return make(name, CheckLevel.WARNING, descriptionFormat); + } + + /** + * Create a DiagnosticType at level CheckLevel.OFF + * + * @param name An identifier + * @param descriptionFormat A format string + * @return A new DiagnosticType + */ + public static DiagnosticType disabled(String name, + String descriptionFormat) { + return make(name, CheckLevel.OFF, descriptionFormat); + } + + /** + * Create a DiagnosticType at a given CheckLevel. + * + * @param name An identifier + * @param level Either CheckLevel.ERROR or CheckLevel.WARNING + * @param descriptionFormat A format string + * @return A new DiagnosticType + */ + public static DiagnosticType make(String name, CheckLevel level, + String descriptionFormat) { + return + new DiagnosticType(name, level, new MessageFormat(descriptionFormat)); + } + + /** + * Create a DiagnosticType. Private to force use of static factory methods. + */ + private DiagnosticType(String key, CheckLevel level, MessageFormat format) { + this.key = key; + this.defaultLevel = level; + this.format = format; + + this.level = this.defaultLevel; + } + + /** + * Create a description from the MessageFormat and the arguments. + * Used by unit tests. + */ + String format(Object ... arguments) { + return format.format(arguments); + } + + @Override + public boolean equals(Object type) { + return type instanceof DiagnosticType && + ((DiagnosticType) type).key.equals(key); + } + + @Override + public int hashCode() { + return key.hashCode(); + } + + @Override + public int compareTo(DiagnosticType diagnosticType) { + return key.compareTo(diagnosticType.key); + } + + @Override + public String toString() { + return key + ": " + format.toPattern(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DisambiguateProperties.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DisambiguateProperties.java new file mode 100644 index 0000000..c64c178 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DisambiguateProperties.java @@ -0,0 +1,1115 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage; +import com.google.javascript.jscomp.ConcreteType.ConcreteFunctionType; +import com.google.javascript.jscomp.ConcreteType.ConcreteInstanceType; +import com.google.javascript.jscomp.ConcreteType.ConcreteUnionType; +import com.google.javascript.jscomp.ConcreteType.ConcreteUniqueType; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.TypeValidator.TypeMismatch; +import com.google.javascript.jscomp.graph.StandardUnionFind; +import com.google.javascript.jscomp.graph.UnionFind; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.StaticScope; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; +import java.util.logging.Logger; + +/** + * DisambiguateProperties renames properties to disambiguate between unrelated + * fields with the same name. Two properties are considered related if they + * share a definition on their prototype chains, or if they are potentially + * referenced together via union types. + * + *

      Renamimg only occurs if there are two or more distinct properties with + * the same name. + * + *

      This pass allows other passes, such as inlining and code removal to take + * advantage of type information implicitly. + * + *

      + *   Foo.a;
      + *   Bar.a;
      + * 
      + * + *

      will become + * + *

      + *   Foo.a$Foo;
      + *   Bar.a$Bar;
      + * 
      + * + */ +class DisambiguateProperties implements CompilerPass { + // To prevent the logs from filling up, we cap the number of warnings + // that we tell the user to fix per-property. + private static final int MAX_INVALDIATION_WARNINGS_PER_PROPERTY = 10; + + private static final Logger logger = Logger.getLogger( + DisambiguateProperties.class.getName()); + + static class Warnings { + // TODO(user): {1} and {2} are not exactly useful for most people. + static final DiagnosticType INVALIDATION = DiagnosticType.disabled( + "JSC_INVALIDATION", + "Property disambiguator skipping all instances of property {0} " + + "because of type {1} node {2}. {3}"); + } + + private final AbstractCompiler compiler; + private final TypeSystem typeSystem; + + /** + * Map of a type to all the related errors that invalidated the type + * for disambiguation. It has be Object because of the generic nature of + * this pass. + */ + private Multimap invalidationMap; + + /** + * In practice any large code base will have thousands and thousands of + * type invalidations, which makes reporting all of the errors useless. + * However, certain properties are worth specifically guarding because of the + * large amount of code that can be removed as dead code. This list contains + * the properties (eg: "toString") that we care about; if any of these + * properties is invalidated it causes an error. + */ + private final Map propertiesToErrorFor; + + private class Property { + /** The name of the property. */ + final String name; + + /** All types on which the field exists, grouped together if related. */ + private UnionFind types; + + /** + * A set of types for which renaming this field should be skipped. This + * list is first filled by fields defined in the externs file. + */ + Set typesToSkip = Sets.newHashSet(); + + /** + * If true, do not rename any instance of this field, as it has been + * referenced from an unknown type. + */ + boolean skipRenaming; + + /** Set of nodes for this field that need renaming. */ + Set renameNodes = Sets.newHashSet(); + + /** + * Map from node to the highest type in the prototype chain containing the + * field for that node. In the case of a union, the type is the highest type + * of one of the types in the union. + */ + final Map rootTypes = Maps.newHashMap(); + + Property(String name) { + this.name = name; + } + + /** Returns the types on which this field is referenced. */ + UnionFind getTypes() { + if (types == null) { + types = new StandardUnionFind(); + } + return types; + } + + /** + * Record that this property is referenced from this type. + * @return true if the type was recorded for this property, else false, + * which would happen if the type was invalidating. + */ + boolean addType(T type, T top, T relatedType) { + checkState(!skipRenaming, "Attempt to record skipped property: %s", name); + if (typeSystem.isInvalidatingType(top)) { + invalidate(); + return false; + } else { + if (typeSystem.isTypeToSkip(top)) { + addTypeToSkip(top); + } + + if (relatedType == null) { + getTypes().add(top); + } else { + getTypes().union(top, relatedType); + } + typeSystem.recordInterfaces(type, top, this); + return true; + } + } + + /** Records the given type as one to skip for this property. */ + void addTypeToSkip(T type) { + for (T skipType : typeSystem.getTypesToSkipForType(type)) { + typesToSkip.add(skipType); + getTypes().union(skipType, type); + } + } + + /** Invalidates any types related to invalid types. */ + void expandTypesToSkip() { + // If we are not going to rename any properties, then we do not need to + // update the list of invalid types, as they are all invalid. + if (shouldRename()) { + int count = 0; + while (true) { + // It should usually only take one time through this do-while. + checkState(++count < 10, "Stuck in loop expanding types to skip."); + + // Make sure that the representative type for each type to skip is + // marked as being skipped. + Set rootTypesToSkip = Sets.newHashSet(); + for (T subType : typesToSkip) { + rootTypesToSkip.add(types.find(subType)); + } + typesToSkip.addAll(rootTypesToSkip); + + Set newTypesToSkip = Sets.newHashSet(); + Set allTypes = types.elements(); + int originalTypesSize = allTypes.size(); + for (T subType : allTypes) { + if (!typesToSkip.contains(subType) + && typesToSkip.contains(types.find(subType))) { + newTypesToSkip.add(subType); + } + } + + for (T newType : newTypesToSkip) { + addTypeToSkip(newType); + } + + // If there were not any new types added, we are done here. + if (types.elements().size() == originalTypesSize) { + break; + } + } + } + } + + /** Returns true if any instance of this property should be renamed. */ + boolean shouldRename() { + return !skipRenaming && types != null + && types.allEquivalenceClasses().size() > 1; + } + + /** + * Returns true if this property should be renamed on this type. + * expandTypesToSkip() should be called before this, if anything has been + * added to the typesToSkip list. + */ + boolean shouldRename(T type) { + return !skipRenaming && !typesToSkip.contains(type); + } + + /** + * Invalidates a field from renaming. Used for field references on an + * object with unknown type. + */ + boolean invalidate() { + boolean changed = !skipRenaming; + skipRenaming = true; + types = null; + return changed; + } + + /** + * Schedule the node to potentially be renamed. + * @param node the node to rename + * @param type the highest type in the prototype chain for which the + * property is defined + * @return True if type was accepted without invalidation or if the property + * was already invalidated. False if this property was invalidated this + * time. + */ + boolean scheduleRenaming(Node node, T type) { + if (!skipRenaming) { + if (typeSystem.isInvalidatingType(type)) { + invalidate(); + return false; + } + renameNodes.add(node); + rootTypes.put(node, type); + } + return true; + } + } + + private Map properties = Maps.newHashMap(); + + static DisambiguateProperties forJSTypeSystem( + AbstractCompiler compiler, + Map propertiesToErrorFor) { + return new DisambiguateProperties( + compiler, new JSTypeSystem(compiler), propertiesToErrorFor); + } + + static DisambiguateProperties forConcreteTypeSystem( + AbstractCompiler compiler, TightenTypes tt, + Map propertiesToErrorFor) { + return new DisambiguateProperties( + compiler, new ConcreteTypeSystem(tt, compiler.getCodingConvention()), + propertiesToErrorFor); + } + + /** + * This constructor should only be called by one of the helper functions + * above for either the JSType system, or the concrete type system. + */ + private DisambiguateProperties(AbstractCompiler compiler, + TypeSystem typeSystem, Map propertiesToErrorFor) { + this.compiler = compiler; + this.typeSystem = typeSystem; + this.propertiesToErrorFor = propertiesToErrorFor; + if (!this.propertiesToErrorFor.isEmpty()) { + this.invalidationMap = LinkedHashMultimap.create(); + } else { + this.invalidationMap = null; + } + } + + @Override + public void process(Node externs, Node root) { + Preconditions.checkState( + compiler.getLifeCycleStage() == LifeCycleStage.NORMALIZED); + for (TypeMismatch mis : compiler.getTypeValidator().getMismatches()) { + addInvalidatingType(mis.typeA, mis.src); + addInvalidatingType(mis.typeB, mis.src); + } + + StaticScope scope = typeSystem.getRootScope(); + NodeTraversal.traverse(compiler, externs, new FindExternProperties()); + NodeTraversal.traverse(compiler, root, new FindRenameableProperties()); + renameProperties(); + } + + private void recordInvalidationError(JSType t, JSError error) { + if (!t.isObject()) { + return; + } + if (invalidationMap != null) { + invalidationMap.put(t, error); + } + } + + /** + * Invalidates the given type, so that no properties on it will be renamed. + */ + private void addInvalidatingType(JSType type, JSError error) { + type = type.restrictByNotNullOrUndefined(); + if (type.isUnionType()) { + for (JSType alt : type.toMaybeUnionType().getAlternates()) { + addInvalidatingType(alt, error); + } + } else if (type.isEnumElementType()) { + addInvalidatingType( + type.toMaybeEnumElementType().getPrimitiveType(), error); + } else { + typeSystem.addInvalidatingType(type); + recordInvalidationError(type, error); + ObjectType objType = ObjectType.cast(type); + if (objType != null && objType.getImplicitPrototype() != null) { + typeSystem.addInvalidatingType(objType.getImplicitPrototype()); + recordInvalidationError(objType.getImplicitPrototype(), error); + } + } + } + + + /** Returns the property for the given name, creating it if necessary. */ + protected Property getProperty(String name) { + if (!properties.containsKey(name)) { + properties.put(name, new Property(name)); + } + return properties.get(name); + } + + /** Public for testing. */ + T getTypeWithProperty(String field, T type) { + return typeSystem.getTypeWithProperty(field, type); + } + + /** Tracks the current type system scope while traversing. */ + private abstract class AbstractScopingCallback implements ScopedCallback { + protected final Stack> scopes = + new Stack>(); + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + return true; + } + + @Override + public void enterScope(NodeTraversal t) { + if (t.inGlobalScope()) { + scopes.push(typeSystem.getRootScope()); + } else { + scopes.push(typeSystem.getFunctionScope(t.getScopeRoot())); + } + } + + @Override + public void exitScope(NodeTraversal t) { + scopes.pop(); + } + + /** Returns the current scope at this point in the file. */ + protected StaticScope getScope() { + return scopes.peek(); + } + } + + /** + * Finds all properties defined in the externs file and sets them as + * ineligible for renaming from the type on which they are defined. + */ + private class FindExternProperties extends AbstractScopingCallback { + @Override public void visit(NodeTraversal t, Node n, Node parent) { + // TODO(johnlenz): Support object-literal property definitions. + if (n.isGetProp()) { + String field = n.getLastChild().getString(); + T type = typeSystem.getType(getScope(), n.getFirstChild(), field); + Property prop = getProperty(field); + if (typeSystem.isInvalidatingType(type)) { + prop.invalidate(); + } else { + prop.addTypeToSkip(type); + + // If this is a prototype property, then we want to skip assignments + // to the instance type as well. These assignments are not usually + // seen in the extern code itself, so we must handle them here. + if ((type = typeSystem.getInstanceFromPrototype(type)) != null) { + prop.getTypes().add(type); + prop.typesToSkip.add(type); + } + } + } + } + } + + /** + * Traverses the tree, building a map from field names to Nodes for all + * fields that can be renamed. + */ + private class FindRenameableProperties extends AbstractScopingCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isGetProp()) { + handleGetProp(t, n); + } else if (n.isObjectLit()) { + handleObjectLit(t, n); + } + } + + /** + * Processes a GETPROP node. + */ + private void handleGetProp(NodeTraversal t, Node n) { + String name = n.getLastChild().getString(); + T type = typeSystem.getType(getScope(), n.getFirstChild(), name); + + Property prop = getProperty(name); + if (!prop.scheduleRenaming(n.getLastChild(), + processProperty(t, prop, type, null))) { + if (propertiesToErrorFor.containsKey(name)) { + String suggestion = ""; + if (type instanceof JSType) { + JSType jsType = (JSType) type; + if (jsType.isAllType() || jsType.isUnknownType()) { + if (n.getFirstChild().isThis()) { + suggestion = "The \"this\" object is unknown in the function,"+ + "consider using @this"; + } else { + String qName = n.getFirstChild().getQualifiedName(); + suggestion = "Consider casting " + qName + + " if you know it's type."; + } + } else { + List errors = Lists.newArrayList(); + printErrorLocations(errors, jsType); + if (!errors.isEmpty()) { + suggestion = "Consider fixing errors for the following types:\n"; + suggestion += Joiner.on("\n").join(errors); + } + } + } + compiler.report(JSError.make( + t.getSourceName(), n, propertiesToErrorFor.get(name), + Warnings.INVALIDATION, name, + (type == null ? "null" : type.toString()), + n.toString(), suggestion)); + } + } + } + + /** + * Processes a OBJECTLIT node. + */ + private void handleObjectLit(NodeTraversal t, Node n) { + Node child = n.getFirstChild(); + while (child != null) { + // Maybe STRING, GET, SET + + // We should never see a mix of numbers and strings. + String name = child.getString(); + T type = typeSystem.getType(getScope(), n, name); + + Property prop = getProperty(name); + if (!prop.scheduleRenaming(child, + processProperty(t, prop, type, null))) { + // TODO(user): It doesn't look like the user can do much in this + // case right now. + if (propertiesToErrorFor.containsKey(name)) { + compiler.report(JSError.make( + t.getSourceName(), child, propertiesToErrorFor.get(name), + Warnings.INVALIDATION, name, + (type == null ? "null" : type.toString()), n.toString(), "")); + } + } + child = child.getNext(); + } + } + + private void printErrorLocations(List errors, JSType t) { + if (!t.isObject() || t.isAllType()) { + return; + } + + if (t.isUnionType()) { + for (JSType alt : t.toMaybeUnionType().getAlternates()) { + printErrorLocations(errors, alt); + } + return; + } + + for (JSError error : invalidationMap.get(t)) { + if (errors.size() > MAX_INVALDIATION_WARNINGS_PER_PROPERTY) { + return; + } + + errors.add( + t.toString() + " at " + error.sourceName + ":" + error.lineNumber); + } + } + + /** + * Processes a property, adding it to the list of properties to rename. + * @return a representative type for the property reference, which will be + * the highest type on the prototype chain of the provided type. In the + * case of a union type, it will be the highest type on the prototype + * chain of one of the members of the union. + */ + private T processProperty( + NodeTraversal t, Property prop, T type, T relatedType) { + type = typeSystem.restrictByNotNullOrUndefined(type); + if (prop.skipRenaming || typeSystem.isInvalidatingType(type)) { + return null; + } + + Iterable alternatives = typeSystem.getTypeAlternatives(type); + if (alternatives != null) { + T firstType = relatedType; + for (T subType : alternatives) { + T lastType = processProperty(t, prop, subType, firstType); + if (lastType != null) { + firstType = firstType == null ? lastType : firstType; + } + } + return firstType; + } else { + T topType = typeSystem.getTypeWithProperty(prop.name, type); + if (typeSystem.isInvalidatingType(topType)) { + return null; + } + prop.addType(type, topType, relatedType); + return topType; + } + } + } + + /** Renames all properties with references on more than one type. */ + void renameProperties() { + int propsRenamed = 0, propsSkipped = 0, instancesRenamed = 0, + instancesSkipped = 0, singleTypeProps = 0; + + for (Property prop : properties.values()) { + if (prop.shouldRename()) { + Map propNames = buildPropNames(prop.getTypes(), prop.name); + + ++propsRenamed; + prop.expandTypesToSkip(); + UnionFind types = prop.getTypes(); + for (Node node : prop.renameNodes) { + T rootType = prop.rootTypes.get(node); + if (prop.shouldRename(rootType)) { + String newName = propNames.get(rootType); + node.setString(newName); + compiler.reportCodeChange(); + ++instancesRenamed; + } else { + ++instancesSkipped; + } + } + } else { + if (prop.skipRenaming) { + ++propsSkipped; + } else { + ++singleTypeProps; + } + } + } + logger.fine("Renamed " + instancesRenamed + " instances of " + + propsRenamed + " properties."); + logger.fine("Skipped renaming " + instancesSkipped + " invalidated " + + "properties, " + propsSkipped + " instances of properties " + + "that were skipped for specific types and " + singleTypeProps + + " properties that were referenced from only one type."); + } + + /** + * Chooses a name to use for renaming in each equivalence class and maps + * each type in that class to it. + */ + private Map buildPropNames(UnionFind types, String name) { + Map names = Maps.newHashMap(); + for (Set set : types.allEquivalenceClasses()) { + checkState(!set.isEmpty()); + + String typeName = null; + for (T type : set) { + if (typeName == null || type.toString().compareTo(typeName) < 0) { + typeName = type.toString(); + } + } + + String newName; + if ("{...}".equals(typeName)) { + newName = name; + } else { + newName = typeName.replaceAll("[^\\w$]", "_") + "$" + name; + } + + for (T type : set) { + names.put(type, newName); + } + } + return names; + } + + /** Returns a map from field name to types for which it will be renamed. */ + Multimap> getRenamedTypesForTesting() { + Multimap> ret = HashMultimap.create(); + for (Map.Entry entry: properties.entrySet()) { + Property prop = entry.getValue(); + if (!prop.skipRenaming) { + for (Collection c : prop.getTypes().allEquivalenceClasses()) { + if (!c.isEmpty() && !prop.typesToSkip.contains(c.iterator().next())) { + ret.put(entry.getKey(), c); + } + } + } + } + return ret; + } + + /** Interface for providing the type information needed by this pass. */ + private interface TypeSystem { + // TODO(user): add a getUniqueName(T type) method that is guaranteed + // to be unique, performant and human-readable. + + /** Returns the top-most scope used by the type system (if any). */ + StaticScope getRootScope(); + + /** Returns the new scope started at the given function node. */ + StaticScope getFunctionScope(Node node); + + /** + * Returns the type of the given node. + * @param prop Only types with this property need to be returned. In general + * with type tightening, this will require no special processing, but in + * the case of an unknown JSType, we might need to add in the native + * types since we don't track them, but only if they have the given + * property. + */ + T getType(StaticScope scope, Node node, String prop); + + /** + * Returns true if a field reference on this type will invalidate all + * references to that field as candidates for renaming. This is true if the + * type is unknown or all-inclusive, as variables with such a type could be + * references to any object. + */ + boolean isInvalidatingType(T type); + + /** + * Informs the given type system that a type is invalidating due to a type + * mismatch found during type checking. + */ + void addInvalidatingType(JSType type); + + /** + * Returns a set of types that should be skipped given the given type. + * This is necessary for interfaces when using JSTypes, as all super + * interfaces must also be skipped. + */ + ImmutableSet getTypesToSkipForType(T type); + + /** + * Determines whether the given type is one whose properties should not be + * considered for renaming. + */ + boolean isTypeToSkip(T type); + + /** Remove null and undefined from the options in the given type. */ + T restrictByNotNullOrUndefined(T type); + + /** + * Returns the alternatives if this is a type that represents multiple + * types, and null if not. Union and interface types can correspond to + * multiple other types. + */ + Iterable getTypeAlternatives(T type); + + /** + * Returns the type in the chain from the given type that contains the given + * field or null if it is not found anywhere. + */ + T getTypeWithProperty(String field, T type); + + /** + * Returns the type of the instance of which this is the prototype or null + * if this is not a function prototype. + */ + T getInstanceFromPrototype(T type); + + /** + * Records that this property could be referenced from any interface that + * this type, or any type in its superclass chain, implements. + */ + void recordInterfaces(T type, T relatedType, + DisambiguateProperties.Property p); + } + + /** Implementation of TypeSystem using JSTypes. */ + private static class JSTypeSystem implements TypeSystem { + private final Set invalidatingTypes; + private JSTypeRegistry registry; + + public JSTypeSystem(AbstractCompiler compiler) { + registry = compiler.getTypeRegistry(); + invalidatingTypes = Sets.newHashSet( + registry.getNativeType(JSTypeNative.ALL_TYPE), + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE), + registry.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE), + registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), + registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE), + registry.getNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE), + registry.getNativeType(JSTypeNative.UNKNOWN_TYPE)); + + } + + @Override public void addInvalidatingType(JSType type) { + checkState(!type.isUnionType()); + invalidatingTypes.add(type); + } + + @Override public StaticScope getRootScope() { return null; } + + @Override public StaticScope getFunctionScope(Node node) { + return null; + } + + @Override public JSType getType( + StaticScope scope, Node node, String prop) { + if (node.getJSType() == null) { + return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); + } + return node.getJSType(); + } + + @Override public boolean isInvalidatingType(JSType type) { + if (type == null || invalidatingTypes.contains(type) || + type.isUnknownType() /* unresolved types */) { + return true; + } + + ObjectType objType = ObjectType.cast(type); + return objType != null && !objType.hasReferenceName(); + } + + @Override public ImmutableSet getTypesToSkipForType(JSType type) { + type = type.restrictByNotNullOrUndefined(); + if (type.isUnionType()) { + Set types = Sets.newHashSet(type); + for (JSType alt : type.toMaybeUnionType().getAlternates()) { + types.addAll(getTypesToSkipForTypeNonUnion(type)); + } + return ImmutableSet.copyOf(types); + } else if (type.isEnumElementType()) { + return getTypesToSkipForType( + type.toMaybeEnumElementType().getPrimitiveType()); + } + return ImmutableSet.copyOf(getTypesToSkipForTypeNonUnion(type)); + } + + private Set getTypesToSkipForTypeNonUnion(JSType type) { + Set types = Sets.newHashSet(); + JSType skipType = type; + while (skipType != null) { + types.add(skipType); + + ObjectType objSkipType = skipType.toObjectType(); + if (objSkipType != null) { + skipType = objSkipType.getImplicitPrototype(); + } else { + break; + } + } + return types; + } + + @Override public boolean isTypeToSkip(JSType type) { + return type.isEnumType() || (type.autoboxesTo() != null); + } + + @Override public JSType restrictByNotNullOrUndefined(JSType type) { + return type.restrictByNotNullOrUndefined(); + } + + @Override public Iterable getTypeAlternatives(JSType type) { + if (type.isUnionType()) { + return type.toMaybeUnionType().getAlternates(); + } else { + ObjectType objType = type.toObjectType(); + if (objType != null && + objType.getConstructor() != null && + objType.getConstructor().isInterface()) { + List list = Lists.newArrayList(); + for (FunctionType impl + : registry.getDirectImplementors(objType)) { + list.add(impl.getInstanceType()); + } + return list; + } else { + return null; + } + } + } + + @Override public ObjectType getTypeWithProperty(String field, JSType type) { + if (type == null) { + return null; + } + + if (type.isEnumElementType()) { + return getTypeWithProperty( + field, type.toMaybeEnumElementType().getPrimitiveType()); + } + + if (!(type instanceof ObjectType)) { + if (type.autoboxesTo() != null) { + type = type.autoboxesTo(); + } else { + return null; + } + } + + // Ignore the prototype itself at all times. + if ("prototype".equals(field)) { + return null; + } + + // We look up the prototype chain to find the highest place (if any) that + // this appears. This will make references to overridden properties look + // like references to the initial property, so they are renamed alike. + ObjectType foundType = null; + ObjectType objType = ObjectType.cast(type); + if (objType != null && objType.getConstructor() != null + && objType.getConstructor().isInterface()) { + ObjectType topInterface = FunctionType.getTopDefiningInterface( + objType, field); + if (topInterface != null && topInterface.getConstructor() != null) { + foundType = topInterface.getConstructor().getPrototype(); + } + } else { + while (objType != null && objType.getImplicitPrototype() != objType) { + if (objType.hasOwnProperty(field)) { + foundType = objType; + } + objType = objType.getImplicitPrototype(); + } + } + + // If the property does not exist on the referenced type but the original + // type is an object type, see if any subtype has the property. + if (foundType == null) { + ObjectType maybeType = ObjectType.cast( + registry.getGreatestSubtypeWithProperty(type, field)); + // getGreatestSubtypeWithProperty does not guarantee that the property + // is defined on the returned type, it just indicates that it might be, + // so we have to double check. + if (maybeType != null && maybeType.hasOwnProperty(field)) { + foundType = maybeType; + } + } + return foundType; + } + + @Override public JSType getInstanceFromPrototype(JSType type) { + if (type.isFunctionPrototypeType()) { + ObjectType prototype = (ObjectType) type; + FunctionType owner = prototype.getOwnerFunction(); + if (owner.isConstructor() || owner.isInterface()) { + return prototype.getOwnerFunction().getInstanceType(); + } + } + return null; + } + + @Override + public void recordInterfaces(JSType type, JSType relatedType, + DisambiguateProperties.Property p) { + ObjectType objType = ObjectType.cast(type); + if (objType != null) { + FunctionType constructor; + if (objType.isFunctionType()) { + constructor = objType.toMaybeFunctionType(); + } else if (objType.isFunctionPrototypeType()) { + constructor = objType.getOwnerFunction(); + } else { + constructor = objType.getConstructor(); + } + while (constructor != null) { + for (ObjectType itype : constructor.getImplementedInterfaces()) { + JSType top = getTypeWithProperty(p.name, itype); + if (top != null) { + p.addType(itype, top, relatedType); + } else { + recordInterfaces(itype, relatedType, p); + } + + // If this interface invalidated this property, return now. + if (p.skipRenaming) return; + } + if (constructor.isInterface() || constructor.isConstructor()) { + constructor = constructor.getSuperClassConstructor(); + } else { + constructor = null; + } + } + } + } + } + + /** Implementation of TypeSystem using concrete types. */ + private static class ConcreteTypeSystem implements TypeSystem { + private final TightenTypes tt; + private int nextUniqueId; + private CodingConvention codingConvention; + private final Set invalidatingTypes = Sets.newHashSet(); + + // An array of native types that are not tracked by type tightening, and + // thus need to be added in if an unknown type is encountered. + private static final JSTypeNative [] nativeTypes = new JSTypeNative[] { + JSTypeNative.BOOLEAN_OBJECT_TYPE, + JSTypeNative.NUMBER_OBJECT_TYPE, + JSTypeNative.STRING_OBJECT_TYPE + }; + + public ConcreteTypeSystem(TightenTypes tt, CodingConvention convention) { + this.tt = tt; + this.codingConvention = convention; + } + + @Override public void addInvalidatingType(JSType type) { + checkState(!type.isUnionType()); + invalidatingTypes.add(type); + } + + @Override public StaticScope getRootScope() { + return tt.getTopScope(); + } + + @Override public StaticScope getFunctionScope(Node decl) { + ConcreteFunctionType func = tt.getConcreteFunction(decl); + return (func != null) ? + func.getScope() : (StaticScope) null; + } + + @Override + public ConcreteType getType( + StaticScope scope, Node node, String prop) { + if (scope != null) { + ConcreteType c = tt.inferConcreteType( + (TightenTypes.ConcreteScope) scope, node); + return maybeAddAutoboxes(c, node, prop); + } else { + return null; + } + } + + /** + * Add concrete types for autoboxing types if necessary. The concrete type + * system does not track native types, like string, so add them if they are + * present in the JSType for the node. + */ + private ConcreteType maybeAddAutoboxes( + ConcreteType cType, Node node, String prop) { + JSType jsType = node.getJSType(); + if (jsType == null) { + return cType; + } else if (jsType.isUnknownType()) { + for (JSTypeNative nativeType : nativeTypes) { + ConcreteType concrete = tt.getConcreteInstance( + tt.getTypeRegistry().getNativeObjectType(nativeType)); + if (concrete != null && !concrete.getPropertyType(prop).isNone()) { + cType = cType.unionWith(concrete); + } + } + return cType; + } + + return maybeAddAutoboxes(cType, jsType, prop); + } + + private ConcreteType maybeAddAutoboxes( + ConcreteType cType, JSType jsType, String prop) { + jsType = jsType.restrictByNotNullOrUndefined(); + if (jsType.isUnionType()) { + for (JSType alt : jsType.toMaybeUnionType().getAlternates()) { + cType = maybeAddAutoboxes(cType, alt, prop); + } + return cType; + } else if (jsType.isEnumElementType()) { + return maybeAddAutoboxes( + cType, jsType.toMaybeEnumElementType().getPrimitiveType(), prop); + } + + if (jsType.autoboxesTo() != null) { + JSType autoboxed = jsType.autoboxesTo(); + return cType.unionWith(tt.getConcreteInstance((ObjectType) autoboxed)); + } else if (jsType.unboxesTo() != null) { + return cType.unionWith(tt.getConcreteInstance((ObjectType) jsType)); + } + + return cType; + } + + @Override public boolean isInvalidatingType(ConcreteType type) { + // We will disallow types on functions so that 'prototype' is not renamed. + // TODO(user): Support properties on functions as well. + return (type == null) || type.isAll() || type.isFunction() + || (type.isInstance() + && invalidatingTypes.contains(type.toInstance().instanceType)); + } + + @Override + public ImmutableSet getTypesToSkipForType(ConcreteType type) { + return ImmutableSet.of(type); + } + + @Override public boolean isTypeToSkip(ConcreteType type) { + // Skip anonymous object literals and enum types. + return type.isInstance() + && !(type.toInstance().isFunctionPrototype() + || type.toInstance().instanceType.isInstanceType()); + } + + @Override + public ConcreteType restrictByNotNullOrUndefined(ConcreteType type) { + // These are not represented in concrete types. + return type; + } + + @Override + public Iterable getTypeAlternatives(ConcreteType type) { + if (type.isUnion()) { + return ((ConcreteUnionType) type).getAlternatives(); + } else { + return null; + } + } + + @Override public ConcreteType getTypeWithProperty(String field, + ConcreteType type) { + if (type.isInstance()) { + ConcreteInstanceType instanceType = (ConcreteInstanceType) type; + return instanceType.getInstanceTypeWithProperty(field); + } else if (type.isFunction()) { + if ("prototype".equals(field) + || codingConvention.isSuperClassReference(field)) { + return type; + } + } else if (type.isNone()) { + // If the receiver is none, then this code is never reached. We will + // return a new fake type to ensure that this access is renamed + // differently from any other, so it can be easily removed. + return new ConcreteUniqueType(++nextUniqueId); + } else if (type.isUnion()) { + // If only one has the property, return that. + for (ConcreteType t : ((ConcreteUnionType) type).getAlternatives()) { + ConcreteType ret = getTypeWithProperty(field, t); + if (ret != null) { + return ret; + } + } + } + return null; + } + + @Override public ConcreteType getInstanceFromPrototype(ConcreteType type) { + if (type.isInstance()) { + ConcreteInstanceType instanceType = (ConcreteInstanceType) type; + if (instanceType.isFunctionPrototype()) { + return instanceType.getConstructorType().getInstanceType(); + } + } + return null; + } + + @Override + public void recordInterfaces(ConcreteType type, ConcreteType relatedType, + DisambiguateProperties.Property p) { + // No need to record interfaces when using concrete types. + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DotFormatter.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DotFormatter.java new file mode 100644 index 0000000..54988ed --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/DotFormatter.java @@ -0,0 +1,296 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.graph.GraphvizGraph; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; +import com.google.javascript.jscomp.graph.GraphvizGraph.GraphvizEdge; +import com.google.javascript.jscomp.graph.GraphvizGraph.GraphvizNode; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.JSType; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +/** + *

      DotFormatter prints out a dot file of the Abstract Syntax Tree. + * For a detailed description of the dot format and visualization tool refer + * to Graphviz.

      + *

      Typical usage of this class

      + * System.out.println(new DotFormatter().toDot(node)); + *

      This class is not thread safe and should not be used without proper + * external synchronization.

      + * + */ +public class DotFormatter { + private static final String INDENT = " "; + private static final String ARROW = " -> "; + private static final String LINE = " -- "; + + // stores the current assignment of node to keys + private HashMap assignments = new HashMap(); + + // key count in order to assign a unique key to each node + private int keyCount = 0; + + // the builder used to generate the dot diagram + private Appendable builder; + + private final ControlFlowGraph cfg; + + private final boolean printAnnotations; + + /** For Testing Only */ + private DotFormatter() { + this.builder = new StringBuilder(); + this.cfg = null; + this.printAnnotations = false; + } + + private DotFormatter(Node n, ControlFlowGraph cfg, + Appendable builder, boolean printAnnotations) throws IOException { + this.cfg = cfg; + this.builder = builder; + this.printAnnotations = printAnnotations; + + formatPreamble(); + traverseNodes(n); + formatConclusion(); + } + + /** + * Converts an AST to dot representation. + * @param n the root of the AST described in the dot formatted string + * @return the dot representation of the AST + */ + public static String toDot(Node n) throws IOException { + return toDot(n, null); + } + + /** + * Converts an AST to dot representation. + * @param n the root of the AST described in the dot formatted string + * @param inCFG Control Flow Graph. + * @param printAnnotations print annotations. + * @return the dot representation of the AST + */ + static String toDot( + Node n, ControlFlowGraph inCFG, boolean printAnnotations) + throws IOException { + StringBuilder builder = new StringBuilder(); + new DotFormatter(n, inCFG, builder, printAnnotations); + return builder.toString(); + } + + /** + * Converts an AST to dot representation. + * @param n the root of the AST described in the dot formatted string + * @param inCFG Control Flow Graph. + * @return the dot representation of the AST + */ + static String toDot(Node n, ControlFlowGraph inCFG) + throws IOException { + StringBuilder builder = new StringBuilder(); + new DotFormatter(n, inCFG, builder, false); + return builder.toString(); + } + + /** + * Converts an AST to dot representation and appends it to the given buffer. + * @param n the root of the AST described in the dot formatted string + * @param inCFG Control Flow Graph. + * @param builder A place to dump the graph. + */ + static void appendDot(Node n, ControlFlowGraph inCFG, + Appendable builder) throws IOException { + new DotFormatter(n, inCFG, builder, false); + } + + /** + * Creates a DotFormatter purely for testing DotFormatter's internal methods. + */ + static DotFormatter newInstanceForTesting() { + return new DotFormatter(); + } + + private void traverseNodes(Node parent) throws IOException { + // key + int keyParent = key(parent); + + // edges + for (Node child = parent.getFirstChild(); child != null; + child = child.getNext()) { + int keyChild = key(child); + builder.append(INDENT); + builder.append(formatNodeName(keyParent)); + builder.append(ARROW); + builder.append(formatNodeName(keyChild)); + builder.append(" [weight=1];\n"); + + traverseNodes(child); + } + + // Flow Edges + if (cfg != null && cfg.hasNode(parent)) { + List> outEdges = + cfg.getOutEdges(parent); + String[] edgeList = new String[outEdges.size()]; + for (int i = 0; i < edgeList.length; i++) { + DiGraphEdge edge = outEdges.get(i); + DiGraphNode succ = edge.getDestination(); + + String toNode = null; + if (succ == cfg.getImplicitReturn()) { + toNode = "RETURN"; + } else { + int keySucc = key(succ.getValue()); + toNode = formatNodeName(keySucc); + } + + edgeList[i] = formatNodeName(keyParent) + ARROW + toNode + " [label=\"" + + edge.getValue().toString() + "\", " + "fontcolor=\"red\", " + + "weight=0.01, color=\"red\"];\n"; + } + + Arrays.sort(edgeList); + + for (int i = 0; i < edgeList.length; i++) { + builder.append(INDENT); + builder.append(edgeList[i]); + } + } + } + + int key(Node n) throws IOException { + Integer key = assignments.get(n); + if (key == null) { + key = keyCount++; + assignments.put(n, key); + builder.append(INDENT); + builder.append(formatNodeName(key)); + builder.append(" [label=\""); + builder.append(name(n)); + JSType type = n.getJSType(); + if (type != null) { + builder.append(" : "); + builder.append(type.toString()); + } + if (printAnnotations && cfg != null && cfg.hasNode(n)) { + Object annotation = cfg.getNode(n).getAnnotation(); + if (annotation != null) { + builder.append("\\n"); + builder.append(annotation.toString()); + } + } + builder.append("\""); + if (n.getJSDocInfo() != null) { + builder.append(" color=\"green\""); + } + builder.append("];\n"); + } + return key; + } + + private String name(Node n) { + int type = n.getType(); + switch (type) { + case Token.VOID: + return "VOID"; + + default: + return Token.name(type); + } + } + + private String formatNodeName(Integer key) { + return "node" + key; + } + + private void formatPreamble() throws IOException { + builder.append("digraph AST {\n"); + builder.append(INDENT); + builder.append("node [color=lightblue2, style=filled];\n"); + } + + private void formatConclusion() throws IOException { + builder.append("}\n"); + } + + /** + * Outputs a string in DOT format that presents the graph. + * + * @param graph Input graph. + * @return A string in Dot format that presents the graph. + */ + public static String toDot(GraphvizGraph graph) { + StringBuilder builder = new StringBuilder (); + builder.append(graph.isDirected() ? "digraph" : "graph"); + builder.append(INDENT); + builder.append(graph.getName()); + builder.append(" {\n"); + builder.append(INDENT); + builder.append("node [color=lightblue2, style=filled];\n"); + + final String edgeSymbol = graph.isDirected() ? ARROW : LINE; + + List nodes = graph.getGraphvizNodes(); + + String[] nodeNames = new String[nodes.size()]; + + for (int i = 0; i < nodeNames.length; i++) { + GraphvizNode gNode = nodes.get(i); + nodeNames[i] = gNode.getId() + " [label=\"" + gNode.getLabel() + + "\" color=\"" + gNode.getColor() + "\"]"; + } + + // We sort the nodes so we get a deterministic output every time regardless + // of the implementation of the graph data structure. + Arrays.sort(nodeNames); + + for (String nodeName : nodeNames) { + builder.append(INDENT); + builder.append(nodeName); + builder.append(";\n"); + } + + List edges = graph.getGraphvizEdges(); + + String[] edgeNames = new String[edges.size()]; + + for (int i = 0; i < edgeNames.length; i++) { + GraphvizEdge edge = edges.get(i); + edgeNames[i] = edge.getNode1Id() + edgeSymbol + edge.getNode2Id(); + } + + // Again, we sort the edges as well. + Arrays.sort(edgeNames); + + for (String edgeName : edgeNames) { + builder.append(INDENT); + builder.append(edgeName); + builder.append(";\n"); + } + + builder.append("}\n"); + return builder.toString(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/EmptyMessageBundle.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/EmptyMessageBundle.java new file mode 100644 index 0000000..3d13d5f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/EmptyMessageBundle.java @@ -0,0 +1,52 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableList; +import com.google.javascript.jscomp.JsMessage.IdGenerator; + +/** + * An implementation of MessageBundle that has no translations. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class EmptyMessageBundle implements MessageBundle { + + /** + * Gets a dummy message ID generator. + */ + @Override + public IdGenerator idGenerator() { + return null; + } + + /** + * Returns null, to indicate it has no message replacements. + */ + @Override + public JsMessage getMessage(String id) { + return null; + } + + /** + * Returns an empty list of messages. + */ + @Override + public Iterable getAllMessages() { + return ImmutableList.of(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ErrorFormat.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ErrorFormat.java new file mode 100644 index 0000000..64e365f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ErrorFormat.java @@ -0,0 +1,70 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt; + +/** + * Error formats available. + */ +public enum ErrorFormat { + LEGACY { + @Override + public MessageFormatter toFormatter( + SourceExcerptProvider source, boolean colorize) { + VerboseMessageFormatter formatter = new VerboseMessageFormatter(source); + formatter.setColorize(colorize); + return formatter; + } + }, + SINGLELINE { + @Override + public MessageFormatter toFormatter( + SourceExcerptProvider source, boolean colorize) { + LightweightMessageFormatter formatter = new LightweightMessageFormatter( + source); + formatter.setColorize(colorize); + return formatter; + } + }, + MULTILINE { + @Override + public MessageFormatter toFormatter( + SourceExcerptProvider source, boolean colorize) { + LightweightMessageFormatter formatter = new LightweightMessageFormatter( + source, SourceExcerpt.REGION); + formatter.setColorize(colorize); + return formatter; + } + }, + SOURCELESS { + @Override + public MessageFormatter toFormatter( + SourceExcerptProvider source, boolean colorize) { + LightweightMessageFormatter formatter = + LightweightMessageFormatter.withoutSource(); + formatter.setColorize(colorize); + return formatter; + } + }; + + /** + * Convert to a concrete formatter. + */ + public abstract MessageFormatter toFormatter( + SourceExcerptProvider source, boolean colorize); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ErrorHandler.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ErrorHandler.java new file mode 100644 index 0000000..99f48c9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ErrorHandler.java @@ -0,0 +1,33 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.CheckLevel; + +/** + * The error handler is any generic sink for warnings and errors, + * after they've passed through any filtering {@code WarningsGuard}s. + * + * @author nicksantos@google.com (Nick Santos) + */ +public interface ErrorHandler { + /** + * @param level the reporting level + * @param error the error to report + */ + void report(CheckLevel level, JSError error); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ErrorManager.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ErrorManager.java new file mode 100644 index 0000000..3a450f2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ErrorManager.java @@ -0,0 +1,72 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.CheckLevel; + +/** + * The error manager is in charge of storing, organizing and displaying + * errors and warnings generated by the compiler. + * + */ +public interface ErrorManager extends ErrorHandler { + /** + * Reports an error. The errors will be displayed by the + * {@link #generateReport()} at the discretion of the implementation. + * + * @param level the reporting level + * @param error the error to report + */ + @Override + void report(CheckLevel level, JSError error); + + /** + * Writes a report to an implementation-specific medium. The compiler calls + * this method after any and all {@link #report} calls. + */ + void generateReport(); + + /** + * Gets the number of reported errors. + */ + int getErrorCount(); + + /** + * Gets the number of reported warnings. + */ + int getWarningCount(); + + /** + * Gets all the errors. + */ + JSError[] getErrors(); + + /** + * Gets all the warnings. + */ + JSError[] getWarnings(); + + /** + * Sets the percentage of typed expressions. + */ + void setTypedPercent(double typedPercent); + + /** + * Gets the percentage of typed expressions. + */ + double getTypedPercent(); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ErrorPass.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ErrorPass.java new file mode 100644 index 0000000..6b8acfe --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ErrorPass.java @@ -0,0 +1,43 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + * A compiler pass that just reports an error. + * + */ + +class ErrorPass implements CompilerPass { + private final AbstractCompiler compiler; + private final JSError error; + + ErrorPass(AbstractCompiler compiler, DiagnosticType error) { + this(compiler, JSError.make(error)); + } + + ErrorPass(AbstractCompiler compiler, JSError error) { + this.compiler = compiler; + this.error = error; + } + + @Override + public void process(Node externs, Node root) { + compiler.report(error); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExpandJqueryAliases.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExpandJqueryAliases.java new file mode 100644 index 0000000..6219d26 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExpandJqueryAliases.java @@ -0,0 +1,583 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.logging.Logger; + +/** + * Replace known jQuery aliases and methods with standard + * conventions so that the compiler recognizes them. Expected + * replacements include: + * - jQuery.fn -> jQuery.prototype + * - jQuery.extend -> expanded into direct object assignments + * - jQuery.expandedEach -> expand into direct assignments + * + * @author chadkillingsworth@missouristate.edu (Chad Killingsworth) + */ +class ExpandJqueryAliases extends AbstractPostOrderCallback + implements CompilerPass { + private final AbstractCompiler compiler; + private final CodingConvention convention; + private static final Logger logger = + Logger.getLogger(ExpandJqueryAliases.class.getName()); + + static final DiagnosticType JQUERY_UNABLE_TO_EXPAND_INVALID_LIT_ERROR = + DiagnosticType.warning("JSC_JQUERY_UNABLE_TO_EXPAND_INVALID_LIT", + "jQuery.expandedEach call cannot be expanded because the first " + + "argument must be an object literal or an array of strings " + + "literal."); + + static final DiagnosticType JQUERY_UNABLE_TO_EXPAND_INVALID_NAME_ERROR = + DiagnosticType.error("JSC_JQUERY_UNABLE_TO_EXPAND_INVALID_NAME", + "jQuery.expandedEach expansion would result in the invalid " + + "property name \"{0}\"."); + + static final DiagnosticType JQUERY_USELESS_EACH_EXPANSION = + DiagnosticType.warning("JSC_JQUERY_USELESS_EACH_EXPANSION", + "jQuery.expandedEach was not expanded as no valid property " + + "assignments were encountered. Consider using jQuery.each instead."); + + private static final Set JQUERY_EXTEND_NAMES = ImmutableSet.of( + "jQuery.extend", "jQuery.fn.extend", "jQuery.prototype.extend"); + + private static final String JQUERY_EXPANDED_EACH_NAME = + "jQuery.expandedEach"; + + private final PeepholeOptimizationsPass peepholePasses; + + ExpandJqueryAliases(AbstractCompiler compiler) { + this.compiler = compiler; + this.convention = compiler.getCodingConvention(); + + // All of the "early" peephole optimizations. + // These passes should make the code easier to analyze. + // Passes, such as StatementFusion, are omitted for this reason. + final boolean late = false; + this.peepholePasses = new PeepholeOptimizationsPass(compiler, + new PeepholeSubstituteAlternateSyntax(late), + new PeepholeReplaceKnownMethods(late), + new PeepholeRemoveDeadCode(), + new PeepholeFoldConstants(late), + new PeepholeCollectPropertyAssignments()); + } + + /** + * Check that Node n is a call to one of the jQuery.extend methods that we + * can expand. Valid calls are single argument calls where the first argument + * is an object literal or two argument calls where the first argument + * is a name and the second argument is an object literal. + */ + public static boolean isJqueryExtendCall(Node n, String qname, + AbstractCompiler compiler) { + if (JQUERY_EXTEND_NAMES.contains(qname)) { + Node firstArgument = n.getNext(); + if (firstArgument == null) { + return false; + } + + Node secondArgument = firstArgument.getNext(); + if ((firstArgument.isObjectLit() && secondArgument == null) || + (firstArgument.isName() || NodeUtil.isGet(firstArgument) && + !NodeUtil.mayHaveSideEffects(firstArgument, compiler) && + secondArgument != null && secondArgument.isObjectLit() && + secondArgument.getNext() == null)) { + return true; + } + } + return false; + } + + public boolean isJqueryExpandedEachCall(Node call, String qName) { + Preconditions.checkArgument(call.isCall()); + if (call.getFirstChild() != null && + JQUERY_EXPANDED_EACH_NAME.equals(qName)) { + return true; + } + return false; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isGetProp() && convention.isPrototypeAlias(n)) { + maybeReplaceJqueryPrototypeAlias(n); + + } else if (n.isCall()) { + Node callTarget = n.getFirstChild(); + String qName = callTarget.getQualifiedName(); + + if (isJqueryExtendCall(callTarget, qName, this.compiler)) { + maybeExpandJqueryExtendCall(n); + + } else if (isJqueryExpandedEachCall(n, qName)) { + maybeExpandJqueryEachCall(t, n); + } + } + } + + @Override + public void process(Node externs, Node root) { + logger.fine("Expanding Jquery Aliases"); + + NodeTraversal.traverse(compiler, root, this); + } + + private void maybeReplaceJqueryPrototypeAlias(Node n) { + // Check to see if this is the assignment of the original alias. + // If so, leave it intact. + if(NodeUtil.isLValue(n)) { + Node maybeAssign = n.getParent(); + while (!NodeUtil.isStatement(maybeAssign) && !maybeAssign.isAssign()) { + maybeAssign = maybeAssign.getParent(); + } + + if (maybeAssign.isAssign()) { + maybeAssign = maybeAssign.getParent(); + if (maybeAssign.isBlock() || maybeAssign.isScript() || + NodeUtil.isStatement(maybeAssign)) { + return; + } + } + } + + Node fn = n.getLastChild(); + if (fn != null) { + n.replaceChild(fn, IR.string("prototype")); + compiler.reportCodeChange(); + } + } + + /** + * Expand jQuery.extend (and derivative) calls into direct object assignments + * Example: jQuery.extend(obj1, {prop1: val1, prop2: val2}) -> + * obj1.prop1 = val1; + * obj1.prop2 = val2; + */ + private void maybeExpandJqueryExtendCall(Node n) { + Node callTarget = n.getFirstChild(); + Node objectToExtend = callTarget.getNext(); // first argument + Node extendArg = objectToExtend.getNext(); // second argument + boolean ensureObjectDefined = true; + + if (extendArg == null) { + // Only one argument was specified, so extend jQuery namespace + extendArg = objectToExtend; + objectToExtend = callTarget.getFirstChild(); + ensureObjectDefined = false; + } else if (objectToExtend.isGetProp() && + (objectToExtend.getLastChild().getString().equals("prototype") || + convention.isPrototypeAlias(objectToExtend))) { + ensureObjectDefined = false; + } + + // Check for an empty object literal + if (!extendArg.hasChildren()) { + return; + } + + // Since we are expanding jQuery.extend calls into multiple statements, + // encapsulate the new statements in a new block. + Node fncBlock = IR.block().srcref(n); + + if (ensureObjectDefined) { + Node assignVal = IR.or(objectToExtend.cloneTree(), + IR.objectlit().srcref(n)).srcref(n); + Node assign = IR.assign(objectToExtend.cloneTree(), assignVal).srcref(n); + fncBlock.addChildrenToFront(IR.exprResult(assign).srcref(n)); + } + + while (extendArg.hasChildren()) { + Node currentProp = extendArg.removeFirstChild(); + currentProp.setType(Token.STRING); + + Node propValue = currentProp.removeFirstChild(); + + Node newProp; + if(currentProp.isQuotedString()) { + newProp = IR.getelem(objectToExtend.cloneTree(), + currentProp).srcref(currentProp); + } else { + newProp = IR.getprop(objectToExtend.cloneTree(), + currentProp).srcref(currentProp); + } + + Node assignNode = IR.assign(newProp, propValue).srcref(currentProp); + fncBlock.addChildToBack(IR.exprResult(assignNode).srcref(currentProp)); + } + + // Check to see if the return value is used. If not, replace the original + // call with new block. Otherwise, wrap the statements in an + // immediately-called anonymous function. + if (n.getParent().isExprResult()) { + Node parent = n.getParent(); + parent.getParent().replaceChild(parent, fncBlock); + } else { + Node targetVal; + if ("jQuery.prototype".equals(objectToExtend.getQualifiedName())) { + // When extending the jQuery prototype, return the jQuery namespace. + // This is not commonly used. + targetVal = objectToExtend.removeFirstChild(); + } else { + targetVal = objectToExtend.detachFromParent(); + } + fncBlock.addChildToBack(IR.returnNode(targetVal).srcref(targetVal)); + + Node fnc = IR.function(IR.name("").srcref(n), + IR.paramList().srcref(n), + fncBlock); + n.replaceChild(callTarget, fnc); + n.putBooleanProp(Node.FREE_CALL, true); + + // remove any other pre-existing call arguments + while(fnc.getNext() != null) { + n.removeChildAfter(fnc); + } + } + compiler.reportCodeChange(); + } + + /** + * Expand a jQuery.expandedEach call + * + * Expanded jQuery.expandedEach calls will replace the GETELEM nodes of a + * property assignment with GETPROP nodes to allow for renaming. + */ + private void maybeExpandJqueryEachCall(NodeTraversal t, Node n) { + Node objectToLoopOver = n.getChildAtIndex(1); + + if (objectToLoopOver == null) { + return; + } + + Node callbackFunction = objectToLoopOver.getNext(); + if (callbackFunction == null || !callbackFunction.isFunction()) { + return; + } + + // Run the peephole optimizations on the first argument to handle + // cases like ("a " + "b").split(" ") + peepholePasses.process(null, n.getChildAtIndex(1)); + + // Create a reference tree + Node nClone = n.cloneTree(); + + objectToLoopOver = nClone.getChildAtIndex(1); + + // Check to see if the first argument is something we recognize and can + // expand. + if (!objectToLoopOver.isObjectLit() && + !(objectToLoopOver.isArrayLit() && + isArrayLitValidForExpansion(objectToLoopOver))) { + t.report(n, JQUERY_UNABLE_TO_EXPAND_INVALID_LIT_ERROR, (String)null); + return; + } + + // Find all references to the callback function arguments + List keyNodeReferences = Lists.newArrayList(); + List valueNodeReferences = Lists.newArrayList(); + + NodeTraversal.traverse(compiler, + NodeUtil.getFunctionBody(callbackFunction), + new FindCallbackArgumentReferences(callbackFunction, + keyNodeReferences, valueNodeReferences, + objectToLoopOver.isArrayLit())); + + if(keyNodeReferences.size() == 0) { + // We didn't do anything useful ... + t.report(n, JQUERY_USELESS_EACH_EXPANSION, (String)null); + return; + } + + Node fncBlock = tryExpandJqueryEachCall(t, nClone, callbackFunction, + keyNodeReferences, valueNodeReferences); + + if (fncBlock != null && fncBlock.hasChildren()) { + replaceOriginalJqueryEachCall(n, fncBlock); + } else { + // We didn't do anything useful ... + t.report(n, JQUERY_USELESS_EACH_EXPANSION, (String)null); + } + } + + private Node tryExpandJqueryEachCall(NodeTraversal t, Node n, + Node callbackFunction, List keyNodes, List valueNodes) { + + Node callTarget = n.getFirstChild(); + Node objectToLoopOver = callTarget.getNext(); + + // New block to contain the expanded statements + Node fncBlock = IR.block().srcref(callTarget); + + boolean isValidExpansion = true; + + // Expand the jQuery.expandedEach call + Node key = objectToLoopOver.getFirstChild(), val = null; + for(int i = 0; key != null; key = key.getNext(), i++) { + if (key != null) { + if (objectToLoopOver.isArrayLit()) { + // Arrays have a value of their index number + val = IR.number(i).srcref(key); + } else { + val = key.getFirstChild(); + } + } + + // Keep track of the replaced nodes so we can reset the tree + List newKeys = Lists.newArrayList(); + List newValues = Lists.newArrayList(); + List origGetElems = Lists.newArrayList(); + List newGetProps = Lists.newArrayList(); + + // Replace all of the key nodes with the prop name + for (int j = 0; j < keyNodes.size(); j++) { + Node origNode = keyNodes.get(j); + Node ancestor = origNode.getParent(); + + Node newNode = IR.string(key.getString()).srcref(key); + newKeys.add(newNode); + ancestor.replaceChild(origNode, newNode); + + // Walk up the tree to see if the key is used in a GETELEM + // assignment + while (ancestor != null && !NodeUtil.isStatement(ancestor) && + !ancestor.isGetElem()) { + ancestor = ancestor.getParent(); + } + + // Convert GETELEM nodes to GETPROP nodes so that they can be + // renamed or removed. + if (ancestor != null && ancestor.isGetElem()) { + + Node propObject = ancestor; + while (propObject.isGetProp() || propObject.isGetElem()) { + propObject = propObject.getFirstChild(); + } + + Node ancestorClone = ancestor.cloneTree(); + // Run the peephole passes to handle cases such as + // obj['lit' + key] = val; + peepholePasses.process(null, ancestorClone.getChildAtIndex(1)); + Node prop = ancestorClone.getChildAtIndex(1); + + if (prop.isString() && + NodeUtil.isValidPropertyName(prop.getString())) { + Node target = ancestorClone.getFirstChild(); + Node newGetProp = IR.getprop(target.detachFromParent(), + prop.detachFromParent()); + newGetProps.add(newGetProp); + origGetElems.add(ancestor); + ancestor.getParent().replaceChild(ancestor, newGetProp); + } else { + if (prop.isString() && + !NodeUtil.isValidPropertyName(prop.getString())) { + t.report(n, + JQUERY_UNABLE_TO_EXPAND_INVALID_NAME_ERROR, + prop.getString()); + } + isValidExpansion = false; + } + } + } + + if (isValidExpansion) { + // Replace all of the value nodes with the prop value + for (int j = 0; val != null && j < valueNodes.size(); j++) { + Node origNode = valueNodes.get(j); + Node newNode = val.cloneTree(); + newValues.add(newNode); + origNode.getParent().replaceChild(origNode, newNode); + } + + // Wrap the new tree in an anonymous function call + Node fnc = IR.function(IR.name("").srcref(key), + IR.paramList().srcref(key), + callbackFunction.getChildAtIndex(2).cloneTree()).srcref(key); + Node call = IR.call(fnc).srcref(key); + call.putBooleanProp(Node.FREE_CALL, true); + fncBlock.addChildToBack(IR.exprResult(call).srcref(call)); + } + + // Reset the source tree + for (int j = 0; j < newGetProps.size(); j++) { + newGetProps.get(j).getParent().replaceChild(newGetProps.get(j), + origGetElems.get(j)); + } + for (int j = 0; j < newKeys.size(); j++) { + newKeys.get(j).getParent().replaceChild(newKeys.get(j), + keyNodes.get(j)); + } + for (int j = 0; j < newValues.size(); j++) { + newValues.get(j).getParent().replaceChild(newValues.get(j), + valueNodes.get(j)); + } + + if (!isValidExpansion) { + return null; + } + } + return fncBlock; + } + + private void replaceOriginalJqueryEachCall(Node n, Node expandedBlock) { + // Check to see if the return value of the original jQuery.expandedEach + // call is used. If so, we need to wrap each loop expansion in an anonymous + // function and return the original objectToLoopOver. + if (n.getParent().isExprResult()) { + Node parent = n.getParent(); + Node grandparent = parent.getParent(); + Node insertAfter = parent; + while (expandedBlock.hasChildren()) { + Node child = expandedBlock.getFirstChild().detachFromParent(); + grandparent.addChildAfter(child, insertAfter); + insertAfter = child; + } + grandparent.removeChild(parent); + } else { + // Return the original object + Node callTarget = n.getFirstChild(); + Node objectToLoopOver = callTarget.getNext(); + + objectToLoopOver.detachFromParent(); + Node ret = IR.returnNode(objectToLoopOver).srcref(callTarget); + expandedBlock.addChildToBack(ret); + + // Wrap all of the expanded loop calls in a new anonymous function + Node fnc = IR.function(IR.name("").srcref(callTarget), + IR.paramList().srcref(callTarget), + expandedBlock); + n.replaceChild(callTarget, fnc); + n.putBooleanProp(Node.FREE_CALL, true); + + // remove any other pre-existing call arguments + while(fnc.getNext() != null) { + n.removeChildAfter(fnc); + } + } + compiler.reportCodeChange(); + } + + private boolean isArrayLitValidForExpansion(Node n) { + Iterator iter = n.children().iterator(); + while (iter.hasNext()) { + Node child = iter.next(); + if (!child.isString()) { + return false; + } + } + return true; + } + + /** + * Given a jQuery.expandedEach callback function, traverse it and collect any + * references to its parameter names. + */ + class FindCallbackArgumentReferences extends AbstractPostOrderCallback + implements ScopedCallback { + + private final String keyName; + private final String valueName; + private Scope startingScope; + private List keyReferences; + private List valueReferences; + + FindCallbackArgumentReferences(Node functionRoot, List keyReferences, + List valueReferences, boolean useArrayMode) { + Preconditions.checkState(functionRoot.isFunction()); + + String keyString = null, valueString = null; + Node callbackParams = NodeUtil.getFunctionParameters(functionRoot); + Node param = callbackParams.getFirstChild(); + if (param != null) { + Preconditions.checkState(param.isName()); + keyString = param.getString(); + + param = param.getNext(); + if (param != null) { + Preconditions.checkState(param.isName()); + valueString = param.getString(); + } + } + + this.keyName = keyString; + this.valueName = valueString; + + // For arrays, the keyString is the index number of the element. + // We're interested in the value of the element instead + if (useArrayMode) { + this.keyReferences = valueReferences; + this.valueReferences = keyReferences; + } else { + this.keyReferences = keyReferences; + this.valueReferences = valueReferences; + } + + this.startingScope = null; + } + + private boolean isShadowed(String name, Scope scope) { + Var nameVar = scope.getVar(name); + return nameVar != null && + nameVar.getScope() != this.startingScope; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + // In the top scope, "this" is a reference to "value" + boolean isThis = false; + if (t.getScope() == this.startingScope) { + isThis = n.isThis(); + } + + if (isThis || n.isName() && !isShadowed(n.getString(), t.getScope())) { + String nodeValue = isThis ? null : n.getString(); + if (!isThis && keyName != null && nodeValue.equals(keyName)) { + keyReferences.add(n); + } else if (isThis || (valueName != null && + nodeValue.equals(valueName))) { + valueReferences.add(n); + } + } + } + + /** + * As we enter each scope, make sure that the scope doesn't define + * a local variable with the same name as our original callback method + * parameter names. + */ + @Override + public void enterScope(NodeTraversal t) { + if (this.startingScope == null) { + this.startingScope = t.getScope(); + } + } + + @Override + public void exitScope(NodeTraversal t) { } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExploitAssigns.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExploitAssigns.java new file mode 100644 index 0000000..1f925ba --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExploitAssigns.java @@ -0,0 +1,244 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * Tries to chain assignments together. + * + * @author nicksantos@google.com (Nick Santos) + * @author acleung@google.com (Alan Leung) + * + */ +class ExploitAssigns extends AbstractPeepholeOptimization { + + @Override + Node optimizeSubtree(Node subtree) { + for (Node child = subtree.getFirstChild(); child != null;) { + Node next = child.getNext(); + if (NodeUtil.isExprAssign(child)) { + collapseAssign(child.getFirstChild(), child, subtree); + } + child = next; + } + return subtree; + } + + /** + * Try to collapse the given assign into subsequent expressions. + */ + private void collapseAssign(Node assign, Node expr, + Node exprParent) { + Node leftValue = assign.getFirstChild(); + Node rightValue = leftValue.getNext(); + if (isCollapsibleValue(leftValue, true) && + collapseAssignEqualTo(expr, exprParent, leftValue)) { + reportCodeChange(); + } else if (isCollapsibleValue(rightValue, false) && + collapseAssignEqualTo(expr, exprParent, rightValue)) { + reportCodeChange(); + } else if (rightValue.isAssign()) { + // Recursively deal with nested assigns. + collapseAssign(rightValue, expr, exprParent); + } + } + + /** + * Determines whether we know enough about the given value to be able + * to collapse it into subsequent expressions. + * + * For example, we can collapse booleans and variable names: + * + * x = 3; y = x; // y = x = 3; + * a = true; b = true; // b = a = true; + * + * But we won't try to collapse complex expressions. + * + * @param value The value node. + * @param isLValue Whether it's on the left-hand side of an expr. + */ + private boolean isCollapsibleValue(Node value, boolean isLValue) { + switch (value.getType()) { + case Token.GETPROP: + // Do not collapse GETPROPs on arbitrary objects, because + // they may be implemented setter functions, and oftentimes + // setter functions fail on native objects. This is OK for "THIS" + // objects, because we assume that they are non-native. + return !isLValue || value.getFirstChild().isThis(); + + case Token.NAME: + return true; + + default: + return NodeUtil.isImmutableValue(value); + } + } + + /** + * Collapse the given assign expression into the expression directly + * following it, if possible. + * + * @param expr The expression that may be moved. + * @param exprParent The parent of {@code expr}. + * @param value The value of this expression, expressed as a node. Each + * expression may have multiple values, so this function may be called + * multiple times for the same expression. For example, + * + * a = true; + * + * is equal to the name "a" and the boolean "true". + * @return Whether the expression was collapsed successfully. + */ + private boolean collapseAssignEqualTo(Node expr, Node exprParent, + Node value) { + Node assign = expr.getFirstChild(); + Node parent = exprParent; + Node next = expr.getNext(); + while (next != null) { + switch (next.getType()) { + case Token.AND: + case Token.OR: + case Token.HOOK: + case Token.IF: + case Token.RETURN: + case Token.EXPR_RESULT: + // Dive down the left side + parent = next; + next = next.getFirstChild(); + break; + + case Token.VAR: + if (next.getFirstChild().hasChildren()) { + parent = next.getFirstChild(); + next = parent.getFirstChild(); + break; + } + return false; + + case Token.GETPROP: + case Token.NAME: + if (next.isQualifiedName()) { + String nextName = next.getQualifiedName(); + if (value.isQualifiedName() && + nextName.equals(value.getQualifiedName())) { + // If the previous expression evaluates to value of a + // qualified name, and that qualified name is used again + // shortly, then we can exploit the assign here. + + // Verify the assignment doesn't change its own value. + if (!isSafeReplacement(next, assign)) { + return false; + } + + exprParent.removeChild(expr); + expr.removeChild(assign); + parent.replaceChild(next, assign); + return true; + } + } + return false; + + case Token.ASSIGN: + // Assigns are really tricky. In lots of cases, we want to inline + // into the right side of the assign. But the left side of the + // assign is evaluated first, and it may have convoluted logic: + // a = null; + // (a = b).c = null; + // We don't want to exploit the first assign. Similarly: + // a.b = null; + // a.b.c = null; + // We don't want to exploit the first assign either. + // + // To protect against this, we simply only inline when the left side + // is guaranteed to evaluate to the same L-value no matter what. + Node leftSide = next.getFirstChild(); + if (leftSide.isName() || + leftSide.isGetProp() && + leftSide.getFirstChild().isThis()) { + // Dive down the right side of the assign. + parent = next; + next = leftSide.getNext(); + break; + } else { + return false; + } + + default: + if (NodeUtil.isImmutableValue(next) + && next.isEquivalentTo(value)) { + // If the r-value of the expr assign is an immutable value, + // and the value is used again shortly, then we can exploit + // the assign here. + exprParent.removeChild(expr); + expr.removeChild(assign); + parent.replaceChild(next, assign); + return true; + } + // Return without inlining a thing + return false; + } + } + + return false; + } + + /** + * Checks name referenced in node to determine if it might have + * changed. + * @return Whether the replacement can be made. + */ + private boolean isSafeReplacement(Node node, Node replacement) { + // No checks are needed for simple names. + if (node.isName()) { + return true; + } + Preconditions.checkArgument(node.isGetProp()); + + Node name = node.getFirstChild(); + if (name.isName() + && isNameAssignedTo(name.getString(), replacement)) { + return false; + } + + return true; + } + + /** + * @return Whether name is assigned in the expression rooted at node. + */ + + private boolean isNameAssignedTo(String name, Node node) { + for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { + if (isNameAssignedTo(name, c)) { + return true; + } + } + + if (node.isName()) { + Node parent = node.getParent(); + if (parent.isAssign() && parent.getFirstChild() == node) { + if (name.equals(node.getString())) { + return true; + } + } + } + + return false; + } +} \ No newline at end of file diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExportTestFunctions.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExportTestFunctions.java new file mode 100644 index 0000000..4bfceee --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExportTestFunctions.java @@ -0,0 +1,174 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import java.util.regex.Pattern; + +/** + * Generates goog.exportSymbol for test functions, so they can be recognized + * by the test runner, even if the code is compiled. + * + */ +class ExportTestFunctions implements CompilerPass { + + private static final Pattern TEST_FUNCTIONS_NAME_PATTERN = + Pattern.compile("^(?:((\\w+\\.)+prototype\\.)*" + + "(setUpPage|setUp|tearDown|tearDownPage|test\\w+))$"); + + private AbstractCompiler compiler; + private final String exportSymbolFunction; + private final String exportPropertyFunction; + + /** + * Creates a new export test functions compiler pass. + * @param compiler + * @param exportSymbolFunction The function name used to export symbols in JS. + * @param exportPropertyFunction The function name used to export properties + * in JS. + */ + ExportTestFunctions(AbstractCompiler compiler, + String exportSymbolFunction, String exportPropertyFunction) { + + Preconditions.checkNotNull(compiler); + this.compiler = compiler; + this.exportSymbolFunction = exportSymbolFunction; + this.exportPropertyFunction = exportPropertyFunction; + } + + private class ExportTestFunctionsNodes extends + NodeTraversal.AbstractShallowCallback { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + + if (parent == null) { + return; + } + + if (parent.isScript()) { + if (NodeUtil.isFunctionDeclaration(n)) { + // Check for a test function statement. + String functionName = NodeUtil.getFunctionName(n); + if (isTestFunction(n, functionName)) { + exportTestFunctionAsSymbol(functionName, n, parent); + } + } else if (isVarDeclaredFunction(n)) { + // Check for a test function expression. + Node functionNode = n.getFirstChild().getFirstChild(); + String functionName = NodeUtil.getFunctionName(functionNode); + if (isTestFunction(functionNode, functionName)) { + exportTestFunctionAsSymbol(functionName, n, parent); + } + } + } else if (NodeUtil.isExprAssign(parent) && + !n.getLastChild().isAssign()) { + // Check for a test method assignment. + Node grandparent = parent.getParent(); + if (grandparent != null && grandparent.isScript()) { + String functionName = n.getFirstChild().getQualifiedName(); + if (isTestFunction(n, functionName)) { + exportTestFunctionAsProperty(functionName, parent, n, grandparent); + } + } + } + } + + /** + * Whether node corresponds to a function expression declared with var, + * which is of the form: + *
      +     * var functionName = function() {
      +     *   // Implementation
      +     * };
      +     * 
      + * This has the AST structure VAR -> NAME -> FUNCTION + * @param node + */ + private boolean isVarDeclaredFunction(Node node) { + if (!node.isVar()) { + return false; + } + Node grandchild = node.getFirstChild().getFirstChild(); + return grandchild != null && grandchild.isFunction(); + } + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, new ExportTestFunctionsNodes()); + } + + // Adds exportSymbol(testFunctionName, testFunction); + private void exportTestFunctionAsSymbol(String testFunctionName, Node node, + Node scriptNode) { + + Node exportCallTarget = NodeUtil.newQualifiedNameNode( + compiler.getCodingConvention(), + exportSymbolFunction, node, testFunctionName); + Node call = IR.call( exportCallTarget); + if (exportCallTarget.isName()) { + call.putBooleanProp(Node.FREE_CALL, true); + } + call.addChildToBack(IR.string(testFunctionName)); + call.addChildToBack(NodeUtil.newQualifiedNameNode( + compiler.getCodingConvention(), + testFunctionName, node, testFunctionName)); + + Node expression = IR.exprResult(call); + + scriptNode.addChildAfter(expression, node); + compiler.reportCodeChange(); + } + + + // Adds exportProperty() of the test function name on the prototype object + private void exportTestFunctionAsProperty(String fullyQualifiedFunctionName, + Node parent, Node node, Node scriptNode) { + + String testFunctionName = + NodeUtil.getPrototypePropertyName(node.getFirstChild()); + String objectName = fullyQualifiedFunctionName.substring(0, + fullyQualifiedFunctionName.lastIndexOf('.')); + String exportCallStr = String.format("%s(%s, '%s', %s);", + exportPropertyFunction, objectName, testFunctionName, + fullyQualifiedFunctionName); + + Node exportCall = this.compiler.parseSyntheticCode(exportCallStr) + .removeChildren(); + exportCall.useSourceInfoFromForTree(scriptNode); + + scriptNode.addChildAfter(exportCall, parent); + compiler.reportCodeChange(); + } + + + /** + * Whether a function is recognized as a test function. We follow the JsUnit + * convention for naming (functions should start with "test"), and we also + * check if it has no parameters declared. + * + * @param n The function node + * @param functionName The name of the function + * @return {@code true} if the function is recognized as a test function. + */ + private boolean isTestFunction(Node n, String functionName) { + return !(functionName == null + || !TEST_FUNCTIONS_NAME_PATTERN.matcher(functionName).matches()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExpressionDecomposer.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExpressionDecomposer.java new file mode 100644 index 0000000..2344537 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExpressionDecomposer.java @@ -0,0 +1,896 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.javascript.jscomp.MakeDeclaredNamesUnique.ContextualRenamer; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Set; + +/** + * Methods necessary for partially or full decomposing an expression. Initially + * this is intended to expanded the locations were inlining can occur, but has + * other uses as well. + * + * For example: + * var x = y() + z(); + * + * Becomes: + * var a = y(); + * var b = z(); + * x = a + b; + * + * @author johnlenz@google.com (John Lenz) + */ +class ExpressionDecomposer { + + /** + * @see #canExposeExpression + */ + enum DecompositionType { + UNDECOMPOSABLE, + MOVABLE, + DECOMPOSABLE + } + + private final AbstractCompiler compiler; + private final Supplier safeNameIdSupplier; + private final Set knownConstants; + + public ExpressionDecomposer( + AbstractCompiler compiler, + Supplier safeNameIdSupplier, + Set constNames) { + Preconditions.checkNotNull(compiler); + Preconditions.checkNotNull(safeNameIdSupplier); + Preconditions.checkNotNull(constNames); + this.compiler = compiler; + this.safeNameIdSupplier = safeNameIdSupplier; + this.knownConstants = constNames; + } + + // An arbitrary limit to prevent catch infinite recursion. + private static final int MAX_INTERATIONS = 100; + + /** + * If required, rewrite the statement containing the expression. + * @param expression The expression to be exposed. + * @see #canExposeExpression + */ + void maybeExposeExpression(Node expression) { + // If the expression needs to exposed. + int i = 0; + while (DecompositionType.DECOMPOSABLE == canExposeExpression(expression)) { + exposeExpression(expression); + i++; + if (i > MAX_INTERATIONS) { + throw new IllegalStateException( + "DecomposeExpression depth exceeded on :\n" + + expression.toStringTree()); + } + } + } + + /** + * Perform any rewriting necessary so that the specified expression + * is movable. This is a partial expression decomposition. + * @see #canExposeExpression + */ + void exposeExpression(Node expression) { + Node expressionRoot = findExpressionRoot(expression); + Preconditions.checkState(expressionRoot != null); + exposeExpression(expressionRoot, expression); + compiler.reportCodeChange(); + } + + // TODO(johnlenz): This is not currently used by the function inliner, + // as moving the call out of the expression before the actual function + // results in additional variables being introduced. As the variable + // inliner is improved, this might be a viable option. + /** + * Extract the specified expression from its parent expression. + * @see #canExposeExpression + */ + void moveExpression(Node expression) { + String resultName = getResultValueName(); + Node injectionPoint = findInjectionPoint(expression); + Preconditions.checkNotNull(injectionPoint); + Node injectionPointParent = injectionPoint.getParent(); + Preconditions.checkNotNull(injectionPointParent); + Preconditions.checkState(NodeUtil.isStatementBlock(injectionPointParent)); + + // Replace the expression with a reference to the new name. + Node expressionParent = expression.getParent(); + expressionParent.replaceChild( + expression, IR.name(resultName)); + + // Re-add the expression at the appropriate place. + Node newExpressionRoot = NodeUtil.newVarNode(resultName, expression); + injectionPointParent.addChildBefore(newExpressionRoot, injectionPoint); + compiler.reportCodeChange(); + } + + /** + * Rewrite the expression such that the sub-expression is in a movable + * expression statement while maintaining evaluation order. + * + * Two types of subexpressions are extracted from the source expression: + * 1) subexpressions with side-effects. + * 2) conditional expressions, that contain the call, which are transformed + * into IF statements. + * + * The following terms are used: + * expressionRoot: The top-level node before which the any extracted + * expressions should be placed before. + * nonconditionalExpr: The node that will be extracted either expres. + * + */ + private void exposeExpression(Node expressionRoot, Node subExpression) { + Node nonconditionalExpr = findNonconditionalParent( + subExpression, expressionRoot); + // Before extraction, record whether there are side-effect + boolean hasFollowingSideEffects = NodeUtil.mayHaveSideEffects( + nonconditionalExpr, compiler); + + Node exprInjectionPoint = findInjectionPoint(nonconditionalExpr); + DecompositionState state = new DecompositionState(); + state.sideEffects = hasFollowingSideEffects; + state.extractBeforeStatement = exprInjectionPoint; + + // Extract expressions in the reverse order of their evaluation. + for (Node grandchild = null, + child = nonconditionalExpr, + parent = child.getParent(); + parent != expressionRoot; + grandchild = child, + child = parent, + parent = child.getParent()) { + int parentType = parent.getType(); + Preconditions.checkState( + !isConditionalOp(parent) || child == parent.getFirstChild()); + if (parentType == Token.ASSIGN) { + if (isSafeAssign(parent, state.sideEffects)) { + // It is always safe to inline "foo()" for expressions such as + // "a = b = c = foo();" + // As the assignment is unaffected by side effect of "foo()" + // and the names assigned-to can not influence the state before + // the call to foo. + // + // This is not true of more complex LHS values, such as + // a.x = foo(); + // next().x = foo(); + // in these cases the checks below are necessary. + } else { + // Alias "next()" in "next().foo" + Node left = parent.getFirstChild(); + int type = left.getType(); + if (left != child) { + Preconditions.checkState(NodeUtil.isGet(left)); + if (type == Token.GETELEM) { + decomposeSubExpressions(left.getLastChild(), null, state); + } + decomposeSubExpressions(left.getFirstChild(), null, state); + } + } + } else if (parentType == Token.CALL + && NodeUtil.isGet(parent.getFirstChild())) { + Node functionExpression = parent.getFirstChild(); + decomposeSubExpressions(functionExpression.getNext(), child, state); + // Now handle the call expression + if (isExpressionTreeUnsafe(functionExpression, state.sideEffects) + && functionExpression.getFirstChild() != grandchild) { + // TODO(johnlenz): In Internet Explorer, non-JavaScript objects such + // as DOM objects can not be decomposed. + Preconditions.checkState(allowObjectCallDecomposing(), + "Object method calls can not be decomposed."); + // Either there were preexisting side-effects, or this node has + // side-effects. + state.sideEffects = true; + + // Rewrite the call so "this" is preserved. + Node replacement = rewriteCallExpression(parent, state); + // Continue from here. + parent = replacement; + } + } else if (parentType == Token.OBJECTLIT) { + decomposeObjectLiteralKeys(parent.getFirstChild(), child, state); + } else { + decomposeSubExpressions(parent.getFirstChild(), child, state); + } + } + + // Now extract the expression that the decomposition is being performed to + // to allow to be moved. All expressions that need to be evaluated before + // this have been extracted, so add the expression statement after the + // other extracted expressions and the original statement (or replace + // the original statement. + if (nonconditionalExpr == subExpression) { + // Don't extract the call, as that introduces an extra constant VAR + // that will simply need to be inlined back. It will be handled as + // an EXPRESSION call site type. + // Node extractedCall = extractExpression(decomposition, expressionRoot); + } else { + Node parent = nonconditionalExpr.getParent(); + boolean needResult = !parent.isExprResult(); + Node extractedConditional = extractConditional( + nonconditionalExpr, exprInjectionPoint, needResult); + } + } + + private static boolean allowObjectCallDecomposing() { + return false; + } + + /** + * @return Whether the node may represent an external method. + */ + private boolean maybeExternMethod(Node node) { + // TODO(johnlenz): Provide some mechanism for determining this. + return true; + } + + /** + * @return "expression" or the node closest to "expression", that does not + * have a conditional ancestor. + */ + private static Node findNonconditionalParent( + Node subExpression, Node expressionRoot) { + Node result = subExpression; + + for (Node child = subExpression, parent = child.getParent(); + parent != expressionRoot; + child = parent, parent = child.getParent()) { + if (isConditionalOp(parent)) { + // Only the first child is always executed, if the function may never + // be called, don't inline it. + if (child != parent.getFirstChild()) { + result = parent; + } + } + } + + return result; + } + + /** + * A simple class to track two things: + * - whether side effects have been seen. + * - the last statement inserted + */ + private static class DecompositionState { + boolean sideEffects; + Node extractBeforeStatement; + } + + /** + * Decompose an object literal. + * @param key The object literal key. + * @param stopNode A node after which to stop iterating. + */ + private void decomposeObjectLiteralKeys( + Node key, Node stopNode, DecompositionState state) { + if (key == null || key == stopNode) { + return; + } + decomposeObjectLiteralKeys(key.getNext(), stopNode, state); + decomposeSubExpressions(key.getFirstChild(), stopNode, state); + } + + /** + * @param n The node with which to start iterating. + * @param stopNode A node after which to stop iterating. + */ + private void decomposeSubExpressions( + Node n, Node stopNode, DecompositionState state) { + if (n == null || n == stopNode) { + return; + } + + // Never try to decompose an object literal key. + Preconditions.checkState(!NodeUtil.isObjectLitKey(n, n.getParent())); + + // Decompose the children in reverse evaluation order. This simplifies + // determining if the any of the children following have side-effects. + // If they do we need to be more aggressive about removing values + // from the expression. + decomposeSubExpressions( + n.getNext(), stopNode, state); + + // Now this node. + // TODO(johnlenz): Move "safety" code to a shared class. + if (isExpressionTreeUnsafe(n, state.sideEffects)) { + // Either there were preexisting side-effects, or this node has + // side-effects. + state.sideEffects = true; + state.extractBeforeStatement = extractExpression( + n, state.extractBeforeStatement); + } + } + + /** + * + * @param expr The conditional expression to extract. + * @param injectionPoint The before which extracted expression, would be + * injected. + * @param needResult Whether the result of the expression is required. + * @return The node that contains the logic of the expression after + * extraction. + */ + private Node extractConditional( + Node expr, Node injectionPoint, boolean needResult) { + Node parent = expr.getParent(); + String tempName = getTempValueName(); + + // Break down the conditional. + Node first = expr.getFirstChild(); + Node second = first.getNext(); + Node last = expr.getLastChild(); + + // Isolate the children nodes. + expr.detachChildren(); + + // Transform the conditional to an IF statement. + Node cond = null; + Node trueExpr = IR.block().srcref(expr); + Node falseExpr = IR.block().srcref(expr); + switch (expr.getType()) { + case Token.HOOK: + // a = x?y:z --> if (x) {a=y} else {a=z} + cond = first; + trueExpr.addChildToFront(NodeUtil.newExpr( + buildResultExpression(second, needResult, tempName))); + falseExpr.addChildToFront(NodeUtil.newExpr( + buildResultExpression(last, needResult, tempName))); + break; + case Token.AND: + // a = x&&y --> if (a=x) {a=y} else {} + cond = buildResultExpression(first, needResult, tempName); + trueExpr.addChildToFront(NodeUtil.newExpr( + buildResultExpression(last, needResult, tempName))); + break; + case Token.OR: + // a = x||y --> if (a=x) {} else {a=y} + cond = buildResultExpression(first, needResult, tempName); + falseExpr.addChildToFront(NodeUtil.newExpr( + buildResultExpression(last, needResult, tempName))); + break; + default: + // With a valid tree we should never get here. + throw new IllegalStateException("Unexpected."); + } + + Node ifNode; + if (falseExpr.hasChildren()) { + ifNode = IR.ifNode(cond, trueExpr, falseExpr); + } else { + ifNode = IR.ifNode(cond, trueExpr); + } + ifNode.copyInformationFrom(expr); + + if (needResult) { + Node tempVarNode = NodeUtil.newVarNode(tempName, null) + .copyInformationFromForTree(expr); + Node injectionPointParent = injectionPoint.getParent(); + injectionPointParent.addChildBefore(tempVarNode, injectionPoint); + injectionPointParent.addChildAfter(ifNode, tempVarNode); + + // Replace the expression with the temporary name. + Node replacementValueNode = IR.name(tempName); + parent.replaceChild(expr, replacementValueNode); + } else { + // Only conditionals that are the direct child of an expression statement + // don't need results, for those simply replace the expression statement. + Preconditions.checkArgument(parent.isExprResult()); + Node gramps = parent.getParent(); + gramps.replaceChild(parent, ifNode); + } + + return ifNode; + } + + /** + * Create an expression tree for an expression. + * If the result of the expression is needed, then: + * ASSIGN + * tempName + * expr + * otherwise, simply: + * expr + */ + private static Node buildResultExpression( + Node expr, boolean needResult, String tempName) { + if (needResult) { + return IR.assign( + IR.name(tempName), + expr).srcrefTree(expr); + } else { + return expr; + } + } + + private boolean isConstantName(Node n, Set knownConstants) { + // Non-constant names values may have been changed. + return n.isName() && (NodeUtil.isConstantName(n) + || knownConstants.contains(n.getString())); + } + + /** + * @param expr The expression to extract. + * @param injectionPoint The node before which to added the extracted + * expression. + * @return The extract statement node. + */ + private Node extractExpression(Node expr, Node injectionPoint) { + Node parent = expr.getParent(); + + boolean isLhsOfAssignOp = NodeUtil.isAssignmentOp(parent) + && !parent.isAssign() + && parent.getFirstChild() == expr; + + Node firstExtractedNode = null; + + // Expressions on the LHS of an assignment-op must have any possible + // side-effects extracted as the value must be duplicated: + // next().foo += 2; + // becomes: + // var t1 = next(); + // t1.foo = t1.foo + 2; + if (isLhsOfAssignOp && NodeUtil.isGet(expr)) { + for (Node n : expr.children()) { + if (!n.isString() && !isConstantName(n, knownConstants)) { + Node extractedNode = extractExpression(n, injectionPoint); + if (firstExtractedNode == null) { + firstExtractedNode = extractedNode; + } + } + } + } + + // The temp is known to be constant. + String tempName = getTempConstantValueName(); + Node replacementValueNode = IR.name(tempName).srcref(expr); + + Node tempNameValue; + + // If it is ASSIGN_XXX, keep the assignment in place and extract the + // original value of the LHS operand. + if (isLhsOfAssignOp) { + Preconditions.checkState(expr.isName() || NodeUtil.isGet(expr)); + // Transform "x += 2" into "x = temp + 2" + Node opNode = new Node(NodeUtil.getOpFromAssignmentOp(parent)) + .copyInformationFrom(parent); + + Node rightOperand = parent.getLastChild(); + + parent.setType(Token.ASSIGN); + parent.replaceChild(rightOperand, opNode); + opNode.addChildToFront(replacementValueNode); + opNode.addChildToBack(rightOperand); + + // The original expression is still being used, so make a clone. + tempNameValue = expr.cloneTree(); + } else { + // Replace the expression with the temporary name. + parent.replaceChild(expr, replacementValueNode); + + // Keep the original node so that CALL expressions can still be found + // and inlined properly. + tempNameValue = expr; + } + + // Re-add the expression in the declaration of the temporary name. + Node tempVarNode = NodeUtil.newVarNode(tempName, tempNameValue); + + Node injectionPointParent = injectionPoint.getParent(); + injectionPointParent.addChildBefore(tempVarNode, injectionPoint); + + if (firstExtractedNode == null) { + firstExtractedNode = tempVarNode; + } + return firstExtractedNode; + } + + /** + * Rewrite the call so "this" is preserved. + * a.b(c); + * becomes: + * var temp1 = a; + * var temp0 = temp1.b; + * temp0.call(temp1,c); + * + * @return The replacement node. + */ + private Node rewriteCallExpression(Node call, DecompositionState state) { + Preconditions.checkArgument(call.isCall()); + Node first = call.getFirstChild(); + Preconditions.checkArgument(NodeUtil.isGet(first)); + + // Extracts the expression representing the function to call. For example: + // "a['b'].c" from "a['b'].c()" + Node getVarNode = extractExpression( + first, state.extractBeforeStatement); + state.extractBeforeStatement = getVarNode; + + // Extracts the object reference to be used as "this". For example: + // "a['b']" from "a['b'].c" + Node getExprNode = getVarNode.getFirstChild().getFirstChild(); + Preconditions.checkArgument(NodeUtil.isGet(getExprNode)); + Node thisVarNode = extractExpression( + getExprNode.getFirstChild(), state.extractBeforeStatement); + state.extractBeforeStatement = thisVarNode; + + // Rewrite the CALL expression. + Node thisNameNode = thisVarNode.getFirstChild(); + Node functionNameNode = getVarNode.getFirstChild(); + + // CALL + // GETPROP + // functionName + // "call" + // thisName + // original-parameter1 + // original-parameter2 + // ... + Node newCall = IR.call( + IR.getprop( + functionNameNode.cloneNode(), + IR.string("call")), + thisNameNode.cloneNode()).srcref(call); + + // Throw away the call name + call.removeFirstChild(); + if (call.hasChildren()) { + // Add the call parameters to the new call. + newCall.addChildrenToBack(call.removeChildren()); + } + + // Replace the call. + Node callParent = call.getParent(); + callParent.replaceChild(call, newCall); + + return newCall; + } + + private String tempNamePrefix = "JSCompiler_temp"; + private String resultNamePrefix = "JSCompiler_inline_result"; + + /** + * Allow the temp name to be overridden to make tests more readable. + */ + @VisibleForTesting + public void setTempNamePrefix(String prefix) { + this.tempNamePrefix = prefix; + } + + /** + * Create a unique temp name. + */ + private String getTempValueName(){ + return tempNamePrefix + ContextualRenamer.UNIQUE_ID_SEPARATOR + + safeNameIdSupplier.get(); + } + + /** + * Allow the temp name to be overridden to make tests more readable. + */ + @VisibleForTesting + public void setResultNamePrefix(String prefix) { + this.resultNamePrefix = prefix; + } + + /** + * Create a unique name for call results. + */ + private String getResultValueName() { + return resultNamePrefix + + ContextualRenamer.UNIQUE_ID_SEPARATOR + safeNameIdSupplier.get(); + } + + /** + * Create a constant unique temp name. + */ + private String getTempConstantValueName(){ + String name = tempNamePrefix + "_const" + + ContextualRenamer.UNIQUE_ID_SEPARATOR + + safeNameIdSupplier.get(); + this.knownConstants.add(name); + return name; + } + + /** + * @return For the subExpression, find the nearest statement Node before which + * it can be inlined. Null if no such location can be found. + */ + static Node findInjectionPoint(Node subExpression) { + Node expressionRoot = findExpressionRoot(subExpression); + Preconditions.checkNotNull(expressionRoot); + + Node injectionPoint = expressionRoot; + + Node parent = injectionPoint.getParent(); + while (parent.isLabel()) { + injectionPoint = parent; + parent = injectionPoint.getParent(); + } + + Preconditions.checkState( + NodeUtil.isStatementBlock(injectionPoint.getParent())); + return injectionPoint; + } + + /** + * @return Whether the node is a conditional op. + */ + private static boolean isConditionalOp(Node n) { + switch(n.getType()) { + case Token.HOOK: + case Token.AND: + case Token.OR: + return true; + default: + return false; + } + } + + /** + * @return The statement containing the expression. null if subExpression + * is not contain by in by a Node where inlining is known to be possible. + * For example, a WHILE node condition expression. + */ + static Node findExpressionRoot(Node subExpression) { + Node child = subExpression; + for (Node parent : child.getAncestors()) { + int parentType = parent.getType(); + switch (parentType) { + // Supported expression roots: + // SWITCH and IF can have multiple children, but the CASE, DEFAULT, + // or BLOCK will be encountered first for any of the children other + // than the condition. + case Token.EXPR_RESULT: + case Token.IF: + case Token.SWITCH: + case Token.RETURN: + case Token.VAR: + Preconditions.checkState(child == parent.getFirstChild()); + return parent; + // Any of these indicate an unsupported expression: + case Token.SCRIPT: + case Token.BLOCK: + case Token.LABEL: + case Token.CASE: + case Token.DEFAULT_CASE: + return null; + } + child = parent; + } + + throw new IllegalStateException("Unexpected AST structure."); + } + + /** + * Determine whether a expression is movable, or can be be made movable be + * decomposing the containing expression. + * + * An subExpression is MOVABLE if it can be replaced with a temporary holding + * its results and moved to immediately before the root of the expression. + * There are three conditions that must be met for this to occur: + * 1) There must be a location to inject a statement for the expression. For + * example, this condition can not be met if the expression is a loop + * condition or CASE condition. + * 2) If the expression can be affect by side-effects, there can not be a + * side-effect between original location and the expression root. + * 3) If the expression has side-effects, there can not be any other + * expression that can be effected between the original location and the + * expression root. + * + * An expression is DECOMPOSABLE if it can be rewritten so that an + * subExpression is MOVABLE. + * + * An expression is decomposed by moving any other sub-expressions that + * preventing an subExpression from being MOVABLE. + * + * @return Whether This is a call that can be moved to an new point in the + * AST to allow it to be inlined. + */ + DecompositionType canExposeExpression(Node subExpression) { + Node expressionRoot = findExpressionRoot(subExpression); + if (expressionRoot != null) { + return isSubexpressionMovable(expressionRoot, subExpression); + } + return DecompositionType.UNDECOMPOSABLE; + } + + /** + * Walk the AST from the call site to the expression root and verify that + * the portions of the expression that are evaluated before the call are: + * 1) Unaffected by the the side-effects, if any, of the call. + * 2) That there are no side-effects, that may influence the call. + * + * For example, if x has side-effects: + * a = 1 + x(); + * the call to x can be moved because "a" final value of a can not be + * influenced by x(), but in: + * a = b + x(); + * the call to x can not be moved because the value of b may be modified + * by the call to x. + * + * If x is without side-effects in: + * a = b + x(); + * the call to x can be moved, but in: + * a = (b.foo = c) + x(); + * the call to x can not be moved because the value of b.foo may be referenced + * by x(). Note: this is true even if b is a local variable; the object that + * b refers to may have a global alias. + * + * @return UNDECOMPOSABLE if the expression can not be moved, DECOMPOSABLE if + * decomposition is required before the expression can be moved, otherwise + * MOVABLE. + */ + private DecompositionType isSubexpressionMovable( + Node expressionRoot, Node subExpression) { + boolean requiresDecomposition = false; + boolean seenSideEffects = NodeUtil.mayHaveSideEffects( + subExpression, compiler); + + Node child = subExpression; + for (Node parent : child.getAncestors()) { + if (parent == expressionRoot) { + // Done. The walk back to the root of the expression is complete, and + // nothing was encountered that blocks the call from being moved. + return requiresDecomposition + ? DecompositionType.DECOMPOSABLE + : DecompositionType.MOVABLE; + } + + int parentType = parent.getType(); + + if (isConditionalOp(parent)) { + // Only the first child is always executed, otherwise it must be + // decomposed. + if (child != parent.getFirstChild()) { + requiresDecomposition = true; + } + } else { + // Only inline the call if none of the preceding siblings in the + // expression have side-effects, and are unaffected by the side-effects, + // if any, of the call in question. + // NOTE: This depends on the siblings being in the same order as they + // are evaluated. + + // SPECIAL CASE: Assignment to a simple name + if (isSafeAssign(parent, seenSideEffects)) { + // It is always safe to inline "foo()" for expressions such as + // "a = b = c = foo();" + // As the assignment is unaffected by side effect of "foo()" + // and the names assigned-to can not influence the state before + // the call to foo. + // + // This is not true of more complex LHS values, such as + // a.x = foo(); + // next().x = foo(); + // in these cases the checks below are necessary. + } else { + // Everything else. + for (Node n : parent.children()) { + if (n == child) { + // None of the preceding siblings have side-effects. + // This is OK. + break; + } + + if (isExpressionTreeUnsafe( + n, seenSideEffects)) { + seenSideEffects = true; + requiresDecomposition = true; + } + } + + // In Internet Explorer, DOM objects and other external objects + // methods can not be called indirectly, as is required when the + // object or its property can be side-effected. For example, + // when exposing expression f() (with side-effects) in: x.m(f()) + // either the value of x or its property m might have changed, so + // both the 'this' value ('x') and the function to be called ('x.m') + // need to be preserved. Like so: + // var t1 = x, t2 = x.m, t3 = f(); + // t2.call(t1, t3); + // As IE doesn't support the call to these non-JavaScript objects + // methods in this way. We can't do this. + // We don't currently distinguish between these types of objects + // in the extern definitions and if we did we would need accurate + // type information. + // + Node first = parent.getFirstChild(); + if (requiresDecomposition + && parent.isCall() + && NodeUtil.isGet(first)) { + if (maybeExternMethod(first)) { + return DecompositionType.UNDECOMPOSABLE; + } else { + return DecompositionType.DECOMPOSABLE; + } + } + } + } + // Continue looking up the expression tree. + child = parent; + } + + // With a valid tree we should never get here. + throw new IllegalStateException("Unexpected."); + } + + /** + * It is always safe to inline "foo()" for expressions such as + * "a = b = c = foo();" + * As the assignment is unaffected by side effect of "foo()" + * and the names assigned-to can not influence the state before + * the call to foo. + * + * It is also safe in cases like where the object is constant: + * CONST_NAME.a = foo() + * CONST_NAME[CONST_VALUE] = foo(); + * + * This is not true of more complex LHS values, such as + * a.x = foo(); + * next().x = foo(); + * in these cases the checks below are necessary. + * + * @param seenSideEffects If true, check to see if node-tree maybe affected by + * side-effects, otherwise if the tree has side-effects. @see + * isExpressionTreeUnsafe + * @return Whether the assignment is safe from side-effects. + */ + private boolean isSafeAssign(Node n, boolean seenSideEffects) { + if (n.isAssign()) { + Node lhs = n.getFirstChild(); + switch (lhs.getType()) { + case Token.NAME: + return true; + case Token.GETPROP: + return !isExpressionTreeUnsafe(lhs.getFirstChild(), seenSideEffects); + case Token.GETELEM: + return !isExpressionTreeUnsafe(lhs.getFirstChild(), seenSideEffects) + && !isExpressionTreeUnsafe(lhs.getLastChild(), seenSideEffects); + } + } + return false; + } + + /** + * @return Whether anything in the expression tree prevents a call from + * being moved. + */ + private boolean isExpressionTreeUnsafe( + Node n, boolean followingSideEffectsExist) { + if (followingSideEffectsExist) { + // If the call to be inlined has side-effects, check to see if this + // expression tree can be affected by any side-effects. + + // This is a superset of "NodeUtil.mayHaveSideEffects". + return NodeUtil.canBeSideEffected(n, this.knownConstants); + } else { + // The function called doesn't have side-effects but check to see if there + // are side-effects that that may affect it. + return NodeUtil.mayHaveSideEffects(n, compiler); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExternExportsPass.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExternExportsPass.java new file mode 100644 index 0000000..5c455d3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExternExportsPass.java @@ -0,0 +1,562 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * Creates an externs file containing all exported symbols and properties + * for later consumption. + * + */ +final class ExternExportsPass extends NodeTraversal.AbstractPostOrderCallback + implements CompilerPass { + + static final DiagnosticType EXPORTED_FUNCTION_UNKNOWN_PARAMETER_TYPE = + DiagnosticType.warning( + "JSC_EXPORTED_FUNCTION_UNKNOWN_PARAMETER_TYPE", + "Unable to determine type of parameter {0} for exported function {1}"); + + static final DiagnosticType EXPORTED_FUNCTION_UNKNOWN_RETURN_TYPE = + DiagnosticType.warning( + "JSC_EXPORTED_FUNCTION_UNKNOWN_RETURN_TYPE", + "Unable to determine return type for exported function {0}"); + + /** The exports found. */ + private final List exports; + + /** A map of all assigns to their parent nodes. */ + private final Map definitionMap; + + /** The parent compiler. */ + private final AbstractCompiler compiler; + + /** The AST root which holds the externs generated. */ + private final Node externsRoot; + + /** A mapping of internal paths to exported paths. */ + private final Map mappedPaths; + + /** A list of exported paths. */ + private final Set alreadyExportedPaths; + + /** A list of function names used to export symbols. */ + private List exportSymbolFunctionNames; + + /** A list of function names used to export properties. */ + private List exportPropertyFunctionNames; + + private abstract class Export { + protected final String symbolName; + protected final Node value; + + Export(String symbolName, Node value) { + this.symbolName = symbolName; + this.value = value; + } + + /** + * Generates the externs representation of this export and appends + * it to the externsRoot AST. + */ + void generateExterns() { + appendExtern(getExportedPath(), getValue(value)); + } + + /** + * Returns the path exported by this export. + */ + abstract String getExportedPath(); + + /** + * Appends the exported function and all paths necessary for the path to be + * declared. For example, for a property "a.b.c", the initializers for + * paths "a", "a.b" will be appended (if they have not already) and a.b.c + * will be initialized with the exported version of the function: + *
      +     * var a = {};
      +     * a.b = {};
      +     * a.b.c = function(x,y) { }
      +     * 
      + */ + void appendExtern(String path, Node valueToExport) { + List pathPrefixes = computePathPrefixes(path); + + for (int i = 0; i < pathPrefixes.size(); ++i) { + String pathPrefix = pathPrefixes.get(i); + + /* The complete path (the last path prefix) must be emitted and + * it gets initialized to the externed version of the value. + */ + boolean isCompletePathPrefix = (i == pathPrefixes.size() - 1); + + boolean skipPathPrefix = pathPrefix.endsWith(".prototype") + || (alreadyExportedPaths.contains(pathPrefix) + && !isCompletePathPrefix); + + if (!skipPathPrefix) { + Node initializer; + + /* Namespaces get initialized to {}, functions to + * externed versions of their value, and if we can't + * figure out where the value came from we initialize + * it to {}. + * + * Since externs are always exported in sorted order, + * we know that if we export a.b = function() {} and later + * a.b.c = function then a.b will always be in alreadyExportedPaths + * when we emit a.b.c and thus we will never overwrite the function + * exported for a.b with a namespace. + */ + + if (isCompletePathPrefix && valueToExport != null) { + if (valueToExport.isFunction()) { + initializer = createExternFunction(valueToExport); + } else { + Preconditions.checkState(valueToExport.isObjectLit()); + initializer = createExternObjectLit(valueToExport); + } + } else { + initializer = IR.empty(); + } + + appendPathDefinition(pathPrefix, initializer); + } + } + } + + /** + * Computes a list of the path prefixes constructed from the components + * of the path. + *
      +     * E.g., if the path is:
      +     *      "a.b.c"
      +     * then then path prefixes will be
      +     *    ["a","a.b","a.b.c"]:
      +     * 
      + */ + private List computePathPrefixes(String path) { + List pieces = Lists.newArrayList(path.split("\\.")); + + List pathPrefixes = Lists.newArrayList(); + + for (int i = 0; i < pieces.size(); i++) { + pathPrefixes.add(Joiner.on(".").join(Iterables.limit(pieces, i + 1))); + } + + return pathPrefixes; + } + + private void appendPathDefinition(String path, Node initializer) { + Node pathDefinition; + + if (!path.contains(".")) { + if (initializer.isEmpty()) { + pathDefinition = IR.var(IR.name(path)); + } else { + pathDefinition = NodeUtil.newVarNode(path, initializer); + } + } else { + Node qualifiedPath = NodeUtil.newQualifiedNameNode( + compiler.getCodingConvention(), path); + if (initializer.isEmpty()) { + pathDefinition = NodeUtil.newExpr(qualifiedPath); + } else { + pathDefinition = NodeUtil.newExpr( + IR.assign(qualifiedPath, initializer)); + } + } + + externsRoot.addChildToBack(pathDefinition); + + alreadyExportedPaths.add(path); + } + + /** + * Given a function to export, create the empty function that + * will be put in the externs file. This extern function should have + * the same type as the original function and the same parameter + * name but no function body. + * + * We create a warning here if the the function to export is missing + * parameter or return types. + */ + private Node createExternFunction(Node exportedFunction) { + Node paramList = NodeUtil.getFunctionParameters(exportedFunction) + .cloneTree(); + Node externFunction = IR.function(IR.name(""), paramList, IR.block()); + + checkForFunctionsWithUnknownTypes(exportedFunction); + externFunction.setJSType(exportedFunction.getJSType()); + + return externFunction; + } + + /** + * Given an object literal to export, create an object lit with all its + * string properties. We don't care what the values of those properties + * are because they are not checked. + */ + private Node createExternObjectLit(Node exportedObjectLit) { + Node lit = IR.objectlit(); + lit.setJSType(exportedObjectLit.getJSType()); + + // This is an indirect way of telling the typed code generator + // "print the type of this" + lit.setJSDocInfo(new JSDocInfo()); + + int index = 1; + for (Node child = exportedObjectLit.getFirstChild(); + child != null; + child = child.getNext()) { + // TODO: handle getters or setters? + if (child.isStringKey()) { + lit.addChildToBack( + IR.propdef( + IR.stringKey(child.getString()), + IR.number(index++))); + } + } + return lit; + } + + /** + * Warn the user if there is an exported function for which a parameter + * or return type is unknown. + */ + private void checkForFunctionsWithUnknownTypes(Node function) { + Preconditions.checkArgument(function.isFunction()); + + FunctionType functionType = + JSType.toMaybeFunctionType(function.getJSType()); + + if (functionType == null) { + // No type information is available (CheckTypes was probably not run) + // so just bail. + return; + } + + /* We must get the JSDocInfo from the function's type since the function + * itself does not have an associated JSDocInfo node. + */ + JSDocInfo functionJSDocInfo = functionType.getJSDocInfo(); + + JSType returnType = functionType.getReturnType(); + + /* It is OK if a constructor doesn't have a return type */ + if (!functionType.isConstructor() && + (returnType == null || returnType.isUnknownType())) { + reportUnknownReturnType(function); + } + + /* We can't just use the function's type's getParameters() to get the + * parameter nodes because the nodes returned from that method + * do not have names or locations. Similarly, the function's AST parameter + * nodes do not have JSTypes(). So we walk both lists of parameter nodes + * in lock step getting parameter names from the first and types from the + * second. + */ + Node astParameterIterator = NodeUtil.getFunctionParameters(function) + .getFirstChild(); + + Node typeParameterIterator = functionType.getParametersNode() + .getFirstChild(); + + while (astParameterIterator != null) { + JSType parameterType = typeParameterIterator.getJSType(); + + if (parameterType == null || parameterType.isUnknownType()) { + reportUnknownParameterType(function, astParameterIterator); + } + + astParameterIterator = astParameterIterator.getNext(); + typeParameterIterator = typeParameterIterator.getNext(); + } + } + + private void reportUnknownParameterType(Node function, Node parameter) { + compiler.report(JSError.make(NodeUtil.getSourceName(function), + parameter, CheckLevel.WARNING, + EXPORTED_FUNCTION_UNKNOWN_PARAMETER_TYPE, + NodeUtil.getFunctionName(function), parameter.getString())); + } + + private void reportUnknownReturnType(Node function) { + compiler.report(JSError.make(NodeUtil.getSourceName(function), + function, CheckLevel.WARNING, EXPORTED_FUNCTION_UNKNOWN_RETURN_TYPE, + NodeUtil.getFunctionName(function))); + } + + /** + * If the given value is a qualified name which refers + * a function or object literal, the node is returned. Otherwise, + * {@code null} is returned. + */ + protected Node getValue(Node qualifiedNameNode) { + String qualifiedName = value.getQualifiedName(); + + if (qualifiedName == null) { + return null; + } + + Node definitionParent = definitionMap.get(qualifiedName); + if (definitionParent == null) { + return null; + } + + Node definition; + + switch (definitionParent.getType()) { + case Token.ASSIGN: + definition = definitionParent.getLastChild(); + break; + case Token.VAR: + definition = definitionParent.getLastChild().getLastChild(); + break; + default: + return null; + } + + if (!definition.isFunction() && !definition.isObjectLit()) { + return null; + } + + return definition; + } + } + + /** + * A symbol export. + */ + private class SymbolExport extends Export { + + public SymbolExport(String symbolName, Node value) { + super(symbolName, value); + + String qualifiedName = value.getQualifiedName(); + + if (qualifiedName != null) { + mappedPaths.put(qualifiedName, symbolName); + } + } + + @Override + String getExportedPath() { + return symbolName; + } + } + + /** + * A property export. + */ + private class PropertyExport extends Export { + private final String exportPath; + + public PropertyExport(String exportPath, String symbolName, Node value) { + super(symbolName, value); + + this.exportPath = exportPath; + } + + @Override + String getExportedPath() { + + // Find the longest path that has been mapped (if any). + List pieces = Lists.newArrayList(exportPath.split("\\.")); + + for (int i = pieces.size(); i > 0; i--) { + // Find the path of the current length. + String cPath = Joiner.on(".").join(Iterables.limit(pieces, i)); + + // If this path is mapped, return the mapped path plus any remaining + // pieces. + if (mappedPaths.containsKey(cPath)) { + String newPath = mappedPaths.get(cPath); + + if (i < pieces.size()) { + newPath += "." + Joiner.on(".").join(Iterables.skip(pieces, i)); + } + + return newPath + "." + symbolName; + } + } + + return exportPath + "." + symbolName; + } + } + + /** + * Creates an instance. + */ + ExternExportsPass(AbstractCompiler compiler) { + this.exports = Lists.newArrayList(); + this.compiler = compiler; + this.definitionMap = Maps.newHashMap(); + this.externsRoot = IR.block(); + this.externsRoot.setIsSyntheticBlock(true); + this.alreadyExportedPaths = Sets.newHashSet(); + this.mappedPaths = Maps.newHashMap(); + + initExportMethods(); + } + + private void initExportMethods() { + exportSymbolFunctionNames = Lists.newArrayList(); + exportPropertyFunctionNames = Lists.newArrayList(); + + // From Closure: + // goog.exportSymbol = function(publicName, symbol) + // goog.exportProperty = function(object, publicName, symbol) + CodingConvention convention = compiler.getCodingConvention(); + exportSymbolFunctionNames.add(convention.getExportSymbolFunction()); + exportPropertyFunctionNames.add(convention.getExportPropertyFunction()); + + // Another common one used inside google: + exportSymbolFunctionNames.add("google_exportSymbol"); + exportPropertyFunctionNames.add("google_exportProperty"); + } + + @Override + public void process(Node externs, Node root) { + new NodeTraversal(compiler, this).traverse(root); + + // Sort by path length to ensure that the longer + // paths (which may depend on the shorter ones) + // come later. + Set sorted = + new TreeSet(new Comparator() { + @Override + public int compare(Export e1, Export e2) { + return e1.getExportedPath().compareTo(e2.getExportedPath()); + } + }); + + sorted.addAll(exports); + + for (Export export : sorted) { + export.generateExterns(); + } + } + + /** + * Returns the generated externs. + */ + public String getGeneratedExterns() { + CodePrinter.Builder builder = new CodePrinter.Builder(externsRoot) + .setPrettyPrint(true).setOutputTypes(true); + + return builder.build(); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + + case Token.NAME: + case Token.GETPROP: + String name = n.getQualifiedName(); + if (name == null) { + return; + } + + if (parent.isAssign() || parent.isVar()) { + definitionMap.put(name, parent); + } + + // Only handle function calls. This avoids assignments + // that do not export items directly. + if (!parent.isCall()) { + return; + } + + if (exportPropertyFunctionNames.contains(name)) { + handlePropertyExport(parent); + } + + if (exportSymbolFunctionNames.contains(name)) { + handleSymbolExport(parent); + } + } + } + + private void handleSymbolExport(Node parent) { + // Ensure that we only check valid calls with the 2 arguments + // (plus the GETPROP node itself). + if (parent.getChildCount() != 3) { + return; + } + + Node thisNode = parent.getFirstChild(); + Node nameArg = thisNode.getNext(); + Node valueArg = nameArg.getNext(); + + // Confirm the arguments are the expected types. If they are not, + // then we have an export that we cannot statically identify. + if (!nameArg.isString()) { + return; + } + + // Add the export to the list. + this.exports.add(new SymbolExport(nameArg.getString(), valueArg)); + } + + private void handlePropertyExport(Node parent) { + // Ensure that we only check valid calls with the 3 arguments + // (plus the GETPROP node itself). + if (parent.getChildCount() != 4) { + return; + } + + Node thisNode = parent.getFirstChild(); + Node objectArg = thisNode.getNext(); + Node nameArg = objectArg.getNext(); + Node valueArg = nameArg.getNext(); + + // Confirm the arguments are the expected types. If they are not, + // then we have an export that we cannot statically identify. + if (!objectArg.isQualifiedName()) { + return; + } + + if (!nameArg.isString()) { + return; + } + + // Add the export to the list. + this.exports.add( + new PropertyExport(objectArg.getQualifiedName(), + nameArg.getString(), + valueArg)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExtractPrototypeMemberDeclarations.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExtractPrototypeMemberDeclarations.java new file mode 100644 index 0000000..04d88ae --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ExtractPrototypeMemberDeclarations.java @@ -0,0 +1,366 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import java.util.LinkedList; +import java.util.List; + +/** + * When there are multiple prototype member declarations to the same class, + * use a temp variable to alias the prototype object. + * + * Example: + * + *
      + * function B() { ... }                 \
      + * B.prototype.foo = function() { ... }  \___ {@link ExtractionInstance}
      + * ...                                   /
      + * B.prototype.bar = function() { ... } /
      + *          ^---------------------------------{@link PrototypeMemberDeclaration}
      + * 
      + *

      becomes + *

      + * function B() { ... }
      + * x = B.prototype;
      + * x.foo = function() { ... }
      + * ...
      + * x.bar = function() { ... }
      + * 
      + * + *

      Works almost like a redundant load elimination but limited to only + * recognizing the class prototype declaration idiom. First it only works within + * a basic block because we avoided {@link DataFlowAnalysis} for compilation + * performance. Secondly, we can avoid having to compute how long to + * sub-expressing has to be. Example: + *

      + * a.b.c.d = ...
      + * a.b.c = ...
      + * a.b = ...
      + * a.b.c = ...
      + * 
      + *

      Further more, we only introduce one temp variable to hold a single + * prototype at a time. So all the {@link PrototypeMemberDeclaration} + * to be extracted must be in a single line. We call this a single + * {@link ExtractionInstance}. + * + *

      Alternatively, for users who do not want a global variable to be + * introduced, we will create an anonymous function instead. + *

      + * function B() { ... }
      + * (function (x) {
      + *   x.foo = function() { ... }
      + *   ...
      + *   x.bar = function() { ... }
      + * )(B.prototype)
      + * 
      + * + * The RHS of the declarations can have side effects, however, one good way to + * break this is the following: + *
      + * function B() { ... }
      + * B.prototype.foo = (function() { B.prototype = somethingElse(); return 0 })();
      + * ...
      + * 
      + * Such logic is highly unlikely and we will assume that it never occurs. + * + */ +class ExtractPrototypeMemberDeclarations implements CompilerPass { + + // The name of variable that will temporary hold the pointer to the prototype + // object. Of cause, we assume that it'll be renamed by RenameVars. + private String prototypeAlias = "JSCompiler_prototypeAlias"; + + private final AbstractCompiler compiler; + + private final Pattern pattern; + + enum Pattern { + USE_GLOBAL_TEMP( + // Global Overhead. + // We need a temp variable to hold all the prototype. + "var t;".length(), + // Per Extract overhead: + // Every extraction instance must first use the temp variable to point + // to the prototype object. + "t=y.prototype;".length(), + // TODO(user): Check to to see if AliasExterns is on + // The gain we get per prototype declaration. Assuming it can be + // aliased. + "t.y=".length() - "x[p].y=".length()), + + USE_ANON_FUNCTION( + // Global Overhead: + 0, + // Per-extraction overhead: + // This is the cost of a single anoynmous function. + "(function(t){})(y.prototype);".length(), + // Per-prototype member declaration overhead: + // Here we assumes that they don't have AliasExterns on (in SIMPLE mode). + "t.y=".length() - "x.prototype.y=".length()); + + + private final int globalOverhead; + private final int perExtractionOverhead; + private final int perMemberOverhead; + + Pattern(int globalOverHead, int perExtractionOverhead, int perMemberOverhead) { + this.globalOverhead = globalOverHead; + this.perExtractionOverhead = perExtractionOverhead; + this.perMemberOverhead = perMemberOverhead; + } + } + + ExtractPrototypeMemberDeclarations(AbstractCompiler compiler, Pattern pattern) { + this.compiler = compiler; + this.pattern = pattern; + } + + @Override + public void process(Node externs, Node root) { + GatherExtractionInfo extractionInfo = new GatherExtractionInfo(); + NodeTraversal.traverse(compiler, root, extractionInfo); + if (extractionInfo.shouldExtract()) { + doExtraction(extractionInfo); + compiler.reportCodeChange(); + } + } + + /** + * Declares the temp variable to point to prototype objects and iterates + * through all ExtractInstance and performs extraction there. + */ + private void doExtraction(GatherExtractionInfo info) { + + // Insert a global temp if we are using the USE_GLOBAL_TEMP pattern. + if (pattern == Pattern.USE_GLOBAL_TEMP) { + Node injectionPoint = compiler.getNodeForCodeInsertion(null); + + Node var = NodeUtil.newVarNode(prototypeAlias, null) + .copyInformationFromForTree(injectionPoint); + + injectionPoint.addChildrenToFront(var); + } + // Go through all extraction instances and extract each of them. + for (ExtractionInstance instance : info.instances) { + extractInstance(instance); + } + } + + /** + * At a given ExtractionInstance, stores and prototype object in the temp + * variable and rewrite each member declaration to assign to the temp variable + * instead. + */ + private void extractInstance(ExtractionInstance instance) { + PrototypeMemberDeclaration first = instance.declarations.getFirst(); + String className = first.qualifiedClassName; + if (pattern == Pattern.USE_GLOBAL_TEMP) { + // Use the temp variable to hold the prototype. + Node stmt = new Node(first.node.getType(), + IR.assign( + IR.name(prototypeAlias), + NodeUtil.newQualifiedNameNode( + compiler.getCodingConvention(), className + ".prototype", + instance.parent, className + ".prototype"))) + .copyInformationFromForTree(first.node); + + instance.parent.addChildBefore(stmt, first.node); + } else if (pattern == Pattern.USE_ANON_FUNCTION){ + Node block = IR.block(); + Node func = IR.function( + IR.name(""), + IR.paramList(IR.name(prototypeAlias)), + block); + + Node call = IR.call(func, + NodeUtil.newQualifiedNameNode( + compiler.getCodingConvention(), className + ".prototype", + instance.parent, className + ".prototype")); + call.putIntProp(Node.FREE_CALL, 1); + + Node stmt = new Node(first.node.getType(), call); + stmt.copyInformationFromForTree(first.node); + instance.parent.addChildBefore(stmt, first.node); + for (PrototypeMemberDeclaration declar : instance.declarations) { + block.addChildToBack(declar.node.detachFromParent()); + } + } + // Go thought each member declaration and replace it with an assignment + // to the prototype variable. + for (PrototypeMemberDeclaration declar : instance.declarations) { + replacePrototypeMemberDeclaration(declar); + } + } + + /** + * Replaces a member declaration to an assignment to the temp prototype + * object. + */ + private void replacePrototypeMemberDeclaration( + PrototypeMemberDeclaration declar) { + // x.prototype.y = ... -> t.y = ... + Node assignment = declar.node.getFirstChild(); + Node lhs = assignment.getFirstChild(); + Node name = NodeUtil.newQualifiedNameNode( + compiler.getCodingConvention(), + prototypeAlias + "." + declar.memberName, declar.node, + declar.memberName); + + // Save the full prototype path on the left hand side of the assignment + // for debugging purposes. + // declar.lhs = x.prototype.y so first child of the first child + // is 'x'. + Node accessNode = declar.lhs.getFirstChild().getFirstChild(); + Object originalName = accessNode.getProp(Node.ORIGINALNAME_PROP); + + String className = "?"; + + if (originalName != null) { + className = originalName.toString(); + } + + NodeUtil.setDebugInformation(name.getFirstChild(), lhs, + className + ".prototype"); + + assignment.replaceChild(lhs, name); + } + + /** + * Collects all the possible extraction instances in a node traversal. + */ + private class GatherExtractionInfo extends AbstractShallowCallback { + + private List instances = Lists.newLinkedList(); + private int totalDelta = pattern.globalOverhead; + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + + if (!n.isScript() && !n.isBlock()) { + return; + } + + for (Node cur = n.getFirstChild(); cur != null; cur = cur.getNext()) { + PrototypeMemberDeclaration prototypeMember = + PrototypeMemberDeclaration.extractDeclaration(cur); + if (prototypeMember == null) { + continue; + } + + // Found a good site here. The constructor will computes the chain of + // declarations that is qualified for extraction. + ExtractionInstance instance = + new ExtractionInstance(prototypeMember, n); + cur = instance.declarations.getLast().node; + + // Only add it to our work list if the extraction at this instance + // makes the code smaller. + if (instance.isFavorable()) { + instances.add(instance); + totalDelta += instance.delta; + } + } + } + + /** + * @return <@code true> if the sum of all the extraction instance gain + * outweighs the overhead of the temp variable declaration. + */ + private boolean shouldExtract() { + return totalDelta < 0; + } + } + + private class ExtractionInstance { + LinkedList declarations = Lists.newLinkedList(); + private int delta = 0; + private final Node parent; + + private ExtractionInstance(PrototypeMemberDeclaration head, Node parent) { + this.parent = parent; + declarations.add(head); + delta = pattern.perExtractionOverhead + pattern.perMemberOverhead; + + for (Node cur = head.node.getNext(); cur != null; cur = cur.getNext()) { + + // We can skip over any named functions because they have no effect on + // the control flow. In fact, they are lifted to the beginning of the + // block. This happens a lot when devirtualization breaks the whole + // chain. + if (cur.isFunction()) { + continue; + } + + PrototypeMemberDeclaration prototypeMember = + PrototypeMemberDeclaration.extractDeclaration(cur); + if (prototypeMember == null || !head.isSameClass(prototypeMember)) { + break; + } + declarations.add(prototypeMember); + delta += pattern.perMemberOverhead; + } + } + + /** + * @return {@code true} if extracting all the declarations at this instance + * will overweight the overhead of aliasing the prototype object. + */ + boolean isFavorable() { + return delta <= 0; + } + } + + /** + * Abstraction for a prototype member declaration. + * + *

      {@code a.b.c.prototype.d = ....} + */ + private static class PrototypeMemberDeclaration { + final String memberName; + final Node node; + final String qualifiedClassName; + final Node lhs; + + private PrototypeMemberDeclaration(Node lhs, Node node) { + this.lhs = lhs; + this.memberName = NodeUtil.getPrototypePropertyName(lhs); + this.node = node; + this.qualifiedClassName = + NodeUtil.getPrototypeClassName(lhs).getQualifiedName(); + } + + private boolean isSameClass(PrototypeMemberDeclaration other) { + return qualifiedClassName.equals(other.qualifiedClassName); + } + + /** + * @return A prototype member declaration representation if there is one + * else it returns {@code null}. + */ + private static PrototypeMemberDeclaration extractDeclaration(Node n) { + if (!NodeUtil.isPrototypePropertyDeclaration(n)) { + return null; + } + Node lhs = n.getFirstChild().getFirstChild(); + return new PrototypeMemberDeclaration(lhs, n); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FieldCleanupPass.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FieldCleanupPass.java new file mode 100644 index 0000000..ba839e6 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FieldCleanupPass.java @@ -0,0 +1,126 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; + +/** + * A CleanupPass implementation that will remove all field declarations on + * JSTypes contributed by the original file. + *

      + * This pass is expected to clear out declarations contributed to any JSType, + * even if the constructor declaration is not provided in the file being + * updated. + * + * @author tylerg@google.com (Tyler Goodwin) + */ +public class FieldCleanupPass implements HotSwapCompilerPass { + + private final AbstractCompiler compiler; + + public FieldCleanupPass(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + String srcName = originalRoot.getSourceFileName(); + Callback cb = + new QualifiedNameSearchTraversal(compiler.getTypeRegistry(), srcName); + new NodeTraversal(compiler, cb).traverse(originalRoot); + } + + @Override + public void process(Node externs, Node root) { + // FieldCleanupPass should not do work during process. + } + + /** + * Search for fields to cleanup by looking for nodes in the tree which are + * root nodes of qualified names and getting the final token of the qualified + * name as a candidate field. + *

      + * Once a candidate field is found, ask the {@code JSTypeRegistry} for all + * JSTypes that have a field with the same name, and check if the field on + * that type is defined in the file the compiler is cleaning up. If so, remove + * the field, and update the {@code JSTypeRegistry} to no longer associate the + * type with the field. + *

      + * This algorithm was chosen for simplicity and is less than optimally + * efficient in two ways: + *

      + * 1) All types with a matching field name are iterated over (when only types + * that extend or implement the JSType indicated by the containing object in + * the found Qualified Name need to be checked). + *

      + * 2) All Qualified Names are checked, even those which are not L-Values or + * single declarations of an Type Expression. In general field should only be + * declared as part of an assignment ('ns.Type.a = 3;') or stand alone name + * declaration ('ns.Type.a;'). + */ + static class QualifiedNameSearchTraversal extends AbstractShallowCallback { + + private final JSTypeRegistry typeRegistry; + private final String srcName; + + public QualifiedNameSearchTraversal( + JSTypeRegistry typeRegistry, String srcName) { + this.typeRegistry = typeRegistry; + this.srcName = srcName; + } + + @Override + public void visit(NodeTraversal t, Node n, Node p) { + // We are a root GetProp + if (n.isGetProp() && !p.isGetProp()) { + String propName = getFieldName(n); + JSType type = n.getFirstChild().getJSType(); + if (type == null || type.toObjectType() == null) { + // Note cases like .field + return; + } + removeProperty(type.toObjectType(), propName); + } + if (n.getJSDocInfo() != null) { + n.getJSDocInfo().setAssociatedNode(null); + } + } + + /** + * Removes a given property from a type and updates type-registry. + * + * @param type the object type to be updated, should not be null + * @param propName the property to remove + */ + private void removeProperty(ObjectType type, String propName) { + Node pNode = type.getPropertyNode(propName); + if (pNode != null && srcName.equals(pNode.getSourceFileName())) { + typeRegistry.unregisterPropertyOnType(propName, type); + type.removeProperty(propName); + } + } + + private String getFieldName(Node n) { + return n.getLastChild().getString(); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FindExportableNodes.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FindExportableNodes.java new file mode 100644 index 0000000..166a957 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FindExportableNodes.java @@ -0,0 +1,136 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.LinkedHashMap; + +/** + * Records all of the symbols and properties that should be exported. + * + * Currently applies to: + * - function foo() {} + * - var foo = function() {} + * - foo.bar = function() {} + * - var FOO = ...; + * - foo.BAR = ...; + * + * FOO = BAR = 5; + * and + * var FOO = BAR = 5; + * are not supported because the annotation is ambiguous to whether it applies + * to all the variables or only the first one. + * + */ +public class FindExportableNodes extends AbstractPostOrderCallback { + + static final DiagnosticType NON_GLOBAL_ERROR = + DiagnosticType.error("JSC_NON_GLOBAL_ERROR", + "@export only applies to symbols/properties defined in the " + + "global scope."); + + /** + * It's convenient to be able to iterate over exports in the order in which + * they are encountered. + */ + private final LinkedHashMap exports; + + private final AbstractCompiler compiler; + + public FindExportableNodes(AbstractCompiler compiler) { + this.compiler = compiler; + this.exports = Maps.newLinkedHashMap(); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + JSDocInfo docInfo = n.getJSDocInfo(); + if (docInfo != null && docInfo.isExport()) { + String export = null; + GenerateNodeContext context = null; + + switch (n.getType()) { + case Token.FUNCTION: + if (parent.isScript()) { + export = NodeUtil.getFunctionName(n); + context = new GenerateNodeContext(n, parent, n); + } + break; + case Token.ASSIGN: + Node grandparent = parent.getParent(); + if (grandparent != null && grandparent.isScript() && + parent.isExprResult() && + !n.getLastChild().isAssign()) { + export = n.getFirstChild().getQualifiedName(); + context = new GenerateNodeContext(n, grandparent, parent); + } + break; + case Token.VAR: + if (parent.isScript()) { + if (n.getFirstChild().hasChildren() && + !n.getFirstChild().getFirstChild().isAssign()) { + export = n.getFirstChild().getString(); + context = new GenerateNodeContext(n, parent, n); + } + } + } + + if (export != null) { + exports.put(export, context); + } else { + compiler.report(t.makeError(n, NON_GLOBAL_ERROR)); + } + } + } + + public LinkedHashMap getExports() { + return exports; + } + + /** + * Context holding the node references required for generating the export + * calls. + */ + public static class GenerateNodeContext { + private final Node scriptNode; + private final Node contextNode; + private final Node node; + + public GenerateNodeContext(Node node, Node scriptNode, Node contextNode) { + this.node = node; + this.scriptNode = scriptNode; + this.contextNode = contextNode; + } + + public Node getNode() { + return node; + } + + public Node getScriptNode() { + return scriptNode; + } + + public Node getContextNode() { + return contextNode; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FlowSensitiveInlineVariables.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FlowSensitiveInlineVariables.java new file mode 100644 index 0000000..522fe94 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FlowSensitiveInlineVariables.java @@ -0,0 +1,539 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.ControlFlowGraph.AbstractCfgNodeTraversalCallback; +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.DataFlowAnalysis.FlowState; +import com.google.javascript.jscomp.MustBeReachingVariableDef.Definition; +import com.google.javascript.jscomp.MustBeReachingVariableDef.MustDef; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * Inline variables when possible. Using the information from + * {@link MaybeReachingVariableUse} and {@link MustBeReachingVariableDef}, + * this pass attempts to inline a variable by placing the value at the + * definition where the variable is used. The basic requirements for inlining + * are the following: + * + *

        + *
      • There is exactly one reaching definition at the use of that variable + *
      • + *
      • There is exactly one use for that definition of the variable + *
      • + *
      + * + *

      Other requirements can be found in {@link Candidate#canInline}. Currently + * this pass does not operate on the global scope due to compilation time. + * + */ +class FlowSensitiveInlineVariables extends AbstractPostOrderCallback + implements CompilerPass, ScopedCallback { + + /** + * Implementation: + * + * This pass first perform a traversal to gather a list of Candidates that + * could be inlined using {@link GatherCandiates}. + * + * The second step involves verifying that each candidate is actually safe + * to inline with {@link Candidate#canInline(Scope)} and finally perform + * inlining using {@link Candidate#inlineVariable()}. + * + * The reason for the delayed evaluation of the candidates is because we + * need two separate dataflow result. + */ + private final AbstractCompiler compiler; + private final Set inlinedNewDependencies = Sets.newHashSet(); + + // These two pieces of data is persistent in the whole execution of enter + // scope. + private ControlFlowGraph cfg; + private List candidates; + private MustBeReachingVariableDef reachingDef; + private MaybeReachingVariableUse reachingUses; + + private static final Predicate SIDE_EFFECT_PREDICATE = + new Predicate() { + @Override + public boolean apply(Node n) { + // When the node is null it means, we reached the implicit return + // where the function returns (possibly without an return statement) + if (n == null) { + return false; + } + + // TODO(user): We only care about calls to functions that + // passes one of the dependent variable to a non-side-effect free + // function. + if (n.isCall() && NodeUtil.functionCallHasSideEffects(n)) { + return true; + } + + if (n.isNew() && NodeUtil.constructorCallHasSideEffects(n)) { + return true; + } + + if (n.isDelProp()) { + return true; + } + + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + if (!ControlFlowGraph.isEnteringNewCfgNode(c) && apply(c)) { + return true; + } + } + return false; + } + }; + + public FlowSensitiveInlineVariables(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void enterScope(NodeTraversal t) { + + if (t.inGlobalScope()) { + return; // Don't even brother. All global variables are likely escaped. + } + + if (LiveVariablesAnalysis.MAX_VARIABLES_TO_ANALYZE < + t.getScope().getVarCount()) { + return; + } + + // Compute the forward reaching definition. + ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); + // Process the body of the function. + Preconditions.checkState(t.getScopeRoot().isFunction()); + cfa.process(null, t.getScopeRoot().getLastChild()); + cfg = cfa.getCfg(); + reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); + reachingDef.analyze(); + candidates = Lists.newLinkedList(); + + // Using the forward reaching definition search to find all the inline + // candidates + new NodeTraversal(compiler, new GatherCandiates()).traverse( + t.getScopeRoot().getLastChild()); + + // Compute the backward reaching use. The CFG can be reused. + reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); + reachingUses.analyze(); + for (Candidate c : candidates) { + if (c.canInline(t.getScope())) { + c.inlineVariable(); + + // If definition c has dependencies, then inlining it may have + // introduced new dependencies for our other inlining candidates. + // + // MustBeReachingVariableDef uses this dependency graph in its + // analysis, so some of these candidates may no longer be valid. + // We keep track of when the variable dependency graph changed + // so that we can back off appropriately. + if (!c.defMetadata.depends.isEmpty()) { + inlinedNewDependencies.add(t.getScope().getVar(c.varName)); + } + } + } + } + + @Override + public void exitScope(NodeTraversal t) {} + + @Override + public void process(Node externs, Node root) { + (new NodeTraversal(compiler, this)).traverseRoots(externs, root); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + // TODO(user): While the helpers do a subtree traversal on the AST, the + // compiler pass itself only traverse the AST to look for function + // declarations to perform dataflow analysis on. We could combine + // the traversal in DataFlowAnalysis's computeEscaped later to save some + // time. + } + + /** + * Gathers a list of possible candidates for inlining based only on + * information from {@link MustBeReachingVariableDef}. The list will be stored + * in {@code candidates} and the validity of each inlining Candidate should + * be later verified with {@link Candidate#canInline(Scope)} when + * {@link MaybeReachingVariableUse} has been performed. + */ + private class GatherCandiates extends AbstractShallowCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + DiGraphNode graphNode = cfg.getDirectedGraphNode(n); + if (graphNode == null) { + // Not a CFG node. + return; + } + FlowState state = graphNode.getAnnotation(); + final MustDef defs = state.getIn(); + final Node cfgNode = n; + AbstractCfgNodeTraversalCallback gatherCb = + new AbstractCfgNodeTraversalCallback() { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isName()) { + + // n.getParent() isn't null. This just the case where n is the root + // node that gatherCb started at. + if (parent == null) { + return; + } + + // Make sure that the name node is purely a read. + if ((NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n) + || parent.isVar() || parent.isInc() || parent.isDec() || + parent.isParamList() || parent.isCatch()) { + return; + } + + String name = n.getString(); + if (compiler.getCodingConvention().isExported(name)) { + return; + } + + Definition def = reachingDef.getDef(name, cfgNode); + // TODO(nicksantos): We need to add some notion of @const outer + // scope vars. We can inline those just fine. + if (def != null && + !reachingDef.dependsOnOuterScopeVars(def)) { + candidates.add(new Candidate(name, def, n, cfgNode)); + } + } + } + }; + + NodeTraversal.traverse(compiler, cfgNode, gatherCb); + } + } + + /** + * Models the connection between a definition and a use of that definition. + */ + private class Candidate { + + // Name of the variable. + private final String varName; + + // Nodes related to the definition. + private Node def; + private final Definition defMetadata; + + // Nodes related to the use. + private final Node use; + private final Node useCfgNode; + + // Number of uses of the variable within the CFG node that represented the + // use in the CFG. + private int numUseWithinUseCfgNode; + + Candidate(String varName, Definition defMetadata, + Node use, Node useCfgNode) { + Preconditions.checkArgument(use.isName()); + this.varName = varName; + this.defMetadata = defMetadata; + this.use = use; + this.useCfgNode = useCfgNode; + } + + private Node getDefCfgNode() { + return defMetadata.node; + } + + private boolean canInline(final Scope scope) { + // Cannot inline a parameter. + if (getDefCfgNode().isFunction()) { + return false; + } + + // If one of our dependencies has been inlined, then our dependency + // graph is wrong. Re-computing it would take another CFG computation, + // so we just back off for now. + for (Var dependency : defMetadata.depends) { + if (inlinedNewDependencies.contains(dependency)) { + return false; + } + } + + getDefinition(getDefCfgNode(), null); + getNumUseInUseCfgNode(useCfgNode, null); + + // Definition was not found. + if (def == null) { + return false; + } + + // Check that the assignment isn't used as a R-Value. + // TODO(user): Certain cases we can still inline. + if (def.isAssign() && !NodeUtil.isExprAssign(def.getParent())) { + return false; + } + + // The right of the definition has side effect: + // Example, for x: + // x = readProp(b), modifyProp(b); print(x); + if (checkRightOf(def, getDefCfgNode(), SIDE_EFFECT_PREDICATE)) { + return false; + } + + // Similar check as the above but this time, all the sub-expressions + // left of the use of the variable. + // x = readProp(b); modifyProp(b), print(x); + if (checkLeftOf(use, useCfgNode, SIDE_EFFECT_PREDICATE)) { + return false; + } + + // TODO(user): Side-effect is OK sometimes. As long as there are no + // side-effect function down all paths to the use. Once we have all the + // side-effect analysis tool. + if (NodeUtil.mayHaveSideEffects(def.getLastChild(), compiler)) { + return false; + } + + // TODO(user): We could inline all the uses if the expression is short. + + // Finally we have to make sure that there are no more than one use + // in the program and in the CFG node. Even when it is semantically + // correctly inlining twice increases code size. + if (numUseWithinUseCfgNode != 1) { + return false; + } + + // Make sure that the name is not within a loop + if (NodeUtil.isWithinLoop(use)) { + return false; + } + + + Collection uses = reachingUses.getUses(varName, getDefCfgNode()); + + if (uses.size() != 1) { + return false; + } + + // We give up inlining stuff with R-Value that has: + // 1) GETPROP, GETELEM, + // 2) anything that creates a new object. + // 3) a direct reference to a catch expression. + // Example: + // var x = a.b.c; j.c = 1; print(x); + // Inlining print(a.b.c) is not safe consider j and be alias to a.b. + // TODO(user): We could get more accuracy by looking more in-detail + // what j is and what x is trying to into to. + // TODO(johnlenz): rework catch expression handling when we + // have lexical scope support so catch expressions don't + // need to be special cased. + if (NodeUtil.has(def.getLastChild(), + new Predicate() { + @Override + public boolean apply(Node input) { + switch (input.getType()) { + case Token.GETELEM: + case Token.GETPROP: + case Token.ARRAYLIT: + case Token.OBJECTLIT: + case Token.REGEXP: + case Token.NEW: + return true; + case Token.NAME: + Var var = scope.getOwnSlot(input.getString()); + if (var != null + && var.getParentNode().isCatch()) { + return true; + } + } + return false; + } + }, + new Predicate() { + @Override + public boolean apply(Node input) { + // Recurse if the node is not a function. + return !input.isFunction(); + } + })) { + return false; + } + + // We can skip the side effect check along the paths of two nodes if + // they are just next to each other. + if (NodeUtil.isStatementBlock(getDefCfgNode().getParent()) && + getDefCfgNode().getNext() != useCfgNode) { + // Similar side effect check as above but this time the side effect is + // else where along the path. + // x = readProp(b); while(modifyProp(b)) {}; print(x); + CheckPathsBetweenNodes + pathCheck = new CheckPathsBetweenNodes( + cfg, + cfg.getDirectedGraphNode(getDefCfgNode()), + cfg.getDirectedGraphNode(useCfgNode), + SIDE_EFFECT_PREDICATE, + Predicates. + >alwaysTrue(), + false); + if (pathCheck.somePathsSatisfyPredicate()) { + return false; + } + } + + return true; + } + + /** + * Actual transformation. + */ + private void inlineVariable() { + Node defParent = def.getParent(); + Node useParent = use.getParent(); + if (def.isAssign()) { + Node rhs = def.getLastChild(); + rhs.detachFromParent(); + // Oh yes! I have grandparent to remove this. + Preconditions.checkState(defParent.isExprResult()); + while (defParent.getParent().isLabel()) { + defParent = defParent.getParent(); + } + defParent.detachFromParent(); + useParent.replaceChild(use, rhs); + } else if (defParent.isVar()) { + Node rhs = def.getLastChild(); + def.removeChild(rhs); + useParent.replaceChild(use, rhs); + } else { + Preconditions.checkState(false, "No other definitions can be inlined."); + } + compiler.reportCodeChange(); + } + + /** + * Set the def node + * + * @param n A node that has a corresponding CFG node in the CFG. + */ + private void getDefinition(Node n, Node parent) { + AbstractCfgNodeTraversalCallback gatherCb = + new AbstractCfgNodeTraversalCallback() { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.NAME: + if (n.getString().equals(varName) && n.hasChildren()) { + def = n; + } + return; + + case Token.ASSIGN: + Node lhs = n.getFirstChild(); + if (lhs.isName() && lhs.getString().equals(varName)) { + def = n; + } + return; + } + } + }; + NodeTraversal.traverse(compiler, n, gatherCb); + } + + /** + * Computes the number of uses of the variable varName and store it in + * numUseWithinUseCfgNode. + */ + private void getNumUseInUseCfgNode(Node n, Node parant) { + + AbstractCfgNodeTraversalCallback gatherCb = + new AbstractCfgNodeTraversalCallback() { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isName() && n.getString().equals(varName) && + // do not count in if it is left child of an assignment operator + !(parent.isAssign() && + (parent.getFirstChild() == n))) { + numUseWithinUseCfgNode++; + } + } + }; + + NodeTraversal.traverse(compiler, n, gatherCb); + } + } + + /** + * Given an expression by its root and sub-expression n, return true if there + * the predicate is true for some expression on the right of n. + * + * Example: + * + * NotChecked(), NotChecked(), n, Checked(), Checked(); + */ + private static boolean checkRightOf( + Node n, Node expressionRoot, Predicate predicate) { + for (Node p = n; p != expressionRoot; p = p.getParent()) { + for (Node cur = p.getNext(); cur != null; cur = cur.getNext()) { + if (predicate.apply(cur)) { + return true; + } + } + } + return false; + } + + /** + * Given an expression by its root and sub-expression n, return true if there + * the predicate is true for some expression on the left of n. + * + * Example: + * + * Checked(), Checked(), n, NotChecked(), NotChecked(); + */ + private static boolean checkLeftOf( + Node n, Node expressionRoot, Predicate predicate) { + for (Node p = n.getParent(); p != expressionRoot; p = p.getParent()) { + for (Node cur = p.getParent().getFirstChild(); cur != p; + cur = cur.getNext()) { + if (predicate.apply(cur)) { + return true; + } + } + } + return false; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionArgumentInjector.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionArgumentInjector.java new file mode 100644 index 0000000..a6efa7d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionArgumentInjector.java @@ -0,0 +1,503 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeUtil.Visitor; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +/** + * A nifty set of functions to deal with the issues of replacing function + * parameters with a set of call argument expressions. + * + * @author johnlenz@google.com (John Lenz) + */ +class FunctionArgumentInjector { + + // A string to use to represent "this". Anything that is not a valid + // identifier can be used, so we use "this". + static final String THIS_MARKER = "this"; + + private FunctionArgumentInjector() { + // A private constructor to prevent instantiation. + } + + /** + * With the map provided, replace the names with expression trees. + * @param node The root of the node tree within which to perform the + * substitutions. + * @param parent The parent root node. + * @param replacements The map of names to template node trees with which + * to replace the name Nodes. + * @returns The root node or its replacement. + */ + static Node inject(AbstractCompiler compiler, Node node, Node parent, + Map replacements) { + return inject(compiler, node, parent, replacements, true); + } + + static Node inject(AbstractCompiler compiler, Node node, Node parent, + Map replacements, boolean replaceThis) { + if (node.isName()) { + Node replacementTemplate = replacements.get(node.getString()); + if (replacementTemplate != null) { + // This should not be replacing declared names. + Preconditions.checkState(!parent.isFunction() + || !parent.isVar() + || !parent.isCatch()); + // The name may need to be replaced more than once, + // so we need to clone the node. + Node replacement = replacementTemplate.cloneTree(); + parent.replaceChild(node, replacement); + return replacement; + } + } else if (replaceThis && node.isThis()) { + Node replacementTemplate = replacements.get(THIS_MARKER); + Preconditions.checkNotNull(replacementTemplate); + if (!replacementTemplate.isThis()) { + // The name may need to be replaced more than once, + // so we need to clone the node. + Node replacement = replacementTemplate.cloneTree(); + parent.replaceChild(node, replacement); + + // Remove the value. This isn't required but it ensures that we won't + // inject side-effects multiple times as it will trigger the null + // check above if we do. + if (NodeUtil.mayHaveSideEffects(replacementTemplate, compiler)) { + replacements.remove(THIS_MARKER); + } + + return replacement; + } + } else if (node.isFunction()) { + // Once we enter another scope the "this" value changes, don't try + // to replace it within an inner scope. + replaceThis = false; + } + + for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { + // We have to reassign c in case it was replaced, because the removed c's + // getNext() would no longer be correct. + c = inject(compiler, c, node, replacements, replaceThis); + } + + return node; + } + + /** + * Get a mapping for function parameter names to call arguments. + */ + static LinkedHashMap getFunctionCallParameterMap( + Node fnNode, Node callNode, Supplier safeNameIdSupplier) { + // Create an argName -> expression map + // NOTE: A linked map is created here to provide ordering. + LinkedHashMap argMap = Maps.newLinkedHashMap(); + + // CALL NODE: [ NAME, ARG1, ARG2, ... ] + Node cArg = callNode.getFirstChild().getNext(); + if (cArg != null && NodeUtil.isFunctionObjectCall(callNode)) { + argMap.put(THIS_MARKER, cArg); + cArg = cArg.getNext(); + } else { + // 'apply' isn't supported yet. + Preconditions.checkState(!NodeUtil.isFunctionObjectApply(callNode)); + argMap.put(THIS_MARKER, NodeUtil.newUndefinedNode(callNode)); + } + + for (Node fnArg : NodeUtil.getFunctionParameters(fnNode).children()) { + if (cArg != null) { + argMap.put(fnArg.getString(), cArg); + cArg = cArg.getNext(); + } else { + Node srcLocation = callNode; + argMap.put(fnArg.getString(), NodeUtil.newUndefinedNode(srcLocation)); + } + } + + // Add temp names for arguments that don't have named parameters in the + // called function. + int anonArg = 0; + while (cArg != null) { + String uniquePlaceholder = + getUniqueAnonymousParameterName(safeNameIdSupplier); + argMap.put(uniquePlaceholder, cArg); + cArg = cArg.getNext(); + } + + return argMap; + } + + /** + * Parameter names will be name unique when at a later time. + */ + private static String getUniqueAnonymousParameterName( + Supplier safeNameIdSupplier) { + return "JSCompiler_inline_anon_param_" + safeNameIdSupplier.get(); + } + + /** + * Retrieve a set of names that can not be safely substituted in place. + * Example: + * function(a) { + * a = 0; + * } + * Inlining this without taking precautions would cause the call site value + * to be modified (bad). + */ + static Set findModifiedParameters(Node fnNode) { + Set names = getFunctionParameterSet(fnNode); + Set unsafeNames = Sets.newHashSet(); + return findModifiedParameters( + fnNode.getLastChild(), null, names, unsafeNames, false); + } + + /** + * Check for uses of the named value that imply a pass-by-value + * parameter is expected. This is used to prevent cases like: + * + * function (x) { + * x=2; + * return x; + * } + * + * We don't want "undefined" to be substituted for "x", and get + * undefined=2 + * + * @param n The node in question. + * @param parent The parent of the node. + * @param names The set of names to check. + * @param unsafe The set of names that require aliases. + * @param inInnerFunction Whether the inspection is occurring on a inner + * function. + */ + private static Set findModifiedParameters( + Node n, Node parent, Set names, Set unsafe, + boolean inInnerFunction) { + Preconditions.checkArgument(unsafe != null); + if (n.isName()) { + if (names.contains(n.getString())) { + if (inInnerFunction || canNameValueChange(n, parent)) { + unsafe.add(n.getString()); + } + } + } else if (n.isFunction()) { + // A function parameter can not be replaced with a direct inlined value + // if it is referred to by an inner function. The inner function + // can out live the call we are replacing, so inner function must + // capture a unique name. This approach does not work within loop + // bodies so those are forbidden elsewhere. + inInnerFunction = true; + } + + for (Node c : n.children()) { + findModifiedParameters(c, n, names, unsafe, inInnerFunction); + } + + return unsafe; + } + + /** + * This is similar to NodeUtil.isLValue except that object properties and + * array member modification aren't important ("o" in "o.a = 2" is still "o" + * after assignment, where in as "o = x", "o" is now "x"). + * + * This also looks for the redefinition of a name. + * function (x){var x;} + * + * @param n The NAME node in question. + * @param parent The parent of the node. + */ + private static boolean canNameValueChange(Node n, Node parent) { + int type = parent.getType(); + return (type == Token.VAR || type == Token.INC || type == Token.DEC || + (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n)); + } + + /** + * Updates the set of parameter names in set unsafe to include any + * arguments from the call site that require aliases. + * @param fnNode The FUNCTION node to be inlined. + * @param argMap The argument list for the call to fnNode. + * @param namesNeedingTemps The set of names to update. + */ + static void maybeAddTempsForCallArguments( + Node fnNode, Map argMap, Set namesNeedingTemps, + CodingConvention convention) { + if (argMap.isEmpty()) { + // No arguments to check, we are done. + return; + } + + Preconditions.checkArgument(fnNode.isFunction()); + Node block = fnNode.getLastChild(); + + Set parameters = argMap.keySet(); + + // Get the list of parameters that may need temporaries due to + // side-effects. + Set namesAfterSideEffects = findParametersReferencedAfterSideEffect( + parameters, block); + + // Check for arguments that are evaluated more than once. + for (Map.Entry entry : argMap.entrySet()) { + String argName = entry.getKey(); + if (namesNeedingTemps.contains(argName)) { + continue; + } + Node cArg = entry.getValue(); + boolean safe = true; + int references = NodeUtil.getNameReferenceCount(block, argName); + + if (NodeUtil.mayEffectMutableState(cArg) && references > 0) { + // Note: Mutable arguments should be assigned to temps, as the + // may be within in a loop: + // function x(a) { + // for(var i=0; i<0; i++) { + // foo(a); + // } + // x( [] ); + // + // The parameter in the call to foo should not become "[]". + safe = false; + } else if (NodeUtil.mayHaveSideEffects(cArg)) { + // Even if there are no references, we still need to evaluate the + // expression if it has side-effects. + safe = false; + } else if (NodeUtil.canBeSideEffected(cArg) + && namesAfterSideEffects.contains(argName)) { + safe = false; + } else if (references > 1) { + // Safe is a misnomer, this is a check for "large". + switch (cArg.getType()) { + case Token.NAME: + String name = cArg.getString(); + safe = !(convention.isExported(name)); + break; + case Token.THIS: + safe = true; + break; + case Token.STRING: + safe = (cArg.getString().length() < 2); + break; + default: + safe = NodeUtil.isImmutableValue(cArg); + break; + } + } + + if (!safe) { + namesNeedingTemps.add(argName); + } + } + } + + /** + * Boot strap a traversal to look for parameters referenced + * after a non-local side-effect. + * NOTE: This assumes no-inner functions. + * @param parameters The set of parameter names. + * @param root The function code block. + * @return The subset of parameters referenced after the first + * seen non-local side-effect. + */ + private static Set findParametersReferencedAfterSideEffect( + Set parameters, Node root) { + + // TODO(johnlenz): Consider using scope for this. + Set locals = Sets.newHashSet(parameters); + gatherLocalNames(root, locals); + + ReferencedAfterSideEffect collector = new ReferencedAfterSideEffect( + parameters, locals); + NodeUtil.visitPostOrder( + root, + collector, + collector); + return collector.getResults(); + } + + /** + * Collect parameter names referenced after a non-local side-effect. + * + * Assumptions: + * - We assume parameters are not modified in the function body + * (that is checked separately). + * - There are no inner functions (also checked separately). + * + * As we are trying to replace parameters with there passed in values + * we are interested in anything that may affect those value. So, ignoring + * changes to local variables, we look for things that may affect anything + * outside the local-state. Once such a side-effect is seen any following + * reference to the function parameters are collected. These will need + * to be assigned to temporaries to prevent changes to their value as would + * have happened during the function call. + * + * To properly handle loop structures all references to the function + * parameters are recorded and the decision to keep or throw away those + * references is deferred until exiting the loop structure. + */ + private static class ReferencedAfterSideEffect + implements Visitor, Predicate { + private final Set parameters; + private final Set locals; + private boolean sideEffectSeen = false; + private Set parametersReferenced = Sets.newHashSet(); + private int loopsEntered = 0; + + ReferencedAfterSideEffect(Set parameters, Set locals) { + this.parameters = parameters; + this.locals = locals; + } + + Set getResults() { + return parametersReferenced; + } + + @Override + public boolean apply(Node node) { + // Keep track of any loop structures entered. + if (NodeUtil.isLoopStructure(node)) { + loopsEntered++; + } + + // If we have found all the parameters, don't bother looking + // at the children. + return !(sideEffectSeen + && parameters.size() == parametersReferenced.size()); + } + + boolean inLoop() { + return loopsEntered != 0; + } + + @Override + public void visit(Node n) { + // If we are exiting a loop. + if (NodeUtil.isLoopStructure(n)) { + loopsEntered--; + if (!inLoop() && !sideEffectSeen) { + // Now that the loops has been fully traversed and + // no side-effects have been seen, throw away + // the references seen in them. + parametersReferenced.clear(); + } + } + + if (!sideEffectSeen) { + // Look for side-effects. + if (hasNonLocalSideEffect(n)) { + sideEffectSeen = true; + } + } + + // If traversing the nodes of a loop save any references + // that are seen. + if (inLoop() || sideEffectSeen) { + // Record references to parameters. + if (n.isName()) { + String name = n.getString(); + if (parameters.contains(name)) { + parametersReferenced.add(name); + } + } else if (n.isThis()) { + parametersReferenced.add(THIS_MARKER); + } + } + } + + /** + * @return Whether the node may have non-local side-effects. + */ + private boolean hasNonLocalSideEffect(Node n) { + boolean sideEffect = false; + int type = n.getType(); + // Note: Only care about changes to non-local names, specifically + // ignore VAR declaration assignments. + if (NodeUtil.isAssignmentOp(n) + || type == Token.INC + || type == Token.DEC) { + Node lhs = n.getFirstChild(); + // Ignore changes to local names. + if (!isLocalName(lhs)) { + sideEffect = true; + } + } else if (type == Token.CALL) { + sideEffect = NodeUtil.functionCallHasSideEffects(n); + } else if (type == Token.NEW) { + sideEffect = NodeUtil.constructorCallHasSideEffects(n); + } else if (type == Token.DELPROP) { + sideEffect = true; + } + + return sideEffect; + } + + /** + * @return Whether node is a reference to locally declared name. + */ + private boolean isLocalName(Node node) { + if (node.isName()) { + String name = node.getString(); + return locals.contains(name); + } + return false; + } + } + + /** + * Gather any names declared in the local scope. + */ + private static void gatherLocalNames(Node n, Set names) { + if (n.isFunction()) { + if (NodeUtil.isFunctionDeclaration(n)) { + names.add(n.getFirstChild().getString()); + } + // Don't traverse into inner function scopes; + return; + } else if (n.isName()) { + switch (n.getParent().getType()) { + case Token.VAR: + case Token.CATCH: + names.add(n.getString()); + } + } + + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + gatherLocalNames(c, names); + } + } + + /** + * Get a set of function parameter names. + */ + private static Set getFunctionParameterSet(Node fnNode) { + Set set = Sets.newHashSet(); + for (Node n : NodeUtil.getFunctionParameters(fnNode).children()) { + set.add(n.getString()); + } + return set; + } + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionInjector.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionInjector.java new file mode 100644 index 0000000..ddfb4ed --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionInjector.java @@ -0,0 +1,937 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.base.Supplier; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.ExpressionDecomposer.DecompositionType; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * A set of utility functions that replaces CALL with a specified + * FUNCTION body, replacing and aliasing function parameters as + * necessary. + * + * @author johnlenz@google.com (John Lenz) + */ +class FunctionInjector { + + private final AbstractCompiler compiler; + private final Supplier safeNameIdSupplier; + private final boolean allowDecomposition; + private Set knownConstants = Sets.newHashSet(); + private final boolean assumeStrictThis; + private final boolean assumeMinimumCapture; + + /** + * @param allowDecomposition Whether an effort should be made to break down + * expressions into simpler expressions to allow functions to be injected + * where they would otherwise be disallowed. + */ + public FunctionInjector( + AbstractCompiler compiler, + Supplier safeNameIdSupplier, + boolean allowDecomposition, + boolean assumeStrictThis, + boolean assumeMinimumCapture) { + Preconditions.checkNotNull(compiler); + Preconditions.checkNotNull(safeNameIdSupplier); + this.compiler = compiler; + this.safeNameIdSupplier = safeNameIdSupplier; + this.allowDecomposition = allowDecomposition; + this.assumeStrictThis = assumeStrictThis; + this.assumeMinimumCapture = assumeMinimumCapture; + } + + /** The type of inlining to perform. */ + enum InliningMode { + /** + * Directly replace the call expression. Only functions of meeting + * strict preconditions can be inlined. + */ + DIRECT, + + /** + * Replaces the call expression with a block of statements. Conditions + * on the function are looser in mode, but stricter on the call site. + */ + BLOCK + } + + /** Holds a reference to the call node of a function call */ + static class Reference { + final Node callNode; + final JSModule module; + final InliningMode mode; + + Reference(Node callNode, JSModule module, InliningMode mode){ + this.callNode = callNode; + this.module = module; + this.mode = mode; + } + } + + /** + * In order to estimate the cost of lining, we make the assumption that + * Identifiers are reduced 2 characters. For the call arguments, the important + * thing is that the cost is assumed to be the same in the call and the + * function, so the actual length doesn't matter in most cases. + */ + private static final int NAME_COST_ESTIMATE = + InlineCostEstimator.ESTIMATED_IDENTIFIER_COST; + + /** The cost of a argument separator (a comma). */ + private static final int COMMA_COST = 1; + + /** The cost of the parentheses needed to make a call.*/ + private static final int PAREN_COST = 2; + + + /** + * @param fnName The name of this function. This either the name of the + * variable to which the function is assigned or the name from the FUNCTION + * node. + * @param fnNode The FUNCTION node of the function to inspect. + * @return Whether the function node meets the minimum requirements for + * inlining. + */ + boolean doesFunctionMeetMinimumRequirements( + final String fnName, Node fnNode) { + Node block = NodeUtil.getFunctionBody(fnNode); + + // Basic restrictions on functions that can be inlined: + // 0) The function is inlinable by convention + // 1) It contains a reference to itself. + // 2) It uses its parameters indirectly using "arguments" (it isn't + // handled yet. + // 3) It references "eval". Inline a function containing eval can have + // large performance implications. + + if (!compiler.getCodingConvention().isInlinableFunction(fnNode)) { + return false; + } + + final String fnRecursionName = fnNode.getFirstChild().getString(); + Preconditions.checkState(fnRecursionName != null); + + // If the function references "arguments" directly in the function + boolean referencesArguments = NodeUtil.isNameReferenced( + block, "arguments", NodeUtil.MATCH_NOT_FUNCTION); + + // or it references "eval" or one of its names anywhere. + Predicate p = new Predicate(){ + @Override + public boolean apply(Node n) { + if (n.isName()) { + return n.getString().equals("eval") + || (!fnName.isEmpty() + && n.getString().equals(fnName)) + || (!fnRecursionName.isEmpty() + && n.getString().equals(fnRecursionName)); + } + return false; + } + }; + + return !referencesArguments + && !NodeUtil.has(block, p, Predicates.alwaysTrue()); + } + + /** + * @param t The traversal use to reach the call site. + * @param callNode The CALL node. + * @param fnNode The function to evaluate for inlining. + * @param needAliases A set of function parameter names that can not be + * used without aliasing. Returned by getUnsafeParameterNames(). + * @param mode Inlining mode to be used. + * @param referencesThis Whether fnNode contains references to its this + * object. + * @param containsFunctions Whether fnNode contains inner functions. + * @return Whether the inlining can occur. + */ + CanInlineResult canInlineReferenceToFunction(NodeTraversal t, + Node callNode, Node fnNode, Set needAliases, + InliningMode mode, boolean referencesThis, boolean containsFunctions) { + // TODO(johnlenz): This function takes too many parameter, without + // context. Modify the API to take a structure describing the function. + + // Allow direct function calls or "fn.call" style calls. + if (!isSupportedCallType(callNode)) { + return CanInlineResult.NO; + } + + // Limit where functions that contain functions can be inline. Introducing + // an inner function into another function can capture a variable and cause + // a memory leak. This isn't a problem in the global scope as those values + // last until explicitly cleared. + if (containsFunctions) { + if (!assumeMinimumCapture && !t.inGlobalScope()) { + // TODO(johnlenz): Allow inlining into any scope without local names or + // inner functions. + return CanInlineResult.NO; + } else if (NodeUtil.isWithinLoop(callNode)) { + // An inner closure maybe relying on a local value holding a value for a + // single iteration through a loop. + return CanInlineResult.NO; + } + } + + // TODO(johnlenz): Add support for 'apply' + if (referencesThis && !NodeUtil.isFunctionObjectCall(callNode)) { + // TODO(johnlenz): Allow 'this' references to be replaced with a + // global 'this' object. + return CanInlineResult.NO; + } + + if (mode == InliningMode.DIRECT) { + return canInlineReferenceDirectly(callNode, fnNode); + } else { + return canInlineReferenceAsStatementBlock( + t, callNode, fnNode, needAliases); + } + } + + /** + * Only ".call" calls and direct calls to functions are supported. + * @param callNode The call evaluate. + * @return Whether the call is of a type that is supported. + */ + private boolean isSupportedCallType(Node callNode) { + if (!callNode.getFirstChild().isName()) { + if (NodeUtil.isFunctionObjectCall(callNode)) { + if (!assumeStrictThis) { + Node thisValue = callNode.getFirstChild().getNext(); + if (thisValue == null || !thisValue.isThis()) { + return false; + } + } + } else if (NodeUtil.isFunctionObjectApply(callNode)) { + return false; + } + } + + return true; + } + + /** + * Inline a function into the call site. + */ + Node inline( + NodeTraversal t, Node callNode, String fnName, Node fnNode, + InliningMode mode) { + Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); + + if (mode == InliningMode.DIRECT) { + return inlineReturnValue(callNode, fnNode); + } else { + return inlineFunction(callNode, fnNode, fnName); + } + } + + /** + * Inline a function that fulfills the requirements of + * canInlineReferenceDirectly into the call site, replacing only the CALL + * node. + */ + private Node inlineReturnValue(Node callNode, Node fnNode) { + Node block = fnNode.getLastChild(); + Node callParentNode = callNode.getParent(); + + // NOTE: As the normalize pass guarantees globals aren't being + // shadowed and an expression can't introduce new names, there is + // no need to check for conflicts. + + // Create an argName -> expression map, checking for side effects. + Map argMap = + FunctionArgumentInjector.getFunctionCallParameterMap( + fnNode, callNode, this.safeNameIdSupplier); + + Node newExpression; + if (!block.hasChildren()) { + Node srcLocation = block; + newExpression = NodeUtil.newUndefinedNode(srcLocation); + } else { + Node returnNode = block.getFirstChild(); + Preconditions.checkArgument(returnNode.isReturn()); + + // Clone the return node first. + Node safeReturnNode = returnNode.cloneTree(); + Node inlineResult = FunctionArgumentInjector.inject( + null, safeReturnNode, null, argMap); + Preconditions.checkArgument(safeReturnNode == inlineResult); + newExpression = safeReturnNode.removeFirstChild(); + } + + callParentNode.replaceChild(callNode, newExpression); + return newExpression; + } + + /** + * Supported call site types. + */ + private enum CallSiteType { + + /** + * Used for a call site for which there does not exist a method + * to inline it. + */ + UNSUPPORTED() { + @Override + public void prepare(FunctionInjector injector, Node callNode) { + throw new IllegalStateException("unexpected"); + } + }, + + /** + * A call as a statement. For example: "foo();". + * EXPR_RESULT + * CALL + */ + SIMPLE_CALL() { + @Override + public void prepare(FunctionInjector injector, Node callNode) { + // Nothing to do. + } + }, + + /** + * An assignment, where the result of the call is assigned to a simple + * name. For example: "a = foo();". + * EXPR_RESULT + * NAME A + * CALL + * FOO + */ + SIMPLE_ASSIGNMENT() { + @Override + public void prepare(FunctionInjector injector, Node callNode) { + // Nothing to do. + } + }, + /** + * An var declaration and initialization, where the result of the call is + * assigned to the declared name + * name. For example: "a = foo();". + * VAR + * NAME A + * CALL + * FOO + */ + VAR_DECL_SIMPLE_ASSIGNMENT() { + @Override + public void prepare(FunctionInjector injector, Node callNode) { + // Nothing to do. + } + }, + /** + * An arbitrary expression, the root of which is a EXPR_RESULT, IF, + * RETURN, SWITCH or VAR. The call must be the first side-effect in + * the expression. + * + * Examples include: + * "if (foo()) {..." + * "return foo();" + * "var a = 1 + foo();" + * "a = 1 + foo()" + * "foo() ? 1:0" + * "foo() && x" + */ + EXPRESSION() { + @Override + public void prepare(FunctionInjector injector, Node callNode) { + injector.getDecomposer().moveExpression(callNode); + + // Reclassify after move + CallSiteType callSiteType = injector.classifyCallSite(callNode); + Preconditions.checkState(this != callSiteType); + callSiteType.prepare(injector, callNode); + } + }, + + /** + * An arbitrary expression, the root of which is a EXPR_RESULT, IF, + * RETURN, SWITCH or VAR. Where the call is not the first side-effect in + * the expression. + */ + DECOMPOSABLE_EXPRESSION() { + @Override + public void prepare(FunctionInjector injector, Node callNode) { + injector.getDecomposer().maybeExposeExpression(callNode); + + // Reclassify after decomposition + CallSiteType callSiteType = injector.classifyCallSite(callNode); + Preconditions.checkState(this != callSiteType); + callSiteType.prepare(injector, callNode); + } + }; + + public abstract void prepare(FunctionInjector injector, Node callNode); + } + + /** + * Determine which, if any, of the supported types the call site is. + */ + private CallSiteType classifyCallSite(Node callNode) { + Node parent = callNode.getParent(); + Node grandParent = parent.getParent(); + + // Verify the call site: + if (NodeUtil.isExprCall(parent)) { + // This is a simple call? Example: "foo();". + return CallSiteType.SIMPLE_CALL; + } else if (NodeUtil.isExprAssign(grandParent) + && !NodeUtil.isVarOrSimpleAssignLhs(callNode, parent) + && parent.getFirstChild().isName() + && !NodeUtil.isConstantName(parent.getFirstChild())) { + // This is a simple assignment. Example: "x = foo();" + return CallSiteType.SIMPLE_ASSIGNMENT; + } else if (parent.isName() + && !NodeUtil.isConstantName(parent) + && grandParent.isVar() + && grandParent.hasOneChild()) { + // This is a var declaration. Example: "var x = foo();" + // TODO(johnlenz): Should we be checking for constants on the + // left-hand-side of the assignments and handling them as EXPRESSION? + return CallSiteType.VAR_DECL_SIMPLE_ASSIGNMENT; + } else { + Node expressionRoot = ExpressionDecomposer.findExpressionRoot(callNode); + if (expressionRoot != null) { + ExpressionDecomposer decomposer = new ExpressionDecomposer( + compiler, safeNameIdSupplier, knownConstants); + DecompositionType type = decomposer.canExposeExpression( + callNode); + if (type == DecompositionType.MOVABLE) { + return CallSiteType.EXPRESSION; + } else if (type == DecompositionType.DECOMPOSABLE) { + return CallSiteType.DECOMPOSABLE_EXPRESSION; + } else { + Preconditions.checkState(type == DecompositionType.UNDECOMPOSABLE); + } + } + } + + return CallSiteType.UNSUPPORTED; + } + + private ExpressionDecomposer getDecomposer() { + return new ExpressionDecomposer( + compiler, safeNameIdSupplier, knownConstants); + } + + /** + * If required, rewrite the statement containing the call expression. + * @see ExpressionDecomposer#canExposeExpression + */ + void maybePrepareCall(Node callNode) { + CallSiteType callSiteType = classifyCallSite(callNode); + callSiteType.prepare(this, callNode); + } + + /** + * Inline a function which fulfills the requirements of + * canInlineReferenceAsStatementBlock into the call site, replacing the + * parent expression. + */ + private Node inlineFunction( + Node callNode, Node fnNode, String fnName) { + Node parent = callNode.getParent(); + Node grandParent = parent.getParent(); + + // TODO(johnlenz): Consider storing the callSite classification in the + // reference object and passing it in here. + CallSiteType callSiteType = classifyCallSite(callNode); + Preconditions.checkArgument(callSiteType != CallSiteType.UNSUPPORTED); + + boolean isCallInLoop = NodeUtil.isWithinLoop(callNode); + + // Store the name for the result. This will be used to + // replace "return expr" statements with "resultName = expr" + // to replace + String resultName = null; + boolean needsDefaultReturnResult = true; + switch (callSiteType) { + case SIMPLE_ASSIGNMENT: + resultName = parent.getFirstChild().getString(); + break; + + case VAR_DECL_SIMPLE_ASSIGNMENT: + resultName = parent.getString(); + break; + + case SIMPLE_CALL: + resultName = null; // "foo()" doesn't need a result. + needsDefaultReturnResult = false; + break; + + case EXPRESSION: + throw new IllegalStateException( + "Movable expressions must be moved before inlining."); + + case DECOMPOSABLE_EXPRESSION: + throw new IllegalStateException( + "Decomposable expressions must be decomposed before inlining."); + + default: + throw new IllegalStateException("Unexpected call site type."); + } + + FunctionToBlockMutator mutator = new FunctionToBlockMutator( + compiler, this.safeNameIdSupplier); + + Node newBlock = mutator.mutate( + fnName, fnNode, callNode, resultName, + needsDefaultReturnResult, isCallInLoop); + + // TODO(nicksantos): Create a common mutation function that + // can replace either a VAR name assignment, assignment expression or + // a EXPR_RESULT. + Node greatGrandParent = grandParent.getParent(); + switch (callSiteType) { + case VAR_DECL_SIMPLE_ASSIGNMENT: + // Remove the call from the name node. + parent.removeChild(parent.getFirstChild()); + Preconditions.checkState(parent.getFirstChild() == null); + // Add the call, after the VAR. + greatGrandParent.addChildAfter(newBlock, grandParent); + break; + + case SIMPLE_ASSIGNMENT: + // The assignment is now part of the inline function so + // replace it completely. + Preconditions.checkState(grandParent.isExprResult()); + greatGrandParent.replaceChild(grandParent, newBlock); + break; + + case SIMPLE_CALL: + // If nothing is looking at the result just replace the call. + Preconditions.checkState(parent.isExprResult()); + grandParent.replaceChild(parent, newBlock); + break; + + default: + throw new IllegalStateException("Unexpected call site type."); + } + + return newBlock; + } + + /** + * Checks if the given function matches the criteria for an inlinable + * function, and if so, adds it to our set of inlinable functions. + */ + boolean isDirectCallNodeReplacementPossible(Node fnNode) { + // Only inline single-statement functions + Node block = NodeUtil.getFunctionBody(fnNode); + + // Check if this function is suitable for direct replacement of a CALL node: + // a function that consists of single return that returns an expression. + if (!block.hasChildren()) { + // special case empty functions. + return true; + } else if (block.hasOneChild()) { + // Only inline functions that return something. + if (block.getFirstChild().isReturn() + && block.getFirstChild().getFirstChild() != null) { + return true; + } + } + + return false; + } + + enum CanInlineResult { + YES, + AFTER_PREPARATION, + NO + } + + /** + * Determines whether a function can be inlined at a particular call site. + * There are several criteria that the function and reference must hold in + * order for the functions to be inlined: + * - It must be a simple call, or assignment, or var initialization. + *

      +   *    f();
      +   *    a = foo();
      +   *    var a = foo();
      +   * 
      + */ + private CanInlineResult canInlineReferenceAsStatementBlock( + NodeTraversal t, Node callNode, Node fnNode, Set namesToAlias) { + CallSiteType callSiteType = classifyCallSite(callNode); + if (callSiteType == CallSiteType.UNSUPPORTED) { + return CanInlineResult.NO; + } + + if (!allowDecomposition + && (callSiteType == CallSiteType.DECOMPOSABLE_EXPRESSION + || callSiteType == CallSiteType.EXPRESSION)) { + return CanInlineResult.NO; + } + + if (!callMeetsBlockInliningRequirements( + t, callNode, fnNode, namesToAlias)) { + return CanInlineResult.NO; + } + + if (callSiteType == CallSiteType.DECOMPOSABLE_EXPRESSION + || callSiteType == CallSiteType.EXPRESSION) { + return CanInlineResult.AFTER_PREPARATION; + } else { + return CanInlineResult.YES; + } + } + + /** + * Determines whether a function can be inlined at a particular call site. + * - Don't inline if the calling function contains an inner function and + * inlining would introduce new globals. + */ + private boolean callMeetsBlockInliningRequirements( + NodeTraversal t, Node callNode, final Node fnNode, + Set namesToAlias) { + final boolean assumeMinimumCapture = this.assumeMinimumCapture; + + // Note: functions that contain function definitions are filtered out + // in isCandidateFunction. + + // TODO(johnlenz): Determining if the called function contains VARs + // or if the caller contains inner functions accounts for 20% of the + // run-time cost of this pass. + + // Don't inline functions with var declarations into a scope with inner + // functions as the new vars would leak into the inner function and + // cause memory leaks. + boolean fnContainsVars = NodeUtil.has( + NodeUtil.getFunctionBody(fnNode), + new NodeUtil.MatchDeclaration(), + new NodeUtil.MatchShallowStatement()); + boolean forbidTemps = false; + if (!t.inGlobalScope()) { + Node fnCaller = t.getScopeRoot(); + Node fnCallerBody = fnCaller.getLastChild(); + + // Don't allow any new vars into a scope that contains eval or one + // that contains functions (excluding the function being inlined). + Predicate match = new Predicate(){ + @Override + public boolean apply(Node n) { + if (n.isName()) { + return n.getString().equals("eval"); + } + if (!assumeMinimumCapture && n.isFunction()) { + return n != fnNode; + } + return false; + } + }; + forbidTemps = NodeUtil.has(fnCallerBody, + match, NodeUtil.MATCH_NOT_FUNCTION); + } + + if (fnContainsVars && forbidTemps) { + return false; + } + + // If the caller contains functions or evals, verify we aren't adding any + // additional VAR declarations because aliasing is needed. + if (forbidTemps) { + Map args = + FunctionArgumentInjector.getFunctionCallParameterMap( + fnNode, callNode, this.safeNameIdSupplier); + boolean hasArgs = !args.isEmpty(); + if (hasArgs) { + // Limit the inlining + Set allNamesToAlias = Sets.newHashSet(namesToAlias); + FunctionArgumentInjector.maybeAddTempsForCallArguments( + fnNode, args, allNamesToAlias, compiler.getCodingConvention()); + if (!allNamesToAlias.isEmpty()) { + return false; + } + } + } + + return true; + } + + /** + * Determines whether a function can be inlined at a particular call site. + * There are several criteria that the function and reference must hold in + * order for the functions to be inlined: + * 1) If a call's arguments have side effects, + * the corresponding argument in the function must only be referenced once. + * For instance, this will not be inlined: + *
      +   *     function foo(a) { return a + a }
      +   *     x = foo(i++);
      +   * 
      + */ + private CanInlineResult canInlineReferenceDirectly( + Node callNode, Node fnNode) { + if (!isDirectCallNodeReplacementPossible(fnNode)) { + return CanInlineResult.NO; + } + + Node block = fnNode.getLastChild(); + + // CALL NODE: [ NAME, ARG1, ARG2, ... ] + Node cArg = callNode.getFirstChild().getNext(); + + // Functions called via 'call' and 'apply' have a this-object as + // the first parameter, but this is not part of the called function's + // parameter list. + if (!callNode.getFirstChild().isName()) { + if (NodeUtil.isFunctionObjectCall(callNode)) { + // TODO(johnlenz): Support replace this with a value. + if (cArg == null || !cArg.isThis()) { + return CanInlineResult.NO; + } + cArg = cArg.getNext(); + } else { + // ".apply" call should be filtered before this. + Preconditions.checkState(!NodeUtil.isFunctionObjectApply(callNode)); + } + } + + // FUNCTION NODE -> LP NODE: [ ARG1, ARG2, ... ] + Node fnParam = NodeUtil.getFunctionParameters(fnNode).getFirstChild(); + while (cArg != null || fnParam != null) { + // For each named parameter check if a mutable argument use more than one. + if (fnParam != null) { + if (cArg != null) { + // Check for arguments that are evaluated more than once. + // Note: Unlike block inlining, there it is not possible that a + // parameter reference will be in a loop. + if (NodeUtil.mayEffectMutableState(cArg, compiler) + && NodeUtil.getNameReferenceCount( + block, fnParam.getString()) > 1) { + return CanInlineResult.NO; + } + } + + // Move to the next name. + fnParam = fnParam.getNext(); + } + + // For every call argument check for side-effects, even if there + // isn't a named parameter to match. + if (cArg != null) { + if (NodeUtil.mayHaveSideEffects(cArg, compiler)) { + return CanInlineResult.NO; + } + cArg = cArg.getNext(); + } + } + + return CanInlineResult.YES; + } + + /** + * Determine if inlining the function is likely to reduce the code size. + * @param namesToAlias + */ + boolean inliningLowersCost( + JSModule fnModule, Node fnNode, Collection refs, + Set namesToAlias, boolean isRemovable, boolean referencesThis) { + int referenceCount = refs.size(); + if (referenceCount == 0) { + return true; + } + + int referencesUsingBlockInlining = 0; + + boolean checkModules = isRemovable && fnModule != null; + JSModuleGraph moduleGraph = compiler.getModuleGraph(); + + for (Reference ref : refs) { + if (ref.mode == InliningMode.BLOCK) { + referencesUsingBlockInlining++; + } + + // Check if any of the references cross the module boundaries. + if (checkModules && ref.module != null) { + if (ref.module != fnModule && + !moduleGraph.dependsOn(ref.module, fnModule)) { + // Calculate the cost as if the function were non-removable, + // if it still lowers the cost inline it. + isRemovable = false; + checkModules = false; // no need to check additional modules. + } + } + } + + int referencesUsingDirectInlining = referenceCount - + referencesUsingBlockInlining; + + // Don't bother calculating the cost of function for simple functions where + // possible. + // However, when inlining a complex function, even a single reference may be + // larger than the original function if there are many returns (resulting + // in additional assignments) or many parameters that need to be aliased + // so use the cost estimating. + if (referenceCount == 1 && isRemovable && + referencesUsingDirectInlining == 1) { + return true; + } + + int callCost = estimateCallCost(fnNode, referencesThis); + int overallCallCost = callCost * referenceCount; + + int costDeltaDirect = inlineCostDelta( + fnNode, namesToAlias, InliningMode.DIRECT); + int costDeltaBlock = inlineCostDelta( + fnNode, namesToAlias, InliningMode.BLOCK); + + return doesLowerCost(fnNode, overallCallCost, + referencesUsingDirectInlining, costDeltaDirect, + referencesUsingBlockInlining, costDeltaBlock, + isRemovable); + } + + /** + * @return Whether inlining will lower cost. + */ + private boolean doesLowerCost( + Node fnNode, int callCost, + int directInlines, int costDeltaDirect, + int blockInlines, int costDeltaBlock, + boolean removable) { + + // Determine the threshold value for this inequality: + // inline_cost < call_cost + // But solve it for the function declaration size so the size of it + // is only calculated once and terminated early if possible. + + int fnInstanceCount = directInlines + blockInlines - (removable ? 1 : 0); + // Prevent division by zero. + if (fnInstanceCount == 0) { + // Special case single reference function that are being block inlined: + // If the cost of the inline is greater than the function definition size, + // don't inline. + if (blockInlines > 0 && costDeltaBlock > 0) { + return false; + } + return true; + } + + int costDelta = (directInlines * costDeltaDirect) + + (blockInlines * costDeltaBlock); + int threshold = (callCost - costDelta) / fnInstanceCount; + + return InlineCostEstimator.getCost(fnNode, threshold + 1) <= threshold; + } + + /** + * Gets an estimate of the cost in characters of making the function call: + * the sum of the identifiers and the separators. + * @param referencesThis + */ + private static int estimateCallCost(Node fnNode, boolean referencesThis) { + Node argsNode = NodeUtil.getFunctionParameters(fnNode); + int numArgs = argsNode.getChildCount(); + + int callCost = NAME_COST_ESTIMATE + PAREN_COST; + if (numArgs > 0) { + callCost += (numArgs * NAME_COST_ESTIMATE) + ((numArgs - 1) * COMMA_COST); + } + + if (referencesThis) { + // TODO(johnlenz): Update this if we start supporting inlining + // other functions that reference this. + // The only functions that reference this that are currently inlined + // are those that are called via ".call" with an explicit "this". + callCost += 5 + 5; // ".call" + "this," + } + + return callCost; + } + + /** + * @return The difference between the function definition cost and + * inline cost. + */ + private static int inlineCostDelta( + Node fnNode, Set namesToAlias, InliningMode mode) { + // The part of the function that is never inlined: + // "function xx(xx,xx){}" (15 + (param count * 3) -1; + int paramCount = NodeUtil.getFunctionParameters(fnNode).getChildCount(); + int commaCount = (paramCount > 1) ? paramCount - 1 : 0; + int costDeltaFunctionOverhead = 15 + commaCount + + (paramCount * InlineCostEstimator.ESTIMATED_IDENTIFIER_COST); + + Node block = fnNode.getLastChild(); + if (!block.hasChildren()) { + // Assume the inline cost is zero for empty functions. + return -costDeltaFunctionOverhead; + } + + if (mode == InliningMode.DIRECT) { + // The part of the function that is inlined using direct inlining: + // "return " (7) + return -(costDeltaFunctionOverhead + 7); + } else { + int aliasCount = namesToAlias.size(); + + // Originally, we estimated purely base on the function code size, relying + // on later optimizations. But that did not produce good results, so here + // we try to estimate the something closer to the actual inlined coded. + + // NOTE 1: Result overhead is only if there is an assignment, but + // getting that information would require some refactoring. + // NOTE 2: The aliasing overhead is currently an under-estimate, + // as some parameters are aliased because of the parameters used. + // Perhaps we should just assume all parameters will be aliased? + final int INLINE_BLOCK_OVERHEAD = 4; // "X:{}" + final int PER_RETURN_OVERHEAD = 2; // "return" --> "break X" + final int PER_RETURN_RESULT_OVERHEAD = 3; // "XX=" + final int PER_ALIAS_OVERHEAD = 3; // "XX=" + + // TODO(johnlenz): Counting the number of returns is relatively expensive + // this information should be determined during the traversal and + // cached. + int returnCount = NodeUtil.getNodeTypeReferenceCount( + block, Token.RETURN, new NodeUtil.MatchShallowStatement()); + int resultCount = (returnCount > 0) ? returnCount - 1 : 0; + int baseOverhead = (returnCount > 0) ? INLINE_BLOCK_OVERHEAD : 0; + + int overhead = baseOverhead + + returnCount * PER_RETURN_OVERHEAD + + resultCount * PER_RETURN_RESULT_OVERHEAD + + aliasCount * PER_ALIAS_OVERHEAD; + + return (overhead - costDeltaFunctionOverhead); + } + } + + /** + * Store the names of known constants to be used when classifying call-sites + * in expressions. + */ + public void setKnownConstants(Set knownConstants) { + // This is only expected to be set once. The same set should be used + // when evaluating call-sites and inlining calls. + Preconditions.checkState(this.knownConstants.isEmpty()); + this.knownConstants = knownConstants; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionNames.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionNames.java new file mode 100644 index 0000000..de4189e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionNames.java @@ -0,0 +1,175 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.Node; +import java.io.Serializable; +import java.util.*; + +/** + * Extract a list of all function nodes defined in a JavaScript + * program, assigns them globally unique ids and computes their fully + * qualified names. Function names are derived from the property they + * are assigned to and the scope they are defined in. For instance, + * the following code + * + * goog.widget = function(str) { + * this.member_fn = function() {} + * local_fn = function() {} + * goog.array.map(arr, function(){}); + * } + * + * defines the following functions + * + * goog.widget + * goog.widget.member_fn + * goog.widget::local_fn + * goog.widget:: + * + */ + +class FunctionNames implements CompilerPass, Serializable { + private static final long serialVersionUID = 1L; + + private final transient AbstractCompiler compiler; + private final Map functionMap = Maps.newLinkedHashMap(); + private final transient FunctionListExtractor functionListExtractor; + + FunctionNames(AbstractCompiler compiler) { + this.compiler = compiler; + this.functionListExtractor = new FunctionListExtractor(functionMap); + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, functionListExtractor); + FunctionExpressionNamer namer = new FunctionExpressionNamer(functionMap); + AnonymousFunctionNamingCallback namingCallback = + new AnonymousFunctionNamingCallback(namer); + NodeTraversal.traverse(compiler, root, namingCallback); + } + + public Iterable getFunctionNodeList() { + return functionMap.keySet(); + } + + public int getFunctionId(Node f) { + FunctionRecord record = functionMap.get(f); + if (record != null) { + return record.id; + } else { + return -1; + } + } + + public String getFunctionName(Node f) { + FunctionRecord record = functionMap.get(f); + if (record == null) { + // Function node was added during compilation and has no name. + return null; + } + + String str = record.name; + if (str.isEmpty()) { + str = ""; + } + + Node parent = record.parent; + if (parent != null) { + str = getFunctionName(parent) + "::" + str; + } + + // this.foo -> foo + str = str.replaceAll("::this\\.", "."); + // foo.prototype.bar -> foo.bar + // AnonymousFunctionNamingCallback already replaces ".prototype." + // with "..", just remove the extra dot. + str = str.replaceAll("\\.\\.", "."); + // remove toplevel anonymous blocks, if they exists. + str = str.replaceFirst("^(::)*", ""); + return str; + } + + private static class FunctionRecord implements Serializable { + private static final long serialVersionUID = 1L; + + public final int id; + public final Node parent; + public String name; + + FunctionRecord(int id, Node parent, String name) { + this.id = id; + this.parent = parent; + this.name = name; + } + } + + private static class FunctionListExtractor extends AbstractPostOrderCallback { + private final Map functionMap; + private int nextId = 0; + + FunctionListExtractor(Map functionMap) { + this.functionMap = functionMap; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isFunction()) { + Node functionNameNode = n.getFirstChild(); + String functionName = functionNameNode.getString(); + + Node enclosingFunction = t.getEnclosingFunction(); + + functionMap.put(n, + new FunctionRecord(nextId, enclosingFunction, functionName)); + nextId++; + } + } + } + + private static class FunctionExpressionNamer + implements AnonymousFunctionNamingCallback.FunctionNamer { + private static final char DELIMITER = '.'; + private static final NodeNameExtractor extractor = + new NodeNameExtractor(DELIMITER); + private final Map functionMap; + + FunctionExpressionNamer(Map functionMap) { + this.functionMap = functionMap; + } + + @Override + public final String getName(Node node) { + return extractor.getName(node); + } + + @Override + public final void setFunctionName(String name, Node fnNode) { + FunctionRecord record = functionMap.get(fnNode); + assert(record != null); + assert(record.name.isEmpty()); + record.name = name; + } + + @Override + public final String getCombinedName(String lhs, String rhs) { + return lhs + DELIMITER + rhs; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionRewriter.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionRewriter.java new file mode 100644 index 0000000..41571f5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionRewriter.java @@ -0,0 +1,532 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Multimap; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Collection; +import java.util.List; + +/** + * Reduces the size of common function expressions. + * + * This pass will rewrite: + * + * C.prototype.getA = function() { return this.a_ }; + * C.prototype.setA = function(newValue) { this.a_ = newValue }; + * + * as: + * + * C.prototype.getA = JSCompiler_get("a_); + * C.prototype.setA = JSCompiler_set("a_); + * + * if by doing so we will save bytes, after the helper functions are + * added and renaming is done. + * + */ +class FunctionRewriter implements CompilerPass { + private final AbstractCompiler compiler; + // Safety margin used to avoid growing simple programs by a few bytes. + // Selected arbitrarily. + private static final int SAVINGS_THRESHOLD = 16; + + FunctionRewriter(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + List reducers = ImmutableList.of(new ReturnConstantReducer(), + new GetterReducer(), + new SetterReducer(), + new EmptyFunctionReducer(), + new IdentityReducer()); + + Multimap reductionMap = HashMultimap.create(); + + // Accumulate possible reductions in the reduction multi-map. They + // will be applied in the loop below. + NodeTraversal.traverse(compiler, root, + new ReductionGatherer(reducers, reductionMap)); + + // Apply reductions iff they will provide some savings. + for (Reducer reducer : reducers) { + Collection reductions = reductionMap.get(reducer); + if (reductions.isEmpty()) { + continue; + } + + Node helperCode = parseHelperCode(reducer); + if (helperCode == null) { + continue; + } + + int helperCodeCost = InlineCostEstimator.getCost(helperCode); + + // Estimate savings + int savings = 0; + for (Reduction reduction : reductions) { + savings += reduction.estimateSavings(); + } + + // Compare estimated savings against the helper cost. Apply + // reductions if doing so will result in some savings. + if (savings > (helperCodeCost + SAVINGS_THRESHOLD)) { + for (Reduction reduction : reductions) { + reduction.apply(); + } + + Node addingRoot = compiler.getNodeForCodeInsertion(null); + addingRoot.addChildrenToFront(helperCode); + compiler.reportCodeChange(); + } + } + } + + /** + * Parse helper code needed by a reducer. + * + * @return Helper code root. If parse fails, return null. + */ + public Node parseHelperCode(Reducer reducer) { + Node root = compiler.parseSyntheticCode( + reducer.getClass().toString() + ":helper", reducer.getHelperSource()); + return (root != null) ? root.removeFirstChild() : null; + } + + private static boolean isReduceableFunctionExpression(Node n) { + return NodeUtil.isFunctionExpression(n) + && !NodeUtil.isGetOrSetKey(n.getParent()); + } + + /** + * Information needed to apply a reduction. + */ + private class Reduction { + private final Node parent; + private final Node oldChild; + private final Node newChild; + + Reduction(Node parent, Node oldChild, Node newChild) { + this.parent = parent; + this.oldChild = oldChild; + this.newChild = newChild; + } + + /** + * Apply the reduction by replacing the old child with the new child. + */ + void apply() { + parent.replaceChild(oldChild, newChild); + compiler.reportCodeChange(); + } + + /** + * Estimate number of bytes saved by applying this reduction. + */ + int estimateSavings() { + return InlineCostEstimator.getCost(oldChild) - + InlineCostEstimator.getCost(newChild); + } + } + + /** + * Gathers a list of reductions to apply later by doing an in-order + * AST traversal. If a suitable reduction is found, stop traversal + * in that branch. + */ + private class ReductionGatherer implements Callback { + private final List reducers; + private final Multimap reductions; + + /** + * @param reducers List of reducers to apply during traversal. + * @param reductions Reducer -> Reduction multimap, + * populated during traversal. + */ + ReductionGatherer(List reducers, + Multimap reductions) { + this.reducers = reducers; + this.reductions = reductions; + } + + @Override + public boolean shouldTraverse(NodeTraversal raversal, + Node node, + Node parent) { + for (Reducer reducer : reducers) { + Node replacement = reducer.reduce(node); + if (replacement != node) { + reductions.put(reducer, new Reduction(parent, node, replacement)); + return false; + } + } + return true; + } + + + @Override + public void visit(NodeTraversal traversal, Node node, Node parent) { + } + } + + /** + * Interface implemented by the strength-reduction optimizers below. + */ + abstract static class Reducer { + /** + * @return JS source for helper methods used by this reduction. + */ + abstract String getHelperSource(); + + /** + * @return root of the reduced subtree if a reduction was applied; + * otherwise returns the node argument. + */ + abstract Node reduce(Node node); + + /** + * Builds a method call based on the the given method name, + * argument and history. + * + * @param methodName Method to call. + * @param argumentNode Method argument. + */ + protected final Node buildCallNode(String methodName, Node argumentNode, + Node srcref) { + Node call = IR.call(IR.name(methodName)).srcref(srcref); + call.putBooleanProp(Node.FREE_CALL, true); + if (argumentNode != null) { + call.addChildToBack(argumentNode.cloneTree()); + } + return call; + } + } + + /** + * Reduces return immutable constant literal methods declarations + * with calls to a constant return method factory. + * + * Example: + * a.prototype.b = function() {} + * is reduced to: + * a.prototype.b = emptyFn(); + */ + private static class EmptyFunctionReducer extends Reducer { + static final String FACTORY_METHOD_NAME = "JSCompiler_emptyFn"; + static final String HELPER_SOURCE = + "function " + FACTORY_METHOD_NAME + "() {" + + " return function() {}" + + "}"; + + @Override + public String getHelperSource() { + return HELPER_SOURCE; + } + + @Override + public Node reduce(Node node) { + if (NodeUtil.isEmptyFunctionExpression(node)) { + return buildCallNode(FACTORY_METHOD_NAME, null, node); + } else { + return node; + } + } + } + + /** + * Base class for reducers that match functions that contain a + * single return statement. + */ + abstract static class SingleReturnStatementReducer extends Reducer { + + /** + * @return function return value node if function body contains a + * single return statement. Otherwise, null. + */ + protected final Node maybeGetSingleReturnRValue(Node functionNode) { + Node body = functionNode.getLastChild(); + if (!body.hasOneChild()) { + return null; + } + + Node statement = body.getFirstChild(); + if (statement.isReturn()) { + return statement.getFirstChild(); + } + return null; + } + } + + /** + * Reduces property getter method declarations with calls to a + * getter method factory. + * + * Example: + * a.prototype.b = function(a) {return a} + * is reduced to: + * a.prototype.b = getter(a); + */ + private static class IdentityReducer extends SingleReturnStatementReducer { + static final String FACTORY_METHOD_NAME = "JSCompiler_identityFn"; + static final String HELPER_SOURCE = + "function " + FACTORY_METHOD_NAME + "() {" + + " return function(" + FACTORY_METHOD_NAME + "_value) {" + + "return " + FACTORY_METHOD_NAME + "_value}" + + "}"; + + @Override + public String getHelperSource() { + return HELPER_SOURCE; + } + + @Override + public Node reduce(Node node) { + if (!isReduceableFunctionExpression(node)) { + return node; + } + + if (isIdentityFunction(node)) { + return buildCallNode(FACTORY_METHOD_NAME, null, node); + } else { + return node; + } + } + + /** + * Checks if the function matches the pattern: + * function(, ) {return } + * + * @return Whether the function matches the pattern. + */ + private boolean isIdentityFunction(Node functionNode) { + Node argList = functionNode.getFirstChild().getNext(); + Node paramNode = argList.getFirstChild(); + if (paramNode == null) { + return false; + } + + Node value = maybeGetSingleReturnRValue(functionNode); + if (value != null && + value.isName() && + value.getString().equals(paramNode.getString())) { + return true; + } + return false; + } + } + + /** + * Reduces return immutable constant literal methods declarations + * with calls to a constant return method factory. + * + * Example: + * a.prototype.b = function() {return 10} + * is reduced to: + * a.prototype.b = returnconst(10); + */ + private static class ReturnConstantReducer + extends SingleReturnStatementReducer { + static final String FACTORY_METHOD_NAME = "JSCompiler_returnArg"; + static final String HELPER_SOURCE = + "function " + FACTORY_METHOD_NAME + + "(" + FACTORY_METHOD_NAME + "_value) {" + + " return function() {return " + FACTORY_METHOD_NAME + "_value}" + + "}"; + + @Override + public String getHelperSource() { + return HELPER_SOURCE; + } + + @Override + public Node reduce(Node node) { + if (!isReduceableFunctionExpression(node)) { + return node; + } + + Node valueNode = getValueNode(node); + if (valueNode != null) { + return buildCallNode(FACTORY_METHOD_NAME, valueNode, node); + } else { + return node; + } + } + + /** + * Checks if the function matches the pattern: + * function() {return } + * and returns if a match is found. + * + * @return the immutable value node; or null. + */ + private Node getValueNode(Node functionNode) { + Node value = maybeGetSingleReturnRValue(functionNode); + if (value != null && + NodeUtil.isImmutableValue(value)) { + return value; + } + return null; + } + } + + /** + * Reduces property getter method declarations with calls to a + * getter method factory. + * + * Example: + * a.prototype.b = function() {return this.b_} + * is reduced to: + * a.prototype.b = getter("b_"); + */ + private static class GetterReducer extends SingleReturnStatementReducer { + static final String FACTORY_METHOD_NAME = "JSCompiler_get"; + static final String HELPER_SOURCE = + "function " + FACTORY_METHOD_NAME + "(" + + FACTORY_METHOD_NAME + "_name) {" + + " return function() {return this[" + FACTORY_METHOD_NAME + "_name]}" + + "}"; + + @Override + public String getHelperSource() { + return HELPER_SOURCE; + } + + @Override + public Node reduce(Node node) { + if (!isReduceableFunctionExpression(node)) { + return node; + } + + Node propName = getGetPropertyName(node); + if (propName != null) { + if (!propName.isString()) { + throw new IllegalStateException( + "Expected STRING, got " + Token.name(propName.getType())); + } + + return buildCallNode(FACTORY_METHOD_NAME, propName, node); + } else { + return node; + } + } + + /** + * Checks if the function matches the pattern: + * function() {return this.} + * and returns if a match is found. + * + * @return STRING node that is the RHS of a this property get; or null. + */ + private Node getGetPropertyName(Node functionNode) { + Node value = maybeGetSingleReturnRValue(functionNode); + if (value != null && + value.isGetProp() && + value.getFirstChild().isThis()) { + return value.getLastChild(); + } + return null; + } + } + + /** + * Reduces property setter method declarations with calls to a + * setter method factory. + * + * Example: + * a.prototype.setB = function(value) {this.b_ = value} + * reduces to: + * a.prototype.setB = getter("b_"); + */ + private static class SetterReducer extends Reducer { + static final String FACTORY_METHOD_NAME = "JSCompiler_set"; + static final String HELPER_SOURCE = + "function " + FACTORY_METHOD_NAME + "(" + + FACTORY_METHOD_NAME + "_name) {" + + " return function(" + FACTORY_METHOD_NAME + "_value) {" + + "this[" + FACTORY_METHOD_NAME + "_name] = " + + FACTORY_METHOD_NAME + "_value}" + + "}"; + + @Override + public String getHelperSource() { + return HELPER_SOURCE; + } + + @Override + public Node reduce(Node node) { + if (!isReduceableFunctionExpression(node)) { + return node; + } + + Node propName = getSetPropertyName(node); + if (propName != null) { + if (!propName.isString()) { + throw new IllegalStateException( + "Expected STRING, got " + Token.name(propName.getType())); + } + + return buildCallNode(FACTORY_METHOD_NAME, propName, node); + } else { + return node; + } + } + + /** + * Checks if the function matches the pattern: + * function(, ) {this. = } + * and returns if a match is found. + * + * @return STRING node that is the RHS of a this property get; or null. + */ + private Node getSetPropertyName(Node functionNode) { + Node body = functionNode.getLastChild(); + if (!body.hasOneChild()) { + return null; + } + + Node argList = functionNode.getFirstChild().getNext(); + Node paramNode = argList.getFirstChild(); + if (paramNode == null) { + return null; + } + + Node statement = body.getFirstChild(); + if (!NodeUtil.isExprAssign(statement)) { + return null; + } + + Node assign = statement.getFirstChild(); + Node lhs = assign.getFirstChild(); + if (lhs.isGetProp() && lhs.getFirstChild().isThis()) { + Node rhs = assign.getLastChild(); + if (rhs.isName() && + rhs.getString().equals(paramNode.getString())) { + Node propertyName = lhs.getLastChild(); + return propertyName; + } + } + return null; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionToBlockMutator.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionToBlockMutator.java new file mode 100644 index 0000000..2ffddf3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionToBlockMutator.java @@ -0,0 +1,509 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import static com.google.javascript.jscomp.FunctionArgumentInjector.THIS_MARKER; + +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.MakeDeclaredNamesUnique.InlineRenamer; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * A class to transform the body of a function into a generic block suitable + * for inlining. + * + * @author johnlenz@google.com (John Lenz) + */ +class FunctionToBlockMutator { + + private AbstractCompiler compiler; + private Supplier safeNameIdSupplier; + + + FunctionToBlockMutator( + AbstractCompiler compiler, Supplier safeNameIdSupplier) { + this.compiler = compiler; + this.safeNameIdSupplier = safeNameIdSupplier; + } + + /** + * @param fnName The name to use when preparing human readable names. + * @param fnNode The function to prepare. + * @param callNode The call node that will be replaced. + * @param resultName Function results should be assigned to this name. + * @param needsDefaultResult Whether the result value must be set. + * @param isCallInLoop Whether the function body must be prepared to be + * injected into the body of a loop. + * @return A clone of the function body mutated to be suitable for injection + * as a statement into another code block. + */ + Node mutate(String fnName, Node fnNode, Node callNode, + String resultName, boolean needsDefaultResult, boolean isCallInLoop) { + Node newFnNode = fnNode.cloneTree(); + // Now that parameter names have been replaced, make sure all the local + // names are unique, to allow functions to be inlined multiple times + // without causing conflicts. + makeLocalNamesUnique(newFnNode, isCallInLoop); + + // Function declarations must be rewritten as function expressions as + // they will be within a block and normalization prevents function + // declarations within block as browser implementations vary. + rewriteFunctionDeclarations(newFnNode.getLastChild()); + + // TODO(johnlenz): Mark NAME nodes constant for parameters that are not + // modified. + Set namesToAlias = + FunctionArgumentInjector.findModifiedParameters(newFnNode); + LinkedHashMap args = + FunctionArgumentInjector.getFunctionCallParameterMap( + newFnNode, callNode, this.safeNameIdSupplier); + boolean hasArgs = !args.isEmpty(); + if (hasArgs) { + FunctionArgumentInjector.maybeAddTempsForCallArguments( + newFnNode, args, namesToAlias, compiler.getCodingConvention()); + } + + Node newBlock = NodeUtil.getFunctionBody(newFnNode); + // Make the newBlock insertable . + newBlock.detachFromParent(); + + if (hasArgs) { + Node inlineResult = aliasAndInlineArguments(newBlock, + args, namesToAlias); + Preconditions.checkState(newBlock == inlineResult); + } + + // + // For calls inlined into loops, VAR declarations are not reinitialized to + // undefined as they would have been if the function were called, so ensure + // that they are properly initialized. + // + if (isCallInLoop) { + fixUnitializedVarDeclarations(newBlock); + } + + String labelName = getLabelNameForFunction(fnName); + Node injectableBlock = replaceReturns( + newBlock, resultName, labelName, needsDefaultResult); + Preconditions.checkState(injectableBlock != null); + + return injectableBlock; + } + + + /** + * @param n The node to inspect + */ + private void rewriteFunctionDeclarations(Node n) { + if (n.isFunction()) { + if (NodeUtil.isFunctionDeclaration(n)) { + // Rewrite: function f() {} ==> var f = function() {} + Node fnNameNode = n.getFirstChild(); + + Node name = IR.name(fnNameNode.getString()).srcref(fnNameNode); + Node var = IR.var(name).srcref(n); + + fnNameNode.setString(""); + // Add the VAR, remove the FUNCTION + n.getParent().replaceChild(n, var); + // readd the function as a function expression + name.addChildToFront(n); + } + return; + } + + for (Node c = n.getFirstChild(), next; c != null; c = next) { + next = c.getNext(); // We may rewrite "c" + rewriteFunctionDeclarations(c); + } + } + + /** + * For all VAR node with uninitialized declarations, set + * the values to be "undefined". + */ + private void fixUnitializedVarDeclarations(Node n) { + // Inner loop structure must already have logic to initialize its + // variables. In particular FOR-IN structures must not be modified. + if (NodeUtil.isLoopStructure(n)) { + return; + } + + // For all VARs + if (n.isVar()) { + Node name = n.getFirstChild(); + // It isn't initialized. + if (!name.hasChildren()) { + Node srcLocation = name; + name.addChildToBack(NodeUtil.newUndefinedNode(srcLocation)); + } + return; + } + + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + fixUnitializedVarDeclarations(c); + } + } + + + /** + * Fix-up all local names to be unique for this subtree. + * @param fnNode A mutable instance of the function to be inlined. + */ + private void makeLocalNamesUnique(Node fnNode, boolean isCallInLoop) { + Supplier idSupplier = compiler.getUniqueNameIdSupplier(); + // Make variable names unique to this instance. + NodeTraversal.traverse( + compiler, fnNode, new MakeDeclaredNamesUnique( + new InlineRenamer( + idSupplier, + "inline_", + isCallInLoop))); + // Make label names unique to this instance. + new RenameLabels(compiler, new LabelNameSupplier(idSupplier), false) + .process(null, fnNode); + } + + static class LabelNameSupplier implements Supplier { + final Supplier idSupplier; + + LabelNameSupplier(Supplier idSupplier) { + this.idSupplier = idSupplier; + } + + @Override + public String get() { + return "JSCompiler_inline_label_" + idSupplier.get(); + } + } + + /** + * Create a unique label name. + */ + private String getLabelNameForFunction(String fnName){ + String name = (fnName == null || fnName.isEmpty()) ? "anon" : fnName; + return "JSCompiler_inline_label_" + name + "_" + safeNameIdSupplier.get(); + } + + /** + * Create a unique "this" name. + */ + private String getUniqueThisName() { + return "JSCompiler_inline_this_" + safeNameIdSupplier.get(); + } + + /** + * Inlines the arguments within the node tree using the given argument map, + * replaces "unsafe" names with local aliases. + * + * The aliases for unsafe require new VAR declarations, so this function + * can not be used in for direct CALL node replacement as VAR nodes can not be + * created there. + * + * @return The node or its replacement. + */ + private Node aliasAndInlineArguments( + Node fnTemplateRoot, LinkedHashMap argMap, + Set namesToAlias) { + + if (namesToAlias == null || namesToAlias.isEmpty()) { + // There are no names to alias, just inline the arguments directly. + Node result = FunctionArgumentInjector.inject( + compiler, fnTemplateRoot, null, argMap); + Preconditions.checkState(result == fnTemplateRoot); + return result; + } else { + // Create local alias of names that can not be safely + // used directly. + + // An arg map that will be updated to contain the + // safe aliases. + Map newArgMap = Maps.newHashMap(argMap); + + // Declare the alias in the same order as they + // are declared. + List newVars = Lists.newLinkedList(); + // NOTE: argMap is a linked map so we get the parameters in the + // order that they were declared. + for (Entry entry : argMap.entrySet()) { + String name = entry.getKey(); + if (namesToAlias.contains(name)) { + if (name.equals(THIS_MARKER)) { + boolean referencesThis = NodeUtil.referencesThis(fnTemplateRoot); + // Update "this", this is only necessary if "this" is referenced + // and the value of "this" is not Token.THIS, or the value of "this" + // has side effects. + + Node value = entry.getValue(); + if (!value.isThis() + && (referencesThis + || NodeUtil.mayHaveSideEffects(value, compiler))) { + String newName = getUniqueThisName(); + Node newValue = entry.getValue().cloneTree(); + Node newNode = NodeUtil.newVarNode(newName, newValue) + .copyInformationFromForTree(newValue); + newVars.add(0, newNode); + // Remove the parameter from the list to replace. + newArgMap.put(THIS_MARKER, + IR.name(newName) + .srcrefTree(newValue)); + } + } else { + Node newValue = entry.getValue().cloneTree(); + Node newNode = NodeUtil.newVarNode(name, newValue) + .copyInformationFromForTree(newValue); + newVars.add(0, newNode); + // Remove the parameter from the list to replace. + newArgMap.remove(name); + } + } + } + + // Inline the arguments. + Node result = FunctionArgumentInjector.inject( + compiler, fnTemplateRoot, null, newArgMap); + Preconditions.checkState(result == fnTemplateRoot); + + // Now that the names have been replaced, add the new aliases for + // the old names. + for (Node n : newVars) { + fnTemplateRoot.addChildToFront(n); + } + + return result; + } + } + + /** + * Convert returns to assignments and breaks, as needed. + * For example, with a labelName of 'foo': + * { + * return a; + * } + * becomes: + * foo: { + * a; + * break foo; + * } + * or + * foo: { + * resultName = a; + * break foo; + * } + * + * @param resultMustBeSet Whether the result must always be set to a value. + * @return The node containing the transformed block, this may be different + * than the passed in node 'block'. + */ + private static Node replaceReturns( + Node block, String resultName, String labelName, + boolean resultMustBeSet) { + Preconditions.checkNotNull(block); + Preconditions.checkNotNull(labelName); + + Node root = block; + + boolean hasReturnAtExit = false; + int returnCount = NodeUtil.getNodeTypeReferenceCount( + block, Token.RETURN, new NodeUtil.MatchShallowStatement()); + if (returnCount > 0) { + hasReturnAtExit = hasReturnAtExit(block); + // TODO(johnlenz): Simpler not to special case this, + // and let it be optimized later. + if (hasReturnAtExit) { + convertLastReturnToStatement(block, resultName); + returnCount--; + } + + if (returnCount > 0) { + // A label and breaks are needed. + + // Add the breaks + replaceReturnWithBreak(block, null, resultName, labelName); + + // Add label + Node name = IR.labelName(labelName).srcref(block); + Node label = IR.label(name, block).srcref(block); + + Node newRoot = IR.block().srcref(block); + newRoot.addChildrenToBack(label); + + + // The label is now the root. + root = newRoot; + } + } + + // If there wasn't an return at the end of the function block, and we need + // a result, add one to the block. + if (resultMustBeSet && !hasReturnAtExit && resultName != null) { + addDummyAssignment(block, resultName); + } + + return root; + } + + /********************************************************************** + * Functions following here are general node transformation functions + **********************************************************************/ + + /** + * Example: + * a = (void) 0; + */ + private static void addDummyAssignment(Node node, String resultName) { + Preconditions.checkArgument(node.isBlock()); + + // A result is needed create a dummy value. + Node srcLocation = node; + Node retVal = NodeUtil.newUndefinedNode(srcLocation); + Node resultNode = createAssignStatementNode(resultName, retVal); + resultNode.copyInformationFromForTree(node); + + node.addChildrenToBack(resultNode); + } + + /** + * Replace the 'return' statement with its child expression. + * "return foo()" becomes "foo()" or "resultName = foo()" + * "return" is removed or becomes "resultName = void 0". + * + * @param block + * @param resultName + */ + private static void convertLastReturnToStatement( + Node block, String resultName) { + Node ret = block.getLastChild(); + Preconditions.checkArgument(ret.isReturn()); + Node resultNode = getReplacementReturnStatement(ret, resultName); + + if (resultNode == null) { + block.removeChild(ret); + } else { + resultNode.copyInformationFromForTree(ret); + block.replaceChild(ret, resultNode); + } + } + + /** + * Create a valid statement Node containing an assignment to name of the + * given expression. + */ + private static Node createAssignStatementNode(String name, Node expression) { + // Create 'name = result-expression;' statement. + // EXPR (ASSIGN (NAME, EXPRESSION)) + Node nameNode = IR.name(name); + Node assign = IR.assign(nameNode, expression); + return NodeUtil.newExpr(assign); + } + + /** + * Replace the 'return' statement with its child expression. + * If the result is needed (resultName != null): + * "return foo()" becomes "resultName = foo()" + * "return" becomes "resultName = void 0". + * Otherwise: + * "return foo()" becomes "foo()" + * "return", null is returned. + */ + private static Node getReplacementReturnStatement( + Node node, String resultName) { + Node resultNode = null; + + Node retVal = null; + if (node.hasChildren()) { + // Clone the child as the child hasn't been removed + // from the node yet. + retVal = node.getFirstChild().cloneTree(); + } + + if (resultName == null) { + if (retVal != null) { + resultNode = NodeUtil.newExpr(retVal); // maybe null. + } + } else { + if (retVal == null) { + // A result is needed create a dummy value. + Node srcLocation = node; + retVal = NodeUtil.newUndefinedNode(srcLocation); + } + // Create a "resultName = retVal;" statement. + resultNode = createAssignStatementNode(resultName, retVal); + } + + return resultNode; + } + + /** + * @return Whether the given block end with an return statement. + */ + private static boolean hasReturnAtExit(Node block) { + // Only inline functions that return something (empty returns + // will be handled by ConstFolding+EmptyFunctionRemoval) + return (block.getLastChild().isReturn()); + } + + /** + * Replace the 'return' statement with its child expression. + * "return foo()" becomes "{foo(); break;}" or + * "{resultName = foo(); break;}" + * "return" becomes {break;} or "{resultName = void 0;break;}". + */ + private static Node replaceReturnWithBreak(Node current, Node parent, + String resultName, String labelName) { + + if (current.isFunction() + || current.isExprResult()) { + // Don't recurse into functions definitions, and expressions can't + // contain RETURN nodes. + return current; + } + + if (current.isReturn()) { + Preconditions.checkState(NodeUtil.isStatementBlock(parent)); + + Node resultNode = getReplacementReturnStatement(current, resultName); + Node breakNode = IR.breakNode(IR.labelName(labelName)); + + // Replace the node in parent, and reset current to the first new child. + breakNode.copyInformationFromForTree(current); + parent.replaceChild(current, breakNode); + if (resultNode != null) { + resultNode.copyInformationFromForTree(current); + parent.addChildBefore(resultNode, breakNode); + } + current = breakNode; + } else { + for (Node c = current.getFirstChild(); c != null; c = c.getNext()) { + // c may be replaced. + c = replaceReturnWithBreak(c, current, resultName, labelName); + } + } + + return current; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionTypeBuilder.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionTypeBuilder.java new file mode 100644 index 0000000..db990fa --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/FunctionTypeBuilder.java @@ -0,0 +1,913 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import static com.google.javascript.jscomp.TypeCheck.BAD_IMPLEMENTED_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.FUNCTION_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMultiset; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Multiset; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.JSTypeExpression; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionBuilder; +import com.google.javascript.rhino.jstype.FunctionParamBuilder; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; + +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nullable; + +/** + * A builder for FunctionTypes, because FunctionTypes are so + * ridiculously complex. All methods return {@code this} for ease of use. + * + * Right now, this mostly uses JSDocInfo to infer type information about + * functions. In the long term, developers should extend it to use other + * signals by overloading the various "inferXXX" methods. For example, we + * might want to use {@code goog.inherits} calls as a signal for inheritance, or + * {@code return} statements as a signal for return type. + * + * NOTE(nicksantos): Organizationally, this feels like it should be in Rhino. + * But it depends on some coding convention stuff that's really part + * of JSCompiler. + * + * @author nicksantos@google.com (Nick Santos) + * @author pascallouis@google.com (Pascal-Louis Perez) + */ +final class FunctionTypeBuilder { + + private final String fnName; + private final AbstractCompiler compiler; + private final CodingConvention codingConvention; + private final JSTypeRegistry typeRegistry; + private final Node errorRoot; + private final String sourceName; + private final Scope scope; + + private FunctionContents contents = UnknownFunctionContents.get(); + + private JSType returnType = null; + private boolean returnTypeInferred = false; + private List implementedInterfaces = null; + private List extendedInterfaces = null; + private ObjectType baseType = null; + private JSType thisType = null; + private boolean isConstructor = false; + private boolean makesStructs = false; + private boolean makesDicts = false; + private boolean isInterface = false; + private Node parametersNode = null; + private ImmutableList templateTypeNames = ImmutableList.of(); + + static final DiagnosticType EXTENDS_WITHOUT_TYPEDEF = DiagnosticType.warning( + "JSC_EXTENDS_WITHOUT_TYPEDEF", + "@extends used without @constructor or @interface for {0}"); + + static final DiagnosticType EXTENDS_NON_OBJECT = DiagnosticType.warning( + "JSC_EXTENDS_NON_OBJECT", + "{0} @extends non-object type {1}"); + + static final DiagnosticType RESOLVED_TAG_EMPTY = DiagnosticType.warning( + "JSC_RESOLVED_TAG_EMPTY", + "Could not resolve type in {0} tag of {1}"); + + static final DiagnosticType IMPLEMENTS_WITHOUT_CONSTRUCTOR = + DiagnosticType.warning( + "JSC_IMPLEMENTS_WITHOUT_CONSTRUCTOR", + "@implements used without @constructor or @interface for {0}"); + + static final DiagnosticType CONSTRUCTOR_REQUIRED = + DiagnosticType.warning("JSC_CONSTRUCTOR_REQUIRED", + "{0} used without @constructor for {1}"); + + static final DiagnosticType VAR_ARGS_MUST_BE_LAST = DiagnosticType.warning( + "JSC_VAR_ARGS_MUST_BE_LAST", + "variable length argument must be last"); + + static final DiagnosticType OPTIONAL_ARG_AT_END = DiagnosticType.warning( + "JSC_OPTIONAL_ARG_AT_END", + "optional arguments must be at the end"); + + static final DiagnosticType INEXISTANT_PARAM = DiagnosticType.warning( + "JSC_INEXISTANT_PARAM", + "parameter {0} does not appear in {1}''s parameter list"); + + static final DiagnosticType TYPE_REDEFINITION = DiagnosticType.warning( + "JSC_TYPE_REDEFINITION", + "attempted re-definition of type {0}\n" + + "found : {1}\n" + + "expected: {2}"); + + static final DiagnosticType TEMPLATE_TYPE_DUPLICATED = DiagnosticType.warning( + "JSC_TEMPLATE_TYPE_DUPLICATED", + "Only one parameter type must be the template type"); + + static final DiagnosticType TEMPLATE_TYPE_EXPECTED = DiagnosticType.warning( + "JSC_TEMPLATE_TYPE_EXPECTED", + "The template type must be a parameter type"); + + static final DiagnosticType THIS_TYPE_NON_OBJECT = + DiagnosticType.warning( + "JSC_THIS_TYPE_NON_OBJECT", + "@this type of a function must be an object\n" + + "Actual type: {0}"); + + private class ExtendedTypeValidator implements Predicate { + @Override + public boolean apply(JSType type) { + ObjectType objectType = ObjectType.cast(type); + if (objectType == null) { + reportWarning(EXTENDS_NON_OBJECT, fnName, type.toString()); + return false; + } else if (objectType.isEmptyType()) { + reportWarning(RESOLVED_TAG_EMPTY, "@extends", fnName); + return false; + } else if (objectType.isUnknownType()) { + if (hasMoreTagsToResolve(objectType)) { + return true; + } else { + reportWarning(RESOLVED_TAG_EMPTY, "@extends", fnName); + return false; + } + } else { + return true; + } + } + } + + private class ImplementedTypeValidator implements Predicate { + @Override + public boolean apply(JSType type) { + ObjectType objectType = ObjectType.cast(type); + if (objectType == null) { + reportError(BAD_IMPLEMENTED_TYPE, fnName); + return false; + } else if (objectType.isEmptyType()) { + reportWarning(RESOLVED_TAG_EMPTY, "@implements", fnName); + return false; + } else if (objectType.isUnknownType()) { + if (hasMoreTagsToResolve(objectType)) { + return true; + } else { + reportWarning(RESOLVED_TAG_EMPTY, "@implements", fnName); + return false; + } + } else { + return true; + } + } + } + + /** + * @param fnName The function name. + * @param compiler The compiler. + * @param errorRoot The node to associate with any warning generated by + * this builder. + * @param sourceName A source name for associating any warnings that + * we have to emit. + * @param scope The syntactic scope. + */ + FunctionTypeBuilder(String fnName, AbstractCompiler compiler, + Node errorRoot, String sourceName, Scope scope) { + Preconditions.checkNotNull(errorRoot); + + this.fnName = fnName == null ? "" : fnName; + this.codingConvention = compiler.getCodingConvention(); + this.typeRegistry = compiler.getTypeRegistry(); + this.errorRoot = errorRoot; + this.sourceName = sourceName; + this.compiler = compiler; + this.scope = scope; + } + + /** + * Sets the contents of this function. + */ + FunctionTypeBuilder setContents(@Nullable FunctionContents contents) { + if (contents != null) { + this.contents = contents; + } + return this; + } + + /** + * Infer the parameter and return types of a function from + * the parameter and return types of the function it is overriding. + * + * @param oldType The function being overridden. Does nothing if this is null. + * @param paramsParent The LP node of the function that we're assigning to. + * If null, that just means we're not initializing this to a function + * literal. + */ + FunctionTypeBuilder inferFromOverriddenFunction( + @Nullable FunctionType oldType, @Nullable Node paramsParent) { + if (oldType == null) { + return this; + } + + returnType = oldType.getReturnType(); + returnTypeInferred = oldType.isReturnTypeInferred(); + if (paramsParent == null) { + // Not a function literal. + parametersNode = oldType.getParametersNode(); + if (parametersNode == null) { + parametersNode = new FunctionParamBuilder(typeRegistry).build(); + } + } else { + // We're overriding with a function literal. Apply type information + // to each parameter of the literal. + FunctionParamBuilder paramBuilder = + new FunctionParamBuilder(typeRegistry); + Iterator oldParams = oldType.getParameters().iterator(); + boolean warnedAboutArgList = false; + boolean oldParamsListHitOptArgs = false; + for (Node currentParam = paramsParent.getFirstChild(); + currentParam != null; currentParam = currentParam.getNext()) { + if (oldParams.hasNext()) { + Node oldParam = oldParams.next(); + Node newParam = paramBuilder.newParameterFromNode(oldParam); + + oldParamsListHitOptArgs = oldParamsListHitOptArgs || + oldParam.isVarArgs() || + oldParam.isOptionalArg(); + + // The subclass method might write its var_args as individual + // arguments. + if (currentParam.getNext() != null && newParam.isVarArgs()) { + newParam.setVarArgs(false); + newParam.setOptionalArg(true); + } + } else { + warnedAboutArgList |= addParameter( + paramBuilder, + typeRegistry.getNativeType(UNKNOWN_TYPE), + warnedAboutArgList, + codingConvention.isOptionalParameter(currentParam) || + oldParamsListHitOptArgs, + codingConvention.isVarArgsParameter(currentParam)); + } + } + + // Clone any remaining params that aren't in the function literal, + // but make them optional. + while (oldParams.hasNext()) { + paramBuilder.newOptionalParameterFromNode(oldParams.next()); + } + + parametersNode = paramBuilder.build(); + } + return this; + } + + /** + * Infer the return type from JSDocInfo. + */ + FunctionTypeBuilder inferReturnType(@Nullable JSDocInfo info) { + if (info != null && info.hasReturnType()) { + returnType = info.getReturnType().evaluate(scope, typeRegistry); + returnTypeInferred = false; + } + + return this; + } + + /** + * Infer the role of the function (whether it's a constructor or interface) + * and what it inherits from in JSDocInfo. + */ + FunctionTypeBuilder inferInheritance(@Nullable JSDocInfo info) { + if (info != null) { + isConstructor = info.isConstructor(); + makesStructs = info.makesStructs(); + makesDicts = info.makesDicts(); + isInterface = info.isInterface(); + + if (makesStructs && !isConstructor) { + reportWarning(CONSTRUCTOR_REQUIRED, "@struct", fnName); + } else if (makesDicts && !isConstructor) { + reportWarning(CONSTRUCTOR_REQUIRED, "@dict", fnName); + } + + // base type + if (info.hasBaseType()) { + if (isConstructor) { + JSType maybeBaseType = + info.getBaseType().evaluate(scope, typeRegistry); + if (maybeBaseType != null && + maybeBaseType.setValidator(new ExtendedTypeValidator())) { + baseType = (ObjectType) maybeBaseType; + } + } else { + reportWarning(EXTENDS_WITHOUT_TYPEDEF, fnName); + } + } + + // Implemented interfaces (for constructors only). + if (info.getImplementedInterfaceCount() > 0) { + if (isConstructor) { + implementedInterfaces = Lists.newArrayList(); + for (JSTypeExpression t : info.getImplementedInterfaces()) { + JSType maybeInterType = t.evaluate(scope, typeRegistry); + if (maybeInterType != null && + maybeInterType.setValidator(new ImplementedTypeValidator())) { + implementedInterfaces.add((ObjectType) maybeInterType); + } + } + } else if (isInterface) { + reportWarning( + TypeCheck.CONFLICTING_IMPLEMENTED_TYPE, fnName); + } else { + reportWarning(CONSTRUCTOR_REQUIRED, "@implements", fnName); + } + } + + // extended interfaces (for interfaces only) + // We've already emitted a warning if this is not an interface. + if (isInterface) { + extendedInterfaces = Lists.newArrayList(); + for (JSTypeExpression t : info.getExtendedInterfaces()) { + JSType maybeInterfaceType = t.evaluate(scope, typeRegistry); + if (maybeInterfaceType != null && + maybeInterfaceType.setValidator(new ExtendedTypeValidator())) { + extendedInterfaces.add((ObjectType) maybeInterfaceType); + } + } + } + } + + return this; + } + + /** + * Infers the type of {@code this}. + * @param type The type of this if the info is missing. + */ + FunctionTypeBuilder inferThisType(JSDocInfo info, JSType type) { + // Look at the @this annotation first. + inferThisType(info); + + if (thisType == null) { + ObjectType objType = ObjectType.cast(type); + if (objType != null && (info == null || !info.hasType())) { + thisType = objType; + } + } + + return this; + } + + /** + * Infers the type of {@code this}. + * @param info The JSDocInfo for this function. + */ + FunctionTypeBuilder inferThisType(JSDocInfo info) { + JSType maybeThisType = null; + if (info != null && info.hasThisType()) { + // TODO(johnlenz): In ES5 strict mode a function can have a null or + // undefined "this" value, but all the existing "@this" annotations + // don't declare restricted types. + maybeThisType = info.getThisType().evaluate(scope, typeRegistry) + .restrictByNotNullOrUndefined(); + } + if (maybeThisType != null) { + thisType = maybeThisType; + } + + return this; + } + + /** + * Infer the parameter types from the doc info alone. + */ + FunctionTypeBuilder inferParameterTypes(JSDocInfo info) { + // Create a fake args parent. + Node lp = IR.paramList(); + for (String name : info.getParameterNames()) { + lp.addChildToBack(IR.name(name)); + } + + return inferParameterTypes(lp, info); + } + + /** + * Infer the parameter types from the list of argument names and + * the doc info. + */ + FunctionTypeBuilder inferParameterTypes(@Nullable Node argsParent, + @Nullable JSDocInfo info) { + if (argsParent == null) { + if (info == null) { + return this; + } else { + return inferParameterTypes(info); + } + } + + // arguments + Node oldParameterType = null; + if (parametersNode != null) { + oldParameterType = parametersNode.getFirstChild(); + } + + FunctionParamBuilder builder = new FunctionParamBuilder(typeRegistry); + boolean warnedAboutArgList = false; + Set allJsDocParams = (info == null) ? + Sets.newHashSet() : + Sets.newHashSet(info.getParameterNames()); + boolean foundTemplateType = false; + boolean isVarArgs = false; + for (Node arg : argsParent.children()) { + String argumentName = arg.getString(); + allJsDocParams.remove(argumentName); + + // type from JSDocInfo + JSType parameterType = null; + boolean isOptionalParam = isOptionalParameter(arg, info); + isVarArgs = isVarArgsParameter(arg, info); + + if (info != null && info.hasParameterType(argumentName)) { + parameterType = + info.getParameterType(argumentName).evaluate(scope, typeRegistry); + } else if (oldParameterType != null && + oldParameterType.getJSType() != null) { + parameterType = oldParameterType.getJSType(); + isOptionalParam = oldParameterType.isOptionalArg(); + isVarArgs = oldParameterType.isVarArgs(); + } else { + parameterType = typeRegistry.getNativeType(UNKNOWN_TYPE); + } + + warnedAboutArgList |= addParameter( + builder, parameterType, warnedAboutArgList, + isOptionalParam, + isVarArgs); + + if (oldParameterType != null) { + oldParameterType = oldParameterType.getNext(); + } + } + + // Copy over any old parameters that aren't in the param list. + if (!isVarArgs) { + while (oldParameterType != null && !isVarArgs) { + builder.newParameterFromNode(oldParameterType); + oldParameterType = oldParameterType.getNext(); + } + } + + for (String inexistentName : allJsDocParams) { + reportWarning(INEXISTANT_PARAM, inexistentName, fnName); + } + + parametersNode = builder.build(); + return this; + } + + /** + * @return Whether the given param is an optional param. + */ + private boolean isOptionalParameter( + Node param, @Nullable JSDocInfo info) { + if (codingConvention.isOptionalParameter(param)) { + return true; + } + + String paramName = param.getString(); + return info != null && info.hasParameterType(paramName) && + info.getParameterType(paramName).isOptionalArg(); + } + + /** + * Determine whether this is a var args parameter. + * @return Whether the given param is a var args param. + */ + private boolean isVarArgsParameter( + Node param, @Nullable JSDocInfo info) { + if (codingConvention.isVarArgsParameter(param)) { + return true; + } + + String paramName = param.getString(); + return info != null && info.hasParameterType(paramName) && + info.getParameterType(paramName).isVarArgs(); + } + + /** + * Infer the template type from the doc info. + */ + FunctionTypeBuilder inferTemplateTypeName(@Nullable JSDocInfo info) { + if (info != null) { + templateTypeNames = info.getTemplateTypeNames(); + typeRegistry.setTemplateTypeNames(templateTypeNames); + } + return this; + } + + /** + * Add a parameter to the param list. + * @param builder A builder. + * @param paramType The parameter type. + * @param warnedAboutArgList Whether we've already warned about arg ordering + * issues (like if optional args appeared before required ones). + * @param isOptional Is this an optional parameter? + * @param isVarArgs Is this a var args parameter? + * @return Whether a warning was emitted. + */ + private boolean addParameter(FunctionParamBuilder builder, + JSType paramType, boolean warnedAboutArgList, + boolean isOptional, boolean isVarArgs) { + boolean emittedWarning = false; + if (isOptional) { + // Remembering that an optional parameter has been encountered + // so that if a non optional param is encountered later, an + // error can be reported. + if (!builder.addOptionalParams(paramType) && !warnedAboutArgList) { + reportWarning(VAR_ARGS_MUST_BE_LAST); + emittedWarning = true; + } + } else if (isVarArgs) { + if (!builder.addVarArgs(paramType) && !warnedAboutArgList) { + reportWarning(VAR_ARGS_MUST_BE_LAST); + emittedWarning = true; + } + } else { + if (!builder.addRequiredParams(paramType) && !warnedAboutArgList) { + // An optional parameter was seen and this argument is not an optional + // or var arg so it is an error. + if (builder.hasVarArgs()) { + reportWarning(VAR_ARGS_MUST_BE_LAST); + } else { + reportWarning(OPTIONAL_ARG_AT_END); + } + emittedWarning = true; + } + } + return emittedWarning; + } + + /** + * Builds the function type, and puts it in the registry. + */ + FunctionType buildAndRegister() { + if (returnType == null) { + // Infer return types. + // We need to be extremely conservative about this, because of two + // competing needs. + // 1) If we infer the return type of f too widely, then we won't be able + // to assign f to other functions. + // 2) If we infer the return type of f too narrowly, then we won't be + // able to override f in subclasses. + // So we only infer in cases where the user doesn't expect to write + // @return annotations--when it's very obvious that the function returns + // nothing. + if (!contents.mayHaveNonEmptyReturns() && + !contents.mayHaveSingleThrow() && + !contents.mayBeFromExterns()) { + returnType = typeRegistry.getNativeType(VOID_TYPE); + returnTypeInferred = true; + } + } + + if (returnType == null) { + returnType = typeRegistry.getNativeType(UNKNOWN_TYPE); + } + + if (parametersNode == null) { + throw new IllegalStateException( + "All Function types must have params and a return type"); + } + + FunctionType fnType; + if (isConstructor) { + fnType = getOrCreateConstructor(); + } else if (isInterface) { + fnType = typeRegistry.createInterfaceType( + fnName, contents.getSourceNode()); + if (getScopeDeclaredIn().isGlobal() && !fnName.isEmpty()) { + typeRegistry.declareType(fnName, fnType.getInstanceType()); + } + maybeSetBaseType(fnType); + } else { + fnType = new FunctionBuilder(typeRegistry) + .withName(fnName) + .withSourceNode(contents.getSourceNode()) + .withParamsNode(parametersNode) + .withReturnType(returnType, returnTypeInferred) + .withTypeOfThis(thisType) + .withTemplateKeys(templateTypeNames) + .build(); + maybeSetBaseType(fnType); + } + + if (implementedInterfaces != null) { + fnType.setImplementedInterfaces(implementedInterfaces); + } + + if (extendedInterfaces != null) { + fnType.setExtendedInterfaces(extendedInterfaces); + } + + typeRegistry.clearTemplateTypeNames(); + + return fnType; + } + + private void maybeSetBaseType(FunctionType fnType) { + if (!fnType.isInterface() && baseType != null) { + fnType.setPrototypeBasedOn(baseType); + } + } + + /** + * Returns a constructor function either by returning it from the + * registry if it exists or creating and registering a new type. If + * there is already a type, then warn if the existing type is + * different than the one we are creating, though still return the + * existing function if possible. The primary purpose of this is + * that registering a constructor will fail for all built-in types + * that are initialized in {@link JSTypeRegistry}. We a) want to + * make sure that the type information specified in the externs file + * matches what is in the registry and b) annotate the externs with + * the {@link JSType} from the registry so that there are not two + * separate JSType objects for one type. + */ + private FunctionType getOrCreateConstructor() { + FunctionType fnType = typeRegistry.createConstructorType( + fnName, contents.getSourceNode(), parametersNode, returnType, null); + JSType existingType = typeRegistry.getType(fnName); + + if (makesStructs) { + fnType.setStruct(); + } else if (makesDicts) { + fnType.setDict(); + } + if (existingType != null) { + boolean isInstanceObject = existingType.isInstanceType(); + if (isInstanceObject || fnName.equals("Function")) { + FunctionType existingFn = + isInstanceObject ? + existingType.toObjectType().getConstructor() : + typeRegistry.getNativeFunctionType(FUNCTION_FUNCTION_TYPE); + + if (existingFn.getSource() == null) { + existingFn.setSource(contents.getSourceNode()); + } + + if (!existingFn.hasEqualCallType(fnType)) { + reportWarning(TYPE_REDEFINITION, fnName, + fnType.toString(), existingFn.toString()); + } + + return existingFn; + } else { + // We fall through and return the created type, even though it will fail + // to register. We have no choice as we have to return a function. We + // issue an error elsewhere though, so the user should fix it. + } + } + + maybeSetBaseType(fnType); + + if (getScopeDeclaredIn().isGlobal() && !fnName.isEmpty()) { + typeRegistry.declareType(fnName, fnType.getInstanceType()); + } + return fnType; + } + + private void reportWarning(DiagnosticType warning, String ... args) { + compiler.report(JSError.make(sourceName, errorRoot, warning, args)); + } + + private void reportError(DiagnosticType error, String ... args) { + compiler.report(JSError.make(sourceName, errorRoot, error, args)); + } + + /** + * Determines whether the given JsDoc info declares a function type. + */ + static boolean isFunctionTypeDeclaration(JSDocInfo info) { + return info.getParameterCount() > 0 || + info.hasReturnType() || + info.hasThisType() || + info.isConstructor() || + info.isInterface(); + } + + /** + * The scope that we should declare this function in, if it needs + * to be declared in a scope. Notice that TypedScopeCreator takes + * care of most scope-declaring. + */ + private Scope getScopeDeclaredIn() { + int dotIndex = fnName.indexOf("."); + if (dotIndex != -1) { + String rootVarName = fnName.substring(0, dotIndex); + Var rootVar = scope.getVar(rootVarName); + if (rootVar != null) { + return rootVar.getScope(); + } + } + return scope; + } + + /** + * Check whether a type is resolvable in the future + * If this has a supertype that hasn't been resolved yet, then we can assume + * this type will be OK once the super type resolves. + * @param objectType + * @return true if objectType is resolvable in the future + */ + private static boolean hasMoreTagsToResolve(ObjectType objectType) { + Preconditions.checkArgument(objectType.isUnknownType()); + if (objectType.getImplicitPrototype() != null) { + // constructor extends class + if (objectType.getImplicitPrototype().isResolved()) { + return false; + } else { + return true; + } + } else { + // interface extends interfaces + FunctionType ctor = objectType.getConstructor(); + if (ctor != null) { + for (ObjectType interfaceType : ctor.getExtendedInterfaces()) { + if (!interfaceType.isResolved()) { + return true; + } + } + } + return false; + } + } + + /** Holds data dynamically inferred about functions. */ + static interface FunctionContents { + /** Returns the source node of this function. May be null. */ + Node getSourceNode(); + + /** Returns if the function may be in externs. */ + boolean mayBeFromExterns(); + + /** Returns if a return of a real value (not undefined) appears. */ + boolean mayHaveNonEmptyReturns(); + + /** Returns if this consists of a single throw. */ + boolean mayHaveSingleThrow(); + + /** Gets a list of variables in this scope that are escaped. */ + Iterable getEscapedVarNames(); + + /** Gets a list of variables whose properties are escaped. */ + Set getEscapedQualifiedNames(); + + /** Gets the number of times each variable has been assigned. */ + Multiset getAssignedNameCounts(); + } + + static class UnknownFunctionContents implements FunctionContents { + private static UnknownFunctionContents singleton = + new UnknownFunctionContents(); + + static FunctionContents get() { + return singleton; + } + + @Override + public Node getSourceNode() { + return null; + } + + @Override + public boolean mayBeFromExterns() { + return true; + } + + @Override + public boolean mayHaveNonEmptyReturns() { + return true; + } + + @Override + public boolean mayHaveSingleThrow() { + return true; + } + + @Override + public Iterable getEscapedVarNames() { + return ImmutableList.of(); + } + + @Override + public Set getEscapedQualifiedNames() { + return ImmutableSet.of(); + } + + @Override + public Multiset getAssignedNameCounts() { + return ImmutableMultiset.of(); + } + } + + static class AstFunctionContents implements FunctionContents { + private final Node n; + private boolean hasNonEmptyReturns = false; + private Set escapedVarNames; + private Set escapedQualifiedNames; + private final Multiset assignedVarNames = HashMultiset.create(); + + AstFunctionContents(Node n) { + this.n = n; + } + + @Override + public Node getSourceNode() { + return n; + } + + @Override + public boolean mayBeFromExterns() { + return n.isFromExterns(); + } + + @Override + public boolean mayHaveNonEmptyReturns() { + return hasNonEmptyReturns; + } + + void recordNonEmptyReturn() { + hasNonEmptyReturns = true; + } + + @Override + public boolean mayHaveSingleThrow() { + Node block = n.getLastChild(); + return block.hasOneChild() && block.getFirstChild().isThrow(); + } + + @Override + public Iterable getEscapedVarNames() { + return escapedVarNames == null + ? ImmutableList.of() : escapedVarNames; + } + + void recordEscapedVarName(String name) { + if (escapedVarNames == null) { + escapedVarNames = Sets.newHashSet(); + } + escapedVarNames.add(name); + } + + @Override + public Set getEscapedQualifiedNames() { + return escapedQualifiedNames == null + ? ImmutableSet.of() : escapedQualifiedNames; + } + + void recordEscapedQualifiedName(String name) { + if (escapedQualifiedNames == null) { + escapedQualifiedNames = Sets.newHashSet(); + } + escapedQualifiedNames.add(name); + } + + @Override + public Multiset getAssignedNameCounts() { + return assignedVarNames; + } + + void recordAssignedName(String name) { + assignedVarNames.add(name); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GatherRawExports.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GatherRawExports.java new file mode 100644 index 0000000..eee9791 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GatherRawExports.java @@ -0,0 +1,82 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.Node; +import java.util.Set; + +/** + * External references of the form: "window['xx']" indicate names that must + * be reserved when variable renaming to avoid conflicts. + * + * @author johnlenz@google.com (John Lenz) + */ +class GatherRawExports extends AbstractPostOrderCallback + implements CompilerPass { + + private final AbstractCompiler compiler; + + private static final String GLOBAL_THIS_NAMES[] = { "window", "top" }; + + private final Set exportedVariables = Sets.newHashSet(); + + GatherRawExports(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + Node sibling = n.getNext(); + if (sibling != null + && sibling.isString() + && NodeUtil.isGet(parent)) { + // TODO(johnlenz): Should we warn if we see a property name that + // hasn't been exported? + if (isGlobalThisObject(t, n)) { + exportedVariables.add(sibling.getString()); + } + } + } + + private boolean isGlobalThisObject(NodeTraversal t, Node n) { + if (n.isThis()) { + return t.inGlobalScope(); + } else if (n.isName()) { + String varName = n.getString(); + int items = GLOBAL_THIS_NAMES.length; + for (int i = 0; i < items; i++) { + if (varName.equals(GLOBAL_THIS_NAMES[i])) { + return true; + } + } + } + return false; + } + + public Set getExportedVariableNames() { + return exportedVariables; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GatherSideEffectSubexpressionsCallback.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GatherSideEffectSubexpressionsCallback.java new file mode 100644 index 0000000..de70974 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GatherSideEffectSubexpressionsCallback.java @@ -0,0 +1,363 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.CodingConvention.SubclassRelationship; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.List; +import java.util.Set; + +/** + * Callback that gathers subexpressions that may have side effects + * and appends copies of those subexpressions to the replacements + * list. In the case of branching subexpressions, it simplifies the + * subexpression before adding it to the replacement list. + * + */ +class GatherSideEffectSubexpressionsCallback implements Callback { + + /** + * Used by GatherSideEffectSubexpressionsCallback to notify client + * code about side effect expressions that should be kept. + */ + interface SideEffectAccumulator { + + /** + * Returns true if the "mixin" and "inherits" function calls + * should be treated as if they had side effects. + */ + boolean classDefiningCallsHaveSideEffects(); + + /** + * Adds subtree to the list of nodes that have side effects. + * + * @param original - root of the tree. + */ + void keepSubTree(Node original); + + /** + * Simplifies a subtree whose root node is an AND or OR expression + * and adds the resulting subtree to the list of nodes that have + * side effects. + * + * @param original - root of the and/or expression. + */ + void keepSimplifiedShortCircuitExpression(Node original); + + /** + * Simplifies a subtree whose root node is a HOOK expression + * and adds the resulting subtree to the list of nodes that have + * side effects. + * + * @param hook - root of the hook expression. + * @param thenHasSideEffects - then branch has side effects + * @param elseHasSideEffects - else branch has side effects + */ + void keepSimplifiedHookExpression(Node hook, + boolean thenHasSideEffects, + boolean elseHasSideEffects); + } + + /** + * Populates the provided replacement list by appending copies of + * subtrees that have side effects. + * + * It is OK if this class tears up the original tree, because + * we're going to throw the tree out anyway. + */ + static final class GetReplacementSideEffectSubexpressions + implements SideEffectAccumulator { + private final AbstractCompiler compiler; + private final List replacements; + + /** + * Creates the accumulator. + * + * @param compiler - the AbstractCompiler + * @param replacements - list to accumulate into + */ + GetReplacementSideEffectSubexpressions(AbstractCompiler compiler, + List replacements) { + this.compiler = compiler; + this.replacements = replacements; + } + + @Override + public boolean classDefiningCallsHaveSideEffects() { + return true; + } + + @Override + public void keepSubTree(Node original) { + if (original.getParent() != null) { + original.detachFromParent(); + } + replacements.add(original); + } + + @Override + public void keepSimplifiedShortCircuitExpression(Node original) { + Preconditions.checkArgument( + (original.isAnd()) || (original.isOr()), + "Expected: AND or OR, Got: %s", Token.name(original.getType())); + Node left = original.getFirstChild(); + Node right = left.getNext(); + Node simplifiedRight = simplifyShortCircuitBranch(right); + original.detachChildren(); + original.addChildToBack(left); + original.addChildToBack(simplifiedRight); + keepSubTree(original); + } + + @Override + public void keepSimplifiedHookExpression(Node hook, + boolean thenHasSideEffects, + boolean elseHasSideEffects) { + Preconditions.checkArgument(hook.isHook(), + "Expected: HOOK, Got: %s", Token.name(hook.getType())); + Node condition = hook.getFirstChild(); + Node thenBranch = condition.getNext(); + Node elseBranch = thenBranch.getNext(); + if (thenHasSideEffects && elseHasSideEffects) { + hook.detachChildren(); + hook.addChildToBack(condition); + hook.addChildToBack(simplifyShortCircuitBranch(thenBranch)); + hook.addChildToBack(simplifyShortCircuitBranch(elseBranch)); + keepSubTree(hook); + } else if (thenHasSideEffects || elseHasSideEffects) { + int type = thenHasSideEffects ? Token.AND : Token.OR; + Node body = thenHasSideEffects ? thenBranch : elseBranch; + Node simplified = new Node( + type, condition.detachFromParent(), + simplifyShortCircuitBranch(body)) + .copyInformationFrom(hook); + keepSubTree(simplified); + } else { + throw new IllegalArgumentException( + "keepSimplifiedHookExpression must keep at least 1 branch"); + } + } + + private Node simplifyShortCircuitBranch(Node node) { + List parts = Lists.newArrayList(); + NodeTraversal.traverse( + compiler, node, + new GatherSideEffectSubexpressionsCallback( + compiler, + new GetReplacementSideEffectSubexpressions(compiler, parts))); + + Node ret = null; + for (Node part : parts) { + if (ret != null) { + ret = IR.comma(ret, part).srcref(node); + } else { + ret = part; + } + } + + if (ret == null) { + throw new IllegalArgumentException( + "expected at least one side effect subexpression in short " + + "circuit branch."); + } + + return ret; + } + } + + private static final Set FORBIDDEN_TYPES = ImmutableSet.of( + Token.BLOCK, Token.SCRIPT, Token.VAR, Token.EXPR_RESULT, Token.RETURN); + private final AbstractCompiler compiler; + private final SideEffectAccumulator accumulator; + + /** + * @param compiler - AbstractCompiler object + * @param accumulator - object that will accumulate roots of + * subtrees that have side effects. + */ + GatherSideEffectSubexpressionsCallback(AbstractCompiler compiler, + SideEffectAccumulator accumulator) { + this.compiler = compiler; + this.accumulator = accumulator; + } + + /** + * Determines if a call defines a class inheritance or mixing + * relation, according to the current coding convention. + */ + private boolean isClassDefiningCall(Node callNode) { + SubclassRelationship classes = + compiler.getCodingConvention().getClassesDefinedByCall(callNode); + return classes != null; + } + + /** + * Computes the list of subtrees whose root nodes have side effects. + * + *

      If the current subtree's root has side effects this method should + * call accumulator.keepSubTree and return 'false' to add the + * subtree to the result list and avoid avoid traversing the nodes children. + * + *

      Branching nodes whose then or else branch contain side effects + * must be simplified by doing a recursive traversal; this method + * should call the appropriate accumulator 'keepSimplified' method + * and return 'false' to stop the regular traversal. + */ + @Override + public boolean shouldTraverse( + NodeTraversal traversal, Node node, Node parent) { + if (FORBIDDEN_TYPES.contains(node.getType()) || + NodeUtil.isControlStructure(node)) { + throw new IllegalArgumentException( + Token.name(node.getType()) + " nodes are not supported."); + } + + // Do not recurse into nested functions. + if (node.isFunction()) { + return false; + } + + // simplify and maybe keep hook expression. + if (node.isHook()) { + return processHook(node); + } + + // simplify and maybe keep AND/OR expression. + if ((node.isAnd()) || (node.isOr())) { + return processShortCircuitExpression(node); + } + + if (!NodeUtil.nodeTypeMayHaveSideEffects(node, compiler)) { + return true; + } else { + + // Node type suggests that the expression has side effects. + + if (node.isCall()) { + return processFunctionCall(node); + } else if (node.isNew()) { + return processConstructorCall(node); + } else { + accumulator.keepSubTree(node); + return false; + } + } + } + + /** + * Processes an AND or OR expression. + * + * @return true to continue traversal, false otherwise + */ + boolean processShortCircuitExpression(Node node) { + Preconditions.checkArgument( + (node.isAnd()) || (node.isOr()), + "Expected: AND or OR, Got: %s", Token.name(node.getType())); + + // keep whole expression if RHS of the branching expression + // contains a call. + Node left = node.getFirstChild(); + Node right = left.getNext(); + if (NodeUtil.mayHaveSideEffects(right, compiler)) { + accumulator.keepSimplifiedShortCircuitExpression(node); + return false; + } else { + return true; + } + } + + /** + * Processes a HOOK expression. + * + * @return true to continue traversal, false otherwise + */ + boolean processHook(Node node) { + Preconditions.checkArgument(node.isHook(), + "Expected: HOOK, Got: %s", Token.name(node.getType())); + + Node condition = node.getFirstChild(); + Node ifBranch = condition.getNext(); + Node elseBranch = ifBranch.getNext(); + boolean thenHasSideEffects = NodeUtil.mayHaveSideEffects( + ifBranch, compiler); + boolean elseHasSideEffects = NodeUtil.mayHaveSideEffects( + elseBranch, compiler); + if (thenHasSideEffects || elseHasSideEffects) { + accumulator.keepSimplifiedHookExpression( + node, thenHasSideEffects, elseHasSideEffects); + return false; + } else { + return true; + } + } + + /** + * Processes a CALL expression. + * + * @return true to continue traversal, false otherwise + */ + boolean processFunctionCall(Node node) { + Preconditions.checkArgument(node.isCall(), + "Expected: CALL, Got: %s", Token.name(node.getType())); + + // Calls to functions that are known to be "pure" have no side + // effects. + Node functionName = node.getFirstChild(); + if (functionName.isName() || functionName.isGetProp()) { + if (!accumulator.classDefiningCallsHaveSideEffects() && + isClassDefiningCall(node)) { + return true; + } + } + + if (!NodeUtil.functionCallHasSideEffects(node)) { + return true; + } + + accumulator.keepSubTree(node); + return false; + } + + /** + * Processes a NEW expression. + * + * @return true to continue traversal, false otherwise + */ + boolean processConstructorCall(Node node) { + Preconditions.checkArgument(node.isNew(), + "Expected: NEW, Got: %s", Token.name(node.getType())); + + // Calls to constructors that are known to be "pure" have no + // side effects. + if (!NodeUtil.constructorCallHasSideEffects(node)) { + return true; + } + + accumulator.keepSubTree(node); + return false; + } + + @Override + public void visit(NodeTraversal traversal, Node node, Node parent) {} +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GenerateExports.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GenerateExports.java new file mode 100644 index 0000000..40b4264 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GenerateExports.java @@ -0,0 +1,156 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.FindExportableNodes.GenerateNodeContext; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import java.util.Map; + +/** + * Generates goog.exportSymbol/goog.exportProperty for the @export annotation. + * + */ +class GenerateExports implements CompilerPass { + + private static final String PROTOTYPE_PROPERTY = "prototype"; + + private final AbstractCompiler compiler; + + private final String exportSymbolFunction; + + private final String exportPropertyFunction; + + /** + * Creates a new generate exports compiler pass. + * @param compiler JS compiler. + * @param exportSymbolFunction function used for exporting symbols. + * @param exportPropertyFunction function used for exporting property names. + */ + GenerateExports(AbstractCompiler compiler, String exportSymbolFunction, + String exportPropertyFunction) { + Preconditions.checkNotNull(compiler); + Preconditions.checkNotNull(exportSymbolFunction); + Preconditions.checkNotNull(exportPropertyFunction); + + this.compiler = compiler; + this.exportSymbolFunction = exportSymbolFunction; + this.exportPropertyFunction = exportPropertyFunction; + } + + @Override + public void process(Node externs, Node root) { + FindExportableNodes findExportableNodes = new FindExportableNodes(compiler); + NodeTraversal.traverse(compiler, root, findExportableNodes); + Map exports = findExportableNodes + .getExports(); + + CodingConvention convention = compiler.getCodingConvention(); + for (Map.Entry entry : exports.entrySet()) { + String export = entry.getKey(); + GenerateNodeContext context = entry.getValue(); + + // Emit the proper CALL expression. + // This is an optimization to avoid exporting everything as a symbol + // because exporting a property is significantly simpler/faster. + // Only export the property if the parent is being exported or + // if the parent is "prototype" and the grandparent is being exported. + String parent = null; + String grandparent = null; + + Node node = context.getNode().getFirstChild(); + if (node.isGetProp()) { + parent = node.getFirstChild().getQualifiedName(); + if (node.getFirstChild().isGetProp() && + getPropertyName(node.getFirstChild()).equals(PROTOTYPE_PROPERTY)) { + grandparent = node.getFirstChild().getFirstChild().getQualifiedName(); + } + } + + boolean useExportSymbol = true; + if (grandparent != null && exports.containsKey(grandparent)) { + useExportSymbol = false; + } else if (parent != null && exports.containsKey(parent)) { + useExportSymbol = false; + } + + Node call; + if (useExportSymbol) { + // exportSymbol(publicPath, object); + call = IR.call( + NodeUtil.newQualifiedNameNode( + convention, exportSymbolFunction, + context.getNode(), export), + IR.string(export), + NodeUtil.newQualifiedNameNode( + convention, export, + context.getNode(), export)); + } else { + // exportProperty(object, publicName, symbol); + String property = getPropertyName(node); + call = IR.call( + NodeUtil.newQualifiedNameNode( + convention, exportPropertyFunction, + context.getNode(), exportPropertyFunction), + NodeUtil.newQualifiedNameNode( + convention, parent, + context.getNode(), exportPropertyFunction), + IR.string(property), + NodeUtil.newQualifiedNameNode( + convention, export, + context.getNode(), exportPropertyFunction)); + } + + Node expression = IR.exprResult(call); + annotate(expression); + + // It's important that any class-building calls (goog.inherits) + // come right after the class definition, so move the export after that. + Node insertionPoint = context.getContextNode().getNext(); + while (insertionPoint != null && + NodeUtil.isExprCall(insertionPoint) && + convention.getClassesDefinedByCall( + insertionPoint.getFirstChild()) != null) { + insertionPoint = insertionPoint.getNext(); + } + + if (insertionPoint == null) { + context.getScriptNode().addChildToBack(expression); + } else { + context.getScriptNode().addChildBefore(expression, insertionPoint); + } + compiler.reportCodeChange(); + } + } + + private void annotate(Node node) { + NodeTraversal.traverse( + compiler, node, new PrepareAst.PrepareAnnotations()); + } + + /** + * Assumes the node type is correct and returns the property name + * (not fully qualified). + * @param node node + * @return property name. + */ + private String getPropertyName(Node node) { + Preconditions.checkArgument(node.isGetProp()); + return node.getLastChild().getString(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GlobalNamespace.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GlobalNamespace.java new file mode 100644 index 0000000..1cd29f0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GlobalNamespace.java @@ -0,0 +1,1335 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import static com.google.javascript.rhino.jstype.JSTypeNative.GLOBAL_THIS; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.TokenStream; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.StaticReference; +import com.google.javascript.rhino.jstype.StaticScope; +import com.google.javascript.rhino.jstype.StaticSlot; +import com.google.javascript.rhino.jstype.StaticSourceFile; +import com.google.javascript.rhino.jstype.StaticSymbolTable; + +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Builds a global namespace of all the objects and their properties in + * the global scope. Also builds an index of all the references to those names. + * + */ +class GlobalNamespace + implements StaticScope, + StaticSymbolTable { + + private AbstractCompiler compiler; + private final Node root; + private final Node externsRoot; + private boolean inExterns; + private Scope externsScope; + private boolean generated = false; + + /** + * Each reference has an index in post-order. + * Notice that some nodes are represented by 2 Ref objects, so + * this index is not necessarily unique. + */ + private int currentPreOrderIndex = 0; + + /** Global namespace tree */ + private List globalNames = new ArrayList(); + + /** Maps names (e.g. "a.b.c") to nodes in the global namespace tree */ + private Map nameMap = new HashMap(); + + /** + * Creates an instance that may emit warnings when building the namespace. + * + * @param compiler The AbstractCompiler, for reporting code changes + * @param root The root of the rest of the code to build a namespace for. + */ + GlobalNamespace(AbstractCompiler compiler, Node root) { + this(compiler, null, root); + } + + /** + * Creates an instance that may emit warnings when building the namespace. + * + * @param compiler The AbstractCompiler, for reporting code changes + * @param externsRoot The root of the externs to build a namespace for. If + * this is null, externs and properties defined on extern types will not + * be included in the global namespace. If non-null, it allows + * user-defined function on extern types to be included in the global + * namespace. E.g. String.foo. + * @param root The root of the rest of the code to build a namespace for. + */ + GlobalNamespace(AbstractCompiler compiler, Node externsRoot, Node root) { + this.compiler = compiler; + this.externsRoot = externsRoot; + this.root = root; + } + + boolean hasExternsRoot() { + return externsRoot != null; + } + + @Override + public Node getRootNode() { + return root.getParent(); + } + + @Override + public StaticScope getParentScope() { + return null; + } + + @Override + public Name getSlot(String name) { + return getOwnSlot(name); + } + + @Override + public Name getOwnSlot(String name) { + ensureGenerated(); + return nameMap.get(name); + } + + @Override + public JSType getTypeOfThis() { + return compiler.getTypeRegistry().getNativeObjectType(GLOBAL_THIS); + } + + @Override + public Iterable getReferences(Name slot) { + ensureGenerated(); + return Collections.unmodifiableList(slot.getRefs()); + } + + @Override + public StaticScope getScope(Name slot) { + return this; + } + + @Override + public Iterable getAllSymbols() { + ensureGenerated(); + return Collections.unmodifiableCollection(getNameIndex().values()); + } + + private void ensureGenerated() { + if (!generated) { + process(); + } + } + + /** + * Gets a list of the roots of the forest of the global names, where the + * roots are the top-level names. + */ + List getNameForest() { + ensureGenerated(); + return globalNames; + } + + /** + * Gets an index of all the global names, indexed by full qualified name + * (as in "a", "a.b.c", etc.). + */ + Map getNameIndex() { + ensureGenerated(); + return nameMap; + } + + /** + * If the client adds new nodes to the AST, scan these new nodes + * to see if they've added any references to the global namespace. + * @param scope The scope to scan. + * @param newNodes New nodes to check. + */ + void scanNewNodes(Scope scope, Set newNodes) { + NodeTraversal t = new NodeTraversal(compiler, + new BuildGlobalNamespace(new NodeFilter(newNodes))); + t.traverseAtScope(scope); + } + + /** + * A filter that looks for qualified names that contain one of the nodes + * in the given set. + */ + private static class NodeFilter implements Predicate { + private final Set newNodes; + + NodeFilter(Set newNodes) { + this.newNodes = newNodes; + } + + @Override + public boolean apply(Node n) { + if (!n.isQualifiedName()) { + return false; + } + + Node current; + for (current = n; + current.isGetProp(); + current = current.getFirstChild()) { + if (newNodes.contains(current)) { + return true; + } + } + + return current.isName() && newNodes.contains(current); + } + } + + /** + * Builds the namespace lazily. + */ + private void process() { + if (externsRoot != null) { + inExterns = true; + NodeTraversal.traverse(compiler, externsRoot, new BuildGlobalNamespace()); + } + inExterns = false; + + NodeTraversal.traverse(compiler, root, new BuildGlobalNamespace()); + generated = true; + } + + /** + * Determines whether a name reference in a particular scope is a global name + * reference. + * + * @param name A variable or property name (e.g. "a" or "a.b.c.d") + * @param s The scope in which the name is referenced + * @return Whether the name reference is a global name reference + */ + private boolean isGlobalNameReference(String name, Scope s) { + String topVarName = getTopVarName(name); + return isGlobalVarReference(topVarName, s); + } + + /** + * Gets the top variable name from a possibly namespaced name. + * + * @param name A variable or qualified property name (e.g. "a" or "a.b.c.d") + * @return The top variable name (e.g. "a") + */ + private String getTopVarName(String name) { + int firstDotIndex = name.indexOf('.'); + return firstDotIndex == -1 ? name : name.substring(0, firstDotIndex); + } + + /** + * Determines whether a variable name reference in a particular scope is a + * global variable reference. + * + * @param name A variable name (e.g. "a") + * @param s The scope in which the name is referenced + * @return Whether the name reference is a global variable reference + */ + private boolean isGlobalVarReference(String name, Scope s) { + Scope.Var v = s.getVar(name); + if (v == null && externsScope != null) { + v = externsScope.getVar(name); + } + return v != null && !v.isLocal(); + } + + /** + * Gets whether a scope is the global scope. + * + * @param s A scope + * @return Whether the scope is the global scope + */ + private boolean isGlobalScope(Scope s) { + return s.getParent() == null; + } + + // ------------------------------------------------------------------------- + + /** + * Builds a tree representation of the global namespace. Omits prototypes. + */ + private class BuildGlobalNamespace implements NodeTraversal.Callback { + + private final Predicate nodeFilter; + + BuildGlobalNamespace() { + this(null); + } + + /** + * Builds a global namespace, but only visits nodes that match the + * given filter. + */ + BuildGlobalNamespace(Predicate nodeFilter) { + this.nodeFilter = nodeFilter; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) {} + + /** Collect the references in pre-order. */ + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + collect(t, n, parent); + return true; + } + + public void collect(NodeTraversal t, Node n, Node parent) { + if (nodeFilter != null && !nodeFilter.apply(n)) { + return; + } + + // If we are traversing the externs, then we save a pointer to the scope + // generated by them, so that we can do lookups in it later. + if (externsRoot != null && n == externsRoot) { + externsScope = t.getScope(); + } + + String name; + boolean isSet = false; + Name.Type type = Name.Type.OTHER; + boolean isPropAssign = false; + + switch (n.getType()) { + case Token.GETTER_DEF: + case Token.SETTER_DEF: + case Token.STRING_KEY: + // This may be a key in an object literal declaration. + name = null; + if (parent != null && parent.isObjectLit()) { + name = getNameForObjLitKey(n); + } + if (name == null) return; + isSet = true; + switch (n.getType()) { + case Token.STRING_KEY: + type = getValueType(n.getFirstChild()); + break; + case Token.GETTER_DEF: + type = Name.Type.GET; + break; + case Token.SETTER_DEF: + type = Name.Type.SET; + break; + default: + throw new IllegalStateException("unexpected:" + n); + } + break; + case Token.NAME: + // This may be a variable get or set. + if (parent != null) { + switch (parent.getType()) { + case Token.VAR: + isSet = true; + Node rvalue = n.getFirstChild(); + type = rvalue == null ? Name.Type.OTHER : getValueType(rvalue); + break; + case Token.ASSIGN: + if (parent.getFirstChild() == n) { + isSet = true; + type = getValueType(n.getNext()); + } + break; + case Token.GETPROP: + return; + case Token.FUNCTION: + Node gramps = parent.getParent(); + if (gramps == null || + NodeUtil.isFunctionExpression(parent)) return; + isSet = true; + type = Name.Type.FUNCTION; + break; + case Token.INC: + case Token.DEC: + isSet = true; + type = Name.Type.OTHER; + break; + default: + if (NodeUtil.isAssignmentOp(parent) && + parent.getFirstChild() == n) { + isSet = true; + type = Name.Type.OTHER; + } + } + } + name = n.getString(); + break; + case Token.GETPROP: + // This may be a namespaced name get or set. + if (parent != null) { + switch (parent.getType()) { + case Token.ASSIGN: + if (parent.getFirstChild() == n) { + isSet = true; + type = getValueType(n.getNext()); + isPropAssign = true; + } + break; + case Token.INC: + case Token.DEC: + isSet = true; + type = Name.Type.OTHER; + break; + case Token.GETPROP: + return; + default: + if (NodeUtil.isAssignmentOp(parent) && + parent.getFirstChild() == n) { + isSet = true; + type = Name.Type.OTHER; + } + } + } + name = n.getQualifiedName(); + if (name == null) return; + break; + default: + return; + } + + // We are only interested in global names. + Scope scope = t.getScope(); + if (!isGlobalNameReference(name, scope)) { + return; + } + + if (isSet) { + if (isGlobalScope(scope)) { + handleSetFromGlobal(t, n, parent, name, isPropAssign, type); + } else { + handleSetFromLocal(t, n, parent, name); + } + } else { + handleGet(t, n, parent, name); + } + } + + /** + * Gets the fully qualified name corresponding to an object literal key, + * as long as it and its prefix property names are valid JavaScript + * identifiers. The object literal may be nested inside of other object + * literals. + * + * For example, if called with node {@code n} representing "z" in any of + * the following expressions, the result would be "w.x.y.z": + * var w = {x: {y: {z: 0}}}; + * w.x = {y: {z: 0}}; + * w.x.y = {'a': 0, 'z': 0}; + * + * @param n A child of an OBJLIT node + * @return The global name, or null if {@code n} doesn't correspond to the + * key of an object literal that can be named + */ + String getNameForObjLitKey(Node n) { + Node parent = n.getParent(); + Preconditions.checkState(parent.isObjectLit()); + + Node gramps = parent.getParent(); + if (gramps == null) { + return null; + } + + Node greatGramps = gramps.getParent(); + String name; + switch (gramps.getType()) { + case Token.NAME: + // VAR + // NAME (gramps) + // OBJLIT (parent) + // STRING (n) + if (greatGramps == null || !greatGramps.isVar()) { + return null; + } + name = gramps.getString(); + break; + case Token.ASSIGN: + // ASSIGN (gramps) + // NAME|GETPROP + // OBJLIT (parent) + // STRING (n) + Node lvalue = gramps.getFirstChild(); + name = lvalue.getQualifiedName(); + break; + case Token.STRING_KEY: + // OBJLIT + // STRING (gramps) + // OBJLIT (parent) + // STRING (n) + if (greatGramps != null && + greatGramps.isObjectLit()) { + name = getNameForObjLitKey(gramps); + } else { + return null; + } + break; + default: + return null; + } + if (name != null) { + String key = n.getString(); + if (TokenStream.isJSIdentifier(key)) { + return name + '.' + key; + } + } + return null; + } + + /** + * Gets the type of a value or simple expression. + * + * @param n An r-value in an assignment or variable declaration (not null) + * @return A {@link Name.Type} + */ + Name.Type getValueType(Node n) { + switch (n.getType()) { + case Token.OBJECTLIT: + return Name.Type.OBJECTLIT; + case Token.FUNCTION: + return Name.Type.FUNCTION; + case Token.OR: + // Recurse on the second value. If the first value were an object + // literal or function, then the OR would be meaningless and the + // second value would be dead code. Assume that if the second value + // is an object literal or function, then the first value will also + // evaluate to one when it doesn't evaluate to false. + return getValueType(n.getLastChild()); + case Token.HOOK: + // The same line of reasoning used for the OR case applies here. + Node second = n.getFirstChild().getNext(); + Name.Type t = getValueType(second); + if (t != Name.Type.OTHER) return t; + Node third = second.getNext(); + return getValueType(third); + } + return Name.Type.OTHER; + } + + /** + * Updates our representation of the global namespace to reflect an + * assignment to a global name in global scope. + * + * @param t The traversal + * @param n The node currently being visited + * @param parent {@code n}'s parent + * @param name The global name (e.g. "a" or "a.b.c.d") + * @param isPropAssign Whether this set corresponds to a property + * assignment of the form a.b.c = ...; + * @param type The type of the value that the name is being assigned + */ + void handleSetFromGlobal(NodeTraversal t, Node n, Node parent, String name, + boolean isPropAssign, Name.Type type) { + if (maybeHandlePrototypePrefix(t, n, parent, name)) return; + + Name nameObj = getOrCreateName(name); + nameObj.type = type; + + Ref set = new Ref(t, n, nameObj, Ref.Type.SET_FROM_GLOBAL, + currentPreOrderIndex++); + nameObj.addRef(set); + + if (isNestedAssign(parent)) { + // This assignment is both a set and a get that creates an alias. + Ref get = new Ref(t, n, nameObj, Ref.Type.ALIASING_GET, + currentPreOrderIndex++); + nameObj.addRef(get); + Ref.markTwins(set, get); + } else if (isTypeDeclaration(n, parent)) { + // Names with a @constructor or @enum annotation are always collapsed + nameObj.setDeclaredType(); + } + } + + /** + * Determines whether a set operation is a constructor or enumeration + * or interface declaration. The set operation may either be an assignment + * to a name, a variable declaration, or an object literal key mapping. + * + * @param n The node that represents the name being set + * @param parent Parent node of {@code n} (an ASSIGN, VAR, or OBJLIT node) + * @return Whether the set operation is either a constructor or enum + * declaration + */ + private boolean isTypeDeclaration(Node n, Node parent) { + Node valueNode = NodeUtil.getRValueOfLValue(n); + JSDocInfo info = NodeUtil.getBestJSDocInfo(n); + // Heed the annotations only if they're sensibly used. + return info != null && valueNode != null && + (info.isConstructor() && valueNode.isFunction() || + info.isInterface() && valueNode.isFunction() || + info.hasEnumParameterType() && valueNode.isObjectLit()); + } + + /** + * Updates our representation of the global namespace to reflect an + * assignment to a global name in a local scope. + * + * @param t The traversal + * @param n The node currently being visited + * @param parent {@code n}'s parent + * @param name The global name (e.g. "a" or "a.b.c.d") + */ + void handleSetFromLocal(NodeTraversal t, Node n, Node parent, + String name) { + if (maybeHandlePrototypePrefix(t, n, parent, name)) return; + + Name nameObj = getOrCreateName(name); + Ref set = new Ref(t, n, nameObj, + Ref.Type.SET_FROM_LOCAL, currentPreOrderIndex++); + nameObj.addRef(set); + + if (isNestedAssign(parent)) { + // This assignment is both a set and a get that creates an alias. + Ref get = new Ref(t, n, nameObj, + Ref.Type.ALIASING_GET, currentPreOrderIndex++); + nameObj.addRef(get); + Ref.markTwins(set, get); + } + } + + /** + * Updates our representation of the global namespace to reflect a read + * of a global name. + * + * @param t The traversal + * @param n The node currently being visited + * @param parent {@code n}'s parent + * @param name The global name (e.g. "a" or "a.b.c.d") + */ + void handleGet(NodeTraversal t, Node n, Node parent, String name) { + if (maybeHandlePrototypePrefix(t, n, parent, name)) return; + + Ref.Type type = Ref.Type.DIRECT_GET; + if (parent != null) { + switch (parent.getType()) { + case Token.IF: + case Token.TYPEOF: + case Token.VOID: + case Token.NOT: + case Token.BITNOT: + case Token.POS: + case Token.NEG: + break; + case Token.CALL: + type = n == parent.getFirstChild() + ? Ref.Type.CALL_GET + : Ref.Type.ALIASING_GET; + break; + case Token.NEW: + type = n == parent.getFirstChild() + ? Ref.Type.DIRECT_GET + : Ref.Type.ALIASING_GET; + break; + case Token.OR: + case Token.AND: + // This node is x or y in (x||y) or (x&&y). We only know that an + // alias is not getting created for this name if the result is used + // in a boolean context or assigned to the same name + // (e.g. var a = a || {}). + type = determineGetTypeForHookOrBooleanExpr(t, parent, name); + break; + case Token.HOOK: + if (n != parent.getFirstChild()) { + // This node is y or z in (x?y:z). We only know that an alias is + // not getting created for this name if the result is assigned to + // the same name (e.g. var a = a ? a : {}). + type = determineGetTypeForHookOrBooleanExpr(t, parent, name); + } + break; + case Token.DELPROP: + type = Ref.Type.DELETE_PROP; + break; + default: + type = Ref.Type.ALIASING_GET; + break; + } + } + + handleGet(t, n, parent, name, type); + } + + /** + * Determines whether the result of a hook (x?y:z) or boolean expression + * (x||y) or (x&&y) is assigned to a specific global name. + * + * @param t The traversal + * @param parent The parent of the current node in the traversal. This node + * should already be known to be a HOOK, AND, or OR node. + * @param name A name that is already known to be global in the current + * scope (e.g. "a" or "a.b.c.d") + * @return The expression's get type, either {@link Ref.Type#DIRECT_GET} or + * {@link Ref.Type#ALIASING_GET} + */ + Ref.Type determineGetTypeForHookOrBooleanExpr( + NodeTraversal t, Node parent, String name) { + Node prev = parent; + for (Node anc : parent.getAncestors()) { + switch (anc.getType()) { + case Token.EXPR_RESULT: + case Token.VAR: + case Token.IF: + case Token.WHILE: + case Token.FOR: + case Token.TYPEOF: + case Token.VOID: + case Token.NOT: + case Token.BITNOT: + case Token.POS: + case Token.NEG: + return Ref.Type.DIRECT_GET; + case Token.HOOK: + if (anc.getFirstChild() == prev) { + return Ref.Type.DIRECT_GET; + } + break; + case Token.ASSIGN: + if (!name.equals(anc.getFirstChild().getQualifiedName())) { + return Ref.Type.ALIASING_GET; + } + break; + case Token.NAME: // a variable declaration + if (!name.equals(anc.getString())) { + return Ref.Type.ALIASING_GET; + } + break; + case Token.CALL: + if (anc.getFirstChild() != prev) { + return Ref.Type.ALIASING_GET; + } + break; + case Token.DELPROP: + return Ref.Type.DELETE_PROP; + } + prev = anc; + } + return Ref.Type.ALIASING_GET; + } + + /** + * Updates our representation of the global namespace to reflect a read + * of a global name. + * + * @param t The current node traversal + * @param n The node currently being visited + * @param parent {@code n}'s parent + * @param name The global name (e.g. "a" or "a.b.c.d") + * @param type The reference type + */ + void handleGet(NodeTraversal t, Node n, Node parent, + String name, Ref.Type type) { + Name nameObj = getOrCreateName(name); + + // No need to look up additional ancestors, since they won't be used. + nameObj.addRef(new Ref(t, n, nameObj, type, currentPreOrderIndex++)); + } + + /** + * Updates our representation of the global namespace to reflect a read + * of a global name's longest prefix before the "prototype" property if the + * name includes the "prototype" property. Does nothing otherwise. + * + * @param t The current node traversal + * @param n The node currently being visited + * @param parent {@code n}'s parent + * @param name The global name (e.g. "a" or "a.b.c.d") + * @return Whether the name was handled + */ + boolean maybeHandlePrototypePrefix(NodeTraversal t, Node n, Node parent, + String name) { + // We use a string-based approach instead of inspecting the parse tree + // to avoid complexities with object literals, possibly nested, beneath + // assignments. + + int numLevelsToRemove; + String prefix; + if (name.endsWith(".prototype")) { + numLevelsToRemove = 1; + prefix = name.substring(0, name.length() - 10); + } else { + int i = name.indexOf(".prototype."); + if (i == -1) { + return false; + } + prefix = name.substring(0, i); + numLevelsToRemove = 2; + i = name.indexOf('.', i + 11); + while (i >= 0) { + numLevelsToRemove++; + i = name.indexOf('.', i + 1); + } + } + + if (parent != null && NodeUtil.isObjectLitKey(n, parent)) { + // Object literal keys have no prefix that's referenced directly per + // key, so we're done. + return true; + } + + for (int i = 0; i < numLevelsToRemove; i++) { + parent = n; + n = n.getFirstChild(); + } + + handleGet(t, n, parent, prefix, Ref.Type.PROTOTYPE_GET); + return true; + } + + /** + * Determines whether an assignment is nested (i.e. whether its return + * value is used). + * + * @param parent The parent of the current traversal node (not null) + * @return Whether it appears that the return value of the assignment is + * used + */ + boolean isNestedAssign(Node parent) { + return parent.isAssign() && + !parent.getParent().isExprResult(); + } + + /** + * Gets a {@link Name} instance for a global name. Creates it if necessary, + * as well as instances for any of its prefixes that are not yet defined. + * + * @param name A global name (e.g. "a", "a.b.c.d") + * @return The {@link Name} instance for {@code name} + */ + Name getOrCreateName(String name) { + Name node = nameMap.get(name); + if (node == null) { + int i = name.lastIndexOf('.'); + if (i >= 0) { + String parentName = name.substring(0, i); + Name parent = getOrCreateName(parentName); + node = parent.addProperty(name.substring(i + 1), inExterns); + } else { + node = new Name(name, null, inExterns); + globalNames.add(node); + } + nameMap.put(name, node); + } + return node; + } + } + + // ------------------------------------------------------------------------- + + /** + * A name defined in global scope (e.g. "a" or "a.b.c.d"). These form a tree. + * As the parse tree traversal proceeds, we'll discover that some names + * correspond to JavaScript objects whose properties we should consider + * collapsing. + */ + static class Name implements StaticSlot { + enum Type { + OBJECTLIT, + FUNCTION, + GET, + SET, + OTHER, + } + + private final String baseName; + final Name parent; + List props; + + /** The first global assignment to a name. */ + private Ref declaration; + + /** All references to a name. This must contain {@code declaration}. */ + private List refs; + + Type type; + private boolean declaredType = false; + private boolean hasDeclaredTypeDescendant = false; + int globalSets = 0; + int localSets = 0; + int aliasingGets = 0; + int totalGets = 0; + int callGets = 0; + int deleteProps = 0; + final boolean inExterns; + + JSDocInfo docInfo = null; + + Name(String name, Name parent, boolean inExterns) { + this.baseName = name; + this.parent = parent; + this.type = Type.OTHER; + this.inExterns = inExterns; + } + + Name addProperty(String name, boolean inExterns) { + if (props == null) { + props = new ArrayList(); + } + Name node = new Name(name, this, inExterns); + props.add(node); + return node; + } + + String getBaseName() { + return baseName; + } + + @Override + public String getName() { + return getFullName(); + } + + String getFullName() { + return parent == null ? baseName : parent.getFullName() + '.' + baseName; + } + + @Override + public Ref getDeclaration() { + return declaration; + } + + @Override + public boolean isTypeInferred() { + return false; + } + + @Override + public JSType getType() { + return null; + } + + void addRef(Ref ref) { + addRefInternal(ref); + switch (ref.type) { + case SET_FROM_GLOBAL: + if (declaration == null) { + declaration = ref; + docInfo = getDocInfoForDeclaration(ref); + } + globalSets++; + break; + case SET_FROM_LOCAL: + localSets++; + break; + case PROTOTYPE_GET: + case DIRECT_GET: + totalGets++; + break; + case ALIASING_GET: + aliasingGets++; + totalGets++; + break; + case CALL_GET: + callGets++; + totalGets++; + break; + case DELETE_PROP: + deleteProps++; + break; + default: + throw new IllegalStateException(); + } + } + + void removeRef(Ref ref) { + if (refs != null && refs.remove(ref)) { + if (ref == declaration) { + declaration = null; + if (refs != null) { + for (Ref maybeNewDecl : refs) { + if (maybeNewDecl.type == Ref.Type.SET_FROM_GLOBAL) { + declaration = maybeNewDecl; + break; + } + } + } + } + + switch (ref.type) { + case SET_FROM_GLOBAL: + globalSets--; + break; + case SET_FROM_LOCAL: + localSets--; + break; + case PROTOTYPE_GET: + case DIRECT_GET: + totalGets--; + break; + case ALIASING_GET: + aliasingGets--; + totalGets--; + break; + case CALL_GET: + callGets--; + totalGets--; + break; + case DELETE_PROP: + deleteProps--; + break; + default: + throw new IllegalStateException(); + } + } + } + + List getRefs() { + return refs == null ? ImmutableList.of() : refs; + } + + void addRefInternal(Ref ref) { + if (refs == null) { + refs = Lists.newArrayList(); + } + refs.add(ref); + } + + boolean canEliminate() { + if (!canCollapseUnannotatedChildNames() || totalGets > 0) { + return false; + } + + if (props != null) { + for (Name n : props) { + if (!n.canCollapse()) { + return false; + } + } + } + return true; + } + + boolean isSimpleStubDeclaration() { + if (getRefs().size() == 1) { + Ref ref = refs.get(0); + JSDocInfo info = ref.node.getJSDocInfo(); + if (ref.node.getParent() != null && + ref.node.getParent().isExprResult()) { + return true; + } + } + return false; + } + + boolean canCollapse() { + return !inExterns && !isGetOrSetDefinition() && (declaredType || + (parent == null || parent.canCollapseUnannotatedChildNames()) && + (globalSets > 0 || localSets > 0) && + deleteProps == 0); + } + + boolean isGetOrSetDefinition() { + return this.type == Type.GET || this.type == Type.SET; + } + + boolean canCollapseUnannotatedChildNames() { + if (type == Type.OTHER || isGetOrSetDefinition() + || globalSets != 1 || localSets != 0 || deleteProps != 0) { + return false; + } + + // Don't try to collapse if the one global set is a twin reference. + // We could theoretically handle this case in CollapseProperties, but + // it's probably not worth the effort. + Preconditions.checkNotNull(declaration); + if (declaration.getTwin() != null) { + return false; + } + + if (declaredType) { + return true; + } + + // If this is a key of an aliased object literal, then it will be aliased + // later. So we won't be able to collapse its properties. + if (parent != null && parent.shouldKeepKeys()) { + return false; + } + + // If this is aliased, then its properties can't be collapsed either. + if (aliasingGets > 0) { + return false; + } + + return (parent == null || parent.canCollapseUnannotatedChildNames()); + } + + /** Whether this is an object literal that needs to keep its keys. */ + boolean shouldKeepKeys() { + return type == Type.OBJECTLIT && aliasingGets > 0; + } + + boolean needsToBeStubbed() { + return globalSets == 0 && localSets > 0; + } + + void setDeclaredType() { + declaredType = true; + for (Name ancestor = parent; ancestor != null; + ancestor = ancestor.parent) { + ancestor.hasDeclaredTypeDescendant = true; + } + } + + boolean isDeclaredType() { + return declaredType; + } + + /** + * Determines whether this name is a prefix of at least one class or enum + * name. Because classes and enums are always collapsed, the namespace will + * have different properties in compiled code than in uncompiled code. + * + * For example, if foo.bar.DomHelper is a class, then foo and foo.bar are + * considered namespaces. + */ + boolean isNamespace() { + return hasDeclaredTypeDescendant && type == Type.OBJECTLIT; + } + + /** + * Determines whether this is a simple name (as opposed to a qualified + * name). + */ + boolean isSimpleName() { + return parent == null; + } + + @Override public String toString() { + return getFullName() + " (" + type + "): globalSets=" + globalSets + + ", localSets=" + localSets + ", totalGets=" + totalGets + + ", aliasingGets=" + aliasingGets + ", callGets=" + callGets; + } + + @Override + public JSDocInfo getJSDocInfo() { + return docInfo; + } + + /** + * Tries to get the doc info for a given declaration ref. + */ + private static JSDocInfo getDocInfoForDeclaration(Ref ref) { + if (ref.node != null) { + Node refParent = ref.node.getParent(); + switch (refParent.getType()) { + case Token.FUNCTION: + case Token.ASSIGN: + return refParent.getJSDocInfo(); + case Token.VAR: + return ref.node == refParent.getFirstChild() ? + refParent.getJSDocInfo() : ref.node.getJSDocInfo(); + } + } + + return null; + } + } + + // ------------------------------------------------------------------------- + + /** + * A global name reference. Contains references to the relevant parse tree + * node and its ancestors that may be affected. + */ + static class Ref implements StaticReference { + enum Type { + SET_FROM_GLOBAL, + SET_FROM_LOCAL, + PROTOTYPE_GET, + ALIASING_GET, // Prevents a name's properties from being collapsed + DIRECT_GET, // Prevents a name from being completely eliminated + CALL_GET, // Prevents a name from being collapsed if never set + DELETE_PROP, // Prevents a name from being collapsed at all. + } + + Node node; + final JSModule module; + final StaticSourceFile source; + final Name name; + final Type type; + final Scope scope; + final int preOrderIndex; + + /** + * Certain types of references are actually double-refs. For example, + * var a = b = 0; + * counts as both a "set" of b and an "alias" of b. + * + * We create two Refs for this node, and mark them as twins of each other. + */ + private Ref twin = null; + + /** + * Creates a reference at the current node. + */ + Ref(NodeTraversal t, Node node, Name name, Type type, int index) { + this.node = node; + this.name = name; + this.module = t.getInput() == null ? null : t.getInput().getModule(); + this.source = node.getStaticSourceFile(); + this.type = type; + this.scope = t.getScope(); + this.preOrderIndex = index; + } + + private Ref(Ref original, Type type, int index) { + this.node = original.node; + this.name = original.name; + this.module = original.module; + this.source = original.source; + this.type = type; + this.scope = original.scope; + this.preOrderIndex = index; + } + + private Ref(Type type, int index) { + this.type = type; + this.module = null; + this.source = null; + this.scope = null; + this.name = null; + this.preOrderIndex = index; + } + + @Override + public Node getNode() { + return node; + } + + @Override + public StaticSourceFile getSourceFile() { + return source; + } + + @Override + public StaticSlot getSymbol() { + return name; + } + + JSModule getModule() { + return module; + } + + String getSourceName() { + return source == null ? "" : source.getName(); + } + + Ref getTwin() { + return twin; + } + + boolean isSet() { + return type == Type.SET_FROM_GLOBAL || type == Type.SET_FROM_LOCAL; + } + + static void markTwins(Ref a, Ref b) { + Preconditions.checkArgument( + (a.type == Type.ALIASING_GET || b.type == Type.ALIASING_GET) && + (a.type == Type.SET_FROM_GLOBAL || a.type == Type.SET_FROM_LOCAL || + b.type == Type.SET_FROM_GLOBAL || b.type == Type.SET_FROM_LOCAL)); + a.twin = b; + b.twin = a; + } + + /** + * Create a new ref that is the same as this one, but of + * a different class. + */ + Ref cloneAndReclassify(Type type) { + return new Ref(this, type, this.preOrderIndex); + } + + static Ref createRefForTesting(Type type) { + return new Ref(type, -1); + } + } + + + /** + * An experimental compiler pass for tracking what symbols were added/removed + * at each stage of compilation. + * + * When "global namespace tracker" mode is on, we rebuild the global namespace + * after each pass, and diff it against the last namespace built. + */ + static class Tracker implements CompilerPass { + private final AbstractCompiler compiler; + private final PrintStream stream; + private final Predicate isInterestingSymbol; + + private Set previousSymbolsInTree = ImmutableSet.of(); + + /** + @param stream The stream to print logs to. + * @param isInterestingSymbol A predicate to determine which symbols + * we care about. + */ + Tracker(AbstractCompiler compiler, PrintStream stream, + Predicate isInterestingSymbol) { + this.compiler = compiler; + this.stream = stream; + this.isInterestingSymbol = isInterestingSymbol; + } + + @Override public void process(Node externs, Node root) { + GlobalNamespace namespace = new GlobalNamespace(compiler, externs, root); + + Set currentSymbols = Sets.newTreeSet(); + for (String name : namespace.getNameIndex().keySet()) { + if (isInterestingSymbol.apply(name)) { + currentSymbols.add(name); + } + } + + String passName = compiler.getLastPassName(); + if (passName == null) { + passName = "[Unknown pass]"; + } + + for (String sym : currentSymbols) { + if (!previousSymbolsInTree.contains(sym)) { + stream.println(String.format("%s: Added by %s", sym, passName)); + } + } + + for (String sym : previousSymbolsInTree) { + if (!currentSymbols.contains(sym)) { + stream.println(String.format("%s: Removed by %s", sym, passName)); + } + } + + previousSymbolsInTree = currentSymbols; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GlobalVarReferenceMap.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GlobalVarReferenceMap.java new file mode 100644 index 0000000..9b7870d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GlobalVarReferenceMap.java @@ -0,0 +1,260 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.ReferenceCollectingCallback.Reference; +import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; +import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceMap; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.Node; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * An implementation for {@code ReferenceMap} that is specific to global scope + * and can be used in different passes. In other words instead of relying on + * Var object it relies on the name of the variable. It also supports hot-swap + * update of reference map for a specific script. + * + * @see ReferenceCollectingCallback#exitScope(NodeTraversal) + * + * @author bashir@google.com (Bashir Sadjad) + */ +class GlobalVarReferenceMap implements ReferenceMap { + + private Map refMap = null; + + private final Map inputOrder; + + /** + * @param inputs The ordered list of all inputs for the compiler. + */ + GlobalVarReferenceMap(List inputs, List externs) { + inputOrder = Maps.newHashMap(); + int ind = 0; + for (CompilerInput extern : externs) { + inputOrder.put(extern.getInputId(), ind); + ind++; + } + for (CompilerInput input : inputs) { + inputOrder.put(input.getInputId(), ind); + ind++; + } + } + + @Override + public ReferenceCollection getReferences(Var var) { + if (!var.isGlobal()) { + return null; + } + return refMap.get(var.getName()); + } + + /** + * Resets global var reference map with the new provide map. + * + * @param globalRefMap The reference map result of a + * {@link ReferenceCollectingCallback} pass collected from the whole AST. + */ + private void resetGlobalVarReferences( + Map globalRefMap) { + refMap = Maps.newHashMap(); + for (Entry entry : globalRefMap.entrySet()) { + Var var = entry.getKey(); + if (var.isGlobal()) { + refMap.put(var.getName(), entry.getValue()); + } + } + } + + /** + * Updates the internal reference map based on the provided parameters. If + * {@code scriptRoot} is not SCRIPT, it basically replaces the internal map + * with the new one, otherwise it replaces all the information associated to + * the given script. + * + * @param refMapPatch The reference map result of a + * {@link ReferenceCollectingCallback} pass which might be collected from + * the whole AST or just a sub-tree associated to a SCRIPT node. + * @param root AST sub-tree root on which reference collection was done. + */ + void updateGlobalVarReferences(Map + refMapPatch, Node root) { + if (refMap == null || !root.isScript()) { + resetGlobalVarReferences(refMapPatch); + return; + } + + InputId inputId = root.getInputId(); + Preconditions.checkNotNull(inputId); + // Note there are two assumptions here (i) the order of compiler inputs + // has not changed and (ii) all references are in the order they appear + // in AST (this is enforced in ReferenceCollectionCallback). + removeScriptReferences(inputId); + for (Entry entry : refMapPatch.entrySet()) { + Var var = entry.getKey(); + if (var.isGlobal()) { + replaceReferences(var.getName(), inputId, entry.getValue()); + } + } + } + + private void removeScriptReferences(InputId inputId) { + Preconditions.checkNotNull(inputId); + + if (!inputOrder.containsKey(inputId)) { + return; // Input did not exist when last computed, so skip + } + // TODO(bashir): If this is too slow it is not too difficult to make it + // faster with keeping an index for variables accessed in sourceName. + for (ReferenceCollection collection : refMap.values()) { + if (collection == null) { + continue; + } + List oldRefs = collection.references; + SourceRefRange range = findSourceRefRange(oldRefs, inputId); + List newRefs = Lists.newArrayList(range.refsBefore()); + newRefs.addAll(range.refsAfter()); + collection.references = newRefs; + } + } + + private void replaceReferences(String varName, InputId inputId, + ReferenceCollection newSourceCollection) { + ReferenceCollection combined = new ReferenceCollection(); + List combinedRefs = combined.references; + ReferenceCollection oldCollection = refMap.get(varName); + refMap.put(varName, combined); + if (oldCollection == null) { + combinedRefs.addAll(newSourceCollection.references); + return; + } + // otherwise replace previous references that are from sourceName + SourceRefRange range = findSourceRefRange(oldCollection.references, + inputId); + combinedRefs.addAll(range.refsBefore()); + combinedRefs.addAll(newSourceCollection.references); + combinedRefs.addAll(range.refsAfter()); + } + + /** + * Finds the range of references associated to {@code sourceName}. Note that + * even if there is no sourceName references the returned information can be + * used to decide where to insert new sourceName refs. + */ + private SourceRefRange findSourceRefRange(List refList, + InputId inputId) { + Preconditions.checkNotNull(inputId); + + // TODO(bashir): We can do binary search here, but since this is fast enough + // right now, we just do a linear search for simplicity. + int lastBefore = -1; + int firstAfter = refList.size(); + int index = 0; + + Preconditions.checkState(inputOrder.containsKey(inputId), inputId.getIdName()); + int sourceInputOrder = inputOrder.get(inputId); + for (Reference ref : refList) { + Preconditions.checkNotNull(ref.getInputId()); + int order = inputOrder.get(ref.getInputId()); + if (order < sourceInputOrder) { + lastBefore = index; + } else if (order > sourceInputOrder) { + firstAfter = index; + break; + } + index++; + } + return new SourceRefRange(refList, lastBefore, firstAfter); + } + + private static class SourceRefRange { + private final int lastBefore; + private final int firstAfter; + private final List refList; + + SourceRefRange(List refList, int lastBefore, + int firstAfter) { + this.lastBefore = Math.max(lastBefore, -1); + this.firstAfter = Math.min(firstAfter, refList.size()); + this.refList = refList; + } + + /** Note that the returned list is backed by {@code refList}! */ + List refsBefore() { + return refList.subList(0, lastBefore + 1); + } + + /** Note that the returned list is backed by {@code refList}! */ + List refsAfter() { + return refList.subList(firstAfter, refList.size()); + } + } + + /** + * @param globalScope a new Global Scope to replace the scope of references + * with. + */ + public void updateReferencesWithGlobalScope(Scope globalScope) { + for (ReferenceCollection collection : refMap.values()) { + List newRefs = + Lists.newArrayListWithCapacity(collection.references.size()); + for (Reference ref : collection.references) { + if (ref.getScope() != globalScope) { + newRefs.add(ref.cloneWithNewScope(globalScope)); + } else { + newRefs.add(ref); + } + } + collection.references = newRefs; + } + } + + /** + * A CleanupPass implementation that will replace references to old Syntactic + * Global Scopes generated in previous compile runs with references to the + * Global Typed Scope. + * + * @author tylerg@google.com (Tyler Goodwin) + */ + static class GlobalVarRefCleanupPass implements HotSwapCompilerPass { + + private final AbstractCompiler compiler; + + public GlobalVarRefCleanupPass(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + GlobalVarReferenceMap refMap = compiler.getGlobalVarReferences(); + if (refMap != null) { + refMap.updateReferencesWithGlobalScope(compiler.getTopScope()); + } + } + + @Override + public void process(Node externs, Node root) { + // GlobalVarRefCleanupPass should not do work during process. + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GoogleCodingConvention.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GoogleCodingConvention.java new file mode 100644 index 0000000..ca8c04f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GoogleCodingConvention.java @@ -0,0 +1,155 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + + + + +import com.google.javascript.rhino.Node; + +import java.util.regex.Pattern; + +/** + * This describes the Google-specific JavaScript coding conventions. + * Within Google, variable names are semantically significant. + * + */ +public class GoogleCodingConvention extends CodingConventions.Proxy { + + private static final long serialVersionUID = 1L; + + private static final String OPTIONAL_ARG_PREFIX = "opt_"; + + private static final String VAR_ARGS_NAME = "var_args"; + + private static final Pattern ENUM_KEY_PATTERN = + Pattern.compile("[A-Z0-9][A-Z0-9_]*"); + + /** By default, decorate the ClosureCodingConvention. */ + public GoogleCodingConvention() { + this(new ClosureCodingConvention()); + } + + /** Decorates a wrapped CodingConvention. */ + public GoogleCodingConvention(CodingConvention convention) { + super(convention); + } + + /** + * {@inheritDoc} + * + *

      This enforces the Google const name convention, that the first character + * after the last $ must be an upper-case letter and all subsequent letters + * must be upper case. The name must be at least 2 characters long. + * + *

      Examples: + *

      +   *      aaa          Not constant - lower-case letters in the name
      +   *      A            Not constant - too short
      +   *      goog$A       Constant - letters after the $ are upper-case.
      +   *      AA17         Constant - digits can appear after the first letter
      +   *      goog$7A      Not constant - first character after the $ must be
      +   *                   upper case.
      +   *      $A           Constant - doesn't have to be anything in front of the $
      +   * 
      + */ + @Override + public boolean isConstant(String name) { + if (name.length() <= 1) { + return false; + } + + // In compiled code, '$' is often a namespace delimiter. To allow inlining + // of namespaced constants, we strip off any namespaces here. + int pos = name.lastIndexOf('$'); + if (pos >= 0) { + name = name.substring(pos + 1); + if (name.length() == 0) { + return false; + } + } + + return isConstantKey(name); + } + + @Override + public boolean isConstantKey(String name) { + if (name.isEmpty() || !Character.isUpperCase(name.charAt(0))) { + return false; + } + + // hack way of checking that there aren't any lower-case letters + return name.toUpperCase().equals(name); + } + + /** + * {@inheritDoc} + * + *

      This enforces Google's convention about enum key names. They must match + * the regular expression {@code [A-Z0-9][A-Z0-9_]*}. + * + *

      Examples: + *

        + *
      • A
      • + *
      • 213
      • + *
      • FOO_BAR
      • + *
      + */ + @Override + public boolean isValidEnumKey(String key) { + return ENUM_KEY_PATTERN.matcher(key).matches(); + } + + /** + * {@inheritDoc} + * + *

      In Google code, parameter names beginning with {@code opt_} are + * treated as optional arguments. + */ + @Override + public boolean isOptionalParameter(Node parameter) { + return parameter.getString().startsWith(OPTIONAL_ARG_PREFIX); + } + + @Override + public boolean isVarArgsParameter(Node parameter) { + return VAR_ARGS_NAME.equals(parameter.getString()); + } + + /** + * {@inheritDoc} + * + *

      In Google code, any global name starting with an underscore is + * considered exported. + */ + @Override + public boolean isExported(String name, boolean local) { + return super.isExported(name, local) || + (!local && name.startsWith("_")); + } + + /** + * {@inheritDoc} + * + *

      In Google code, private names end with an underscore, and exported + * names are never considered private (see {@link #isExported}). + */ + @Override + public boolean isPrivate(String name) { + return name.endsWith("_") && !isExported(name); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java new file mode 100644 index 0000000..a36adac --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java @@ -0,0 +1,237 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.CaseFormat; +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.JsMessage.IdGenerator; +import com.google.javascript.jscomp.JsMessage.PlaceholderReference; + +import java.util.List; + +/** + * An {@link IdGenerator} designed to play nicely with Google's Translation + * systems. Each message is scoped to a project id, so that it does + * not conflict with other messages at Google. + *

      + * Just as reminder what key type used in different formats: + *

        + *
      1. XMB - id. We export using this format. + *
      2. XTB - id. Internal, result of translation. + *
      3. XLB - name. External, use it if we need to share translation with third + * part. + *
      4. PROPERTIES - name. + *
      + * + * @see xmb + */ +public class GoogleJsMessageIdGenerator implements IdGenerator { + + private final String projectId; + + /** + * Creates an instance. + * + * @param projectId A TC project name (e.g. "MyProject") + */ + public GoogleJsMessageIdGenerator(String projectId) { + this.projectId = projectId; + } + + @Override + public String generateId(String meaning, List messageParts) { + Preconditions.checkState(meaning != null); + + StringBuilder sb = new StringBuilder(); + for (CharSequence part : messageParts) { + if (part instanceof PlaceholderReference) { + sb.append(CaseFormat.LOWER_CAMEL.to( + CaseFormat.UPPER_UNDERSCORE, + ((PlaceholderReference) part).getName())); + } else { + sb.append(part); + } + } + String tcValue = sb.toString(); + + String projectScopedMeaning = + (projectId != null ? (projectId + ": ") : "") + meaning; + return String.valueOf( + MessageId.GenerateId(tcValue, projectScopedMeaning)); + } + + + /** + * 64-bit fingerprint support. + * + * Forked from the guava-internal library. + */ + private static final class FP { + private FP() {} + + /** Generate fingerprint of "byte[start,limit-1]". */ + private static long fingerprint(byte[] str, int start, int limit) { + int hi = hash32(str, start, limit, 0); + int lo = hash32(str, start, limit, 102072); + if ((hi == 0) && (lo == 0 || lo == 1)) { + // Turn 0/1 into another fingerprint + hi ^= 0x130f9bef; + lo ^= 0x94a0a928; + } + return (((long) hi) << 32) | (lo & 0xffffffffl); + } + + /** + * Generate fingerprint of "str". Equivalent to UTF-encoding "str" into + * bytes and then fingerprinting those bytes. + */ + private static long fingerprint(String str) { + byte[] tmp = str.getBytes(Charsets.UTF_8); + return FP.fingerprint(tmp, 0, tmp.length); + } + + @SuppressWarnings("fallthrough") + private static int hash32(byte[] str, int start, int limit, int c) { + int a = 0x9e3779b9; + int b = 0x9e3779b9; + int i; + for (i = start; i + 12 <= limit; i += 12) { + a += (((str[i + 0] & 0xff) << 0) + | ((str[i + 1] & 0xff) << 8) + | ((str[i + 2] & 0xff) << 16) + | ((str[i + 3] & 0xff) << 24)); + b += (((str[i + 4] & 0xff) << 0) + | ((str[i + 5] & 0xff) << 8) + | ((str[i + 6] & 0xff) << 16) + | ((str[i + 7] & 0xff) << 24)); + c += (((str[i + 8] & 0xff) << 0) + | ((str[i + 9] & 0xff) << 8) | ((str[i + 10] & 0xff) << 16) + | ((str[i + 11] & 0xff) << 24)); + + // Mix + a -= b; + a -= c; + a ^= (c >>> 13); + b -= c; + b -= a; + b ^= (a << 8); + c -= a; + c -= b; + c ^= (b >>> 13); + a -= b; + a -= c; + a ^= (c >>> 12); + b -= c; + b -= a; + b ^= (a << 16); + c -= a; + c -= b; + c ^= (b >>> 5); + a -= b; + a -= c; + a ^= (c >>> 3); + b -= c; + b -= a; + b ^= (a << 10); + c -= a; + c -= b; + c ^= (b >>> 15); + } + + c += limit - start; + switch (limit - i) { // deal with rest. Cases fall through + case 11: + c += (str[i + 10] & 0xff) << 24; + case 10: + c += (str[i + 9] & 0xff) << 16; + case 9: + c += (str[i + 8] & 0xff) << 8; + // the first byte of c is reserved for the length + case 8: + b += (str[i + 7] & 0xff) << 24; + case 7: + b += (str[i + 6] & 0xff) << 16; + case 6: + b += (str[i + 5] & 0xff) << 8; + case 5: + b += (str[i + 4] & 0xff); + case 4: + a += (str[i + 3] & 0xff) << 24; + case 3: + a += (str[i + 2] & 0xff) << 16; + case 2: + a += (str[i + 1] & 0xff) << 8; + case 1: + a += (str[i + 0] & 0xff); + // case 0 : nothing left to add + } + + // Mix + a -= b; + a -= c; + a ^= (c >>> 13); + b -= c; + b -= a; + b ^= (a << 8); + c -= a; + c -= b; + c ^= (b >>> 13); + a -= b; + a -= c; + a ^= (c >>> 12); + b -= c; + b -= a; + b ^= (a << 16); + c -= a; + c -= b; + c ^= (b >>> 5); + a -= b; + a -= c; + a ^= (c >>> 3); + b -= c; + b -= a; + b ^= (a << 10); + c -= a; + c -= b; + c ^= (b >>> 15); + return c; + } + } + + /** + * Generates fingerprint for an English message using the FP package. + * This supersedes the message id generation using C fingerprint + * functions and JNI. This is slower than the C implementation ( + * we're talking about microseconds here) but it avoids using JNI and + * shared libraries.

      + * + * Forked from the i18n library. + */ + private static class MessageId { + private final static long GenerateId(String message, String meaning) { + long fp = FP.fingerprint(message); + if (null != meaning && meaning.length() > 0) { + // combine the fingerprints of message and meaning + long fp2 = FP.fingerprint(meaning); + fp = fp2 + (fp << 1) + (fp < 0 ? 1 : 0); + } + // To avoid negative ids we strip the high-order bit + return fp & 0x7fffffffffffffffL; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GroupVariableDeclarations.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GroupVariableDeclarations.java new file mode 100644 index 0000000..19cc616 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/GroupVariableDeclarations.java @@ -0,0 +1,189 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import java.util.Iterator; +import java.util.Set; + +/** + * Groups multiple variable declarations (that may or may not be contiguous) + * in the same scope into a single one. i.e. + * + *

      + * var a, b = 10;
      + * f1();
      + * var c, d;
      + * ... some other code ...
      + * var x, y, z = 100;
      + * ... some other code ...
      + * var p = 200, q = 300;
      + * 
      + * + * becomes: + * + *
      + * var a, b = 10, c, d, x, y, z;
      + * f1();
      + * ... some other code ...
      + * z = 100;
      + * ... some other code ...
      + * var p = 200, q = 300;
      + * 
      + * + * This reduces the generated uncompressed code size. + * + * For any scope, we use the first VAR statement as the statement to collapse + * the other declarations into. For other VAR statements in the scope, we only + * consider ones that either (a) have no variable that is initialized in the + * the statement, or (b) have exactly one variable that is initialized (e.g. + * the "var x, y, z = 100;" statement in the example above. VAR statements + * with more than one variable initialization are not collapsed. This is + * because doing so would increase uncompressed code size. + * + */ +class GroupVariableDeclarations implements CompilerPass, ScopedCallback { + private final AbstractCompiler compiler; + + GroupVariableDeclarations(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void enterScope(NodeTraversal t) { + Set varNodes = Sets.newLinkedHashSet(); + Iterator scopeVarIter = t.getScope().getVars(); + while (scopeVarIter.hasNext()) { + Node parentNode = scopeVarIter.next().getParentNode(); + if (parentNode.isVar()) { + varNodes.add(parentNode); + } + } + if (varNodes.size() <= 1) { + return; + } + Iterator varNodeIter = varNodes.iterator(); + Node firstVarNode = varNodeIter.next(); + while (varNodeIter.hasNext()) { + Node varNode = varNodeIter.next(); + applyGroupingToVar(firstVarNode, varNode); + } + } + + @Override + public void exitScope(NodeTraversal t) { + } + + @Override + public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, + Node parent) { + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + } + + /** + * Attempts to collapse groupVar. This can only happen if groupVar has at most + * one variable initialization (it may have multiple variable declarations). + * If successful, then detaches groupVar's children and appends them to + * firstVar + * + * @param firstVar The first VAR {@code Node} in that scope. This is the node + * that we want to collapse groupVar into + * @param groupVar The VAR {@code Node} that we want to try collapsing + * into the first VAR node of that scope + */ + private void applyGroupingToVar(Node firstVar, Node groupVar) { + Node child = groupVar.getFirstChild(); + // if some variable is initialized, then the corresponding NAME node will be + // stored here + Node initializedName = null; + while (child != null) { + if (child.hasChildren()) { + // check that no more than one var is initialized + if (initializedName != null) { + return; + } + initializedName = child; + } + child = child.getNext(); + } + + // we will be modifying the groupVar subtree so get its parent + Node groupVarParent = groupVar.getParent(); + + + if (initializedName != null) { + if (NodeUtil.isForIn(groupVarParent)) { + // The target of the for-in expression must be an assignable expression. + return; + } + + // we have an initialized var in the VAR node. We will replace the + // VAR node with an assignment. + + // first create a detached childless clone of initializedName. + Node clone = initializedName.cloneNode(); + // replace + groupVar.replaceChild(initializedName, clone); + // add the assignment now. + Node initializedVal = initializedName.removeFirstChild(); + Node assignmentNode = IR.assign(initializedName, initializedVal); + if (groupVarParent.isFor()) { + // Handle For and For-In Loops specially. For these, we do not need + // to construct an EXPR_RESULT node. + groupVarParent.replaceChild(groupVar, assignmentNode); + } else { + Node exprNode = NodeUtil.newExpr(assignmentNode); + groupVarParent.replaceChild(groupVar, exprNode); + } + } else { + // There is no initialized var. But we need to handle FOR and + // FOR-IN loops specially + if (groupVarParent.isFor()) { + if (NodeUtil.isForIn(groupVarParent)) { + // In For-In loop, we replace the VAR node with a NAME node + Node nameNodeClone = groupVar.getFirstChild().cloneNode(); + groupVarParent.replaceChild(groupVar, nameNodeClone); + } else { + // In For loop, we replace the VAR node with an EMPTY node + Node emptyNode = IR.empty(); + groupVarParent.replaceChild(groupVar, emptyNode); + } + } else { + // we can safely remove the VAR node + groupVarParent.removeChild(groupVar); + } + } + + Node children = groupVar.removeChildren(); + firstVar.addChildrenToBack(children); + + compiler.reportCodeChange(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/HotSwapCompilerPass.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/HotSwapCompilerPass.java new file mode 100644 index 0000000..dfacca4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/HotSwapCompilerPass.java @@ -0,0 +1,47 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + * Interface for compiler passes that can be used in a hot-swap fashion. + *

      + * The additional method is {@code hotSwapScript} which runs this pass on a + * subtree of the AST. Each pass that is intended to support hot-swap style + * should implement this interface. + *

      + * It is assumed that {@code Node} argument of {@code hotSwapScript} is the root + * of a sub-tree in AST that represents a JS file and so is of type {@code + * Token.SCRIPT}. + * + * @author bashir@google.com (Bashir Sadjad) + */ +public interface HotSwapCompilerPass extends CompilerPass { + + /** + * Process the JS with root node root. This is supposed to be significantly + * faster compared to corresponding full-compiler passes. + * + * @param scriptRoot Root node corresponding to the file that is modified, + * should be of type {@code Token.SCRIPT}. + * @param originalRoot Root node corresponding to the original version of the + * file that is modified. Should be of type {@code token.SCRIPT}. + */ + void hotSwapScript(Node scriptRoot, Node originalRoot); + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/IgnoreCajaProperties.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/IgnoreCajaProperties.java new file mode 100644 index 0000000..44e69a7 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/IgnoreCajaProperties.java @@ -0,0 +1,119 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; + +/** + * Caja is a system that rewrites web content (JavaScript, CSS, HTML) + * into web content that is safe to inline directly into a page. + * The rewritten ("cajoled") code runs in the presence of a JS library + * that adds some properties to Object.prototype. Because JS does not + * yet (until ES5) allow programmers to mark properties as DontEnum, + * for..in loops will see unexpected properties. + * + * This pass adds a conditional to for..in loops that filters out these + * properties. + * + */ + +class IgnoreCajaProperties implements CompilerPass { + + final AbstractCompiler compiler; + + // Counts the number of temporary variables introduced. + int counter; + + public IgnoreCajaProperties(AbstractCompiler compiler) { + this.compiler = compiler; + this.counter = 0; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, new Traversal()); + } + + private class Traversal extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + // Look for a for..in loop. + if (n.isFor() && n.getChildCount() == 3) { + Node body = n.getLastChild(); + n.removeChild(body); + Node key = n.getFirstChild(); + n.removeChild(key); + Node tmp = IR.name( + "JSCompiler_IgnoreCajaProperties_" + counter++); + n.addChildToFront(IR.var(tmp)); + Node assignment; + Node ifBody; + + // Construct the body of the if statement. + if (key.isVar()) { + // for (var key in x) { body; } + // => + // for (var tmp in x) { + // if (!tmp.match(/___$/)) { + // var key; + // key = tmp; + // body; + // } + // } + ifBody = IR.block( + key, + IR.exprResult( + IR.assign( + key.getFirstChild().cloneNode(), + tmp.cloneTree())), + body); + } else { + // for (key in x) { body; } + // => + // for (var tmp in x) { + // if (!tmp.match(/___$/)) { + // key = tmp; + // body; + // } + // } + ifBody = IR.block( + IR.exprResult( + IR.assign( + key, + tmp.cloneTree())), + body); + } + + // Construct the new body of the for loop. + Node newBody = IR.block( + IR.ifNode( + IR.not( + IR.call( + IR.getprop( + tmp.cloneTree(), + IR.string("match")), + IR.regexp( + IR.string("___$")))), + ifBody)); + n.addChildToBack(newBody); + compiler.reportCodeChange(); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InferJSDocInfo.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InferJSDocInfo.java new file mode 100644 index 0000000..b2cb704 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InferJSDocInfo.java @@ -0,0 +1,224 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.EnumType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.ObjectType; + +import javax.annotation.Nullable; + +/** + * Set the JSDocInfo on all types. + * + * Propagates JSDoc across the type graph, but not across the symbol graph. + * This means that if you have: + * + * var x = new Foo(); + * x.bar; + * + * then the JSType attached to x.bar may get associated JSDoc, but the + * Node and Var will not. + * + * JSDoc is initially attached to AST Nodes at parse time. + * There are 3 ways that JSDoc get propagated across the type system. + * 1) Nominal types (e.g., constructors) may contain JSDocInfo for their + * declaration. + * 2) Object types have a JSDocInfo slot for each property on that type. + * 3) Shape types (like structural functions) may have JSDocInfo. + * + * #1 and #2 should be self-explanatory, and non-controversial. #3 is + * a bit trickier. It means that if you have: + * + * /** @param {number} x / + * Foo.prototype.bar = goog.abstractMethod; + * + * the JSDocInfo will appear in two places in the type system: in the 'bar' + * slot of Foo.prototype, and on the function expression type created by + * this expression. + * + * @author nicksantos@google.com (Nick Santos) + */ +class InferJSDocInfo extends AbstractPostOrderCallback + implements HotSwapCompilerPass { + + private final AbstractCompiler compiler; + @SuppressWarnings("unused") + private boolean inExterns; + + InferJSDocInfo(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + if (externs != null) { + inExterns = true; + NodeTraversal.traverse(compiler, externs, this); + } + if (root != null) { + inExterns = false; + NodeTraversal.traverse(compiler, root, this); + } + } + + @Override + public void hotSwapScript(Node root, Node originalRoot) { + Preconditions.checkNotNull(root); + Preconditions.checkState(root.isScript()); + inExterns = false; + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + JSDocInfo docInfo; + + switch (n.getType()) { + // Infer JSDocInfo on types of all type declarations on variables. + case Token.NAME: + if (parent == null) { + return; + } + + // Only allow JSDoc on VARs, function declarations, and assigns. + if (!parent.isVar() && + !NodeUtil.isFunctionDeclaration(parent) && + !(parent.isAssign() && + n == parent.getFirstChild())) { + return; + } + + // There are four places the doc info could live. + // 1) A FUNCTION node. + // /** ... */ function f() { ... } + // 2) An ASSIGN parent. + // /** ... */ x = function () { ... } + // 3) A NAME parent. + // var x, /** ... */ y = function() { ... } + // 4) A VAR gramps. + // /** ... */ var x = function() { ... } + docInfo = n.getJSDocInfo(); + if (docInfo == null && + !(parent.isVar() && + !parent.hasOneChild())) { + docInfo = parent.getJSDocInfo(); + } + + // Try to find the type of the NAME. + JSType varType = n.getJSType(); + if (varType == null && parent.isFunction()) { + varType = parent.getJSType(); + } + + // If we have no type to attach JSDocInfo to, then there's nothing + // we can do. + if (varType == null || docInfo == null) { + return; + } + + // Dereference the type. If the result is not an object, or already + // has docs attached, then do nothing. + ObjectType objType = dereferenceToObject(varType); + if (objType == null || objType.getJSDocInfo() != null) { + return; + } + + attachJSDocInfoToNominalTypeOrShape(objType, docInfo, n.getString()); + break; + + case Token.GETPROP: + // Infer JSDocInfo on properties. + // There are two ways to write doc comments on a property. + // + // 1) + // /** @deprecated */ + // obj.prop = ... + // + // 2) + // /** @deprecated */ + // obj.prop; + if (parent.isExprResult() || + (parent.isAssign() && + parent.getFirstChild() == n)) { + docInfo = n.getJSDocInfo(); + if (docInfo == null) { + docInfo = parent.getJSDocInfo(); + } + if (docInfo != null) { + ObjectType lhsType = + dereferenceToObject(n.getFirstChild().getJSType()); + if (lhsType != null) { + // Put the JSDoc in the property slot, if there is one. + String propName = n.getLastChild().getString(); + if (lhsType.hasOwnProperty(propName)) { + lhsType.setPropertyJSDocInfo(propName, docInfo); + } + + // Put the JSDoc in any constructors or function shapes as well. + ObjectType propType = + dereferenceToObject(lhsType.getPropertyType(propName)); + if (propType != null) { + attachJSDocInfoToNominalTypeOrShape( + propType, docInfo, n.getQualifiedName()); + } + } + } + } + break; + } + } + + /** + * Dereferences the given type to an object, or returns null. + */ + private ObjectType dereferenceToObject(JSType type) { + return ObjectType.cast(type == null ? null : type.dereference()); + } + + /** + * Handle cases #1 and #3 in the class doc. + */ + private void attachJSDocInfoToNominalTypeOrShape( + ObjectType objType, JSDocInfo docInfo, @Nullable String qName) { + if (objType.isConstructor() || + objType.isEnumType() || + objType.isInterface()) { + // Named types. + if (objType.hasReferenceName() && + objType.getReferenceName().equals(qName)) { + objType.setJSDocInfo(docInfo); + + if (objType.isConstructor() || objType.isInterface()) { + JSType.toMaybeFunctionType(objType).getInstanceType().setJSDocInfo( + docInfo); + } else if (objType instanceof EnumType) { + ((EnumType) objType).getElementsType().setJSDocInfo(docInfo); + } + } + } else if (!objType.isNativeObjectType() && + objType.isFunctionType()) { + // Structural functions. + objType.setJSDocInfo(docInfo); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineCostEstimator.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineCostEstimator.java new file mode 100644 index 0000000..878e36f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineCostEstimator.java @@ -0,0 +1,107 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + * For use with CodeGenerator to determine the cost of generated code. + * + * @see CodeGenerator + * @see CodePrinter + */ +class InlineCostEstimator { + // For now simply assume identifiers are 2 characters. + private static final String ESTIMATED_IDENTIFIER = "ab"; + static final int ESTIMATED_IDENTIFIER_COST = ESTIMATED_IDENTIFIER.length(); + + private InlineCostEstimator() { + } + + /** + * Determines the size of the JS code. + */ + static int getCost(Node root) { + return getCost(root, Integer.MAX_VALUE); + } + + /** + * Determines the estimated size of the JS snippet represented by the node. + */ + static int getCost(Node root, int costThreshhold) { + CompiledSizeEstimator estimator = new CompiledSizeEstimator(costThreshhold); + estimator.add(root); + return estimator.getCost(); + } + + /** + * Code consumer that estimates compiled size by assuming names are + * shortened and all whitespace is stripped. + */ + private static class CompiledSizeEstimator extends CodeConsumer { + private int maxCost; + private int cost = 0; + private char last = '\0'; + private boolean continueProcessing = true; + + CompiledSizeEstimator(int costThreshhold) { + this.maxCost = costThreshhold; + } + + void add(Node root) { + CodeGenerator cg = CodeGenerator.forCostEstimation(this); + cg.add(root); + } + + int getCost() { + return cost; + } + + @Override + boolean continueProcessing() { + return continueProcessing; + } + + @Override + char getLastChar() { + return last; + } + + @Override + void append(String str){ + last = str.charAt(str.length() - 1); + cost += str.length(); + if (maxCost <= cost) { + continueProcessing = false; + } + } + + @Override + void addIdentifier(String identifier) { + add(ESTIMATED_IDENTIFIER); + } + + /** + * Constants (true, false, null) are considered basically free, + * because it's likely that they will get folded when we're done. + */ + @Override + void addConstant(String newcode) { + add("0"); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineFunctions.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineFunctions.java new file mode 100644 index 0000000..008ffe5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineFunctions.java @@ -0,0 +1,1120 @@ +/* + * Copyright 2005 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.FunctionInjector.CanInlineResult; +import com.google.javascript.jscomp.FunctionInjector.InliningMode; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + + + +/** + * Inlines functions that are divided into two types: "direct call node + * replacement" (aka "direct") and as a block of statements (aka block). + * Function that can be inlined "directly" functions consist of a single + * return statement, everything else is must be inlined as a "block". These + * functions must meet these general requirements: + * - it is not recursive + * - the function does not contain another function -- these may be + * intentional to to limit the scope of closures. + * - function is called only once OR the size of the inline function is smaller + * than the call itself. + * - the function name is not referenced in any other manner + * + * "directly" inlined functions must meet these additional requirements: + * - consists of a single return statement + * + */ +class InlineFunctions implements SpecializationAwareCompilerPass { + + // TODO(nicksantos): This needs to be completely rewritten to use scopes + // to do variable lookups. Right now, it assumes that all functions are + // uniquely named variables. There's currently a stopgap scope-check + // to ensure that this doesn't produce invalid code. But in the long run, + // this needs a major refactor. + private final Map fns = Maps.newHashMap(); + private final Map anonFns = Maps.newHashMap(); + + private final AbstractCompiler compiler; + + private final FunctionInjector injector; + + private final boolean blockFunctionInliningEnabled; + private final boolean inlineGlobalFunctions; + private final boolean inlineLocalFunctions; + private final boolean assumeMinimumCapture; + + private SpecializeModule.SpecializationState specializationState; + + InlineFunctions(AbstractCompiler compiler, + Supplier safeNameIdSupplier, + boolean inlineGlobalFunctions, + boolean inlineLocalFunctions, + boolean blockFunctionInliningEnabled, + boolean assumeStrictThis, + boolean assumeMinimumCapture) { + Preconditions.checkArgument(compiler != null); + Preconditions.checkArgument(safeNameIdSupplier != null); + this.compiler = compiler; + + this.inlineGlobalFunctions = inlineGlobalFunctions; + this.inlineLocalFunctions = inlineLocalFunctions; + this.blockFunctionInliningEnabled = blockFunctionInliningEnabled; + this.assumeMinimumCapture = assumeMinimumCapture; + + this.injector = new FunctionInjector( + compiler, safeNameIdSupplier, + true, assumeStrictThis, assumeMinimumCapture); + } + + FunctionState getOrCreateFunctionState(String fnName) { + FunctionState fs = fns.get(fnName); + if (fs == null) { + fs = new FunctionState(); + fns.put(fnName, fs); + } + return fs; + } + + @Override + public void enableSpecialization(SpecializeModule.SpecializationState + specializationState) { + this.specializationState = specializationState; + } + + @Override + public void process(Node externs, Node root) { + Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); + + NodeTraversal.traverse(compiler, root, new FindCandidateFunctions()); + if (fns.isEmpty()) { + return; // Nothing left to do. + } + NodeTraversal.traverse(compiler, root, + new FindCandidatesReferences(fns, anonFns)); + trimCanidatesNotMeetingMinimumRequirements(); + if (fns.isEmpty()) { + return; // Nothing left to do. + } + + // Store the set of function names eligible for inlining and use this to + // prevent function names from being moved into temporaries during + // expression decomposition. If this movement were allowed it would prevent + // the Inline callback from finding the function calls. + // + // This pass already assumes these are constants, so this is safe for anyone + // using function inlining. + // + Set fnNames = Sets.newHashSet(fns.keySet()); + injector.setKnownConstants(fnNames); + + trimCanidatesUsingOnCost(); + if (fns.isEmpty()) { + return; // Nothing left to do. + } + resolveInlineConflicts(); + decomposeExpressions(fnNames); + NodeTraversal.traverse(compiler, root, + new CallVisitor( + fns, anonFns, new Inline(injector, specializationState))); + + removeInlinedFunctions(); + } + + /** + * Find functions that might be inlined. + */ + private class FindCandidateFunctions implements Callback { + private int callsSeen = 0; + + @Override + public boolean shouldTraverse( + NodeTraversal nodeTraversal, Node n, Node parent) { + // Don't traverse into function bodies + // if we aren't inlining local functions. + return inlineLocalFunctions || nodeTraversal.inGlobalScope(); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if ((t.inGlobalScope() && inlineGlobalFunctions) + || (!t.inGlobalScope() && inlineLocalFunctions)) { + findNamedFunctions(t, n, parent); + + findFunctionExpressions(t, n); + } + } + + public void findNamedFunctions(NodeTraversal t, Node n, Node parent) { + if (!NodeUtil.isStatement(n)) { + // There aren't any interesting functions here. + return; + } + + switch (n.getType()) { + // Functions expressions in the form of: + // var fooFn = function(x) { return ... } + case Token.VAR: + Preconditions.checkState(n.hasOneChild()); + Node nameNode = n.getFirstChild(); + if (nameNode.isName() && nameNode.hasChildren() + && nameNode.getFirstChild().isFunction()) { + maybeAddFunction(new FunctionVar(n), t.getModule()); + } + break; + + // Named functions + // function Foo(x) { return ... } + case Token.FUNCTION: + Preconditions.checkState(NodeUtil.isStatementBlock(parent) + || parent.isLabel()); + if (!NodeUtil.isFunctionExpression(n)) { + Function fn = new NamedFunction(n); + maybeAddFunction(fn, t.getModule()); + } + break; + } + } + + /** + * Find function expressions that are called directly in the form of + * (function(a,b,...){...})(a,b,...) + * or + * (function(a,b,...){...}).call(this,a,b, ...) + */ + public void findFunctionExpressions(NodeTraversal t, Node n) { + switch (n.getType()) { + // Functions expressions in the form of: + // (function(){})(); + case Token.CALL: + Node fnNode = null; + if (n.getFirstChild().isFunction()) { + fnNode = n.getFirstChild(); + } else if (NodeUtil.isFunctionObjectCall(n)) { + Node fnIdentifingNode = n.getFirstChild().getFirstChild(); + if (fnIdentifingNode.isFunction()) { + fnNode = fnIdentifingNode; + } + } + + // If a interesting function was discovered, add it. + if (fnNode != null) { + Function fn = new FunctionExpression(fnNode, callsSeen++); + maybeAddFunction(fn, t.getModule()); + anonFns.put(fnNode, fn.getName()); + } + break; + } + } + } + + /** + * Updates the FunctionState object for the given function. Checks if the + * given function matches the criteria for an inlinable function. + */ + private void maybeAddFunction(Function fn, JSModule module) { + String name = fn.getName(); + FunctionState fs = getOrCreateFunctionState(name); + + // TODO(johnlenz): Maybe "smarten" FunctionState by adding this logic + // to it? + + // If the function has multiple definitions, don't inline it. + if (fs.hasExistingFunctionDefinition()) { + fs.setInline(false); + } else { + // verify the function hasn't already been marked as "don't inline" + if (fs.canInline()) { + // store it for use when inlining. + fs.setFn(fn); + if (injector.isDirectCallNodeReplacementPossible( + fn.getFunctionNode())) { + fs.inlineDirectly(true); + } + + // verify the function meets all the requirements. + // TODO(johnlenz): Minimum requirement checks are about 5% of the + // run-time cost of this pass. + if (!isCandidateFunction(fn)) { + // It doesn't meet the requirements. + fs.setInline(false); + } + + // Set the module and gather names that need temporaries. + if (fs.canInline()) { + fs.setModule(module); + + Node fnNode = fn.getFunctionNode(); + Set namesToAlias = + FunctionArgumentInjector.findModifiedParameters(fnNode); + if (!namesToAlias.isEmpty()) { + fs.inlineDirectly(false); + fs.setNamesToAlias(namesToAlias); + } + + Node block = NodeUtil.getFunctionBody(fnNode); + if (NodeUtil.referencesThis(block)) { + fs.setReferencesThis(true); + } + + if (NodeUtil.containsFunction(block)) { + fs.setHasInnerFunctions(true); + // If there are inner functions, we can inline into global scope + // if there are no local vars or named functions. + // TODO(johnlenz): this can be improved by looking at the possible + // values for locals. If there are simple values, or constants + // we could still inline. + if (!assumeMinimumCapture && hasLocalNames(fnNode)) { + fs.setInline(false); + } + } + } + + // Check if block inlining is allowed. + if (fs.canInline() && !fs.canInlineDirectly()) { + if (!blockFunctionInliningEnabled) { + fs.setInline(false); + } + } + } + } + } + + /** + * @param fnNode The function to inspect. + * @return Whether the function has parameters, var, or function declarations. + */ + private boolean hasLocalNames(Node fnNode) { + Node block = NodeUtil.getFunctionBody(fnNode); + return NodeUtil.getFunctionParameters(fnNode).hasChildren() + || NodeUtil.has( + block, + new NodeUtil.MatchDeclaration(), + new NodeUtil.MatchShallowStatement()); + } + + /** + * Returns the function the traversal is currently traversing, or null + * if in the global scope. + */ + private static Node getContainingFunction(NodeTraversal t) { + return (t.inGlobalScope()) ? null : t.getScopeRoot(); + } + + /** + * Checks if the given function matches the criteria for an inlinable + * function. + */ + private boolean isCandidateFunction(Function fn) { + // Don't inline exported functions. + String fnName = fn.getName(); + if (compiler.getCodingConvention().isExported(fnName)) { + // TODO(johnlenz): Should we allow internal references to be inlined? + // An exported name can be replaced externally, any inlined instance + // would not reflect this change. + // To allow inlining we need to be able to distinguish between exports + // that are used in a read-only fashion and those that can be replaced + // by external definitions. + return false; + } + + // Don't inline this special function + if (RenameProperties.RENAME_PROPERTY_FUNCTION_NAME.equals(fnName)) { + return false; + } + + // Don't inline if we are specializing and the function can't be fixed up + if (specializationState != null && + !specializationState.canFixupFunction(fn.getFunctionNode())) { + return false; + } + + Node fnNode = fn.getFunctionNode(); + return injector.doesFunctionMeetMinimumRequirements(fnName, fnNode); + } + + /** + * @see CallVisitor + */ + private interface CallVisitorCallback { + public void visitCallSite( + NodeTraversal t, Node callNode, Node parent, FunctionState fs); + } + + /** + * Visit call sites for functions in functionMap. + */ + private static class CallVisitor extends AbstractPostOrderCallback { + + protected CallVisitorCallback callback; + private Map functionMap; + private Map anonFunctionMap; + + CallVisitor(Map fns, + Map anonFns, + CallVisitorCallback callback) { + this.functionMap = fns; + this.anonFunctionMap = anonFns; + this.callback = callback; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + // Function calls + case Token.CALL: + Node child = n.getFirstChild(); + String name = null; + // NOTE: The normalization pass insures that local names do not + // collide with global names. + if (child.isName()) { + name = child.getString(); + } else if (child.isFunction()) { + name = anonFunctionMap.get(child); + } else if (NodeUtil.isFunctionObjectCall(n)) { + Preconditions.checkState(NodeUtil.isGet(child)); + Node fnIdentifingNode = child.getFirstChild(); + if (fnIdentifingNode.isName()) { + name = fnIdentifingNode.getString(); + } else if (fnIdentifingNode.isFunction()) { + name = anonFunctionMap.get(fnIdentifingNode); + } + } + + if (name != null) { + FunctionState fs = functionMap.get(name); + // Only visit call-sites for functions that can be inlined. + if (fs != null) { + callback.visitCallSite(t, n, parent, fs); + } + } + break; + } + } + } + + /** + * @return Whether the name is used in a way that might be a candidate + * for inlining. + */ + static boolean isCandidateUsage(Node name) { + Node parent = name.getParent(); + Preconditions.checkState(name.isName()); + if (parent.isVar() || parent.isFunction()) { + // This is a declaration. Duplicate declarations are handle during + // function candidate gathering. + return true; + } + + if (parent.isCall() && parent.getFirstChild() == name) { + // This is a normal reference to the function. + return true; + } + + // Check for a ".call" to the named function: + // CALL + // GETPROP/GETELEM + // NAME + // STRING == "call" + // This-Value + // Function-parameter-1 + // ... + if (NodeUtil.isGet(parent) + && name == parent.getFirstChild() + && name.getNext().isString() + && name.getNext().getString().equals("call")) { + Node gramps = name.getAncestor(2); + if (gramps.isCall() + && gramps.getFirstChild() == parent) { + // Yep, a ".call". + return true; + } + } + return false; + } + + /** + * Find references to functions that are inlinable. + */ + private class FindCandidatesReferences + extends CallVisitor + implements CallVisitorCallback { + FindCandidatesReferences( + Map fns, + Map anonFns) { + super(fns, anonFns, null); + this.callback = this; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + super.visit(t, n, parent); + if (n.isName()) { + checkNameUsage(t, n, parent); + } + } + + @Override + public void visitCallSite( + NodeTraversal t, Node callNode, Node parent, FunctionState fs) { + maybeAddReference(t, fs, callNode, t.getModule()); + } + + void maybeAddReference(NodeTraversal t, FunctionState fs, + Node callNode, JSModule module) { + if (!fs.canInline()) { + return; + } + + boolean referenceAdded = false; + InliningMode mode = fs.canInlineDirectly() + ? InliningMode.DIRECT : InliningMode.BLOCK; + referenceAdded = maybeAddReferenceUsingMode( + t, fs, callNode, module, mode); + if (!referenceAdded && + mode == InliningMode.DIRECT && blockFunctionInliningEnabled) { + // This reference can not be directly inlined, see if + // block replacement inlining is possible. + mode = InliningMode.BLOCK; + referenceAdded = maybeAddReferenceUsingMode( + t, fs, callNode, module, mode); + } + + if (!referenceAdded) { + // Don't try to remove a function if we can't inline all + // the references. + fs.setRemove(false); + } + } + + private boolean maybeAddReferenceUsingMode( + NodeTraversal t, FunctionState fs, Node callNode, + JSModule module, InliningMode mode) { + + if (specializationState != null) { + // If we're specializing, make sure we can fixup + // the containing function before inlining + Node containingFunction = getContainingFunction(t); + if (containingFunction != null && !specializationState.canFixupFunction( + containingFunction)) { + return false; + } + } + + CanInlineResult result = injector.canInlineReferenceToFunction( + t, callNode, fs.getFn().getFunctionNode(), + fs.getNamesToAlias(), mode, fs.getReferencesThis(), + fs.hasInnerFunctions()); + if (result != CanInlineResult.NO) { + // Yeah! + boolean decompose = + (result == CanInlineResult.AFTER_PREPARATION); + fs.addReference(new Reference(callNode, module, mode, decompose)); + return true; + } + + return false; + } + + /** + * Find functions that can be inlined. + */ + private void checkNameUsage(NodeTraversal t, Node n, Node parent) { + Preconditions.checkState(n.isName()); + + if (isCandidateUsage(n)) { + return; + } + + // Other refs to a function name remove its candidacy for inlining + String name = n.getString(); + FunctionState fs = fns.get(name); + if (fs == null) { + return; + } + + // Unlike normal call/new parameters, references passed to + // JSCompiler_ObjectPropertyString are not aliases of a value, but + // a reference to the name itself, as such the value of the name is + // unknown and can not be inlined. + if (parent.isNew()) { + Node target = parent.getFirstChild(); + if (target.isName() && target.getString().equals( + ObjectPropertyStringPreprocess.EXTERN_OBJECT_PROPERTY_STRING)) { + // This method is going to be replaced so don't inline it anywhere. + fs.setInline(false); + } + } + + // If the name is being assigned to it can not be inlined. + if (parent.isAssign() && parent.getFirstChild() == n) { + // e.g. bar = something; <== we can't inline "bar" + // so mark the function as uninlinable. + // TODO(johnlenz): Should we just remove it from fns here? + fs.setInline(false); + } else { + // e.g. var fn = bar; <== we can't inline "bar" + // As this reference can't be inlined mark the function as + // unremovable. + fs.setRemove(false); + } + } + } + + /** + * Inline functions at the call sites. + */ + private static class Inline implements CallVisitorCallback { + private final FunctionInjector injector; + private final SpecializeModule.SpecializationState specializationState; + + Inline(FunctionInjector injector, + SpecializeModule.SpecializationState specializationState) { + this.injector = injector; + this.specializationState = specializationState; + } + + @Override + public void visitCallSite( + NodeTraversal t, Node callNode, Node parent, FunctionState fs) { + Preconditions.checkState(fs.hasExistingFunctionDefinition()); + if (fs.canInline()) { + Reference ref = fs.getReference(callNode); + // There are two cases ref can be null: if the call site was introduce + // because it was part of a function that was inlined during this pass + // or if the call site was trimmed from the list of references because + // the function couldn't be inlined at this location. + if (ref != null) { + if (specializationState != null) { + Node containingFunction = getContainingFunction(t); + + if (containingFunction != null) { + // Report that the function was specialized so that + // {@link SpecializeModule} can fix it up. + specializationState.reportSpecializedFunction(containingFunction); + } + } + + inlineFunction(t, callNode, fs, ref.mode); + // Keep track of references that have been inlined so that + // we can verify that none have been missed. + ref.inlined = true; + } + } + } + + /** + * Inline a function into the call site. + */ + private void inlineFunction( + NodeTraversal t, Node callNode, FunctionState fs, InliningMode mode) { + Function fn = fs.getFn(); + String fnName = fn.getName(); + Node fnNode = fs.getSafeFnNode(); + + Node newCode = injector.inline(t, callNode, fnName, fnNode, mode); + t.getCompiler().reportCodeChange(); + t.getCompiler().addToDebugLog("Inlined function: " + fn.getName()); + } + } + + /** + * Remove entries that aren't a valid inline candidates, from the list of + * encountered names. + */ + private void trimCanidatesNotMeetingMinimumRequirements() { + Iterator> i; + for (i = fns.entrySet().iterator(); i.hasNext();) { + FunctionState fs = i.next().getValue(); + if (!fs.hasExistingFunctionDefinition() || !fs.canInline()) { + i.remove(); + } + } + } + + /** + * Remove entries from the list of candidates that can't be inlined. + */ + void trimCanidatesUsingOnCost() { + Iterator> i; + for (i = fns.entrySet().iterator(); i.hasNext();) { + FunctionState fs = i.next().getValue(); + if (fs.hasReferences()) { + // Only inline function if it decreases the code size. + boolean lowersCost = mimimizeCost(fs); + if (!lowersCost) { + // It shouldn't be inlined; remove it from the list. + i.remove(); + } + } else if (!fs.canRemove()) { + // Don't bother tracking functions without references that can't be + // removed. + i.remove(); + } + } + } + + /** + * Determines if the function is worth inlining and potentially + * trims references that increase the cost. + * @return Whether inlining the references lowers the overall cost. + */ + private boolean mimimizeCost(FunctionState fs) { + if (!inliningLowersCost(fs)) { + // Try again without Block inlining references + if (fs.hasBlockInliningReferences()) { + fs.setRemove(false); + fs.removeBlockInliningReferences(); + if (!fs.hasReferences() || !inliningLowersCost(fs)) { + return false; + } + } else { + return false; + } + } + return true; + } + + /** + * @return Whether inlining the function reduces code size. + */ + private boolean inliningLowersCost(FunctionState fs) { + return injector.inliningLowersCost( + fs.getModule(), + fs.getFn().getFunctionNode(), + fs.getReferences(), + fs.getNamesToAlias(), + fs.canRemove(), + fs.getReferencesThis()); + } + + + /** + * Size base inlining calculations are thrown off when a function that is + * being inlined also contains calls to functions that are slated for + * inlining. + * + * Specifically, a clone of the FUNCTION node tree is used when the function + * is inlined. Calls in this new tree are not included in the list of function + * references so they won't be inlined (which is what we want). Here we mark + * those functions as non-removable (as they will have new references in the + * cloned node trees). + * + * This prevents a function that would only be inlined because it is + * referenced once from being inlined into multiple call sites because + * the calling function has been inlined in multiple locations or the + * function being removed while there are still references. + */ + private void resolveInlineConflicts() { + for (FunctionState fs : fns.values()) { + resolveInlineConflictsForFunction(fs); + } + } + + /** + * @see #resolveInlineConflicts + */ + private void resolveInlineConflictsForFunction(FunctionState fs) { + // Functions that aren't referenced don't cause conflicts. + if (!fs.hasReferences() || !fs.canInline()) { + return; + } + + Node fnNode = fs.getFn().getFunctionNode(); + Set names = findCalledFunctions(fnNode); + if (!names.isEmpty()) { + // Prevent the removal of the referenced functions. + for (String name : names) { + FunctionState fsCalled = fns.get(name); + if (fsCalled != null && fsCalled.canRemove()) { + fsCalled.setRemove(false); + // For functions that can no longer be removed, check if they should + // still be inlined. + if (!mimimizeCost(fsCalled)) { + // It can't be inlined remove it from the list. + fsCalled.setInline(false); + } + } + } + + // Make a copy of the Node, so it isn't changed by other inlines. + fs.setSafeFnNode(fs.getFn().getFunctionNode().cloneTree()); + } + } + + /** + * This functions that may be called directly. + */ + private Set findCalledFunctions(Node node) { + Set changed = Sets.newHashSet(); + findCalledFunctions(NodeUtil.getFunctionBody(node), changed); + return changed; + } + + /** + * @see #findCalledFunctions(Node) + */ + private void findCalledFunctions( + Node node, Set changed) { + Preconditions.checkArgument(changed != null); + // For each referenced function, add a new reference + if (node.isName()) { + if (isCandidateUsage(node)) { + changed.add(node.getString()); + } + } + + for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { + findCalledFunctions(c, changed); + } + } + + /** + * For any call-site that needs it, prepare the call-site for inlining + * by rewriting the containing expression. + */ + private void decomposeExpressions(Set fnNames) { + ExpressionDecomposer decomposer = new ExpressionDecomposer( + compiler, compiler.getUniqueNameIdSupplier(), fnNames); + + for (FunctionState fs : fns.values()) { + if (fs.canInline()) { + for (Reference ref : fs.getReferences()) { + if (ref.requiresDecomposition) { + injector.maybePrepareCall(ref.callNode); + } + } + } + } + } + + /** + * Removed inlined functions that no longer have any references. + */ + void removeInlinedFunctions() { + for (FunctionState fs : fns.values()) { + if (fs.canRemove()) { + Function fn = fs.getFn(); + Preconditions.checkState(fs.canInline()); + Preconditions.checkState(fn != null); + verifyAllReferencesInlined(fs); + + if (specializationState != null) { + specializationState.reportRemovedFunction( + fn.getFunctionNode(), fn.getDeclaringBlock()); + } + + fn.remove(); + compiler.reportCodeChange(); + } + } + } + + /** + * Sanity check to verify, that expression rewriting didn't + * make a call inaccessible. + */ + void verifyAllReferencesInlined(FunctionState fs) { + for (Reference ref : fs.getReferences()) { + if (!ref.inlined) { + throw new IllegalStateException("Call site missed.\n call: " + + ref.callNode.toStringTree() + "\n parent: " + + ref.callNode.getParent().toStringTree()); + } + } + } + + /** + * Use to track the decisions that have been make about a function. + */ + private static class FunctionState { + private Function fn = null; + private Node safeFnNode = null; + private boolean inline = true; + private boolean remove = true; + private boolean inlineDirectly = false; + private boolean referencesThis = false; + private boolean hasInnerFunctions = false; + private Map references = null; + private JSModule module = null; + private Set namesToAlias = null; + + boolean hasExistingFunctionDefinition() { + return (fn != null); + } + + public void setReferencesThis(boolean referencesThis) { + this.referencesThis = referencesThis; + } + + public boolean getReferencesThis() { + return this.referencesThis; + } + + public void setHasInnerFunctions(boolean hasInnerFunctions) { + this.hasInnerFunctions = hasInnerFunctions; + } + + + public boolean hasInnerFunctions() { + return hasInnerFunctions; + } + + void removeBlockInliningReferences() { + Iterator> i; + for (i = getReferencesInternal().entrySet().iterator(); i.hasNext();) { + Entry entry = i.next(); + if (entry.getValue().mode == InliningMode.BLOCK) { + i.remove(); + } + } + } + + public boolean hasBlockInliningReferences() { + for (Reference r : getReferencesInternal().values()) { + if (r.mode == InliningMode.BLOCK) { + return true; + } + } + return false; + } + + public Function getFn() { + return fn; + } + + public void setFn(Function fn) { + Preconditions.checkState(this.fn == null); + this.fn = fn; + } + + public Node getSafeFnNode() { + return (safeFnNode != null) ? safeFnNode : fn.getFunctionNode(); + } + + public void setSafeFnNode(Node safeFnNode) { + this.safeFnNode = safeFnNode; + } + + public boolean canInline() { + return inline; + } + + public void setInline(boolean inline) { + this.inline = inline; + if (inline == false) { + // No need to keep references to function that can't be inlined. + references = null; + // Don't remove functions that we aren't inlining. + remove = false; + } + } + + public boolean canRemove() { + return remove; + } + + public void setRemove(boolean remove) { + this.remove = remove; + } + + public boolean canInlineDirectly() { + return inlineDirectly; + } + + public void inlineDirectly(boolean directReplacement) { + this.inlineDirectly = directReplacement; + } + + public boolean hasReferences() { + return (references != null && !references.isEmpty()); + } + + private Map getReferencesInternal() { + if (references == null) { + return Collections.emptyMap(); + } + return references; + } + + public void addReference(Reference ref) { + if (references == null) { + references = Maps.newHashMap(); + } + references.put(ref.callNode, ref); + } + + public Collection getReferences() { + return getReferencesInternal().values(); + } + + public Reference getReference(Node n) { + return getReferencesInternal().get(n); + } + + public Set getNamesToAlias() { + if (namesToAlias == null) { + return Collections.emptySet(); + } + return Collections.unmodifiableSet(namesToAlias); + } + + public void setNamesToAlias(Set names) { + namesToAlias = names; + } + + public void setModule(JSModule module) { + this.module = module; + } + + public JSModule getModule() { + return module; + } + } + + /** + * Interface for dealing with function declarations and function + * expressions equally + */ + private static interface Function { + /** Gets the name of the function */ + public String getName(); + + /** Gets the function node */ + public Node getFunctionNode(); + + /** Removes itself from the JavaScript */ + public void remove(); + + public Node getDeclaringBlock(); + } + + /** NamedFunction implementation of the Function interface */ + private static class NamedFunction implements Function { + private final Node fn; + + public NamedFunction(Node fn) { + this.fn = fn; + } + + @Override + public String getName() { + return fn.getFirstChild().getString(); + } + + @Override + public Node getFunctionNode() { + return fn; + } + + @Override + public void remove() { + NodeUtil.removeChild(fn.getParent(), fn); + } + + @Override + public Node getDeclaringBlock() { + return fn.getParent(); + } + } + + /** FunctionVar implementation of the Function interface */ + private static class FunctionVar implements Function { + private final Node var; + + public FunctionVar(Node var) { + this.var = var; + } + + @Override + public String getName() { + return var.getFirstChild().getString(); + } + + @Override + public Node getFunctionNode() { + return var.getFirstChild().getFirstChild(); + } + + @Override + public void remove() { + NodeUtil.removeChild(var.getParent(), var); + } + + @Override + public Node getDeclaringBlock() { + return var.getParent(); + } + } + + /** FunctionExpression implementation of the Function interface */ + private static class FunctionExpression implements Function { + private final Node fn; + private final String fakeName; + + public FunctionExpression(Node fn, int index) { + this.fn = fn; + // A number is not a valid function JavaScript identifier + // so we don't need to worry about collisions. + this.fakeName = String.valueOf(index); + } + + @Override + public String getName() { + return fakeName; + } + + @Override + public Node getFunctionNode() { + return fn; + } + + @Override + public void remove() { + // Nothing to do. The function is removed with the call. + } + + @Override + public Node getDeclaringBlock() { + return null; + } + + } + + class Reference extends FunctionInjector.Reference { + final boolean requiresDecomposition; + boolean inlined = false; + Reference( + Node callNode, JSModule module, InliningMode mode, boolean decompose) { + super(callNode, module, mode); + this.requiresDecomposition = decompose; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineObjectLiterals.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineObjectLiterals.java new file mode 100644 index 0000000..d03988c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineObjectLiterals.java @@ -0,0 +1,472 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.ReferenceCollectingCallback.Behavior; +import com.google.javascript.jscomp.ReferenceCollectingCallback.Reference; +import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; +import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceMap; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +/** + * Using the infrastructure provided by {@link ReferenceCollectingCallback}, + * identify variables that are only ever assigned to object literals + * and that are never used in entirety, and expand the objects into + * individual variables. + * + * Based on the InlineVariables pass + * + */ +class InlineObjectLiterals implements CompilerPass { + + public static final String VAR_PREFIX = "JSCompiler_object_inline_"; + + private final AbstractCompiler compiler; + + private final Supplier safeNameIdSupplier; + + InlineObjectLiterals( + AbstractCompiler compiler, + Supplier safeNameIdSupplier) { + this.compiler = compiler; + this.safeNameIdSupplier = safeNameIdSupplier; + } + + @Override + public void process(Node externs, Node root) { + ReferenceCollectingCallback callback = new ReferenceCollectingCallback( + compiler, new InliningBehavior()); + callback.process(externs, root); + } + + /** + * Builds up information about nodes in each scope. When exiting the + * scope, inspects all variables in that scope, and inlines any + * that we can. + */ + private class InliningBehavior implements Behavior { + + /** + * A list of variables that should not be inlined, because their + * reference information is out of sync with the state of the AST. + */ + private final Set staleVars = Sets.newHashSet(); + + @Override + public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) { + for (Iterator it = t.getScope().getVars(); it.hasNext();) { + Var v = it.next(); + + if (isVarInlineForbidden(v)) { + continue; + } + + ReferenceCollection referenceInfo = referenceMap.getReferences(v); + + if (isInlinableObject(referenceInfo.references)) { + // Blacklist the object itself, as well as any other values + // that it refers to, since they will have been moved around. + staleVars.add(v); + + Reference declaration = referenceInfo.references.get(0); + Reference init = referenceInfo.getInitializingReference(); + + // Split up the object into individual variables if the object + // is never referenced directly in full. + splitObject(v, declaration, init, referenceInfo); + } + } + } + + /** + * If there are any variable references in the given node tree, + * blacklist them to prevent the pass from trying to inline the + * variable. Any code modifications will have potentially made the + * ReferenceCollection invalid. + */ + private void blacklistVarReferencesInTree(Node root, final Scope scope) { + NodeUtil.visitPreOrder(root, new NodeUtil.Visitor() { + @Override + public void visit(Node node) { + if (node.isName()) { + staleVars.add(scope.getVar(node.getString())); + } + } + }, NodeUtil.MATCH_NOT_FUNCTION); + } + + /** + * Whether the given variable is forbidden from being inlined. + */ + private boolean isVarInlineForbidden(Var var) { + // A variable may not be inlined if: + // 1) The variable is defined in the externs + // 2) The variable is exported, + // 3) Don't inline the special RENAME_PROPERTY_FUNCTION_NAME + // 4) A reference to the variable has been inlined. We're downstream + // of the mechanism that creates variable references, so we don't + // have a good way to update the reference. Just punt on it. + + // Additionally, exclude global variables for now. + + return var.isGlobal() + || var.isExtern() + || compiler.getCodingConvention().isExported(var.name) + || RenameProperties.RENAME_PROPERTY_FUNCTION_NAME.equals(var.name) + || staleVars.contains(var); + } + + /** + * Counts the number of direct (full) references to an object. + * Specifically, we check for references of the following type: + *

      +     *   x;
      +     *   x.fn();
      +     * 
      + */ + private boolean isInlinableObject(List refs) { + boolean ret = false; + Set validProperties = Sets.newHashSet(); + for (Reference ref : refs) { + Node name = ref.getNode(); + Node parent = ref.getParent(); + Node gramps = ref.getGrandparent(); + + // Ignore most indirect references, like x.y (but not x.y(), + // since the function referenced by y might reference 'this'). + // + if (parent.isGetProp()) { + Preconditions.checkState(parent.getFirstChild() == name); + // A call target may be using the object as a 'this' value. + if (gramps.isCall() + && gramps.getFirstChild() == parent) { + return false; + } + + // Deleting a property has different semantics from deleting + // a variable, so deleted properties should not be inlined. + if (gramps.isDelProp()) { + return false; + } + + // NOTE(nicksantos): This pass's object-splitting algorithm has + // a blind spot. It assumes that if a property isn't defined on an + // object, then the value is undefined. This is not true, because + // Object.prototype can have arbitrary properties on it. + // + // We short-circuit this problem by bailing out if we see a reference + // to a property that isn't defined on the object literal. This + // isn't a perfect algorithm, but it should catch most cases. + String propName = parent.getLastChild().getString(); + if (!validProperties.contains(propName)) { + if (NodeUtil.isVarOrSimpleAssignLhs(parent, gramps)) { + validProperties.add(propName); + } else { + return false; + } + } + continue; + } + + // Only rewrite VAR declarations or simple assignment statements + if (!isVarOrAssignExprLhs(name)) { + return false; + } + + Node val = ref.getAssignedValue(); + if (val == null) { + // A var with no assignment. + continue; + } + + // We're looking for object literal assignments only. + if (!val.isObjectLit()) { + return false; + } + + // Make sure that the value is not self-referential. IOW, + // disallow things like x = {b: x.a}. + // + // TODO: Only exclude unorderable self-referential + // assignments. i.e. x = {a: x.b, b: x.a} is not orderable, + // but x = {a: 1, b: x.a} is. + // + // Also, ES5 getters/setters aren't handled by this pass. + for (Node child = val.getFirstChild(); child != null; + child = child.getNext()) { + if (child.isGetterDef() || + child.isSetterDef()) { + // ES5 get/set not supported. + return false; + } + + validProperties.add(child.getString()); + + Node childVal = child.getFirstChild(); + // Check if childVal is the parent of any of the passed in + // references, as that is how self-referential assignments + // will happen. + for (Reference t : refs) { + Node refNode = t.getParent(); + while (!NodeUtil.isStatementBlock(refNode)) { + if (refNode == childVal) { + // There's a self-referential assignment + return false; + } + refNode = refNode.getParent(); + } + } + } + + + // We have found an acceptable object literal assignment. As + // long as there are no other assignments that mess things up, + // we can inline. + ret = true; + } + return ret; + } + + private boolean isVarOrAssignExprLhs(Node n) { + Node parent = n.getParent(); + return parent.isVar() || + (parent.isAssign() + && parent.getFirstChild() == n + && parent.getParent().isExprResult()); + } + + /** + * Computes a list of ever-referenced keys in the object being + * inlined, and returns a mapping of key name -> generated + * variable name. + */ + private Map computeVarList( + Var v, ReferenceCollection referenceInfo) { + Map varmap = Maps.newLinkedHashMap(); + + for (Reference ref : referenceInfo.references) { + if (ref.isLvalue() || ref.isInitializingDeclaration()) { + Node val = ref.getAssignedValue(); + if (val != null) { + Preconditions.checkState(val.isObjectLit()); + for (Node child = val.getFirstChild(); child != null; + child = child.getNext()) { + String varname = child.getString(); + if (varmap.containsKey(varname)) { + continue; + } + + String var = VAR_PREFIX + varname + "_" + + safeNameIdSupplier.get(); + varmap.put(varname, var); + } + } + } else if (ref.getParent().isVar()) { + // This is the var. There is no value. + } else { + Node getprop = ref.getParent(); + Preconditions.checkState(getprop.isGetProp()); + + // The key being looked up in the original map. + String varname = getprop.getLastChild().getString(); + if (varmap.containsKey(varname)) { + continue; + } + + String var = VAR_PREFIX + varname + "_" + safeNameIdSupplier.get(); + varmap.put(varname, var); + } + } + + return varmap; + } + + /** + * Populates a map of key names -> initial assigned values. The + * object literal these are being pulled from is invalidated as + * a result. + */ + private void fillInitialValues(Reference init, Map initvals) { + Node object = init.getAssignedValue(); + Preconditions.checkState(object.isObjectLit()); + for (Node key = object.getFirstChild(); key != null; + key = key.getNext()) { + initvals.put(key.getString(), key.removeFirstChild()); + } + } + + /** + * Replaces an assignment like x = {...} with t1=a,t2=b,t3=c,true. + * Note that the resulting expression will always evaluate to + * true, as would the x = {...} expression. + */ + private void replaceAssignmentExpression(Var v, Reference ref, + Map varmap) { + // Compute all of the assignments necessary + List nodes = Lists.newArrayList(); + Node val = ref.getAssignedValue(); + blacklistVarReferencesInTree(val, v.scope); + Preconditions.checkState(val.isObjectLit()); + Set all = Sets.newLinkedHashSet(varmap.keySet()); + for (Node key = val.getFirstChild(); key != null; + key = key.getNext()) { + String var = key.getString(); + Node value = key.removeFirstChild(); + // TODO(user): Copy type information. + nodes.add( + IR.assign( + IR.name(varmap.get(var)), + value)); + all.remove(var); + } + + // TODO(user): Better source information. + for (String var : all) { + nodes.add( + IR.assign( + IR.name(varmap.get(var)), + NodeUtil.newUndefinedNode(null))); + } + + Node replacement; + if (nodes.isEmpty()) { + replacement = IR.trueNode(); + } else { + // All assignments evaluate to true, so make sure that the + // expr statement evaluates to true in case it matters. + nodes.add(IR.trueNode()); + + // Join these using COMMA. A COMMA node must have 2 children, so we + // create a tree. In the tree the first child be the COMMA to match + // the parser, otherwise tree equality tests fail. + nodes = Lists.reverse(nodes); + replacement = new Node(Token.COMMA); + Node cur = replacement; + int i; + for (i = 0; i < nodes.size() - 2; i++) { + cur.addChildToFront(nodes.get(i)); + Node t = new Node(Token.COMMA); + cur.addChildToFront(t); + cur = t; + } + cur.addChildToFront(nodes.get(i)); + cur.addChildToFront(nodes.get(i + 1)); + } + + Node replace = ref.getParent(); + replacement.copyInformationFromForTree(replace); + + if (replace.isVar()) { + replace.getParent().replaceChild( + replace, NodeUtil.newExpr(replacement)); + } else { + replace.getParent().replaceChild(replace, replacement); + } + } + + /** + * Splits up the object literal into individual variables, and + * updates all uses. + */ + private void splitObject(Var v, Reference declaration, + Reference init, + ReferenceCollection referenceInfo) { + // First figure out the FULL set of possible keys, so that they + // can all be properly set as necessary. + Map varmap = computeVarList(v, referenceInfo); + + Map initvals = Maps.newHashMap(); + // Figure out the top-level of the var assign node. If it's a plain + // ASSIGN, then there's an EXPR_STATEMENT above it, if it's a + // VAR then it should be directly replaced. + Node vnode; + boolean defined = referenceInfo.isWellDefined() && + init.getParent().isVar(); + if (defined) { + vnode = init.getParent(); + fillInitialValues(init, initvals); + } else { + // TODO(user): More test / rewrite this part. + // Find the beginning of the function / script. + vnode = v.getScope().getRootNode().getLastChild().getFirstChild(); + } + + for (Map.Entry entry : varmap.entrySet()) { + Node val = initvals.get(entry.getKey()); + Node varnode = NodeUtil.newVarNode(entry.getValue(), val); + if (val == null) { + // is this right? + varnode.copyInformationFromForTree(vnode); + } else { + blacklistVarReferencesInTree(val, v.scope); + } + vnode.getParent().addChildBefore(varnode, vnode); + } + + if (defined) { + vnode.getParent().removeChild(vnode); + } + + for (Reference ref : referenceInfo.references) { + // The init/decl have already been converted. + if (defined && ref == init) continue; + + if (ref.isLvalue()) { + // Assignments have to be handled specially, since they + // expand out into multiple assignments. + replaceAssignmentExpression(v, ref, varmap); + } else if (ref.getParent().isVar()) { + // The old variable declaration. It didn't have a + // value. Remove it entirely as it should now be unused. + ref.getGrandparent().removeChild(ref.getParent()); + } else { + // Make sure that the reference is a GETPROP as we expect it to be. + Node getprop = ref.getParent(); + Preconditions.checkState(getprop.isGetProp()); + + // The key being looked up in the original map. + String var = getprop.getChildAtIndex(1).getString(); + + // If the variable hasn't already been declared, add an empty + // declaration near all the other declarations. + Preconditions.checkState(varmap.containsKey(var)); + + // Replace the GETPROP node with a NAME. + Node replacement = IR.name(varmap.get(var)); + replacement.copyInformationFrom(getprop); + ref.getGrandparent().replaceChild(ref.getParent(), replacement); + } + } + + compiler.reportCodeChange(); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineProperties.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineProperties.java new file mode 100644 index 0000000..185c38e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineProperties.java @@ -0,0 +1,303 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.TypeValidator.TypeMismatch; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; + +import java.util.Map; +import java.util.Set; + + +/** + * InlineProperties attempts to find references to properties that are known to + * be constants and inline the known value. + * + * This pass relies on type information to find these property references and + * properties are assumed to be constant if either: + * - the property is assigned unconditionally in the instance constructor + * - the property is assigned unconditionally to the type's prototype + * + * The current implementation only inlines immutable values (as defined by + * NodeUtil.isImmutableValue). + * + * @author johnlenz@google.com (John Lenz) + */ +public class InlineProperties implements CompilerPass { + + private final AbstractCompiler compiler; + + static class PropertyInfo { + PropertyInfo(JSType type, Node value) { + this.type = type; + this.value = value; + } + final JSType type; + final Node value; + } + + private static final PropertyInfo INVALIDATED = new PropertyInfo( + null, null); + + private final Map props = Maps.newHashMap(); + + private Set invalidatingTypes; + + InlineProperties(AbstractCompiler compiler) { + this.compiler = compiler; + buildInvalidatingTypeSet(); + } + + // TODO(johnlenz): this is a direct copy of the invalidation code + // from AmbiguateProperties, if in the end we don't need to modify it + // we should move it to a common location. + private void buildInvalidatingTypeSet() { + JSTypeRegistry registry = compiler.getTypeRegistry(); + invalidatingTypes = Sets.newHashSet( + registry.getNativeType(JSTypeNative.ALL_TYPE), + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE), + registry.getNativeType(JSTypeNative.NULL_TYPE), + registry.getNativeType(JSTypeNative.VOID_TYPE), + registry.getNativeType(JSTypeNative.FUNCTION_FUNCTION_TYPE), + registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), + registry.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE), + registry.getNativeType(JSTypeNative.GLOBAL_THIS), + registry.getNativeType(JSTypeNative.OBJECT_TYPE), + registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE), + registry.getNativeType(JSTypeNative.OBJECT_FUNCTION_TYPE), + registry.getNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE), + registry.getNativeType(JSTypeNative.UNKNOWN_TYPE)); + + for (TypeMismatch mis : compiler.getTypeValidator().getMismatches()) { + addInvalidatingType(mis.typeA); + addInvalidatingType(mis.typeB); + } + } + + /** + * Invalidates the given type, so that no properties on it will be renamed. + */ + private void addInvalidatingType(JSType type) { + type = type.restrictByNotNullOrUndefined(); + if (type.isUnionType()) { + for (JSType alt : type.toMaybeUnionType().getAlternates()) { + addInvalidatingType(alt); + } + } + + invalidatingTypes.add(type); + ObjectType objType = ObjectType.cast(type); + if (objType != null && objType.isInstanceType()) { + invalidatingTypes.add(objType.getImplicitPrototype()); + } + } + + /** Returns true if properties on this type should not be renamed. */ + private boolean isInvalidatingType(JSType type) { + if (type.isUnionType()) { + type = type.restrictByNotNullOrUndefined(); + if (type.isUnionType()) { + for (JSType alt : type.toMaybeUnionType().getAlternates()) { + if (isInvalidatingType(alt)) { + return true; + } + } + return false; + } + } + ObjectType objType = ObjectType.cast(type); + return objType == null + || invalidatingTypes.contains(objType) + || !objType.hasReferenceName() + || objType.isUnknownType() + || objType.isEmptyType() /* unresolved types */ + || objType.isEnumType() + || objType.autoboxesTo() != null; + } + + /** + * This method gets the JSType from the Node argument and verifies that it is + * present. + */ + private JSType getJSType(Node n) { + JSType jsType = n.getJSType(); + if (jsType == null) { + return compiler.getTypeRegistry().getNativeType( + JSTypeNative.UNKNOWN_TYPE); + } else { + return jsType; + } + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverseRoots( + compiler, new GatherCandidates(), externs, root); + NodeTraversal.traverseRoots( + compiler, new ReplaceCandidates(), externs, root); + } + + class GatherCandidates extends AbstractPostOrderCallback { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + boolean invalidatingPropRef = false; + String propName = null; + if (n.isGetProp()) { + propName = n.getLastChild().getString(); + if (t.getInput().isExtern()) { + // Any extern reference invalidates + invalidatingPropRef = true; + } else if (parent.isAssign()) { + invalidatingPropRef = !maybeCandidateDefinition(t, n, parent); + } else if (NodeUtil.isLValue(n)) { + // Other LValue references invalidate + invalidatingPropRef = true; + } else if (parent.isDelProp()) { + // Deletes invalidate + invalidatingPropRef = true; + } else { + // A property read doesn't invalidate + invalidatingPropRef = false; + } + } else if (n.isStringKey()) { + propName = n.getString(); + if (t.getInput().isExtern()) { + // Any extern reference invalidates + invalidatingPropRef = true; + } else { + // For now, any object literal key invalidates + // TODO(johnlenz): support prototype properties like: + // foo.prototype = { a: 1, b: 2 }; + invalidatingPropRef = true; + } + } + + if (invalidatingPropRef) { + Preconditions.checkNotNull(propName); + invalidateProperty(propName); + } + } + + /** + * @return Whether this is a valid definition for a candidate property. + */ + private boolean maybeCandidateDefinition( + NodeTraversal t, Node n, Node parent) { + Preconditions.checkState(n.isGetProp() && parent.isAssign()); + boolean isCandidate = false; + Node src = n.getFirstChild(); + String propName = n.getLastChild().getString(); + + Node value = parent.getLastChild(); + if (src.isThis()) { + // This is a simple assignment like: + // this.foo = 1; + if (inContructor(t)) { + // This maybe a valid assignment. + isCandidate = maybeStoreCandidateValue( + getJSType(src), propName, value); + } + } else if (t.inGlobalScope() + && src.isGetProp() + && src.getLastChild().getString().equals("prototype")) { + // This is a prototype assignment like: + // x.prototype.foo = 1; + JSType instanceType = maybeGetInstanceTypeFromPrototypeRef(src); + if (instanceType != null) { + isCandidate = maybeStoreCandidateValue( + instanceType, propName, value); + } + } + return isCandidate; + } + + private JSType maybeGetInstanceTypeFromPrototypeRef(Node src) { + JSType ownerType = getJSType(src.getFirstChild()); + if (ownerType.isFunctionType() && ownerType.isConstructor()) { + FunctionType functionType = ((FunctionType) ownerType); + return functionType.getInstanceType(); + } + return null; + } + + private void invalidateProperty(String propName) { + props.put(propName, INVALIDATED); + } + + private boolean maybeStoreCandidateValue( + JSType type, String propName, Node value) { + Preconditions.checkNotNull(value); + if (!props.containsKey(propName) + && !isInvalidatingType(type) + && NodeUtil.isImmutableValue(value) + && NodeUtil.isExecutedExactlyOnce(value)) { + props.put(propName, new PropertyInfo(type, value)); + return true; + } + return false; + } + + private boolean inContructor(NodeTraversal t) { + Node root = t.getScopeRoot(); + JSDocInfo info = NodeUtil.getBestJSDocInfo(root); + return info != null && info.isConstructor(); + } + } + + class ReplaceCandidates extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isGetProp() && !NodeUtil.isLValue(n)) { + Node target = n.getFirstChild(); + String propName = n.getLastChild().getString(); + PropertyInfo info = props.get(propName); + if (info != null + && info != INVALIDATED + && isMatchingType(target, info.type)) { + Node replacement = info.value.cloneTree(); + if (NodeUtil.mayHaveSideEffects(n.getFirstChild(), compiler)) { + replacement = IR.comma(n.removeFirstChild(), replacement).srcref(n); + } + parent.replaceChild(n, replacement); + compiler.reportCodeChange(); + } + } + } + + private boolean isMatchingType(Node n, JSType src) { + src = src.restrictByNotNullOrUndefined(); + JSType dest = getJSType(n).restrictByNotNullOrUndefined(); + if (!isInvalidatingType(dest) + && dest.isSubtype(src)) { + return true; + } + return false; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineSimpleMethods.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineSimpleMethods.java new file mode 100644 index 0000000..7de631f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineSimpleMethods.java @@ -0,0 +1,303 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import java.util.Collection; +import java.util.List; +import java.util.logging.Logger; + +/** + * Inlines methods that take no arguments and have only a return statement + * returning a property. Because it works on method names rather than type + * inference, a method with multiple definitions will be inlined if each + * definition is identical. + * + *
      + * A.prototype.foo = function() { return this.b; }
      + * B.prototype.foo = function() { return this.b; }
      + * 
      + * + * will inline foo, but + * + *
      + * A.prototype.foo = function() { return this.b; }
      + * B.prototype.foo = function() { return this.c; }
      + * 
      + * + * will not. + * + * Declarations are not removed because we do not find all possible + * call sites. For examples, calls of the form foo["bar"] are not + * detected. + * + */ +class InlineSimpleMethods extends MethodCompilerPass { + + private static final Logger logger = + Logger.getLogger(InlineSimpleMethods.class.getName()); + + InlineSimpleMethods(AbstractCompiler compiler) { + super(compiler); + } + + /** + * For each method call, see if it is a candidate for inlining. + * TODO(kushal): Cache the results of the checks + */ + private class InlineTrivialAccessors extends InvocationsCallback { + + @Override + void visit(NodeTraversal t, Node callNode, Node parent, String callName) { + if (externMethods.contains(callName) || + nonMethodProperties.contains(callName)) { + return; + } + + Collection definitions = methodDefinitions.get(callName); + if (definitions == null || definitions.size() == 0) { + return; + } + + // Do check of arity, complexity, and consistency in what we think is + // the order from least to most complex + Node firstDefinition = definitions.iterator().next(); + + // Check any multiple definitions + if (definitions.size() == 1 || allDefinitionsEquivalent(definitions)) { + + if (!argsMayHaveSideEffects(callNode)) { + // Verify this is a trivial return + Node returned = returnedExpression(firstDefinition); + if (returned != null) { + if (isPropertyTree(returned)) { + logger.fine("Inlining property accessor: " + callName); + inlinePropertyReturn(parent, callNode, returned); + } else if (NodeUtil.isLiteralValue(returned, false) && + !NodeUtil.mayHaveSideEffects( + callNode.getFirstChild(), compiler)) { + logger.fine("Inlining constant accessor: " + callName); + inlineConstReturn(parent, callNode, returned); + } + } else if (isEmptyMethod(firstDefinition) && + !NodeUtil.mayHaveSideEffects( + callNode.getFirstChild(), compiler)) { + logger.fine("Inlining empty method: " + callName); + inlineEmptyMethod(t, parent, callNode); + } + } + } else { + logger.fine("Method '" + callName + "' has conflicting definitions."); + } + } + } + + @Override + Callback getActingCallback() { + return new InlineTrivialAccessors(); + } + + /** + * Returns true if the provided node is a getprop for + * which the left child is this or a valid property tree + * and for which the right side is a string. + */ + private static boolean isPropertyTree(Node expectedGetprop) { + if (!expectedGetprop.isGetProp()) { + return false; + } + + Node leftChild = expectedGetprop.getFirstChild(); + if (!leftChild.isThis() && + !isPropertyTree(leftChild)) { + return false; + } + + Node retVal = leftChild.getNext(); + if (NodeUtil.getStringValue(retVal) == null) { + return false; + } + return true; + } + + /** + * Finds the occurrence of "this" in the provided property tree and replaces + * it with replacement + */ + private static void replaceThis(Node expectedGetprop, Node replacement) { + Node leftChild = expectedGetprop.getFirstChild(); + if (leftChild.isThis()) { + expectedGetprop.replaceChild(leftChild, replacement); + } else { + replaceThis(leftChild, replacement); + } + } + + /** + * Return the node that represents the expression returned + * by the method, given a FUNCTION node. + */ + private static Node returnedExpression(Node fn) { + Node expectedBlock = getMethodBlock(fn); + if (!expectedBlock.hasOneChild()) { + return null; + } + + Node expectedReturn = expectedBlock.getFirstChild(); + if (!expectedReturn.isReturn()) { + return null; + } + + if (!expectedReturn.hasOneChild()) { + return null; + } + + return expectedReturn.getLastChild(); + } + + + /** + * Return whether the given FUNCTION node is an empty method definition. + * + * Must be private, or moved to NodeUtil. + */ + private static boolean isEmptyMethod(Node fn) { + Node expectedBlock = getMethodBlock(fn); + return expectedBlock == null ? + false : NodeUtil.isEmptyBlock(expectedBlock); + } + + /** + * Return a BLOCK node if the given FUNCTION node is a valid method + * definition, null otherwise. + * + * Must be private, or moved to NodeUtil. + */ + private static Node getMethodBlock(Node fn) { + if (fn.getChildCount() != 3) { + return null; + } + + Node expectedBlock = fn.getLastChild(); + return expectedBlock.isBlock() ? + expectedBlock : null; + } + + /** + * Given a set of method definitions, verify they are the same. + */ + private boolean allDefinitionsEquivalent( + Collection definitions) { + List list = Lists.newArrayList(); + list.addAll(definitions); + Node node0 = list.get(0); + for (int i = 1; i < list.size(); i++) { + if (!compiler.areNodesEqualForInlining(list.get(i), node0)) { + return false; + } + } + return true; + } + + /** + * Replace the provided method call with the tree specified in returnedValue + * + * Parse tree of a call is + * name + * call + * getprop + * obj + * string + */ + private void inlinePropertyReturn(Node parent, Node call, + Node returnedValue) { + Node getProp = returnedValue.cloneTree(); + replaceThis(getProp, call.getFirstChild().removeFirstChild()); + parent.replaceChild(call, getProp); + compiler.reportCodeChange(); + } + + /** + * Replace the provided object and its method call with the tree specified + * in returnedValue. Should be called only if the object reference has + * no side effects. + */ + private void inlineConstReturn(Node parent, Node call, + Node returnedValue) { + Node retValue = returnedValue.cloneTree(); + parent.replaceChild(call, retValue); + compiler.reportCodeChange(); + } + + /** + * Remove the provided object and its method call. + */ + private void inlineEmptyMethod(NodeTraversal t, Node parent, Node call) { + // If the return value of the method call is read, + // replace it with "void 0". Otherwise, remove the call entirely. + if (NodeUtil.isExprCall(parent)) { + parent.getParent().replaceChild(parent, IR.empty()); + } else { + Node srcLocation = call; + parent.replaceChild(call, NodeUtil.newUndefinedNode(srcLocation)); + } + compiler.reportCodeChange(); + } + + /** + * Check whether the given method call's arguments have side effects. + * @param call The call node of a method invocation. + */ + private boolean argsMayHaveSideEffects(Node call) { + for (Node currentChild = call.getFirstChild().getNext(); + currentChild != null; + currentChild = currentChild.getNext()) { + if (NodeUtil.mayHaveSideEffects(currentChild, compiler)) { + return true; + } + } + + return false; + } + + /** + * A do-nothing signature store. + */ + static final MethodCompilerPass.SignatureStore DUMMY_SIGNATURE_STORE = + new MethodCompilerPass.SignatureStore() { + @Override + public void addSignature( + String functionName, Node functionNode, String sourceFile) { + } + + @Override + public void removeSignature(String functionName) { + } + + @Override + public void reset() { + } + }; + + @Override + SignatureStore getSignatureStore() { + return DUMMY_SIGNATURE_STORE; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineVariables.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineVariables.java new file mode 100644 index 0000000..5c7408d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InlineVariables.java @@ -0,0 +1,735 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.CodingConvention.SubclassRelationship; +import com.google.javascript.jscomp.ReferenceCollectingCallback.Behavior; +import com.google.javascript.jscomp.ReferenceCollectingCallback.Reference; +import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; +import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceMap; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.Node; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +/** + * Using the infrastructure provided by VariableReferencePass, identify + * variables that are used only once and in a way that is safe to move, and then + * inline them. + * + * This pass has two "modes." One mode only inlines variables declared as + * constants, for legacy compiler clients. The second mode inlines any + * variable that we can provably inline. Note that the second mode is a + * superset of the first mode. We only support the first mode for + * backwards-compatibility with compiler clients that don't want + * --inline_variables. + * + * The approach of this pass is similar to {@link CrossModuleCodeMotion} + * + * @author kushal@google.com (Kushal Dave) + * @author nicksantos@google.com (Nick Santos) + */ +class InlineVariables implements CompilerPass { + + private final AbstractCompiler compiler; + + enum Mode { + // Only inline things explicitly marked as constant. + CONSTANTS_ONLY, + // Locals only + LOCALS_ONLY, + ALL + } + + private final Mode mode; + + // Inlines all strings, even if they increase the size of the gzipped binary. + private final boolean inlineAllStrings; + + private final IdentifyConstants identifyConstants = new IdentifyConstants(); + + InlineVariables( + AbstractCompiler compiler, + Mode mode, + boolean inlineAllStrings) { + this.compiler = compiler; + this.mode = mode; + this.inlineAllStrings = inlineAllStrings; + } + + @Override + public void process(Node externs, Node root) { + ReferenceCollectingCallback callback = new ReferenceCollectingCallback( + compiler, new InliningBehavior(), getFilterForMode()); + callback.process(externs, root); + } + + private Predicate getFilterForMode() { + switch (mode) { + case ALL: + return Predicates.alwaysTrue(); + case LOCALS_ONLY: + return new IdentifyLocals(); + case CONSTANTS_ONLY: + return new IdentifyConstants(); + default: + throw new IllegalStateException(); + } + } + + /** + * Filters variables declared as "constant", and declares them in the outer + * declaredConstants map. + * + * In Google coding conventions, this means anything declared with @const + * or named in all caps, and initialized to an immutable value. + * CheckConsts has already verified that these are truly constants. + */ + private class IdentifyConstants implements Predicate { + @Override + public boolean apply(Var var) { + return var.isConst(); + } + } + + /** + * Filters non-global variables. + */ + private class IdentifyLocals implements Predicate { + @Override + public boolean apply(Var var) { + return var.scope.isLocal(); + } + } + + private static class AliasCandidate { + private final Var alias; + private final ReferenceCollection refInfo; + + AliasCandidate(Var alias, ReferenceCollection refInfo) { + this.alias = alias; + this.refInfo = refInfo; + } + } + + /** + * Builds up information about nodes in each scope. When exiting the + * scope, inspects all variables in that scope, and inlines any + * that we can. + */ + private class InliningBehavior implements Behavior { + + /** + * A list of variables that should not be inlined, because their + * reference information is out of sync with the state of the AST. + */ + private final Set staleVars = Sets.newHashSet(); + + /** + * Stored possible aliases of variables that never change, with + * all the reference info about those variables. Hashed by the NAME + * node of the variable being aliased. + */ + final Map aliasCandidates = Maps.newHashMap(); + + @Override + public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) { + collectAliasCandidates(t, referenceMap); + doInlinesForScope(t, referenceMap); + } + + /** + * If any of the variables are well-defined and alias other variables, + * mark them as aliasing candidates. + */ + private void collectAliasCandidates(NodeTraversal t, + ReferenceMap referenceMap) { + if (mode != Mode.CONSTANTS_ONLY) { + for (Iterator it = t.getScope().getVars(); it.hasNext();) { + Var v = it.next(); + ReferenceCollection referenceInfo = referenceMap.getReferences(v); + + // NOTE(nicksantos): Don't handle variables that are never used. + // The tests are much easier to write if you don't, and there's + // another pass that handles unused variables much more elegantly. + if (referenceInfo != null && referenceInfo.references.size() >= 2 && + referenceInfo.isWellDefined() && + referenceInfo.isAssignedOnceInLifetime()) { + Reference init = referenceInfo.getInitializingReference(); + Node value = init.getAssignedValue(); + if (value != null && value.isName()) { + aliasCandidates.put(value, new AliasCandidate(v, referenceInfo)); + } + } + } + } + } + + /** + * For all variables in this scope, see if they are only used once. + * If it looks safe to do so, inline them. + */ + private void doInlinesForScope(NodeTraversal t, ReferenceMap referenceMap) { + + boolean maybeModifiedArguments = + maybeEscapedOrModifiedArguments(t.getScope(), referenceMap); + for (Iterator it = t.getScope().getVars(); it.hasNext();) { + Var v = it.next(); + + ReferenceCollection referenceInfo = referenceMap.getReferences(v); + + // referenceInfo will be null if we're in constants-only mode + // and the variable is not a constant. + if (referenceInfo == null || isVarInlineForbidden(v)) { + // Never try to inline exported variables or variables that + // were not collected or variables that have already been inlined. + continue; + } else if (isInlineableDeclaredConstant(v, referenceInfo)) { + Reference init = referenceInfo.getInitializingReferenceForConstants(); + Node value = init.getAssignedValue(); + inlineDeclaredConstant(v, value, referenceInfo.references); + staleVars.add(v); + } else if (mode == Mode.CONSTANTS_ONLY) { + // If we're in constants-only mode, don't run more aggressive + // inlining heuristics. See InlineConstantsTest. + continue; + } else { + inlineNonConstants(v, referenceInfo, maybeModifiedArguments); + } + } + } + + private boolean maybeEscapedOrModifiedArguments( + Scope scope, ReferenceMap referenceMap) { + if (scope.isLocal()) { + Var arguments = scope.getArgumentsVar(); + ReferenceCollection refs = referenceMap.getReferences(arguments); + if (refs != null && !refs.references.isEmpty()) { + for (Reference ref : refs.references) { + Node refNode = ref.getNode(); + Node refParent = ref.getParent(); + // Any reference that is not a read of the arguments property + // consider a escape of the arguments object. + if (!(NodeUtil.isGet(refParent) + && refNode == ref.getParent().getFirstChild() + && !isLValue(refParent))) { + return true; + } + } + } + } + return false; + } + + private boolean isLValue(Node n) { + Node parent = n.getParent(); + return (parent.isInc() + || parent.isDec() + || (NodeUtil.isAssignmentOp(parent) + && parent.getFirstChild() == n)); + } + + private void inlineNonConstants( + Var v, ReferenceCollection referenceInfo, + boolean maybeModifiedArguments) { + int refCount = referenceInfo.references.size(); + Reference declaration = referenceInfo.references.get(0); + Reference init = referenceInfo.getInitializingReference(); + int firstRefAfterInit = (declaration == init) ? 2 : 3; + + if (refCount > 1 && + isImmutableAndWellDefinedVariable(v, referenceInfo)) { + // if the variable is referenced more than once, we can only + // inline it if it's immutable and never defined before referenced. + Node value; + if (init != null) { + value = init.getAssignedValue(); + } else { + // Create a new node for variable that is never initialized. + Node srcLocation = declaration.getNode(); + value = NodeUtil.newUndefinedNode(srcLocation); + } + Preconditions.checkNotNull(value); + inlineWellDefinedVariable(v, value, referenceInfo.references); + staleVars.add(v); + } else if (refCount == firstRefAfterInit) { + // The variable likely only read once, try some more + // complex inlining heuristics. + Reference reference = referenceInfo.references.get( + firstRefAfterInit - 1); + if (canInline(declaration, init, reference)) { + inline(v, declaration, init, reference); + staleVars.add(v); + } + } else if (declaration != init && refCount == 2) { + if (isValidDeclaration(declaration) && isValidInitialization(init)) { + // The only reference is the initialization, remove the assignment and + // the variable declaration. + Node value = init.getAssignedValue(); + Preconditions.checkNotNull(value); + inlineWellDefinedVariable(v, value, referenceInfo.references); + staleVars.add(v); + } + } + + // If this variable was not inlined normally, check if we can + // inline an alias of it. (If the variable was inlined, then the + // reference data is out of sync. We're better off just waiting for + // the next pass.) + if (!maybeModifiedArguments && + !staleVars.contains(v) && referenceInfo.isWellDefined() && + referenceInfo.isAssignedOnceInLifetime()) { + List refs = referenceInfo.references; + for (int i = 1 /* start from a read */; i < refs.size(); i++) { + Node nameNode = refs.get(i).getNode(); + if (aliasCandidates.containsKey(nameNode)) { + AliasCandidate candidate = aliasCandidates.get(nameNode); + if (!staleVars.contains(candidate.alias) && + !isVarInlineForbidden(candidate.alias)) { + Reference aliasInit; + aliasInit = candidate.refInfo.getInitializingReference(); + Node value = aliasInit.getAssignedValue(); + Preconditions.checkNotNull(value); + inlineWellDefinedVariable(candidate.alias, + value, + candidate.refInfo.references); + staleVars.add(candidate.alias); + } + } + } + } + } + + /** + * If there are any variable references in the given node tree, blacklist + * them to prevent the pass from trying to inline the variable. + */ + private void blacklistVarReferencesInTree(Node root, Scope scope) { + for (Node c = root.getFirstChild(); c != null; c = c.getNext()) { + blacklistVarReferencesInTree(c, scope); + } + + if (root.isName()) { + staleVars.add(scope.getVar(root.getString())); + } + } + + /** + * Whether the given variable is forbidden from being inlined. + */ + private boolean isVarInlineForbidden(Var var) { + // A variable may not be inlined if: + // 1) The variable is exported, + // 2) A reference to the variable has been inlined. We're downstream + // of the mechanism that creates variable references, so we don't + // have a good way to update the reference. Just punt on it. + // 3) Don't inline the special RENAME_PROPERTY_FUNCTION_NAME + return var.isExtern() + || compiler.getCodingConvention().isExported(var.name) + || RenameProperties.RENAME_PROPERTY_FUNCTION_NAME.equals(var.name) + || staleVars.contains(var); + } + + /** + * Do the actual work of inlining a single declaration into a single + * reference. + */ + private void inline(Var v, Reference declaration, + Reference init, Reference reference) { + Node value = init.getAssignedValue(); + Preconditions.checkState(value != null); + // Check for function declarations before the value is moved in the AST. + boolean isFunctionDeclaration = NodeUtil.isFunctionDeclaration(value); + + inlineValue(v, reference, value.detachFromParent()); + if (declaration != init) { + Node expressRoot = init.getGrandparent(); + Preconditions.checkState(expressRoot.isExprResult()); + NodeUtil.removeChild(expressRoot.getParent(), expressRoot); + } + + // Function declarations have already been removed. + if (!isFunctionDeclaration) { + removeDeclaration(declaration); + } else { + compiler.reportCodeChange(); + } + } + + /** + * Inline an immutable variable into all of its references. + */ + private void inlineWellDefinedVariable(Var v, Node value, + List refSet) { + Reference decl = refSet.get(0); + for (int i = 1; i < refSet.size(); i++) { + inlineValue(v, refSet.get(i), value.cloneTree()); + } + removeDeclaration(decl); + } + + /** + * Inline a declared constant. + */ + private void inlineDeclaredConstant(Var v, Node value, + List refSet) { + // Replace the references with the constant value + Reference decl = null; + + for (Reference r : refSet) { + if (r.getNode() == v.getNameNode()) { + decl = r; + } else { + inlineValue(v, r, value.cloneTree()); + } + } + + removeDeclaration(decl); + } + + /** + * Remove the given VAR declaration. + */ + private void removeDeclaration(Reference declaration) { + Node varNode = declaration.getParent(); + Node grandparent = declaration.getGrandparent(); + + varNode.removeChild(declaration.getNode()); + + // Remove var node if empty + if (!varNode.hasChildren()) { + Preconditions.checkState(varNode.isVar()); + NodeUtil.removeChild(grandparent, varNode); + } + + compiler.reportCodeChange(); + } + + /** + * Replace the given reference with the given value node. + * + * @param v The variable that's referenced. + * @param ref The reference to replace. + * @param value The node tree to replace it with. This tree should be safe + * to re-parent. + */ + private void inlineValue(Var v, Reference ref, Node value) { + if (ref.isSimpleAssignmentToName()) { + // This is the initial assignment. + ref.getGrandparent().replaceChild(ref.getParent(), value); + } else { + ref.getParent().replaceChild(ref.getNode(), value); + } + + blacklistVarReferencesInTree(value, v.scope); + compiler.reportCodeChange(); + } + + /** + * Determines whether the given variable is declared as a constant + * and may be inlined. + */ + private boolean isInlineableDeclaredConstant(Var var, + ReferenceCollection refInfo) { + if (!identifyConstants.apply(var)) { + return false; + } + + if (!refInfo.isAssignedOnceInLifetime()) { + return false; + } + + Reference init = refInfo.getInitializingReferenceForConstants(); + if (init == null) { + return false; + } + + Node value = init.getAssignedValue(); + if (value == null) { + // This constant is either externally defined or initialized indirectly + // (e.g. in an function expression used to hide + // temporary variables), so the constant is ineligible for inlining. + return false; + } + + // Is the constant's value immutable? + if (!NodeUtil.isImmutableValue(value)) { + return false; + } + + // Determine if we should really inline a String or not. + return !value.isString() || + isStringWorthInlining(var, refInfo.references); + } + + /** + * Compute whether the given string is worth inlining. + */ + private boolean isStringWorthInlining(Var var, List refs) { + if (!inlineAllStrings && !var.isDefine()) { + int len = var.getInitialValue().getString().length() + "''".length(); + + // if not inlined: var xx="value"; .. xx .. xx .. + // The 4 bytes per reference is just a heuristic: + // 2 bytes per var name plus maybe 2 bytes if we don't inline, e.g. + // in the case of "foo " + CONST + " bar" + int noInlineBytes = "var xx=;".length() + len + + 4 * (refs.size() - 1); + + // if inlined: + // I'm going to assume that half of the quotes will be eliminated + // thanks to constant folding, therefore I subtract 1 (2/2=1) from + // the string length. + int inlineBytes = (len - 1) * (refs.size() - 1); + + // Not inlining if doing so uses more bytes, or this constant is being + // defined. + return noInlineBytes >= inlineBytes; + } + + return true; + } + + /** + * @return true if the provided reference and declaration can be safely + * inlined according to our criteria + */ + private boolean canInline( + Reference declaration, + Reference initialization, + Reference reference) { + if (!isValidDeclaration(declaration) + || !isValidInitialization(initialization) + || !isValidReference(reference)) { + return false; + } + + // If the value is read more than once, skip it. + // VAR declarations and EXPR_RESULT don't need the value, but other + // ASSIGN expressions parents do. + if (declaration != initialization && + !initialization.getGrandparent().isExprResult()) { + return false; + } + + // Be very conservative and do no cross control structures or + // scope boundaries + if (declaration.getBasicBlock() != initialization.getBasicBlock() + || declaration.getBasicBlock() != reference.getBasicBlock()) { + return false; + } + + // Do not inline into a call node. This would change + // the context in which it was being called. For example, + // var a = b.c; + // a(); + // should not be inlined, because it calls a in the context of b + // rather than the context of the window. + // var a = b.c; + // f(a) + // is OK. + Node value = initialization.getAssignedValue(); + Preconditions.checkState(value != null); + if (value.isGetProp() + && reference.getParent().isCall() + && reference.getParent().getFirstChild() == reference.getNode()) { + return false; + } + + if (value.isFunction()) { + Node callNode = reference.getParent(); + if (reference.getParent().isCall()) { + CodingConvention convention = compiler.getCodingConvention(); + // Bug 2388531: Don't inline subclass definitions into class defining + // calls as this confused class removing logic. + SubclassRelationship relationship = + convention.getClassesDefinedByCall(callNode); + if (relationship != null) { + return false; + } + + // issue 668: Don't inline singleton getter methods + // calls as this confused class removing logic. + if (convention.getSingletonGetterClassName(callNode) != null) { + return false; + } + } + } + + return canMoveAggressively(value) || + canMoveModerately(initialization, reference); + } + + /** + * If the value is a literal, we can cross more boundaries to inline it. + */ + private boolean canMoveAggressively(Node value) { + // Function expressions and other mutable objects can move within + // the same basic block. + return NodeUtil.isLiteralValue(value, true) + || value.isFunction(); + } + + /** + * If the value of a variable is not constant, then it may read or modify + * state. Therefore it cannot be moved past anything else that may modify + * the value being read or read values that are modified. + */ + private boolean canMoveModerately( + Reference initialization, + Reference reference) { + // Check if declaration can be inlined without passing + // any side-effect causing nodes. + Iterator it; + if (initialization.getParent().isVar()) { + it = NodeIterators.LocalVarMotion.forVar( + initialization.getNode(), // NAME + initialization.getParent(), // VAR + initialization.getGrandparent()); // VAR container + } else if (initialization.getParent().isAssign()) { + Preconditions.checkState( + initialization.getGrandparent().isExprResult()); + it = NodeIterators.LocalVarMotion.forAssign( + initialization.getNode(), // NAME + initialization.getParent(), // ASSIGN + initialization.getGrandparent(), // EXPR_RESULT + initialization.getGrandparent().getParent()); // EXPR container + } else { + throw new IllegalStateException("Unexpected initialization parent " + + initialization.getParent().toStringTree()); + } + Node targetName = reference.getNode(); + while (it.hasNext()) { + Node curNode = it.next(); + if (curNode == targetName) { + return true; + } + } + + return false; + } + + /** + * @return true if the reference is a normal VAR or FUNCTION declaration. + */ + private boolean isValidDeclaration(Reference declaration) { + return (declaration.getParent().isVar() + && !declaration.getGrandparent().isFor()) + || NodeUtil.isFunctionDeclaration(declaration.getParent()); + } + + /** + * @return Whether there is a initial value. + */ + private boolean isValidInitialization(Reference initialization) { + if (initialization == null) { + return false; + } else if (initialization.isDeclaration()) { + // The reference is a FUNCTION declaration or normal VAR declaration + // with a value. + if (!NodeUtil.isFunctionDeclaration(initialization.getParent()) + && initialization.getNode().getFirstChild() == null) { + return false; + } + } else { + Node parent = initialization.getParent(); + Preconditions.checkState( + parent.isAssign() + && parent.getFirstChild() == initialization.getNode()); + } + + Node n = initialization.getAssignedValue(); + if (n.isFunction()) { + return compiler.getCodingConvention().isInlinableFunction(n); + } + + return true; + } + + /** + * @return true if the reference is a candidate for inlining + */ + private boolean isValidReference(Reference reference) { + return !reference.isDeclaration() && !reference.isLvalue(); + } + + /** + * Determines whether the reference collection describes a variable that + * is initialized to an immutable value, never modified, and defined before + * every reference. + */ + private boolean isImmutableAndWellDefinedVariable(Var v, + ReferenceCollection refInfo) { + List refSet = refInfo.references; + int startingReadRef = 1; + Reference refDecl = refSet.get(0); + if (!isValidDeclaration(refDecl)) { + return false; + } + + boolean isNeverAssigned = refInfo.isNeverAssigned(); + // For values that are never assigned, only the references need to be + // checked. + if (!isNeverAssigned) { + Reference refInit = refInfo.getInitializingReference(); + if (!isValidInitialization(refInit)) { + return false; + } + + if (refDecl != refInit) { + Preconditions.checkState(refInit == refSet.get(1)); + startingReadRef = 2; + } + + if (!refInfo.isWellDefined()) { + return false; + } + + Node value = refInit.getAssignedValue(); + Preconditions.checkNotNull(value); + + boolean isImmutableValueWorthInlining = + NodeUtil.isImmutableValue(value) && + (!value.isString() || + isStringWorthInlining(v, refInfo.references)); + boolean isInlinableThisAlias = + value.isThis() && + !refInfo.isEscaped(); + if (!isImmutableValueWorthInlining && !isInlinableThisAlias) { + return false; + } + } + + for (int i = startingReadRef; i < refSet.size(); i++) { + Reference ref = refSet.get(i); + if (!isValidReference(ref)) { + return false; + } + } + + return true; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InstrumentFunctions.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InstrumentFunctions.java new file mode 100644 index 0000000..330e767 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InstrumentFunctions.java @@ -0,0 +1,345 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.protobuf.TextFormat; + +import java.io.IOException; +import java.util.List; + +/** + * Instruments functions for when functions first get called and defined. + * + * This pass be used to instrument code to: + * 1. Gather statistics from real users in the wild. + * 2. Incorporate utilization statistics into Selenium tests + * 3. Access utilization statistics from an app's debug UI + * + * By parametrizing the whole instrumentation process we expect to be + * able to support a wide variety of use cases and minimize the cost + * of developing new instrumentation schemes. + * + * TODO(user): This pass currently runs just before the variable and + * property renaming near the end of the optimization pass. I think + * Mark put it there to minimize the difference between the code + * generated with/without instrumentation; instrumentation makes + * several optimization passes do less, for example inline functions. + * + * My opinion is that we want utilization/profiling information for + * all function. This pass should run before most passes that modify + * the AST (exception being the localization pass, which makes + * assumptions about the structure of the AST). We should move the + * pass up, list inlined functions or give clients the option to + * instrument before or after optimization. + * + */ +class InstrumentFunctions implements CompilerPass { + + private final AbstractCompiler compiler; + private final FunctionNames functionNames; + private final String templateFilename; + private final String appNameStr; + private final String initCodeSource; + private final String definedFunctionName; + private final String reportFunctionName; + private final String reportFunctionExitName; + private final String appNameSetter; + private final List declarationsToRemove; + + /** + * Creates an instrument functions compiler pass. + * + * @param compiler The JSCompiler + * @param functionNames Assigned function identifiers. + * @param templateFilename Template filename; for use during error + * reporting only. + * @param appNameStr String to pass to appNameSetter. + * @param readable Instrumentation template protobuf text. + */ + InstrumentFunctions(AbstractCompiler compiler, + FunctionNames functionNames, + String templateFilename, + String appNameStr, + Readable readable) { + this.compiler = compiler; + this.functionNames = functionNames; + this.templateFilename = templateFilename; + this.appNameStr = appNameStr; + + Instrumentation.Builder builder = Instrumentation.newBuilder(); + try { + TextFormat.merge(readable, builder); + } catch (IOException e) { + compiler.report(JSError.make(RhinoErrorReporter.PARSE_ERROR, + "Error reading instrumentation template protobuf at " + + templateFilename)); + this.initCodeSource = ""; + this.definedFunctionName = ""; + this.reportFunctionName = ""; + this.reportFunctionExitName = ""; + this.appNameSetter = ""; + this.declarationsToRemove = Lists.newArrayList(); + return; + } + + Instrumentation template = builder.build(); + + StringBuilder initCodeSourceBuilder = new StringBuilder(); + for (String line : template.getInitList()) { + initCodeSourceBuilder.append(line).append("\n"); + } + this.initCodeSource = initCodeSourceBuilder.toString(); + + this.definedFunctionName = template.getReportDefined(); + this.reportFunctionName = template.getReportCall(); + this.reportFunctionExitName = template.getReportExit(); + this.appNameSetter = template.getAppNameSetter(); + + this.declarationsToRemove = ImmutableList.copyOf( + template.getDeclarationToRemoveList()); + } + + @Override + public void process(Node externs, Node root) { + Node initCode = null; + if (!initCodeSource.isEmpty()) { + Node initCodeRoot = compiler.parseSyntheticCode( + templateFilename + ":init", initCodeSource); + if (initCodeRoot != null && initCodeRoot.getFirstChild() != null) { + initCode = initCodeRoot.removeChildren(); + } else { + return; // parse failure + } + } + + NodeTraversal.traverse(compiler, root, + new RemoveCallback(declarationsToRemove)); + NodeTraversal.traverse(compiler, root, new InstrumentCallback()); + + if (!appNameSetter.isEmpty()) { + Node call = IR.call( + IR.name(appNameSetter), + IR.string(appNameStr)); + call.putBooleanProp(Node.FREE_CALL, true); + Node expr = IR.exprResult(call); + + Node addingRoot = compiler.getNodeForCodeInsertion(null); + addingRoot.addChildrenToFront(expr); + compiler.reportCodeChange(); + } + + if (initCode != null) { + Node addingRoot = compiler.getNodeForCodeInsertion(null); + addingRoot.addChildrenToFront(initCode); + compiler.reportCodeChange(); + } + } + + /** + * The application must refer to these variables to output them so the + * application must also declare these variables for the first + * {@link VarCheck} pass. These declarations must be removed before the + * second {@link VarCheck} pass. Otherwise, the second pass would warn about + * duplicate declarations. + */ + private static class RemoveCallback extends AbstractPostOrderCallback { + private final List removable; + RemoveCallback(List removable) { + this.removable = removable; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (NodeUtil.isVarDeclaration(n)) { + if (removable.contains(n.getString())) { + parent.removeChild(n); + if (!parent.hasChildren()) { + parent.getParent().removeChild(parent); + } + } + } + } + } + + /** + * Traverse a function's body by instrument return sites by + * inserting calls to {@code reportFunctionExitName}. If the + * function is missing an explicit return statement in some control + * path, this pass inserts a call to {@code reportFunctionExitName} + * as the last statement in the function's body. + * + * Example: + * Input: + * function f() { + * if (pred) { + * return a; + * } + * } + * + * Template: + * reportFunctionExitName: "onExitFn" + * + * Output: + * function f() { + * if (pred) { + * return onExitFn(0, a); + * } + * onExitFn(0); + * } + * + **/ + private class InstrumentReturns implements NodeTraversal.Callback { + private final int functionId; + /** + * @param functionId Function identifier computed by FunctionNames; + * used as first argument to {@code reportFunctionExitName} + * {@code reportFunctionExitName} must be a 2 argument function that + * returns it's second argument. + */ + InstrumentReturns(int functionId) { + this.functionId = functionId; + } + + /** + * @param body body of function with id == this.functionId + */ + void process(Node body) { + NodeTraversal.traverse(compiler, body, this); + + if (!allPathsReturn(body)) { + Node call = newReportFunctionExitNode(); + Node expr = IR.exprResult(call); + body.addChildToBack(expr); + compiler.reportCodeChange(); + } + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + return !n.isFunction(); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isReturn()) { + return; + } + + Node call = newReportFunctionExitNode(); + Node returnRhs = n.removeFirstChild(); + if (returnRhs != null) { + call.addChildToBack(returnRhs); + } + n.addChildToFront(call); + compiler.reportCodeChange(); + } + + private Node newReportFunctionExitNode() { + Node call = IR.call( + IR.name(reportFunctionExitName), + IR.number(functionId)); + call.putBooleanProp(Node.FREE_CALL, true); + return call; + } + + /** + * @returns true if all paths from block must exit with an explicit return. + */ + private boolean allPathsReturn(Node block) { + // Computes the control flow graph. + ControlFlowAnalysis cfa = new ControlFlowAnalysis( + compiler, false, false); + cfa.process(null, block); + ControlFlowGraph cfg = cfa.getCfg(); + + Node returnPathsParent = cfg.getImplicitReturn().getValue(); + for (DiGraphNode pred : + cfg.getDirectedPredNodes(returnPathsParent)) { + Node n = pred.getValue(); + if (!n.isReturn()) { + return false; + } + } + return true; + } + } + + private class InstrumentCallback extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isFunction()) { + return; + } + + int id = functionNames.getFunctionId(n); + if (id < 0) { + // Function node was added during compilation; don't instrument. + return; + } + + if (!reportFunctionName.isEmpty()) { + Node body = n.getFirstChild().getNext().getNext(); + Node call = IR.call( + IR.name(reportFunctionName), + IR.number(id)); + call.putBooleanProp(Node.FREE_CALL, true); + Node expr = IR.exprResult(call); + body.addChildToFront(expr); + compiler.reportCodeChange(); + } + + if (!reportFunctionExitName.isEmpty()) { + Node body = n.getFirstChild().getNext().getNext(); + (new InstrumentReturns(id)).process(body); + } + + if (!definedFunctionName.isEmpty()) { + Node call = IR.call( + IR.name(definedFunctionName), + IR.number(id)); + call.putBooleanProp(Node.FREE_CALL, true); + Node expr = NodeUtil.newExpr(call); + + Node addingRoot = null; + if (NodeUtil.isFunctionDeclaration(n)) { + JSModule module = t.getModule(); + addingRoot = compiler.getNodeForCodeInsertion(module); + addingRoot.addChildToFront(expr); + } else { + Node beforeChild = n; + for (Node ancestor : n.getAncestors()) { + int type = ancestor.getType(); + if (type == Token.BLOCK || type == Token.SCRIPT) { + addingRoot = ancestor; + break; + } + beforeChild = ancestor; + } + addingRoot.addChildBefore(expr, beforeChild); + } + compiler.reportCodeChange(); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InvocationsCallback.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InvocationsCallback.java new file mode 100644 index 0000000..554d161 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/InvocationsCallback.java @@ -0,0 +1,69 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.Node; + +/** + * Traversal callback that finds method invocations of the form + * + *
      + * call
      + *   getprop
      + *     ...
      + *     string
      + *   ...
      + * 
      + * + * and invokes a method defined by subclasses for processing these invocations. + * + */ +abstract class InvocationsCallback extends AbstractPostOrderCallback { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isCall()) { + return; + } + + Node function = n.getFirstChild(); + + if (!function.isGetProp()) { + return; + } + + Node nameNode = function.getFirstChild().getNext(); + + // Don't care about numerical or variable indexes + if (!nameNode.isString()) { + return; + } + + visit(t, n, parent, nameNode.getString()); + } + + /** + * Called for each callnode that is a method invocation. + * + * @param callNode node of type call + * @param parent parent of callNode + * @param callName name of method invoked by first child of call + */ + abstract void visit(NodeTraversal t, Node callNode, Node parent, + String callName); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JSError.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JSError.java new file mode 100644 index 0000000..105e0f7 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JSError.java @@ -0,0 +1,291 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +import javax.annotation.Nullable; + +/** + * Compile error description + * + */ +public class JSError { + /** A type of the error */ + private final DiagnosticType type; + + /** Description of the error */ + public final String description; + + /** Name of the source */ + public final String sourceName; + + /** Node where the warning occurred. */ + final Node node; + + /** Line number of the source */ + public final int lineNumber; + + /** @deprecated Use #getDefaultLevel */ + @Deprecated + public final CheckLevel level; + + private final CheckLevel defaultLevel; + + // character number + private final int charno; + + // + // JSError.make - static factory methods for creating JSError objects + // + // The general form of the arguments is + // + // [source location] [level] DiagnosticType [argument ...] + // + // This order echos a typical command line diagnostic. Source location + // arguments are arranged to be sources of information in the order + // file-line-column. + // + // If the level is not given, it is taken from the level of the + // DiagnosticType. + + + /** + * Creates a JSError with no source information + * + * @param type The DiagnosticType + * @param arguments Arguments to be incorporated into the message + */ + public static JSError make(DiagnosticType type, String... arguments) { + return new JSError(null, null, -1, -1, type, null, arguments); + } + + /** + * Creates a JSError at a given source location + * + * @param sourceName The source file name + * @param lineno Line number with source file, or -1 if unknown + * @param charno Column number within line, or -1 for whole line. + * @param type The DiagnosticType + * @param arguments Arguments to be incorporated into the message + */ + public static JSError make(String sourceName, int lineno, int charno, + DiagnosticType type, String... arguments) { + return new JSError(sourceName, null, lineno, charno, type, null, arguments); + } + + /** + * Creates a JSError at a given source location + * + * @param sourceName The source file name + * @param lineno Line number with source file, or -1 if unknown + * @param charno Column number within line, or -1 for whole line. + * @param type The DiagnosticType + * @param arguments Arguments to be incorporated into the message + */ + public static JSError make(String sourceName, int lineno, int charno, + CheckLevel level, DiagnosticType type, String... arguments) { + return new JSError( + sourceName, null, lineno, charno, type, level, arguments); + } + + /** + * Creates a JSError from a file and Node position. + * + * @param sourceName The source file name + * @param n Determines the line and char position within the source file name + * @param type The DiagnosticType + * @param arguments Arguments to be incorporated into the message + */ + public static JSError make(String sourceName, Node n, + DiagnosticType type, String... arguments) { + return new JSError(sourceName, n, type, arguments); + } + + /** + * Creates a JSError from a file and Node position. + * + * @param n Determines the line and char position and source file name + * @param type The DiagnosticType + * @param arguments Arguments to be incorporated into the message + */ + public static JSError make(Node n, DiagnosticType type, String... arguments) { + return new JSError(n.getSourceFileName(), n, type, arguments); + } + + /** + * Creates a JSError from a file and Node position. + * + * @param sourceName The source file name + * @param n Determines the line and char position within the source file name + * @param type The DiagnosticType + * @param arguments Arguments to be incorporated into the message + */ + public static JSError make(String sourceName, Node n, CheckLevel level, + DiagnosticType type, String... arguments) { + + return new JSError(sourceName, n, n.getLineno(), n.getCharno(), type, level, + arguments); + } + + // + // JSError constructors + // + + /** + * Creates a JSError at a CheckLevel for a source file location. + * Private to avoid any entanglement with code outside of the compiler. + */ + private JSError( + String sourceName, @Nullable Node node, int lineno, int charno, + DiagnosticType type, CheckLevel level, String... arguments) { + this.type = type; + this.node = node; + this.description = type.format.format(arguments); + this.lineNumber = lineno; + this.charno = charno; + this.sourceName = sourceName; + this.defaultLevel = level == null ? type.level : level; + this.level = level == null ? type.level : level; + } + + /** + * Creates a JSError for a source file location. Private to avoid + * any entanglement with code outside of the compiler. + */ + private JSError(String sourceName, @Nullable Node node, + DiagnosticType type, String... arguments) { + this(sourceName, + node, + (node != null) ? node.getLineno() : -1, + (node != null) ? node.getCharno() : -1, + type, null, arguments); + } + + public DiagnosticType getType() { + return type; + } + + /** + * Format a message at the given level. + * + * @return the formatted message or {@code null} + */ + public String format(CheckLevel level, MessageFormatter formatter) { + switch (level) { + case ERROR: + return formatter.formatError(this); + + case WARNING: + return formatter.formatWarning(this); + + default: + return null; + } + } + + @Override + public String toString() { + // TODO(user): remove custom toString. + return type.key + ". " + description + " at " + + (sourceName != null && sourceName.length() > 0 ? + sourceName : "(unknown source)") + " line " + + (lineNumber != -1 ? String.valueOf(lineNumber) : "(unknown line)") + + " : " + (charno != -1 ? String.valueOf(charno) : "(unknown column)"); + } + + /** + * Get the character number. + */ + public int getCharno() { + return charno; + } + + /** + * Get the line number. One-based. + */ + public int getLineNumber() { + return lineNumber; + } + + /** + * @return the offset of the region the Error applies to, or -1 if the offset + * is unknown. + */ + public int getNodeSourceOffset() { + return node != null ? node.getSourceOffset() : -1; + } + + /** + * @return the length of the region the Error applies to, or 0 if the length + * is unknown. + */ + public int getNodeLength() { + return node != null ? node.getLength() : 0; + } + + /** The default level, before any of the WarningsGuards are applied. */ + public CheckLevel getDefaultLevel() { + return defaultLevel; + } + + @Override + public boolean equals(Object o) { + // Generated by Intellij IDEA + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + JSError jsError = (JSError) o; + + if (charno != jsError.charno) { + return false; + } + if (lineNumber != jsError.lineNumber) { + return false; + } + if (!description.equals(jsError.description)) { + return false; + } + if (defaultLevel != jsError.defaultLevel) { + return false; + } + if (sourceName != null ? !sourceName.equals(jsError.sourceName) + : jsError.sourceName != null) { + return false; + } + if (!type.equals(jsError.type)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + // Generated by Intellij IDEA + int result = type.hashCode(); + result = 31 * result + description.hashCode(); + result = 31 * result + (sourceName != null ? sourceName.hashCode() : 0); + result = 31 * result + lineNumber; + result = 31 * result + defaultLevel.hashCode(); + result = 31 * result + charno; + return result; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JSModule.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JSModule.java new file mode 100644 index 0000000..9f2c194 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JSModule.java @@ -0,0 +1,304 @@ +/* + * Copyright 2005 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.deps.DependencyInfo; +import com.google.javascript.jscomp.deps.SortedDependencies; +import com.google.javascript.jscomp.deps.SortedDependencies.CircularDependencyException; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * A JavaScript module has a unique name, consists of a list of compiler inputs, + * and can depend on other modules. + * + */ +public class JSModule implements DependencyInfo, Serializable { + private static final long serialVersionUID = 1; + + static final DiagnosticType CIRCULAR_DEPENDENCY_ERROR = + DiagnosticType.error("JSC_CIRCULAR_DEP", + "Circular dependency detected: {0}"); + + /** Module name */ + private final String name; + + /** Source code inputs */ + private final List inputs = new ArrayList(); + + /** Modules that this module depends on */ + private final List deps = new ArrayList(); + + private int depth; + /** + * Creates an instance. + * + * @param name A unique name for the module + */ + public JSModule(String name) { + this.name = name; + this.depth = -1; + } + + /** Gets the module name. */ + @Override + public String getName() { + return name; + } + + @Override + public List getProvides() { + return ImmutableList.of(name); + } + + @Override + public List getRequires() { + ImmutableList.Builder builder = ImmutableList.builder(); + for (JSModule m : deps) { + builder.add(m.getName()); + } + return builder.build(); + } + + @Override + public String getPathRelativeToClosureBase() { + throw new UnsupportedOperationException(); + } + + /** Adds a source file input to this module. */ + public void add(SourceFile file) { + add(new CompilerInput(file)); + } + + /** Adds a source file input to this module. */ + public void addFirst(SourceFile file) { + addFirst(new CompilerInput(file)); + } + + /** Adds a source code input to this module. */ + public void add(CompilerInput input) { + inputs.add(input); + input.setModule(this); + } + + /** + * Adds a source code input to this module. Call only if the input might + * already be associated with a module. Otherwise, use + * add(CompilerInput input). + */ + void addAndOverrideModule(CompilerInput input) { + inputs.add(input); + input.overrideModule(this); + } + + /** Adds a source code input to this module. */ + public void addFirst(CompilerInput input) { + inputs.add(0, input); + input.setModule(this); + } + + /** Adds a source code input to this module directly after other. */ + public void addAfter(CompilerInput input, CompilerInput other) { + Preconditions.checkState(inputs.contains(other)); + inputs.add(inputs.indexOf(other), input); + input.setModule(this); + } + + /** Adds a dependency on another module. */ + public void addDependency(JSModule dep) { + Preconditions.checkNotNull(dep); + Preconditions.checkState(dep != this); + deps.add(dep); + } + + /** Removes an input from this module. */ + public void remove(CompilerInput input) { + input.setModule(null); + inputs.remove(input); + } + + /** Removes all of the inputs from this module. */ + public void removeAll() { + for (CompilerInput input : inputs) { + input.setModule(null); + } + inputs.clear(); + } + + /** + * Gets the list of modules that this module depends on. + * + * @return A list that may be empty but not null + */ + public List getDependencies() { + return deps; + } + + /** + * Gets the names of the modules that this module depends on, + * sorted alphabetically. + */ + List getSortedDependencyNames() { + List names = Lists.newArrayList(); + for (JSModule module : getDependencies()) { + names.add(module.getName()); + } + Collections.sort(names); + return names; + } + + /** + * Returns the transitive closure of dependencies starting from the + * dependencies of this module. + */ + public Set getAllDependencies() { + Set allDeps = Sets.newHashSet(deps); + List workList = Lists.newArrayList(deps); + while (workList.size() > 0) { + JSModule module = workList.remove(workList.size() - 1); + for (JSModule dep : module.getDependencies()) { + if (allDeps.add(dep)) { + workList.add(dep); + } + } + } + return allDeps; + } + + /** Returns this module and all of its dependencies in one list. */ + public Set getThisAndAllDependencies() { + Set deps = getAllDependencies(); + deps.add(this); + return deps; + } + + /** + * Gets this module's list of source code inputs. + * + * @return A list that may be empty but not null + */ + public List getInputs() { + return inputs; + } + + /** Returns the input with the given name or null if none. */ + public CompilerInput getByName(String name) { + for (CompilerInput input : inputs) { + if (name.equals(input.getName())) { + return input; + } + } + return null; + } + + /** + * Removes any input with the given name. Returns whether any were removed. + */ + public boolean removeByName(String name) { + boolean found = false; + Iterator iter = inputs.iterator(); + while (iter.hasNext()) { + CompilerInput file = iter.next(); + if (name.equals(file.getName())) { + iter.remove(); + file.setModule(null); + found = true; + } + } + return found; + } + + /** Returns the module name (primarily for debugging). */ + @Override + public String toString() { + return name; + } + + /** + * Removes any references to nodes of the AST. This method is needed to + * allow the ASTs to be garbage collected if the modules are kept around. + */ + public void clearAsts() { + for (CompilerInput input : inputs) { + input.clearAst(); + } + } + + /** + * Puts the JS files into a topologically sorted order by their dependencies. + */ + public void sortInputsByDeps(Compiler compiler) { + // Set the compiler, so that we can parse requires/provides and report + // errors properly. + for (CompilerInput input : inputs) { + input.setCompiler(compiler); + } + + // Sort the JSModule in this order. + try { + List sortedList = + (new SortedDependencies( + Collections.unmodifiableList(inputs))) + .getSortedList(); + inputs.clear(); + inputs.addAll(sortedList); + } catch (CircularDependencyException e) { + compiler.report( + JSError.make(CIRCULAR_DEPENDENCY_ERROR, e.getMessage())); + } + } + + /** + * Returns the given collection of modules in topological order. + * + * Note that this will return the modules in the same order if they are + * already sorted, and in general, will only change the order as necessary to + * satisfy the ordering dependencies. This can be important for cases where + * the modules do not properly specify all dependencies. + */ + public static JSModule[] sortJsModules(Collection modules) + throws CircularDependencyException { + // Sort the JSModule in this order. + List sortedList = (new SortedDependencies( + Lists.newArrayList(modules))).getSortedList(); + return sortedList.toArray(new JSModule[sortedList.size()]); + } + + /** + * @param dep the depth to set + */ + public void setDepth(int dep) { + this.depth = dep; + } + + /** + * @return the depth + */ + public int getDepth() { + return depth; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JSModuleGraph.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JSModuleGraph.java new file mode 100644 index 0000000..114ad2a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JSModuleGraph.java @@ -0,0 +1,497 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.LinkedListMultimap; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.deps.SortedDependencies; +import com.google.javascript.jscomp.deps.SortedDependencies.CircularDependencyException; +import com.google.javascript.jscomp.deps.SortedDependencies.MissingProvideException; +import com.google.javascript.jscomp.graph.LinkedDirectedGraph; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * A {@link JSModule} dependency graph that assigns a depth to each module and + * can answer depth-related queries about them. For the purposes of this class, + * a module's depth is defined as the number of hops in the longest path from + * the module to a module with no dependencies. + * + */ +public class JSModuleGraph { + + private List modules; + + /** + * Lists of modules at each depth. modulesByDepth.get(3) is a + * list of the modules at depth 3, for example. + */ + private List> modulesByDepth; + + /** + * dependencyMap is a cache of dependencies that makes the dependsOn + * function faster. Each map entry associates a starting + * JSModule with the set of JSModules that are transitively dependent on the + * starting module. + * + * If the cache returns null, then the entry hasn't been filled in for that + * module. + * + * dependencyMap should be filled from leaf to root so that + * getTransitiveDepsDeepestFirst can use its results directly. + */ + private Map> dependencyMap = Maps.newHashMap(); + + /** + * Creates a module graph from a list of modules in dependency order. + */ + public JSModuleGraph(JSModule[] modulesInDepOrder) { + this(ImmutableList.copyOf(modulesInDepOrder)); + } + + /** + * Creates a module graph from a list of modules in dependency order. + */ + public JSModuleGraph(List modulesInDepOrder) { + Preconditions.checkState( + modulesInDepOrder.size() == Sets.newHashSet(modulesInDepOrder).size(), + "Found duplicate modules"); + modules = ImmutableList.copyOf(modulesInDepOrder); + modulesByDepth = Lists.newArrayList(); + + for (JSModule module : modulesInDepOrder) { + int depth = 0; + for (JSModule dep : module.getDependencies()) { + int depDepth = dep.getDepth(); + if (depDepth < 0) { + throw new ModuleDependenceException(String.format( + "Modules not in dependency order: %s preceded %s", + module.getName(), dep.getName()), + module, dep); + } + depth = Math.max(depth, depDepth + 1); + } + + module.setDepth(depth); + if (depth == modulesByDepth.size()) { + modulesByDepth.add(new ArrayList()); + } + modulesByDepth.get(depth).add(module); + } + } + + /** + * Gets an iterable over all modules in dependency order. + */ + Iterable getAllModules() { + return modules; + } + + /** + * Gets the total number of modules. + */ + int getModuleCount() { + return modules.size(); + } + + /** + * Gets the root module. + */ + JSModule getRootModule() { + return Iterables.getOnlyElement(modulesByDepth.get(0)); + } + + /** + * Returns a JSON representation of the JSModuleGraph. Specifically a + * JSONArray of "Modules" where each module has a + * - "name" + * - "dependencies" (list of module names) + * - "transitive-dependencies" (list of module names, deepest first) + * - "inputs" (list of file names) + * @return List of module JSONObjects. + */ + JSONArray toJson() { + JSONArray modules = new JSONArray(); + for (JSModule module : getAllModules()) { + JSONObject node = new JSONObject(); + try { + node.put("name", module.getName()); + JSONArray deps = new JSONArray(); + node.put("dependencies", deps); + for (JSModule m : module.getDependencies()) { + deps.put(m.getName()); + } + JSONArray transitiveDeps = new JSONArray(); + node.put("transitive-dependencies", transitiveDeps); + for (JSModule m : getTransitiveDepsDeepestFirst(module)) { + transitiveDeps.put(m.getName()); + } + JSONArray inputs = new JSONArray(); + node.put("inputs", inputs); + for (CompilerInput input : module.getInputs()) { + inputs.put(input.getSourceFile().getOriginalPath()); + } + modules.put(node); + } catch (JSONException e) { + Throwables.propagate(e); + } + } + return modules; + } + + /** + * Determines whether this module depends on a given module. Note that a + * module never depends on itself, as that dependency would be cyclic. + */ + public boolean dependsOn(JSModule src, JSModule m) { + Set deps = dependencyMap.get(src); + if (deps == null) { + deps = getTransitiveDepsDeepestFirst(src); + dependencyMap.put(src, deps); + } + + return deps.contains(m); + } + + /** + * Finds the deepest common dependency of two modules, not including the two + * modules themselves. + * + * @param m1 A module in this graph + * @param m2 A module in this graph + * @return The deepest common dep of {@code m1} and {@code m2}, or null if + * they have no common dependencies + */ + JSModule getDeepestCommonDependency(JSModule m1, JSModule m2) { + int m1Depth = m1.getDepth(); + int m2Depth = m2.getDepth(); + // According our definition of depth, the result must have a strictly + // smaller depth than either m1 or m2. + for (int depth = Math.min(m1Depth, m2Depth) - 1; depth >= 0; depth--) { + List modulesAtDepth = modulesByDepth.get(depth); + // Look at the modules at this depth in reverse order, so that we use the + // original ordering of the modules to break ties (later meaning deeper). + for (int i = modulesAtDepth.size() - 1; i >= 0; i--) { + JSModule m = modulesAtDepth.get(i); + if (dependsOn(m1, m) && dependsOn(m2, m)) { + return m; + } + } + } + return null; + } + + /** + * Finds the deepest common dependency of two modules, including the + * modules themselves. + * + * @param m1 A module in this graph + * @param m2 A module in this graph + * @return The deepest common dep of {@code m1} and {@code m2}, or null if + * they have no common dependencies + */ + public JSModule getDeepestCommonDependencyInclusive( + JSModule m1, JSModule m2) { + if (m2 == m1 || dependsOn(m2, m1)) { + return m1; + } else if (dependsOn(m1, m2)) { + return m2; + } + + return getDeepestCommonDependency(m1, m2); + } + + /** Returns the deepest common dependency of the given modules. */ + public JSModule getDeepestCommonDependencyInclusive( + Collection modules) { + Iterator iter = modules.iterator(); + JSModule dep = iter.next(); + while (iter.hasNext()) { + dep = getDeepestCommonDependencyInclusive(dep, iter.next()); + } + return dep; + } + + /** + * Creates an iterable over the transitive dependencies of module {@code m} + * in a non-increasing depth ordering. The result does not include the module + * {@code m}. + * + * @param m A module in this graph + * @return The transitive dependencies of module {@code m} + */ + Set getTransitiveDepsDeepestFirst(JSModule m) { + Set deps = dependencyMap.get(m); + if (deps != null) { + return deps; + } + deps = new TreeSet(new InverseDepthComparator()); + addDeps(deps, m); + dependencyMap.put(m, deps); + return deps; + } + + /** + * Adds a module's transitive dependencies to a set. + */ + private void addDeps(Set deps, JSModule m) { + for (JSModule dep : m.getDependencies()) { + deps.add(dep); + addDeps(deps, dep); + } + } + + /** + * Replaces any files that are found multiple times with a single instance in + * the closest parent module that is common to all modules where it appears. + * + * JSCompiler normally errors if you attempt to compile modules containing the + * same file. This method can be used to remove duplicates before compiling + * to avoid such an error. + */ + public void coalesceDuplicateFiles() { + Multimap fileRefs = LinkedHashMultimap.create(); + for (JSModule module : modules) { + for (CompilerInput jsFile : module.getInputs()) { + fileRefs.put(jsFile.getName(), module); + } + } + + for (String path : fileRefs.keySet()) { + Collection refModules = fileRefs.get(path); + if (refModules.size() > 1) { + JSModule depModule = getDeepestCommonDependencyInclusive(refModules); + CompilerInput file = refModules.iterator().next().getByName(path); + for (JSModule module : refModules) { + if (module != depModule) { + module.removeByName(path); + } + } + if (!refModules.contains(depModule)) { + depModule.add(file); + } + } + } + } + + /** + * Applies a DependencyOptions in "dependency sorting" and "dependency pruning" + * mode to the given list of inputs. Returns a new list with the files sorted + * and removed. This module graph will be updated to reflect the new list. + * + * If you need more fine-grained dependency management, you should create your + * own DependencyOptions and call + * {@code manageDependencies(DependencyOptions, List)}. + * + * @param entryPoints The entry points into the program. + * Expressed as JS symbols. + * @param inputs The original list of sources. Used to ensure that the sort + * is stable. + * @throws CircularDependencyException if there is a circular dependency + * between the provides and requires. + * @throws MissingProvideException if an entry point was not provided + * by any of the inputs. + * @see DependencyOptions for more info on how this works. + */ + public List manageDependencies( + List entryPoints, + List inputs) + throws CircularDependencyException, MissingProvideException { + DependencyOptions depOptions = new DependencyOptions(); + depOptions.setDependencySorting(true); + depOptions.setDependencyPruning(true); + depOptions.setEntryPoints(entryPoints); + return manageDependencies(depOptions, inputs); + } + + /** + * Apply the dependency options to the list of sources, returning a new + * source list re-ordering and dropping files as necessary. + * This module graph will be updated to reflect the new list. + * + * @param inputs The original list of sources. Used to ensure that the sort + * is stable. + * @throws CircularDependencyException if there is a circular dependency + * between the provides and requires. + * @throws MissingProvideException if an entry point was not provided + * by any of the inputs. + * @see DependencyOptions for more info on how this works. + */ + public List manageDependencies( + DependencyOptions depOptions, + List inputs) + throws CircularDependencyException, MissingProvideException { + + SortedDependencies sorter = + new SortedDependencies(inputs); + Set entryPointInputs = Sets.newLinkedHashSet(); + if (depOptions.shouldPruneDependencies()) { + if (!depOptions.shouldDropMoochers()) { + entryPointInputs.addAll(sorter.getInputsWithoutProvides()); + } + + for (String entryPoint : depOptions.getEntryPoints()) { + entryPointInputs.add(sorter.getInputProviding(entryPoint)); + } + + CompilerInput baseJs = sorter.maybeGetInputProviding("goog"); + if (baseJs != null) { + entryPointInputs.add(baseJs); + } + } else { + entryPointInputs.addAll(inputs); + } + + // The order of inputs, sorted independently of modules. + List absoluteOrder = + sorter.getDependenciesOf(inputs, depOptions.shouldSortDependencies()); + + // Figure out which sources *must* be in each module. + ListMultimap entryPointInputsPerModule = + LinkedListMultimap.create(); + for (CompilerInput input : entryPointInputs) { + JSModule module = input.getModule(); + Preconditions.checkNotNull(module); + entryPointInputsPerModule.put(module, input); + } + + // Clear the modules of their inputs. This also nulls out + // the input's reference to its module. + for (JSModule module : getAllModules()) { + module.removeAll(); + } + + // Figure out which sources *must* be in each module, or in one + // of that module's dependencies. + for (JSModule module : entryPointInputsPerModule.keySet()) { + List transitiveClosure = + sorter.getDependenciesOf( + entryPointInputsPerModule.get(module), + depOptions.shouldSortDependencies()); + for (CompilerInput input : transitiveClosure) { + JSModule oldModule = input.getModule(); + if (oldModule == null) { + input.setModule(module); + } else { + input.setModule(null); + input.setModule( + getDeepestCommonDependencyInclusive(oldModule, module)); + } + } + } + + // All the inputs are pointing to the modules that own them. Yeah! + // Update the modules to reflect this. + for (CompilerInput input : absoluteOrder) { + JSModule module = input.getModule(); + if (module != null) { + module.add(input); + } + } + + // Now, generate the sorted result. + List result = Lists.newArrayList(); + for (JSModule module : getAllModules()) { + result.addAll(module.getInputs()); + } + + return result; + } + + LinkedDirectedGraph toGraphvizGraph() { + LinkedDirectedGraph graphViz = + LinkedDirectedGraph.create(); + for (JSModule module : getAllModules()) { + graphViz.createNode(module); + for (JSModule dep : module.getDependencies()) { + graphViz.createNode(dep); + graphViz.connect(module, "->", dep); + } + } + return graphViz; + } + + /** + * A module depth comparator that considers a deeper module to be "less than" + * a shallower module. Uses module names to consistently break ties. + */ + private class InverseDepthComparator implements Comparator { + @Override + public int compare(JSModule m1, JSModule m2) { + return depthCompare(m2, m1); + } + } + + private int depthCompare(JSModule m1, JSModule m2) { + if (m1 == m2) { + return 0; + } + int d1 = m1.getDepth(); + int d2 = m2.getDepth(); + return d1 < d2 ? -1 : d2 == d1 ? m1.getName().compareTo(m2.getName()) : 1; + } + + /* + * Exception class for declaring when the modules being fed into a + * JSModuleGraph as input aren't in dependence order, and so can't be + * processed for caching of various dependency-related queries. + */ + + protected static class ModuleDependenceException + extends IllegalArgumentException { + private static final long serialVersionUID = 1; + + private final JSModule module; + private final JSModule dependentModule; + + protected ModuleDependenceException(String message, + JSModule module, JSModule dependentModule) { + super(message); + this.module = module; + this.dependentModule = dependentModule; + } + + public JSModule getModule() { + return module; + } + + public JSModule getDependentModule() { + return dependentModule; + } + } + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JSSourceFile.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JSSourceFile.java new file mode 100644 index 0000000..98233f8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JSSourceFile.java @@ -0,0 +1,96 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Charsets; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.nio.charset.Charset; + +/** + * An abstract representation of a JavaScript source file, as input to + * JSCompiler. + * + * @author nicksantos@google.com (Nick Santos) + * @author moedinger@google.com (Andrew Moedinger) + * @deprecated JSSourceFile is an empty wrapper around SourceFile. Just + * use SourceFile directly. + */ +// TODO(nicksantos): Delete this file. +@Deprecated +public class JSSourceFile extends SourceFile implements Serializable { + private static final long serialVersionUID = 1L; + + public static JSSourceFile fromFile(String fileName, Charset charSet) { + return new JSSourceFile(SourceFile.fromFile(fileName, charSet)); + } + + public static JSSourceFile fromFile(String fileName) { + return new JSSourceFile(SourceFile.fromFile(fileName, Charsets.UTF_8)); + } + + public static JSSourceFile fromFile(File file, Charset charSet) { + return new JSSourceFile(SourceFile.fromFile(file, charSet)); + } + + public static JSSourceFile fromFile(File file) { + return new JSSourceFile(SourceFile.fromFile(file, Charsets.UTF_8)); + } + + public static JSSourceFile fromCode(String fileName, String code) { + return new JSSourceFile(SourceFile.fromCode(fileName, code)); + } + + public static JSSourceFile fromInputStream(String fileName, InputStream s) + throws IOException { + return new JSSourceFile(SourceFile.fromInputStream(fileName, s)); + } + + public static JSSourceFile fromGenerator(String fileName, + Generator generator) { + return new JSSourceFile(SourceFile.fromGenerator(fileName, generator)); + } + + + private SourceFile referenced; + + private JSSourceFile(SourceFile referenced) { + super(referenced.getName()); + this.referenced = referenced; + } + + @Override + public String getCode() throws IOException { + return referenced.getCode(); + } + + @Override + public void clearCachedSource() { + referenced.clearCachedSource(); + } + + @Override + @VisibleForTesting + String getCodeNoCache() { + return referenced.getCodeNoCache(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JoinOp.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JoinOp.java new file mode 100644 index 0000000..abeda09 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JoinOp.java @@ -0,0 +1,76 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.graph.LatticeElement; + +import java.util.List; + +/** + * Defines a way join a list of LatticeElements. + */ +interface JoinOp extends Function, L> { + + /** + * An implementation of {@code JoinOp} that makes it easy to join to + * lattice elements at a time. + */ + static abstract class BinaryJoinOp + implements JoinOp { + @Override + public final L apply(List values) { + Preconditions.checkArgument(!values.isEmpty()); + int size = values.size(); + if (size == 1) { + return values.get(0); + } else if (size == 2) { + return apply(values.get(0), values.get(1)); + } else { + int mid = computeMidPoint(size); + return apply( + apply(values.subList(0, mid)), + apply(values.subList(mid, size))); + } + } + + /** + * Creates a new lattice that will be the join of two input lattices. + * + * @return The join of {@code latticeA} and {@code latticeB}. + */ + abstract L apply(L latticeA, L latticeB); + + /** + * Finds the midpoint of a list. The function will favor two lists of + * even length instead of two lists of the same odd length. The list + * must be at least length two. + * + * @param size Size of the list. + */ + static int computeMidPoint(int size) { + int midpoint = size >>> 1; + if (size > 4) { + /* Any list longer than 4 should prefer an even split point + * over the true midpoint, so that [0,6] splits at 2, not 3. */ + midpoint &= -2; // (0xfffffffe) clears low bit so midpoint is even + } + return midpoint; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JqueryCodingConvention.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JqueryCodingConvention.java new file mode 100644 index 0000000..1069ab9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JqueryCodingConvention.java @@ -0,0 +1,63 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import java.util.Set; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.javascript.rhino.Node; + +/** + * This describes the jQuery specific JavaScript coding conventions. + */ +public class JqueryCodingConvention extends CodingConventions.Proxy { + private static final long serialVersionUID = 1L; + + public JqueryCodingConvention() { + this(CodingConventions.getDefault()); + } + + public JqueryCodingConvention(CodingConvention wrapped) { + super(wrapped); + } + + @Override + public String getGlobalObject() { + return "window"; + } + + private final static Set propertyTestFunctions = ImmutableSet.of( + "jQuery.isPlainObject", "jQuery.isFunction", "jQuery.isNumeric", + "jQuery.isEmptyObject"); + + @Override + public boolean isPropertyTestFunction(Node call) { + Preconditions.checkArgument(call.isCall()); + return propertyTestFunctions.contains( + call.getFirstChild().getQualifiedName()); + } + + private final static Set prototypeAliases = ImmutableSet.of( + "jQuery.fn", "jQuerySub.fn"); + + @Override + public boolean isPrototypeAlias(Node getProp) { + Preconditions.checkArgument(getProp.isGetProp()); + return prototypeAliases.contains(getProp.getQualifiedName()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JsAst.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JsAst.java new file mode 100644 index 0000000..1da141f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JsAst.java @@ -0,0 +1,108 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; + +import com.google.javascript.jscomp.parsing.ParserRunner; + +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.Node; +import java.io.IOException; + +import java.util.logging.Logger; + +/** + * Generates an AST for a JavaScript source file. + * + */ +public class JsAst implements SourceAst { + private static final Logger logger_ = Logger.getLogger(JsAst.class.getName()); + private static final long serialVersionUID = 1L; + + private transient InputId inputId; + private transient SourceFile sourceFile; + private String fileName; + private Node root; + + public JsAst(SourceFile sourceFile) { + this.inputId = new InputId(sourceFile.getName()); + this.sourceFile = sourceFile; + this.fileName = sourceFile.getName(); + } + + @Override + public Node getAstRoot(AbstractCompiler compiler) { + if (root == null) { + parse(compiler); + root.setInputId(inputId); + } + return root; + } + + @Override + public void clearAst() { + root = null; + // While we're at it, clear out any saved text in the source file on + // the assumption that if we're dumping the parse tree, then we probably + // assume regenerating everything else is a smart idea also. + sourceFile.clearCachedSource(); + } + + @Override + public InputId getInputId() { + return inputId; + } + + @Override + public SourceFile getSourceFile() { + return sourceFile; + } + + @Override + public void setSourceFile(SourceFile file) { + Preconditions.checkState(fileName.equals(file.getName())); + sourceFile = file; + } + + private void parse(AbstractCompiler compiler) { + try { + logger_.fine("Parsing: " + sourceFile.getName()); + ParserRunner.ParseResult result = ParserRunner.parse(sourceFile, sourceFile.getCode(), + compiler.getParserConfig(), + compiler.getDefaultErrorReporter(), + logger_); + root = result.ast; + compiler.setOldParseTree(sourceFile.getName(), result.oldAst); + } catch (IOException e) { + compiler.report( + JSError.make(AbstractCompiler.READ_ERROR, sourceFile.getName())); + } + + if (root == null || compiler.hasHaltingErrors()) { + // There was a parse error or IOException, so use a dummy block. + root = IR.script(); + } else { + compiler.prepareAst(root); + } + + // Set the source name so that the compiler passes can track + // the source file and module. + root.setStaticSourceFile(sourceFile); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JsMessage.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JsMessage.java new file mode 100644 index 0000000..1360c1b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JsMessage.java @@ -0,0 +1,670 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.Nullable; + +/** + * A representation of a translatable message in JavaScript source code. + * + *

      Instances are created using a {@link JsMessage.Builder}, + * like this: + *

      + * JsMessage m = new JsMessage.Builder(key)
      + *     .appendPart("Hi ")
      + *     .appendPlaceholderReference("firstName")
      + *     .appendPart("!")
      + *     .setDesc("A welcome message")
      + *     .build();
      + * 
      + * + */ +public class JsMessage { + + /** + * Message style that could be used for JS code parsing. + * The enum order is from most relaxed to most restricted. + */ + public enum Style { + LEGACY, // All legacy code is completely OK + RELAX, // You allowed to use legacy code but it would be reported as warn + CLOSURE; // Any legacy code is prohibited + + /** + * Calculates current messages {@link Style} based on the given arguments. + * + * @param useClosure if true then use closure style, otherwise not + * @param allowLegacyMessages if true then allow legacy messages otherwise + * not + * @return the message style based on the given arguments + */ + static Style getFromParams(boolean useClosure, + boolean allowLegacyMessages) { + if (useClosure) { + return allowLegacyMessages ? RELAX : CLOSURE; + } else { + return LEGACY; + } + } + } + + private static final String MESSAGE_REPRESENTATION_FORMAT = "{$%s}"; + + private final String key; + private final String id; + private final List parts; + private final Set placeholders; + private final String desc; + private final boolean hidden; + private final String meaning; + + private final String sourceName; + private final boolean isAnonymous; + private final boolean isExternal; + + /** + * Creates an instance. Client code should use a {@link JsMessage.Builder}. + * + * @param key a key that should identify this message in sources; typically + * it is the message's name (e.g. {@code "MSG_HELLO"}). + * @param id an id that *uniquely* identifies the message in the bundle. + * It could be either the message name or id generated from the message + * content. + * @param meaning The user-specified meaning of the message. May be null if + * the user did not specify an explicit meaning. + */ + private JsMessage(String sourceName, String key, + boolean isAnonymous, boolean isExternal, + String id, List parts, Set placeholders, + String desc, boolean hidden, String meaning) { + + Preconditions.checkState(key != null); + Preconditions.checkState(id != null); + + this.key = key; + this.id = id; + this.parts = Collections.unmodifiableList(parts); + this.placeholders = Collections.unmodifiableSet(placeholders); + this.desc = desc; + this.hidden = hidden; + this.meaning = meaning; + + this.sourceName = sourceName; + this.isAnonymous = isAnonymous; + this.isExternal = isExternal; + } + + /** + * Gets the message's sourceName. + */ + public String getSourceName() { + return sourceName; + } + + /** + * Gets the message's key, or name (e.g. {@code "MSG_HELLO"}). + */ + public String getKey() { + return key; + } + + public boolean isAnonymous() { + return isAnonymous; + } + + public boolean isExternal() { + return isExternal; + } + + /** + * Gets the message's id, or name (e.g. {@code "92430284230902938293"}). + */ + public String getId() { + return id; + } + + /** + * Gets the description associated with this message, intended to help + * translators, or null if this message has no description. + */ + public String getDesc() { + return desc; + } + + /** + * Gets the meaning annotated to the message, intended to force different + * translations. + */ + String getMeaning() { + return meaning; + } + + /** + * Gets whether this message should be hidden from volunteer translators (to + * reduce the chances of a new feature leak). + */ + public boolean isHidden() { + return hidden; + } + + /** + * Gets a read-only list of the parts of this message. Each part is either a + * {@link String} or a {@link PlaceholderReference}. + */ + public List parts() { + return parts; + } + + /** Gets a read-only set of the registered placeholders in this message. */ + public Set placeholders() { + return placeholders; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (CharSequence p : parts) { + sb.append(p.toString()); + } + return sb.toString(); + } + + /** @return false iff the message is represented by empty string. */ + public boolean isEmpty() { + for (CharSequence part : parts) { + if (part.length() > 0) { + return false; + } + } + + return true; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof JsMessage)) return false; + + JsMessage m = (JsMessage) o; + return id.equals(m.id) && + key.equals(m.key) && + isAnonymous == m.isAnonymous && + parts.equals(m.parts) && + (meaning == null ? m.meaning == null : meaning.equals(m.meaning)) && + placeholders.equals(m.placeholders) && + (desc == null ? m.desc == null : desc.equals(m.desc)) && + (sourceName == null + ? m.sourceName == null + : sourceName.equals(m.sourceName)) && + hidden == m.hidden; + } + + @Override + public int hashCode() { + int hash = key.hashCode(); + hash = 31 * hash + (isAnonymous ? 1 : 0); + hash = 31 * hash + id.hashCode(); + hash = 31 * hash + parts.hashCode(); + hash = 31 * hash + (desc != null ? desc.hashCode() : 0); + hash = 31 * hash + (hidden ? 1 : 0); + hash = 31 * hash + (sourceName != null ? sourceName.hashCode() : 0); + return hash; + } + + /** A reference to a placeholder in a translatable message. */ + public static class PlaceholderReference implements CharSequence { + + private final String name; + + PlaceholderReference(String name) { + this.name = name; + } + + @Override + public int length() { + return name.length(); + } + + @Override + public char charAt(int index) { + return name.charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return name.subSequence(start, end); + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return String.format(MESSAGE_REPRESENTATION_FORMAT, name); + } + + @Override + public boolean equals(Object o) { + return o == this || + o instanceof PlaceholderReference && + name.equals(((PlaceholderReference) o).name); + } + + @Override + public int hashCode() { + return 31 * name.hashCode(); + } + } + + /** + * Contains functionality for creating JS messages. Generates authoritative + * keys and fingerprints for a message that must stay constant over time. + * + * This implementation correctly processes unnamed messages and creates a key + * for them that looks like MSG_. + */ + public static class Builder { + + private static final Pattern MSG_EXTERNAL_PATTERN = + Pattern.compile("MSG_EXTERNAL_(\\d+)"); + + /** + * @return an external message id or null if this is not an + * external message identifier + */ + private static String getExternalMessageId(String identifier) { + Matcher m = MSG_EXTERNAL_PATTERN.matcher(identifier); + return m.matches() ? m.group(1) : null; + } + + private String key; + + private String meaning; + + private String desc; + private boolean hidden; + + private List parts = Lists.newLinkedList(); + private Set placeholders = Sets.newHashSet(); + + private String sourceName; + + public Builder() { + this(null); + } + + /** Creates an instance. */ + public Builder(String key) { + this.key = key; + } + + /** Gets the message's key (e.g. {@code "MSG_HELLO"}). */ + public String getKey() { + return key; + } + + /** + * @param key a key that should uniquely identify this message; typically + * it is the message's name (e.g. {@code "MSG_HELLO"}). + */ + public Builder setKey(String key) { + this.key = key; + return this; + } + + /** + * @param sourceName The message's sourceName. + */ + public Builder setSourceName(String sourceName) { + this.sourceName = sourceName; + return this; + } + + /** + * Appends a placeholder reference to the message + */ + public Builder appendPlaceholderReference(String name) { + Preconditions.checkNotNull(name, "Placeholder name could not be null"); + parts.add(new PlaceholderReference(name)); + placeholders.add(name); + return this; + } + + /** Appends a translatable string literal to the message. */ + public Builder appendStringPart(String part) { + Preconditions.checkNotNull(part, + "String part of the message could not be null"); + parts.add(part); + return this; + } + + /** Returns the message registered placeholders */ + public Set getPlaceholders() { + return placeholders; + } + + /** Sets the description of the message, which helps translators. */ + public Builder setDesc(String desc) { + this.desc = desc; + return this; + } + + /** + * Sets the programmer-specified meaning of this message, which + * forces this message to translate differently. + */ + public Builder setMeaning(String meaning) { + this.meaning = meaning; + return this; + } + + /** Sets whether the message should be hidden from volunteer translators. */ + public Builder setIsHidden(boolean hidden) { + this.hidden = hidden; + return this; + } + + /** Gets whether at least one part has been appended. */ + public boolean hasParts() { + return !parts.isEmpty(); + } + + public List getParts() { + return parts; + } + + public JsMessage build() { + return build(null); + } + + public JsMessage build(IdGenerator idGenerator) { + boolean isAnonymous = false; + boolean isExternal = false; + String id = null; + + if (getKey() == null) { + // Before constructing a message we need to change unnamed messages name + // to the unique one. + key = JsMessageVisitor.MSG_PREFIX + fingerprint(getParts()); + isAnonymous = true; + } + + if (!isAnonymous) { + String externalId = getExternalMessageId(key); + if (externalId != null) { + isExternal = true; + id = externalId; + } + } + + if (!isExternal) { + String defactoMeaning = meaning != null ? meaning : key; + id = idGenerator == null ? defactoMeaning : + idGenerator.generateId(defactoMeaning, parts); + } + + return new JsMessage(sourceName, key, isAnonymous, isExternal, id, parts, + placeholders, desc, hidden, meaning); + } + + /** + * Generates a compact uppercase alphanumeric text representation of a + * 63-bit fingerprint of the content parts of a message. + */ + private static String fingerprint(List messageParts) { + StringBuilder sb = new StringBuilder(); + for (CharSequence part : messageParts) { + if (part instanceof JsMessage.PlaceholderReference) { + sb.append(part.toString()); + } else { + sb.append(part); + } + } + long nonnegativeHash = Long.MAX_VALUE & Hash.hash64(sb.toString()); + return Long.toString(nonnegativeHash, 36).toUpperCase(); + } + } + + /** + * This class contains routines for hashing. + * + *

      The hash takes a byte array representing arbitrary data (a + * number, String, or Object) and turns it into a small, hopefully + * unique, number. There are additional convenience functions which + * hash int, long, and String types. + * + *

      Note: this hash has weaknesses in the two + * most-significant key bits and in the three least-significant seed + * bits. The weaknesses are small and practically speaking, will not + * affect the distribution of hash values. Still, it would be good + * practice not to choose seeds 0, 1, 2, 3, ..., n to yield n, + * independent hash functions. Use pseudo-random seeds instead. + * + *

      This code is based on the work of Craig Silverstein and Sanjay + * Ghemawat in, then forked from com.google.common. + * + *

      The original code for the hash function is courtesy + * Bob Jenkins. + * + *

      TODO: Add stream hashing functionality. + */ + final static class Hash { + private Hash() {} + + /** Default hash seed (64 bit) */ + private static final long SEED64 = + 0x2b992ddfa23249d6L; // part of pi, arbitrary + + /** Hash constant (64 bit) */ + private static final long CONSTANT64 = + 0xe08c1d668b756f82L; // part of golden ratio, arbitrary + + + /****************** + * STRING HASHING * + ******************/ + + /** + * Hash a string to a 64 bit value. The digits of pi are used for + * the hash seed. + * + * @param value the string to hash + * @return 64 bit hash value + */ + static long hash64(@Nullable String value) { + return hash64(value, SEED64); + } + + /** + * Hash a string to a 64 bit value using the supplied seed. + * + * @param value the string to hash + * @param seed the seed + * @return 64 bit hash value + */ + private static long hash64(@Nullable String value, long seed) { + if (value == null) { + return hash64(null, 0, 0, seed); + } + return hash64(value.getBytes(), seed); + } + + /** + * Hash byte array to a 64 bit value using the supplied seed. + * + * @param value the bytes to hash + * @param seed the seed + * @return 64 bit hash value + */ + private static long hash64(byte[] value, long seed) { + return hash64(value, 0, value == null ? 0 : value.length, seed); + } + + /** + * Hash byte array to a 64 bit value using the supplied seed. + * + * @param value the bytes to hash + * @param offset the starting position of value where bytes are + * used for the hash computation + * @param length number of bytes of value that are used for the + * hash computation + * @param seed the seed + * @return 64 bit hash value + */ + @SuppressWarnings("fallthrough") + private static long hash64( + byte[] value, int offset, int length, long seed) { + long a = CONSTANT64; + long b = a; + long c = seed; + int keylen; + + for (keylen = length; keylen >= 24; keylen -= 24, offset += 24) { + a += word64At(value, offset); + b += word64At(value, offset + 8); + c += word64At(value, offset + 16); + + // Mix + a -= b; a -= c; a ^= c >>> 43; + b -= c; b -= a; b ^= a << 9; + c -= a; c -= b; c ^= b >>> 8; + a -= b; a -= c; a ^= c >>> 38; + b -= c; b -= a; b ^= a << 23; + c -= a; c -= b; c ^= b >>> 5; + a -= b; a -= c; a ^= c >>> 35; + b -= c; b -= a; b ^= a << 49; + c -= a; c -= b; c ^= b >>> 11; + a -= b; a -= c; a ^= c >>> 12; + b -= c; b -= a; b ^= a << 18; + c -= a; c -= b; c ^= b >>> 22; + } + + c += length; + switch (keylen) { // deal with rest. Cases fall through + case 23: + c += ((long) value[offset + 22]) << 56; + case 22: + c += (value[offset + 21] & 0xffL) << 48; + case 21: + c += (value[offset + 20] & 0xffL) << 40; + case 20: + c += (value[offset + 19] & 0xffL) << 32; + case 19: + c += (value[offset + 18] & 0xffL) << 24; + case 18: + c += (value[offset + 17] & 0xffL) << 16; + case 17: + c += (value[offset + 16] & 0xffL) << 8; + // the first byte of c is reserved for the length + case 16: + b += word64At(value, offset + 8); + a += word64At(value, offset); + break; + case 15: + b += (value[offset + 14] & 0xffL) << 48; + case 14: + b += (value[offset + 13] & 0xffL) << 40; + case 13: + b += (value[offset + 12] & 0xffL) << 32; + case 12: + b += (value[offset + 11] & 0xffL) << 24; + case 11: + b += (value[offset + 10] & 0xffL) << 16; + case 10: + b += (value[offset + 9] & 0xffL) << 8; + case 9: + b += (value[offset + 8] & 0xffL); + case 8: + a += word64At(value, offset); + break; + case 7: + a += (value[offset + 6] & 0xffL) << 48; + case 6: + a += (value[offset + 5] & 0xffL) << 40; + case 5: + a += (value[offset + 4] & 0xffL) << 32; + case 4: + a += (value[offset + 3] & 0xffL) << 24; + case 3: + a += (value[offset + 2] & 0xffL) << 16; + case 2: + a += (value[offset + 1] & 0xffL) << 8; + case 1: + a += (value[offset + 0] & 0xffL); + // case 0: nothing left to add + } + return mix64(a, b, c); + } + + private static long word64At(byte[] bytes, int offset) { + return (bytes[offset + 0] & 0xffL) + + ((bytes[offset + 1] & 0xffL) << 8) + + ((bytes[offset + 2] & 0xffL) << 16) + + ((bytes[offset + 3] & 0xffL) << 24) + + ((bytes[offset + 4] & 0xffL) << 32) + + ((bytes[offset + 5] & 0xffL) << 40) + + ((bytes[offset + 6] & 0xffL) << 48) + + ((bytes[offset + 7] & 0xffL) << 56); + } + + /** + * Mixes longs a, b, and c, and returns the final value of c. + */ + private static long mix64(long a, long b, long c) { + a -= b; a -= c; a ^= c >>> 43; + b -= c; b -= a; b ^= a << 9; + c -= a; c -= b; c ^= b >>> 8; + a -= b; a -= c; a ^= c >>> 38; + b -= c; b -= a; b ^= a << 23; + c -= a; c -= b; c ^= b >>> 5; + a -= b; a -= c; a ^= c >>> 35; + b -= c; b -= a; b ^= a << 49; + c -= a; c -= b; c ^= b >>> 11; + a -= b; a -= c; a ^= c >>> 12; + b -= c; b -= a; b ^= a << 18; + c -= a; c -= b; c ^= b >>> 22; + return c; + } + } + + public interface IdGenerator { + /** + * Generate the ID for the message. Messages with the same messageParts + * and meaning will get the same id. Messages with the same id + * will get the same translation. + * + * @param meaning The programmer-specified meaning. If no {@code @meaning} + * annotation appears, we will use the name of the variable it's + * assigned to. If the variable is unnamed, then we will just + * use a fingerprint of the message. + * @param messageParts The parts of the message, including the main + * message text. + */ + String generateId(String meaning, List messageParts); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JsMessageDefinition.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JsMessageDefinition.java new file mode 100644 index 0000000..6a56f82 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JsMessageDefinition.java @@ -0,0 +1,77 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + * Container class that holds information about JS message source. + * + * This class is specific to our JsMessage syntax. Allows you to use the + * new-style or the old-style messages. + * + * Old-style: + * + * var MSG_LEOPARD = 'Leopard'; + * var MSG_LEOPARD_HELP = 'The Leopard operating system'; + * + * + * New-style: + * + * /** @desc The leopard operating system * / + * var MSG_LEOPARD = goog.getMsg('Leopard'); + * + * + * @author anatol@google.com (Anatol Pomazau) + */ +class JsMessageDefinition { + + private final Node messageNode; + private final Node messageParentNode; + private final Node visitingNode; + + /** + * Constructs JS message definition. + * + * @param visitingNode Node that is visited by + * {@link JsMessageVisitor}. Take into + * account that visiting node could differ from the node the message + * was found. + * @param messageNode A node that contains the message. It could be node with + * goog.getMsg() call or string/function for old-style messages. + * @param messageParentNode The parent of the message node. + */ + JsMessageDefinition(Node visitingNode, Node messageNode, + Node messageParentNode) { + + this.messageNode = messageNode; + this.visitingNode = visitingNode; + this.messageParentNode = messageParentNode; + } + + Node getMessageNode() { + return messageNode; + } + + Node getVisitingNode() { + return visitingNode; + } + + Node getMessageParentNode() { + return messageParentNode; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JsMessageExtractor.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JsMessageExtractor.java new file mode 100644 index 0000000..710eb92 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JsMessageExtractor.java @@ -0,0 +1,150 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; + + +/** + * Extracts messages and message comments from JS code. + * + *

      Uses a special prefix (e.g. {@code MSG_}) to determine which variables + * are messages. Here are the recognized formats: + * + * + * var MSG_FOO = "foo"; + * var MSG_FOO_HELP = "this message is used for foo"; + * + * + * + * var MSG_BAR = function(a, b) { + * return a + " bar " + b; + * } + * var MSG_BAR_HELP = "the bar message"; + * + * + *

      This class enforces the policy that message variable names must be unique + * across all JS files. + * + */ +public class JsMessageExtractor { + + private final JsMessage.Style style; + private final JsMessage.IdGenerator idGenerator; + private final CompilerOptions options; + + public JsMessageExtractor( + JsMessage.IdGenerator idGenerator, + JsMessage.Style style) { + this(idGenerator, style, new CompilerOptions()); + } + + public JsMessageExtractor( + JsMessage.IdGenerator idGenerator, + JsMessage.Style style, + CompilerOptions options) { + this.idGenerator = idGenerator; + this.style = style; + this.options = options; + } + + /** + * Visitor that collects messages. + */ + private class ExtractMessagesVisitor extends JsMessageVisitor { + // We use List here as we want to preserve insertion-order for found + // messages. + // Take into account that messages with the same id could be present in the + // result list. Message could have the same id only in case if they are + // unnamed and have the same text but located in different source files. + private final List messages = Lists.newLinkedList(); + + private ExtractMessagesVisitor(AbstractCompiler compiler) { + super(compiler, true, style, idGenerator); + } + + @Override + void processJsMessage(JsMessage message, + JsMessageDefinition definition) { + if (!message.isExternal()) { + messages.add(message); + } + } + + /** + * Returns extracted messages. + * + * @return collection of JsMessage objects that was found in js sources. + */ + public Collection getMessages() { + return messages; + } + } + + /** + * Extracts JS messages from JavaScript code. + */ + public Collection extractMessages(SourceFile... inputs) + throws IOException { + return extractMessages(ImmutableList.copyOf(inputs)); + } + + + /** + * Extracts JS messages from JavaScript code. + * + * @param inputs the JavaScript source code inputs + * @return the extracted messages collection + * @throws IOException if there is a problem reading the JS code + * @throws RuntimeException if there are problems parsing the JS code or the + * JS messages, or if two messages have the same key + */ + public Collection extractMessages( + Iterable inputs) throws IOException { + + Compiler compiler = new Compiler(); + compiler.init( + ImmutableList.of(), + Lists.newArrayList(inputs), + options); + compiler.parseInputs(); + + ExtractMessagesVisitor extractCompilerPass = + new ExtractMessagesVisitor(compiler); + if (compiler.getErrors().length == 0) { + extractCompilerPass.process(null, compiler.getRoot()); + } + + JSError[] errors = compiler.getErrors(); + // Check for errors. + if (errors.length > 0) { + StringBuilder msg = new StringBuilder("JSCompiler errors\n"); + MessageFormatter formatter = new LightweightMessageFormatter(compiler); + for (JSError e : errors) { + msg.append(formatter.formatError(e)); + } + throw new RuntimeException(msg.toString()); + } + + return extractCompilerPass.getMessages(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JsMessageVisitor.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JsMessageVisitor.java new file mode 100644 index 0000000..becd724 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JsMessageVisitor.java @@ -0,0 +1,943 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import static com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; + +import com.google.common.base.CaseFormat; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.JsMessage.Builder; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.*; +import java.util.regex.*; + +import javax.annotation.Nullable; + +/** + * Traverses across parsed tree and finds I18N messages. Then it passes it to + * {@link JsMessageVisitor#processJsMessage(JsMessage, JsMessageDefinition)}. + * + * @author anatol@google.com (Anatol Pomazau) + */ +abstract class JsMessageVisitor extends AbstractPostOrderCallback + implements CompilerPass { + + private static final String MSG_FUNCTION_NAME = "goog.getMsg"; + private static final String MSG_FALLBACK_FUNCTION_NAME = + "goog.getMsgWithFallback"; + + static final DiagnosticType MESSAGE_HAS_NO_DESCRIPTION = + DiagnosticType.warning("JSC_MSG_HAS_NO_DESCRIPTION", + "Message {0} has no description. Add @desc JsDoc tag."); + + static final DiagnosticType MESSAGE_HAS_NO_TEXT = + DiagnosticType.warning("JSC_MSG_HAS_NO_TEXT", + "Message value of {0} is just an empty string. " + + "Empty messages are forbidden." ); + + static final DiagnosticType MESSAGE_TREE_MALFORMED = + DiagnosticType.error("JSC_MSG_TREE_MALFORMED", + "Message parse tree malformed. {0}"); + + static final DiagnosticType MESSAGE_HAS_NO_VALUE = + DiagnosticType.error("JSC_MSG_HAS_NO_VALUE", + "message node {0} has no value"); + + static final DiagnosticType MESSAGE_DUPLICATE_KEY = + DiagnosticType.error("JSC_MSG_KEY_DUPLICATED", + "duplicate message variable name found for {0}, " + + "initial definition {1}:{2}"); + + static final DiagnosticType MESSAGE_NODE_IS_ORPHANED = + DiagnosticType.warning("JSC_MSG_ORPHANED_NODE", MSG_FUNCTION_NAME + + "() function could be used only with MSG_* property or variable"); + + static final DiagnosticType MESSAGE_NOT_INITIALIZED_USING_NEW_SYNTAX = + DiagnosticType.error("JSC_MSG_NOT_INITIALIZED_USING_NEW_SYNTAX", + "message not initialized using " + MSG_FUNCTION_NAME); + + static final DiagnosticType BAD_FALLBACK_SYNTAX = + DiagnosticType.error("JSC_MSG_BAD_FALLBACK_SYNTAX", + String.format( + "Bad syntax. " + + "Expected syntax: goog.getMsgWithFallback(MSG_1, MSG_2)", + MSG_FALLBACK_FUNCTION_NAME)); + + static final DiagnosticType FALLBACK_ARG_ERROR = + DiagnosticType.error("JSC_MSG_FALLBACK_ARG_ERROR", + "Could not find message entry for fallback argument {0}"); + + private static final String PH_JS_PREFIX = "{$"; + private static final String PH_JS_SUFFIX = "}"; + + static final String MSG_PREFIX = "MSG_"; + + /** + * Pattern for unnamed messages. + *

      + * All JS messages in JS code should have unique name but messages in + * generated code (i.e. from soy template) could have duplicated message names. + * Later we replace the message names with ids constructed as a hash of the + * message content. + *

      + * + * Soy generates messages with names MSG_UNNAMED_ . This + * pattern recognizes such messages. + */ + private static final Pattern MSG_UNNAMED_PATTERN = + Pattern.compile("MSG_UNNAMED_\\d+"); + + private static final Pattern CAMELCASE_PATTERN = + Pattern.compile("[a-z][a-zA-Z\\d]*[_\\d]*"); + + static final String HIDDEN_DESC_PREFIX = "@hidden"; + + // For old-style JS messages + private static final String DESC_SUFFIX = "_HELP"; + + private final boolean needToCheckDuplications; + private final JsMessage.Style style; + private final JsMessage.IdGenerator idGenerator; + final AbstractCompiler compiler; + + /** + * The names encountered associated with their defining node and source. We + * use it for tracking duplicated message ids in the source code. + */ + private final Map messageNames = + Maps.newHashMap(); + + private final Map unnamedMessages = + Maps.newHashMap(); + + /** + * List of found goog.getMsg call nodes. + * + * When we visit goog.getMsg() node we add a pair node:sourcename and later + * when we visit its parent we remove this pair. All nodes that are left at + * the end of traversing are orphaned nodes. It means have no corresponding + * var or property node. + */ + private final Map googMsgNodes = Maps.newHashMap(); + + private final CheckLevel checkLevel; + + /** + * Creates JS message visitor. + * + * @param compiler the compiler instance + * @param needToCheckDuplications whether to check duplicated messages in + * traversed + * @param style style that should be used during parsing + * @param idGenerator generator that used for creating unique ID for the + * message + */ + JsMessageVisitor(AbstractCompiler compiler, + boolean needToCheckDuplications, + JsMessage.Style style, JsMessage.IdGenerator idGenerator) { + + this.compiler = compiler; + this.needToCheckDuplications = needToCheckDuplications; + this.style = style; + this.idGenerator = idGenerator; + + checkLevel = (style == JsMessage.Style.CLOSURE) + ? CheckLevel.ERROR : CheckLevel.WARNING; + + // TODO(anatol): add flag that decides whether to process UNNAMED messages. + // Some projects would not want such functionality (unnamed) as they don't + // use SOY templates. + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + + for (Map.Entry msgNode : googMsgNodes.entrySet()) { + compiler.report(JSError.make(msgNode.getValue(), msgNode.getKey(), + checkLevel, MESSAGE_NODE_IS_ORPHANED)); + } + } + + @Override + public void visit(NodeTraversal traversal, Node node, Node parent) { + String messageKey; + boolean isVar; + Node msgNode, msgNodeParent; + + switch (node.getType()) { + case Token.NAME: + // var MSG_HELLO = 'Message' + if ((parent != null) && (parent.isVar())) { + messageKey = node.getString(); + isVar = true; + } else { + return; + } + + msgNode = node.getFirstChild(); + msgNodeParent = node; + break; + case Token.ASSIGN: + // somenamespace.someclass.MSG_HELLO = 'Message' + isVar = false; + + Node getProp = node.getFirstChild(); + if (!getProp.isGetProp()) { + return; + } + + Node propNode = getProp.getLastChild(); + + messageKey = propNode.getString(); + msgNode = node.getLastChild(); + msgNodeParent = node; + break; + case Token.CALL: + // goog.getMsg() + String fnName = node.getFirstChild().getQualifiedName(); + if (MSG_FUNCTION_NAME.equals(fnName)) { + googMsgNodes.put(node, traversal.getSourceName()); + } else if (MSG_FALLBACK_FUNCTION_NAME.equals(fnName)) { + visitFallbackFunctionCall(traversal, node); + } + return; + default: + return; + } + + // Is this a message name? + boolean isNewStyleMessage = + msgNode != null && msgNode.isCall(); + if (!isMessageName(messageKey, isNewStyleMessage)) { + return; + } + + if (msgNode == null) { + compiler.report( + traversal.makeError(node, MESSAGE_HAS_NO_VALUE, messageKey)); + return; + } + + // Just report a warning if a qualified messageKey that looks like a message + // (e.g. "a.b.MSG_X") doesn't use goog.getMsg(). + if (isNewStyleMessage) { + googMsgNodes.remove(msgNode); + } else if (style != JsMessage.Style.LEGACY) { + compiler.report(traversal.makeError(node, checkLevel, + MESSAGE_NOT_INITIALIZED_USING_NEW_SYNTAX)); + } + + boolean isUnnamedMsg = isUnnamedMessageName(messageKey); + + Builder builder = new Builder( + isUnnamedMsg ? null : messageKey); + builder.setSourceName(traversal.getSourceName()); + + try { + if (isVar) { + extractMessageFromVariable(builder, node, parent, parent.getParent()); + } else { + extractMessageFromProperty(builder, node.getFirstChild(), node); + } + } catch (MalformedException ex) { + compiler.report(traversal.makeError(ex.getNode(), + MESSAGE_TREE_MALFORMED, ex.getMessage())); + return; + } + + JsMessage extractedMessage = builder.build(idGenerator); + + // If asked to check named internal messages. + if (needToCheckDuplications + && !isUnnamedMsg + && !extractedMessage.isExternal()) { + checkIfMessageDuplicated(messageKey, msgNode); + } + trackMessage(traversal, extractedMessage, + messageKey, msgNode, isUnnamedMsg); + + if (extractedMessage.isEmpty()) { + // value of the message is an empty string. Translators do not like it. + compiler.report(traversal.makeError(node, MESSAGE_HAS_NO_TEXT, + messageKey)); + } + + // New-style messages must have descriptions. We don't emit a warning + // for legacy-style messages, because there are thousands of + // them in legacy code that are not worth the effort to fix, since they've + // already been translated anyway. + String desc = extractedMessage.getDesc(); + if (isNewStyleMessage + && (desc == null || desc.trim().isEmpty()) + && !extractedMessage.isExternal()) { + compiler.report(traversal.makeError(node, MESSAGE_HAS_NO_DESCRIPTION, + messageKey)); + } + + JsMessageDefinition msgDefinition = new JsMessageDefinition( + node, msgNode, msgNodeParent); + processJsMessage(extractedMessage, msgDefinition); + } + + /** + * Track a message for later retrieval. + * + * This is used for tracking duplicates, and for figuring out message + * fallback. Not all message types are trackable, because that would + * require a more sophisticated analysis. e.g., + * function f(s) { s.MSG_UNNAMED_X = 'Some untrackable message'; } + */ + private void trackMessage( + NodeTraversal t, JsMessage message, String msgName, + Node msgNode, boolean isUnnamedMessage) { + if (!isUnnamedMessage) { + MessageLocation location = new MessageLocation(message, msgNode); + messageNames.put(msgName, location); + } else if (msgNode.isName()) { + Var var = t.getScope().getVar(msgName); + if (var != null) { + unnamedMessages.put(var, message); + } + } + } + + /** Get a previously tracked message. */ + private JsMessage getTrackedMessage(NodeTraversal t, String msgName) { + boolean isUnnamedMessage = isUnnamedMessageName(msgName); + if (!isUnnamedMessage) { + MessageLocation location = messageNames.get(msgName); + return location == null ? null : location.message; + } else { + Var var = t.getScope().getVar(msgName); + if (var != null) { + return unnamedMessages.get(var); + } + } + return null; + } + + /** + * Checks if message already processed. If so - it generates 'message + * duplicated' compiler error. + * + * @param msgName the name of the message + * @param msgNode the node that represents JS message + */ + private void checkIfMessageDuplicated(String msgName, Node msgNode) { + if (messageNames.containsKey(msgName)) { + MessageLocation location = messageNames.get(msgName); + compiler.report(JSError.make(msgNode, MESSAGE_DUPLICATE_KEY, + msgName, location.messageNode.getSourceFileName(), + Integer.toString(location.messageNode.getLineno()))); + } + } + + /** + * Creates a {@link JsMessage} for a JS message defined using a JS variable + * declaration (e.g var MSG_X = ...;). + * + * @param builder the message builder + * @param nameNode a NAME node for a JS message variable + * @param parentNode a VAR node, parent of {@code nameNode} + * @param grandParentNode the grandparent of {@code nameNode}. This node is + * only used to get meta data about the message that might be + * surrounding it (e.g. a message description). This argument may be + * null if the meta data is not needed. + * @throws MalformedException if {@code varNode} does not + * correspond to a valid JS message VAR node + */ + private void extractMessageFromVariable( + Builder builder, Node nameNode, Node parentNode, + @Nullable Node grandParentNode) throws MalformedException { + + // Determine the message's value + Node valueNode = nameNode.getFirstChild(); + switch (valueNode.getType()) { + case Token.STRING: + case Token.ADD: + maybeInitMetaDataFromJsDocOrHelpVar(builder, parentNode, + grandParentNode); + builder.appendStringPart(extractStringFromStringExprNode(valueNode)); + break; + case Token.FUNCTION: + maybeInitMetaDataFromJsDocOrHelpVar(builder, parentNode, + grandParentNode); + extractFromFunctionNode(builder, valueNode); + break; + case Token.CALL: + maybeInitMetaDataFromJsDoc(builder, parentNode); + extractFromCallNode(builder, valueNode); + break; + default: + throw new MalformedException("Cannot parse value of message " + + builder.getKey(), valueNode); + } + } + + /** + * Creates a {@link JsMessage} for a JS message defined using an assignment to + * a qualified name (e.g a.b.MSG_X = goog.getMsg(...);). + * + * @param builder the message builder + * @param getPropNode a GETPROP node in a JS message assignment + * @param assignNode an ASSIGN node, parent of {@code getPropNode}. + * @throws MalformedException if {@code getPropNode} does not + * correspond to a valid JS message node + */ + private void extractMessageFromProperty( + Builder builder, Node getPropNode, Node assignNode) + throws MalformedException { + Node callNode = getPropNode.getNext(); + maybeInitMetaDataFromJsDoc(builder, assignNode); + extractFromCallNode(builder, callNode); + } + + /** + * Initializes the meta data in a JsMessage by examining the nodes just before + * and after a message VAR node. + * + * @param builder the message builder whose meta data will be initialized + * @param varNode the message VAR node + * @param parentOfVarNode {@code varNode}'s parent node + */ + private void maybeInitMetaDataFromJsDocOrHelpVar( + Builder builder, Node varNode, @Nullable Node parentOfVarNode) + throws MalformedException { + + // First check description in @desc + if (maybeInitMetaDataFromJsDoc(builder, varNode)) { + return; + } + + // Check the preceding node for meta data + if ((parentOfVarNode != null) && + maybeInitMetaDataFromHelpVar(builder, + parentOfVarNode.getChildBefore(varNode))) { + return; + } + + // Check the subsequent node for meta data + maybeInitMetaDataFromHelpVar(builder, varNode.getNext()); + } + + /** + * Initializes the meta data in a JsMessage by examining a node just before or + * after a message VAR node. + * + * @param builder the message builder whose meta data will be initialized + * @param sibling a node adjacent to the message VAR node + * @return true iff message has corresponding description variable + */ + private boolean maybeInitMetaDataFromHelpVar(Builder builder, + @Nullable Node sibling) throws MalformedException { + if ((sibling != null) && (sibling.isVar())) { + Node nameNode = sibling.getFirstChild(); + String name = nameNode.getString(); + if (name.equals(builder.getKey() + DESC_SUFFIX)) { + Node valueNode = nameNode.getFirstChild(); + String desc = extractStringFromStringExprNode(valueNode); + if (desc.startsWith(HIDDEN_DESC_PREFIX)) { + builder.setDesc(desc.substring(HIDDEN_DESC_PREFIX.length()).trim()); + builder.setIsHidden(true); + } else { + builder.setDesc(desc); + } + return true; + } + } + return false; + } + + /** + * Initializes the meta data in a message builder given a node that may + * contain JsDoc properties. + * + * @param builder the message builder whose meta data will be initialized + * @param node the node with the message's JSDoc properties + * @return true if message has JsDoc with valid description in @desc + * annotation + */ + private boolean maybeInitMetaDataFromJsDoc(Builder builder, Node node) { + boolean messageHasDesc = false; + JSDocInfo info = node.getJSDocInfo(); + if (info != null) { + String desc = info.getDescription(); + if (desc != null) { + builder.setDesc(desc); + messageHasDesc = true; + } + if (info.isHidden()) { + builder.setIsHidden(true); + } + if (info.getMeaning() != null) { + builder.setMeaning(info.getMeaning()); + } + } + + return messageHasDesc; + } + + /** + * Returns the string value associated with a node representing a JS string or + * several JS strings added together (e.g. {@code 'str'} or {@code 's' + 't' + + * 'r'}). + * + * @param node the node from where we extract the string + * @return String representation of the node + * @throws MalformedException if the parsed message is invalid + */ + private static String extractStringFromStringExprNode(Node node) + throws MalformedException { + switch (node.getType()) { + case Token.STRING: + return node.getString(); + case Token.ADD: + StringBuilder sb = new StringBuilder(); + for (Node child : node.children()) { + sb.append(extractStringFromStringExprNode(child)); + } + return sb.toString(); + default: + throw new MalformedException("STRING or ADD node expected; found: " + + getReadableTokenName(node), node); + } + } + + /** + * Initializes a message builder from a FUNCTION node. + *

      + *

      +   * The tree should look something like:
      +   *
      +   * function
      +   *  |-- name
      +   *  |-- lp
      +   *  |   |-- name 
      +   *  |    -- name 
      +   *   -- block
      +   *      |
      +   *       --return
      +   *           |
      +   *            --add
      +   *               |-- string foo
      +   *                -- name 
      +   * 
      + * + * @param builder the message builder + * @param node the function node that contains a message + * @throws MalformedException if the parsed message is invalid + */ + private void extractFromFunctionNode(Builder builder, Node node) + throws MalformedException { + Set phNames = Sets.newHashSet(); + + for (Node fnChild : node.children()) { + switch (fnChild.getType()) { + case Token.NAME: + // This is okay. The function has a name, but it is empty. + break; + case Token.PARAM_LIST: + // Parse the placeholder names from the function argument list. + for (Node argumentNode : fnChild.children()) { + if (argumentNode.isName()) { + String phName = argumentNode.getString(); + if (phNames.contains(phName)) { + throw new MalformedException("Duplicate placeholder name: " + + phName, argumentNode); + } else { + phNames.add(phName); + } + } + } + break; + case Token.BLOCK: + // Build the message's value by examining the return statement + Node returnNode = fnChild.getFirstChild(); + if (!returnNode.isReturn()) { + throw new MalformedException("RETURN node expected; found: " + + getReadableTokenName(returnNode), returnNode); + } + for (Node child : returnNode.children()) { + extractFromReturnDescendant(builder, child); + } + + // Check that all placeholders from the message text have appropriate + // object literal keys + for (String phName : builder.getPlaceholders()) { + if(!phNames.contains(phName)) { + throw new MalformedException( + "Unrecognized message placeholder referenced: " + phName, + returnNode); + } + } + break; + default: + throw new MalformedException( + "NAME, LP, or BLOCK node expected; found: " + + getReadableTokenName(node), fnChild); + } + } + } + + /** + * Appends value parts to the message builder by traversing the descendants + * of the given RETURN node. + * + * @param builder the message builder + * @param node the node from where we extract a message + * @throws MalformedException if the parsed message is invalid + */ + private void extractFromReturnDescendant(Builder builder, Node node) + throws MalformedException { + + switch (node.getType()) { + case Token.STRING: + builder.appendStringPart(node.getString()); + break; + case Token.NAME: + builder.appendPlaceholderReference(node.getString()); + break; + case Token.ADD: + for (Node child : node.children()) { + extractFromReturnDescendant(builder, child); + } + break; + default: + throw new MalformedException( + "STRING, NAME, or ADD node expected; found: " + + getReadableTokenName(node), node); + } + } + + /** + * Initializes a message builder from a CALL node. + *

      + * The tree should look something like: + * + *

      +   * call
      +   *  |-- getprop
      +   *  |   |-- name 'goog'
      +   *  |   +-- string 'getMsg'
      +   *  |
      +   *  |-- string 'Hi {$userName}! Welcome to {$product}.'
      +   *  +-- objlit
      +   *      |-- string 'userName'
      +   *      |-- name 'someUserName'
      +   *      |-- string 'product'
      +   *      +-- call
      +   *          +-- name 'getProductName'
      +   * 
      + * + * @param builder the message builder + * @param node the call node from where we extract the message + * @throws MalformedException if the parsed message is invalid + */ + private void extractFromCallNode(Builder builder, + Node node) throws MalformedException { + // Check the function being called + if (!node.isCall()) { + throw new MalformedException( + "Message must be initialized using " + MSG_FUNCTION_NAME + + " function.", node); + } + + Node fnNameNode = node.getFirstChild(); + if (!MSG_FUNCTION_NAME.equals(fnNameNode.getQualifiedName())) { + throw new MalformedException( + "Message initialized using unrecognized function. " + + "Please use " + MSG_FUNCTION_NAME + "() instead.", fnNameNode); + } + + // Get the message string + Node stringLiteralNode = fnNameNode.getNext(); + if (stringLiteralNode == null) { + throw new MalformedException("Message string literal expected", + stringLiteralNode); + } + // Parse the message string and append parts to the builder + parseMessageTextNode(builder, stringLiteralNode); + + Node objLitNode = stringLiteralNode.getNext(); + Set phNames = Sets.newHashSet(); + if (objLitNode != null) { + // Register the placeholder names + if (!objLitNode.isObjectLit()) { + throw new MalformedException("OBJLIT node expected", objLitNode); + } + for (Node aNode = objLitNode.getFirstChild(); aNode != null; + aNode = aNode.getNext()) { + if (!aNode.isStringKey()) { + throw new MalformedException("STRING_KEY node expected as OBJLIT key", + aNode); + } + String phName = aNode.getString(); + if (!isLowerCamelCaseWithNumericSuffixes(phName)) { + throw new MalformedException( + "Placeholder name not in lowerCamelCase: " + phName, aNode); + } + + if (phNames.contains(phName)) { + throw new MalformedException("Duplicate placeholder name: " + + phName, aNode); + } + + phNames.add(phName); + } + } + + // Check that all placeholders from the message text have appropriate objlit + // values + Set usedPlaceholders = builder.getPlaceholders(); + for (String phName : usedPlaceholders) { + if(!phNames.contains(phName)) { + throw new MalformedException( + "Unrecognized message placeholder referenced: " + phName, + objLitNode); + } + } + + // Check that objLiteral have only names that are present in the + // message text + for (String phName : phNames) { + if(!usedPlaceholders.contains(phName)) { + throw new MalformedException( + "Unused message placeholder: " + phName, + objLitNode); + } + } + } + + /** + * Appends the message parts in a JS message value extracted from the given + * text node. + * + * @param builder the JS message builder to append parts to + * @param node the node with string literal that contains the message text + * @throws MalformedException if {@code value} contains a reference to + * an unregistered placeholder + */ + private void parseMessageTextNode(Builder builder, Node node) + throws MalformedException { + String value = extractStringFromStringExprNode(node); + + while(true) { + int phBegin = value.indexOf(PH_JS_PREFIX); + if (phBegin < 0) { + // Just a string literal + builder.appendStringPart(value); + return; + } else { + if (phBegin > 0) { + // A string literal followed by a placeholder + builder.appendStringPart(value.substring(0, phBegin)); + } + + // A placeholder. Find where it ends + int phEnd = value.indexOf(PH_JS_SUFFIX, phBegin); + if (phEnd < 0) { + throw new MalformedException( + "Placeholder incorrectly formatted in: " + builder.getKey(), + node); + } + + String phName = value.substring(phBegin + PH_JS_PREFIX.length(), + phEnd); + builder.appendPlaceholderReference(phName); + int nextPos = phEnd + PH_JS_SUFFIX.length(); + if (nextPos < value.length()) { + // Iterate on the rest of the message value + value = value.substring(nextPos); + } else { + // The message is parsed + return; + } + } + } + } + + /** Visit a call to goog.getMsgWithFallback. */ + private void visitFallbackFunctionCall(NodeTraversal t, Node call) { + // Check to make sure the function call looks like: + // goog.getMsgWithFallback(MSG_1, MSG_2); + if (call.getChildCount() != 3 || + !call.getChildAtIndex(1).isName() || + !call.getChildAtIndex(2).isName()) { + compiler.report(t.makeError(call, BAD_FALLBACK_SYNTAX)); + return; + } + + Node firstArg = call.getChildAtIndex(1); + JsMessage firstMessage = getTrackedMessage(t, firstArg.getString()); + if (firstMessage == null) { + compiler.report( + t.makeError(firstArg, FALLBACK_ARG_ERROR, firstArg.getString())); + return; + } + + Node secondArg = firstArg.getNext(); + JsMessage secondMessage = getTrackedMessage( + t, call.getChildAtIndex(2).getString()); + if (secondMessage == null) { + compiler.report( + t.makeError(secondArg, FALLBACK_ARG_ERROR, secondArg.getString())); + return; + } + + processMessageFallback(call, firstMessage, secondMessage); + } + + + /** + * Processes found JS message. Several examples of "standard" processing + * routines are: + *
        + *
      1. extract all JS messages + *
      2. replace JS messages with localized versions for some specific language + *
      3. check that messages have correct syntax and present in localization + * bundle + *
      + * + * @param message the found message + * @param definition the definition of the object and usually contains all + * additional message information like message node/parent's node + */ + abstract void processJsMessage(JsMessage message, + JsMessageDefinition definition); + + /** + * Processes the goog.getMsgWithFallback primitive. + * goog.getMsgWithFallback(MSG_1, MSG_2); + * + * By default, does nothing. + */ + void processMessageFallback(Node callNode, JsMessage message1, + JsMessage message2) {} + + /** + * Returns whether the given JS identifier is a valid JS message name. + */ + boolean isMessageName(String identifier, boolean isNewStyleMessage) { + return identifier.startsWith(MSG_PREFIX) && + (style == JsMessage.Style.CLOSURE || isNewStyleMessage || + !identifier.endsWith(DESC_SUFFIX)); + } + + /** + * Returns whether the given message name is in the unnamed namespace. + */ + private static boolean isUnnamedMessageName(String identifier) { + return MSG_UNNAMED_PATTERN.matcher(identifier).matches(); + } + + /** + * Returns whether a string is nonempty, begins with a lowercase letter, and + * contains only digits and underscores after the first underscore. + */ + static boolean isLowerCamelCaseWithNumericSuffixes(String input) { + return CAMELCASE_PATTERN.matcher(input).matches(); + } + + /** + * Returns human-readable name of the given node's type. + */ + private static String getReadableTokenName(Node node) { + return Token.name(node.getType()); + } + + /** + * Converts the given string from upper-underscore case to lower-camel case, + * preserving numeric suffixes. For example: "NAME" -> "name" "A4_LETTER" -> + * "a4Letter" "START_SPAN_1_23" -> "startSpan_1_23". + */ + static String toLowerCamelCaseWithNumericSuffixes(String input) { + // Determine where the numeric suffixes begin + int suffixStart = input.length(); + while (suffixStart > 0) { + char ch = '\0'; + int numberStart = suffixStart; + while (numberStart > 0) { + ch = input.charAt(numberStart - 1); + if (Character.isDigit(ch)) { + numberStart--; + } else { + break; + } + } + if ((numberStart > 0) && (numberStart < suffixStart) && (ch == '_')) { + suffixStart = numberStart - 1; + } else { + break; + } + } + + if (suffixStart == input.length()) { + return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, input); + } else { + return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, + input.substring(0, suffixStart)) + + input.substring(suffixStart); + } + } + + /** + * Checks a node's type. + * + * @throws MalformedException if the node is null or the wrong type + */ + protected void checkNode(@Nullable Node node, int type) throws MalformedException { + if (node == null) { + throw new MalformedException( + "Expected node type " + type + "; found: null", node); + } + if (node.getType() != type) { + throw new MalformedException( + "Expected node type " + type + "; found: " + node.getType(), node); + } + } + + static class MalformedException extends Exception { + private static final long serialVersionUID = 1L; + + private final Node node; + + MalformedException(String message, Node node) { + super(message); + this.node = node; + } + + Node getNode() { + return node; + } + } + + private static class MessageLocation { + private final JsMessage message; + private final Node messageNode; + + private MessageLocation(JsMessage message, Node messageNode) { + this.message = message; + this.messageNode = messageNode; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JvmMetrics.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JvmMetrics.java new file mode 100644 index 0000000..c7ec381 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/JvmMetrics.java @@ -0,0 +1,275 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import java.io.PrintStream; +import java.lang.management.CompilationMXBean; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryUsage; +import java.util.List; +import java.util.StringTokenizer; + +/** + * A class to report jvm/jmx statistics. + * Borrowed from: + * http://code.google.com/p/dart/source/browse/trunk/dart/compiler/java/com/google/dart/compiler/metrics/JvmMetrics.java + */ +class JvmMetrics { + + private static int TABULAR_COLON_POS = 40; + private static long ONE_KILO_BYTE = 1L << 10L; + private static long ONE_MEGA_BYTE = 1L << 20L; + private static long ONE_GIGA_BYTE = 1L << 30L; + + public static void maybeWriteJvmMetrics(PrintStream out, String options) { + if (options == null) { + return; + } + + boolean verboseMode = false; + boolean prettyMode = false; + StringTokenizer st = new StringTokenizer(options, ":"); + // options are grouped in order 'detail:format:types' + if (st.hasMoreTokens()) { + String mode = st.nextToken(); + if (mode.equalsIgnoreCase("verbose")) { + verboseMode = true; + } + } + + if (st.hasMoreTokens()) { + String mode = st.nextToken(); + if (mode.equalsIgnoreCase("pretty")) { + prettyMode = true; + } + } + + if (st.hasMoreTokens()) { + while (st.hasMoreTokens()) { + String types = st.nextToken(); + StringTokenizer typeSt = new StringTokenizer(types, ","); + while (typeSt.hasMoreElements()) { + String type = typeSt.nextToken(); + writeMetrics(out, type, verboseMode, prettyMode); + } + } + } else { + // the default + writeMetrics(out, "all", verboseMode, prettyMode); + } + } + + private static void writeMetrics( + PrintStream out, String type, boolean verbose, boolean pretty) { + + if (type.equals("gc") || type.equalsIgnoreCase("all")) { + writeGarbageCollectionStats(out, verbose, pretty); + } + if (type.equals("mem") || type.equalsIgnoreCase("all")) { + writeMemoryMetrics(out, verbose, pretty); + } + if (type.equals("jit") || type.equalsIgnoreCase("all")) { + writeJitMetrics(out, verbose, pretty); + } + } + + private static void writeJitMetrics( + PrintStream out, boolean verbose, boolean pretty) { + + CompilationMXBean cBean = ManagementFactory.getCompilationMXBean(); + + String name; + if (verbose) { + name = cBean.getName(); + } else { + name = "total"; + } + + if (pretty) { + out.println("\nJIT Stats"); + out.println(String.format( + "\t%s jit time: %d ms", name, cBean.getTotalCompilationTime())); + } else { + out.println(normalizeTabularColonPos(String.format("%s-jit-time-ms : %d", + normalizeName(name), cBean.getTotalCompilationTime()))); + } + } + + private static void writeOverallMemoryUsage( + PrintStream out, MemoryUsage usage, String prefix, boolean pretty) { + if (pretty) { + out.format("\t%s\n", prefix); + out.format("\t\tavailable : %s\n", formatBytes(usage.getMax())); + out.format("\t\tcurrent : %s\n", formatBytes(usage.getUsed())); + } else { + prefix = normalizeName(prefix); + out.println(normalizeTabularColonPos( + String.format(prefix + "-available-bytes : %d", usage.getMax()))); + out.println(normalizeTabularColonPos( + String.format(prefix + "-current-bytes : %d", usage.getUsed()))); + } + } + + private static void writePoolMemoryUsage(PrintStream out, MemoryUsage usage, + MemoryUsage peakUsage, String prefix, boolean pretty) { + if (pretty) { + out.format("\t\tavailable : %s\n", + formatBytes(usage.getMax())); + out.format("\t\tpeak : %s\n", + formatBytes(peakUsage.getUsed())); + out.format("\t\tcurrent : %s\n", + formatBytes(usage.getUsed())); + } else { + out.println(normalizeTabularColonPos( + String.format(prefix + "-available-bytes : %d", usage.getMax()))); + out.println(normalizeTabularColonPos( + String.format(prefix + "-peak-bytes : %d", peakUsage.getUsed()))); + out.println(normalizeTabularColonPos( + String.format(prefix + "-current-bytes : %d", usage.getUsed()))); + } + } + + private static void writeMemoryMetrics( + PrintStream out, boolean verbose, boolean pretty) { + if (pretty) { + out.println("\nMemory usage"); + } + + // only show overall stats in verbose mode + if (verbose) { + MemoryMXBean overallMemBean = ManagementFactory.getMemoryMXBean(); + MemoryUsage usage = overallMemBean.getHeapMemoryUsage(); + writeOverallMemoryUsage(out, usage, "Heap", pretty); + + usage = overallMemBean.getNonHeapMemoryUsage(); + writeOverallMemoryUsage(out, usage, "Non-heap", pretty); + } + + if (verbose) { + List mpBeans = ManagementFactory.getMemoryPoolMXBeans(); + for (MemoryPoolMXBean mpBean : mpBeans) { + MemoryUsage currentUsage = mpBean.getUsage(); + MemoryUsage peakUsage = mpBean.getPeakUsage(); + if (pretty) { + out.println("\tPool " + mpBean.getName()); + writePoolMemoryUsage(out, currentUsage, peakUsage, null, true); + } else { + writePoolMemoryUsage(out, currentUsage, peakUsage, + "mem-pool-" + normalizeName(mpBean.getName()), false); + } + } + } else { + long available = 0; + long current = 0; + long peak = 0; + List mpBeans = ManagementFactory.getMemoryPoolMXBeans(); + for (MemoryPoolMXBean mpBean : mpBeans) { + MemoryUsage currentUsage = mpBean.getUsage(); + available += currentUsage.getMax(); + current += currentUsage.getUsed(); + MemoryUsage peakUsage = mpBean.getPeakUsage(); + peak += peakUsage.getUsed(); + } + MemoryUsage summaryUsage = new MemoryUsage( + 0, current, current, available); + MemoryUsage summaryPeakUsage = new MemoryUsage(0, peak, peak, peak); + if (pretty) { + out.format("\tAggregate of %d memory pools\n", mpBeans.size()); + writePoolMemoryUsage(out, summaryUsage, summaryPeakUsage, null, true); + } else { + writePoolMemoryUsage(out, summaryUsage, summaryPeakUsage, "mem", false); + } + } + } + + private static void writeGarbageCollectionStats( + PrintStream out, boolean verbose, boolean pretty) { + List gcBeans = + ManagementFactory.getGarbageCollectorMXBeans(); + + if (verbose) { + if (pretty) { + out.println("\nGarbage collection stats"); + for (GarbageCollectorMXBean gcBean : gcBeans) { + out.println("\tCollector " + gcBean.getName()); + out.format( + "\t\tcollection count : %d\n", gcBean.getCollectionCount()); + out.format( + "\t\tcollection time : %d ms\n", gcBean.getCollectionTime()); + } + } else { + for (GarbageCollectorMXBean gcBean : gcBeans) { + String name = normalizeName(gcBean.getName()); + out.println(normalizeTabularColonPos(String.format("gc-" + name + + "-collection-count : %d", gcBean.getCollectionCount()))); + out.println(normalizeTabularColonPos(String.format("gc-" + name + + "-collection-time-ms : %d", gcBean.getCollectionTime()))); + } + } + } else { + long collectionCount = 0; + long collectionTime = 0; + int collectorCount = gcBeans.size(); + for (GarbageCollectorMXBean gcBean : gcBeans) { + collectionCount += gcBean.getCollectionCount(); + collectionTime += gcBean.getCollectionTime(); + } + if (pretty) { + out.println("\nGarbage collection stats"); + out.format("\tAggregate of %d collectors\n", collectorCount); + out.format("\t\tcollection count : %d\n", collectionCount); + out.format("\t\tcollection time : %d ms\n", collectionTime); + } else { + String name = normalizeName("aggregate"); + out.println(normalizeTabularColonPos( + String.format("gc-" + name + "-collection-count : %d", + collectionCount))); + out.println(normalizeTabularColonPos( + String.format("gc-" + name + "-collection-time-ms : %d", + collectionTime))); + } + } + } + + private static String normalizeName(String name) { + return name.replace(" ", "_").toLowerCase(); + } + + private static String normalizeTabularColonPos(String string) { + StringBuilder sb = new StringBuilder(string); + int index = sb.indexOf(":"); + for (; index < TABULAR_COLON_POS; ++index) { + sb.insert(index, ' '); + } + return sb.toString(); + } + + private static String formatBytes(long numBytes) { + if (numBytes < ONE_KILO_BYTE) { + return String.format("%d B", numBytes); + } else if (numBytes < ONE_MEGA_BYTE) { + return String.format("%d KB", numBytes / ONE_KILO_BYTE); + } else if (numBytes < ONE_GIGA_BYTE) { + return String.format("%d MB", numBytes / ONE_MEGA_BYTE); + } else { + return String.format("%d GB", numBytes / ONE_GIGA_BYTE); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/LightweightMessageFormatter.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/LightweightMessageFormatter.java new file mode 100644 index 0000000..f8750ca --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/LightweightMessageFormatter.java @@ -0,0 +1,182 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import static com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt.LINE; + +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.SourceExcerptProvider.ExcerptFormatter; +import com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt; + +/** + * Lightweight message formatter. The format of messages this formatter + * produces is very compact and to the point. + * + */ +public class LightweightMessageFormatter extends AbstractMessageFormatter { + private SourceExcerpt excerpt; + private static final ExcerptFormatter excerptFormatter = + new LineNumberingFormatter(); + + /** + * A constructor for when the client doesn't care about source information. + */ + private LightweightMessageFormatter() { + super(null); + this.excerpt = LINE; + } + + public LightweightMessageFormatter(SourceExcerptProvider source) { + this(source, LINE); + } + + public LightweightMessageFormatter(SourceExcerptProvider source, + SourceExcerpt excerpt) { + super(source); + Preconditions.checkNotNull(source); + this.excerpt = excerpt; + } + + static LightweightMessageFormatter withoutSource() { + return new LightweightMessageFormatter(); + } + + @Override + public String formatError(JSError error) { + return format(error, false); + } + + @Override + public String formatWarning(JSError warning) { + return format(warning, true); + } + + private String format(JSError error, boolean warning) { + // extract source excerpt + SourceExcerptProvider source = getSource(); + String sourceExcerpt = source == null ? null : + excerpt.get( + source, error.sourceName, error.lineNumber, excerptFormatter); + + // formatting the message + StringBuilder b = new StringBuilder(); + if (error.sourceName != null) { + b.append(error.sourceName); + if (error.lineNumber > 0) { + b.append(':'); + b.append(error.lineNumber); + } + b.append(": "); + } + + b.append(getLevelName(warning ? CheckLevel.WARNING : CheckLevel.ERROR)); + b.append(" - "); + + b.append(error.description); + b.append('\n'); + if (sourceExcerpt != null) { + b.append(sourceExcerpt); + b.append('\n'); + int charno = error.getCharno(); + + // padding equal to the excerpt and arrow at the end + // charno == sourceExpert.length() means something is missing + // at the end of the line + if (excerpt.equals(LINE) + && 0 <= charno && charno <= sourceExcerpt.length()) { + for (int i = 0; i < charno; i++) { + char c = sourceExcerpt.charAt(i); + if (Character.isWhitespace(c)) { + b.append(c); + } else { + b.append(' '); + } + } + b.append("^\n"); + } + } + return b.toString(); + } + + /** + * Formats a region by appending line numbers in front, e.g. + *
         9| if (foo) {
      +   *   10|   alert('bar');
      +   *   11| }
      + * and return line excerpt without any modification. + */ + static class LineNumberingFormatter implements ExcerptFormatter { + @Override + public String formatLine(String line, int lineNumber) { + return line; + } + + @Override + public String formatRegion(Region region) { + if (region == null) { + return null; + } + String code = region.getSourceExcerpt(); + if (code.length() == 0) { + return null; + } + + // max length of the number display + int numberLength = Integer.toString(region.getEndingLineNumber()) + .length(); + + // formatting + StringBuilder builder = new StringBuilder(code.length() * 2); + int start = 0; + int end = code.indexOf('\n', start); + int lineNumber = region.getBeginningLineNumber(); + while (start >= 0) { + // line extraction + String line; + if (end < 0) { + line = code.substring(start); + if (line.length() == 0) { + return builder.substring(0, builder.length() - 1); + } + } else { + line = code.substring(start, end); + } + builder.append(" "); + + // nice spaces for the line number + int spaces = numberLength - Integer.toString(lineNumber).length(); + builder.append(Strings.repeat(" ", spaces)); + builder.append(lineNumber); + builder.append("| "); + + // end & update + if (end < 0) { + builder.append(line); + start = -1; + } else { + builder.append(line); + builder.append('\n'); + start = end + 1; + end = code.indexOf('\n', start); + lineNumber++; + } + } + return builder.toString(); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/LineNumberCheck.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/LineNumberCheck.java new file mode 100644 index 0000000..648b596 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/LineNumberCheck.java @@ -0,0 +1,81 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.rhino.Node; + +/** + * A simple pass to ensure that all AST nodes have line numbers, + * an that the line numbers are monotonically increasing. + * + * @author nicksantos@google.com (Nick Santos) + */ +class LineNumberCheck implements Callback, CompilerPass { + + static final DiagnosticType MISSING_LINE_INFO = DiagnosticType.error( + "JSC_MISSING_LINE_INFO", + "No source location information associated with {0}.\n" + + "Most likely a Node has been created with settings the source file " + + "and line/column location. Usually this is done using " + + "Node.copyInformationFrom and supplying a Node from the source AST."); + + private final AbstractCompiler compiler; + private boolean requiresLineNumbers = false; + + LineNumberCheck(AbstractCompiler compiler) { + this.compiler = compiler; + } + + public void setCheckSubTree(Node root) { + requiresLineNumbers = true; + + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void process(Node externs, Node root) { + requiresLineNumbers = false; + + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + // Each JavaScript file is rooted in a script node, so we'll only + // have line number information inside the script node. + if (n.isScript()) { + requiresLineNumbers = true; + } + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isScript()) { + requiresLineNumbers = false; + } else if (requiresLineNumbers) { + if (n.getLineno() == -1) { + // The tree version of the node is really the best diagnostic + // info we have to offer here. + compiler.report( + t.makeError(n, MISSING_LINE_INFO, + n.toStringTree())); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/LinkedFlowScope.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/LinkedFlowScope.java new file mode 100644 index 0000000..a97d855 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/LinkedFlowScope.java @@ -0,0 +1,495 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.type.FlowScope; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.SimpleSlot; +import com.google.javascript.rhino.jstype.StaticScope; +import com.google.javascript.rhino.jstype.StaticSlot; + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * A flow scope that tries to store as little symbol information as possible, + * instead delegating to its parents. Optimized for low memory use. + * + * @author nicksantos@google.com (Nick Santos) + */ +class LinkedFlowScope implements FlowScope { + // The closest flow scope cache. + private final FlatFlowScopeCache cache; + + // The parent flow scope. + private final LinkedFlowScope parent; + + // The distance between this flow scope and the closest flat flow scope. + private int depth; + + static final int MAX_DEPTH = 250; + + // A FlatFlowScopeCache equivalent to this scope. + private FlatFlowScopeCache flattened; + + // Flow scopes assume that all their ancestors are immutable. + // So once a child scope is created, this flow scope may not be modified. + private boolean frozen = false; + + // The last slot defined in this flow instruction, and the head of the + // linked list of slots. + private LinkedFlowSlot lastSlot; + + private LinkedFlowScope(FlatFlowScopeCache cache, + LinkedFlowScope directParent) { + this.cache = cache; + if (directParent == null) { + this.lastSlot = null; + this.depth = 0; + this.parent = cache.linkedEquivalent; + } else { + this.lastSlot = directParent.lastSlot; + this.depth = directParent.depth + 1; + this.parent = directParent; + } + } + + LinkedFlowScope(FlatFlowScopeCache cache) { + this(cache, null); + } + + LinkedFlowScope(LinkedFlowScope directParent) { + this(directParent.cache, directParent); + } + + /** Gets the function scope for this flow scope. */ + private Scope getFunctionScope() { + return cache.functionScope; + } + + /** Whether this flows from a bottom scope. */ + private boolean flowsFromBottom() { + return getFunctionScope().isBottom(); + } + + /** + * Creates an entry lattice for the flow. + */ + public static LinkedFlowScope createEntryLattice(Scope scope) { + return new LinkedFlowScope(new FlatFlowScopeCache(scope)); + } + + @Override + public void inferSlotType(String symbol, JSType type) { + Preconditions.checkState(!frozen); + lastSlot = new LinkedFlowSlot(symbol, type, lastSlot); + depth++; + cache.dirtySymbols.add(symbol); + } + + @Override + public void inferQualifiedSlot(Node node, String symbol, JSType bottomType, + JSType inferredType) { + Scope functionScope = getFunctionScope(); + if (functionScope.isLocal()) { + if (functionScope.getVar(symbol) == null && !functionScope.isBottom()) { + functionScope.declare(symbol, node, bottomType, null); + } + + inferSlotType(symbol, inferredType); + } + } + + @Override + public JSType getTypeOfThis() { + return cache.functionScope.getTypeOfThis(); + } + + @Override + public Node getRootNode() { + return getFunctionScope().getRootNode(); + } + + @Override + public StaticScope getParentScope() { + return getFunctionScope().getParentScope(); + } + + /** + * Get the slot for the given symbol. + */ + @Override + public StaticSlot getSlot(String name) { + if (cache.dirtySymbols.contains(name)) { + for (LinkedFlowSlot slot = lastSlot; + slot != null; slot = slot.parent) { + if (slot.getName().equals(name)) { + return slot; + } + } + } + return cache.getSlot(name); + } + + @Override + public StaticSlot getOwnSlot(String name) { + throw new UnsupportedOperationException(); + } + + @Override + public FlowScope createChildFlowScope() { + frozen = true; + + if (depth > MAX_DEPTH) { + if (flattened == null) { + flattened = new FlatFlowScopeCache(this); + } + return new LinkedFlowScope(flattened); + } + + return new LinkedFlowScope(this); + } + + /** + * Iterate through all the linked flow scopes before this one. + * If there's one and only one slot defined between this scope + * and the blind scope, return it. + */ + @Override + public StaticSlot findUniqueRefinedSlot(FlowScope blindScope) { + StaticSlot result = null; + + for (LinkedFlowScope currentScope = this; + currentScope != blindScope; + currentScope = currentScope.parent) { + for (LinkedFlowSlot currentSlot = currentScope.lastSlot; + currentSlot != null && + (currentScope.parent == null || + currentScope.parent.lastSlot != currentSlot); + currentSlot = currentSlot.parent) { + if (result == null) { + result = currentSlot; + } else if (!currentSlot.getName().equals(result.getName())) { + return null; + } + } + } + + return result; + } + + /** + * Look through the given scope, and try to find slots where it doesn't + * have enough type information. Then fill in that type information + * with stuff that we've inferred in the local flow. + */ + @Override + public void completeScope(StaticScope staticScope) { + Scope scope = (Scope) staticScope; + for (Iterator it = scope.getVars(); it.hasNext();) { + Var var = it.next(); + if (var.isTypeInferred()) { + JSType type = var.getType(); + if (type == null || type.isUnknownType()) { + JSType flowType = getSlot(var.getName()).getType(); + var.setType(flowType); + } + } + } + } + + /** + * Remove flow scopes that add nothing to the flow. + */ + // NOTE(nicksantos): This function breaks findUniqueRefinedSlot, because + // findUniqueRefinedSlot assumes that this scope is a direct descendant + // of blindScope. This is not necessarily true if this scope has been + // optimize()d and blindScope has not. This should be fixed. For now, + // we only use optimize() where we know that we won't have to do + // a findUniqueRefinedSlot on it. + @Override + public LinkedFlowScope optimize() { + LinkedFlowScope current; + for (current = this; + current.parent != null && + current.lastSlot == current.parent.lastSlot; + current = current.parent) {} + return current; + } + + /** Join the two FlowScopes. */ + static class FlowScopeJoinOp extends JoinOp.BinaryJoinOp { + @SuppressWarnings("unchecked") + @Override + public FlowScope apply(FlowScope a, FlowScope b) { + // To join the two scopes, we have to + LinkedFlowScope linkedA = (LinkedFlowScope) a; + LinkedFlowScope linkedB = (LinkedFlowScope) b; + linkedA.frozen = true; + linkedB.frozen = true; + if (linkedA.optimize() == linkedB.optimize()) { + return linkedA.createChildFlowScope(); + } + return new LinkedFlowScope(new FlatFlowScopeCache(linkedA, linkedB)); + } + } + + @Override + public boolean equals(Object other) { + if (other instanceof LinkedFlowScope) { + LinkedFlowScope that = (LinkedFlowScope) other; + if (this.optimize() == that.optimize()) { + return true; + } + + // If two flow scopes are in the same function, then they could have + // two possible function scopes: the real one and the BOTTOM scope. + // If they have different function scopes, we *should* iterate through all + // the variables in each scope and compare. However, 99.9% of the time, + // they're not equal. And the other .1% of the time, we can pretend + // they're equal--this just means that data flow analysis will have + // to propagate the entry lattice a little bit further than it + // really needs to. Everything will still come out ok. + if (this.getFunctionScope() != that.getFunctionScope()) { + return false; + } + + if (cache == that.cache) { + // If the two flow scopes have the same cache, then we can check + // equality a lot faster: by just looking at the "dirty" elements + // in the cache, and comparing them in both scopes. + for (String name : cache.dirtySymbols) { + if (diffSlots(getSlot(name), that.getSlot(name))) { + return false; + } + } + + return true; + } + + Map> myFlowSlots = allFlowSlots(); + Map> otherFlowSlots = that.allFlowSlots(); + + for (StaticSlot slot : myFlowSlots.values()) { + if (diffSlots(slot, otherFlowSlots.get(slot.getName()))) { + return false; + } + otherFlowSlots.remove(slot.getName()); + } + for (StaticSlot slot : otherFlowSlots.values()) { + if (diffSlots(slot, myFlowSlots.get(slot.getName()))) { + return false; + } + } + return true; + } + return false; + } + + /** + * Determines whether two slots are meaningfully different for the + * purposes of data flow analysis. + */ + private boolean diffSlots(StaticSlot slotA, + StaticSlot slotB) { + boolean aIsNull = slotA == null || slotA.getType() == null; + boolean bIsNull = slotB == null || slotB.getType() == null; + if (aIsNull && bIsNull) { + return false; + } else if (aIsNull ^ bIsNull) { + return true; + } + + // Both slots and types must be non-null. + return slotA.getType().differsFrom(slotB.getType()); + } + + /** + * Gets all the symbols that have been defined before this point + * in the current flow. Does not return slots that have not changed during + * the flow. + * + * For example, consider the code: + * + * var x = 3; + * function f() { + * var y = 5; + * y = 6; // FLOW POINT + * var z = y; + * return z; + * } + * + * A FlowScope at FLOW POINT will return a slot for y, but not + * a slot for x or z. + */ + private Map> allFlowSlots() { + Map> slots = Maps.newHashMap(); + for (LinkedFlowSlot slot = lastSlot; + slot != null; slot = slot.parent) { + if (!slots.containsKey(slot.getName())) { + slots.put(slot.getName(), slot); + } + } + + for (Map.Entry> symbolEntry : cache.symbols.entrySet()) { + if (!slots.containsKey(symbolEntry.getKey())) { + slots.put(symbolEntry.getKey(), symbolEntry.getValue()); + } + } + + return slots; + } + + /** + * A static slot that can be used in a linked list. + */ + private static class LinkedFlowSlot extends SimpleSlot { + final LinkedFlowSlot parent; + + LinkedFlowSlot(String name, JSType type, LinkedFlowSlot parent) { + super(name, type, true); + this.parent = parent; + } + } + + /** + * A map that tries to cache as much symbol table information + * as possible in a map. Optimized for fast lookup. + */ + private static class FlatFlowScopeCache { + // The Scope for the entire function or for the global scope. + private final Scope functionScope; + + // The linked flow scope that this cache represents. + private final LinkedFlowScope linkedEquivalent; + + // All the symbols defined before this point in the local flow. + // May not include lazily declared qualified names. + private Map> symbols = Maps.newHashMap(); + + // Used to help make lookup faster for LinkedFlowScopes by recording + // symbols that may be redefined "soon", for an arbitrary definition + // of "soon". ;) + // + // More rigorously, if a symbol is redefined in a LinkedFlowScope, + // and this is the closest FlatFlowScopeCache, then that symbol is marked + // "dirty". In this way, we don't waste time looking in the LinkedFlowScope + // list for symbols that aren't defined anywhere nearby. + final Set dirtySymbols = Sets.newHashSet(); + + // The cache at the bottom of the lattice. + FlatFlowScopeCache(Scope functionScope) { + this.functionScope = functionScope; + symbols = ImmutableMap.of(); + linkedEquivalent = null; + } + + // A cache in the middle of a long scope chain. + FlatFlowScopeCache(LinkedFlowScope directParent) { + FlatFlowScopeCache cache = directParent.cache; + + functionScope = cache.functionScope; + symbols = directParent.allFlowSlots(); + linkedEquivalent = directParent; + } + + // A cache at the join of two scope chains. + FlatFlowScopeCache(LinkedFlowScope joinedScopeA, + LinkedFlowScope joinedScopeB) { + linkedEquivalent = null; + + // Always prefer the "real" function scope to the faked-out + // bottom scope. + functionScope = joinedScopeA.flowsFromBottom() ? + joinedScopeB.getFunctionScope() : joinedScopeA.getFunctionScope(); + + Map> slotsA = joinedScopeA.allFlowSlots(); + Map> slotsB = joinedScopeB.allFlowSlots(); + + symbols = slotsA; + + // There are 5 different join cases: + // 1) The type is declared in joinedScopeA, not in joinedScopeB, + // and not in functionScope. Just use the one in A. + // 2) The type is declared in joinedScopeB, not in joinedScopeA, + // and not in functionScope. Just use the one in B. + // 3) The type is declared in functionScope and joinedScopeA, but + // not in joinedScopeB. Join the two types. + // 4) The type is declared in functionScope and joinedScopeB, but + // not in joinedScopeA. Join the two types. + // 5) The type is declared in joinedScopeA and joinedScopeB. Join + // the two types. + Set symbolNames = Sets.newHashSet(symbols.keySet()); + symbolNames.addAll(slotsB.keySet()); + + for (String name : symbolNames) { + StaticSlot slotA = slotsA.get(name); + StaticSlot slotB = slotsB.get(name); + + JSType joinedType = null; + if (slotB == null || slotB.getType() == null) { + StaticSlot fnSlot + = joinedScopeB.getFunctionScope().getSlot(name); + JSType fnSlotType = fnSlot == null ? null : fnSlot.getType(); + if (fnSlotType == null) { + // Case #1 -- already inserted. + } else { + // Case #3 + joinedType = slotA.getType().getLeastSupertype(fnSlotType); + } + } else if (slotA == null || slotA.getType() == null) { + StaticSlot fnSlot + = joinedScopeA.getFunctionScope().getSlot(name); + JSType fnSlotType = fnSlot == null ? null : fnSlot.getType(); + if (fnSlotType == null) { + // Case #2 + symbols.put(name, slotB); + } else { + // Case #4 + joinedType = slotB.getType().getLeastSupertype(fnSlotType); + } + } else { + // Case #5 + joinedType = + slotA.getType().getLeastSupertype(slotB.getType()); + } + + if (joinedType != null) { + symbols.put(name, new SimpleSlot(name, joinedType, true)); + } + } + } + + /** + * Get the slot for the given symbol. + */ + public StaticSlot getSlot(String name) { + if (symbols.containsKey(name)) { + return symbols.get(name); + } else { + return functionScope.getSlot(name); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/LiveVariablesAnalysis.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/LiveVariablesAnalysis.java new file mode 100644 index 0000000..888fd39 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/LiveVariablesAnalysis.java @@ -0,0 +1,309 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; +import com.google.javascript.jscomp.graph.LatticeElement; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.BitSet; +import java.util.List; +import java.util.Set; + +/** + * Compute the "liveness" of all local variables. A variable is "live" at a + * point of a program if the value it is currently holding might be read later. + * Otherwise, the variable is considered "dead" if we know for sure that it will + * no longer be read. Dead variables are candidates for dead assignment + * elimination and variable name sharing. The worst case safe assumption is to + * assume that all variables are live. In that case, we will have no opportunity + * for optimizations. This is especially the case within a TRY block when an + * assignment is not guaranteed to take place. We bail out by assuming that + * all variables are live. + *

      + * Due to the possibility of inner functions and closures, certain "local" + * variables can escape the function. These variables will be considered as + * global and they can be retrieved with {@link #getEscapedLocals()}. + * + */ +class LiveVariablesAnalysis extends + DataFlowAnalysis { + + // 100 = ((# of original Power Rangers) ^ + // (# years of Warren Harding in office)) * + // (# of Ninja Turtles) + static final int MAX_VARIABLES_TO_ANALYZE = 100; + + public static final String ARGUMENT_ARRAY_ALIAS = "arguments"; + + private static class LiveVariableJoinOp + implements JoinOp { + @Override + public LiveVariableLattice apply(List in) { + LiveVariableLattice result = new LiveVariableLattice(in.get(0)); + for (int i = 1; i < in.size(); i++) { + result.liveSet.or(in.get(i).liveSet); + } + return result; + } + } + + /** + * The lattice that stores the liveness of all local variables at a given + * point in the program. The whole lattice is the power set of all local + * variables and a variable is live if it is in the set. + */ + static class LiveVariableLattice implements LatticeElement { + private final BitSet liveSet; + + /** + * @param numVars Number of all local variables. + */ + private LiveVariableLattice(int numVars) { + this.liveSet = new BitSet(numVars); + } + + private LiveVariableLattice(LiveVariableLattice other) { + Preconditions.checkNotNull(other); + this.liveSet = (BitSet) other.liveSet.clone(); + } + + @Override + public boolean equals(Object other) { + Preconditions.checkNotNull(other); + return (other instanceof LiveVariableLattice) && + this.liveSet.equals(((LiveVariableLattice) other).liveSet); + } + + public boolean isLive(Var v) { + Preconditions.checkNotNull(v); + return liveSet.get(v.index); + } + + public boolean isLive(int index) { + return liveSet.get(index); + } + + @Override + public String toString() { + return liveSet.toString(); + } + + @Override + public int hashCode() { + return liveSet.hashCode(); + } + } + + // The scope of the function that we are analyzing. + private final Scope jsScope; + private final Set escaped; + + LiveVariablesAnalysis(ControlFlowGraph cfg, Scope jsScope, + AbstractCompiler compiler) { + super(cfg, new LiveVariableJoinOp()); + this.jsScope = jsScope; + this.escaped = Sets.newHashSet(); + computeEscaped(jsScope, escaped, compiler); + } + + public Set getEscapedLocals() { + return escaped; + } + + public int getVarIndex(String var) { + return jsScope.getVar(var).index; + } + + @Override + boolean isForward() { + return false; + } + + @Override + LiveVariableLattice createEntryLattice() { + return new LiveVariableLattice(jsScope.getVarCount()); + } + + @Override + LiveVariableLattice createInitialEstimateLattice() { + return new LiveVariableLattice(jsScope.getVarCount()); + } + + @Override + LiveVariableLattice flowThrough(Node node, LiveVariableLattice input) { + final BitSet gen = new BitSet(input.liveSet.size()); + final BitSet kill = new BitSet(input.liveSet.size()); + + // Make kills conditional if the node can end abruptly by an exception. + boolean conditional = false; + List> edgeList = getCfg().getOutEdges(node); + for (DiGraphEdge edge : edgeList) { + if (Branch.ON_EX.equals(edge.getValue())) { + conditional = true; + } + } + computeGenKill(node, gen, kill, conditional); + LiveVariableLattice result = new LiveVariableLattice(input); + // L_in = L_out - Kill + Gen + result.liveSet.andNot(kill); + result.liveSet.or(gen); + return result; + } + + /** + * Computes the GEN and KILL set. + * + * @param n Root node. + * @param gen Local variables that are live because of the instruction at + * {@code n} will be added to this set. + * @param kill Local variables that are killed because of the instruction at + * {@code n} will be added to this set. + * @param conditional {@code true} if any assignments encountered are + * conditionally executed. These assignments might not kill a variable. + */ + private void computeGenKill(Node n, BitSet gen, BitSet kill, + boolean conditional) { + + switch (n.getType()) { + case Token.SCRIPT: + case Token.BLOCK: + case Token.FUNCTION: + return; + + case Token.WHILE: + case Token.DO: + case Token.IF: + computeGenKill(NodeUtil.getConditionExpression(n), gen, kill, + conditional); + return; + + case Token.FOR: + if (!NodeUtil.isForIn(n)) { + computeGenKill(NodeUtil.getConditionExpression(n), gen, kill, + conditional); + } else { + // for(x in y) {...} + Node lhs = n.getFirstChild(); + Node rhs = lhs.getNext(); + if (lhs.isVar()) { + // for(var x in y) {...} + lhs = lhs.getLastChild(); + } + + if (lhs.isName()) { + addToSetIfLocal(lhs, kill); + addToSetIfLocal(lhs, gen); + } else { + computeGenKill(lhs, gen, kill, conditional); + } + + // rhs is executed only once so we don't go into it every loop. + } + return; + + case Token.VAR: + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + if (c.hasChildren()) { + computeGenKill(c.getFirstChild(), gen, kill, conditional); + if (!conditional) { + addToSetIfLocal(c, kill); + } + } + } + return; + + case Token.AND: + case Token.OR: + computeGenKill(n.getFirstChild(), gen, kill, conditional); + // May short circuit. + computeGenKill(n.getLastChild(), gen, kill, true); + return; + + case Token.HOOK: + computeGenKill(n.getFirstChild(), gen, kill, conditional); + // Assume both sides are conditional. + computeGenKill(n.getFirstChild().getNext(), gen, kill, true); + computeGenKill(n.getLastChild(), gen, kill, true); + return; + + case Token.NAME: + if (isArgumentsName(n)) { + markAllParametersEscaped(); + } else { + addToSetIfLocal(n, gen); + } + return; + + default: + if (NodeUtil.isAssignmentOp(n) && n.getFirstChild().isName()) { + Node lhs = n.getFirstChild(); + if (!conditional) { + addToSetIfLocal(lhs, kill); + } + if (!n.isAssign()) { + // assignments such as a += 1 reads a. + addToSetIfLocal(lhs, gen); + } + computeGenKill(lhs.getNext(), gen, kill, conditional); + } else { + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + computeGenKill(c, gen, kill, conditional); + } + } + return; + } + } + + private void addToSetIfLocal(Node node, BitSet set) { + Preconditions.checkState(node.isName()); + String name = node.getString(); + if (!jsScope.isDeclared(name, false)) { + return; + } + Var var = jsScope.getVar(name); + if (!escaped.contains(var)) { + set.set(var.index); + } + } + + /** + * Give up computing liveness of formal parameter by putting all the parameter + * names in the escaped set. + */ + void markAllParametersEscaped() { + Node lp = jsScope.getRootNode().getFirstChild().getNext(); + for(Node arg = lp.getFirstChild(); arg != null; arg = arg.getNext()) { + escaped.add(jsScope.getVar(arg.getString())); + } + } + + private boolean isArgumentsName(Node n) { + if (!n.isName()|| + !n.getString().equals(ARGUMENT_ARRAY_ALIAS) || + jsScope.isDeclared(ARGUMENT_ARRAY_ALIAS, false)) { + return false; + } else { + return true; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/LoggerErrorManager.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/LoggerErrorManager.java new file mode 100644 index 0000000..00a76b7 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/LoggerErrorManager.java @@ -0,0 +1,77 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.CheckLevel; + +import java.util.logging.Logger; +import java.util.logging.Level; + +/** + * An error manager that logs errors and warnings using a logger in addition to + * collecting them in memory. Errors are logged at the SEVERE level and warnings + * are logged at the WARNING level. + * + */ +public class LoggerErrorManager extends BasicErrorManager { + private final MessageFormatter formatter; + private final Logger logger; + + /** + * Creates an instance. + */ + public LoggerErrorManager(MessageFormatter formatter, Logger logger) { + this.formatter = formatter; + this.logger = logger; + } + + /** + * Creates an instance with a source-less error formatter. + */ + public LoggerErrorManager(Logger logger) { + this(ErrorFormat.SOURCELESS.toFormatter(null, false), logger); + } + + @Override + public void println(CheckLevel level, JSError error) { + switch (level) { + case ERROR: + logger.severe(error.format(level, formatter)); + break; + case WARNING: + logger.warning(error.format(level, formatter)); + break; + case OFF: + break; + } + } + + @Override + protected void printSummary() { + Level level = (getErrorCount() + getWarningCount() == 0) ? + Level.INFO : Level.WARNING; + if (getTypedPercent() > 0.0) { + logger.log(level, "{0} error(s), {1} warning(s), {2,number,#.#}% typed", + new Object[] {getErrorCount(), getWarningCount(), getTypedPercent()}); + } else { + if (getErrorCount() + getWarningCount() > 0) { + logger.log(level, "{0} error(s), {1} warning(s)", + new Object[] {getErrorCount(), getWarningCount()}); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MakeDeclaredNamesUnique.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MakeDeclaredNamesUnique.java new file mode 100644 index 0000000..3743287 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MakeDeclaredNamesUnique.java @@ -0,0 +1,624 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multiset; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.TokenStream; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Find all Functions, VARs, and Exception names and make them + * unique. Specifically, it will not modify object properties. + * @author johnlenz@google.com (John Lenz) + * TODO(johnlenz): Try to merge this with the ScopeCreator. + */ +class MakeDeclaredNamesUnique + implements NodeTraversal.ScopedCallback { + + // Arguments is special cased to handle cases where a local name shadows + // the arguments declaration. + public static final String ARGUMENTS = "arguments"; + + // The name stack is similar to how we model scopes but handles some + // additional cases that are not handled by the current Scope object. + // Specifically, a Scope currently has only two concepts of scope (global, + // and function local). But there are in reality a couple of additional + // case to worry about: + // catch expressions + // function expressions names + // Both belong to a scope by themselves. + private Deque nameStack = new ArrayDeque(); + private final Renamer rootRenamer; + + MakeDeclaredNamesUnique() { + this(new ContextualRenamer()); + } + + MakeDeclaredNamesUnique(Renamer renamer) { + this.rootRenamer = renamer; + } + + static CompilerPass getContextualRenameInverter(AbstractCompiler compiler) { + return new ContextualRenameInverter(compiler); + } + + @Override + public void enterScope(NodeTraversal t) { + Node declarationRoot = t.getScopeRoot(); + Renamer renamer; + if (nameStack.isEmpty()) { + // If the contextual renamer is being used, the starting context can not + // be a function. + Preconditions.checkState( + !declarationRoot.isFunction() || + !(rootRenamer instanceof ContextualRenamer)); + Preconditions.checkState(t.inGlobalScope()); + renamer = rootRenamer; + } else { + renamer = nameStack.peek().forChildScope(); + } + + if (!declarationRoot.isFunction()) { + // Add the block declarations + findDeclaredNames(declarationRoot, null, renamer); + } + nameStack.push(renamer); + } + + @Override + public void exitScope(NodeTraversal t) { + if (!t.inGlobalScope()) { + nameStack.pop(); + } + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + + switch (n.getType()) { + case Token.FUNCTION: + { + // Add recursive function name, if needed. + // NOTE: "enterScope" is called after we need to pick up this name. + Renamer renamer = nameStack.peek().forChildScope(); + + // If needed, add the function recursive name. + String name = n.getFirstChild().getString(); + if (name != null && !name.isEmpty() && parent != null + && !NodeUtil.isFunctionDeclaration(n)) { + renamer.addDeclaredName(name); + } + + nameStack.push(renamer); + } + break; + + case Token.PARAM_LIST: { + Renamer renamer = nameStack.peek().forChildScope(); + + // Add the function parameters + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + String name = c.getString(); + renamer.addDeclaredName(name); + } + + // Add the function body declarations + Node functionBody = n.getNext(); + findDeclaredNames(functionBody, null, renamer); + + nameStack.push(renamer); + } + break; + + case Token.CATCH: + { + Renamer renamer = nameStack.peek().forChildScope(); + + String name = n.getFirstChild().getString(); + renamer.addDeclaredName(name); + + nameStack.push(renamer); + } + break; + } + + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.NAME: + String newName = getReplacementName(n.getString()); + if (newName != null) { + Renamer renamer = nameStack.peek(); + if (renamer.stripConstIfReplaced()) { + // TODO(johnlenz): Do we need to do anything about the Javadoc? + n.removeProp(Node.IS_CONSTANT_NAME); + } + n.setString(newName); + t.getCompiler().reportCodeChange(); + } + break; + + case Token.FUNCTION: + // Remove the function body scope + nameStack.pop(); + // Remove function recursive name (if any). + nameStack.pop(); + break; + + case Token.PARAM_LIST: + // Note: The parameters and function body variables live in the + // same scope, we introduce the scope when in the "shouldTraverse" + // visit of LP, but remove it when when we exit the function above. + break; + + case Token.CATCH: + // Remove catch except name from the stack of names. + nameStack.pop(); + break; + } + } + + /** + * Walks the stack of name maps and finds the replacement name for the + * current scope. + */ + private String getReplacementName(String oldName) { + for (Renamer names : nameStack) { + String newName = names.getReplacementName(oldName); + if (newName != null) { + return newName; + } + } + return null; + } + + /** + * Traverses the current scope and collects declared names. Does not + * decent into functions or add CATCH exceptions. + */ + private void findDeclaredNames(Node n, Node parent, Renamer renamer) { + // Do a shallow traversal, so don't traverse into function declarations, + // except for the name of the function itself. + if (parent == null + || !parent.isFunction() + || n == parent.getFirstChild()) { + if (NodeUtil.isVarDeclaration(n)) { + renamer.addDeclaredName(n.getString()); + } else if (NodeUtil.isFunctionDeclaration(n)) { + Node nameNode = n.getFirstChild(); + renamer.addDeclaredName(nameNode.getString()); + } + + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + findDeclaredNames(c, n, renamer); + } + } + } + + /** + * Declared names renaming policy interface. + */ + interface Renamer { + + /** + * Called when a declared name is found in the local current scope. + */ + void addDeclaredName(String name); + + /** + * @return A replacement name, null if oldName is unknown or should not + * be replaced. + */ + String getReplacementName(String oldName); + + /** + * @return Whether the constant-ness of a name should be removed. + */ + boolean stripConstIfReplaced(); + + /** + * @return A Renamer for a scope within the scope of the current Renamer. + */ + Renamer forChildScope(); + } + + /** + * Inverts the transformation by {@link ContextualRenamer}, when possible. + */ + static class ContextualRenameInverter + implements ScopedCallback, CompilerPass { + private final AbstractCompiler compiler; + + // The set of names referenced in the current scope. + private Set referencedNames = ImmutableSet.of(); + + // Stack reference sets. + private Deque> referenceStack = new ArrayDeque>(); + + // Name are globally unique initially, so we don't need a per-scope map. + private Map> nameMap = Maps.newHashMap(); + + private ContextualRenameInverter(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node js) { + NodeTraversal.traverse(compiler, js, this); + } + + public static String getOrginalName(String name) { + int index = indexOfSeparator(name); + return (index == -1) ? name : name.substring(0, index); + } + + private static int indexOfSeparator(String name) { + return name.lastIndexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR); + } + + private boolean containsSeparator(String name) { + return name.indexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR) != -1; + } + + /** + * Prepare a set for the new scope. + */ + @Override + public void enterScope(NodeTraversal t) { + if (t.inGlobalScope()) { + return; + } + + referenceStack.push(referencedNames); + referencedNames = Sets.newHashSet(); + } + + /** + * Rename vars for the current scope, and merge any referenced + * names into the parent scope reference set. + */ + @Override + public void exitScope(NodeTraversal t) { + if (t.inGlobalScope()) { + return; + } + + for (Iterator it = t.getScope().getVars(); it.hasNext();) { + Var v = it.next(); + handleScopeVar(v); + } + + // Merge any names that were referenced but not declared in the current + // scope. + Set current = referencedNames; + referencedNames = referenceStack.pop(); + // If there isn't anything left in the stack we will be going into the + // global scope: don't try to build a set of referenced names for the + // global scope. + if (!referenceStack.isEmpty()) { + referencedNames.addAll(current); + } + } + + /** + * For the Var declared in the current scope determine if it is possible + * to revert the name to its original form without conflicting with other + * values. + */ + void handleScopeVar(Var v) { + String name = v.getName(); + if (containsSeparator(name) && !getOrginalName(name).isEmpty()) { + String newName = findReplacementName(name); + referencedNames.remove(name); + // Adding a reference to the new name to prevent either the parent + // scopes or the current scope renaming another var to this new name. + referencedNames.add(newName); + List references = nameMap.get(name); + Preconditions.checkState(references != null); + for (Node n : references) { + Preconditions.checkState(n.isName()); + n.setString(newName); + } + compiler.reportCodeChange(); + nameMap.remove(name); + } + } + + /** + * Find a name usable in the local scope. + */ + private String findReplacementName(String name) { + String original = getOrginalName(name); + String newName = original; + int i = 0; + while (!isValidName(newName)) { + newName = original + + ContextualRenamer.UNIQUE_ID_SEPARATOR + String.valueOf(i++); + } + return newName; + } + + /** + * @return Whether the name is valid to use in the local scope. + */ + private boolean isValidName(String name) { + if (TokenStream.isJSIdentifier(name) && + !referencedNames.contains(name) && + !name.equals(ARGUMENTS)) { + return true; + } + return false; + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + return true; + } + + @Override + public void visit(NodeTraversal t, Node node, Node parent) { + if (t.inGlobalScope()) { + return; + } + + if (NodeUtil.isReferenceName(node)) { + String name = node.getString(); + // Add all referenced names to the set so it is possible to check for + // conflicts. + referencedNames.add(name); + // Store only references to candidate names in the node map. + if (containsSeparator(name)) { + addCandidateNameReference(name, node); + } + } + } + + private void addCandidateNameReference(String name, Node n) { + List nodes = nameMap.get(name); + if (null == nodes) { + nodes = Lists.newLinkedList(); + nameMap.put(name, nodes); + } + nodes.add(n); + } + } + + /** + * Rename every locally name to be unique, the first encountered declaration + * (specifically global names) are left in their original form. Those that are + * renamed are made unique by giving them a unique suffix based on + * the number of declarations of the name. + * + * The root ContextualRenamer is assumed to be in GlobalScope. + * + * Used by the Normalize pass. + * @see Normalize + */ + static class ContextualRenamer implements Renamer { + private final Multiset nameUsage; + private final Map declarations = Maps.newHashMap(); + private final boolean global; + + final static String UNIQUE_ID_SEPARATOR = "$$"; + + ContextualRenamer() { + this.global = true; + nameUsage = HashMultiset.create(); + } + + /** + * Constructor for child scopes. + */ + private ContextualRenamer(Multiset nameUsage) { + this.global = false; + this.nameUsage = nameUsage; + } + + /** + * Create a ContextualRenamer + */ + @Override + public Renamer forChildScope() { + return new ContextualRenamer(nameUsage); + } + + /** + * Adds a name to the map of names declared in this scope. + */ + @Override + public void addDeclaredName(String name) { + if (!name.equals(ARGUMENTS)) { + if (global) { + reserveName(name); + } else { + // It hasn't been declared locally yet, so increment the count. + if (!declarations.containsKey(name)) { + int id = incrementNameCount(name); + String newName = null; + if (id != 0) { + newName = getUniqueName(name, id); + } + declarations.put(name, newName); + } + } + } + } + + @Override + public String getReplacementName(String oldName) { + return declarations.get(oldName); + } + + /** + * Given a name and the associated id, create a new unique name. + */ + private String getUniqueName(String name, int id) { + return name + UNIQUE_ID_SEPARATOR + id; + } + + private void reserveName(String name) { + nameUsage.setCount(name, 0, 1); + } + + private int incrementNameCount(String name) { + return nameUsage.add(name, 1); + } + + @Override + public boolean stripConstIfReplaced() { + return false; + } + } + + + /** + * Rename every declared name to be unique. Typically this would be used + * when injecting code to insure that names do not conflict with existing + * names. + * + * Used by the FunctionInjector + * @see FunctionInjector + */ + static class InlineRenamer implements Renamer { + private final Map declarations = Maps.newHashMap(); + private final Supplier uniqueIdSupplier; + private final String idPrefix; + private final boolean removeConstness; + + InlineRenamer( + Supplier uniqueIdSupplier, + String idPrefix, + boolean removeConstness) { + this.uniqueIdSupplier = uniqueIdSupplier; + // To ensure that the id does not conflict with the id from the + // ContextualRenamer some prefix is needed. + Preconditions.checkArgument(!idPrefix.isEmpty()); + this.idPrefix = idPrefix; + this.removeConstness = removeConstness; + } + + @Override + public void addDeclaredName(String name) { + Preconditions.checkState(!name.equals(ARGUMENTS)); + if (!declarations.containsKey(name)) { + declarations.put(name, getUniqueName(name)); + } + } + + private String getUniqueName(String name) { + if (name.isEmpty()) { + return name; + } + + if (name.indexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR) != -1) { + name = name.substring( + 0, name.lastIndexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR)); + } + + // By using the same separator the id will be stripped if it isn't + // needed when variable renaming is turned off. + return name + ContextualRenamer.UNIQUE_ID_SEPARATOR + + idPrefix + uniqueIdSupplier.get(); + } + + @Override + public String getReplacementName(String oldName) { + return declarations.get(oldName); + } + + @Override + public Renamer forChildScope() { + return new InlineRenamer(uniqueIdSupplier, idPrefix, removeConstness); + } + + @Override + public boolean stripConstIfReplaced() { + return removeConstness; + } + } + + /** + * For injecting boilerplate libraries. Leaves global names alone + * and renames local names like InlineRenamer. + */ + static class BoilerplateRenamer extends ContextualRenamer { + private final Supplier uniqueIdSupplier; + private final String idPrefix; + + BoilerplateRenamer( + Supplier uniqueIdSupplier, + String idPrefix) { + this.uniqueIdSupplier = uniqueIdSupplier; + this.idPrefix = idPrefix; + } + + @Override + public Renamer forChildScope() { + return new InlineRenamer(uniqueIdSupplier, idPrefix, false); + } + } + + /** Only rename things that match the whitelist. Wraps another renamer. */ + static class WhitelistedRenamer implements Renamer { + private Renamer delegate; + private Set whitelist; + + WhitelistedRenamer(Renamer delegate, Set whitelist) { + this.delegate = delegate; + this.whitelist = whitelist; + } + + @Override public void addDeclaredName(String name) { + if (whitelist.contains(name)) { + delegate.addDeclaredName(name); + } + } + + @Override public String getReplacementName(String oldName) { + return whitelist.contains(oldName) + ? delegate.getReplacementName(oldName) : null; + } + + @Override public boolean stripConstIfReplaced() { + return delegate.stripConstIfReplaced(); + } + + @Override public Renamer forChildScope() { + return new WhitelistedRenamer(delegate.forChildScope(), whitelist); + } + } + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MarkNoSideEffectCalls.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MarkNoSideEffectCalls.java new file mode 100644 index 0000000..c38fe65 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MarkNoSideEffectCalls.java @@ -0,0 +1,199 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.DefinitionsRemover.Definition; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * Set the NoSideEffects property for function and constructor calls + * that refer to functions that are known to have no side effects. + * Current implementation relies on @nosideeffects annotations at + * function definition sites; eventually we should traverse function + * bodies to determine if they have side effects. + * + */ +class MarkNoSideEffectCalls implements CompilerPass { + static final DiagnosticType INVALID_NO_SIDE_EFFECT_ANNOTATION = + DiagnosticType.error( + "JSC_INVALID_NO_SIDE_EFFECT_ANNOTATION", + "@nosideeffects may only appear in externs files."); + + private final AbstractCompiler compiler; + + // Left hand side expression associated with a function node that + // has a @nosideeffects annotation. + private final Set noSideEffectFunctionNames; + + MarkNoSideEffectCalls(AbstractCompiler compiler) { + this.compiler = compiler; + this.noSideEffectFunctionNames = Sets.newHashSet(); + } + + @Override + public void process(Node externs, Node root) { + SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); + defFinder.process(externs, root); + + // Gather the list of function nodes that have @nosideeffects annotations. + // For use by SetNoSideEffectCallProperty. + NodeTraversal.traverse( + compiler, externs, new GatherNoSideEffectFunctions(true)); + NodeTraversal.traverse( + compiler, root, new GatherNoSideEffectFunctions(false)); + + NodeTraversal.traverse(compiler, root, + new SetNoSideEffectCallProperty(defFinder)); + } + + /** + * Determines if the type of the value of the RHS expression can + * be a function node. + */ + private static boolean definitionTypeContainsFunctionType(Definition def) { + Node rhs = def.getRValue(); + if (rhs == null) { + return true; + } + + switch (rhs.getType()) { + case Token.ASSIGN: + case Token.AND: + case Token.CALL: + case Token.GETPROP: + case Token.GETELEM: + case Token.FUNCTION: + case Token.HOOK: + case Token.NAME: + case Token.NEW: + case Token.OR: + return true; + default: + return false; + } + } + + /** + * Get the value of the @nosideeffects annotation stored in the + * doc info. + */ + private static boolean hasNoSideEffectsAnnotation(Node node) { + JSDocInfo docInfo = node.getJSDocInfo(); + return docInfo != null && docInfo.isNoSideEffects(); + } + + /** + * Gather function nodes that have @nosideeffects annotations. + */ + private class GatherNoSideEffectFunctions extends AbstractPostOrderCallback { + private final boolean inExterns; + + GatherNoSideEffectFunctions(boolean inExterns) { + this.inExterns = inExterns; + } + + @Override + public void visit(NodeTraversal traversal, Node node, Node parent) { + if (!inExterns && hasNoSideEffectsAnnotation(node)) { + traversal.report(node, INVALID_NO_SIDE_EFFECT_ANNOTATION); + } + + if (node.isGetProp()) { + if (parent.isExprResult() && + hasNoSideEffectsAnnotation(node)) { + noSideEffectFunctionNames.add(node); + } + } else if (node.isFunction()) { + + // The annotation may attached to the function node, the + // variable declaration or assignment expression. + boolean hasAnnotation = hasNoSideEffectsAnnotation(node); + List nameNodes = Lists.newArrayList(); + nameNodes.add(node.getFirstChild()); + + Node nameNode = null; + + if (parent.isName()) { + Node gramp = parent.getParent(); + if (gramp.isVar() && + gramp.hasOneChild() && + hasNoSideEffectsAnnotation(gramp)) { + hasAnnotation = true; + } + + nameNodes.add(parent); + } else if (parent.isAssign()) { + if (hasNoSideEffectsAnnotation(parent)) { + hasAnnotation = true; + } + + nameNodes.add(parent.getFirstChild()); + } + + if (hasAnnotation) { + noSideEffectFunctionNames.addAll(nameNodes); + } + } + } + } + + /** + * Set the no side effects property for CALL and NEW nodes that + * refer to function names that are known to have no side effects. + */ + private class SetNoSideEffectCallProperty extends AbstractPostOrderCallback { + private final SimpleDefinitionFinder defFinder; + + SetNoSideEffectCallProperty(SimpleDefinitionFinder defFinder) { + this.defFinder = defFinder; + } + + @Override + public void visit(NodeTraversal traversal, Node node, Node parent) { + if (!node.isCall() && !node.isNew()) { + return; + } + + Collection definitions = + defFinder.getDefinitionsReferencedAt(node.getFirstChild()); + if (definitions == null) { + return; + } + + for (Definition def : definitions) { + Node lValue = def.getLValue(); + Preconditions.checkNotNull(lValue); + if (!noSideEffectFunctionNames.contains(lValue) && + definitionTypeContainsFunctionType(def)) { + return; + } + } + + node.setSideEffectFlags(Node.NO_SIDE_EFFECTS); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MaybeReachingVariableUse.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MaybeReachingVariableUse.java new file mode 100644 index 0000000..8361ac9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MaybeReachingVariableUse.java @@ -0,0 +1,302 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; +import com.google.javascript.jscomp.graph.GraphNode; +import com.google.javascript.jscomp.graph.LatticeElement; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * Computes "may be" reaching use for all definitions of each variables. + * + * A use of {@code A} in {@code alert(A)} is a "may be" reaching use of + * the definition of {@code A} at {@code A = foo()} if at least one path from + * the use node reaches that definition and it is the last definition before + * the use on that path. + * + */ +class MaybeReachingVariableUse extends + DataFlowAnalysis { + + // The scope of the function that we are analyzing. + private final Scope jsScope; + private final Set escaped; + + MaybeReachingVariableUse( + ControlFlowGraph cfg, Scope jsScope, AbstractCompiler compiler) { + super(cfg, new ReachingUsesJoinOp()); + this.jsScope = jsScope; + this.escaped = Sets.newHashSet(); + + // TODO(user): Maybe compute it somewhere else and re-use the escape + // local set here. + computeEscaped(jsScope, escaped, compiler); + } + + /** + * May use definition lattice representation. It captures a product + * lattice for each local (non-escaped) variable. The sub-lattice is + * a n + 2 power set element lattice with all the Nodes in the program, + * TOP and BOTTOM. This is better explained with an example: + * + * Consider: A sub-lattice element representing the variable A represented + * by { N_4, N_5} where N_x is a Node in the program. This implies at + * that particular point in the program the content of A is "upward exposed" + * at point N_4 and N_5. + * + * Example: + * + * A = 1; + * ... + * N_3: + * N_4: print(A); + * N_5: y = A; + * N_6: A = 1; + * N_7: print(A); + * + * At N_3, reads of A in {N_4, N_5} are said to be upward exposed. + */ + static final class ReachingUses implements LatticeElement { + final Multimap mayUseMap; + + public ReachingUses() { + mayUseMap = HashMultimap.create(); + } + + /** + * Copy constructor. + * + * @param other The constructed object is a replicated copy of this element. + */ + public ReachingUses(ReachingUses other) { + mayUseMap = HashMultimap.create(other.mayUseMap); + } + + @Override + public boolean equals(Object other) { + return (other instanceof ReachingUses) && + ((ReachingUses) other).mayUseMap.equals(this.mayUseMap); + } + + @Override + public int hashCode() { + return mayUseMap.hashCode(); + } + } + + /** + * The join is a simple union because of the "may be" nature of the analysis. + * + * Consider: A = 1; if (x) { A = 2 }; alert(A); + * + * The read of A "may be" exposed to A = 1 in the beginning. + */ + private static class ReachingUsesJoinOp implements JoinOp { + @Override + public ReachingUses apply(List from) { + ReachingUses result = new ReachingUses(); + for (ReachingUses uses : from) { + result.mayUseMap.putAll(uses.mayUseMap); + } + return result; + } + } + + @Override + boolean isForward() { + return false; + } + + @Override + ReachingUses createEntryLattice() { + return new ReachingUses(); + } + + @Override + ReachingUses createInitialEstimateLattice() { + return new ReachingUses(); + } + + @Override + ReachingUses flowThrough(Node n, ReachingUses input) { + ReachingUses output = new ReachingUses(input); + + // If there's an ON_EX edge, this cfgNode may or may not get executed. + // We can express this concisely by just pretending this happens in + // a conditional. + boolean conditional = hasExceptionHandler(n); + computeMayUse(n, n, output, conditional); + + return output; + } + + private boolean hasExceptionHandler(Node cfgNode) { + List> branchEdges = getCfg().getOutEdges(cfgNode); + for (DiGraphEdge edge : branchEdges) { + if (edge.getValue() == Branch.ON_EX) { + return true; + } + } + return false; + } + + private void computeMayUse( + Node n, Node cfgNode, ReachingUses output, boolean conditional) { + switch (n.getType()) { + + case Token.BLOCK: + case Token.FUNCTION: + return; + + case Token.NAME: + addToUseIfLocal(n.getString(), cfgNode, output); + return; + + case Token.WHILE: + case Token.DO: + case Token.IF: + computeMayUse( + NodeUtil.getConditionExpression(n), cfgNode, output, conditional); + return; + + case Token.FOR: + if (!NodeUtil.isForIn(n)) { + computeMayUse( + NodeUtil.getConditionExpression(n), cfgNode, output, conditional); + } else { + // for(x in y) {...} + Node lhs = n.getFirstChild(); + Node rhs = lhs.getNext(); + if (lhs.isVar()) { + lhs = lhs.getLastChild(); // for(var x in y) {...} + } + if (lhs.isName() && !conditional) { + removeFromUseIfLocal(lhs.getString(), output); + } + computeMayUse(rhs, cfgNode, output, conditional); + } + return; + + case Token.AND: + case Token.OR: + computeMayUse(n.getLastChild(), cfgNode, output, true); + computeMayUse(n.getFirstChild(), cfgNode, output, conditional); + return; + + case Token.HOOK: + computeMayUse(n.getLastChild(), cfgNode, output, true); + computeMayUse(n.getFirstChild().getNext(), cfgNode, output, true); + computeMayUse(n.getFirstChild(), cfgNode, output, conditional); + return; + + case Token.VAR: + Node varName = n.getFirstChild(); + Preconditions.checkState(n.hasChildren(), "AST should be normalized"); + + if (varName.hasChildren()) { + computeMayUse(varName.getFirstChild(), cfgNode, output, conditional); + if (!conditional) { + removeFromUseIfLocal(varName.getString(), output); + } + } + return; + + default: + if (NodeUtil.isAssignmentOp(n) && n.getFirstChild().isName()) { + Node name = n.getFirstChild(); + if (!conditional) { + removeFromUseIfLocal(name.getString(), output); + } + + // In case of a += "Hello". There is a read of a. + if (!n.isAssign()) { + addToUseIfLocal(name.getString(), cfgNode, output); + } + + computeMayUse(name.getNext(), cfgNode, output, conditional); + } else { + /* + * We want to traverse in reverse order because we want the LAST + * definition in the sub-tree.... + * But we have no better way to traverse in reverse other :'( + */ + for (Node c = n.getLastChild(); c != null; c = n.getChildBefore(c)) { + computeMayUse(c, cfgNode, output, conditional); + } + } + } + } + + /** + * Sets the variable for the given name to the node value in the upward + * exposed lattice. Do nothing if the variable name is one of the escaped + * variable. + */ + private void addToUseIfLocal(String name, Node node, ReachingUses use) { + Var var = jsScope.getVar(name); + if (var == null || var.scope != jsScope) { + return; + } + if (!escaped.contains(var)) { + use.mayUseMap.put(var, node); + } + } + + /** + * Removes the variable for the given name from the node value in the upward + * exposed lattice. Do nothing if the variable name is one of the escaped + * variable. + */ + private void removeFromUseIfLocal(String name, ReachingUses use) { + Var var = jsScope.getVar(name); + if (var == null || var.scope != jsScope) { + return; + } + if (!escaped.contains(var)) { + use.mayUseMap.removeAll(var); + } + } + + /** + * Gets a list of nodes that may be using the value assigned to {@code name} + * in {@code defNode}. {@code defNode} must be one of the control flow graph + * nodes. + * + * @param name name of the variable. It can only be names of local variable + * that are not function parameters, escaped variables or variables + * declared in catch. + * @param defNode The list of upward exposed use for the variable. + */ + Collection getUses(String name, Node defNode) { + GraphNode n = getCfg().getNode(defNode); + Preconditions.checkNotNull(n); + FlowState state = n.getAnnotation(); + return state.getOut().mayUseMap.get(jsScope.getVar(name)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MemoizedScopeCreator.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MemoizedScopeCreator.java new file mode 100644 index 0000000..47767e0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MemoizedScopeCreator.java @@ -0,0 +1,110 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.StaticSymbolTable; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Memoize a scope creator. + * + * This allows you to make multiple passes, without worrying about + * the expense of generating Scope objects over and over again. + * + * On the other hand, you also have to be more aware of what your passes + * are doing. Scopes are memoized stupidly, so if the underlying tree + * changes, the scope may be out of sync. + * + * @author nicksantos@google.com (Nick Santos) + */ +class MemoizedScopeCreator + implements ScopeCreator, StaticSymbolTable { + + private final Map scopes = Maps.newHashMap(); + private final ScopeCreator delegate; + + /** + * @param delegate The real source of Scope objects. + */ + MemoizedScopeCreator(ScopeCreator delegate) { + this.delegate = delegate; + } + + @Override + public Iterable getReferences(Var var) { + return ImmutableList.of(var); + } + + @Override + public Scope getScope(Var var) { + return var.scope; + } + + @Override + public Iterable getAllSymbols() { + List vars = Lists.newArrayList(); + for (Scope s : scopes.values()) { + Iterables.addAll(vars, s.getAllSymbols()); + } + return vars; + } + + @Override + public Scope createScope(Node n, Scope parent) { + Scope scope = scopes.get(n); + if (scope == null) { + scope = delegate.createScope(n, parent); + scopes.put(n, scope); + } else { + Preconditions.checkState(parent == scope.getParent()); + } + return scope; + } + + Collection getAllMemoizedScopes() { + return Collections.unmodifiableCollection(scopes.values()); + } + + Scope getScopeIfMemoized(Node n) { + return scopes.get(n); + } + + /** + * Removes all scopes with root nodes from a given script file. + * + * @param scriptName the name of the script file to remove nodes for. + */ + void removeScopesForScript(String scriptName) { + for (Node scopeRoot : ImmutableSet.copyOf(scopes.keySet())) { + if (scriptName.equals(scopeRoot.getSourceFileName())) { + scopes.remove(scopeRoot); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MessageBundle.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MessageBundle.java new file mode 100644 index 0000000..14efdda --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MessageBundle.java @@ -0,0 +1,50 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * An interface for providing alternative values for user-visible messages in + * JavaScript code. + * + */ +public interface MessageBundle { + + /** + * Gets the message ID generator to use to compute message IDs for this + * type of bundle. + * @return idGenerator instance or null if we do not want to use any custom + * id generation. In case if idGenerator is null caller should decide how + * to create id by itself. In the most cases using the message key is + * enough. + */ + public JsMessage.IdGenerator idGenerator(); + + /** + * Gets a message replacement. + * + * @param id the id of the message being replaced; the key is message ID + * generated by {@link JsMessage.IdGenerator} + * @return the message replacement, which may be null. + */ + public JsMessage getMessage(String id); + + /** + * Returns an iterable over the keys that this object has replacements for. + * @return all messages from this bundle. + */ + public Iterable getAllMessages(); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MessageFormatter.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MessageFormatter.java new file mode 100644 index 0000000..28a5a70 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MessageFormatter.java @@ -0,0 +1,33 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +/** + * Format warnings and errors. This interface may be used by implementations of + * {@link ErrorManager} to request message formatting capabilities. + * + */ +public interface MessageFormatter { + /** + * Format an error. + */ + String formatError(JSError error); + + /** + * Format a warning. + */ + String formatWarning(JSError warning); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MethodCompilerPass.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MethodCompilerPass.java new file mode 100644 index 0000000..786d143 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MethodCompilerPass.java @@ -0,0 +1,260 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.List; +import java.util.Set; + +/** + * Finds all method declarations and pulls them into data structures + * for use during cleanups such as arity checks or inlining. + * + */ +abstract class MethodCompilerPass implements CompilerPass { + /** List of methods defined in externs */ + final Set externMethods = Sets.newHashSet(); + + /** List of extern methods without signatures that we can't warn about */ + final Set externMethodsWithoutSignatures = Sets.newHashSet(); + + /** List of property names that may not be methods */ + final Set nonMethodProperties = Sets.newHashSet(); + + // Use a linked map here to keep the output deterministic. Otherwise, + // the choice of method bodies is random when multiple identical definitions + // are found which causes problems in the source maps. + final Multimap methodDefinitions = + LinkedHashMultimap.create(); + + final AbstractCompiler compiler; + + /** + * The signature storage is provided by the implementing class. + */ + interface SignatureStore { + public void reset(); + public void addSignature( + String functionName, Node functionNode, String sourceFile); + public void removeSignature(String functionName); + } + + MethodCompilerPass(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + externMethods.clear(); + externMethodsWithoutSignatures.clear(); + getSignatureStore().reset(); + methodDefinitions.clear(); + + if (externs != null) { + NodeTraversal.traverse(compiler, externs, new GetExternMethods()); + } + + + List externsAndJs = Lists.newArrayList(externs, root); + NodeTraversal.traverseRoots( + compiler, Lists.newArrayList(externs, root), new GatherSignatures()); + NodeTraversal.traverseRoots( + compiler, externsAndJs, getActingCallback()); + } + + /** + * Subclasses should return a callback that does the actual work they + * want to perform given the computed list of method signatures + */ + abstract Callback getActingCallback(); + + /** + * Subclasses should return a SignatureStore for storing discovered + * signatures. + */ + abstract SignatureStore getSignatureStore(); + + /** + * Adds a node that may represent a function signature (if it's a function + * itself or the name of a function). + */ + private void addPossibleSignature(String name, Node node, NodeTraversal t) { + if (node.isFunction()) { + // The node we're looking at is a function, so we can add it directly + addSignature(name, node, t.getSourceName()); + } else { + nonMethodProperties.add(name); + } + } + + private void addSignature(String name, Node function, String fnSourceName) { + if (externMethodsWithoutSignatures.contains(name)) { + return; + } + + getSignatureStore().addSignature(name, function, fnSourceName); + methodDefinitions.put(name, function); + } + + /** + * Gathers methods from the externs file. Methods that are listed there but + * do not have a signature are flagged to be ignored when doing arity checks. + * Methods that do include signatures will be checked. + */ + private class GetExternMethods extends AbstractPostOrderCallback { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.GETPROP: + case Token.GETELEM: { + Node dest = n.getFirstChild().getNext(); + + if (!dest.isString()) { + return; + } + + String name = dest.getString(); + + // We have a signature. Parse tree of the form: + // assign <- parent + // getprop <- n + // name methods + // string setTimeout + // function + if (parent.isAssign() && + parent.getFirstChild() == n && + n.getNext().isFunction()) { + addSignature(name, n.getNext(), t.getSourceName()); + } else { + getSignatureStore().removeSignature(name); + externMethodsWithoutSignatures.add(name); + } + + externMethods.add(name); + } break; + + case Token.OBJECTLIT: { + for (Node key = n.getFirstChild(); key != null; key = key.getNext()) { + Node value = key.getFirstChild(); + String name = key.getString(); + if (key.isStringKey() + && value.isFunction()) { + addSignature(name, value, t.getSourceName()); + } else { + getSignatureStore().removeSignature(name); + externMethodsWithoutSignatures.add(name); + } + externMethods.add(name); + } + } break; + } + } + } + + /** + * Gather signatures from the source to be compiled. + */ + private class GatherSignatures extends AbstractPostOrderCallback { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.GETPROP: + case Token.GETELEM: + Node dest = n.getFirstChild().getNext(); + + if (dest.isString()) { + if (dest.getString().equals("prototype")) { + processPrototypeParent(t, parent); + } else { + // Static methods of the form Foo.bar = function() {} or + // Static methods of the form Foo.bar = baz (where baz is a + // function name). Parse tree looks like: + // assign <- parent + // getprop <- n + // name Foo + // string bar + // function or name <- n.getNext() + if (parent.isAssign() && + parent.getFirstChild() == n) { + addPossibleSignature(dest.getString(), n.getNext(), t); + } + } + } + break; + + case Token.OBJECTLIT: + for (Node key = n.getFirstChild(); key != null; key = key.getNext()) { + switch(key.getType()) { + case Token.STRING_KEY: + addPossibleSignature(key.getString(), key.getFirstChild(), t); + break; + case Token.SETTER_DEF: + case Token.GETTER_DEF: + nonMethodProperties.add(key.getString()); + break; + default: + throw new IllegalStateException( + "unexpect OBJECTLIT key: " + key); + } + } + break; + } + } + + /** + * Processes the parent of a GETPROP prototype, which can either be + * another GETPROP (in the case of Foo.prototype.bar), or can be + * an assignment (in the case of Foo.prototype = ...). + */ + private void processPrototypeParent(NodeTraversal t, Node n) { + switch (n.getType()) { + // Foo.prototype.getBar = function() { ... } or + // Foo.prototype.getBar = getBaz (where getBaz is a function) + // parse tree looks like: + // assign <- parent + // getprop <- n + // getprop + // name Foo + // string prototype + // string getBar + // function or name <- assignee + case Token.GETPROP: + case Token.GETELEM: + Node dest = n.getFirstChild().getNext(); + Node parent = n.getParent().getParent(); + + if (dest.isString() && + parent.isAssign()) { + Node assignee = parent.getFirstChild().getNext(); + + addPossibleSignature(dest.getString(), assignee, t); + } + break; + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MinimizeExitPoints.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MinimizeExitPoints.java new file mode 100644 index 0000000..317947b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MinimizeExitPoints.java @@ -0,0 +1,323 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.AbstractCompiler; +import com.google.javascript.jscomp.CompilerPass; +import com.google.javascript.jscomp.NodeTraversal; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeUtil; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.TernaryValue; + +/** + * Transform the structure of the AST so that the number of explicit exits + * are minimized. + * + * @author johnlenz@google.com (John Lenz) + */ +class MinimizeExitPoints + extends AbstractPostOrderCallback + implements CompilerPass { + + AbstractCompiler compiler; + + MinimizeExitPoints(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.LABEL: + tryMinimizeExits( + n.getLastChild(), Token.BREAK, n.getFirstChild().getString()); + break; + + case Token.FOR: + case Token.WHILE: + tryMinimizeExits( + NodeUtil.getLoopCodeBlock(n), Token.CONTINUE, null); + break; + + case Token.DO: + tryMinimizeExits( + NodeUtil.getLoopCodeBlock(n), Token.CONTINUE, null); + + Node cond = NodeUtil.getConditionExpression(n); + if (NodeUtil.getImpureBooleanValue(cond) == TernaryValue.FALSE) { + // Normally, we wouldn't be able to optimize BREAKs inside a loop + // but as we know the condition will always false, we can treat them + // as we would a CONTINUE. + tryMinimizeExits( + n.getFirstChild(), Token.BREAK, null); + } + break; + + case Token.FUNCTION: + tryMinimizeExits( + n.getLastChild(), Token.RETURN, null); + break; + } + } + + /** + * Attempts to minimize the number of explicit exit points in a control + * structure to take advantage of the implied exit at the end of the + * structure. This is accomplished by removing redundant statements, and + * moving statements following a qualifying IF node into that node. + * For example: + * + * function () { + * if (x) return; + * else blah(); + * foo(); + * } + * + * becomes: + * + * function () { + * if (x) ; + * else { + * blah(); + * foo(); + * } + * + * @param n The execution node of a parent to inspect. + * @param exitType The type of exit to look for. + * @param labelName If parent is a label the name of the label to look for, + * null otherwise. + * @nullable labelName non-null only for breaks within labels. + */ + void tryMinimizeExits(Node n, int exitType, String labelName) { + + // Just an 'exit'. + if (matchingExitNode(n, exitType, labelName)) { + NodeUtil.removeChild(n.getParent(), n); + compiler.reportCodeChange(); + return; + } + + // Just an 'if'. + if (n.isIf()) { + Node ifBlock = n.getFirstChild().getNext(); + tryMinimizeExits(ifBlock, exitType, labelName); + Node elseBlock = ifBlock.getNext(); + if (elseBlock != null) { + tryMinimizeExits(elseBlock, exitType, labelName); + } + return; + } + + // Just a 'try/catch/finally'. + if (n.isTry()) { + Node tryBlock = n.getFirstChild(); + tryMinimizeExits(tryBlock, exitType, labelName); + Node allCatchNodes = NodeUtil.getCatchBlock(n); + if (NodeUtil.hasCatchHandler(allCatchNodes)) { + Preconditions.checkState(allCatchNodes.hasOneChild()); + Node catchNode = allCatchNodes.getFirstChild(); + Node catchCodeBlock = catchNode.getLastChild(); + tryMinimizeExits(catchCodeBlock, exitType, labelName); + } + if (NodeUtil.hasFinally(n)) { + Node finallyBlock = n.getLastChild(); + tryMinimizeExits(finallyBlock, exitType, labelName); + } + } + + // Just a 'label'. + if (n.isLabel()) { + Node labelBlock = n.getLastChild(); + tryMinimizeExits(labelBlock, exitType, labelName); + } + + // TODO(johnlenz): The last case of SWITCH statement? + + // The rest assumes a block with at least one child, bail on anything else. + if (!n.isBlock() || n.getLastChild() == null) { + return; + } + + // Multiple if-exits can be converted in a single pass. + // Convert "if (blah) break; if (blah2) break; other_stmt;" to + // become "if (blah); else { if (blah2); else { other_stmt; } }" + // which will get converted to "if (!blah && !blah2) { other_stmt; }". + for (Node c : n.children()) { + + // An 'if' block to process below. + if (c.isIf()) { + Node ifTree = c; + Node trueBlock, falseBlock; + + // First, the true condition block. + trueBlock = ifTree.getFirstChild().getNext(); + falseBlock = trueBlock.getNext(); + tryMinimizeIfBlockExits(trueBlock, falseBlock, + ifTree, exitType, labelName); + + // Now the else block. + // The if blocks may have changed, get them again. + trueBlock = ifTree.getFirstChild().getNext(); + falseBlock = trueBlock.getNext(); + if (falseBlock != null) { + tryMinimizeIfBlockExits(falseBlock, trueBlock, + ifTree, exitType, labelName); + } + } + + if (c == n.getLastChild()) { + break; + } + } + + // Now try to minimize the exits of the last child, if it is removed + // look at what has become the last child. + for (Node c = n.getLastChild(); c != null; c = n.getLastChild()) { + tryMinimizeExits(c, exitType, labelName); + // If the node is still the last child, we are done. + if (c == n.getLastChild()) { + break; + } + } + } + + /** + * Look for exits (returns, breaks, or continues, depending on the context) at + * the end of a block and removes them by moving the if node's siblings, + * if any, into the opposite condition block. + * + * @param srcBlock The block to inspect. + * @param destBlock The block to move sibling nodes into. + * @param ifNode The if node to work with. + * @param exitType The type of exit to look for. + * @param labelName The name associated with the exit, if any. + * @nullable labelName null for anything excepted for named-break associated + * with a label. + */ + private void tryMinimizeIfBlockExits(Node srcBlock, Node destBlock, + Node ifNode, int exitType, String labelName) { + Node exitNodeParent = null; + Node exitNode = null; + + // Pick an exit node candidate. + if (srcBlock.isBlock()) { + if (!srcBlock.hasChildren()) { + return; + } + exitNodeParent = srcBlock; + exitNode = exitNodeParent.getLastChild(); + } else { + // Just a single statement, if it isn't an exit bail. + exitNodeParent = ifNode; + exitNode = srcBlock; + } + + // Verify the candidate. + if (!matchingExitNode(exitNode, exitType, labelName)) { + return; + } + + // Take case of the if nodes siblings, if any. + if (ifNode.getNext() != null) { + // Move siblings of the if block into the opposite + // logic block of the exit. + Node newDestBlock = IR.block().srcref(ifNode); + if (destBlock == null) { + // Only possible if this is the false block. + ifNode.addChildToBack(newDestBlock); + } else if (destBlock.isEmpty()) { + // Use the new block. + ifNode.replaceChild(destBlock, newDestBlock); + } else if (destBlock.isBlock()) { + // Reuse the existing block. + newDestBlock = destBlock; + } else { + // Add the existing statement to the new block. + ifNode.replaceChild(destBlock, newDestBlock); + newDestBlock.addChildToBack(destBlock); + } + + // Move all the if node's following siblings. + moveAllFollowing(ifNode, ifNode.getParent(), newDestBlock); + } + + // Get rid of the "exit", replace with an empty item if needed. + NodeUtil.removeChild(exitNodeParent, exitNode); + + compiler.reportCodeChange(); + } + + /** + * Determines if n matches the type and name for the following types of + * "exits": + * - return without values + * - continues and breaks with or without names. + * @param n The node to inspect. + * @param type The Token type to look for. + * @param labelName The name that must be associated with the exit type. + * @nullable labelName non-null only for breaks associated with labels. + * @return Whether the node matches the specified block-exit type. + */ + static private boolean matchingExitNode(Node n, int type, String labelName) { + if (n.getType() == type) { + if (type == Token.RETURN) { + // only returns without expressions. + return !n.hasChildren(); + } else { + if (labelName == null) { + return !n.hasChildren(); + } else { + return n.hasChildren() + && labelName.equals(n.getFirstChild().getString()); + } + } + } + return false; + } + + /** + * Move all the child nodes following start in srcParent to the end of + * destParent's child list. + * @param start The start point in the srcParent child list. + * @param srcParent The parent node of start. + * @param destParent The destination node. + */ + static private void moveAllFollowing( + Node start, Node srcParent, Node destParent) { + for (Node n = start.getNext(); n != null; n = start.getNext()) { + boolean isFunctionDeclaration = + NodeUtil.isFunctionDeclaration(n); + + srcParent.removeChild(n); + + if (isFunctionDeclaration) { + destParent.addChildToFront(n); + } else { + destParent.addChildToBack(n); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MoveFunctionDeclarations.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MoveFunctionDeclarations.java new file mode 100644 index 0000000..b563c51 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MoveFunctionDeclarations.java @@ -0,0 +1,93 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.rhino.Node; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Moves top-level function declarations to the top. + * + * Enable this pass if a try catch block wraps the output after compilation, + * and the output runs on Firefox because function declarations are only + * defined when reached inside a try catch block on Firefox. + * + * On Firefox, this code works: + * + * var g = f; + * function f() {} + * + * but this code does not work: + * + * try { + * var g = f; + * function f() {} + * } catch(e) {} + * + */ +class MoveFunctionDeclarations implements Callback, CompilerPass { + private final AbstractCompiler compiler; + private final Map> functions; + + MoveFunctionDeclarations(AbstractCompiler compiler) { + this.compiler = compiler; + functions = Maps.newHashMap(); + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + for (Entry> entry : functions.entrySet()) { + JSModule module = entry.getKey(); + Node addingRoot = compiler.getNodeForCodeInsertion(module); + for (Node n : Lists.reverse(entry.getValue())) { + addingRoot.addChildToFront(n); + } + } + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + Node gramps = n.getAncestor(2); + return gramps == null || !gramps.isScript(); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (parent == null || !parent.isScript()) { + return; + } + + if (NodeUtil.isFunctionDeclaration(n)) { + parent.removeChild(n); + compiler.reportCodeChange(); + + JSModule module = t.getModule(); + List moduleFunctions = functions.get(module); + if (moduleFunctions == null) { + moduleFunctions = Lists.newArrayList(); + functions.put(module, moduleFunctions); + } + moduleFunctions.add(n); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MustBeReachingVariableDef.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MustBeReachingVariableDef.java new file mode 100644 index 0000000..e374ca0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/MustBeReachingVariableDef.java @@ -0,0 +1,441 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.ControlFlowGraph.AbstractCfgNodeTraversalCallback; +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.graph.GraphNode; +import com.google.javascript.jscomp.graph.LatticeElement; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.Nullable; + +/** + * Computes reaching definition for all use of each variables. + * + * A definition of {@code A} in {@code A = foo()} is a reaching definition of + * the use of {@code A} in {@code alert(A)} if all paths from entry node must + * reaches that definition and it is the last definition before the use. + * + */ +final class MustBeReachingVariableDef extends + DataFlowAnalysis { + + // The scope of the function that we are analyzing. + private final Scope jsScope; + private final AbstractCompiler compiler; + private final Set escaped; + + MustBeReachingVariableDef( + ControlFlowGraph cfg, Scope jsScope, AbstractCompiler compiler) { + super(cfg, new MustDefJoin()); + this.jsScope = jsScope; + this.compiler = compiler; + this.escaped = Sets.newHashSet(); + computeEscaped(jsScope, escaped, compiler); + } + + /** + * Abstraction of a local variable definition. It represents the node which + * a local variable is defined as well as a set of other local variables that + * this definition reads from. For example N: a = b + foo.bar(c). The + * definition node will be N, the depending set would be {b,c}. + */ + static class Definition { + final Node node; + final Set depends = Sets.newHashSet(); + private boolean unknownDependencies = false; + + Definition(Node node) { + this.node = node; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof Definition)) { + return false; + } + Definition otherDef = (Definition) other; + // If the var has the same definition node we can assume they have the + // same depends set. + return otherDef.node == node; + } + } + + /** + * Must reaching definition lattice representation. It captures a product + * lattice for each local (non-escaped) variable. The sub-lattice is + * a n + 2 element lattice with all the {@link Definition} in the program, + * TOP and BOTTOM. + * + *

      Since this is a Must-Define analysis, BOTTOM represents the case where + * there might be more than one reaching definition for the variable. + * + * + * (TOP) + * / | | \ + * N1 N2 N3 ....Nn + * \ | | / + * (BOTTOM) + * + */ + static final class MustDef implements LatticeElement { + + // TODO(user): Use bit vector instead of maps might get better + // performance. Change it after this is tested to be fully functional. + + // When a Var "A" = "TOP", "A" does not exist in reachingDef's keySet. + // When a Var "A" = Node N, "A" maps to that node. + // When a Var "A" = "BOTTOM", "A" maps to null. + final Map reachingDef; + + public MustDef() { + reachingDef = Maps.newHashMap(); + } + + public MustDef(Iterator vars) { + this(); + while(vars.hasNext()) { + Var var = vars.next(); + // Every variable in the scope is defined once in the beginning of the + // function: all the declared variables are undefined, all functions + // have been assigned and all arguments has its value from the caller. + reachingDef.put(var, new Definition(var.scope.getRootNode())); + } + } + + /** + * Copy constructor. + * + * @param other The constructed object is a replicated copy of this element. + */ + public MustDef(MustDef other) { + reachingDef = Maps.newHashMap(other.reachingDef); + } + + @Override + public boolean equals(Object other) { + return (other instanceof MustDef) && + ((MustDef) other).reachingDef.equals(this.reachingDef); + } + } + + private static class MustDefJoin extends JoinOp.BinaryJoinOp { + @Override + public MustDef apply(MustDef a, MustDef b) { + MustDef result = new MustDef(); + Map resultMap = result.reachingDef; + + // Take the join of all variables that are not TOP in this. + for (Map.Entry varEntry : a.reachingDef.entrySet()) { + Var var = varEntry.getKey(); + Definition aDef = varEntry.getValue(); + + if (aDef == null) { + // "a" is BOTTOM implies that the variable has more than one possible + // definition. We set the join of this to be BOTTOM regardless of what + // "b" might be. + resultMap.put(var, null); + continue; + } + + Node aNode = aDef.node; + + if (b.reachingDef.containsKey(var)) { + Definition bDef = b.reachingDef.get(var); + + if (aDef.equals(bDef)) { + resultMap.put(var, aDef); + } else { + resultMap.put(var, null); + } + } else { + resultMap.put(var, aDef); + } + } + + // Take the join of all variables that are not TOP in other but it is TOP + // in this. + for (Map.Entry entry : b.reachingDef.entrySet()) { + Var var = entry.getKey(); + if (!a.reachingDef.containsKey(var)) { + resultMap.put(var, entry.getValue()); + } + } + return result; + } + } + + @Override + boolean isForward() { + return true; + } + + @Override + MustDef createEntryLattice() { + return new MustDef(jsScope.getVars()); + } + + @Override + MustDef createInitialEstimateLattice() { + return new MustDef(); + } + + @Override + MustDef flowThrough(Node n, MustDef input) { + // TODO(user): We are doing a straight copy from input to output. There + // might be some opportunities to cut down overhead. + MustDef output = new MustDef(input); + // TODO(user): This must know about ON_EX edges but it should handle + // it better than what we did in liveness. Because we are in a forward mode, + // we can used the branched forward analysis. + computeMustDef(n, n, output, false); + return output; + } + + /** + * @param n The node in question. + * @param cfgNode The node to add + * @param conditional true if the definition is not always executed. + */ + private void computeMustDef( + Node n, Node cfgNode, MustDef output, boolean conditional) { + switch (n.getType()) { + + case Token.BLOCK: + case Token.FUNCTION: + return; + + case Token.WHILE: + case Token.DO: + case Token.IF: + computeMustDef( + NodeUtil.getConditionExpression(n), cfgNode, output, conditional); + return; + + case Token.FOR: + if (!NodeUtil.isForIn(n)) { + computeMustDef( + NodeUtil.getConditionExpression(n), cfgNode, output, conditional); + } else { + // for(x in y) {...} + Node lhs = n.getFirstChild(); + Node rhs = lhs.getNext(); + if (lhs.isVar()) { + lhs = lhs.getLastChild(); // for(var x in y) {...} + } + if (lhs.isName()) { + addToDefIfLocal(lhs.getString(), cfgNode, rhs, output); + } + } + return; + + case Token.AND: + case Token.OR: + computeMustDef(n.getFirstChild(), cfgNode, output, conditional); + computeMustDef(n.getLastChild(), cfgNode, output, true); + return; + + case Token.HOOK: + computeMustDef(n.getFirstChild(), cfgNode, output, conditional); + computeMustDef(n.getFirstChild().getNext(), cfgNode, output, true); + computeMustDef(n.getLastChild(), cfgNode, output, true); + return; + + case Token.VAR: + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + if (c.hasChildren()) { + computeMustDef(c.getFirstChild(), cfgNode, output, conditional); + addToDefIfLocal(c.getString(), conditional ? null : cfgNode, + c.getFirstChild(), output); + } + } + return; + + default: + if (NodeUtil.isAssignmentOp(n)) { + if (n.getFirstChild().isName()) { + Node name = n.getFirstChild(); + computeMustDef(name.getNext(), cfgNode, output, conditional); + addToDefIfLocal(name.getString(), conditional ? null : cfgNode, + n.getLastChild(), output); + return; + } else if (NodeUtil.isGet(n.getFirstChild())) { + // Treat all assignments to arguments as redefining the + // parameters itself. + Node obj = n.getFirstChild().getFirstChild(); + if (obj.isName() && "arguments".equals(obj.getString())) { + // TODO(user): More accuracy can be introduced + // i.e. We know exactly what arguments[x] is if x is a constant + // number. + escapeParameters(output); + } + } + } + + if (n.isName() && "arguments".equals(n.getString())) { + escapeParameters(output); + } + + // DEC and INC actually defines the variable. + if (n.isDec() || n.isInc()) { + Node target = n.getFirstChild(); + if (target.isName()) { + addToDefIfLocal(target.getString(), + conditional ? null : cfgNode, null, output); + return; + } + } + + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + computeMustDef(c, cfgNode, output, conditional); + } + } + } + + /** + * Set the variable lattice for the given name to the node value in the def + * lattice. Do nothing if the variable name is one of the escaped variable. + * + * @param node The CFG node where the definition should be record to. + * {@code null} if this is a conditional define. + */ + private void addToDefIfLocal( String name, @Nullable Node node, + @Nullable Node rValue, MustDef def) { + Var var = jsScope.getVar(name); + + // var might be null because the variable might be defined in the extern + // that we might not traverse. + if (var == null || var.scope != jsScope) { + return; + } + + for (Var other : def.reachingDef.keySet()) { + Definition otherDef = def.reachingDef.get(other); + if (otherDef == null) { + continue; + } + if (otherDef.depends.contains(var)) { + def.reachingDef.put(other, null); + } + } + + if (!escaped.contains(var)) { + if (node == null) { + def.reachingDef.put(var, null); + } else { + Definition definition = new Definition(node); + if (rValue != null) { + computeDependence(definition, rValue); + } + def.reachingDef.put(var, definition); + } + } + } + + private void escapeParameters(MustDef output) { + for (Iterator i = jsScope.getVars(); i.hasNext();) { + Var v = i.next(); + if (isParameter(v)) { + // Assume we no longer know where the parameter comes from + // anymore. + output.reachingDef.put(v, null); + } + } + + // Also, assume we no longer know anything that depends on a parameter. + for (Entry pair: output.reachingDef.entrySet()) { + Definition value = pair.getValue(); + if (value == null) { + continue; + } + for (Var dep : value.depends) { + if (isParameter(dep)) { + output.reachingDef.put(pair.getKey(), null); + } + } + } + } + + private boolean isParameter(Var v) { + return v.getParentNode().isParamList(); + } + + /** + * Computes all the local variables that rValue reads from and store that + * in the def's depends set. + */ + private void computeDependence(final Definition def, Node rValue) { + NodeTraversal.traverse(compiler, rValue, + new AbstractCfgNodeTraversalCallback() { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isName()) { + Var dep = jsScope.getVar(n.getString()); + if (dep == null) { + def.unknownDependencies = true; + } else { + def.depends.add(dep); + } + } + } + }); + } + + /** + * Gets the must reaching definition of a given node. + * + * @param name name of the variable. It can only be names of local variable + * that are not function parameters, escaped variables or variables + * declared in catch. + * @param useNode the location of the use where the definition reaches. + */ + Definition getDef(String name, Node useNode) { + Preconditions.checkArgument(getCfg().hasNode(useNode)); + GraphNode n = getCfg().getNode(useNode); + FlowState state = n.getAnnotation(); + return state.getIn().reachingDef.get(jsScope.getVar(name)); + } + + Node getDefNode(String name, Node useNode) { + Definition def = getDef(name, useNode); + return def == null ? null : def.node; + } + + boolean dependsOnOuterScopeVars(Definition def) { + if (def.unknownDependencies) { + return true; + } + + for (Var s : def.depends) { + if (s.scope != jsScope) { + return true; + } + } + return false; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameAnalyzer.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameAnalyzer.java new file mode 100644 index 0000000..781fc3c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameAnalyzer.java @@ -0,0 +1,1883 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.LinkedListMultimap; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.CodingConvention.SubclassRelationship; +import com.google.javascript.jscomp.GatherSideEffectSubexpressionsCallback.GetReplacementSideEffectSubexpressions; +import com.google.javascript.jscomp.GatherSideEffectSubexpressionsCallback.SideEffectAccumulator; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.graph.DiGraph; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; +import com.google.javascript.jscomp.graph.FixedPointGraphTraversal; +import com.google.javascript.jscomp.graph.FixedPointGraphTraversal.EdgeCallback; +import com.google.javascript.jscomp.graph.LinkedDirectedGraph; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * This pass identifies all global names, simple (e.g. a) or + * qualified (e.g. a.b.c), and the dependencies between them, then + * removes code associated with unreferenced names. It starts by assuming that + * only externally accessible names (e.g. window) are referenced, + * then iteratively marks additional names as referenced (e.g. Foo + * in window['foo'] = new Foo();). This makes it possible to + * eliminate code containing circular references. + * + *

      Qualified names can be defined using dotted or object literal syntax + * (a.b.c = x; or a.b = {c: x};, respectively). + * + *

      Removal of prototype classes is currently all or nothing. In other words, + * prototype properties and methods are never individually removed. + * + *

      Optionally generates pretty HTML output of data so that it is easy to + * analyze dependencies. + * + *

      Only operates on names defined in the global scope, but it would be easy + * to extend the pass to names defined in local scopes. + * + * TODO(nicksantos): In the initial implementation of this pass, it was + * important to understand namespaced names (e.g., that a.b is distinct from + * a.b.c). Now that this pass comes after CollapseProperties, this is no longer + * necessary. For now, I've changed so that {@code referenceParentNames} + * creates a two-way reference between a.b and a.b.c, so that they're + * effectively the same name. When someone has the time, we should completely + * rip out all the logic that understands namespaces. + * + */ +final class NameAnalyzer implements CompilerPass { + + /** Reference to the JS compiler */ + private final AbstractCompiler compiler; + + /** Map of all JS names found */ + private final Map allNames = Maps.newTreeMap(); + + /** Reference dependency graph */ + private DiGraph referenceGraph = + LinkedDirectedGraph.createWithoutAnnotations(); + + /** + * Map of name scopes - all children of the Node key have a dependency on the + * name value. + * + * If scopes.get(node).equals(name) && node2 is a child of node, then node2 + * will not get executed unless name is referenced via a get operation + */ + private final ListMultimap scopes = + LinkedListMultimap.create(); + + /** Used to parse prototype names */ + private static final String PROTOTYPE_SUBSTRING = ".prototype."; + + private static final int PROTOTYPE_SUBSTRING_LEN = + PROTOTYPE_SUBSTRING.length(); + + private static final int PROTOTYPE_SUFFIX_LEN = ".prototype".length(); + + /** Window root */ + private static final String WINDOW = "window"; + + /** Function class name */ + private static final String FUNCTION = "Function"; + + /** All of these refer to global scope. These can be moved to config */ + static final Set DEFAULT_GLOBAL_NAMES = ImmutableSet.of( + "window", "goog.global"); + + /** Whether to remove unreferenced variables in main pass */ + private final boolean removeUnreferenced; + + /** Names that refer to the global scope */ + private final Set globalNames; + + /** Ast change helper */ + private final AstChangeProxy changeProxy; + + /** Names that are externally defined */ + private final Set externalNames = Sets.newHashSet(); + + /** Name declarations or assignments, in post-order traversal order */ + private final List refNodes = Lists.newArrayList(); + + /** + * When multiple names in the global scope point to the same object, we + * call them aliases. Store a map from each alias name to the alias set. + */ + private final Map aliases = Maps.newHashMap(); + + /** + * All the aliases in a program form a graph, where each global name is + * a node in the graph, and two names are connected if one directly aliases + * the other. + * + * An {@code AliasSet} represents a connected component in that graph. We do + * not explicitly track the graph--we just track the connected components. + */ + private static class AliasSet { + Set names = Sets.newHashSet(); + + // Every alias set starts with exactly 2 names. + AliasSet(String name1, String name2) { + names.add(name1); + names.add(name2); + } + } + + /** + * Relationship between the two names. + * Currently only two different reference types exists: + * goog.inherits class relations and all other references. + */ + private static enum RefType { + REGULAR, + INHERITANCE, + } + + /** + * Callback that propagates reference information. + */ + private static class ReferencePropagationCallback + implements EdgeCallback { + @Override + public boolean traverseEdge(JsName from, + RefType callSite, + JsName to) { + if (from.referenced && !to.referenced) { + to.referenced = true; + return true; + } else { + return false; + } + } + } + + /** + * Class to hold information that can be determined from a node tree about a + * given name + */ + private static class NameInformation { + /** Fully qualified name */ + String name; + + /** Whether the name is guaranteed to be externally referenceable */ + boolean isExternallyReferenceable = false; + + /** Whether this name is a prototype function */ + boolean isPrototype = false; + + /** Name of the prototype class, i.e. "a" if name is "a.prototype.b" */ + String prototypeClass = null; + + /** Local name of prototype property i.e. "b" if name is "a.prototype.b" */ + String prototypeProperty = null; + + /** Name of the super class of name */ + String superclass = null; + + /** Whether this is a call that only affects the class definition */ + boolean onlyAffectsClassDef = false; + } + + /** + * Struct to hold information about a fully qualified JS name + */ + private static class JsName implements Comparable { + /** Fully qualified name */ + String name; + + /** Name of prototype functions attached to this name */ + List prototypeNames = Lists.newArrayList(); + + /** Whether this is an externally defined name */ + boolean externallyDefined = false; + + /** Whether this node is referenced */ + boolean referenced = false; + + /** Whether the name has descendants that are written to. */ + boolean hasWrittenDescendants = false; + + /** Whether the name is used in a instanceof check */ + boolean hasInstanceOfReference = false; + + /** + * Output the node as a string + * + * @return Node as a string + */ + @Override + public String toString() { + StringBuilder out = new StringBuilder(); + out.append(name); + + if (prototypeNames.size() > 0) { + out.append(" (CLASS)\n"); + out.append(" - FUNCTIONS: "); + Iterator pIter = prototypeNames.iterator(); + while (pIter.hasNext()) { + out.append(pIter.next()); + if (pIter.hasNext()) { + out.append(", "); + } + } + } + + return out.toString(); + } + + @Override + public int compareTo(JsName rhs) { + return this.name.compareTo(rhs.name); + } + } + + /** + * Interface to get information about and remove unreferenced names. + */ + interface RefNode { + JsName name(); + void remove(); + } + + /** + * Class for nodes that reference a fully-qualified JS name. Fully qualified + * names are of form A or A.B (A.B.C, etc.). References can get the value or + * set the value of the JS name. + */ + private class JsNameRefNode implements RefNode { + /** JsName node for this reference */ + JsName name; + + /** + * Top GETPROP or NAME or STRING [objlit key] node defining the name of + * this node + */ + @SuppressWarnings("unused") + Node node; + + /** + * Parent node of the name access + * (ASSIGN, VAR, FUNCTION, OBJECTLIT, or CALL) + */ + Node parent; + + + /** + * Create a node that refers to a name + * + * @param name The name + * @param node The top node representing the name (GETPROP, NAME, STRING) + */ + JsNameRefNode(JsName name, Node node) { + this.name = name; + this.node = node; + this.parent = node.getParent(); + } + + @Override + public JsName name() { + return name; + } + + @Override + public void remove() { + // Setters have VAR, FUNCTION, or ASSIGN parent nodes. CALL parent + // nodes are global refs, and are handled later in this function. + Node containingNode = parent.getParent(); + switch (parent.getType()) { + case Token.VAR: + Preconditions.checkState(parent.hasOneChild()); + replaceWithRhs(containingNode, parent); + break; + case Token.FUNCTION: + replaceWithRhs(containingNode, parent); + break; + case Token.ASSIGN: + if (containingNode.isExprResult()) { + replaceWithRhs(containingNode.getParent(), containingNode); + } else { + replaceWithRhs(containingNode, parent); + } + break; + case Token.OBJECTLIT: + // TODO(nicksantos): Come up with a way to remove this. + // If we remove object lit keys, then we will need to also + // create dependency scopes for them. + break; + } + } + } + + + /** + * Class for nodes that set prototype properties or methods. + */ + private class PrototypeSetNode extends JsNameRefNode { + /** + * Create a set node from the name & setter node + * + * @param name The name + * @param parent Parent node that assigns the expression (an ASSIGN) + */ + PrototypeSetNode(JsName name, Node parent) { + super(name, parent.getFirstChild()); + + Preconditions.checkState(parent.isAssign()); + } + + @Override public void remove() { + Node gramps = parent.getParent(); + if (gramps.isExprResult()) { + // name.prototype.foo = function() { ... }; + changeProxy.removeChild(gramps.getParent(), gramps); + } else { + // ... name.prototype.foo = function() { ... } ... + changeProxy.replaceWith(gramps, parent, + parent.getLastChild().detachFromParent()); + } + } + } + + /** + * Base class for special reference nodes. + */ + private abstract class SpecialReferenceNode implements RefNode { + /** JsName node for the function */ + JsName name; + + /** The CALL node */ + Node node; + + /** + * Create a special reference node. + * + * @param name The name + * @param node The CALL node + */ + SpecialReferenceNode(JsName name, Node node) { + this.name = name; + this.node = node; + } + + @Override + public JsName name() { + return name; + } + + Node getParent() { + return node.getParent(); + } + + Node getGramps() { + return node.getParent() == null ? null : node.getParent().getParent(); + } + } + + + + /** + * Class for nodes that are function calls that may change a function's + * prototype + */ + private class ClassDefiningFunctionNode extends SpecialReferenceNode { + /** + * Create a class defining function node from the name & setter node + * + * @param name The name + * @param node The CALL node + */ + ClassDefiningFunctionNode(JsName name, Node node) { + super(name, node); + Preconditions.checkState(node.isCall()); + } + + @Override + public void remove() { + Preconditions.checkState(node.isCall()); + Node parent = getParent(); + if (parent.isExprResult()) { + changeProxy.removeChild(getGramps(), parent); + } else { + changeProxy.replaceWith(parent, node, IR.voidNode(IR.number(0))); + } + } + } + + + + /** + * Class for nodes that check instanceof + */ + private class InstanceOfCheckNode extends SpecialReferenceNode { + /** + * Create an instanceof node from the name and parent node + * + * @param name The name + * @param node The qualified name node + */ + InstanceOfCheckNode(JsName name, Node node) { + super(name, node); + Preconditions.checkState(node.isQualifiedName()); + Preconditions.checkState(getParent().isInstanceOf()); + } + + @Override + public void remove() { + changeProxy.replaceWith(getGramps(), getParent(), IR.falseNode()); + } + } + + /** + * Walk through externs and mark nodes as externally declared if declared + */ + private class ProcessExternals extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + NameInformation ns = null; + if (NodeUtil.isVarDeclaration(n)) { + ns = createNameInformation(t, n); + } else if (NodeUtil.isFunctionDeclaration(n)) { + ns = createNameInformation(t, n.getFirstChild()); + } + if (ns != null) { + JsName jsName = getName(ns.name, true); + jsName.externallyDefined = true; + externalNames.add(ns.name); + } + } + } + + /** + *

      Identifies all dependency scopes. + * + *

      A dependency scope is a relationship between a node tree and a name that + * implies that the node tree will not execute (and thus can be eliminated) if + * the name is never referenced. + * + *

      The entire parse tree is ultimately in a dependency scope relationship + * with window (or an equivalent name for the global scope), but + * the goal here is to find finer-grained relationships. This callback creates + * dependency scopes for every assignment statement, variable declaration, and + * function call in the global scope. + * + *

      Note that dependency scope node trees aren't necessarily disjoint. + * In the following code snippet, for example, the function definition + * forms a dependency scope with the name f and the assignment + * inside the function forms a dependency scope with the name x. + *

      +   * var x; function f() { x = 1; }
      +   * 
      + */ + private class FindDependencyScopes extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!t.inGlobalScope()) { + return; + } + + if (n.isAssign()) { + recordAssignment(t, n, n); + if (!NodeUtil.isImmutableResult(n.getLastChild())) { + recordConsumers(t, n, n); + } + } else if (NodeUtil.isVarDeclaration(n)) { + NameInformation ns = createNameInformation(t, n); + recordDepScope(n, ns); + } else if (NodeUtil.isFunctionDeclaration(n)) { + NameInformation ns = createNameInformation(t, n.getFirstChild()); + recordDepScope(n, ns); + } else if (NodeUtil.isExprCall(n)) { + Node callNode = n.getFirstChild(); + Node nameNode = callNode.getFirstChild(); + NameInformation ns = createNameInformation(t, nameNode); + if (ns != null && ns.onlyAffectsClassDef) { + recordDepScope(n, ns); + } + } + } + + private void recordConsumers(NodeTraversal t, Node n, Node recordNode) { + Node parent = n.getParent(); + switch (parent.getType()) { + case Token.ASSIGN: + if (n == parent.getLastChild()) { + recordAssignment(t, parent, recordNode); + } + recordConsumers(t, parent, recordNode); + break; + case Token.NAME: + NameInformation ns = createNameInformation(t, parent); + recordDepScope(recordNode, ns); + break; + case Token.OR: + recordConsumers(t, parent, recordNode); + break; + case Token.AND: + // In "a && b" only "b" can be meaningfully aliased. + // "a" must be falsy, which it must be an immutable, non-Object + case Token.COMMA: + case Token.HOOK: + if (n != parent.getFirstChild()) { + recordConsumers(t, parent, recordNode); + } + break; + } + } + + private void recordAssignment(NodeTraversal t, Node n, Node recordNode) { + Node nameNode = n.getFirstChild(); + Node parent = n.getParent(); + NameInformation ns = createNameInformation(t, nameNode); + if (ns != null) { + if (parent.isFor() && !NodeUtil.isForIn(parent)) { + // Patch for assignments that appear in the init, + // condition or iteration part of a FOR loop. Without + // this change, all 3 of those parts try to claim the for + // loop as their dependency scope. The last assignment in + // those three fields wins, which can result in incorrect + // reference edges between referenced and assigned variables. + // + // TODO(user) revisit the dependency scope calculation + // logic. + if (parent.getFirstChild().getNext() != n) { + recordDepScope(recordNode, ns); + } else { + recordDepScope(nameNode, ns); + } + } else { + recordDepScope(recordNode, ns); + } + } + } + + /** + * Defines a dependency scope. + */ + private void recordDepScope(Node node, NameInformation name) { + Preconditions.checkNotNull(name); + scopes.put(node, name); + } + } + + /** + * Create JsName objects for variable and function declarations in + * the global scope before computing name references. In JavaScript + * it is legal to refer to variable and function names before the + * actual declaration. + */ + private class HoistVariableAndFunctionDeclarations + extends NodeTraversal.AbstractShallowCallback { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (NodeUtil.isVarDeclaration(n)) { + NameInformation ns = createNameInformation(t, n); + Preconditions.checkNotNull(ns, "NameInformation is null"); + createName(ns.name); + } else if (NodeUtil.isFunctionDeclaration(n)) { + Node nameNode = n.getFirstChild(); + NameInformation ns = createNameInformation(t, nameNode); + Preconditions.checkNotNull(ns, "NameInformation is null"); + createName(nameNode.getString()); + } + } + } + + /** + * Identifies all declarations of global names and setter statements + * affecting global symbols (assignments to global names). + * + * All declarations and setters must be gathered in a single + * traversal and stored in traversal order so "removeUnreferenced" + * can perform modifications in traversal order. + */ + private class FindDeclarationsAndSetters extends AbstractPostOrderCallback { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + + // Record global variable and function declarations + if (t.inGlobalScope()) { + if (NodeUtil.isVarDeclaration(n)) { + NameInformation ns = createNameInformation(t, n); + Preconditions.checkNotNull(ns); + recordSet(ns.name, n); + } else if (NodeUtil.isFunctionDeclaration(n)) { + Node nameNode = n.getFirstChild(); + NameInformation ns = createNameInformation(t, nameNode); + if (ns != null) { + JsName nameInfo = getName(nameNode.getString(), true); + recordSet(nameInfo.name, nameNode); + } + } else if (NodeUtil.isObjectLitKey(n, parent)) { + NameInformation ns = createNameInformation(t, n); + if (ns != null) { + recordSet(ns.name, n); + } + } + } + + // Record assignments and call sites + if (n.isAssign()) { + Node nameNode = n.getFirstChild(); + + NameInformation ns = createNameInformation(t, nameNode); + if (ns != null) { + if (ns.isPrototype) { + recordPrototypeSet(ns.prototypeClass, ns.prototypeProperty, n); + } else { + recordSet(ns.name, nameNode); + } + } + } else if (n.isCall()) { + Node nameNode = n.getFirstChild(); + NameInformation ns = createNameInformation(t, nameNode); + if (ns != null && ns.onlyAffectsClassDef) { + JsName name = getName(ns.name, true); + refNodes.add(new ClassDefiningFunctionNode(name, n)); + } + } + } + + /** + * Records the assignment of a value to a global name. + * + * @param name Fully qualified name + * @param node The top node representing the name (GETPROP, NAME, or STRING + * [objlit key]) + */ + private void recordSet(String name, Node node) { + JsName jsn = getName(name, true); + JsNameRefNode nameRefNode = new JsNameRefNode(jsn, node); + refNodes.add(nameRefNode); + + // Now, look at all parent names and record that their properties have + // been written to. + if (node.isGetElem()) { + recordWriteOnProperties(name); + } else if (name.indexOf('.') != -1) { + recordWriteOnProperties(name.substring(0, name.lastIndexOf('.'))); + } + } + + /** + * Records the assignment to a prototype property of a global name, + * if possible. + * + * @param className The name of the class. + * @param prototypeProperty The name of the prototype property. + * @param node The top node representing the name (GETPROP) + */ + private void recordPrototypeSet(String className, String prototypeProperty, + Node node) { + JsName name = getName(className, true); + name.prototypeNames.add(prototypeProperty); + refNodes.add(new PrototypeSetNode(name, node)); + recordWriteOnProperties(className); + } + + /** + * Record that the properties of this name have been written to. + */ + private void recordWriteOnProperties(String parentName) { + do { + JsName parent = getName(parentName, true); + if (parent.hasWrittenDescendants) { + // If we already recorded this name, then all its parents must + // also be recorded. short-circuit this loop. + return; + } else { + parent.hasWrittenDescendants = true; + } + + if (parentName.indexOf('.') == -1) { + return; + } + parentName = parentName.substring(0, parentName.lastIndexOf('.')); + } while(true); + } + } + + private static final Predicate NON_LOCAL_RESULT_PREDICATE = + new Predicate() { + @Override + public boolean apply(Node input) { + if (input.isCall()) { + return false; + } + // TODO(johnlenz): handle NEW calls that record their 'this' + // in global scope and effectively return an alias. + // Other non-local references are handled by this pass. + return true; + } + }; + + /** + *

      Identifies all references between global names. + * + *

      A reference from a name f to a name g means + * that if the name f must be defined, then the name + * g must also be defined. This would be the case if, for + * example, f were a function that called g. + */ + private class FindReferences implements Callback { + Set nodesToKeep; + FindReferences() { + nodesToKeep = Sets.newHashSet(); + } + + private void addAllChildren(Node n) { + nodesToKeep.add(n); + for (Node child = n.getFirstChild(); + child != null; + child = child.getNext()) { + addAllChildren(child); + } + } + + private void addSimplifiedChildren(Node n) { + NodeTraversal.traverse( + compiler, n, + new GatherSideEffectSubexpressionsCallback( + compiler, new NodeAccumulator())); + } + + private void addSimplifiedExpression(Node n, Node parent) { + if (parent.isVar()) { + Node value = n.getFirstChild(); + if (value != null) { + addSimplifiedChildren(value); + } + } else if (n.isAssign() && + (parent.isExprResult() || + parent.isFor() || + parent.isReturn())) { + for (Node child : n.children()) { + addSimplifiedChildren(child); + } + } else if (n.isCall() && + parent.isExprResult()) { + addSimplifiedChildren(n); + } else { + addAllChildren(n); + } + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + if (parent == null) { + return true; + } + + // Gather the list of nodes that either have side effects, are + // arguments to function calls with side effects or are used in + // control structure predicates. These names are always + // referenced when the enclosing function is called. + if (n.isFor()) { + if (!NodeUtil.isForIn(n)) { + Node decl = n.getFirstChild(); + Node pred = decl.getNext(); + Node step = pred.getNext(); + addSimplifiedExpression(decl, n); + addSimplifiedExpression(pred, n); + addSimplifiedExpression(step, n); + } else { // n.getChildCount() == 3 + Node decl = n.getFirstChild(); + Node iter = decl.getNext(); + addAllChildren(decl); + addAllChildren(iter); + } + } + + if (parent.isVar() || + parent.isExprResult() || + parent.isReturn() || + parent.isThrow()) { + addSimplifiedExpression(n, parent); + } + + if ((parent.isIf() || + parent.isWhile() || + parent.isWith() || + parent.isSwitch() || + parent.isCase()) && + parent.getFirstChild() == n) { + addAllChildren(n); + } + + if (parent.isDo() && parent.getLastChild() == n) { + addAllChildren(n); + } + + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!(n.isName() || + NodeUtil.isGet(n) && !parent.isGetProp())) { + // This is not a simple or qualified name. + return; + } + + NameInformation nameInfo = createNameInformation(t, n); + if (nameInfo == null) { + // The name is not a global name + return; + } + + if (nameInfo.onlyAffectsClassDef) { + if (nameInfo.superclass != null) { + recordReference( + nameInfo.name, nameInfo.superclass, RefType.INHERITANCE); + } + + // Make sure that we record a reference to the function that does + // the inheritance, so that the inherits() function itself does + // not get stripped. + String nodeName = n.getQualifiedName(); + if (nodeName != null) { + recordReference( + nameInfo.name, nodeName, RefType.REGULAR); + } + + return; + } + + if (parent.isInstanceOf() && + parent.getLastChild() == n && + // Don't cover GETELEMs with a global root node. + n.isQualifiedName()) { + JsName checkedClass = getName(nameInfo.name, true); + refNodes.add(new InstanceOfCheckNode(checkedClass, n)); + checkedClass.hasInstanceOfReference = true; + return; + } + + // Determine which name might be potentially referring to this one by + // looking up the nearest enclosing dependency scope. It's unnecessary to + // determine all enclosing dependency scopes because this callback should + // create a chain of references between them. + List referers = getDependencyScope(n); + if (referers.isEmpty()) { + maybeRecordReferenceOrAlias(t, n, parent, nameInfo, null); + } else { + for (NameInformation referring : referers) { + maybeRecordReferenceOrAlias(t, n, parent, nameInfo, referring); + } + recordAliases(referers); + } + } + + private void maybeRecordReferenceOrAlias( + NodeTraversal t, Node n, Node parent, + NameInformation nameInfo, NameInformation referring) { + String referringName = ""; + if (referring != null) { + referringName = referring.isPrototype + ? referring.prototypeClass + : referring.name; + } + + String name = nameInfo.name; + + // A value whose result is the return value of a function call + // can be an alias to global object. + // Here we add a alias to the general "global" object + // to act as a placeholder for the actual (unnamed) value. + if (maybeHiddenAlias(name, n)) { + recordAlias(name, WINDOW); + } + + // An externally referenceable name must always be defined, so we add a + // reference to it from the global scope (a.k.a. window). + if (nameInfo.isExternallyReferenceable) { + recordReference(WINDOW, name, RefType.REGULAR); + maybeRecordAlias(name, parent, referring, referringName); + return; + } + + // An assignment implies a reference from the enclosing dependency scope. + // For example, foo references bar in: function foo() {bar=5}. + if (NodeUtil.isVarOrSimpleAssignLhs(n, parent)) { + if (referring != null) { + recordReference(referringName, name, RefType.REGULAR); + } + return; + } + + if (nodesToKeep.contains(n)) { + List functionScopes = + getEnclosingFunctionDependencyScope(t); + if (!functionScopes.isEmpty()) { + for (NameInformation functionScope : functionScopes) { + recordReference(functionScope.name, name, RefType.REGULAR); + } + } else { + recordReference(WINDOW, name, RefType.REGULAR); + if (referring != null) { + maybeRecordAlias(name, parent, referring, referringName); + } + } + } else if (referring != null) { + if (!maybeRecordAlias(name, parent, referring, referringName)) { + RefType depType = referring.onlyAffectsClassDef ? + RefType.INHERITANCE : RefType.REGULAR; + recordReference(referringName, name, depType); + } + } else { + // No named dependency scope found. Unfortunately that might + // mean that the expression is a child of an function expression + // or assignment with a complex lhs. In those cases, + // protect this node by creating a reference to WINDOW. + for (Node ancestor : n.getAncestors()) { + if (NodeUtil.isAssignmentOp(ancestor) || + ancestor.isFunction()) { + recordReference(WINDOW, name, RefType.REGULAR); + break; + } + } + } + } + + private void recordAliases(List referers) { + int size = referers.size(); + for (int i = 0; i < size; i++) { + for (int j = i + 1; j < size; j++) { + recordAlias(referers.get(i).name, referers.get(j).name); + recordAlias(referers.get(j).name, referers.get(i).name); + } + } + } + + /** + * A value whose result is the return value of a function call + * can be an alias to global object. The dependency on the call target will + * prevent the removal of the function and its dependent values, but won't + * prevent the alias' removal. + */ + private boolean maybeHiddenAlias(String name, Node n) { + Node parent = n.getParent(); + if (NodeUtil.isVarOrSimpleAssignLhs(n, parent)) { + Node rhs = (parent.isVar()) + ? n.getFirstChild() : parent.getLastChild(); + return (rhs != null && !NodeUtil.evaluatesToLocalValue( + rhs, NON_LOCAL_RESULT_PREDICATE)); + } + return false; + } + + /** + * @return Whether the alias was recorded. + */ + private boolean maybeRecordAlias( + String name, Node parent, + NameInformation referring, String referringName) { + // A common type of reference is + // function F() {} + // F.prototype.bar = goog.nullFunction; + // + // In this specific case, we do not want a reference to goog.nullFunction + // to preserve F. + // + // In the general case, the user could do something like + // function F() {} + // F.prototype.bar = goog.nullFunction; + // F.prototype.bar.baz = 3; + // where it would not be safe to remove F. + // + // So we do not treat this alias as a backdoor for people to mutate the + // original object. We think that this heuristic will always be + // OK in real code. + boolean isPrototypePropAssignment = + parent.isAssign() + && NodeUtil.isPrototypeProperty(parent.getFirstChild()); + + if ((parent.isName() || + parent.isAssign()) && + !isPrototypePropAssignment && + referring != null && + scopes.get(parent).contains(referring)) { + recordAlias(referringName, name); + return true; + } + return false; + } + + /** + * Helper class that gathers the list of nodes that would be left + * behind after simplification. + */ + private class NodeAccumulator + implements SideEffectAccumulator { + + @Override + public boolean classDefiningCallsHaveSideEffects() { + return false; + } + + @Override + public void keepSubTree(Node original) { + addAllChildren(original); + } + + @Override + public void keepSimplifiedShortCircuitExpression(Node original) { + Node condition = original.getFirstChild(); + Node thenBranch = condition.getNext(); + addAllChildren(condition); + addSimplifiedChildren(thenBranch); + } + + @Override + public void keepSimplifiedHookExpression(Node hook, + boolean thenHasSideEffects, + boolean elseHasSideEffects) { + Node condition = hook.getFirstChild(); + Node thenBranch = condition.getNext(); + Node elseBranch = thenBranch.getNext(); + addAllChildren(condition); + if (thenHasSideEffects) { + addSimplifiedChildren(thenBranch); + } + if (elseHasSideEffects) { + addSimplifiedChildren(elseBranch); + } + } + } + } + + private class RemoveListener implements AstChangeProxy.ChangeListener { + @Override + public void nodeRemoved(Node n) { + compiler.reportCodeChange(); + } + } + + /** + * Creates a name analyzer, with option to remove unreferenced variables when + * calling process(). + * + * The analyzer make a best guess at whether functions affect global scope + * based on usage (no assignment of return value means that a function has + * side effects). + * + * @param compiler The AbstractCompiler + * @param removeUnreferenced If true, remove unreferenced variables during + * process() + */ + NameAnalyzer(AbstractCompiler compiler, boolean removeUnreferenced) { + this.compiler = compiler; + this.removeUnreferenced = removeUnreferenced; + this.globalNames = DEFAULT_GLOBAL_NAMES; + this.changeProxy = new AstChangeProxy(); + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, externs, new ProcessExternals()); + NodeTraversal.traverse(compiler, root, new FindDependencyScopes()); + NodeTraversal.traverse( + compiler, root, new HoistVariableAndFunctionDeclarations()); + NodeTraversal.traverse(compiler, root, new FindDeclarationsAndSetters()); + NodeTraversal.traverse(compiler, root, new FindReferences()); + + // Create bi-directional references between parent names and their + // descendants. This may create new names. + referenceParentNames(); + + // If we modify the property of an alias, make sure that modification + // gets reflected in the original object. + referenceAliases(); + + calculateReferences(); + + if (removeUnreferenced) { + removeUnreferenced(); + } + } + + /** + * Records an alias of one name to another name. + */ + private void recordAlias(String fromName, String toName) { + recordReference(fromName, toName, RefType.REGULAR); + + // We need to add an edge to the alias graph. The alias graph is expressed + // implicitly as a set of connected components, called AliasSets. + // + // There are three possibilities: + // 1) Neither name is part of a connected component. Create a new one. + // 2) Exactly one name is part of a connected component. Merge the new + // name into the component. + // 3) The two names are already part of connected components. Merge + // those components together. + AliasSet toNameAliasSet = aliases.get(toName); + AliasSet fromNameAliasSet = aliases.get(fromName); + AliasSet resultSet = null; + if (toNameAliasSet == null && fromNameAliasSet == null) { + resultSet = new AliasSet(toName, fromName); + } else if (toNameAliasSet != null && fromNameAliasSet != null) { + resultSet = toNameAliasSet; + resultSet.names.addAll(fromNameAliasSet.names); + for (String name : fromNameAliasSet.names) { + aliases.put(name, resultSet); + } + } else if (toNameAliasSet != null) { + resultSet = toNameAliasSet; + resultSet.names.add(fromName); + } else { + resultSet = fromNameAliasSet; + resultSet.names.add(toName); + } + aliases.put(fromName, resultSet); + aliases.put(toName, resultSet); + } + + /** + * Records a reference from one name to another name. + */ + private void recordReference(String fromName, String toName, + RefType depType) { + if (fromName.equals(toName)) { + // Don't bother recording self-references. + return; + } + + JsName from = getName(fromName, true); + JsName to = getName(toName, true); + referenceGraph.createNode(from); + referenceGraph.createNode(to); + if (!referenceGraph.isConnectedInDirection(from, depType, to)) { + referenceGraph.connect(from, depType, to); + } + } + + /** + * Removes all unreferenced variables. + */ + void removeUnreferenced() { + RemoveListener listener = new RemoveListener(); + changeProxy.registerListener(listener); + + for (RefNode refNode : refNodes) { + JsName name = refNode.name(); + if (!name.referenced && !name.externallyDefined) { + refNode.remove(); + } + } + + changeProxy.unregisterListener(listener); + } + + /** + * Generates an HTML report + * + * @return The report + */ + String getHtmlReport() { + StringBuilder sb = new StringBuilder(); + sb.append(""); + sb.append("OVERALL STATS

        "); + appendListItem(sb, "Total Names: " + countOf(TriState.BOTH, TriState.BOTH)); + appendListItem(sb, "Total Classes: " + + countOf(TriState.TRUE, TriState.BOTH)); + appendListItem(sb, "Total Static Functions: " + + countOf(TriState.FALSE, TriState.BOTH)); + appendListItem(sb, "Referenced Names: " + + countOf(TriState.BOTH, TriState.TRUE)); + appendListItem(sb, "Referenced Classes: " + + countOf(TriState.TRUE, TriState.TRUE)); + appendListItem(sb, "Referenced Functions: " + + countOf(TriState.FALSE, TriState.TRUE)); + sb.append("
      "); + + sb.append("ALL NAMES
        \n"); + for (JsName node : allNames.values()) { + sb.append("
      • " + nameAnchor(node.name) + "
          "); + if (node.prototypeNames.size() > 0) { + sb.append("
        • PROTOTYPES: "); + Iterator protoIter = node.prototypeNames.iterator(); + while (protoIter.hasNext()) { + sb.append(protoIter.next()); + if (protoIter.hasNext()) { + sb.append(", "); + } + } + } + + if (referenceGraph.hasNode(node)) { + List> refersTo = + referenceGraph.getOutEdges(node); + if (refersTo.size() > 0) { + sb.append("
        • REFERS TO: "); + Iterator> toIter = refersTo.iterator(); + while (toIter.hasNext()) { + sb.append(nameLink(toIter.next().getDestination().getValue().name)); + if (toIter.hasNext()) { + sb.append(", "); + } + } + } + + List> referencedBy = + referenceGraph.getInEdges(node); + if (referencedBy.size() > 0) { + sb.append("
        • REFERENCED BY: "); + Iterator> fromIter = refersTo.iterator(); + while (fromIter.hasNext()) { + sb.append( + nameLink(fromIter.next().getDestination().getValue().name)); + if (fromIter.hasNext()) { + sb.append(", "); + } + } + } + } + sb.append("
        • "); + sb.append("
      • "); + } + sb.append("
      "); + sb.append(""); + + return sb.toString(); + } + + private void appendListItem(StringBuilder sb, String text) { + sb.append("
    7. " + text + "
    8. \n"); + } + + private String nameLink(String name) { + return "" + name + ""; + } + + private String nameAnchor(String name) { + return "" + name + ""; + } + + /** + * Looks up a {@link JsName} by name, optionally creating one if it doesn't + * already exist. + * + * @param name A fully qualified name + * @param canCreate Whether to create the object if necessary + * @return The {@code JsName} object, or null if one can't be found and + * can't be created. + */ + private JsName getName(String name, boolean canCreate) { + if (canCreate) { + createName(name); + } + return allNames.get(name); + } + + /** + * Creates a {@link JsName} for the given name if it doesn't already + * exist. + * + * @param name A fully qualified name + */ + private void createName(String name) { + JsName jsn = allNames.get(name); + if (jsn == null) { + jsn = new JsName(); + jsn.name = name; + allNames.put(name, jsn); + } + } + + /** + * The NameAnalyzer algorithm works best when all objects have a canonical + * name in the global scope. When multiple names in the global scope + * point to the same object, things start to break down. + * + * For example, if we have + * + * var a = {}; + * var b = a; + * a.foo = 3; + * alert(b.foo); + * + * then a.foo and b.foo are the same name, even though NameAnalyzer doesn't + * represent them as such. + * + * To handle this case, we look at all the aliases in the program. + * If descendant properties of that alias are assigned, then we create a + * directional reference from the original name to the alias. For example, + * in this case, the assign to {@code a.foo} triggers a reference from + * {@code b} to {@code a}, but NOT from a to b. + * + * Similarly, "instanceof" checks do not prevent the removal + * of a unaliased name but an instanceof check on an alias can only be removed + * if the other aliases are also removed, so we add a connection here. + */ + private void referenceAliases() { + for (Map.Entry entry : aliases.entrySet()) { + JsName name = getName(entry.getKey(), false); + if (name.hasWrittenDescendants || name.hasInstanceOfReference) { + for (String alias : entry.getValue().names) { + recordReference(alias, entry.getKey(), RefType.REGULAR); + } + } + } + } + + /** + * Adds mutual references between all known global names and their parent + * names. (e.g. between a.b.c and a.b). + */ + private void referenceParentNames() { + // Duplicate set of nodes to process so we don't modify set we are + // currently iterating over + Set allNamesCopy = Sets.newHashSet(allNames.values()); + + for (JsName name : allNamesCopy) { + String curName = name.name; + JsName curJsName = name; + while (curName.indexOf('.') != -1) { + String parentName = curName.substring(0, curName.lastIndexOf('.')); + if (!globalNames.contains(parentName)) { + + JsName parentJsName = getName(parentName, true); + + recordReference(curJsName.name, parentJsName.name, RefType.REGULAR); + recordReference(parentJsName.name, curJsName.name, RefType.REGULAR); + + curJsName = parentJsName; + } + curName = parentName; + } + } + } + + /** + * Creates name information for the current node during a traversal. + * + * @param t The node traversal + * @param n The current node + * @return The name information, or null if the name is irrelevant to this + * pass + */ + private NameInformation createNameInformation(NodeTraversal t, Node n) { + Node parent = n.getParent(); + // Build the full name and find its root node by iterating down through all + // GETPROP/GETELEM nodes. + String name = ""; + Node rootNameNode = n; + boolean bNameWasShortened = false; + while (true) { + if (NodeUtil.isGet(rootNameNode)) { + Node prop = rootNameNode.getLastChild(); + if (rootNameNode.isGetProp()) { + name = "." + prop.getString() + name; + } else { + // We consider the name to be "a.b" in a.b['c'] or a.b[x].d. + bNameWasShortened = true; + name = ""; + } + rootNameNode = rootNameNode.getFirstChild(); + } else if (NodeUtil.isObjectLitKey( + rootNameNode, rootNameNode.getParent())) { + name = "." + rootNameNode.getString() + name; + + // Check if this is an object literal assigned to something. + Node objLit = rootNameNode.getParent(); + Node objLitParent = objLit.getParent(); + if (objLitParent.isAssign()) { + // This must be the right side of the assign. + rootNameNode = objLitParent.getFirstChild(); + } else if (objLitParent.isName()) { + // This must be a VAR initialization. + rootNameNode = objLitParent; + } else if (objLitParent.isStringKey()) { + // This must be a object literal key initialization. + rootNameNode = objLitParent; + } else { + return null; + } + } else { + break; + } + } + + // Check whether this is a class-defining call. Classes may only be defined + // in the global scope. + if (parent.isCall() && t.inGlobalScope()) { + CodingConvention convention = compiler.getCodingConvention(); + SubclassRelationship classes = convention.getClassesDefinedByCall(parent); + if (classes != null) { + NameInformation nameInfo = new NameInformation(); + nameInfo.name = classes.subclassName; + nameInfo.onlyAffectsClassDef = true; + nameInfo.superclass = classes.superclassName; + return nameInfo; + } + + String singletonGetterClass = + convention.getSingletonGetterClassName(parent); + if (singletonGetterClass != null) { + NameInformation nameInfo = new NameInformation(); + nameInfo.name = singletonGetterClass; + nameInfo.onlyAffectsClassDef = true; + return nameInfo; + } + } + + switch (rootNameNode.getType()) { + case Token.NAME: + // Check whether this is an assignment to a prototype property + // of an object defined in the global scope. + if (!bNameWasShortened && + n.isGetProp() && + parent.isAssign() && + "prototype".equals(n.getLastChild().getString())) { + if (createNameInformation(t, n.getFirstChild()) != null) { + name = rootNameNode.getString() + name; + name = name.substring(0, name.length() - PROTOTYPE_SUFFIX_LEN); + NameInformation nameInfo = new NameInformation(); + nameInfo.name = name; + return nameInfo; + } else { + return null; + } + } + return createNameInformation( + rootNameNode.getString() + name, t.getScope(), rootNameNode); + case Token.THIS: + if (t.inGlobalScope()) { + NameInformation nameInfo = new NameInformation(); + if (name.indexOf('.') == 0) { + nameInfo.name = name.substring(1); // strip leading "." + } else { + nameInfo.name = name; + } + nameInfo.isExternallyReferenceable = true; + return nameInfo; + } + return null; + default: + return null; + } + } + + /** + * Creates name information for a particular qualified name that occurs in a + * particular scope. + * + * @param name A qualified name (e.g. "x" or "a.b.c") + * @param scope The scope in which {@code name} occurs + * @param rootNameNode The NAME node for the first token of {@code name} + * @return The name information, or null if the name is irrelevant to this + * pass + */ + private NameInformation createNameInformation( + String name, Scope scope, Node rootNameNode) { + // Check the scope. Currently we're only looking at globally scoped vars. + String rootName = rootNameNode.getString(); + Var v = scope.getVar(rootName); + boolean isExtern = (v == null && externalNames.contains(rootName)); + boolean isGlobalRef = (v != null && v.isGlobal()) || isExtern || + rootName.equals(WINDOW); + if (!isGlobalRef) { + return null; + } + + NameInformation nameInfo = new NameInformation(); + + // If a prototype property or method, fill in prototype information. + int idx = name.indexOf(PROTOTYPE_SUBSTRING); + if (idx != -1) { + nameInfo.isPrototype = true; + nameInfo.prototypeClass = name.substring(0, idx); + nameInfo.prototypeProperty = name.substring( + idx + PROTOTYPE_SUBSTRING_LEN); + } + + nameInfo.name = name; + nameInfo.isExternallyReferenceable = + isExtern || isExternallyReferenceable(scope, name); + return nameInfo; + } + + /** + * Checks whether a name can be referenced outside of the compiled code. + * These names will be the root of dependency trees. + * + * @param scope The current variable scope + * @param name The name + * @return True if can be referenced outside + */ + private boolean isExternallyReferenceable(Scope scope, String name) { + if (compiler.getCodingConvention().isExported(name)) { + return true; + } + if (scope.isLocal()) { + return false; + } + for (String s : globalNames) { + if (name.startsWith(s)) { + return true; + } + } + return false; + } + + /** + * Gets the nearest enclosing dependency scope, or null if there isn't one. + */ + private List getDependencyScope(Node n) { + for (Node node : n.getAncestors()) { + List refs = scopes.get(node); + if (!refs.isEmpty()) { + return refs; + } + } + + return Collections.emptyList(); + } + + /** + * Get dependency scope defined by the enclosing function, or null. + * If enclosing function is a function expression, determine scope based on + * its parent if the parent node is a variable declaration or + * assignment. + */ + private List getEnclosingFunctionDependencyScope( + NodeTraversal t) { + Node function = t.getEnclosingFunction(); + if (function == null) { + return Collections.emptyList(); + } + + List refs = scopes.get(function); + if (!refs.isEmpty()) { + return refs; + } + + // Function expression. try to get a name from the parent var + // declaration or assignment. + Node parent = function.getParent(); + if (parent != null) { + // Account for functions defined in the form: + // var a = cond ? function a() {} : function b() {}; + while (parent.isHook()) { + parent = parent.getParent(); + } + + if (parent.isName()) { + return scopes.get(parent); + } + + if (parent.isAssign()) { + return scopes.get(parent); + } + } + + return Collections.emptyList(); + } + + /** + * Propagate "referenced" property down the graph. + */ + private void calculateReferences() { + JsName window = getName(WINDOW, true); + window.referenced = true; + JsName function = getName(FUNCTION, true); + function.referenced = true; + + // Propagate "referenced" property to a fixed point. + FixedPointGraphTraversal.newTraversal(new ReferencePropagationCallback()) + .computeFixedPoint(referenceGraph); + } + + + /** + * Enum for saying a value can be true, false, or either (cleaner than using a + * Boolean with null) + */ + private enum TriState { + /** If value is true */ + TRUE, + /** If value is false */ + FALSE, + /** If value can be true or false */ + BOTH + } + + /** + * Gets the count of nodes matching the criteria + * + * @param isClass Whether the node is a class + * @param referenced Whether the node is referenced + * @return Number of matches + */ + private int countOf(TriState isClass, TriState referenced) { + int count = 0; + for (JsName name : allNames.values()) { + + boolean nodeIsClass = name.prototypeNames.size() > 0; + + boolean classMatch = isClass == TriState.BOTH + || (nodeIsClass && isClass == TriState.TRUE) + || (!nodeIsClass && isClass == TriState.FALSE); + + boolean referenceMatch = referenced == TriState.BOTH + || (name.referenced && referenced == TriState.TRUE) + || (!name.referenced && referenced == TriState.FALSE); + + if (classMatch && referenceMatch && !name.externallyDefined) { + count++; + } + } + return count; + } + + + /** + * Extract a list of replacement nodes to use. + */ + private List getSideEffectNodes(Node n) { + List subexpressions = Lists.newArrayList(); + NodeTraversal.traverse( + compiler, n, + new GatherSideEffectSubexpressionsCallback( + compiler, + new GetReplacementSideEffectSubexpressions( + compiler, subexpressions))); + + List replacements = + Lists.newArrayListWithExpectedSize(subexpressions.size()); + for (Node subexpression : subexpressions) { + replacements.add(NodeUtil.newExpr(subexpression)); + } + return replacements; + } + + /** + * Replace n with a simpler expression, while preserving program + * behavior. + * + * If the n's value is used, replace it with its RHS; otherwise + * replace it with the subexpressions that have side effects. + */ + private void replaceWithRhs(Node parent, Node n) { + if (valueConsumedByParent(n, parent)) { + // parent reads from n directly; replace it with n's rhs + lhs + // subexpressions with side effects. + List replacements = getRhsSubexpressions(n); + List newReplacements = Lists.newArrayList(); + for (int i = 0; i < replacements.size() - 1; i++) { + newReplacements.addAll(getSideEffectNodes(replacements.get(i))); + } + Node valueExpr = replacements.get(replacements.size() - 1); + valueExpr.detachFromParent(); + newReplacements.add(valueExpr); + changeProxy.replaceWith( + parent, n, collapseReplacements(newReplacements)); + } else if (n.isAssign() && !parent.isFor()) { + // assignment appears in a RHS expression. we have already + // considered names in the assignment's RHS as being referenced; + // replace the assignment with its RHS. + // TODO(user) make the pass smarter about these cases and/or run + // this pass and RemoveConstantExpressions together in a loop. + Node replacement = n.getLastChild(); + replacement.detachFromParent(); + changeProxy.replaceWith(parent, n, replacement); + } else { + replaceTopLevelExpressionWithRhs(parent, n); + } + } + + /** + * Simplify a toplevel expression, while preserving program + * behavior. + */ + private void replaceTopLevelExpressionWithRhs(Node parent, Node n) { + // validate inputs + switch (parent.getType()) { + case Token.BLOCK: + case Token.SCRIPT: + case Token.FOR: + case Token.LABEL: + break; + default: + throw new IllegalArgumentException( + "Unsupported parent node type in replaceWithRhs " + + Token.name(parent.getType())); + } + + switch (n.getType()) { + case Token.EXPR_RESULT: + case Token.FUNCTION: + case Token.VAR: + break; + case Token.ASSIGN: + Preconditions.checkArgument(parent.isFor(), + "Unsupported assignment in replaceWithRhs. parent: %s", + Token.name(parent.getType())); + break; + default: + throw new IllegalArgumentException( + "Unsupported node type in replaceWithRhs " + + Token.name(n.getType())); + } + + // gather replacements + List replacements = Lists.newArrayList(); + for (Node rhs : getRhsSubexpressions(n)) { + replacements.addAll(getSideEffectNodes(rhs)); + } + + if (parent.isFor()) { + // tweak replacements array s.t. it is a single expression node. + if (replacements.isEmpty()) { + replacements.add(IR.empty()); + } else { + Node expr = collapseReplacements(replacements); + replacements.clear(); + replacements.add(expr); + } + } + + changeProxy.replaceWith(parent, n, replacements); + } + + /** + * Determine if the parent reads the value of a child expression + * directly. This is true children used in predicates, RETURN + * statements and, RHS of variable declarations and assignments. + * + * In the case of: + * if (a) b else c + * + * This method returns true for "a", and false for "b" and "c": the + * IF expression does something special based on "a"'s value. "b" + * and "c" are effectively outputs. Same logic applies to FOR, + * WHILE and DO loop predicates. AND/OR/HOOK expressions are + * syntactic sugar for IF statements; therefore this method returns + * true for the predicate and false otherwise. + */ + private boolean valueConsumedByParent(Node n, Node parent) { + if (NodeUtil.isAssignmentOp(parent)) { + return parent.getLastChild() == n; + } + + switch (parent.getType()) { + case Token.NAME: + case Token.RETURN: + return true; + case Token.AND: + case Token.OR: + case Token.HOOK: + return parent.getFirstChild() == n; + case Token.FOR: + return parent.getFirstChild().getNext() == n; + case Token.IF: + case Token.WHILE: + return parent.getFirstChild() == n; + case Token.DO: + return parent.getLastChild() == n; + default: + return false; + } + } + + /** + * Merge a list of nodes into a single expression. The value of the + * new expression is determined by the last expression in the list. + */ + private Node collapseReplacements(List replacements) { + Node expr = null; + for (Node rep : replacements) { + if (rep.isExprResult()) { + rep = rep.getFirstChild(); + rep.detachFromParent(); + } + + if (expr == null) { + expr = rep; + } else { + expr = IR.comma(expr, rep); + } + } + + return expr; + } + + /** + * Extract a list of subexpressions that act as right hand sides. + */ + private List getRhsSubexpressions(Node n) { + switch (n.getType()) { + case Token.EXPR_RESULT: + // process body + return getRhsSubexpressions(n.getFirstChild()); + case Token.FUNCTION: + // function nodes have no RHS + return Collections.emptyList(); + case Token.NAME: + { + // parent is a var node. RHS is the first child + Node rhs = n.getFirstChild(); + if (rhs != null) { + return Lists.newArrayList(rhs); + } else { + return Collections.emptyList(); + } + } + case Token.ASSIGN: + { + // add LHS and RHS expressions - LHS may be a complex expression + Node lhs = n.getFirstChild(); + Node rhs = lhs.getNext(); + return Lists.newArrayList(lhs, rhs); + } + case Token.VAR: + { + // recurse on all children + List nodes = Lists.newArrayList(); + for (Node child : n.children()) { + nodes.addAll(getRhsSubexpressions(child)); + } + return nodes; + } + default: + throw new IllegalArgumentException("AstChangeProxy::getRhs " + n); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameAnonymousFunctions.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameAnonymousFunctions.java new file mode 100644 index 0000000..9f85ad0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameAnonymousFunctions.java @@ -0,0 +1,102 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +import java.util.logging.*; + +/** + * Gives anonymous function names. This makes it way easier to debug because + * debuggers and stack traces use the function names. So if you have + * + * goog.string.htmlEscape = function(str) { + * } + * + * It will become + * + * goog.string.htmlEscape = function $goog$string$htmlEscape$(str) { + * } + * + */ +class NameAnonymousFunctions implements CompilerPass { + private static final Logger logger = Logger.getLogger( + NameAnonymousFunctions.class.getName()); + + static final char DELIMITER = '$'; + + private final AbstractCompiler compiler; + + private int namedCount = 0; + private int bytesUsed = 0; + + NameAnonymousFunctions(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + AnonymousFunctionNamingCallback namingCallback = + new AnonymousFunctionNamingCallback(new AnonymousFunctionNamer()); + NodeTraversal.traverse(compiler, root, namingCallback); + logger.fine("Named " + namedCount + " anon functions using " + + bytesUsed + " bytes"); + } + + /** + * Names anonymous functions. The function names don't have to be globally + * unique or even locally unique. We make them somewhat unique because of a + * bug in IE (and there may be other bugs we haven't found). See unit test for + * more info. + */ + private class AnonymousFunctionNamer + implements AnonymousFunctionNamingCallback.FunctionNamer { + private NodeNameExtractor nameExtractor; + + AnonymousFunctionNamer() { + this.nameExtractor = new NodeNameExtractor(DELIMITER); + } + + /** + * Returns a likely not conflicting name to make IE happy. See unit test + * for more info. + */ + private String getLikelyNonConflictingName(String name) { + return DELIMITER + name + DELIMITER; + } + + @Override + public final String getName(Node node) { + return nameExtractor.getName(node); + } + + @Override + public final void setFunctionName(String name, Node fnNode) { + Node fnNameNode = fnNode.getFirstChild(); + String uniqueName = getLikelyNonConflictingName(name); + fnNameNode.setString(uniqueName); + compiler.reportCodeChange(); + namedCount++; + bytesUsed += uniqueName.length(); + } + + @Override + public final String getCombinedName(String lhs, String rhs) { + return lhs + DELIMITER + rhs; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameAnonymousFunctionsMapped.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameAnonymousFunctionsMapped.java new file mode 100644 index 0000000..d49e353 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameAnonymousFunctionsMapped.java @@ -0,0 +1,144 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.*; +import java.util.logging.*; + +/** + * Gives anonymous function names that are optimized to be small and provides a + * mapping back to the original names. This makes it way easier to debug because + * debuggers and stack traces use the function names. So if you have + * + * goog.string.htmlEscape = function(str) { + * } + * + * It will become + * + * goog.string.htmlEscape = function $qv(str) { + * } + * + * And there will be mapping from $qv to goog.string.htmlEscape + * + */ +class NameAnonymousFunctionsMapped implements CompilerPass { + + private static Logger logger = Logger.getLogger( + NameAnonymousFunctionsMapped.class.getName()); + + static final char PREFIX = '$'; + static final String PREFIX_STRING = "$"; + + private final AbstractCompiler compiler; + private final NameGenerator nameGenerator; + private final VariableMap previousMap; + private final Map renameMap; + + private int namedCount = 0; + private int bytesUsed = 0; + + NameAnonymousFunctionsMapped( + AbstractCompiler compiler, VariableMap previousMap) { + this.compiler = compiler; + Set reserved = + previousMap != null ? + previousMap.getNewNameToOriginalNameMap().keySet() : + Collections.emptySet(); + this.nameGenerator = new NameGenerator(reserved, PREFIX_STRING, null); + this.previousMap = previousMap; + this.renameMap = Maps.newHashMap(); + } + + @Override + public void process(Node externs, Node root) { + AnonymousFunctionNamingCallback namingCallback = + new AnonymousFunctionNamingCallback(new MappedFunctionNamer()); + NodeTraversal.traverse(compiler, root, namingCallback); + logger.fine("Named " + namedCount + " anon functions using " + + bytesUsed + " bytes"); + if (namedCount > 0) { + compiler.reportCodeChange(); + } + } + + /** + * Names anonymous functions. The function names don't have to be globally + * unique or even locally unique. We make them somewhat unique because of a + * bug in IE (and there may be other bugs we haven't found). See unit test for + * more info. + */ + private class MappedFunctionNamer + implements AnonymousFunctionNamingCallback.FunctionNamer { + static final char DELIMITER = '.'; + + @Override + public final String getName(Node node) { + switch (node.getType()) { + case Token.NAME: + case Token.STRING: + case Token.STRING_KEY: + return node.getString(); + default: + return new CodePrinter.Builder(node).build(); + } + } + + @Override + public final void setFunctionName(String name, Node fnNode) { + Node fnNameNode = fnNode.getFirstChild(); + String newName = getAlternateName(name); + fnNameNode.setString(newName); + namedCount++; + bytesUsed += newName.length(); + } + + String getAlternateName(String name) { + String newName = renameMap.get(name); + if (newName == null) { + // Use the previously used name, if possible. + if (previousMap != null) { + newName = previousMap.lookupNewName(name); + } + if (newName == null) { + // otherwise generate a new name. + newName = nameGenerator.generateNextName(); + } + renameMap.put(name, newName); + } + return newName; + } + + @Override + public final String getCombinedName(String lhs, String rhs) { + return lhs + DELIMITER + rhs; + } + } + + /** + * Gets the function renaming map (the "answer key"). + * + * @return A mapping from original names to new names + */ + VariableMap getFunctionMap() { + return new VariableMap(ImmutableMap.copyOf(renameMap)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameGenerator.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameGenerator.java new file mode 100644 index 0000000..5960007 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameGenerator.java @@ -0,0 +1,148 @@ +/* + * Copyright 2005 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.TokenStream; +import javax.annotation.Nullable; +import com.google.common.collect.Sets; +import com.google.common.primitives.Chars; + +import java.util.*; + +/** + * A simple class for generating unique JavaScript variable/property names. + * + *

      This class is not thread safe. + * + */ +final class NameGenerator { + /** Generate short name with this first character */ + static final char[] FIRST_CHAR = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$".toCharArray(); + + /** These appear after after the first character */ + static final char[] NONFIRST_CHAR = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789$" + .toCharArray(); + + private final Set reservedNames; + private final String prefix; + private int nameCount; + + private final char[] firstChars; + private final char[] nonFirstChars; + + /** + * Creates a NameGenerator. + * + * @param reservedNames set of names that are reserved; generated names will + * not include these names. This set is referenced rather than copied, + * so changes to the set will be reflected in how names are generated. + * @param prefix all generated names begin with this prefix. + * @param reservedCharacters If specified these characters won't be used in + * generated names + */ + NameGenerator(Set reservedNames, String prefix, + @Nullable char[] reservedCharacters) { + this.reservedNames = reservedNames; + this.prefix = prefix; + + // build the character arrays to use + this.firstChars = reserveCharacters(FIRST_CHAR, reservedCharacters); + this.nonFirstChars = reserveCharacters(NONFIRST_CHAR, reservedCharacters); + + checkPrefix(prefix); + } + + /** + * Provides the array of available characters based on the specified arrays. + * @param chars The list of characters that are legal + * @param reservedCharacters The characters that should not be used + * @return An array of characters to use. Will return the chars array if + * reservedCharacters is null or empty, otherwise creates a new array. + */ + static char[] reserveCharacters(char[] chars, char[] reservedCharacters) { + if (reservedCharacters == null || reservedCharacters.length == 0) { + return chars; + } + Set charSet = Sets.newLinkedHashSet(Chars.asList(chars)); + for (char reservedCharacter : reservedCharacters) { + charSet.remove(reservedCharacter); + } + return Chars.toArray(charSet); + } + + /** Validates a name prefix. */ + private void checkPrefix(String prefix) { + if (prefix.length() > 0) { + // Make sure that prefix starts with a legal character. + if (!contains(firstChars, prefix.charAt(0))) { + throw new IllegalArgumentException("prefix must start with one of: " + + Arrays.toString(firstChars)); + } + for (int pos = 1; pos < prefix.length(); ++pos) { + if (!contains(nonFirstChars, prefix.charAt(pos))) { + throw new IllegalArgumentException("prefix has invalid characters, " + + "must be one of: " + + Arrays.toString(nonFirstChars)); + } + } + } + } + + private boolean contains(char[] arr, char c) { + for (int i = 0; i < arr.length; i++) { + if (arr[i] == c) { + return true; + } + } + return false; + } + + /** + * Generates the next short name. + */ + String generateNextName() { + while (true) { + String name = prefix; + + int i = nameCount; + + if (name.isEmpty()) { + int pos = i % firstChars.length; + name += firstChars[pos]; + i /= firstChars.length; + } + + while (i > 0) { + i--; + int pos = i % nonFirstChars.length; + name += nonFirstChars[pos]; + i /= nonFirstChars.length; + } + + nameCount++; + + // Make sure it's not a JS keyword or reserved name. + if (TokenStream.isKeyword(name) || reservedNames.contains(name)) { + continue; + } + + return name; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameReferenceGraph.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameReferenceGraph.java new file mode 100644 index 0000000..d9da214 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameReferenceGraph.java @@ -0,0 +1,406 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.javascript.jscomp.DefinitionsRemover.AssignmentDefinition; +import com.google.javascript.jscomp.DefinitionsRemover.Definition; +import com.google.javascript.jscomp.DefinitionsRemover.NamedFunctionDefinition; +import com.google.javascript.jscomp.graph.GraphNode; +import com.google.javascript.jscomp.graph.LinkedDirectedGraph; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * A graph represents all the referencing of global names in the program. In + * other words, it is a call and variable-name graph. + * + *

      The NameReferenceGraph G for a program P is a directed graph G = (V, E) + * where: + * + *

      V ({@link Name}) represents all global names in P and E = (v, v'), v and + * v' in V ({@link Reference} represents a reference use or definition from the + * name v to v' in P. + * + *

      There are two core results we are trying to compute. The first being able + * to precisely identify the function body at any given call site with + * {@link #getReferencesAt(Node)}. + * + *

      The second result come directly from the previous one. The directed edge + * provides us with dependency information. If A->B, B might be needed (in this + * module) if A is needed (in this module). The converse of the this result is + * more useful. B is not needed if A is not needed. + * + */ +class NameReferenceGraph extends + LinkedDirectedGraph + implements DefinitionProvider { + + // This is the key result of the name graph. Given a node in the AST, this map + // will give us the Reference edges. For example a CALL node will map to a + // list of possible call edge destinations. + private final Multimap + referenceMap = HashMultimap.create(); + + // Given a qualified name, provides the Name object. + private Map nameMap = Maps.newHashMap(); + + // The following are some implicit nodes of the graph. + + // If we have a call site that we absolutely have no idea what variable it + // it calls or reference, we'd point it to UNKNOWN. + final Name UNKNOWN; + + // Represents the "main" global block as well as externs. + final Name MAIN; + + // The implicit "window" object. + final Name WINDOW; + + final AbstractCompiler compiler; + + public NameReferenceGraph(AbstractCompiler compiler) { + super(true, true); + this.compiler = compiler; + + // Initialize builtins. + UNKNOWN = new Name("{UNKNOWN}", true); + UNKNOWN.isAliased = true; + UNKNOWN.type = compiler.getTypeRegistry().getNativeType( + JSTypeNative.NO_TYPE); + this.createNode(UNKNOWN); + + MAIN = new Name("{Global Main}", true); + this.createNode(MAIN); + + WINDOW = new Name("window", true); + this.createNode(WINDOW); + } + + public Name defineNameIfNotExists(String name, boolean isExtern) { + Name symbol = null; + if (nameMap.containsKey(name)) { + // This is a re-declaration. + symbol = nameMap.get(name); + } else { + symbol = new Name(name, isExtern); + nameMap.put(name, symbol); + createNode(symbol); + } + return symbol; + } + + /** + * Retrieves a list of all possible Names that this site is referring to. + */ + public List getReferencesAt(Node site) { + Preconditions.checkArgument( + site.isGetProp() || site.isName()); + List result = new ArrayList(); + for (Name target : referenceMap.get(site)) { + result.add(target); + } + return result; + } + + @Override + public Collection getDefinitionsReferencedAt(Node useSite) { + List nameRefs = getReferencesAt(useSite); + if (nameRefs.isEmpty()) { + return null; + } + + List result = Lists.newArrayList(); + for (Name nameRef : nameRefs) { + List decls = nameRef.getDeclarations(); + if (!decls.isEmpty()) { + result.addAll(decls); + } + } + + if (!result.isEmpty()) { + return result; + } else { + return null; + } + } + + public Name getSymbol(String name) { + return nameMap.get(name); + } + + @Override + public GraphNode createNode(Name value) { + nameMap.put(value.qName, value); + return super.createNode(value); + } + + @Override + public void connect(Name src, Reference ref, Name dest) { + super.connect(src, ref, dest); + referenceMap.put(ref.site, dest); + } + + /** + * Represents function or variable names that can be referenced globally. + */ + class Name { + // Full name + private final String qName; + + private JSType type; + + // A list (re)declarations + private List declarations = Lists.newLinkedList(); + + final boolean isExtern; + + private boolean isExported = false; + + private boolean isAliased = false; + + // Function invocations that use ".call" and ".apply" syntax may prevent + // several of the possible optimizations. We keep track of all functions + // invoked in this way so those passes can exclude them. + // Ex: + // some_func.call(some_obj, 1, 2 , 3); + // The name graph does not currently recognize this as a call to some_func. + // This Set is meant to keep track of such occurrence until the name graph + // becomes aware of those cases. + private boolean exposedToCallOrApply = false; + + public Name(String qName, boolean isExtern) { + this.qName = qName; + this.isExtern = isExtern; + int lastDot = qName.lastIndexOf('.'); + String name = (lastDot == -1) ? qName : qName.substring(lastDot + 1); + this.isExported = compiler.getCodingConvention().isExported(name); + this.type = compiler.getTypeRegistry().getNativeType( + JSTypeNative.UNKNOWN_TYPE); + } + + public JSType getType() { + return type; + } + + public void setType(JSType type) { + this.type = type; + } + + public List getDeclarations() { + return declarations; + } + + public void addAssignmentDeclaration(Node node) { + declarations.add(new AssignmentDefinition(node, isExtern)); + } + + public void addFunctionDeclaration(Node node) { + declarations.add(new NamedFunctionDefinition(node, isExtern)); + } + + public boolean isExtern() { + return isExtern; + } + + public void markExported() { + this.isExported = true; + } + + public boolean isExported() { + return isExported; + } + + /** Removes all of the declarations of this name. */ + public final void remove() { + for (Definition declaration : getDeclarations()) { + declaration.remove(); + } + } + + /** + * @return {@code} True if this name has been dereferenced. Removing from + * the program or the module is no longer safe unless further analysis + * can prove otherwise. + */ + public boolean isAliased() { + return isAliased; + } + + public void setAliased(boolean isAliased) { + this.isAliased = isAliased; + } + + public boolean hasSideEffect() { + return isCallable(); + } + + public String getQualifiedName() { + return qName; + } + + /** + * @return The short property name of this object if it is a property, else + * {@code null}. + */ + public String getPropertyName() { + int lastIndexOfDot = qName.lastIndexOf('.'); + if (lastIndexOfDot == -1) { + return null; + } else { + return qName.substring(lastIndexOfDot + 1); + } + } + + public boolean isCallable() { + return type.canBeCalled(); + } + + public boolean exposedToCallOrApply() { + return exposedToCallOrApply; + } + + public void markExposedToCallOrApply() { + exposedToCallOrApply = true; + } + + @Override + public String toString() { + return qName + " : " + type; + } + + @Override + public int hashCode() { + return qName.hashCode(); + } + + /** + * Return true if it's safe to change the signature of the function + * references by this name. It is safe to change the signature if the Name + * is: + *

        + *
      • callable
      • + *
      • not an extern
      • + *
      • not been aliased
      • + *
      • not been exported
      • + *
      • Referred by call or apply functions
      • + *
      • The function uses the arguments property
      • + *
      + * + * @return true if it's safe to change the signature of the name. + */ + public boolean canChangeSignature() { + // Ignore anything that is extern as they should not be changed. + // Also skip over any non-function names. Finally if a function has been + // alias, we don't know all of its callers and should not optimize. + // + // Also, if the function is called using .call or .apply, we don't try to + // optimize those call because the name graph does not give us enough + // information on the parameters. + + // TODO(user) We'll be able to remove the check for call or apply once + // the name graph handles those call. The issue for now is that those + // calls aren't edges in the graph, so we don't have enough information to + // know if it's safe to change the method's signature. + return !(isExtern() || + !isCallable() || + isAliased() || + isExported() || + exposedToCallOrApply() || + nameUsesArgumentsProperty()); + } + + /** + * Returns true if the the arguments property is used in any of the function + * definition. + * Ex. function foo(a,b,c) {return arguments.size;}; + * @return True is arguments is present in one of the definitions. + */ + private boolean nameUsesArgumentsProperty() { + for (Definition definition : getDeclarations()) { + if (NodeUtil.isVarArgsFunction(definition.getRValue())) { + return true; + } + } + return false; + } + } + + /** + * A reference site for a function or a variable reference. It can be a + * reference use or an assignment to that name. + */ + static class Reference { + // The node that references the name. + public final Node site; + + // Parent pointer. + public final Node parent; + + private JSModule module = null; + + // A reference is unknown because we don't know the object's type. + // If A.x->B.y in the name graph and the edge is unknown. It implies + // A.x() reference to someObject.y and B.y MAY be the site. + private boolean isUnknown = false; + + public Reference(Node site, Node parent) { + this.site = site; + this.parent = parent; + } + + public boolean isUnknown() { + return isUnknown; + } + + public void setUnknown(boolean isUnknown) { + this.isUnknown = isUnknown; + } + + public JSModule getModule() { + return module; + } + + public void setModule(JSModule module) { + this.module = module; + } + + boolean isCall() { + return site.isCall(); + } + + /** + * Get accessor for retrieving the actual node corresponding to the + * reference. + * + * @return node representing the access/reference/call site + */ + public Node getSite() { + return site; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameReferenceGraphConstruction.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameReferenceGraphConstruction.java new file mode 100644 index 0000000..d61f9e9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameReferenceGraphConstruction.java @@ -0,0 +1,634 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.javascript.jscomp.NameReferenceGraph.Name; +import com.google.javascript.jscomp.NameReferenceGraph.Reference; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.graph.GraphNode; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.ObjectType; + +import java.util.ArrayList; +import java.util.Collection; + +import javax.annotation.Nullable; + +/** + * Constructs a name reference graph. + * + * @see NameReferenceGraph + * + */ +class NameReferenceGraphConstruction implements CompilerPass { + + private final AbstractCompiler compiler; + private final NameReferenceGraph graph; + + // Maps "foo" -> (curFuncName, unknownObject.foo) if we have no idea what + // the unknown object is. After we finish one pass, we must go through all + // the nodes that might have a name foo and connect that to the curFuncName. + // The accuracy of the analysis will depend heavily on eliminating the need + // to resort to this map. + private final Multimap unknownNameUse = + HashMultimap.create(); + + // Should we continue even if we found a type checker bug. + private static final boolean CONSERVATIVE = false; + + // The symbol for the current function so we can quickly create a reference + // edge when we see a call: Example when this symbol is foo() and we see + // bar(), we connect foo -> bar. + private final ArrayList currentFunctionStack = new ArrayList(); + + NameReferenceGraphConstruction(AbstractCompiler compiler) { + this.compiler = compiler; + this.graph = new NameReferenceGraph(compiler); + } + + NameReferenceGraph getNameReferenceGraph() { + return this.graph; + } + + @Override + public void process(Node externs, Node root) { + // Use the MemoizedScopeCreator instance from TypeCheck if available + // as FunctionTypeBuilder warns about existing types if TypedScopeCreator is + // ran a second time. + ScopeCreator scopeCreator = compiler.getTypedScopeCreator(); + if (scopeCreator == null) { + // The TypedScopeCreator gives us correct handling of namespaces, + // while the default NodeTraversal only gives us a + // SyntacticScopeCreator. + scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); + } + NodeTraversal externsTraversal = new NodeTraversal(compiler, + new Traversal(true), scopeCreator); + NodeTraversal codeTraversal = new NodeTraversal(compiler, + new Traversal(false), scopeCreator); + Scope topScope = compiler.getTopScope(); + if (topScope != null) { + externsTraversal.traverseWithScope(externs, topScope); + codeTraversal.traverseWithScope(root, topScope); + } else { + externsTraversal.traverse(externs); + codeTraversal.traverse(root); + } + connectUnknowns(); + } + + private class Traversal implements ScopedCallback { + + final boolean isExtern; + + private Traversal(boolean isExtern) { + this.isExtern = isExtern; + pushContainingFunction(graph.MAIN); + } + + @Override + public void enterScope(NodeTraversal t) { + Node root = t.getScopeRoot(); + Node parent = root.getParent(); + + // When we are not in a {{GLOBAL MAIN}}, we need to determine what the + // current function is. + if (!t.inGlobalScope()) { + + // TODO(user): A global function foo() is treated as the same + // function as a inner function named foo(). We should use some clever + // naming scheme to avoid this lost of precision. + String name = NodeUtil.getFunctionName(root); + + if (name == null) { + // When the name is null, we have a function that is presumably not + // reference-able again and should not be modeled in the name graph. + // A common example would be (function() { ... })(); + pushContainingFunction(graph.UNKNOWN); + return; + } + + // If we've done type analysis, then we should be able to get the + // correct JSFunctionType for the containing function. If not, + // we're probably going to get an unknown type here. + JSType type = getType(root); + + Node gParent = parent.getParent(); + Node ggParent = gParent.getParent(); + if (parent.isAssign() && + NodeUtil.isPrototypeProperty(parent.getFirstChild())) { + pushContainingFunction( + recordPrototypePropDefinition(t, parent.getFirstChild(), type, + parent, gParent, ggParent)); + } else { + pushContainingFunction( + recordStaticNameDefinition( + t, name, type, root, parent, gParent, root.getLastChild())); + } + } + } + + @Override + public void exitScope(NodeTraversal t) { + if (!t.inGlobalScope()) { + popContainingFunction(); + } + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + return true; + } + + @SuppressWarnings("fallthrough") + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.NAME: + case Token.GETPROP: + if (parent.isGetProp()) { + // We will resolve this when we visit parent later in the traversal. + return; + } else if (parent.isFunction()) { + // Function declarations have been taken care of in enterScope(); + return; + } else if (parent.isAssign()) { + // Handled below. + return; + } + + if (isLocalNameReference(t, n)) { + // Ignore all local variable references unless is creates a closure. + return; + } + + if (isPrototypeNameReference(n)) { + recordPrototypePropUse(t, n, parent); + } else if (isStaticNameReference(n, t.getScope())) { + recordStaticNameUse(t, n, parent); + } else { + recordUnknownUse(t, n, parent); + } + break; + + case Token.ASSIGN: + Node lhs = n.getFirstChild(); + Node rhs = n.getLastChild(); + if (rhs.isFunction()) { + // These are recorded when entering the scope. + return; + } + if (lhs.isName() || + lhs.isGetProp() || + rhs.isGetProp()) { + if (NodeUtil.isPrototypeProperty(lhs)) { + Name name = recordPrototypePropDefinition( + t, lhs, getType(rhs), n, parent, parent.getParent()); + name.setAliased(true); + } + } + maybeAliasNamesOnAssign(lhs, rhs); + break; + + case Token.VAR: + // var foo = bar; + Node varName = n.getFirstChild(); + Node assignedValue = varName.getFirstChild(); + if (assignedValue == null) { + return; + } + maybeAliasNamesOnAssign(varName, assignedValue); + break; + + case Token.CALL: + Node param = n.getFirstChild(); + // We need to alias every name that is passed as a parameter because + // they have different names inside the function's scope. + while ((param = param.getNext()) != null) { + if (param.isName() || param.isGetProp()) { + safeAlias(param); + } + } + + maybeRecordExport(n); + break; + } + } + + private boolean containsName(Node n) { + return NodeUtil.containsType(n, Token.NAME) || + NodeUtil.containsType(n, Token.GETELEM) || + NodeUtil.containsType(n, Token.GETPROP); + } + + /** + * Given a node, this alias all the names in the node that need aliasing. + * This is safer than just calling getQualifiedName() because it can return + * null it several situations. + * @param n node to alias + */ + private void safeAlias(Node n) { + if (n.isName() || n.isGetProp()) { + String name = n.getQualifiedName(); + // getQualifiedName can return null in cases like bar[0].baz + if (name != null) { + defineAndAlias(name); + return; + } + } + + if (n.isGetProp()) { + // var foo = bar[0].baz; + defineAndAlias(n.getLastChild().getString()); + } else if (n.isAssign()) { + // In case of nested assignment, we only consider the name of the + // immediate neighbor. + safeAlias(n.getFirstChild()); + } else if (n.hasChildren()) { + Node cur = n.getFirstChild(); + do { + safeAlias(cur); + } while ((cur = cur.getNext()) != null); + } else { + // No name to alias + } + } + + private void maybeAliasNamesOnAssign(Node lhs, Node rhs) { + if ((lhs.isName() || lhs.isGetProp()) && + containsName(rhs) && + !rhs.isFunction() && + !rhs.isNew()) { + safeAlias(lhs); + safeAlias(rhs); + } + } + + private void defineAndAlias(String name) { + graph.defineNameIfNotExists(name, isExtern).setAliased(true); + } + + private void maybeRecordExport(Node call) { + Preconditions.checkArgument(call.isCall()); + Node getProp = call.getFirstChild(); + if (!getProp.isGetProp()) { + return; + } + + String propQName = getProp.getQualifiedName(); + + if (propQName == null) { + return; + } + + // Keep track of calls to "call" and "apply" because they mess up the name + // graph. + if (propQName.endsWith(".call") || propQName.endsWith(".apply")) { + graph.defineNameIfNotExists(getProp.getFirstChild().getQualifiedName(), + isExtern).markExposedToCallOrApply(); + } + + if (!"goog.exportSymbol".equals(propQName)) { + return; + } + + Node symbol = getProp.getNext(); + if (!symbol.isString()) { + return; + } + + Node obj = symbol.getNext(); + String qName = obj.getQualifiedName(); + + if (qName == null || obj.getNext() != null) { + return; + } + + graph.defineNameIfNotExists(qName, false).markExported(); + } + + /** + * @return true if n MUST be a local name reference. + */ + private boolean isLocalNameReference(NodeTraversal t, Node n) { + // TODO(user): What happen if it is a reference to an outer local + // variable (closures)? + if (n.isName()) { + Var v = t.getScope().getVar(n.getString()); + return v != null && v.isLocal(); + } + return false; + } + + /** + * @return true if n MUST be a static name reference. + */ + private boolean isStaticNameReference(Node n, Scope scope) { + Preconditions.checkArgument(n.isName() || n.isGetProp()); + if (n.isName()) { + return true; + } + String qName = n.getQualifiedName(); + if (qName == null) { + return false; + } + // TODO(user): This does not always work due to type system bugs. + return scope.isDeclared(qName, true); + } + + /** + * @return true if n MUST be a prototype name reference. + */ + private boolean isPrototypeNameReference(Node n) { + if (!n.isGetProp()) { + return false; + } + JSType type = getType(n.getFirstChild()); + if (type.isUnknownType() || type.isUnionType()) { + return false; + } + return (type.isInstanceType() || type.autoboxesTo() != null); + } + + private Name recordStaticNameDefinition(NodeTraversal t, String name, + JSType type, Node n, Node parent, Node gParent, Node rValue) { + if (getNamedContainingFunction() != graph.MAIN) { + // TODO(user): if A.B() defines A.C(), there is a dependence from + // A.C() -> A.B(). However, this is not important in module code motion + // and will be ignored (for now). + } + if (type.isConstructor()) { + return recordClassConstructorOrInterface( + name, type.toMaybeFunctionType(), + n, parent, parent.getParent(), rValue); + } else { + Name symbol = graph.defineNameIfNotExists(name, isExtern); + symbol.setType(type); + if (n.isAssign()) { + symbol.addAssignmentDeclaration(n); + } else { + symbol.addFunctionDeclaration(n); + } + return symbol; + } + } + + /** + * @param assign The assignment node, null if it is just a "forward" + * declaration for recording the rValue's type. + */ + private Name recordPrototypePropDefinition( + NodeTraversal t, Node qName, JSType type, + @Nullable Node assign, @Nullable Node parent, @Nullable Node gParent) { + JSType constructor = getType(NodeUtil.getPrototypeClassName(qName)); + FunctionType classType = null; + String className = null; + + if (constructor != null && constructor.isConstructor()) { + // Case where the class has been properly declared with @constructor + classType = constructor.toMaybeFunctionType(); + className = classType.getReferenceName(); + } else { + // We'll guess it is a constructor even if it didn't have @constructor + classType = compiler.getTypeRegistry().getNativeFunctionType( + JSTypeNative.U2U_CONSTRUCTOR_TYPE); + className = NodeUtil.getPrototypeClassName(qName).getQualifiedName(); + } + // In case we haven't seen the function yet. + recordClassConstructorOrInterface( + className, classType, null, null, null, null); + + String qNameStr = className + ".prototype." + + NodeUtil.getPrototypePropertyName(qName); + Name prototypeProp = graph.defineNameIfNotExists(qNameStr, isExtern); + Preconditions.checkNotNull(prototypeProp, + "%s should be in the name graph as a node.", qNameStr); + if (assign != null) { + prototypeProp.addAssignmentDeclaration(assign); + } + prototypeProp.setType(type); + return prototypeProp; + } + + private Reference recordStaticNameUse( + NodeTraversal t, Node n, Node parent) { + if (isExtern) { + // Don't count reference in extern as a use. + return null; + } else { + Reference reference = new Reference(n, parent); + Name name = graph.defineNameIfNotExists(n.getQualifiedName(), isExtern); + name.setType(getType(n)); + graph.connect(getNamedContainingFunction(), reference, name); + return reference; + } + } + + private void recordPrototypePropUse( + NodeTraversal t, Node n, Node parent) { + Preconditions.checkArgument(n.isGetProp()); + Node instance = n.getFirstChild(); + JSType instanceType = getType(instance); + JSType boxedType = instanceType.autoboxesTo(); + instanceType = boxedType != null ? boxedType : instanceType; + + // Retrieves the property. + ObjectType objType = instanceType.toObjectType(); + Preconditions.checkState(objType != null); + + if (!isExtern) { + // Don't count reference in extern as a use. + Reference ref = new Reference(n, parent); + + FunctionType constructor = objType.getConstructor(); + if (constructor != null) { + String propName = n.getLastChild().getString(); + if (!constructor.getPrototype().hasOwnProperty(propName)) { + recordSuperClassPrototypePropUse(constructor, propName, ref); + } + + // TODO(user): TightenType can help a whole lot here. + recordSubclassPrototypePropUse(constructor, propName, ref); + } else { + recordUnknownUse(t, n, parent); + } + } + } + + /** + * Look for the super class implementation up the tree. + */ + private void recordSuperClassPrototypePropUse( + FunctionType classType, String prop, Reference ref) { + FunctionType superClass = classType.getSuperClassConstructor(); + while (superClass != null) { + if (superClass.getPrototype().hasOwnProperty(prop)) { + graph.connect(getNamedContainingFunction(), ref, + graph.defineNameIfNotExists( + superClass.getReferenceName() + ".prototype." + prop, false)); + return; + } else { + superClass = superClass.getSuperClassConstructor(); + } + } + } + + /** + * Conservatively assumes that all subclass implementation of this property + * might be called. + */ + private void recordSubclassPrototypePropUse( + FunctionType classType, String prop, Reference ref) { + if (classType.getPrototype().hasOwnProperty(prop)) { + graph.connect(getNamedContainingFunction(), ref, + graph.defineNameIfNotExists( + classType.getReferenceName() + ".prototype." + prop, false)); + } + if (classType.getSubTypes() != null) { + for (FunctionType subclass : classType.getSubTypes()) { + recordSubclassPrototypePropUse(subclass, prop, ref); + } + } + } + + private void recordUnknownUse(NodeTraversal t, Node n, Node parent) { + if (isExtern) { + // Don't count reference in extern as a use. + return; + } else { + Preconditions.checkArgument(n.isGetProp()); + Reference ref = new Reference(n, parent); + ref.setUnknown(true); + unknownNameUse.put(n.getLastChild().getString(), + new NameUse(getNamedContainingFunction(), ref)); + } + } + + /** + * Creates the name in the graph if it does not already exist. Also puts all + * the properties and prototype properties of this name in the graph. + */ + private Name recordClassConstructorOrInterface( + String name, FunctionType type, @Nullable Node n, @Nullable Node parent, + @Nullable Node gParent, @Nullable Node rhs) { + Preconditions.checkArgument(type.isConstructor() || type.isInterface()); + Name symbol = graph.defineNameIfNotExists(name, isExtern); + if (rhs != null) { + // TODO(user): record the definition. + symbol.setType(getType(rhs)); + if (n.isAssign()) { + symbol.addAssignmentDeclaration(n); + } else { + symbol.addFunctionDeclaration(n); + } + } + ObjectType prototype = type.getPrototype(); + for (String prop : prototype.getOwnPropertyNames()) { + graph.defineNameIfNotExists( + name + ".prototype." + prop, isExtern); + } + return symbol; + } + } + + private void connectUnknowns() { + for (GraphNode node : graph.getNodes()) { + Name name = node.getValue(); + String propName = name.getPropertyName(); + if (propName == null) { + continue; + } + Collection uses = unknownNameUse.get(propName); + if (uses != null) { + for (NameUse use : uses) { + graph.connect(use.name, use.reference, name); + } + } + } + } + + /** + * A helper to retrieve the type of a node. + */ + private JSType getType(Node n) { + JSType type = n.getJSType(); + if (type == null) { + if (CONSERVATIVE) { + throw new RuntimeException("Type system failed us :("); + } else { + return compiler.getTypeRegistry().getNativeType( + JSTypeNative.UNKNOWN_TYPE); + } + } + // Null-ability does not affect the name graph's result. + return type.restrictByNotNullOrUndefined(); + } + + /** + * Mark the provided node as the current function that we are analyzing. + * and add it to the stack of scopes we are inside. + * + * @param functionNode node representing current function. + */ + private void pushContainingFunction(Name functionNode) { + currentFunctionStack.add(functionNode); + } + + /** + * Remove the top item off the containing function stack, and restore the + * previous containing scope to the be the current containing function. + */ + private void popContainingFunction() { + currentFunctionStack.remove(currentFunctionStack.size() - 1); + } + + /** + * Find the first containing function that's not an function expression + * closure. + */ + private Name getNamedContainingFunction() { + Name containingFn = null; + int pos; + for (pos = currentFunctionStack.size() - 1; pos >= 0; pos = pos - 1) { + Name cf = currentFunctionStack.get(pos); + if (cf != graph.UNKNOWN) { + containingFn = cf; + break; + } + } + Preconditions.checkNotNull(containingFn); + return containingFn; + } + + private static class NameUse { + private final Name name; + private final Reference reference; + + private NameUse(Name name, Reference reference) { + this.name = name; + this.reference = reference; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameReferenceGraphReport.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameReferenceGraphReport.java new file mode 100644 index 0000000..86a5203 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NameReferenceGraphReport.java @@ -0,0 +1,321 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.NameReferenceGraph.Name; +import com.google.javascript.jscomp.NameReferenceGraph.Reference; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.JSType; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * Generate a nice HTML file describing the name reference graph. + * For each declaration, list the sites where the declaration's name + * is referenced, and list all the names that the declaration references. + * For each, name exactly where use occurs in the source code. + * + *

      This report should be useful both for internal compiler + * developers and for engineers trying to understand running behavior + * of their code or who want to understand why the compiler won't + * move their code into a new module. + * + * @author bowdidge@google.com (Robert Bowdidge) + */ + +final class NameReferenceGraphReport { + private NameReferenceGraph graph = null; + + /** + * Create a NameReferenceGraphReport object. + * + * @param g name reference graph to describe in report. + */ + NameReferenceGraphReport(NameReferenceGraph g) { + this.graph = g; + } + + /** + * Generate a nice HTML file describing the name reference graph. + * For each declaration, list the sites where the declaration's name + * is referenced, and list all the names that the declaration references. + * For each, name exactly where use occurs in the source code. + * + *

      This report should be useful both for internal compiler + * developers and for engineers trying to understand running + * behavior of their code or who want to understand why + * AbstractCompiler won't move their code into a new module. + * + * @return String containing the entire HTML for the report. + */ + public String getHtmlReport() { + StringBuilder builder = new StringBuilder(); + List> nodes = Lists.newArrayList( + graph.getDirectedGraphNodes()); + + generateHtmlReportHeader(builder); + + builder.append("

      Name Reference Graph Dump

      \n"); + builder.append("OVERALL STATS\n"); + builder.append("
        \n"); + builder.append("
      • Total names: " + nodes.size()); + builder.append("
      \n"); + + builder.append("ALL NAMES\n"); + builder.append("
        \n"); + + // Sort declarations in alphabetical order. + Collections.sort(nodes, new DiGraphNodeComparator()); + + for (DiGraphNode n : nodes) { + // Generate the HTML describing the declaration itself. + generateDeclarationReport(builder, n); + + // Next, list the places where this name is used (REFERS TO), and the + // names that this declaration refers to (REFERENCED BY). + List> outEdges = + graph.getOutEdges(n.getValue()); + List> inEdges = + graph.getInEdges(n.getValue()); + + // Don't bother to create the dotted list if we don't have anything to + // put in it. + if (!outEdges.isEmpty() || !inEdges.isEmpty()) { + builder.append("
          "); + + if (outEdges.size() > 0) { + builder.append("
        • REFERS TO:
          \n"); + builder.append("
            "); + for (DiGraphEdge edge : outEdges) { + generateEdgeReport(builder, edge.getDestination().getValue(), + edge); + } + builder.append("
          \n"); + } + + if (inEdges.size() > 0) { + builder.append("
        • REFERENCED BY:
          \n"); + builder.append("
            "); + for (DiGraphEdge edge : inEdges) { + generateEdgeReport(builder, edge.getSource().getValue(), edge); + } + builder.append("
          "); + } + builder.append("
        \n"); + } + } + builder.append("
      \n"); + generateHtmlReportFooter(builder); + return builder.toString(); + } + + /** + * Given a node, find the name of the containing source file. + * + * @param node Parse tree node whose filename is requested + * @return String containing name of source file, or empty string if name + * cannot be identified. + */ + private String getSourceFile(Node node) { + String filename = node.getSourceFileName(); + if (filename == null) { + return ""; + } + return filename; + } + + /** + * Generate the HTML for describing a specific declaration. + * @param builder contents of report to be generated + * @param declarationNode declaration to describe + */ + private void generateDeclarationReport(StringBuilder builder, + DiGraphNode declarationNode) { + // Provide the name and location of declaration, + // with an anchor to allow navigation to this declaration. + String declName = declarationNode.getValue().getQualifiedName(); + JSType declType = declarationNode.getValue().getType(); + + builder.append("
    9. "); + builder.append(""); + builder.append(declName); + builder.append("\n"); + + // Provide the type of the declaration. + // This is helpful for debugging. + generateType(builder, declType); + + // List all the definitions of this name that were found in the code. + // For each, list + List defs = + declarationNode.getValue().getDeclarations(); + + if (defs.size() == 0) { + builder.append("
      No definitions found
      "); + } else { + // Otherwise, provide a list of definitions in a dotted list. + // For each definition, print the location where that definition is + // found. + builder.append("
        "); + for (DefinitionsRemover.Definition def : defs) { + Node fnDef = def.getRValue(); + String sourceFileName = getSourceFile(fnDef); + builder.append("
      • Defined: "); + generateSourceReferenceLink(builder, + sourceFileName, fnDef.getLineno(), fnDef.getCharno()); + } + builder.append("
      "); + } + } + + /** + * Generate the HTML header for the report style. + * Borrowed straight from NameAnalyzer's report style. + * + * @param builder contents of the report to be generated + */ + private void generateHtmlReportHeader(StringBuilder builder) { + builder.append("\n" + + "" + + "" + + "" + + "Name Reference Graph Dump" + + "\n"); + } + + /** + * Generate the HTML footer for the report style. + */ + private void generateHtmlReportFooter(StringBuilder builder) { + builder.append(""); + } + + /** + * Generate a description of a specific edge between two nodes. + * For each edge, name the element being linked, the location of the + * reference in the source file, and the type of the reference. + * + * @param builder contents of the report to be generated + * @param referencedDecl name of the declaration being referenced + * @param edge the graph edge being described + */ + private void generateEdgeReport(StringBuilder builder, + Name referencedDecl, DiGraphEdge edge) { + String srcDeclName = referencedDecl.getQualifiedName(); + builder.append("
    10. "); + builder.append(srcDeclName); + builder.append(" "); + + Node def = edge.getValue().getSite(); + int lineNumber = def.getLineno(); + int columnNumber = def.getCharno(); + String sourceFile = getSourceFile(def); + + generateSourceReferenceLink(builder, sourceFile, lineNumber, columnNumber); + + JSType defType = edge.getValue().getSite().getJSType(); + generateType(builder, defType); + } + + /** + * Generate a link and text for a reference to a particular location + * in a source file. Selecting the link should take the programmer + * to a browsable version of the file. + * + * @param builder contents of the report to be generated + * @param sourceFile Path to the file + * @param lineNumber line where the object to view is located + * @param columnNumber column where the object to highlight is located. + */ + private void generateSourceReferenceLink(StringBuilder builder, + String sourceFile, int lineNumber, int columnNumber) { + assert(sourceFile != null); + + builder.append("("); + + + // Print out the text path so the user knows where things come from. + builder.append(sourceFile + ":" + + lineNumber + "," + columnNumber); + + + builder.append(")"); + } + + /** + * Dump a type in a nice, readable way. + * + * @param builder contents of the report to be generated. + * @param defType type to describe + */ + private void generateType(StringBuilder builder, JSType defType) { + if (defType == null) { + builder.append(" (type: null) "); + } else if (defType.isUnknownType()) { + builder.append(" (type: unknown) "); + } else { + builder.append(" (type: " + + defType.toString() + ") "); + } + } + + /** + * DiGraphNodeComparator gives us a way to generate sorted lists + * of DiGraphNodes. It provides a compare function used by the + * String class's sort method. + */ + class DiGraphNodeComparator implements + Comparator> { + @Override + public int compare(DiGraphNode node1, + DiGraphNode node2) { + Preconditions.checkNotNull(node1.getValue()); + Preconditions.checkNotNull(node2.getValue()); + + if ((node1.getValue().getQualifiedName() == null) && + (node2.getValue().getQualifiedName() == null)) { + return 0; + } + + // Node 1, if null, comes before node 2. + if (node1.getValue().getQualifiedName() == null) { + return -1; + } + + // Node 2, if null, comes before node 1. + if (node2.getValue().getQualifiedName() == null) { + return 1; + } + + return node1.getValue().getQualifiedName().compareTo( + node2.getValue().getQualifiedName()); + } + } + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NodeIterators.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NodeIterators.java new file mode 100644 index 0000000..e6b5d04 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NodeIterators.java @@ -0,0 +1,290 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Stack; + +/** + * A package for common iteration patterns. + * + * All iterators are forward, post-order traversals unless otherwise noted. + * + * @author nicksantos@google.com (Nick Santos) + */ +class NodeIterators { + + private NodeIterators() {} /* all static */ + + /** + * Traverses the local scope, skipping all function nodes. + */ + static class FunctionlessLocalScope implements Iterator { + private final Stack ancestors = new Stack(); + + /** + * @param ancestors The ancestors of the point where iteration will start, + * beginning with the deepest ancestor. The start node will not be + * exposed in the iteration. + */ + FunctionlessLocalScope(Node ... ancestors) { + Preconditions.checkArgument(ancestors.length > 0); + + for (Node n : ancestors) { + if (n.isFunction()) { + break; + } + + this.ancestors.add(0, n); + } + } + + @Override + public boolean hasNext() { + // Check if the current node has any nodes after it. + return !(ancestors.size() == 1 && ancestors.peek().getNext() == null); + } + + @Override + public Node next() { + Node current = ancestors.pop(); + if (current.getNext() == null) { + current = ancestors.peek(); + + // If this is a function node, skip it. + if (current.isFunction()) { + return next(); + } + } else { + current = current.getNext(); + ancestors.push(current); + + // If this is a function node, skip it. + if (current.isFunction()) { + return next(); + } + + while (current.hasChildren()) { + current = current.getFirstChild(); + ancestors.push(current); + + // If this is a function node, skip it. + if (current.isFunction()) { + return next(); + } + } + } + + return current; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not implemented"); + } + + /** + * Gets the node most recently returned by next(). + */ + protected Node current() { + return ancestors.peek(); + } + + /** + * Gets the parent of the node most recently returned by next(). + */ + protected Node currentParent() { + return ancestors.size() >= 2 ? + ancestors.get(ancestors.size() - 2) : null; + } + + /** + * Gets the ancestors of the current node, with the deepest node first. + * Only exposed for testing purposes. + */ + List currentAncestors() { + List list = Lists.newArrayList(ancestors); + Collections.reverse(list); + return list; + } + } + + /** + * An iterator to help with variable inlining. Given a variable declaration, + * find all the nodes in post-order where the variable is guaranteed to + * retain its original value. + * + * Consider: + *
      +   * var X = 1;
      +   * var Y = 3; // X is still 1
      +   * if (Y) {
      +   *   // X is still 1
      +   * } else {
      +   *   X = 5;
      +   * }
      +   * // X may not be 1
      +   * 
      + * In the above example, the iterator will iterate past the declaration of + * Y and into the first block of the IF branch, and will stop at the + * assignment {@code X = 5}. + */ + static class LocalVarMotion implements Iterator { + private final boolean valueHasSideEffects; + private final FunctionlessLocalScope iterator; + private final String varName; + private Node lookAhead; + + /** + * @return Create a LocalVarMotion for use with moving a value assigned + * at a variable declaration. + */ + static LocalVarMotion forVar( + Node name, Node var, Node block) { + Preconditions.checkArgument(var.isVar()); + Preconditions.checkArgument(NodeUtil.isStatement(var)); + // The FunctionlessLocalScope must start at "name" as this may be used + // before the Normalize pass, and thus the VAR node may define multiple + // names and the "name" node may have siblings. The actual assigned + // value is skipped as it is a child of name. + return new LocalVarMotion( + name, new FunctionlessLocalScope(name, var, block)); + } + + /** + * @return Create a LocalVarMotion for use with moving a value assigned + * as part of a simple assignment expression ("a = b;"). + */ + static LocalVarMotion forAssign( + Node name, Node assign, Node expr, Node block) { + Preconditions.checkArgument(assign.isAssign()); + Preconditions.checkArgument(expr.isExprResult()); + // The FunctionlessLocalScope must start at "assign", to skip the value + // assigned to "name" (which would be its sibling). + return new LocalVarMotion( + name, new FunctionlessLocalScope(assign, expr, block)); + } + + /** + * @param iterator The iterator to use while inspecting the node + * beginning with the deepest ancestor. + */ + private LocalVarMotion(Node nameNode, FunctionlessLocalScope iterator) { + Preconditions.checkArgument(nameNode.isName()); + Node valueNode = NodeUtil.getAssignedValue(nameNode); + this.varName = nameNode.getString(); + this.valueHasSideEffects = valueNode != null && + NodeUtil.mayHaveSideEffects(valueNode); + this.iterator = iterator; + advanceLookAhead(true); + } + + @Override + public boolean hasNext() { + return lookAhead != null; + } + + @Override + public Node next() { + Node next = lookAhead; + advanceLookAhead(false); + return next; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not implemented"); + } + + private void advanceLookAhead(boolean atStart) { + if (!atStart) { + if (lookAhead == null) { + return; + } + + // Don't advance past a reference to the variable that we're trying + // to inline. + Node curNode = iterator.current(); + if (curNode.isName() && + varName.equals(curNode.getString())) { + lookAhead = null; + return; + } + } + + if (!iterator.hasNext()) { + lookAhead = null; + return; + } + + Node nextNode = iterator.next(); + Node nextParent = iterator.currentParent(); + int type = nextNode.getType(); + + if (valueHasSideEffects) { + // Reject anything that might read state + boolean readsState = false; + + if (// Any read of a different variable. + (nextNode.isName() && !varName.equals(nextNode.getString())) || + // Any read of a property. + (nextNode.isGetProp() || nextNode.isGetElem())) { + + // If this is a simple assign, we'll be ok. + if (nextParent == null || + !NodeUtil.isVarOrSimpleAssignLhs(nextNode, nextParent)) { + readsState = true; + } + + } else if (nextNode.isCall() || nextNode.isNew()) { + // This isn't really an important case. In most cases when we use + // CALL or NEW, we're invoking it on a NAME or a GETPROP. And in the + // few cases where we're not, it's because we have an anonymous + // function that escapes the variable we're worried about. But we + // include this for completeness. + readsState = true; + } + + if (readsState) { + lookAhead = null; + return; + } + } + + // Reject anything that might modify relevant state. We assume that + // nobody relies on variables being undeclared, which will break + // constructions like: + // var a = b; + // var b = 3; + // alert(a); + if (NodeUtil.nodeTypeMayHaveSideEffects(nextNode) && type != Token.NAME + || type == Token.NAME && nextParent.isCatch()) { + lookAhead = null; + return; + } + + lookAhead = nextNode; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NodeNameExtractor.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NodeNameExtractor.java new file mode 100644 index 0000000..c393861 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NodeNameExtractor.java @@ -0,0 +1,96 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.TokenStream; + +/** + * Utility class that extracts the qualified name out of a node. + * Useful when trying to get a human-friendly string representation of + * a property node that can be used to describe the node or name + * related nodes based on it (as done by the NameAnonymousFunctions + * compiler pass). + * + */ +class NodeNameExtractor { + private final char delimiter; + private int nextUniqueInt = 0; + + NodeNameExtractor(char delimiter) { + this.delimiter = delimiter; + } + + /** + * Returns a qualified name of the specified node. Dots and brackets + * are changed to the delimiter passed in when constructing the + * NodeNameExtractor object. We also replace ".prototype" with the + * delimiter to keep names short, while still differentiating them + * from static properties. (Prototype properties will end up + * looking like "a$b$$c" if this.delimiter = '$'.) + */ + String getName(Node node) { + switch (node.getType()) { + case Token.FUNCTION: + Node functionNameNode = node.getFirstChild(); + return functionNameNode.getString(); + case Token.GETPROP: + Node lhsOfDot = node.getFirstChild(); + Node rhsOfDot = lhsOfDot.getNext(); + String lhsOfDotName = getName(lhsOfDot); + String rhsOfDotName = getName(rhsOfDot); + if ("prototype".equals(rhsOfDotName)) { + return lhsOfDotName + delimiter; + } else { + return lhsOfDotName + delimiter + rhsOfDotName; + } + case Token.GETELEM: + Node outsideBrackets = node.getFirstChild(); + Node insideBrackets = outsideBrackets.getNext(); + String nameOutsideBrackets = getName(outsideBrackets); + String nameInsideBrackets = getName(insideBrackets); + if ("prototype".equals(nameInsideBrackets)) { + return nameOutsideBrackets + delimiter; + } else { + return nameOutsideBrackets + delimiter + nameInsideBrackets; + } + case Token.NAME: + return node.getString(); + case Token.STRING: + case Token.STRING_KEY: + return TokenStream.isJSIdentifier(node.getString()) ? + node.getString() : ("__" + nextUniqueInt++); + case Token.NUMBER: + return NodeUtil.getStringValue(node); + case Token.THIS: + return "this"; + case Token.CALL: + return getName(node.getFirstChild()); + default: + StringBuilder sb = new StringBuilder(); + for (Node child = node.getFirstChild(); child != null; + child = child.getNext()) { + if (sb.length() > 0) { + sb.append(delimiter); + } + sb.append(getName(child)); + } + return sb.toString(); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NodeTraversal.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NodeTraversal.java new file mode 100644 index 0000000..39dd0bd --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NodeTraversal.java @@ -0,0 +1,690 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + + +/** + * NodeTraversal allows an iteration through the nodes in the parse tree, + * and facilitates the optimizations on the parse tree. + * + */ +public class NodeTraversal { + // Package protected for tests + private final AbstractCompiler compiler; + private final Callback callback; + + /** Contains the current node*/ + private Node curNode; + + public static final DiagnosticType NODE_TRAVERSAL_ERROR = + DiagnosticType.error("JSC_NODE_TRAVERSAL_ERROR", "{0}"); + + /** + * Stack containing the Scopes that have been created. The Scope objects + * are lazily created; so the {@code scopeRoots} stack contains the + * Nodes for all Scopes that have not been created yet. + */ + private final Deque scopes = new ArrayDeque(); + + /** + * A stack of scope roots. All scopes that have not been created + * are represented in this Deque. + */ + private final Deque scopeRoots = new ArrayDeque(); + + + /** + * Stack of control flow graphs (CFG). There is one CFG per scope. CFGs + * are lazily populated: elements are {@code null} until requested by + * {@link #getControlFlowGraph()}. Note that {@link ArrayDeque} does not allow + * {@code null} elements, so {@link LinkedList} is used instead. + */ + Deque> cfgs = new LinkedList>(); + + /** The current source file name */ + private String sourceName; + + /** The current input */ + private InputId inputId; + + /** The scope creator */ + private ScopeCreator scopeCreator; + + /** Possible callback for scope entry and exist **/ + private ScopedCallback scopeCallback; + + /** + * Callback + */ + public interface Callback { + /** + *

      Visits a node in pre order (before visiting its children) and decides + * whether this node's children should be traversed. If children are + * traversed, they will be visited by + * {@link #visit(NodeTraversal, Node, Node)} in post order.

      + *

      Implementations can have side effects (e.g. modifying the parse + * tree).

      + * @return whether the children of this node should be visited + */ + boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent); + + /** + *

      Visits a node in post order (after its children have been visited). + * A node is visited only if all its parents should be traversed + * ({@link #shouldTraverse(NodeTraversal, Node, Node)}).

      + *

      Implementations can have side effects (e.g. modifying the parse + * tree).

      + */ + void visit(NodeTraversal t, Node n, Node parent); + } + + /** + * Callback that also knows about scope changes + */ + public interface ScopedCallback extends Callback { + + /** + * Called immediately after entering a new scope. The new scope can + * be accessed through t.getScope() + */ + void enterScope(NodeTraversal t); + + /** + * Called immediately before exiting a scope. The ending scope can + * be accessed through t.getScope() + */ + void exitScope(NodeTraversal t); + } + + /** + * Abstract callback to visit all nodes in post order. + */ + public abstract static class AbstractPostOrderCallback implements Callback { + @Override + public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, + Node parent) { + return true; + } + } + + /** + * Abstract scoped callback to visit all nodes in post order. + */ + public abstract static class AbstractScopedCallback + implements ScopedCallback { + @Override + public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, + Node parent) { + return true; + } + + @Override + public void enterScope(NodeTraversal t) {} + + @Override + public void exitScope(NodeTraversal t) {} + } + + /** + * Abstract callback to visit all nodes but not traverse into function + * bodies. + */ + public abstract static class AbstractShallowCallback implements Callback { + @Override + public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, + Node parent) { + // We do want to traverse the name of a named function, but we don't + // want to traverse the arguments or body. + return parent == null || !parent.isFunction() || + n == parent.getFirstChild(); + } + } + + /** + * Abstract callback to visit all structure and statement nodes but doesn't + * traverse into functions or expressions. + */ + public abstract static class AbstractShallowStatementCallback + implements Callback { + @Override + public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, + Node parent) { + return parent == null || NodeUtil.isControlStructure(parent) + || NodeUtil.isStatementBlock(parent); + } + } + + /** + * Abstract callback to visit a pruned set of nodes. + */ + public abstract static class AbstractNodeTypePruningCallback + implements Callback { + private final Set nodeTypes; + private final boolean include; + + /** + * Creates an abstract pruned callback. + * @param nodeTypes the nodes to include in the traversal + */ + public AbstractNodeTypePruningCallback(Set nodeTypes) { + this(nodeTypes, true); + } + + /** + * Creates an abstract pruned callback. + * @param nodeTypes the nodes to include/exclude in the traversal + * @param include whether to include or exclude the nodes in the traversal + */ + public AbstractNodeTypePruningCallback(Set nodeTypes, + boolean include) { + this.nodeTypes = nodeTypes; + this.include = include; + } + + @Override + public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, + Node parent) { + return include == nodeTypes.contains(n.getType()); + } + } + + /** + * Creates a node traversal using the specified callback interface. + */ + public NodeTraversal(AbstractCompiler compiler, Callback cb) { + this(compiler, cb, new SyntacticScopeCreator(compiler)); + } + + /** + * Creates a node traversal using the specified callback interface + * and the scope creator. + */ + public NodeTraversal(AbstractCompiler compiler, Callback cb, + ScopeCreator scopeCreator) { + this.callback = cb; + if (cb instanceof ScopedCallback) { + this.scopeCallback = (ScopedCallback) cb; + } + this.compiler = compiler; + this.inputId = null; + this.sourceName = ""; + this.scopeCreator = scopeCreator; + } + + private void throwUnexpectedException(Exception unexpectedException) { + // If there's an unexpected exception, try to get the + // line number of the code that caused it. + String message = unexpectedException.getMessage(); + + // TODO(user): It is possible to get more information if curNode or + // its parent is missing. We still have the scope stack in which it is still + // very useful to find out at least which function caused the exception. + if (inputId != null) { + message = + unexpectedException.getMessage() + "\n" + + formatNodeContext("Node", curNode) + + (curNode == null ? + "" : + formatNodeContext("Parent", curNode.getParent())); + } + compiler.throwInternalError(message, unexpectedException); + } + + private String formatNodeContext(String label, Node n) { + if (n == null) { + return " " + label + ": NULL"; + } + return " " + label + "(" + n.toString(false, false, false) + "): " + + formatNodePosition(n); + } + + /** + * Traverses a parse tree recursively. + */ + public void traverse(Node root) { + try { + inputId = NodeUtil.getInputId(root); + sourceName = ""; + curNode = root; + pushScope(root); + traverseBranch(root, null); + popScope(); + } catch (Exception unexpectedException) { + throwUnexpectedException(unexpectedException); + } + } + + public void traverseRoots(Node ... roots) { + traverseRoots(Lists.newArrayList(roots)); + } + + public void traverseRoots(List roots) { + if (roots.isEmpty()) { + return; + } + + try { + Node scopeRoot = roots.get(0).getParent(); + Preconditions.checkState(scopeRoot != null); + + inputId = NodeUtil.getInputId(scopeRoot); + sourceName = ""; + curNode = scopeRoot; + pushScope(scopeRoot); + + for (Node root : roots) { + Preconditions.checkState(root.getParent() == scopeRoot); + traverseBranch(root, scopeRoot); + } + + popScope(); + } catch (Exception unexpectedException) { + throwUnexpectedException(unexpectedException); + } + } + + private static final String MISSING_SOURCE = "[source unknown]"; + + private String formatNodePosition(Node n) { + String sourceFileName = getBestSourceFileName(n); + if (sourceFileName == null) { + return MISSING_SOURCE + "\n"; + } + + int lineNumber = n.getLineno(); + int columnNumber = n.getCharno(); + String src = compiler.getSourceLine(sourceFileName, lineNumber); + if (src == null) { + src = MISSING_SOURCE; + } + return sourceFileName + ":" + lineNumber + ":" + columnNumber + "\n" + + src + "\n"; + } + + /** + * Traverses a parse tree recursively with a scope, starting with the given + * root. This should only be used in the global scope. Otherwise, use + * {@link #traverseAtScope}. + */ + void traverseWithScope(Node root, Scope s) { + Preconditions.checkState(s.isGlobal()); + + inputId = null; + sourceName = ""; + curNode = root; + pushScope(s); + traverseBranch(root, null); + popScope(); + } + + /** + * Traverses a parse tree recursively with a scope, starting at that scope's + * root. + */ + void traverseAtScope(Scope s) { + Node n = s.getRootNode(); + if (n.isFunction()) { + // We need to do some extra magic to make sure that the scope doesn't + // get re-created when we dive into the function. + if (inputId == null) { + inputId = NodeUtil.getInputId(n); + } + sourceName = getSourceName(n); + curNode = n; + pushScope(s); + + Node args = n.getFirstChild().getNext(); + Node body = args.getNext(); + traverseBranch(args, n); + traverseBranch(body, n); + + popScope(); + } else { + traverseWithScope(n, s); + } + } + + /** + * Traverses an inner node recursively with a refined scope. An inner node may + * be any node with a non {@code null} parent (i.e. all nodes except the + * root). + * + * @param node the node to traverse + * @param parent the node's parent, it may not be {@code null} + * @param refinedScope the refined scope of the scope currently at the top of + * the scope stack or in trivial cases that very scope or {@code null} + */ + protected void traverseInnerNode(Node node, Node parent, Scope refinedScope) { + Preconditions.checkNotNull(parent); + if (refinedScope != null && getScope() != refinedScope) { + curNode = node; + pushScope(refinedScope); + traverseBranch(node, parent); + popScope(); + } else { + traverseBranch(node, parent); + } + } + + /** + * Gets the compiler. + */ + public Compiler getCompiler() { + // TODO(nicksantos): Remove this type cast. This is just temporary + // while refactoring. + return (Compiler) compiler; + } + + /** + * Gets the current line number, or zero if it cannot be determined. The line + * number is retrieved lazily as a running time optimization. + */ + public int getLineNumber() { + Node cur = curNode; + while (cur != null) { + int line = cur.getLineno(); + if (line >=0) { + return line; + } + cur = cur.getParent(); + } + return 0; + } + + /** + * Gets the current input source name. + * + * @return A string that may be empty, but not null + */ + public String getSourceName() { + return sourceName; + } + + /** + * Gets the current input source. + */ + public CompilerInput getInput() { + return compiler.getInput(inputId); + } + + /** + * Gets the current input module. + */ + public JSModule getModule() { + CompilerInput input = getInput(); + return input == null ? null : input.getModule(); + } + + /** Returns the node currently being traversed. */ + public Node getCurrentNode() { + return curNode; + } + + /** + * Traverses a node recursively. + */ + public static void traverse( + AbstractCompiler compiler, Node root, Callback cb) { + NodeTraversal t = new NodeTraversal(compiler, cb); + t.traverse(root); + } + + /** + * Traverses a list of node trees. + */ + public static void traverseRoots( + AbstractCompiler compiler, List roots, Callback cb) { + NodeTraversal t = new NodeTraversal(compiler, cb); + t.traverseRoots(roots); + } + + public static void traverseRoots( + AbstractCompiler compiler, Callback cb, Node ... roots) { + NodeTraversal t = new NodeTraversal(compiler, cb); + t.traverseRoots(roots); + } + + /** + * Traverses a branch. + */ + @SuppressWarnings("fallthrough") + private void traverseBranch(Node n, Node parent) { + int type = n.getType(); + if (type == Token.SCRIPT) { + inputId = n.getInputId(); + sourceName = getSourceName(n); + } + + curNode = n; + if (!callback.shouldTraverse(this, n, parent)) return; + + switch (type) { + case Token.FUNCTION: + traverseFunction(n, parent); + break; + + default: + for (Node child = n.getFirstChild(); child != null; ) { + // child could be replaced, in which case our child node + // would no longer point to the true next + Node next = child.getNext(); + traverseBranch(child, n); + child = next; + } + break; + } + + curNode = n; + callback.visit(this, n, parent); + } + + /** + * Traverses a function. + */ + private void traverseFunction(Node n, Node parent) { + Preconditions.checkState(n.getChildCount() == 3); + Preconditions.checkState(n.isFunction()); + + final Node fnName = n.getFirstChild(); + + boolean isFunctionExpression = (parent != null) + && NodeUtil.isFunctionExpression(n); + + if (!isFunctionExpression) { + // Functions declarations are in the scope containing the declaration. + traverseBranch(fnName, n); + } + + curNode = n; + pushScope(n); + + if (isFunctionExpression) { + // Function expression names are only accessible within the function + // scope. + traverseBranch(fnName, n); + } + + final Node args = fnName.getNext(); + final Node body = args.getNext(); + + // Args + traverseBranch(args, n); + + // Body + Preconditions.checkState(body.getNext() == null && + body.isBlock(), body); + traverseBranch(body, n); + + popScope(); + } + + /** Examines the functions stack for the last instance of a function node. */ + @SuppressWarnings("unchecked") + public Node getEnclosingFunction() { + if (scopes.size() + scopeRoots.size() < 2) { + return null; + } else { + if (scopeRoots.isEmpty()) { + return scopes.peek().getRootNode(); + } else { + return scopeRoots.peek(); + } + } + } + + /** Creates a new scope (e.g. when entering a function). */ + private void pushScope(Node node) { + Preconditions.checkState(curNode != null); + scopeRoots.push(node); + cfgs.push(null); + if (scopeCallback != null) { + scopeCallback.enterScope(this); + } + } + + /** Creates a new scope (e.g. when entering a function). */ + private void pushScope(Scope s) { + Preconditions.checkState(curNode != null); + scopes.push(s); + cfgs.push(null); + if (scopeCallback != null) { + scopeCallback.enterScope(this); + } + } + + /** Pops back to the previous scope (e.g. when leaving a function). */ + private void popScope() { + if (scopeCallback != null) { + scopeCallback.exitScope(this); + } + if (scopeRoots.isEmpty()) { + scopes.pop(); + } else { + scopeRoots.pop(); + } + cfgs.pop(); + } + + /** Gets the current scope. */ + public Scope getScope() { + Scope scope = scopes.isEmpty() ? null : scopes.peek(); + if (scopeRoots.isEmpty()) { + return scope; + } + + Iterator it = scopeRoots.descendingIterator(); + while (it.hasNext()) { + scope = scopeCreator.createScope(it.next(), scope); + scopes.push(scope); + } + scopeRoots.clear(); + + return scope; + } + + /** Gets the control flow graph for the current JS scope. */ + public ControlFlowGraph getControlFlowGraph() { + if (cfgs.peek() == null) { + ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); + cfa.process(null, getScopeRoot()); + cfgs.pop(); + cfgs.push(cfa.getCfg()); + } + return cfgs.peek(); + } + + /** Returns the current scope's root. */ + public Node getScopeRoot() { + if (scopeRoots.isEmpty()) { + return scopes.peek().getRootNode(); + } else { + return scopeRoots.peek(); + } + } + + /** + * Determines whether the traversal is currently in the global scope. + */ + boolean inGlobalScope() { + return getScopeDepth() <= 1; + } + + int getScopeDepth() { + return scopes.size() + scopeRoots.size(); + } + + public boolean hasScope() { + return !(scopes.isEmpty() && scopeRoots.isEmpty()); + } + + /** Reports a diagnostic (error or warning) */ + public void report(Node n, DiagnosticType diagnosticType, + String... arguments) { + JSError error = JSError.make(getBestSourceFileName(n), + n, diagnosticType, arguments); + compiler.report(error); + } + + private static String getSourceName(Node n) { + String name = n.getSourceFileName(); + return name == null ? "" : name; + } + + InputId getInputId() { + return inputId; + } + + /** + * Creates a JSError during NodeTraversal. + * + * @param n Determines the line and char position within the source file name + * @param type The DiagnosticType + * @param arguments Arguments to be incorporated into the message + */ + public JSError makeError(Node n, CheckLevel level, DiagnosticType type, + String... arguments) { + return JSError.make(getBestSourceFileName(n), n, level, type, arguments); + } + + /** + * Creates a JSError during NodeTraversal. + * + * @param n Determines the line and char position within the source file name + * @param type The DiagnosticType + * @param arguments Arguments to be incorporated into the message + */ + public JSError makeError(Node n, DiagnosticType type, String... arguments) { + return JSError.make(getBestSourceFileName(n), n, type, arguments); + } + + private String getBestSourceFileName(Node n) { + return n == null ? sourceName : n.getSourceFileName(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NodeUtil.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NodeUtil.java new file mode 100644 index 0000000..e36d1fe --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/NodeUtil.java @@ -0,0 +1,3279 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.TokenStream; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.StaticSourceFile; +import com.google.javascript.rhino.jstype.TernaryValue; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +/** + * NodeUtil contains generally useful AST utilities. + * + */ +public final class NodeUtil { + + static final long MAX_POSITIVE_INTEGER_NUMBER = (long) Math.pow(2, 53); + + static final String JSC_PROPERTY_NAME_FN = "JSCompiler_renameProperty"; + + static final char LARGEST_BASIC_LATIN = 0x7f; + + /** the set of builtin constructors that don't have side effects. */ + private static final Set CONSTRUCTORS_WITHOUT_SIDE_EFFECTS = + new HashSet(Arrays.asList( + "Array", + "Date", + "Error", + "Object", + "RegExp", + "XMLHttpRequest")); + + // Utility class; do not instantiate. + private NodeUtil() {} + + /** + * Gets the boolean value of a node that represents a expression. This method + * effectively emulates the Boolean() JavaScript cast function. + * Note: unlike getBooleanValue this function does not return UNKNOWN + * for expressions with side-effects. + */ + static TernaryValue getImpureBooleanValue(Node n) { + switch (n.getType()) { + case Token.ASSIGN: + case Token.COMMA: + // For ASSIGN and COMMA the value is the value of the RHS. + return getImpureBooleanValue(n.getLastChild()); + case Token.NOT: + TernaryValue value = getImpureBooleanValue(n.getLastChild()); + return value.not(); + case Token.AND: { + TernaryValue lhs = getImpureBooleanValue(n.getFirstChild()); + TernaryValue rhs = getImpureBooleanValue(n.getLastChild()); + return lhs.and(rhs); + } + case Token.OR: { + TernaryValue lhs = getImpureBooleanValue(n.getFirstChild()); + TernaryValue rhs = getImpureBooleanValue(n.getLastChild()); + return lhs.or(rhs); + } + case Token.HOOK: { + TernaryValue trueValue = getImpureBooleanValue( + n.getFirstChild().getNext()); + TernaryValue falseValue = getImpureBooleanValue(n.getLastChild()); + if (trueValue.equals(falseValue)) { + return trueValue; + } else { + return TernaryValue.UNKNOWN; + } + } + case Token.ARRAYLIT: + case Token.OBJECTLIT: + // ignoring side-effects + return TernaryValue.TRUE; + + case Token.VOID: + return TernaryValue.FALSE; + + default: + return getPureBooleanValue(n); + } + } + + /** + * Gets the boolean value of a node that represents a literal. This method + * effectively emulates the Boolean() JavaScript cast function + * except it return UNKNOWN for known values with side-effects, use + * getImpureBooleanValue if you don't care about side-effects. + */ + static TernaryValue getPureBooleanValue(Node n) { + switch (n.getType()) { + case Token.STRING: + return TernaryValue.forBoolean(n.getString().length() > 0); + + case Token.NUMBER: + return TernaryValue.forBoolean(n.getDouble() != 0); + + case Token.NOT: + return getPureBooleanValue(n.getLastChild()).not(); + + case Token.NULL: + case Token.FALSE: + return TernaryValue.FALSE; + + case Token.VOID: + if (!mayHaveSideEffects(n.getFirstChild())) { + return TernaryValue.FALSE; + } + break; + + case Token.NAME: + String name = n.getString(); + if ("undefined".equals(name) + || "NaN".equals(name)) { + // We assume here that programs don't change the value of the keyword + // undefined to something other than the value undefined. + return TernaryValue.FALSE; + } else if ("Infinity".equals(name)) { + return TernaryValue.TRUE; + } + break; + + case Token.TRUE: + case Token.REGEXP: + return TernaryValue.TRUE; + + case Token.ARRAYLIT: + case Token.OBJECTLIT: + if (!mayHaveSideEffects(n)) { + return TernaryValue.TRUE; + } + break; + } + + return TernaryValue.UNKNOWN; + } + + /** + * Gets the value of a node as a String, or null if it cannot be converted. + * When it returns a non-null String, this method effectively emulates the + * String() JavaScript cast function. + */ + static String getStringValue(Node n) { + // TODO(user): regex literals as well. + switch (n.getType()) { + case Token.STRING: + case Token.STRING_KEY: + return n.getString(); + + case Token.NAME: + String name = n.getString(); + if ("undefined".equals(name) + || "Infinity".equals(name) + || "NaN".equals(name)) { + return name; + } + break; + + case Token.NUMBER: + return getStringValue(n.getDouble()); + + case Token.FALSE: + return "false"; + + case Token.TRUE: + return "true"; + + case Token.NULL: + return "null"; + + case Token.VOID: + return "undefined"; + + case Token.NOT: + TernaryValue child = getPureBooleanValue(n.getFirstChild()); + if (child != TernaryValue.UNKNOWN) { + return child.toBoolean(true) ? "false" : "true"; // reversed. + } + break; + + case Token.ARRAYLIT: + return arrayToString(n); + + case Token.OBJECTLIT: + return "[object Object]"; + } + return null; + } + + static String getStringValue(double value) { + long longValue = (long) value; + + // Return "1" instead of "1.0" + if (longValue == value) { + return Long.toString(longValue); + } else { + return Double.toString(value); + } + } + + /** + * When converting arrays to string using Array.prototype.toString or + * Array.prototype.join, the rules for conversion to String are different + * than converting each element individually. Specifically, "null" and + * "undefined" are converted to an empty string. + * @param n A node that is a member of an Array. + * @return The string representation. + */ + static String getArrayElementStringValue(Node n) { + return (NodeUtil.isNullOrUndefined(n) || n.isEmpty()) + ? "" : getStringValue(n); + } + + static String arrayToString(Node literal) { + Node first = literal.getFirstChild(); + StringBuilder result = new StringBuilder(); + int nextSlot = 0; + int nextSkipSlot = 0; + for (Node n = first; n != null; n = n.getNext()) { + String childValue = getArrayElementStringValue(n); + if (childValue == null) { + return null; + } + if (n != first) { + result.append(','); + } + result.append(childValue); + + nextSlot++; + } + return result.toString(); + } + + /** + * Gets the value of a node as a Number, or null if it cannot be converted. + * When it returns a non-null Double, this method effectively emulates the + * Number() JavaScript cast function. + */ + static Double getNumberValue(Node n) { + switch (n.getType()) { + case Token.TRUE: + return 1.0; + + case Token.FALSE: + case Token.NULL: + return 0.0; + + case Token.NUMBER: + return n.getDouble(); + + case Token.VOID: + if (mayHaveSideEffects(n.getFirstChild())) { + return null; + } else { + return Double.NaN; + } + + case Token.NAME: + // Check for known constants + String name = n.getString(); + if (name.equals("undefined")) { + return Double.NaN; + } + if (name.equals("NaN")) { + return Double.NaN; + } + if (name.equals("Infinity")) { + return Double.POSITIVE_INFINITY; + } + return null; + + case Token.NEG: + if (n.getChildCount() == 1 && n.getFirstChild().isName() + && n.getFirstChild().getString().equals("Infinity")) { + return Double.NEGATIVE_INFINITY; + } + return null; + + case Token.NOT: + TernaryValue child = getPureBooleanValue(n.getFirstChild()); + if (child != TernaryValue.UNKNOWN) { + return child.toBoolean(true) ? 0.0 : 1.0; // reversed. + } + break; + + case Token.STRING: + return getStringNumberValue(n.getString()); + + case Token.ARRAYLIT: + case Token.OBJECTLIT: + String value = getStringValue(n); + return value != null ? getStringNumberValue(value) : null; + } + + return null; + } + + static Double getStringNumberValue(String rawJsString) { + if (rawJsString.contains("\u000b")) { + // vertical tab is not always whitespace + return null; + } + + String s = trimJsWhiteSpace(rawJsString); + // return ScriptRuntime.toNumber(s); + if (s.length() == 0) { + return 0.0; + } + + if (s.length() > 2 + && s.charAt(0) == '0' + && (s.charAt(1) == 'x' || s.charAt(1) == 'X')) { + // Attempt to convert hex numbers. + try { + return Double.valueOf(Integer.parseInt(s.substring(2), 16)); + } catch (NumberFormatException e) { + return Double.NaN; + } + } + + if (s.length() > 3 + && (s.charAt(0) == '-' || s.charAt(0) == '+') + && s.charAt(1) == '0' + && (s.charAt(2) == 'x' || s.charAt(2) == 'X')) { + // hex numbers with explicit signs vary between browsers. + return null; + } + + // Firefox and IE treat the "Infinity" differently. Firefox is case + // insensitive, but IE treats "infinity" as NaN. So leave it alone. + if (s.equals("infinity") + || s.equals("-infinity") + || s.equals("+infinity")) { + return null; + } + + try { + return Double.parseDouble(s); + } catch (NumberFormatException e) { + return Double.NaN; + } + } + + static String trimJsWhiteSpace(String s) { + int start = 0; + int end = s.length(); + while (end > 0 + && isStrWhiteSpaceChar(s.charAt(end - 1)) == TernaryValue.TRUE) { + end--; + } + while (start < end + && isStrWhiteSpaceChar(s.charAt(start)) == TernaryValue.TRUE) { + start++; + } + return s.substring(start, end); + } + + /** + * Copied from Rhino's ScriptRuntime + */ + public static TernaryValue isStrWhiteSpaceChar(int c) { + switch (c) { + case '\u000B': // + return TernaryValue.UNKNOWN; // IE says "no", ECMAScript says "yes" + case ' ': // + case '\n': // + case '\r': // + case '\t': // + case '\u00A0': // + case '\u000C': // + case '\u2028': // + case '\u2029': // + case '\uFEFF': // + return TernaryValue.TRUE; + default: + return (Character.getType(c) == Character.SPACE_SEPARATOR) + ? TernaryValue.TRUE : TernaryValue.FALSE; + } + } + + /** + * Gets the function's name. This method recognizes five forms: + *
        + *
      • {@code function name() ...}
      • + *
      • {@code var name = function() ...}
      • + *
      • {@code qualified.name = function() ...}
      • + *
      • {@code var name2 = function name1() ...}
      • + *
      • {@code qualified.name2 = function name1() ...}
      • + *
      + * In two last cases with named function expressions, the second name is + * returned (the variable of qualified name). + * + * @param n a node whose type is {@link Token#FUNCTION} + * @return the function's name, or {@code null} if it has no name + */ + static String getFunctionName(Node n) { + Preconditions.checkState(n.isFunction()); + Node parent = n.getParent(); + switch (parent.getType()) { + case Token.NAME: + // var name = function() ... + // var name2 = function name1() ... + return parent.getQualifiedName(); + + case Token.ASSIGN: + // qualified.name = function() ... + // qualified.name2 = function name1() ... + return parent.getFirstChild().getQualifiedName(); + + default: + // function name() ... + String name = n.getFirstChild().getQualifiedName(); + return name; + } + } + + /** + * Gets the function's name. This method recognizes the forms: + *
        + *
      • {@code {'name': function() ...}}
      • + *
      • {@code {name: function() ...}}
      • + *
      • {@code function name() ...}
      • + *
      • {@code var name = function() ...}
      • + *
      • {@code qualified.name = function() ...}
      • + *
      • {@code var name2 = function name1() ...}
      • + *
      • {@code qualified.name2 = function name1() ...}
      • + *
      + * + * @param n a node whose type is {@link Token#FUNCTION} + * @return the function's name, or {@code null} if it has no name + */ + public static String getNearestFunctionName(Node n) { + if (!n.isFunction()) { + return null; + } + + String name = getFunctionName(n); + if (name != null) { + return name; + } + + // Check for the form { 'x' : function() { } } + Node parent = n.getParent(); + switch (parent.getType()) { + case Token.SETTER_DEF: + case Token.GETTER_DEF: + case Token.STRING_KEY: + // Return the name of the literal's key. + return parent.getString(); + case Token.NUMBER: + return getStringValue(parent); + } + + return null; + } + + + /** + * Returns true if this is an immutable value. + */ + static boolean isImmutableValue(Node n) { + switch (n.getType()) { + case Token.STRING: + case Token.NUMBER: + case Token.NULL: + case Token.TRUE: + case Token.FALSE: + return true; + case Token.CAST: + case Token.NOT: + return isImmutableValue(n.getFirstChild()); + case Token.VOID: + case Token.NEG: + return isImmutableValue(n.getFirstChild()); + case Token.NAME: + String name = n.getString(); + // We assume here that programs don't change the value of the keyword + // undefined to something other than the value undefined. + return "undefined".equals(name) + || "Infinity".equals(name) + || "NaN".equals(name); + } + + return false; + } + + /** + * Returns true if the operator on this node is symmetric + */ + static boolean isSymmetricOperation(Node n) { + switch (n.getType()) { + case Token.EQ: // equal + case Token.NE: // not equal + case Token.SHEQ: // exactly equal + case Token.SHNE: // exactly not equal + case Token.MUL: // multiply, unlike add it only works on numbers + // or results NaN if any of the operators is not a number + return true; + } + return false; + } + + /** + * Returns true if the operator on this node is relational. + * the returned set does not include the equalities. + */ + static boolean isRelationalOperation(Node n) { + switch (n.getType()) { + case Token.GT: // equal + case Token.GE: // not equal + case Token.LT: // exactly equal + case Token.LE: // exactly not equal + return true; + } + return false; + } + + /** + * Returns the inverse of an operator if it is invertible. + * ex. '>' ==> '<' + */ + static int getInverseOperator(int type) { + switch (type) { + case Token.GT: + return Token.LT; + case Token.LT: + return Token.GT; + case Token.GE: + return Token.LE; + case Token.LE: + return Token.GE; + } + return Token.ERROR; + } + + /** + * Returns true if this is a literal value. We define a literal value + * as any node that evaluates to the same thing regardless of when or + * where it is evaluated. So /xyz/ and [3, 5] are literals, but + * the name a is not. + * + * Function literals do not meet this definition, because they + * lexically capture variables. For example, if you have + * + * function() { return a; } + * + * If it is evaluated in a different scope, then it + * captures a different variable. Even if the function did not read + * any captured variables directly, it would still fail this definition, + * because it affects the lifecycle of variables in the enclosing scope. + * + * However, a function literal with respect to a particular scope is + * a literal. + * + * @param includeFunctions If true, all function expressions will be + * treated as literals. + */ + static boolean isLiteralValue(Node n, boolean includeFunctions) { + switch (n.getType()) { + case Token.CAST: + return isLiteralValue(n.getFirstChild(), includeFunctions); + + case Token.ARRAYLIT: + for (Node child = n.getFirstChild(); child != null; + child = child.getNext()) { + if ((!child.isEmpty()) && !isLiteralValue(child, includeFunctions)) { + return false; + } + } + return true; + + case Token.REGEXP: + // Return true only if all children are const. + for (Node child = n.getFirstChild(); child != null; + child = child.getNext()) { + if (!isLiteralValue(child, includeFunctions)) { + return false; + } + } + return true; + + case Token.OBJECTLIT: + // Return true only if all values are const. + for (Node child = n.getFirstChild(); child != null; + child = child.getNext()) { + if (!isLiteralValue(child.getFirstChild(), includeFunctions)) { + return false; + } + } + return true; + + case Token.FUNCTION: + return includeFunctions && !NodeUtil.isFunctionDeclaration(n); + + default: + return isImmutableValue(n); + } + } + + /** + * Determines whether the given value may be assigned to a define. + * + * @param val The value being assigned. + * @param defines The list of names of existing defines. + */ + static boolean isValidDefineValue(Node val, Set defines) { + switch (val.getType()) { + case Token.STRING: + case Token.NUMBER: + case Token.TRUE: + case Token.FALSE: + return true; + + // Binary operators are only valid if both children are valid. + case Token.ADD: + case Token.BITAND: + case Token.BITNOT: + case Token.BITOR: + case Token.BITXOR: + case Token.DIV: + case Token.EQ: + case Token.GE: + case Token.GT: + case Token.LE: + case Token.LSH: + case Token.LT: + case Token.MOD: + case Token.MUL: + case Token.NE: + case Token.RSH: + case Token.SHEQ: + case Token.SHNE: + case Token.SUB: + case Token.URSH: + return isValidDefineValue(val.getFirstChild(), defines) + && isValidDefineValue(val.getLastChild(), defines); + + // Unary operators are valid if the child is valid. + case Token.NOT: + case Token.NEG: + case Token.POS: + return isValidDefineValue(val.getFirstChild(), defines); + + // Names are valid if and only if they are defines themselves. + case Token.NAME: + case Token.GETPROP: + if (val.isQualifiedName()) { + return defines.contains(val.getQualifiedName()); + } + } + return false; + } + + /** + * Returns whether this a BLOCK node with no children. + * + * @param block The node. + */ + static boolean isEmptyBlock(Node block) { + if (!block.isBlock()) { + return false; + } + + for (Node n = block.getFirstChild(); n != null; n = n.getNext()) { + if (!n.isEmpty()) { + return false; + } + } + return true; + } + + static boolean isSimpleOperator(Node n) { + return isSimpleOperatorType(n.getType()); + } + + /** + * A "simple" operator is one whose children are expressions, + * has no direct side-effects (unlike '+='), and has no + * conditional aspects (unlike '||'). + */ + static boolean isSimpleOperatorType(int type) { + switch (type) { + case Token.ADD: + case Token.BITAND: + case Token.BITNOT: + case Token.BITOR: + case Token.BITXOR: + case Token.COMMA: + case Token.DIV: + case Token.EQ: + case Token.GE: + case Token.GETELEM: + case Token.GETPROP: + case Token.GT: + case Token.INSTANCEOF: + case Token.LE: + case Token.LSH: + case Token.LT: + case Token.MOD: + case Token.MUL: + case Token.NE: + case Token.NOT: + case Token.RSH: + case Token.SHEQ: + case Token.SHNE: + case Token.SUB: + case Token.TYPEOF: + case Token.VOID: + case Token.POS: + case Token.NEG: + case Token.URSH: + return true; + + default: + return false; + } + } + + /** + * Creates an EXPR_RESULT. + * + * @param child The expression itself. + * @return Newly created EXPR node with the child as subexpression. + */ + static Node newExpr(Node child) { + return IR.exprResult(child).srcref(child); + } + + /** + * Returns true if the node may create new mutable state, or change existing + * state. + * + * @see XKCD Cartoon + */ + static boolean mayEffectMutableState(Node n) { + return mayEffectMutableState(n, null); + } + + static boolean mayEffectMutableState(Node n, AbstractCompiler compiler) { + return checkForStateChangeHelper(n, true, compiler); + } + + /** + * Returns true if the node which may have side effects when executed. + */ + static boolean mayHaveSideEffects(Node n) { + return mayHaveSideEffects(n, null); + } + + static boolean mayHaveSideEffects(Node n, AbstractCompiler compiler) { + return checkForStateChangeHelper(n, false, compiler); + } + + /** + * Returns true if some node in n's subtree changes application state. + * If {@code checkForNewObjects} is true, we assume that newly created + * mutable objects (like object literals) change state. Otherwise, we assume + * that they have no side effects. + */ + private static boolean checkForStateChangeHelper( + Node n, boolean checkForNewObjects, AbstractCompiler compiler) { + // Rather than id which ops may have side effects, id the ones + // that we know to be safe + switch (n.getType()) { + // other side-effect free statements and expressions + case Token.CAST: + case Token.AND: + case Token.BLOCK: + case Token.EXPR_RESULT: + case Token.HOOK: + case Token.IF: + case Token.IN: + case Token.PARAM_LIST: + case Token.NUMBER: + case Token.OR: + case Token.THIS: + case Token.TRUE: + case Token.FALSE: + case Token.NULL: + case Token.STRING: + case Token.STRING_KEY: + case Token.SWITCH: + case Token.TRY: + case Token.EMPTY: + break; + + // Throws are by definition side effects + case Token.THROW: + return true; + + case Token.OBJECTLIT: + if (checkForNewObjects) { + return true; + } + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + if (checkForStateChangeHelper( + c.getFirstChild(), checkForNewObjects, compiler)) { + return true; + } + } + return false; + + case Token.ARRAYLIT: + case Token.REGEXP: + if (checkForNewObjects) { + return true; + } + break; + + case Token.VAR: // empty var statement (no declaration) + case Token.NAME: // variable by itself + if (n.getFirstChild() != null) { + return true; + } + break; + + case Token.FUNCTION: + // Function expressions don't have side-effects, but function + // declarations change the namespace. Either way, we don't need to + // check the children, since they aren't executed at declaration time. + return checkForNewObjects || !isFunctionExpression(n); + + case Token.NEW: + if (checkForNewObjects) { + return true; + } + + if (!constructorCallHasSideEffects(n)) { + // loop below will see if the constructor parameters have + // side-effects + break; + } + return true; + + case Token.CALL: + // calls to functions that have no side effects have the no + // side effect property set. + if (!functionCallHasSideEffects(n, compiler)) { + // loop below will see if the function parameters have + // side-effects + break; + } + return true; + + default: + if (isSimpleOperator(n)) { + break; + } + + if (isAssignmentOp(n)) { + Node assignTarget = n.getFirstChild(); + if (assignTarget.isName()) { + return true; + } + + // Assignments will have side effects if + // a) The RHS has side effects, or + // b) The LHS has side effects, or + // c) A name on the LHS will exist beyond the life of this statement. + if (checkForStateChangeHelper( + n.getFirstChild(), checkForNewObjects, compiler) || + checkForStateChangeHelper( + n.getLastChild(), checkForNewObjects, compiler)) { + return true; + } + + if (isGet(assignTarget)) { + // If the object being assigned to is a local object, don't + // consider this a side-effect as it can't be referenced + // elsewhere. Don't do this recursively as the property might + // be an alias of another object, unlike a literal below. + Node current = assignTarget.getFirstChild(); + if (evaluatesToLocalValue(current)) { + return false; + } + + // A literal value as defined by "isLiteralValue" is guaranteed + // not to be an alias, or any components which are aliases of + // other objects. + // If the root object is a literal don't consider this a + // side-effect. + while (isGet(current)) { + current = current.getFirstChild(); + } + + return !isLiteralValue(current, true); + } else { + // TODO(johnlenz): remove this code and make this an exception. This + // is here only for legacy reasons, the AST is not valid but + // preserve existing behavior. + return !isLiteralValue(assignTarget, true); + } + } + + return true; + } + + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + if (checkForStateChangeHelper(c, checkForNewObjects, compiler)) { + return true; + } + } + + return false; + } + + /** + * Do calls to this constructor have side effects? + * + * @param callNode - constructor call node + */ + static boolean constructorCallHasSideEffects(Node callNode) { + return constructorCallHasSideEffects(callNode, null); + } + + static boolean constructorCallHasSideEffects( + Node callNode, AbstractCompiler compiler) { + if (!callNode.isNew()) { + throw new IllegalStateException( + "Expected NEW node, got " + Token.name(callNode.getType())); + } + + if (callNode.isNoSideEffectsCall()) { + return false; + } + + Node nameNode = callNode.getFirstChild(); + if (nameNode.isName() && + CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(nameNode.getString())) { + return false; + } + + return true; + } + + // A list of built-in object creation or primitive type cast functions that + // can also be called as constructors but lack side-effects. + // TODO(johnlenz): consider adding an extern annotation for this. + private static final Set BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS = + ImmutableSet.of( + "Object", "Array", "String", "Number", "Boolean", "RegExp", "Error"); + private static final Set OBJECT_METHODS_WITHOUT_SIDEEFFECTS = + ImmutableSet.of("toString", "valueOf"); + private static final Set REGEXP_METHODS = + ImmutableSet.of("test", "exec"); + private static final Set STRING_REGEXP_METHODS = + ImmutableSet.of("match", "replace", "search", "split"); + + /** + * Returns true if calls to this function have side effects. + * + * @param callNode - function call node + */ + static boolean functionCallHasSideEffects(Node callNode) { + return functionCallHasSideEffects(callNode, null); + } + + /** + * Returns true if calls to this function have side effects. + * + * @param callNode The call node to inspected. + * @param compiler A compiler object to provide program state changing + * context information. Can be null. + */ + static boolean functionCallHasSideEffects( + Node callNode, @Nullable AbstractCompiler compiler) { + if (!callNode.isCall()) { + throw new IllegalStateException( + "Expected CALL node, got " + Token.name(callNode.getType())); + } + + if (callNode.isNoSideEffectsCall()) { + return false; + } + + Node nameNode = callNode.getFirstChild(); + + // Built-in functions with no side effects. + if (nameNode.isName()) { + String name = nameNode.getString(); + if (BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS.contains(name)) { + return false; + } + } else if (nameNode.isGetProp()) { + if (callNode.hasOneChild() + && OBJECT_METHODS_WITHOUT_SIDEEFFECTS.contains( + nameNode.getLastChild().getString())) { + return false; + } + + if (callNode.isOnlyModifiesThisCall() + && evaluatesToLocalValue(nameNode.getFirstChild())) { + return false; + } + + // Math.floor has no side-effects. + // TODO(nicksantos): This is a terrible terrible hack, until + // I create a definitionProvider that understands namespacing. + if (nameNode.getFirstChild().isName()) { + if ("Math.floor".equals(nameNode.getQualifiedName())) { + return false; + } + } + + if (compiler != null && !compiler.hasRegExpGlobalReferences()) { + if (nameNode.getFirstChild().isRegExp() + && REGEXP_METHODS.contains(nameNode.getLastChild().getString())) { + return false; + } else if (nameNode.getFirstChild().isString() + && STRING_REGEXP_METHODS.contains( + nameNode.getLastChild().getString())) { + Node param = nameNode.getNext(); + if (param != null && + (param.isString() || param.isRegExp())) { + return false; + } + } + } + } + + return true; + } + + /** + * @return Whether the call has a local result. + */ + static boolean callHasLocalResult(Node n) { + Preconditions.checkState(n.isCall()); + return (n.getSideEffectFlags() & Node.FLAG_LOCAL_RESULTS) > 0; + } + + /** + * @return Whether the new has a local result. + */ + static boolean newHasLocalResult(Node n) { + Preconditions.checkState(n.isNew()); + return n.isOnlyModifiesThisCall(); + } + + /** + * Returns true if the current node's type implies side effects. + * + * This is a non-recursive version of the may have side effects + * check; used to check wherever the current node's type is one of + * the reason's why a subtree has side effects. + */ + static boolean nodeTypeMayHaveSideEffects(Node n) { + return nodeTypeMayHaveSideEffects(n, null); + } + + static boolean nodeTypeMayHaveSideEffects(Node n, AbstractCompiler compiler) { + if (isAssignmentOp(n)) { + return true; + } + + switch(n.getType()) { + case Token.DELPROP: + case Token.DEC: + case Token.INC: + case Token.THROW: + return true; + case Token.CALL: + return NodeUtil.functionCallHasSideEffects(n, compiler); + case Token.NEW: + return NodeUtil.constructorCallHasSideEffects(n, compiler); + case Token.NAME: + // A variable definition. + return n.hasChildren(); + default: + return false; + } + } + + /** + * @return Whether the tree can be affected by side-effects or + * has side-effects. + */ + static boolean canBeSideEffected(Node n) { + Set emptySet = Collections.emptySet(); + return canBeSideEffected(n, emptySet); + } + + /** + * @param knownConstants A set of names known to be constant value at + * node 'n' (such as locals that are last written before n can execute). + * @return Whether the tree can be affected by side-effects or + * has side-effects. + */ + static boolean canBeSideEffected(Node n, Set knownConstants) { + switch (n.getType()) { + case Token.CALL: + case Token.NEW: + // Function calls or constructor can reference changed values. + // TODO(johnlenz): Add some mechanism for determining that functions + // are unaffected by side effects. + return true; + case Token.NAME: + // Non-constant names values may have been changed. + return !isConstantName(n) + && !knownConstants.contains(n.getString()); + + // Properties on constant NAMEs can still be side-effected. + case Token.GETPROP: + case Token.GETELEM: + return true; + + case Token.FUNCTION: + // Function expression are not changed by side-effects, + // and function declarations are not part of expressions. + Preconditions.checkState(isFunctionExpression(n)); + return false; + } + + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + if (canBeSideEffected(c, knownConstants)) { + return true; + } + } + + return false; + } + + /* + * 0 comma , + * 1 assignment = += -= *= /= %= <<= >>= >>>= &= ^= |= + * 2 conditional ?: + * 3 logical-or || + * 4 logical-and && + * 5 bitwise-or | + * 6 bitwise-xor ^ + * 7 bitwise-and & + * 8 equality == != + * 9 relational < <= > >= + * 10 bitwise shift << >> >>> + * 11 addition/subtraction + - + * 12 multiply/divide * / % + * 13 negation/increment ! ~ - ++ -- + * 14 call, member () [] . + */ + static int precedence(int type) { + switch (type) { + case Token.COMMA: return 0; + case Token.ASSIGN_BITOR: + case Token.ASSIGN_BITXOR: + case Token.ASSIGN_BITAND: + case Token.ASSIGN_LSH: + case Token.ASSIGN_RSH: + case Token.ASSIGN_URSH: + case Token.ASSIGN_ADD: + case Token.ASSIGN_SUB: + case Token.ASSIGN_MUL: + case Token.ASSIGN_DIV: + case Token.ASSIGN_MOD: + case Token.ASSIGN: return 1; + case Token.HOOK: return 2; // ?: operator + case Token.OR: return 3; + case Token.AND: return 4; + case Token.BITOR: return 5; + case Token.BITXOR: return 6; + case Token.BITAND: return 7; + case Token.EQ: + case Token.NE: + case Token.SHEQ: + case Token.SHNE: return 8; + case Token.LT: + case Token.GT: + case Token.LE: + case Token.GE: + case Token.INSTANCEOF: + case Token.IN: return 9; + case Token.LSH: + case Token.RSH: + case Token.URSH: return 10; + case Token.SUB: + case Token.ADD: return 11; + case Token.MUL: + case Token.MOD: + case Token.DIV: return 12; + case Token.INC: + case Token.DEC: + case Token.NEW: + case Token.DELPROP: + case Token.TYPEOF: + case Token.VOID: + case Token.NOT: + case Token.BITNOT: + case Token.POS: + case Token.NEG: return 13; + + case Token.CALL: + case Token.GETELEM: + case Token.GETPROP: + // Data values + case Token.ARRAYLIT: + case Token.EMPTY: // TODO(johnlenz): remove this. + case Token.FALSE: + case Token.FUNCTION: + case Token.NAME: + case Token.NULL: + case Token.NUMBER: + case Token.OBJECTLIT: + case Token.REGEXP: + case Token.STRING: + case Token.STRING_KEY: + case Token.THIS: + case Token.TRUE: + return 15; + case Token.CAST: + return 16; + + default: throw new Error("Unknown precedence for " + + Token.name(type) + + " (type " + type + ")"); + } + } + + static boolean isUndefined(Node n) { + switch (n.getType()) { + case Token.VOID: + return true; + case Token.NAME: + return n.getString().equals("undefined"); + } + return false; + } + + static boolean isNullOrUndefined(Node n) { + return n.isNull() || isUndefined(n); + } + + static final Predicate IMMUTABLE_PREDICATE = new Predicate() { + @Override + public boolean apply(Node n) { + return isImmutableValue(n); + } + }; + + static boolean isImmutableResult(Node n) { + return allResultsMatch(n, IMMUTABLE_PREDICATE); + } + + /** + * Apply the supplied predicate against + * all possible result Nodes of the expression. + */ + static boolean allResultsMatch(Node n, Predicate p) { + switch (n.getType()) { + case Token.CAST: + return allResultsMatch(n.getFirstChild(), p); + case Token.ASSIGN: + case Token.COMMA: + return allResultsMatch(n.getLastChild(), p); + case Token.AND: + case Token.OR: + return allResultsMatch(n.getFirstChild(), p) + && allResultsMatch(n.getLastChild(), p); + case Token.HOOK: + return allResultsMatch(n.getFirstChild().getNext(), p) + && allResultsMatch(n.getLastChild(), p); + default: + return p.apply(n); + } + } + + /** + * Apply the supplied predicate against + * all possible result Nodes of the expression. + */ + static boolean anyResultsMatch(Node n, Predicate p) { + switch (n.getType()) { + case Token.CAST: + return anyResultsMatch(n.getFirstChild(), p); + case Token.ASSIGN: + case Token.COMMA: + return anyResultsMatch(n.getLastChild(), p); + case Token.AND: + case Token.OR: + return anyResultsMatch(n.getFirstChild(), p) + || anyResultsMatch(n.getLastChild(), p); + case Token.HOOK: + return anyResultsMatch(n.getFirstChild().getNext(), p) + || anyResultsMatch(n.getLastChild(), p); + default: + return p.apply(n); + } + } + + static class NumbericResultPredicate implements Predicate { + @Override + public boolean apply(Node n) { + return isNumericResultHelper(n); + } + } + + static final NumbericResultPredicate NUMBERIC_RESULT_PREDICATE = + new NumbericResultPredicate(); + + /** + * Returns true if the result of node evaluation is always a number + */ + static boolean isNumericResult(Node n) { + return allResultsMatch(n, NUMBERIC_RESULT_PREDICATE); + } + + static boolean isNumericResultHelper(Node n) { + switch (n.getType()) { + case Token.ADD: + return !mayBeString(n.getFirstChild()) + && !mayBeString(n.getLastChild()); + case Token.BITNOT: + case Token.BITOR: + case Token.BITXOR: + case Token.BITAND: + case Token.LSH: + case Token.RSH: + case Token.URSH: + case Token.SUB: + case Token.MUL: + case Token.MOD: + case Token.DIV: + case Token.INC: + case Token.DEC: + case Token.POS: + case Token.NEG: + case Token.NUMBER: + return true; + case Token.NAME: + String name = n.getString(); + if (name.equals("NaN")) { + return true; + } + if (name.equals("Infinity")) { + return true; + } + return false; + default: + return false; + } + } + + static class BooleanResultPredicate implements Predicate { + @Override + public boolean apply(Node n) { + return isBooleanResultHelper(n); + } + } + + static final BooleanResultPredicate BOOLEAN_RESULT_PREDICATE = + new BooleanResultPredicate(); + + /** + * @return Whether the result of node evaluation is always a boolean + */ + static boolean isBooleanResult(Node n) { + return allResultsMatch(n, BOOLEAN_RESULT_PREDICATE); + } + + static boolean isBooleanResultHelper(Node n) { + switch (n.getType()) { + // Primitives + case Token.TRUE: + case Token.FALSE: + // Comparisons + case Token.EQ: + case Token.NE: + case Token.SHEQ: + case Token.SHNE: + case Token.LT: + case Token.GT: + case Token.LE: + case Token.GE: + // Queries + case Token.IN: + case Token.INSTANCEOF: + // Inversion + case Token.NOT: + // delete operator returns a boolean. + case Token.DELPROP: + return true; + default: + return false; + } + } + + + + static class MayBeStringResultPredicate implements Predicate { + @Override + public boolean apply(Node n) { + return mayBeStringHelper(n); + } + } + + static final MayBeStringResultPredicate MAY_BE_STRING_PREDICATE = + new MayBeStringResultPredicate(); + + /** + * @returns Whether the results is possibly a string. + */ + static boolean mayBeString(Node n) { + return mayBeString(n, true); + } + + static boolean mayBeString(Node n, boolean recurse) { + if (recurse) { + return anyResultsMatch(n, MAY_BE_STRING_PREDICATE); + } else { + return mayBeStringHelper(n); + } + } + + static boolean mayBeStringHelper(Node n) { + return !isNumericResult(n) && !isBooleanResult(n) + && !isUndefined(n) && !n.isNull(); + } + + /** + * Returns true if the operator is associative. + * e.g. (a * b) * c = a * (b * c) + * Note: "+" is not associative because it is also the concatenation + * for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2 + */ + static boolean isAssociative(int type) { + switch (type) { + case Token.MUL: + case Token.AND: + case Token.OR: + case Token.BITOR: + case Token.BITXOR: + case Token.BITAND: + return true; + default: + return false; + } + } + + /** + * Returns true if the operator is commutative. + * e.g. (a * b) * c = c * (b * a) + * Note 1: "+" is not commutative because it is also the concatenation + * for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2 + * Note 2: only operations on literals and pure functions are commutative. + */ + static boolean isCommutative(int type) { + switch (type) { + case Token.MUL: + case Token.BITOR: + case Token.BITXOR: + case Token.BITAND: + return true; + default: + return false; + } + } + + static boolean isAssignmentOp(Node n) { + switch (n.getType()){ + case Token.ASSIGN: + case Token.ASSIGN_BITOR: + case Token.ASSIGN_BITXOR: + case Token.ASSIGN_BITAND: + case Token.ASSIGN_LSH: + case Token.ASSIGN_RSH: + case Token.ASSIGN_URSH: + case Token.ASSIGN_ADD: + case Token.ASSIGN_SUB: + case Token.ASSIGN_MUL: + case Token.ASSIGN_DIV: + case Token.ASSIGN_MOD: + return true; + } + return false; + } + + static int getOpFromAssignmentOp(Node n) { + switch (n.getType()){ + case Token.ASSIGN_BITOR: + return Token.BITOR; + case Token.ASSIGN_BITXOR: + return Token.BITXOR; + case Token.ASSIGN_BITAND: + return Token.BITAND; + case Token.ASSIGN_LSH: + return Token.LSH; + case Token.ASSIGN_RSH: + return Token.RSH; + case Token.ASSIGN_URSH: + return Token.URSH; + case Token.ASSIGN_ADD: + return Token.ADD; + case Token.ASSIGN_SUB: + return Token.SUB; + case Token.ASSIGN_MUL: + return Token.MUL; + case Token.ASSIGN_DIV: + return Token.DIV; + case Token.ASSIGN_MOD: + return Token.MOD; + } + throw new IllegalArgumentException("Not an assignment op:" + n); + } + + /** + * Determines if the given node contains a function statement or function + * expression. + */ + static boolean containsFunction(Node n) { + return containsType(n, Token.FUNCTION); + } + + /** + * Returns true if the shallow scope contains references to 'this' keyword + */ + static boolean referencesThis(Node n) { + Node start = (n.isFunction()) ? n.getLastChild() : n; + return containsType(start, Token.THIS, MATCH_NOT_FUNCTION); + } + + /** + * Is this a GETPROP or GETELEM node? + */ + static boolean isGet(Node n) { + return n.isGetProp() || n.isGetElem(); + } + + /** + * Is this node the name of a variable being declared? + * + * @param n The node + * @return True if {@code n} is NAME and {@code parent} is VAR + */ + static boolean isVarDeclaration(Node n) { + // There is no need to verify that parent != null because a NAME node + // always has a parent in a valid parse tree. + return n.isName() && n.getParent().isVar(); + } + + /** + * For an assignment or variable declaration get the assigned value. + * @return The value node representing the new value. + */ + static Node getAssignedValue(Node n) { + Preconditions.checkState(n.isName()); + Node parent = n.getParent(); + if (parent.isVar()) { + return n.getFirstChild(); + } else if (parent.isAssign() && parent.getFirstChild() == n) { + return n.getNext(); + } else { + return null; + } + } + + /** + * Is this node an assignment expression statement? + * + * @param n The node + * @return True if {@code n} is EXPR_RESULT and {@code n}'s + * first child is ASSIGN + */ + static boolean isExprAssign(Node n) { + return n.isExprResult() + && n.getFirstChild().isAssign(); + } + + /** + * Is this node a call expression statement? + * + * @param n The node + * @return True if {@code n} is EXPR_RESULT and {@code n}'s + * first child is CALL + */ + static boolean isExprCall(Node n) { + return n.isExprResult() + && n.getFirstChild().isCall(); + } + + /** + * @return Whether the node represents a FOR-IN loop. + */ + static boolean isForIn(Node n) { + return n.isFor() + && n.getChildCount() == 3; + } + + /** + * Determines whether the given node is a FOR, DO, or WHILE node. + */ + static boolean isLoopStructure(Node n) { + switch (n.getType()) { + case Token.FOR: + case Token.DO: + case Token.WHILE: + return true; + default: + return false; + } + } + + /** + * @param n The node to inspect. + * @return If the node, is a FOR, WHILE, or DO, it returns the node for + * the code BLOCK, null otherwise. + */ + static Node getLoopCodeBlock(Node n) { + switch (n.getType()) { + case Token.FOR: + case Token.WHILE: + return n.getLastChild(); + case Token.DO: + return n.getFirstChild(); + default: + return null; + } + } + + /** + * @return Whether the specified node has a loop parent that + * is within the current scope. + */ + static boolean isWithinLoop(Node n) { + for (Node parent : n.getAncestors()) { + if (NodeUtil.isLoopStructure(parent)) { + return true; + } + + if (parent.isFunction()) { + break; + } + } + return false; + } + + /** + * Determines whether the given node is a FOR, DO, WHILE, WITH, or IF node. + */ + static boolean isControlStructure(Node n) { + switch (n.getType()) { + case Token.FOR: + case Token.DO: + case Token.WHILE: + case Token.WITH: + case Token.IF: + case Token.LABEL: + case Token.TRY: + case Token.CATCH: + case Token.SWITCH: + case Token.CASE: + case Token.DEFAULT_CASE: + return true; + default: + return false; + } + } + + /** + * Determines whether the given node is code node for FOR, DO, + * WHILE, WITH, or IF node. + */ + static boolean isControlStructureCodeBlock(Node parent, Node n) { + switch (parent.getType()) { + case Token.FOR: + case Token.WHILE: + case Token.LABEL: + case Token.WITH: + return parent.getLastChild() == n; + case Token.DO: + return parent.getFirstChild() == n; + case Token.IF: + return parent.getFirstChild() != n; + case Token.TRY: + return parent.getFirstChild() == n || parent.getLastChild() == n; + case Token.CATCH: + return parent.getLastChild() == n; + case Token.SWITCH: + case Token.CASE: + return parent.getFirstChild() != n; + case Token.DEFAULT_CASE: + return true; + default: + Preconditions.checkState(isControlStructure(parent)); + return false; + } + } + + /** + * Gets the condition of an ON_TRUE / ON_FALSE CFG edge. + * @param n a node with an outgoing conditional CFG edge + * @return the condition node or null if the condition is not obviously a node + */ + static Node getConditionExpression(Node n) { + switch (n.getType()) { + case Token.IF: + case Token.WHILE: + return n.getFirstChild(); + case Token.DO: + return n.getLastChild(); + case Token.FOR: + switch (n.getChildCount()) { + case 3: + return null; + case 4: + return n.getFirstChild().getNext(); + } + throw new IllegalArgumentException("malformed 'for' statement " + n); + case Token.CASE: + return null; + } + throw new IllegalArgumentException(n + " does not have a condition."); + } + + /** + * @return Whether the node is of a type that contain other statements. + */ + static boolean isStatementBlock(Node n) { + return n.isScript() || n.isBlock(); + } + + /** + * @return Whether the node is used as a statement. + */ + static boolean isStatement(Node n) { + return isStatementParent(n.getParent()); + } + + static boolean isStatementParent(Node parent) { + // It is not possible to determine definitely if a node is a statement + // or not if it is not part of the AST. A FUNCTION node can be + // either part of an expression or a statement. + Preconditions.checkState(parent != null); + switch (parent.getType()) { + case Token.SCRIPT: + case Token.BLOCK: + case Token.LABEL: + return true; + default: + return false; + } + } + + /** Whether the node is part of a switch statement. */ + static boolean isSwitchCase(Node n) { + return n.isCase() || n.isDefaultCase(); + } + + /** + * @return Whether the name is a reference to a variable, function or + * function parameter (not a label or a empty function expression name). + */ + static boolean isReferenceName(Node n) { + return n.isName() && !n.getString().isEmpty(); + } + + /** Whether the child node is the FINALLY block of a try. */ + static boolean isTryFinallyNode(Node parent, Node child) { + return parent.isTry() && parent.getChildCount() == 3 + && child == parent.getLastChild(); + } + + /** Whether the node is a CATCH container BLOCK. */ + static boolean isTryCatchNodeContainer(Node n) { + Node parent = n.getParent(); + return parent.isTry() + && parent.getFirstChild().getNext() == n; + } + + /** Safely remove children while maintaining a valid node structure. */ + static void removeChild(Node parent, Node node) { + if (isTryFinallyNode(parent, node)) { + if (NodeUtil.hasCatchHandler(getCatchBlock(parent))) { + // A finally can only be removed if there is a catch. + parent.removeChild(node); + } else { + // Otherwise, only its children can be removed. + node.detachChildren(); + } + } else if (node.isCatch()) { + // The CATCH can can only be removed if there is a finally clause. + Node tryNode = node.getParent().getParent(); + Preconditions.checkState(NodeUtil.hasFinally(tryNode)); + node.detachFromParent(); + } else if (isTryCatchNodeContainer(node)) { + // The container node itself can't be removed, but the contained CATCH + // can if there is a 'finally' clause + Node tryNode = node.getParent(); + Preconditions.checkState(NodeUtil.hasFinally(tryNode)); + node.detachChildren(); + } else if (node.isBlock()) { + // Simply empty the block. This maintains source location and + // "synthetic"-ness. + node.detachChildren(); + } else if (isStatementBlock(parent) + || isSwitchCase(node)) { + // A statement in a block can simply be removed. + parent.removeChild(node); + } else if (parent.isVar()) { + if (parent.hasMoreThanOneChild()) { + parent.removeChild(node); + } else { + // Remove the node from the parent, so it can be reused. + parent.removeChild(node); + // This would leave an empty VAR, remove the VAR itself. + removeChild(parent.getParent(), parent); + } + } else if (parent.isLabel() + && node == parent.getLastChild()) { + // Remove the node from the parent, so it can be reused. + parent.removeChild(node); + // A LABEL without children can not be referred to, remove it. + removeChild(parent.getParent(), parent); + } else if (parent.isFor() + && parent.getChildCount() == 4) { + // Only Token.FOR can have an Token.EMPTY other control structure + // need something for the condition. Others need to be replaced + // or the structure removed. + parent.replaceChild(node, IR.empty()); + } else { + throw new IllegalStateException("Invalid attempt to remove node: " + + node.toString() + " of " + parent.toString()); + } + } + + /** + * Add a finally block if one does not exist. + */ + static void maybeAddFinally(Node tryNode) { + Preconditions.checkState(tryNode.isTry()); + if (!NodeUtil.hasFinally(tryNode)) { + tryNode.addChildrenToBack(IR.block().srcref(tryNode)); + } + } + + /** + * Merge a block with its parent block. + * @return Whether the block was removed. + */ + static boolean tryMergeBlock(Node block) { + Preconditions.checkState(block.isBlock()); + Node parent = block.getParent(); + // Try to remove the block if its parent is a block/script or if its + // parent is label and it has exactly one child. + if (isStatementBlock(parent)) { + Node previous = block; + while (block.hasChildren()) { + Node child = block.removeFirstChild(); + parent.addChildAfter(child, previous); + previous = child; + } + parent.removeChild(block); + return true; + } else { + return false; + } + } + + /** + * @param node A node + * @return Whether the call is a NEW or CALL node. + */ + static boolean isCallOrNew(Node node) { + return node.isCall() || node.isNew(); + } + + /** + * Return a BLOCK node for the given FUNCTION node. + */ + static Node getFunctionBody(Node fn) { + Preconditions.checkArgument(fn.isFunction()); + return fn.getLastChild(); + } + + /** + * Is this node a function declaration? A function declaration is a function + * that has a name that is added to the current scope (i.e. a function that + * is not part of a expression; see {@link #isFunctionExpression}). + */ + static boolean isFunctionDeclaration(Node n) { + return n.isFunction() && isStatement(n); + } + + /** + * Is this node a hoisted function declaration? A function declaration in the + * scope root is hoisted to the top of the scope. + * See {@link #isFunctionDeclaration}). + */ + static boolean isHoistedFunctionDeclaration(Node n) { + return isFunctionDeclaration(n) + && (n.getParent().isScript() + || n.getParent().getParent().isFunction()); + } + + /** + * Is a FUNCTION node an function expression? An function expression is one + * that has either no name or a name that is not added to the current scope. + * + *

      Some examples of function expressions: + *

      +   * (function () {})
      +   * (function f() {})()
      +   * [ function f() {} ]
      +   * var f = function f() {};
      +   * for (function f() {};;) {}
      +   * 
      + * + *

      Some examples of functions that are not expressions: + *

      +   * function f() {}
      +   * if (x); else function f() {}
      +   * for (;;) { function f() {} }
      +   * 
      + * + * @param n A node + * @return Whether n is an function used within an expression. + */ + static boolean isFunctionExpression(Node n) { + return n.isFunction() && !isStatement(n); + } + + /** + * Returns whether this is a bleeding function (an anonymous named function + * that bleeds into the inner scope). + */ + static boolean isBleedingFunctionName(Node n) { + return n.isName() && !n.getString().isEmpty() && + isFunctionExpression(n.getParent()); + } + + /** + * Determines if a node is a function expression that has an empty body. + * + * @param node a node + * @return whether the given node is a function expression that is empty + */ + static boolean isEmptyFunctionExpression(Node node) { + return isFunctionExpression(node) && isEmptyBlock(node.getLastChild()); + } + + /** + * Determines if a function takes a variable number of arguments by + * looking for references to the "arguments" var_args object. + */ + static boolean isVarArgsFunction(Node function) { + // TODO(johnlenz): rename this function + Preconditions.checkArgument(function.isFunction()); + return isNameReferenced( + function.getLastChild(), + "arguments", + MATCH_NOT_FUNCTION); + } + + /** + * @return Whether node is a call to methodName. + * a.f(...) + * a['f'](...) + */ + static boolean isObjectCallMethod(Node callNode, String methodName) { + if (callNode.isCall()) { + Node functionIndentifyingExpression = callNode.getFirstChild(); + if (isGet(functionIndentifyingExpression)) { + Node last = functionIndentifyingExpression.getLastChild(); + if (last != null && last.isString()) { + String propName = last.getString(); + return (propName.equals(methodName)); + } + } + } + return false; + } + + + /** + * @return Whether the callNode represents an expression in the form of: + * x.call(...) + * x['call'](...) + */ + static boolean isFunctionObjectCall(Node callNode) { + return isObjectCallMethod(callNode, "call"); + } + + /** + * @return Whether the callNode represents an expression in the form of: + * x.apply(...) + * x['apply'](...) + */ + static boolean isFunctionObjectApply(Node callNode) { + return isObjectCallMethod(callNode, "apply"); + } + + /** + * Determines whether this node is strictly on the left hand side of an assign + * or var initialization. Notably, this does not include all L-values, only + * statements where the node is used only as an L-value. + * + * @param n The node + * @param parent Parent of the node + * @return True if n is the left hand of an assign + */ + static boolean isVarOrSimpleAssignLhs(Node n, Node parent) { + return (parent.isAssign() && parent.getFirstChild() == n) || + parent.isVar(); + } + + /** + * Determines whether this node is used as an L-value. Notice that sometimes + * names are used as both L-values and R-values. + * + * We treat "var x;" as a pseudo-L-value, which kind of makes sense if you + * treat it as "assignment to 'undefined' at the top of the scope". But if + * we're honest with ourselves, it doesn't make sense, and we only do this + * because it makes sense to treat this as syntactically similar to + * "var x = 0;". + * + * @param n The node + * @return True if n is an L-value. + */ + public static boolean isLValue(Node n) { + Preconditions.checkArgument(n.isName() || n.isGetProp() || + n.isGetElem()); + Node parent = n.getParent(); + if (parent == null) { + return false; + } + return (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n) + || (NodeUtil.isForIn(parent) && parent.getFirstChild() == n) + || parent.isVar() + || (parent.isFunction() && parent.getFirstChild() == n) + || parent.isDec() + || parent.isInc() + || parent.isParamList() + || parent.isCatch(); + } + + /** + * Determines whether a node represents an object literal key + * (e.g. key1 in {key1: value1, key2: value2}). + * + * @param node A node + * @param parent The node's parent + */ + static boolean isObjectLitKey(Node node, Node parent) { + switch (node.getType()) { + case Token.STRING_KEY: + case Token.GETTER_DEF: + case Token.SETTER_DEF: + return true; + } + return false; + } + + /** + * Get the name of an object literal key. + * + * @param key A node + */ + static String getObjectLitKeyName(Node key) { + switch (key.getType()) { + case Token.STRING_KEY: + case Token.GETTER_DEF: + case Token.SETTER_DEF: + return key.getString(); + } + throw new IllegalStateException("Unexpected node type: " + key); + } + + /** + * @param key A OBJECTLIT key node. + * @return The type expected when using the key. + */ + static JSType getObjectLitKeyTypeFromValueType(Node key, JSType valueType) { + if (valueType != null) { + switch (key.getType()) { + case Token.GETTER_DEF: + // GET must always return a function type. + if (valueType.isFunctionType()) { + FunctionType fntype = valueType.toMaybeFunctionType(); + valueType = fntype.getReturnType(); + } else { + return null; + } + break; + case Token.SETTER_DEF: + if (valueType.isFunctionType()) { + // SET must always return a function type. + FunctionType fntype = valueType.toMaybeFunctionType(); + Node param = fntype.getParametersNode().getFirstChild(); + // SET function must always have one parameter. + valueType = param.getJSType(); + } else { + return null; + } + break; + } + } + return valueType; + } + + /** + * Determines whether a node represents an object literal get or set key + * (e.g. key1 in {get key1() {}, set key2(a){}). + * + * @param node A node + */ + static boolean isGetOrSetKey(Node node) { + switch (node.getType()) { + case Token.GETTER_DEF: + case Token.SETTER_DEF: + return true; + } + return false; + } + + /** + * Converts an operator's token value (see {@link Token}) to a string + * representation. + * + * @param operator the operator's token value to convert + * @return the string representation or {@code null} if the token value is + * not an operator + */ + static String opToStr(int operator) { + switch (operator) { + case Token.BITOR: return "|"; + case Token.OR: return "||"; + case Token.BITXOR: return "^"; + case Token.AND: return "&&"; + case Token.BITAND: return "&"; + case Token.SHEQ: return "==="; + case Token.EQ: return "=="; + case Token.NOT: return "!"; + case Token.NE: return "!="; + case Token.SHNE: return "!=="; + case Token.LSH: return "<<"; + case Token.IN: return "in"; + case Token.LE: return "<="; + case Token.LT: return "<"; + case Token.URSH: return ">>>"; + case Token.RSH: return ">>"; + case Token.GE: return ">="; + case Token.GT: return ">"; + case Token.MUL: return "*"; + case Token.DIV: return "/"; + case Token.MOD: return "%"; + case Token.BITNOT: return "~"; + case Token.ADD: return "+"; + case Token.SUB: return "-"; + case Token.POS: return "+"; + case Token.NEG: return "-"; + case Token.ASSIGN: return "="; + case Token.ASSIGN_BITOR: return "|="; + case Token.ASSIGN_BITXOR: return "^="; + case Token.ASSIGN_BITAND: return "&="; + case Token.ASSIGN_LSH: return "<<="; + case Token.ASSIGN_RSH: return ">>="; + case Token.ASSIGN_URSH: return ">>>="; + case Token.ASSIGN_ADD: return "+="; + case Token.ASSIGN_SUB: return "-="; + case Token.ASSIGN_MUL: return "*="; + case Token.ASSIGN_DIV: return "/="; + case Token.ASSIGN_MOD: return "%="; + case Token.VOID: return "void"; + case Token.TYPEOF: return "typeof"; + case Token.INSTANCEOF: return "instanceof"; + default: return null; + } + } + + /** + * Converts an operator's token value (see {@link Token}) to a string + * representation or fails. + * + * @param operator the operator's token value to convert + * @return the string representation + * @throws Error if the token value is not an operator + */ + static String opToStrNoFail(int operator) { + String res = opToStr(operator); + if (res == null) { + throw new Error("Unknown op " + operator + ": " + + Token.name(operator)); + } + return res; + } + + /** + * @return true if n or any of its children are of the specified type + */ + static boolean containsType(Node node, + int type, + Predicate traverseChildrenPred) { + return has(node, new MatchNodeType(type), traverseChildrenPred); + } + + /** + * @return true if n or any of its children are of the specified type + */ + static boolean containsType(Node node, int type) { + return containsType(node, type, Predicates.alwaysTrue()); + } + + + /** + * Given a node tree, finds all the VAR declarations in that tree that are + * not in an inner scope. Then adds a new VAR node at the top of the current + * scope that redeclares them, if necessary. + */ + static void redeclareVarsInsideBranch(Node branch) { + Collection vars = getVarsDeclaredInBranch(branch); + if (vars.isEmpty()) { + return; + } + + Node parent = getAddingRoot(branch); + for (Node nameNode : vars) { + Node var = IR.var( + IR.name(nameNode.getString()) + .srcref(nameNode)) + .srcref(nameNode); + copyNameAnnotations(nameNode, var.getFirstChild()); + parent.addChildToFront(var); + } + } + + /** + * Copy any annotations that follow a named value. + * @param source + * @param destination + */ + static void copyNameAnnotations(Node source, Node destination) { + if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) { + destination.putBooleanProp(Node.IS_CONSTANT_NAME, true); + } + } + + /** + * Gets a Node at the top of the current scope where we can add new var + * declarations as children. + */ + private static Node getAddingRoot(Node n) { + Node addingRoot = null; + Node ancestor = n; + while (null != (ancestor = ancestor.getParent())) { + int type = ancestor.getType(); + if (type == Token.SCRIPT) { + addingRoot = ancestor; + break; + } else if (type == Token.FUNCTION) { + addingRoot = ancestor.getLastChild(); + break; + } + } + + // make sure that the adding root looks ok + Preconditions.checkState(addingRoot.isBlock() || + addingRoot.isScript()); + Preconditions.checkState(addingRoot.getFirstChild() == null || + !addingRoot.getFirstChild().isScript()); + return addingRoot; + } + + /** + * Creates a node representing a qualified name. + * + * @param name A qualified name (e.g. "foo" or "foo.bar.baz") + * @return A NAME or GETPROP node + */ + public static Node newQualifiedNameNode( + CodingConvention convention, String name) { + int endPos = name.indexOf('.'); + if (endPos == -1) { + return newName(convention, name); + } + Node node = newName(convention, name.substring(0, endPos)); + int startPos; + do { + startPos = endPos + 1; + endPos = name.indexOf('.', startPos); + String part = (endPos == -1 + ? name.substring(startPos) + : name.substring(startPos, endPos)); + Node propNode = IR.string(part); + if (convention.isConstantKey(part)) { + propNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); + } + node = IR.getprop(node, propNode); + } while (endPos != -1); + + return node; + } + + /** + * Creates a node representing a qualified name, copying over the source + * location information from the basis node and assigning the given original + * name to the node. + * + * @param name A qualified name (e.g. "foo" or "foo.bar.baz") + * @param basisNode The node that represents the name as currently found in + * the AST. + * @param originalName The original name of the item being represented by the + * NAME node. Used for debugging information. + * + * @return A NAME or GETPROP node + */ + static Node newQualifiedNameNode( + CodingConvention convention, String name, Node basisNode, + String originalName) { + Node node = newQualifiedNameNode(convention, name); + setDebugInformation(node, basisNode, originalName); + return node; + } + + /** + * Gets the root node of a qualified name. Must be either NAME or THIS. + */ + static Node getRootOfQualifiedName(Node qName) { + for (Node current = qName; true; + current = current.getFirstChild()) { + if (current.isName() || current.isThis()) { + return current; + } + Preconditions.checkState(current.isGetProp()); + } + } + + /** + * Sets the debug information (source file info and original name) + * on the given node. + * + * @param node The node on which to set the debug information. + * @param basisNode The basis node from which to copy the source file info. + * @param originalName The original name of the node. + */ + static void setDebugInformation(Node node, Node basisNode, + String originalName) { + node.copyInformationFromForTree(basisNode); + node.putProp(Node.ORIGINALNAME_PROP, originalName); + } + + private static Node newName( + CodingConvention convention, String name) { + Node nameNode = IR.name(name); + if (convention.isConstant(name)) { + nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); + } + return nameNode; + } + + /** + * Creates a new node representing an *existing* name, copying over the source + * location information from the basis node. + * + * @param name The name for the new NAME node. + * @param srcref The node that represents the name as currently found in + * the AST. + * + * @return The node created. + */ + static Node newName(CodingConvention convention, String name, Node srcref) { + return newName(convention, name).srcref(srcref); + } + + /** + * Creates a new node representing an *existing* name, copying over the source + * location information from the basis node and assigning the given original + * name to the node. + * + * @param name The name for the new NAME node. + * @param basisNode The node that represents the name as currently found in + * the AST. + * @param originalName The original name of the item being represented by the + * NAME node. Used for debugging information. + * + * @return The node created. + */ + static Node newName( + CodingConvention convention, String name, + Node basisNode, String originalName) { + Node nameNode = newName(convention, name, basisNode); + nameNode.putProp(Node.ORIGINALNAME_PROP, originalName); + return nameNode; + } + + /** Test if all characters in the string are in the Basic Latin (aka ASCII) + * character set - that they have UTF-16 values equal to or below 0x7f. + * This check can find which identifiers with Unicode characters need to be + * escaped in order to allow resulting files to be processed by non-Unicode + * aware UNIX tools and editors. + * * + * See http://en.wikipedia.org/wiki/Latin_characters_in_Unicode + * for more on Basic Latin. + * + * @param s The string to be checked for ASCII-goodness. + * + * @return True if all characters in the string are in Basic Latin set. + */ + static boolean isLatin(String s) { + int len = s.length(); + for (int index = 0; index < len; index++) { + char c = s.charAt(index); + if (c > LARGEST_BASIC_LATIN) { + return false; + } + } + return true; + } + + /** + * Determines whether the given name is a valid variable name. + */ + static boolean isValidSimpleName(String name) { + return TokenStream.isJSIdentifier(name) && + !TokenStream.isKeyword(name) && + // no Unicode escaped characters - some browsers are less tolerant + // of Unicode characters that might be valid according to the + // language spec. + // Note that by this point, Unicode escapes have been converted + // to UTF-16 characters, so we're only searching for character + // values, not escapes. + isLatin(name); + } + + /** + * Determines whether the given name is a valid qualified name. + */ + // TODO(nicksantos): This should be moved into a "Language" API, + // so that the results are different for es5 and es3. + public static boolean isValidQualifiedName(String name) { + if (name.endsWith(".") || name.startsWith(".")) { + return false; + } + String[] parts = name.split("\\."); + for (String part : parts) { + if (!isValidSimpleName(part)) { + return false; + } + } + return true; + } + + /** + * Determines whether the given name can appear on the right side of + * the dot operator. Many properties (like reserved words) cannot. + */ + static boolean isValidPropertyName(String name) { + return isValidSimpleName(name); + } + + private static class VarCollector implements Visitor { + final Map vars = Maps.newLinkedHashMap(); + + @Override + public void visit(Node n) { + if (n.isName()) { + Node parent = n.getParent(); + if (parent != null && parent.isVar()) { + String name = n.getString(); + if (!vars.containsKey(name)) { + vars.put(name, n); + } + } + } + } + } + + /** + * Retrieves vars declared in the current node tree, excluding descent scopes. + */ + static Collection getVarsDeclaredInBranch(Node root) { + VarCollector collector = new VarCollector(); + visitPreOrder( + root, + collector, + MATCH_NOT_FUNCTION); + return collector.vars.values(); + } + + /** + * @return {@code true} if the node an assignment to a prototype property of + * some constructor. + */ + static boolean isPrototypePropertyDeclaration(Node n) { + if (!isExprAssign(n)) { + return false; + } + return isPrototypeProperty(n.getFirstChild().getFirstChild()); + } + + /** + * @return Whether the node represents a qualified prototype property. + */ + static boolean isPrototypeProperty(Node n) { + String lhsString = n.getQualifiedName(); + if (lhsString == null) { + return false; + } + int prototypeIdx = lhsString.indexOf(".prototype."); + return prototypeIdx != -1; + } + + /** + * @return The class name part of a qualified prototype name. + */ + static Node getPrototypeClassName(Node qName) { + Node cur = qName; + while (cur.isGetProp()) { + if (cur.getLastChild().getString().equals("prototype")) { + return cur.getFirstChild(); + } else { + cur = cur.getFirstChild(); + } + } + return null; + } + + /** + * @return The string property name part of a qualified prototype name. + */ + static String getPrototypePropertyName(Node qName) { + String qNameStr = qName.getQualifiedName(); + int prototypeIdx = qNameStr.lastIndexOf(".prototype."); + int memberIndex = prototypeIdx + ".prototype".length() + 1; + return qNameStr.substring(memberIndex); + } + + /** + * Create a node for an empty result expression: + * "void 0" + */ + static Node newUndefinedNode(Node srcReferenceNode) { + Node node = IR.voidNode(IR.number(0)); + if (srcReferenceNode != null) { + node.copyInformationFromForTree(srcReferenceNode); + } + return node; + } + + /** + * Create a VAR node containing the given name and initial value expression. + */ + static Node newVarNode(String name, Node value) { + Node nodeName = IR.name(name); + if (value != null) { + Preconditions.checkState(value.getNext() == null); + nodeName.addChildToBack(value); + nodeName.srcref(value); + } + Node var = IR.var(nodeName).srcref(nodeName); + + return var; + } + + /** + * A predicate for matching name nodes with the specified node. + */ + private static class MatchNameNode implements Predicate{ + final String name; + + MatchNameNode(String name){ + this.name = name; + } + + @Override + public boolean apply(Node n) { + return n.isName() && n.getString().equals(name); + } + } + + /** + * A predicate for matching nodes with the specified type. + */ + static class MatchNodeType implements Predicate{ + final int type; + + MatchNodeType(int type){ + this.type = type; + } + + @Override + public boolean apply(Node n) { + return n.getType() == type; + } + } + + + /** + * A predicate for matching var or function declarations. + */ + static class MatchDeclaration implements Predicate { + @Override + public boolean apply(Node n) { + return isFunctionDeclaration(n) || n.isVar(); + } + } + + /** + * A predicate for matching anything except function nodes. + */ + private static class MatchNotFunction implements Predicate{ + @Override + public boolean apply(Node n) { + return !n.isFunction(); + } + } + + static final Predicate MATCH_NOT_FUNCTION = new MatchNotFunction(); + + /** + * A predicate for matching statements without exiting the current scope. + */ + static class MatchShallowStatement implements Predicate{ + @Override + public boolean apply(Node n) { + Node parent = n.getParent(); + return n.isBlock() + || (!n.isFunction() && (parent == null + || isControlStructure(parent) + || isStatementBlock(parent))); + } + } + + /** + * Finds the number of times a type is referenced within the node tree. + */ + static int getNodeTypeReferenceCount( + Node node, int type, Predicate traverseChildrenPred) { + return getCount(node, new MatchNodeType(type), traverseChildrenPred); + } + + /** + * Whether a simple name is referenced within the node tree. + */ + static boolean isNameReferenced(Node node, + String name, + Predicate traverseChildrenPred) { + return has(node, new MatchNameNode(name), traverseChildrenPred); + } + + /** + * Whether a simple name is referenced within the node tree. + */ + static boolean isNameReferenced(Node node, String name) { + return isNameReferenced(node, name, Predicates.alwaysTrue()); + } + + /** + * Finds the number of times a simple name is referenced within the node tree. + */ + static int getNameReferenceCount(Node node, String name) { + return getCount( + node, new MatchNameNode(name), Predicates.alwaysTrue()); + } + + /** + * @return Whether the predicate is true for the node or any of its children. + */ + static boolean has(Node node, + Predicate pred, + Predicate traverseChildrenPred) { + if (pred.apply(node)) { + return true; + } + + if (!traverseChildrenPred.apply(node)) { + return false; + } + + for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { + if (has(c, pred, traverseChildrenPred)) { + return true; + } + } + + return false; + } + + /** + * @return The number of times the the predicate is true for the node + * or any of its children. + */ + static int getCount( + Node n, Predicate pred, Predicate traverseChildrenPred) { + int total = 0; + + if (pred.apply(n)) { + total++; + } + + if (traverseChildrenPred.apply(n)) { + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + total += getCount(c, pred, traverseChildrenPred); + } + } + + return total; + } + + /** + * Interface for use with the visit method. + * @see #visit + */ + static interface Visitor { + void visit(Node node); + } + + /** + * A pre-order traversal, calling Visitor.visit for each child matching + * the predicate. + */ + static void visitPreOrder(Node node, + Visitor visitor, + Predicate traverseChildrenPred) { + visitor.visit(node); + + if (traverseChildrenPred.apply(node)) { + for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { + visitPreOrder(c, visitor, traverseChildrenPred); + } + } + } + + /** + * A post-order traversal, calling Visitor.visit for each child matching + * the predicate. + */ + static void visitPostOrder(Node node, + Visitor visitor, + Predicate traverseChildrenPred) { + if (traverseChildrenPred.apply(node)) { + for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { + visitPostOrder(c, visitor, traverseChildrenPred); + } + } + + visitor.visit(node); + } + + /** + * @return Whether a TRY node has a finally block. + */ + static boolean hasFinally(Node n) { + Preconditions.checkArgument(n.isTry()); + return n.getChildCount() == 3; + } + + /** + * @return The BLOCK node containing the CATCH node (if any) + * of a TRY. + */ + static Node getCatchBlock(Node n) { + Preconditions.checkArgument(n.isTry()); + return n.getFirstChild().getNext(); + } + + /** + * @return Whether BLOCK (from a TRY node) contains a CATCH. + * @see NodeUtil#getCatchBlock + */ + static boolean hasCatchHandler(Node n) { + Preconditions.checkArgument(n.isBlock()); + return n.hasChildren() && n.getFirstChild().isCatch(); + } + + /** + * @param fnNode The function. + * @return The Node containing the Function parameters. + */ + public static Node getFunctionParameters(Node fnNode) { + // Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ] + Preconditions.checkArgument(fnNode.isFunction()); + return fnNode.getFirstChild().getNext(); + } + + /** + * Returns true if a name node represents a constant variable. + * + *

      Determining whether a variable is constant has three steps: + *

        + *
      1. In CodingConventionAnnotator, any name that matches the + * {@link CodingConvention#isConstant(String)} is annotated with an + * IS_CONSTANT_NAME property. + *
      2. The normalize pass renames any variable with the IS_CONSTANT_NAME + * annotation and that is initialized to a constant value with + * a variable name including $$constant. + *
      3. Return true here if the variable includes $$constant in its name. + *
      + * + * @param node A NAME or STRING node + * @return True if the variable is constant + */ + static boolean isConstantName(Node node) { + return node.getBooleanProp(Node.IS_CONSTANT_NAME); + } + + /** Whether the given name is constant by coding convention. */ + static boolean isConstantByConvention( + CodingConvention convention, Node node, Node parent) { + String name = node.getString(); + if (parent.isGetProp() && + node == parent.getLastChild()) { + return convention.isConstantKey(name); + } else if (isObjectLitKey(node, parent)) { + return convention.isConstantKey(name); + } else { + return convention.isConstant(name); + } + } + + /** + * Get the JSDocInfo for a function. + */ + public static JSDocInfo getFunctionJSDocInfo(Node n) { + Preconditions.checkState(n.isFunction()); + JSDocInfo fnInfo = n.getJSDocInfo(); + if (fnInfo == null && NodeUtil.isFunctionExpression(n)) { + // Look for the info on other nodes. + Node parent = n.getParent(); + if (parent.isAssign()) { + // on ASSIGNs + fnInfo = parent.getJSDocInfo(); + } else if (parent.isName()) { + // on var NAME = function() { ... }; + fnInfo = parent.getParent().getJSDocInfo(); + } + } + return fnInfo; + } + + /** + * @param n The node. + * @return The source name property on the node or its ancestors. + */ + public static String getSourceName(Node n) { + String sourceName = null; + while (sourceName == null && n != null) { + sourceName = n.getSourceFileName(); + n = n.getParent(); + } + return sourceName; + } + + /** + * @param n The node. + * @return The source name property on the node or its ancestors. + */ + public static StaticSourceFile getSourceFile(Node n) { + StaticSourceFile sourceName = null; + while (sourceName == null && n != null) { + sourceName = n.getStaticSourceFile(); + n = n.getParent(); + } + return sourceName; + } + + /** + * @param n The node. + * @return The InputId property on the node or its ancestors. + */ + public static InputId getInputId(Node n) { + while (n != null && !n.isScript()) { + n = n.getParent(); + } + + return (n != null && n.isScript()) ? n.getInputId() : null; + } + + /** + * A new CALL node with the "FREE_CALL" set based on call target. + */ + static Node newCallNode(Node callTarget, Node... parameters) { + boolean isFreeCall = !isGet(callTarget); + Node call = IR.call(callTarget); + call.putBooleanProp(Node.FREE_CALL, isFreeCall); + for (Node parameter : parameters) { + call.addChildToBack(parameter); + } + return call; + } + + /** + * @return Whether the node is known to be a value that is not referenced + * elsewhere. + */ + static boolean evaluatesToLocalValue(Node value) { + return evaluatesToLocalValue(value, Predicates.alwaysFalse()); + } + + /** + * @param locals A predicate to apply to unknown local values. + * @return Whether the node is known to be a value that is not a reference + * outside the expression scope. + */ + static boolean evaluatesToLocalValue(Node value, Predicate locals) { + switch (value.getType()) { + case Token.CAST: + return evaluatesToLocalValue(value.getFirstChild(), locals); + case Token.ASSIGN: + // A result that is aliased by a non-local name, is the effectively the + // same as returning a non-local name, but this doesn't matter if the + // value is immutable. + return NodeUtil.isImmutableValue(value.getLastChild()) + || (locals.apply(value) + && evaluatesToLocalValue(value.getLastChild(), locals)); + case Token.COMMA: + return evaluatesToLocalValue(value.getLastChild(), locals); + case Token.AND: + case Token.OR: + return evaluatesToLocalValue(value.getFirstChild(), locals) + && evaluatesToLocalValue(value.getLastChild(), locals); + case Token.HOOK: + return evaluatesToLocalValue(value.getFirstChild().getNext(), locals) + && evaluatesToLocalValue(value.getLastChild(), locals); + case Token.INC: + case Token.DEC: + if (value.getBooleanProp(Node.INCRDECR_PROP)) { + return evaluatesToLocalValue(value.getFirstChild(), locals); + } else { + return true; + } + case Token.THIS: + return locals.apply(value); + case Token.NAME: + return isImmutableValue(value) || locals.apply(value); + case Token.GETELEM: + case Token.GETPROP: + // There is no information about the locality of object properties. + return locals.apply(value); + case Token.CALL: + return callHasLocalResult(value) + || isToStringMethodCall(value) + || locals.apply(value); + case Token.NEW: + return newHasLocalResult(value) + || locals.apply(value); + case Token.FUNCTION: + case Token.REGEXP: + case Token.ARRAYLIT: + case Token.OBJECTLIT: + // Literals objects with non-literal children are allowed. + return true; + case Token.DELPROP: + case Token.IN: + // TODO(johnlenz): should IN operator be included in #isSimpleOperator? + return true; + default: + // Other op force a local value: + // x = '' + g (x is now an local string) + // x -= g (x is now an local number) + if (isAssignmentOp(value) + || isSimpleOperator(value) + || isImmutableValue(value)) { + return true; + } + + throw new IllegalStateException( + "Unexpected expression node" + value + + "\n parent:" + value.getParent()); + } + } + + /** + * Given the first sibling, this returns the nth + * sibling or null if no such sibling exists. + * This is like "getChildAtIndex" but returns null for non-existent indexes. + */ + private static Node getNthSibling(Node first, int index) { + Node sibling = first; + while (index != 0 && sibling != null) { + sibling = sibling.getNext(); + index--; + } + return sibling; + } + + /** + * Given the function, this returns the nth + * argument or null if no such parameter exists. + */ + static Node getArgumentForFunction(Node function, int index) { + Preconditions.checkState(function.isFunction()); + return getNthSibling( + function.getFirstChild().getNext().getFirstChild(), index); + } + + /** + * Given the new or call, this returns the nth + * argument of the call or null if no such argument exists. + */ + static Node getArgumentForCallOrNew(Node call, int index) { + Preconditions.checkState(isCallOrNew(call)); + return getNthSibling( + call.getFirstChild().getNext(), index); + } + + /** + * Returns whether this is a target of a call or new. + */ + static boolean isCallOrNewTarget(Node target) { + Node parent = target.getParent(); + return parent != null + && NodeUtil.isCallOrNew(parent) + && parent.getFirstChild() == target; + } + + private static boolean isToStringMethodCall(Node call) { + Node getNode = call.getFirstChild(); + if (isGet(getNode)) { + Node propNode = getNode.getLastChild(); + return propNode.isString() && "toString".equals(propNode.getString()); + } + return false; + } + + /** Find the best JSDoc for the given node. */ + static JSDocInfo getBestJSDocInfo(Node n) { + JSDocInfo info = n.getJSDocInfo(); + if (info == null) { + Node parent = n.getParent(); + if (parent == null) { + return null; + } + + if (parent.isName()) { + return getBestJSDocInfo(parent); + } else if (parent.isAssign()) { + return parent.getJSDocInfo(); + } else if (isObjectLitKey(parent, parent.getParent())) { + return parent.getJSDocInfo(); + } else if (parent.isFunction()) { + return parent.getJSDocInfo(); + } else if (parent.isVar() && parent.hasOneChild()) { + return parent.getJSDocInfo(); + } else if ((parent.isHook() && parent.getFirstChild() != n) || + parent.isOr() || + parent.isAnd() || + (parent.isComma() && parent.getFirstChild() != n)) { + return getBestJSDocInfo(parent); + } else if (parent.isCast()) { + return parent.getJSDocInfo(); + } + } + return info; + } + + /** Find the l-value that the given r-value is being assigned to. */ + static Node getBestLValue(Node n) { + Node parent = n.getParent(); + boolean isFunctionDeclaration = isFunctionDeclaration(n); + if (isFunctionDeclaration) { + return n.getFirstChild(); + } else if (parent.isName()) { + return parent; + } else if (parent.isAssign()) { + return parent.getFirstChild(); + } else if (isObjectLitKey(parent, parent.getParent())) { + return parent; + } else if ( + (parent.isHook() && parent.getFirstChild() != n) || + parent.isOr() || + parent.isAnd() || + (parent.isComma() && parent.getFirstChild() != n)) { + return getBestLValue(parent); + } else if (parent.isCast()) { + return getBestLValue(parent); + } + return null; + } + + /** Gets the r-value of a node returned by getBestLValue. */ + static Node getRValueOfLValue(Node n) { + Node parent = n.getParent(); + switch (parent.getType()) { + case Token.ASSIGN: + return n.getNext(); + case Token.VAR: + return n.getFirstChild(); + case Token.FUNCTION: + return parent; + } + return null; + } + + /** Get the owner of the given l-value node. */ + static Node getBestLValueOwner(@Nullable Node lValue) { + if (lValue == null || lValue.getParent() == null) { + return null; + } + if (isObjectLitKey(lValue, lValue.getParent())) { + return getBestLValue(lValue.getParent()); + } else if (isGet(lValue)) { + return lValue.getFirstChild(); + } + + return null; + } + + /** Get the name of the given l-value node. */ + static String getBestLValueName(@Nullable Node lValue) { + if (lValue == null || lValue.getParent() == null) { + return null; + } + if (isObjectLitKey(lValue, lValue.getParent())) { + Node owner = getBestLValue(lValue.getParent()); + if (owner != null) { + String ownerName = getBestLValueName(owner); + if (ownerName != null) { + return ownerName + "." + getObjectLitKeyName(lValue); + } + } + return null; + } + return lValue.getQualifiedName(); + } + + /** + * @returns false iff the result of the expression is not consumed. + */ + static boolean isExpressionResultUsed(Node expr) { + // TODO(johnlenz): consider sharing some code with trySimpleUnusedResult. + Node parent = expr.getParent(); + switch (parent.getType()) { + case Token.BLOCK: + case Token.EXPR_RESULT: + return false; + case Token.CAST: + return isExpressionResultUsed(parent); + case Token.HOOK: + case Token.AND: + case Token.OR: + return (expr == parent.getFirstChild()) + ? true : isExpressionResultUsed(parent); + case Token.COMMA: + Node gramps = parent.getParent(); + if (gramps.isCall() && + parent == gramps.getFirstChild()) { + // Semantically, a direct call to eval is different from an indirect + // call to an eval. See ECMA-262 S15.1.2.1. So it's OK for the first + // expression to a comma to be a no-op if it's used to indirect + // an eval. This we pretend that this is "used". + if (expr == parent.getFirstChild() && + parent.getChildCount() == 2 && + expr.getNext().isName() && + "eval".equals(expr.getNext().getString())) { + return true; + } + } + + return (expr == parent.getFirstChild()) + ? false : isExpressionResultUsed(parent); + case Token.FOR: + if (!NodeUtil.isForIn(parent)) { + // Only an expression whose result is in the condition part of the + // expression is used. + return (parent.getChildAtIndex(1) == expr); + } + break; + } + return true; + } + + /** + * @param n The expression to check. + * @return Whether the expression is unconditionally executed only once in the + * containing execution scope. + */ + static boolean isExecutedExactlyOnce(Node n) { + inspect: do { + Node parent = n.getParent(); + switch (parent.getType()) { + case Token.IF: + case Token.HOOK: + case Token.AND: + case Token.OR: + if (parent.getFirstChild() != n) { + return false; + } + // other ancestors may be conditional + continue inspect; + case Token.FOR: + if (NodeUtil.isForIn(parent)) { + if (parent.getChildAtIndex(1) != n) { + return false; + } + } else { + if (parent.getFirstChild() != n) { + return false; + } + } + // other ancestors may be conditional + continue inspect; + case Token.WHILE: + case Token.DO: + return false; + case Token.TRY: + // Consider all code under a try/catch to be conditionally executed. + if (!hasFinally(parent) || parent.getLastChild() != n) { + return false; + } + continue inspect; + case Token.CASE: + case Token.DEFAULT_CASE: + return false; + case Token.SCRIPT: + case Token.FUNCTION: + // Done, we've reached the scope root. + break inspect; + } + } while ((n = n.getParent()) != null); + return true; + } + + /** + * @return An appropriate AST node for the boolean value. + */ + static Node booleanNode(boolean value) { + return value ? IR.trueNode() : IR.falseNode(); + } + + /** + * @return An appropriate AST node for the double value. + */ + static Node numberNode(double value, Node srcref) { + Node result; + if (Double.isNaN(value)) { + result = IR.name("NaN"); + } else if (value == Double.POSITIVE_INFINITY) { + result = IR.name("Infinity"); + } else if (value == Double.NEGATIVE_INFINITY) { + result = IR.neg(IR.name("Infinity")); + } else { + result = IR.number(value); + } + if (srcref != null) { + result.srcrefTree(srcref); + } + return result; + } + + static boolean isNaN(Node n) { + if ((n.isName() && n.getString().equals("NaN")) || + (n.getType() == Token.DIV && + n.getFirstChild().isNumber() && n.getFirstChild().getDouble() == 0 && + n.getLastChild().isNumber() && n.getLastChild().getDouble() == 0)) { + return true; + } + return false; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Normalize.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Normalize.java new file mode 100644 index 0000000..98b136e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Normalize.java @@ -0,0 +1,847 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage; +import com.google.javascript.jscomp.MakeDeclaredNamesUnique.BoilerplateRenamer; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Map; +import java.util.Set; + +/** + * The goal with this pass is to simplify the other passes, + * by making less complex statements. + * + * Starting with statements like: + * var a = 0, b = foo(); + * + * Which become: + * var a = 0; + * var b = foo(); + * + * The key here is only to break down things that help the other passes + * and can be put back together in a form that is at least as small when + * all is said and done. + * + * This pass currently does the following: + * 1) Simplifies the AST by splitting var statements, moving initializers + * out of for loops, and converting whiles to fors. + * 2) Moves hoisted functions to the top of function scopes. + * 3) Rewrites unhoisted named function declarations to be var declarations. + * 4) Makes all variable names globally unique (extern or otherwise) so that + * no value is ever shadowed (note: "arguments" may require special + * handling). + * 5) Removes duplicate variable declarations. + * 6) Marks constants with the IS_CONSTANT_NAME annotation. + * 7) Finds properties marked @expose, and rewrites them in [] notation. + * + * @author johnlenz@google.com (johnlenz) + */ +// public for ReplaceDebugStringsTest +class Normalize implements CompilerPass { + + private final AbstractCompiler compiler; + private final boolean assertOnChange; + private static final boolean CONVERT_WHILE_TO_FOR = true; + static final boolean MAKE_LOCAL_NAMES_UNIQUE = true; + + public static final DiagnosticType CATCH_BLOCK_VAR_ERROR = + DiagnosticType.error( + "JSC_CATCH_BLOCK_VAR_ERROR", + "The use of scope variable {0} is not allowed within a catch block " + + "with a catch exception of the same name."); + + + Normalize(AbstractCompiler compiler, boolean assertOnChange) { + this.compiler = compiler; + this.assertOnChange = assertOnChange; + + // TODO(nicksantos): assertOnChange should only be true if the tree + // is normalized. + } + + static Node parseAndNormalizeSyntheticCode( + AbstractCompiler compiler, String code, String prefix) { + Node js = compiler.parseSyntheticCode(code); + NodeTraversal.traverse(compiler, js, + new Normalize.NormalizeStatements(compiler, false)); + NodeTraversal.traverse( + compiler, js, + new MakeDeclaredNamesUnique( + new BoilerplateRenamer( + compiler.getUniqueNameIdSupplier(), + prefix))); + return js; + } + + static Node parseAndNormalizeTestCode( + AbstractCompiler compiler, String code, String prefix) { + Node js = compiler.parseTestCode(code); + NodeTraversal.traverse(compiler, js, + new Normalize.NormalizeStatements(compiler, false)); + NodeTraversal.traverse( + compiler, js, + new MakeDeclaredNamesUnique()); + return js; + } + + private void reportCodeChange(String changeDescription) { + if (assertOnChange) { + throw new IllegalStateException( + "Normalize constraints violated:\n" + changeDescription); + } + compiler.reportCodeChange(); + } + + @Override + public void process(Node externs, Node root) { + new NodeTraversal( + compiler, new NormalizeStatements(compiler, assertOnChange)) + .traverseRoots(externs, root); + if (MAKE_LOCAL_NAMES_UNIQUE) { + MakeDeclaredNamesUnique renamer = new MakeDeclaredNamesUnique(); + NodeTraversal t = new NodeTraversal(compiler, renamer); + t.traverseRoots(externs, root); + } + // It is important that removeDuplicateDeclarations runs after + // MakeDeclaredNamesUnique in order for catch block exception names to be + // handled properly. Specifically, catch block exception names are + // only valid within the catch block, but our current Scope logic + // has no concept of this and includes it in the containing function + // (or global scope). MakeDeclaredNamesUnique makes the catch exception + // names unique so that removeDuplicateDeclarations() will properly handle + // cases where a function scope variable conflict with a exception name: + // function f() { + // try {throw 0;} catch(e) {e; /* catch scope 'e'*/} + // var e = 1; // f scope 'e' + // } + // otherwise 'var e = 1' would be rewritten as 'e = 1'. + // TODO(johnlenz): Introduce a separate scope for catch nodes. + removeDuplicateDeclarations(externs, root); + new PropagateConstantAnnotationsOverVars(compiler, assertOnChange) + .process(externs, root); + + FindExposeAnnotations findExposeAnnotations = new FindExposeAnnotations(); + NodeTraversal.traverse(compiler, root, findExposeAnnotations); + if (!findExposeAnnotations.exposedProperties.isEmpty()) { + NodeTraversal.traverse(compiler, root, + new RewriteExposedProperties( + findExposeAnnotations.exposedProperties)); + } + + if (!compiler.getLifeCycleStage().isNormalized()) { + compiler.setLifeCycleStage(LifeCycleStage.NORMALIZED); + } + } + + /** + * Find all the @expose annotations. + */ + private static class FindExposeAnnotations extends AbstractPostOrderCallback { + private final Set exposedProperties = Sets.newHashSet(); + + @Override public void visit(NodeTraversal t, Node n, Node parent) { + if (NodeUtil.isExprAssign(n)) { + Node assign = n.getFirstChild(); + Node lhs = assign.getFirstChild(); + if (lhs.isGetProp() && isMarkedExpose(assign)) { + exposedProperties.add(lhs.getLastChild().getString()); + } + } else if (n.isStringKey() && isMarkedExpose(n)) { + exposedProperties.add(n.getString()); + } + } + + private boolean isMarkedExpose(Node n) { + JSDocInfo info = n.getJSDocInfo(); + return info != null && info.isExpose(); + } + } + + /** + * Rewrite all exposed properties in [] form. + */ + private class RewriteExposedProperties + extends AbstractPostOrderCallback { + private final Set exposedProperties; + + RewriteExposedProperties(Set exposedProperties) { + this.exposedProperties = exposedProperties; + } + + @Override public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isGetProp()) { + String propName = n.getLastChild().getString(); + if (exposedProperties.contains(propName)) { + Node obj = n.removeFirstChild(); + Node prop = n.removeFirstChild(); + n.getParent().replaceChild(n, IR.getelem(obj, prop)); + compiler.reportCodeChange(); + } + } else if (n.isStringKey()) { + String propName = n.getString(); + if (exposedProperties.contains(propName)) { + n.setQuotedString(); + compiler.reportCodeChange(); + } + } + } + } + + /** + * Propagate constant annotations over the Var graph. + */ + static class PropagateConstantAnnotationsOverVars + extends AbstractPostOrderCallback + implements CompilerPass { + private final AbstractCompiler compiler; + private final boolean assertOnChange; + + PropagateConstantAnnotationsOverVars( + AbstractCompiler compiler, boolean forbidChanges) { + this.compiler = compiler; + this.assertOnChange = forbidChanges; + } + + @Override + public void process(Node externs, Node root) { + new NodeTraversal(compiler, this).traverseRoots(externs, root); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + // Note: Constant properties annotations are not propagated. + if (n.isName()) { + if (n.getString().isEmpty()) { + return; + } + + JSDocInfo info = null; + // Find the JSDocInfo for a top-level variable. + Var var = t.getScope().getVar(n.getString()); + if (var != null) { + info = var.getJSDocInfo(); + } + + boolean shouldBeConstant = + (info != null && info.isConstant()) || + NodeUtil.isConstantByConvention( + compiler.getCodingConvention(), n, parent); + boolean isMarkedConstant = n.getBooleanProp(Node.IS_CONSTANT_NAME); + if (shouldBeConstant && !isMarkedConstant) { + if (assertOnChange) { + String name = n.getString(); + throw new IllegalStateException( + "Unexpected const change.\n" + + " name: "+ name + "\n" + + " parent:" + n.getParent().toStringTree()); + } + n.putBooleanProp(Node.IS_CONSTANT_NAME, true); + } + } + } + } + + /** + * Walk the AST tree and verify that constant names are used consistently. + */ + static class VerifyConstants extends AbstractPostOrderCallback + implements CompilerPass { + + final private AbstractCompiler compiler; + final private boolean checkUserDeclarations; + + VerifyConstants(AbstractCompiler compiler, boolean checkUserDeclarations) { + this.compiler = compiler; + this.checkUserDeclarations = checkUserDeclarations; + } + + @Override + public void process(Node externs, Node root) { + Node externsAndJs = root.getParent(); + Preconditions.checkState(externsAndJs != null); + Preconditions.checkState(externsAndJs.hasChild(externs)); + + NodeTraversal.traverseRoots( + compiler, Lists.newArrayList(externs, root), this); + } + + private Map constantMap = Maps.newHashMap(); + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isName()) { + String name = n.getString(); + if (n.getString().isEmpty()) { + return; + } + + boolean isConst = n.getBooleanProp(Node.IS_CONSTANT_NAME); + if (checkUserDeclarations) { + boolean expectedConst = false; + CodingConvention convention = compiler.getCodingConvention(); + if (NodeUtil.isConstantName(n) + || NodeUtil.isConstantByConvention(convention, n, parent)) { + expectedConst = true; + } else { + expectedConst = false; + + JSDocInfo info = null; + Var var = t.getScope().getVar(n.getString()); + if (var != null) { + info = var.getJSDocInfo(); + } + + if (info != null && info.isConstant()) { + expectedConst = true; + } else { + expectedConst = false; + } + } + + if (expectedConst) { + Preconditions.checkState(expectedConst == isConst, + "The name %s is not annotated as constant.", name); + } else { + Preconditions.checkState(expectedConst == isConst, + "The name %s should not be annotated as constant.", name); + } + } + + Boolean value = constantMap.get(name); + if (value == null) { + constantMap.put(name, isConst); + } else { + Preconditions.checkState(value.booleanValue() == isConst, + "The name %s is not consistently annotated as constant.", name); + } + } + } + } + + /** + * Simplify the AST: + * - VAR declarations split, so they represent exactly one child + * declaration. + * - WHILEs are converted to FORs + * - FOR loop are initializers are moved out of the FOR structure + * - LABEL node of children other than LABEL, BLOCK, WHILE, FOR, or DO are + * moved into a block. + * - Add constant annotations based on coding convention. + */ + static class NormalizeStatements implements Callback { + private final AbstractCompiler compiler; + private final boolean assertOnChange; + + NormalizeStatements(AbstractCompiler compiler, boolean assertOnChange) { + this.compiler = compiler; + this.assertOnChange = assertOnChange; + } + + private void reportCodeChange(String changeDescription) { + if (assertOnChange) { + throw new IllegalStateException( + "Normalize constraints violated:\n" + changeDescription); + } + compiler.reportCodeChange(); + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + doStatementNormalizations(t, n, parent); + + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.WHILE: + if (CONVERT_WHILE_TO_FOR) { + Node expr = n.getFirstChild(); + n.setType(Token.FOR); + Node empty = IR.empty(); + empty.copyInformationFrom(n); + n.addChildBefore(empty, expr); + n.addChildAfter(empty.cloneNode(), expr); + reportCodeChange("WHILE node"); + } + break; + + case Token.FUNCTION: + normalizeFunctionDeclaration(n); + break; + + case Token.NAME: + case Token.STRING: + case Token.STRING_KEY: + case Token.GETTER_DEF: + case Token.SETTER_DEF: + if (!compiler.getLifeCycleStage().isNormalizedObfuscated()) { + annotateConstantsByConvention(n, parent); + } + break; + + case Token.CAST: + parent.replaceChild(n, n.removeFirstChild()); + break; + } + } + + /** + * Mark names and properties that are constants by convention. + */ + private void annotateConstantsByConvention(Node n, Node parent) { + Preconditions.checkState( + n.isName() + || n.isString() + || n.isStringKey() + || n.isGetterDef() + || n.isSetterDef()); + + // There are only two cases where a string token + // may be a variable reference: The right side of a GETPROP + // or an OBJECTLIT key. + boolean isObjLitKey = NodeUtil.isObjectLitKey(n, parent); + boolean isProperty = isObjLitKey || + (parent.isGetProp() && + parent.getLastChild() == n); + if (n.isName() || isProperty) { + boolean isMarkedConstant = n.getBooleanProp(Node.IS_CONSTANT_NAME); + if (!isMarkedConstant && + NodeUtil.isConstantByConvention( + compiler.getCodingConvention(), n, parent)) { + if (assertOnChange) { + String name = n.getString(); + throw new IllegalStateException( + "Unexpected const change.\n" + + " name: "+ name + "\n" + + " parent:" + n.getParent().toStringTree()); + } + n.putBooleanProp(Node.IS_CONSTANT_NAME, true); + } + } + } + + /** + * Rewrite named unhoisted functions declarations to a known + * consistent behavior so we don't to different logic paths for the same + * code. From: + * function f() {} + * to: + * var f = function () {}; + */ + private void normalizeFunctionDeclaration(Node n) { + Preconditions.checkState(n.isFunction()); + if (!NodeUtil.isFunctionExpression(n) + && !NodeUtil.isHoistedFunctionDeclaration(n)) { + rewriteFunctionDeclaration(n); + } + } + + /** + * Rewrite the function declaration from: + * function x() {} + * FUNCTION + * NAME + * LP + * BLOCK + * to: + * var x = function() {}; + * VAR + * NAME + * FUNCTION + * NAME (w/ empty string) + * LP + * BLOCK + */ + private void rewriteFunctionDeclaration(Node n) { + // Prepare a spot for the function. + Node oldNameNode = n.getFirstChild(); + Node fnNameNode = oldNameNode.cloneNode(); + Node var = IR.var(fnNameNode).srcref(n); + + // Prepare the function + oldNameNode.setString(""); + + // Move the function + Node parent = n.getParent(); + parent.replaceChild(n, var); + fnNameNode.addChildToFront(n); + + reportCodeChange("Function declaration"); + } + + /** + * Do normalizations that introduce new siblings or parents. + */ + private void doStatementNormalizations( + NodeTraversal t, Node n, Node parent) { + if (n.isLabel()) { + normalizeLabels(n); + } + + // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these + // are the only legal place for VARs and FOR statements. + if (NodeUtil.isStatementBlock(n) || n.isLabel()) { + extractForInitializer(n, null, null); + } + + // Only inspect the children of SCRIPTs, BLOCKs, as all these + // are the only legal place for VARs. + if (NodeUtil.isStatementBlock(n)) { + splitVarDeclarations(n); + } + + if (n.isFunction()) { + moveNamedFunctions(n.getLastChild()); + } + } + + // TODO(johnlenz): Move this to NodeTypeNormalizer once the unit tests are + // fixed. + /** + * Limit the number of special cases where LABELs need to be handled. Only + * BLOCK and loops are allowed to be labeled. Loop labels must remain in + * place as the named continues are not allowed for labeled blocks. + */ + private void normalizeLabels(Node n) { + Preconditions.checkArgument(n.isLabel()); + + Node last = n.getLastChild(); + switch (last.getType()) { + case Token.LABEL: + case Token.BLOCK: + case Token.FOR: + case Token.WHILE: + case Token.DO: + return; + default: + Node block = IR.block(); + block.copyInformationFrom(last); + n.replaceChild(last, block); + block.addChildToFront(last); + reportCodeChange("LABEL normalization"); + return; + } + } + + /** + * Bring the initializers out of FOR loops. These need to be placed + * before any associated LABEL nodes. This needs to be done from the top + * level label first so this is called as a pre-order callback (from + * shouldTraverse). + * + * @param n The node to inspect. + * @param before The node to insert the initializer before. + * @param beforeParent The parent of the node before which the initializer + * will be inserted. + */ + private void extractForInitializer( + Node n, Node before, Node beforeParent) { + + for (Node next, c = n.getFirstChild(); c != null; c = next) { + next = c.getNext(); + Node insertBefore = (before == null) ? c : before; + Node insertBeforeParent = (before == null) ? n : beforeParent; + switch (c.getType()) { + case Token.LABEL: + extractForInitializer(c, insertBefore, insertBeforeParent); + break; + case Token.FOR: + if (NodeUtil.isForIn(c)) { + Node first = c.getFirstChild(); + if (first.isVar()) { + // Transform: + // for (var a = 1 in b) {} + // to: + // var a = 1; for (a in b) {}; + Node newStatement = first; + // Clone just the node, to remove any initialization. + Node name = newStatement.getFirstChild().cloneNode(); + first.getParent().replaceChild(first, name); + insertBeforeParent.addChildBefore(newStatement, insertBefore); + reportCodeChange("FOR-IN var declaration"); + } + } else if (!c.getFirstChild().isEmpty()) { + Node init = c.getFirstChild(); + Node empty = IR.empty(); + empty.copyInformationFrom(c); + c.replaceChild(init, empty); + + Node newStatement; + // Only VAR statements, and expressions are allowed, + // but are handled differently. + if (init.isVar()) { + newStatement = init; + } else { + newStatement = NodeUtil.newExpr(init); + } + + insertBeforeParent.addChildBefore(newStatement, insertBefore); + reportCodeChange("FOR initializer"); + } + break; + } + } + } + + /** + * Split a var node such as: + * var a, b; + * into individual statements: + * var a; + * var b; + * @param n The whose children we should inspect. + */ + private void splitVarDeclarations(Node n) { + for (Node next, c = n.getFirstChild(); c != null; c = next) { + next = c.getNext(); + if (c.isVar()) { + if (assertOnChange && !c.hasChildren()) { + throw new IllegalStateException("Empty VAR node."); + } + + while (c.getFirstChild() != c.getLastChild()) { + Node name = c.getFirstChild(); + c.removeChild(name); + Node newVar = IR.var(name).srcref(n); + n.addChildBefore(newVar, c); + reportCodeChange("VAR with multiple children"); + } + } + } + } + + /** + * Move all the functions that are valid at the execution of the first + * statement of the function to the beginning of the function definition. + */ + private void moveNamedFunctions(Node functionBody) { + Preconditions.checkState( + functionBody.getParent().isFunction()); + Node previous = null; + Node current = functionBody.getFirstChild(); + // Skip any declarations at the beginning of the function body, they + // are already in the right place. + while (current != null && NodeUtil.isFunctionDeclaration(current)) { + previous = current; + current = current.getNext(); + } + + // Find any remaining declarations and move them. + Node insertAfter = previous; + while (current != null) { + // Save off the next node as the current node maybe removed. + Node next = current.getNext(); + if (NodeUtil.isFunctionDeclaration(current)) { + // Remove the declaration from the body. + Preconditions.checkNotNull(previous); + functionBody.removeChildAfter(previous); + + // Read the function at the top of the function body (after any + // previous declarations). + insertAfter = addToFront(functionBody, current, insertAfter); + reportCodeChange("Move function declaration not at top of function"); + } else { + // Update the previous only if the current node hasn't been moved. + previous = current; + } + current = next; + } + } + + /** + * @param after The child node to insert the newChild after, or null if + * newChild should be added to the front of parent's child list. + * @return The inserted child node. + */ + private Node addToFront(Node parent, Node newChild, Node after) { + if (after == null) { + parent.addChildToFront(newChild); + } else { + parent.addChildAfter(newChild, after); + } + return newChild; + } + } + + /** + * Remove duplicate VAR declarations. + */ + private void removeDuplicateDeclarations(Node externs, Node root) { + Callback tickler = new ScopeTicklingCallback(); + ScopeCreator scopeCreator = new SyntacticScopeCreator( + compiler, new DuplicateDeclarationHandler()); + NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); + t.traverseRoots(externs, root); + } + + /** + * ScopeCreator duplicate declaration handler. + */ + private final class DuplicateDeclarationHandler implements + SyntacticScopeCreator.RedeclarationHandler { + + private Set hasOkDuplicateDeclaration = Sets.newHashSet(); + + /** + * Remove duplicate VAR declarations encountered discovered during + * scope creation. + */ + @Override + public void onRedeclaration( + Scope s, String name, Node n, CompilerInput input) { + Preconditions.checkState(n.isName()); + Node parent = n.getParent(); + Var v = s.getVar(name); + + if (v != null && s.isGlobal()) { + // We allow variables to be duplicate declared if one + // declaration appears in source and the other in externs. + // This deals with issues where a browser built-in is declared + // in one browser but not in another. + if (v.isExtern() && !input.isExtern()) { + if (hasOkDuplicateDeclaration.add(v)) { + return; + } + } + } + + // If name is "arguments", Var maybe null. + if (v != null && v.getParentNode().isCatch()) { + // Redeclaration of a catch expression variable is hard to model + // without support for "with" expressions. + // The ECMAScript spec (section 12.14), declares that a catch + // "catch (e) {}" is handled like "with ({'e': e}) {}" so that + // "var e" would refer to the scope variable, but any following + // reference would still refer to "e" of the catch expression. + // Until we have support for this disallow it. + // Currently the Scope object adds the catch expression to the + // function scope, which is technically not true but a good + // approximation for most uses. + + // TODO(johnlenz): Consider improving how scope handles catch + // expression. + + // Use the name of the var before it was made unique. + name = MakeDeclaredNamesUnique.ContextualRenameInverter.getOrginalName( + name); + compiler.report( + JSError.make( + input.getName(), n, + CATCH_BLOCK_VAR_ERROR, name)); + } else if (v != null && parent.isFunction()) { + if (v.getParentNode().isVar()) { + s.undeclare(v); + s.declare(name, n, n.getJSType(), v.input); + replaceVarWithAssignment(v.getNameNode(), v.getParentNode(), + v.getParentNode().getParent()); + } + } else if (parent.isVar()) { + Preconditions.checkState(parent.hasOneChild()); + + replaceVarWithAssignment(n, parent, parent.getParent()); + } + } + + /** + * Remove the parent VAR. There are three cases that need to be handled: + * 1) "var a = b;" which is replaced with "a = b" + * 2) "label:var a;" which is replaced with "label:;". Ideally, the + * label itself would be removed but that is not possible in the + * context in which "onRedeclaration" is called. + * 3) "for (var a in b) ..." which is replaced with "for (a in b)..." + * Cases we don't need to handle are VARs with multiple children, + * which have already been split into separate declarations, so there + * is no need to handle that here, and "for (var a;;);", which has + * been moved out of the loop. + * The result of this is that in each case the parent node is replaced + * which is generally dangerous in a traversal but is fine here with + * the scope creator, as the next node of interest is the parent's + * next sibling. + */ + private void replaceVarWithAssignment(Node n, Node parent, Node gramps) { + if (n.hasChildren()) { + // The * is being initialize, preserve the new value. + parent.removeChild(n); + // Convert "var name = value" to "name = value" + Node value = n.getFirstChild(); + n.removeChild(value); + Node replacement = IR.assign(n, value); + replacement.copyInformationFrom(parent); + gramps.replaceChild(parent, NodeUtil.newExpr(replacement)); + } else { + // It is an empty reference remove it. + if (NodeUtil.isStatementBlock(gramps)) { + gramps.removeChild(parent); + } else if (gramps.isFor()) { + // This is the "for (var a in b)..." case. We don't need to worry + // about initializers in "for (var a;;)..." as those are moved out + // as part of the other normalizations. + parent.removeChild(n); + gramps.replaceChild(parent, n); + } else { + Preconditions.checkState(gramps.isLabel()); + // We should never get here. LABELs with a single VAR statement should + // already have been normalized to have a BLOCK. + throw new IllegalStateException("Unexpected LABEL"); + } + } + reportCodeChange("Duplicate VAR declaration"); + } + } + + /** + * A simple class that causes scope to be created. + */ + private final class ScopeTicklingCallback + implements NodeTraversal.ScopedCallback { + @Override + public void enterScope(NodeTraversal t) { + // Cause the scope to be created, which will cause duplicate + // to be found. + t.getScope(); + } + + @Override + public void exitScope(NodeTraversal t) { + // Nothing to do. + } + + @Override + public boolean shouldTraverse( + NodeTraversal nodeTraversal, Node n, Node parent) { + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + // Nothing to do. + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ObjectPropertyStringPostprocess.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ObjectPropertyStringPostprocess.java new file mode 100644 index 0000000..3189487 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ObjectPropertyStringPostprocess.java @@ -0,0 +1,95 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * Rewrites + * new JSCompiler_ObjectPropertyString(window, foo.prototype.bar) + * to new JSCompiler_ObjectPropertyString(foo.prototype, 'bar') + * + * Rewrites + * new JSCompiler_ObjectPropertyString(window, foo[bar]) + * to new JSCompiler_ObjectPropertyString(foo, bar) + + * Rewrites + * new JSCompiler_ObjectPropertyString(window, foo$bar$baz) to + * new JSCompiler_ObjectPropertyString(window, 'foo$bar$baz') + * + * @see ObjectPropertyStringPreprocess + * + */ +class ObjectPropertyStringPostprocess implements CompilerPass { + private final AbstractCompiler compiler; + + public ObjectPropertyStringPostprocess(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, new Callback()); + } + + private class Callback extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isNew()) { + return; + } + + Node objectName = n.getFirstChild(); + + if (!ObjectPropertyStringPreprocess.EXTERN_OBJECT_PROPERTY_STRING.equals( + objectName.getQualifiedName())) { + return; + } + + Node firstArgument = objectName.getNext(); + Node secondArgument = firstArgument.getNext(); + int secondArgumentType = secondArgument.getType(); + if (secondArgumentType == Token.GETPROP) { + // Rewrite "new goog.testing.ObjectPropertyString(window, foo.bar)" + // as "new goog.testing.ObjectPropertyString(foo, 'bar')". + Node newChild = secondArgument.getFirstChild(); + secondArgument.removeChild(newChild); + n.replaceChild(firstArgument, newChild); + n.replaceChild(secondArgument, + IR.string(secondArgument.getFirstChild().getString())); + } else if (secondArgumentType == Token.GETELEM) { + // Rewrite "new goog.testing.ObjectPropertyString(window, foo[bar])" + // as "new goog.testing.ObjectPropertyString(foo, bar)". + Node newFirstArgument = secondArgument.getFirstChild(); + secondArgument.removeChild(newFirstArgument); + Node newSecondArgument = secondArgument.getLastChild(); + secondArgument.removeChild(newSecondArgument); + n.replaceChild(firstArgument, newFirstArgument); + n.replaceChild(secondArgument, newSecondArgument); + } else { + // Rewrite "new goog.testing.ObjectPropertyString(window, foo)" as + // "new goog.testing.ObjectPropertyString(window, 'foo')" + n.replaceChild(secondArgument, + IR.string(secondArgument.getString())); + } + compiler.reportCodeChange(); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ObjectPropertyStringPreprocess.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ObjectPropertyStringPreprocess.java new file mode 100644 index 0000000..4196e66 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ObjectPropertyStringPreprocess.java @@ -0,0 +1,149 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + + + +/** + * Rewrites new goog.testing.ObjectPropertyString(foo, 'bar') to + * new JSCompiler_ObjectPropertyString(window, foo.bar). + * + * These two passes are for use with goog.testing.PropertyReplacer. + * + * + * var ops = new goog.testing.ObjectPropertyString(foo.prototype, 'bar'); + * propertyReplacer.set(ops,object, ops.propertyString, baz); + * + * + * @see ObjectPropertyStringPostprocess + * + */ +public class ObjectPropertyStringPreprocess implements CompilerPass { + static final String OBJECT_PROPERTY_STRING = + "goog.testing.ObjectPropertyString"; + + public static final String EXTERN_OBJECT_PROPERTY_STRING = + "JSCompiler_ObjectPropertyString"; + + static final DiagnosticType INVALID_NUM_ARGUMENTS_ERROR = + DiagnosticType.error("JSC_OBJECT_PROPERTY_STRING_NUM_ARGS", + "goog.testing.ObjectPropertyString instantiated with \"{0}\" " + + "arguments, expected 2."); + + static final DiagnosticType QUALIFIED_NAME_EXPECTED_ERROR = + DiagnosticType.error("JSC_OBJECT_PROPERTY_STRING_QUALIFIED_NAME_EXPECTED", + "goog.testing.ObjectPropertyString instantiated with invalid " + + "argument, qualified name expected. Was \"{0}\"."); + + static final DiagnosticType STRING_LITERAL_EXPECTED_ERROR = + DiagnosticType.error("JSC_OBJECT_PROPERTY_STRING_STRING_LITERAL_EXPECTED", + "goog.testing.ObjectPropertyString instantiated with invalid " + + "argument, string literal expected. Was \"{0}\"."); + + private final AbstractCompiler compiler; + + ObjectPropertyStringPreprocess(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + addExternDeclaration(externs, + IR.var( + IR.name(EXTERN_OBJECT_PROPERTY_STRING))); + NodeTraversal.traverse(compiler, root, new Callback()); + } + + private void addExternDeclaration(Node externs, Node declarationStmt) { + Node script = externs.getLastChild(); + if (script == null || !script.isScript()) { + script = IR.script(); + externs.addChildToBack(script); + } + script.addChildToBack(declarationStmt); + } + + private class Callback extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (OBJECT_PROPERTY_STRING.equals(n.getQualifiedName())) { + Node newName = IR.name(EXTERN_OBJECT_PROPERTY_STRING); + newName.copyInformationFrom(n); + parent.replaceChild(n, newName); + compiler.reportCodeChange(); + return; + } + + // Rewrite "new goog.testing.ObjectPropertyString(foo, 'bar')" to + // "new goog.testing.ObjectPropertyString(window, foo.bar)" and + // issues errors if bad arguments are encountered. + if (!n.isNew()) { + return; + } + + Node objectName = n.getFirstChild(); + + if (!EXTERN_OBJECT_PROPERTY_STRING.equals( + objectName.getQualifiedName())) { + return; + } + + if (n.getChildCount() != 3) { + compiler.report(t.makeError(n, INVALID_NUM_ARGUMENTS_ERROR, + "" + n.getChildCount())); + return; + } + + Node firstArgument = objectName.getNext(); + if (!firstArgument.isQualifiedName()) { + compiler.report(t.makeError(firstArgument, + QUALIFIED_NAME_EXPECTED_ERROR, + Token.name(firstArgument.getType()))); + return; + } + + Node secondArgument = firstArgument.getNext(); + if (!secondArgument.isString()) { + compiler.report(t.makeError(secondArgument, + STRING_LITERAL_EXPECTED_ERROR, + Token.name(secondArgument.getType()))); + return; + } + + Node newFirstArgument = NodeUtil.newQualifiedNameNode( + compiler.getCodingConvention(), + compiler.getCodingConvention().getGlobalObject()) + .srcrefTree(firstArgument); + + Node newSecondArgument = NodeUtil.newQualifiedNameNode( + compiler.getCodingConvention(), + firstArgument.getQualifiedName() + "." + + firstArgument.getNext().getString()) + .srcrefTree(secondArgument); + + n.replaceChild(firstArgument, newFirstArgument); + n.replaceChild(secondArgument, newSecondArgument); + + compiler.reportCodeChange(); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/OptimizeArgumentsArray.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/OptimizeArgumentsArray.java new file mode 100644 index 0000000..03e257f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/OptimizeArgumentsArray.java @@ -0,0 +1,297 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import java.util.Deque; +import java.util.List; + +/** + * Optimization for functions that have {@code var_args} or access the + * arguments array. + * + *

      Example: + *

      + * function() { alert(arguments[0] + argument[1]) }
      + * 
      + * to: + *
      + * function(a, b) { alert(a, b) }
      + * 
      + * + * Each newly inserted variable name will be unique very much like the output + * of the AST found after the {@link Normalize} pass. + * + */ +class OptimizeArgumentsArray implements CompilerPass, ScopedCallback { + + // The arguments object as described by ECMAScript version 3 + // section 10.1.8 + private static final String ARGUMENTS = "arguments"; + + // To ensure that the newly introduced parameter names are unique. We will + // use this string as prefix unless the caller specify a different prefix. + private static final String PARAMETER_PREFIX = + "JSCompiler_OptimizeArgumentsArray_p"; + + // The prefix for the newly introduced parameter name. + private final String paramPredix; + + // To make each parameter name unique in the function. We append an + // unique integer at the end. + private int uniqueId = 0; + + // Reference to the compiler object to notify any changes to source code AST. + private final AbstractCompiler compiler; + + // A stack of arguments access list to the corresponding outer functions. + private final Deque> argumentsAccessStack = Lists.newLinkedList(); + + // This stores a list of argument access in the current scope. + private List currentArgumentsAccess = null; + + /** + * Construct this pass and use {@link #PARAMETER_PREFIX} as the prefix for + * all parameter names that it introduces. + */ + OptimizeArgumentsArray(AbstractCompiler compiler) { + this(compiler, PARAMETER_PREFIX); + } + + /** + * @param paramPrefix the prefix to use for all parameter names that this + * pass introduces + */ + OptimizeArgumentsArray(AbstractCompiler compiler, String paramPrefix) { + this.compiler = Preconditions.checkNotNull(compiler); + this.paramPredix = Preconditions.checkNotNull(paramPrefix); + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, Preconditions.checkNotNull(root), this); + } + + @Override + public void enterScope(NodeTraversal traversal) { + Preconditions.checkNotNull(traversal); + + // This optimization is valid only within a function so we are going to + // skip over the initial entry to the global scope. + Node function = traversal.getScopeRoot(); + if (!function.isFunction()) { + return; + } + + // Introduces a new access list and stores the access list of the outer + // scope in the stack if necessary. + if (currentArgumentsAccess != null) { + argumentsAccessStack.push(currentArgumentsAccess); + } + currentArgumentsAccess = Lists.newLinkedList(); + } + + @Override + public void exitScope(NodeTraversal traversal) { + Preconditions.checkNotNull(traversal); + + // This is the case when we are exiting the global scope where we had never + // collected argument access list. Since we do not perform this optimization + // for the global scope, we will skip this exit point. + if (currentArgumentsAccess == null) { + return; + } + + // Attempt to replace the argument access and if the AST has been change, + // report back to the compiler. + if (tryReplaceArguments(traversal.getScope())) { + traversal.getCompiler().reportCodeChange(); + } + + // After the attempt to replace the arguments. The currentArgumentsAccess + // is stale and as we exit the Scope, no longer holds all the access to the + // current scope anymore. We'll pop the access list from the outer scope + // and set it as currentArgumentsAcess if the outer scope is not the global + // scope. + if (!argumentsAccessStack.isEmpty()) { + currentArgumentsAccess = argumentsAccessStack.pop(); + } else { + currentArgumentsAccess = null; + } + } + + @Override + public boolean shouldTraverse( + NodeTraversal nodeTraversal, Node node, Node parent) { + // We will continuously recurse down the AST regardless of the node types. + return true; + } + + @Override + public void visit(NodeTraversal traversal, Node node, Node parent) { + Preconditions.checkNotNull(traversal); + Preconditions.checkNotNull(node); + + + // Searches for all the references to the arguments array. + + // We don't have an arguments list set up for this scope. This implies we + // are currently in the global scope so we will not record any arguments + // array access. + if (currentArgumentsAccess == null) { + return; + } + + // Otherwise, we are in a function scope and we should record if the current + // name is referring to the implicit arguments array. + if (node.isName() && ARGUMENTS.equals(node.getString())) { + currentArgumentsAccess.add(node); + } + } + + /** + * Tries to optimize all the arguments array access in this scope by assigning + * a name to each element. + * + * @param scope scope of the function + * @return true if any modification has been done to the AST + */ + private boolean tryReplaceArguments(Scope scope) { + + Node parametersList = scope.getRootNode().getFirstChild().getNext(); + Preconditions.checkState(parametersList.isParamList()); + + // Keep track of rather this function modified the AST and needs to be + // reported back to the compiler later. + boolean changed = false; + + // Number of parameter that can be accessed without using the arguments + // array. + int numNamedParameter = parametersList.getChildCount(); + + // We want to guess what the highest index that has been access from the + // arguments array. We will guess that it does not use anything index higher + // than the named parameter list first until we see other wise. + int highestIndex = numNamedParameter - 1; + + // Iterate through all the references to arguments array in the function to + // determine the real highestIndex. + for (Node ref : currentArgumentsAccess) { + + Node getElem = ref.getParent(); + + // Bail on anything but argument[c] access where c is a constant. + // TODO(user): We might not need to bail out all the time, there might + // be more cases that we can cover. + if (!getElem.isGetElem()) { + return false; + } + + Node index = ref.getNext(); + + // We have something like arguments[x] where x is not a constant. That + // means at least one of the access is not known. + if (!index.isNumber()) { + // TODO(user): Its possible not to give up just yet. The type + // inference did a 'semi value propagation'. If we know that string + // is never a subclass of the type of the index. We'd know that + // it is never 'callee'. + return false; // Give up. + } + + Node getElemParent = getElem.getParent(); + // When we have argument[0](), replacing it with a() is semantically + // different if argument[0] is a function call that refers to 'this' + if (getElemParent.isCall() && + getElemParent.getFirstChild() == getElem) { + // TODO(user): We can consider using .call() if aliasing that + // argument allows shorter alias for other arguments. + return false; + } + + // Replace the highest index if we see an access that has a higher index + // than all the one we saw before. + int value = (int) index.getDouble(); + if (value > highestIndex) { + highestIndex = value; + } + } + + // Number of extra arguments we need. + // For example: function() { arguments[3] } access index 3 so + // it will need 4 extra named arguments to changed into: + // function(a,b,c,d) { d }. + int numExtraArgs = highestIndex - numNamedParameter + 1; + + // Temporary holds the new names as string for quick access later. + String[] argNames = new String[numExtraArgs]; + + // Insert the formal parameter to the method's signature. + // Example: function() --> function(r0, r1, r2) + for (int i = 0; i < numExtraArgs; i++) { + String name = getNewName(); + argNames[i] = name; + parametersList.addChildrenToBack(IR.name(name)); + changed = true; + } + + // This loop performs the replacement of arguments[x] -> a if x is known. + for (Node ref : currentArgumentsAccess) { + Node index = ref.getNext(); + + // Skip if it is unknown. + if (!index.isNumber()) { + continue; + } + int value = (int) index.getDouble(); + + // Unnamed parameter. + if (value >= numNamedParameter) { + ref.getParent().getParent().replaceChild(ref.getParent(), + IR.name(argNames[value - numNamedParameter])); + } else { + + // Here, for no apparent reason, the user is accessing a named parameter + // with arguments[idx]. We can replace it with the actual name for them. + Node name = parametersList.getFirstChild(); + + // This is a linear search for the actual name from the signature. + // It is not necessary to make this fast because chances are the user + // will not deliberately write code like this. + for (int i = 0; i < value; i++) { + name = name.getNext(); + } + ref.getParent().getParent().replaceChild(ref.getParent(), + IR.name(name.getString())); + } + changed = true; + } + + return changed; + } + + /** + * Generate a unique name for the next parameter. + */ + private String getNewName() { + return paramPredix + uniqueId++; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/OptimizeCalls.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/OptimizeCalls.java new file mode 100644 index 0000000..7260627 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/OptimizeCalls.java @@ -0,0 +1,61 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.javascript.rhino.Node; + +import java.util.List; + +/** + * A root pass that container for other passes that should run on + * with a single call graph (currently a SimpleDefinitionFinder). + * Expected passes include: + * - optimize parameters + * - optimize returns + * - devirtualize prototype methods + * + * @author johnlenz@google.com (John Lenz) + */ +class OptimizeCalls implements CompilerPass { + List passes = Lists.newArrayList(); + private AbstractCompiler compiler; + + OptimizeCalls(AbstractCompiler compiler) { + this.compiler = compiler; + } + + OptimizeCalls addPass(CallGraphCompilerPass pass) { + passes.add(pass); + return this; + } + + interface CallGraphCompilerPass { + void process(Node externs, Node root, SimpleDefinitionFinder definitions); + } + + @Override + public void process(Node externs, Node root) { + if (passes.size() > 0) { + SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); + defFinder.process(externs, root); + for (CallGraphCompilerPass pass : passes) { + pass.process(externs, root, defFinder); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/OptimizeParameters.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/OptimizeParameters.java new file mode 100644 index 0000000..76e12a5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/OptimizeParameters.java @@ -0,0 +1,548 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage; +import com.google.javascript.jscomp.DefinitionsRemover.Definition; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Collection; +import java.util.List; + +/** + * Optimize function calls and function signatures. + * + *
        + *
      • Removes optional parameters if no caller specifies it as argument.
      • + *
      • Removes arguments at call site to function that + * ignores the parameter.
      • + *
      • Inline a parameter if the function is always called with that constant. + *
      • + *
      + * + */ +class OptimizeParameters + implements CompilerPass, OptimizeCalls.CallGraphCompilerPass { + + private final AbstractCompiler compiler; + private List removedNodes = Lists.newArrayList(); + + OptimizeParameters(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + @VisibleForTesting + public void process(Node externs, Node root) { + Preconditions.checkState( + compiler.getLifeCycleStage() == LifeCycleStage.NORMALIZED); + SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); + defFinder.process(externs, root); + process(externs, root, defFinder); + } + + @Override + public void process( + Node externs, Node root, SimpleDefinitionFinder definitions) { + for (DefinitionSite defSite : definitions.getDefinitionSites()) { + if (canChangeSignature(defSite, definitions)) { + tryEliminateConstantArgs(defSite, definitions); + tryEliminateOptionalArgs(defSite, definitions); + } + } + + // Remove any references or definitions that have been removed to keep it + // in a consistent state for the next pass. + for (Node n : removedNodes) { + definitions.removeReferences(n); + } + } + + /** + * @return Whether the definitionSite represents a function whose call + * signature can be modified. + */ + private boolean canChangeSignature( + DefinitionSite definitionSite, SimpleDefinitionFinder defFinder) { + Definition definition = definitionSite.definition; + + if (definitionSite.inExterns) { + return false; + } + + // Only functions may be rewritten. + // Functions that access "arguments" are not eligible since + // rewrite changes the structure of this object. + Node rValue = definition.getRValue(); + if (rValue == null || + !rValue.isFunction() || + NodeUtil.isVarArgsFunction(rValue)) { + return false; + } + + // TODO(johnlenz): support rewriting methods defined as part of + // object literals (they are generally problematic because they may be + // maps of functions use in for-in expressions, etc). + // Be conservative, don't try to optimize any declaration that isn't as + // simple function declaration or assignment. + if (!SimpleDefinitionFinder.isSimpleFunctionDeclaration(rValue)) { + return false; + } + + // Assume an exported method result is used. + if (!defFinder.canModifyDefinition(definition)) { + return false; + } + + Collection useSites = defFinder.getUseSites(definition); + + if (useSites.isEmpty()) { + return false; + } + + for (UseSite site : useSites) { + // Any non-call reference maybe introducing an alias. Don't try to + // change the function signature, if all the aliases can't also be + // changed. + // TODO(johnlenz): Support .call signature changes. + if (!SimpleDefinitionFinder.isCallOrNewSite(site)) { + return false; + } + + // TODO(johnlenz): support specialization + + // Multiple definitions prevent rewrite. + // TODO(johnlenz): Allow rewrite all definitions are valid. + Node nameNode = site.node; + Collection singleSiteDefinitions = + defFinder.getDefinitionsReferencedAt(nameNode); + if (singleSiteDefinitions.size() > 1) { + return false; + } + Preconditions.checkState(!singleSiteDefinitions.isEmpty()); + Preconditions.checkState(singleSiteDefinitions.contains(definition)); + } + + return true; + } + + /** + * Removes any optional parameters if no callers specifies it as an argument. + */ + private void tryEliminateOptionalArgs( + DefinitionSite defSite, SimpleDefinitionFinder defFinder) { + // Count the maximum number of arguments passed into this function all + // all points of the program. + int maxArgs = -1; + + Definition definition = defSite.definition; + Collection useSites = defFinder.getUseSites(definition); + for (UseSite site : useSites) { + Preconditions.checkState(SimpleDefinitionFinder.isCallOrNewSite(site)); + Node call = site.node.getParent(); + + int numArgs = call.getChildCount() - 1; + if (numArgs > maxArgs) { + maxArgs = numArgs; + } + } + + eliminateParamsAfter(definition.getRValue(), maxArgs); + } + + /** + * Eliminate parameters if they are always constant. + * + * function foo(a, b) {...} + * foo(1,2); + * foo(1,3) + * becomes + * function foo(b) { var a = 1 ... } + * foo(2); + * foo(3); + */ + private void tryEliminateConstantArgs( + DefinitionSite defSite, SimpleDefinitionFinder defFinder) { + + List parameters = Lists.newArrayList(); + boolean firstCall = true; + + // Build a list of parameters to remove + Definition definition = defSite.definition; + Collection useSites = defFinder.getUseSites(definition); + boolean continueLooking = false; + for (UseSite site : useSites) { + Preconditions.checkState(SimpleDefinitionFinder.isCallOrNewSite(site)); + Node call = site.node.getParent(); + + Node cur = call.getFirstChild(); + if (firstCall) { + // Use the first call to construct a list of parameters of the + // function. + continueLooking = buildParameterList(parameters, cur, site.scope); + firstCall = false; + } else { + continueLooking= findFixedParameters(parameters, cur); + } + if (!continueLooking) { + return; + } + } + + continueLooking = adjustForSideEffects(parameters); + if (!continueLooking) { + return; + } + + // Remove the constant parameters in all the calls + for (UseSite site : useSites) { + Preconditions.checkState(SimpleDefinitionFinder.isCallOrNewSite(site)); + Node call = site.node.getParent(); + + optimizeCallSite(defFinder, parameters, call); + } + + // Remove the constant parameters in the definitions and add it as a local + // variable. + Node function = definition.getRValue(); + if (function.isFunction()) { + optimizeFunctionDefinition(parameters, function); + } + } + + /** + * Adjust the parameters to move based on the side-effects seen. + * @return Whether there are any movable parameters. + */ + private boolean adjustForSideEffects(List parameters) { + // If a parameter is moved, that has side-effect no parameters that + // can be effected by side-effects can be left. + + // A parameter can be moved if it can't be side-effected (immutable), + // or there are no following side-effects, that aren't moved. + + boolean anyMovable = false; + boolean seenUnmovableSideEffects = false; + boolean seenUnmoveableSideEfffected = false; + for (int i = parameters.size() - 1; i >= 0; i--) { + Parameter current = parameters.get(i); + + // Preserve side-effect ordering, don't move this parameter if: + // * the current parameter has side-effects and a following + // parameters that will not be move can be effected. + // * the current parameter can be effected and a following + // parameter that will not be moved has side-effects + + if (current.shouldRemove + && ((seenUnmovableSideEffects && current.canBeSideEffected()) + || (seenUnmoveableSideEfffected && current.hasSideEffects()))) { + current.shouldRemove = false; + } + + if (current.shouldRemove) { + anyMovable = true; + } else { + if (current.canBeSideEffected) { + seenUnmoveableSideEfffected = true; + } + + if (current.hasSideEffects) { + seenUnmovableSideEffects = true; + } + } + } + return anyMovable; + } + + /** + * Determine which parameters use the same expression. + * @return Whether any parameter was found that can be updated. + */ + private boolean findFixedParameters(List parameters, Node cur) { + boolean anyMovable = false; + int index = 0; + while ((cur = cur.getNext()) != null) { + Parameter p; + if (index >= parameters.size()) { + p = new Parameter(cur, false); + parameters.add(p); + } else { + p = parameters.get(index); + if (p.shouldRemove()) { + Node value = p.getArg(); + if (!cur.isEquivalentTo(value)) { + p.setShouldRemove(false); + } else { + anyMovable = true; + } + } + } + + setParameterSideEffectInfo(p, cur); + index++; + } + + for (;index < parameters.size(); index++) { + parameters.get(index).setShouldRemove(false); + } + + return anyMovable; + } + + /** + * @return Whether any parameter was movable. + */ + private boolean buildParameterList( + List parameters, Node cur, Scope s) { + boolean anyMovable = false; + while ((cur = cur.getNext()) != null) { + boolean movable = isMovableValue(cur, s); + Parameter p = new Parameter(cur, movable); + setParameterSideEffectInfo(p, cur); + parameters.add(p); + if (movable) { + anyMovable = true; + } + } + return anyMovable; + } + + private void setParameterSideEffectInfo(Parameter p, Node value) { + if (!p.hasSideEffects()) { + p.setHasSideEffects(NodeUtil.mayHaveSideEffects(value, compiler)); + } + + if (!p.canBeSideEffected()) { + p.setCanBeSideEffected(NodeUtil.canBeSideEffected(value)); + } + } + + + /** + * @return Whether the expression can be safely moved to another function + * in another scope. + */ + private boolean isMovableValue(Node n, Scope s) { + // Things that can change value or are inaccessible can't be moved, these + // are "this", "arguments", local names, and functions that capture local + // values. + switch (n.getType()) { + case Token.THIS: + return false; + case Token.FUNCTION: + // Don't move function closures. + // TODO(johnlenz): Closure that only contain global reference can be + // moved. + return false; + case Token.NAME: + if (n.getString().equals("arguments")) { + return false; + } else { + Var v = s.getVar(n.getString()); + // Make sure that the variable is global. A caught exception, while + // it is in the global scope object in the compiler, it is not a + // global variable. + if (v != null && + (v.isLocal() || + v.nameNode.getParent().isCatch())) { + return false; + } + } + break; + } + + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + if (!isMovableValue(c, s)) { + return false; + } + } + return true; + } + + private void optimizeFunctionDefinition(List parameters, + Node function) { + for (int index = parameters.size() - 1; index >= 0; index--) { + if (parameters.get(index).shouldRemove()) { + Node paramName = eliminateFunctionParamAt(function, index); + addVariableToFunction(function, paramName, + parameters.get(index).getArg()); + } + } + } + + private void optimizeCallSite( + SimpleDefinitionFinder defFinder, List parameters, Node call) { + for (int index = parameters.size() - 1; index >= 0; index--) { + Parameter p = parameters.get(index); + if (p.shouldRemove()) { + eliminateCallParamAt(defFinder, p, call, index); + } + } + } + + /** + * Simple container class that keeps tracks of a parameter and whether it + * should be removed. + */ + private static class Parameter { + private final Node arg; + private boolean shouldRemove; + private boolean hasSideEffects; + private boolean canBeSideEffected; + + public Parameter(Node arg, boolean shouldRemove) { + this.shouldRemove = shouldRemove; + this.arg = arg; + } + + public Node getArg() { + return arg; + } + + public boolean shouldRemove() { + return shouldRemove; + } + + public void setShouldRemove(boolean value) { + shouldRemove = value; + } + + public void setHasSideEffects(boolean hasSideEffects) { + this.hasSideEffects = hasSideEffects; + } + + public boolean hasSideEffects() { + return hasSideEffects; + } + + public void setCanBeSideEffected(boolean canBeSideEffected) { + this.canBeSideEffected = canBeSideEffected; + } + + public boolean canBeSideEffected() { + return canBeSideEffected; + } + } + + /** + * Adds a variable to the top of a function block. + * @param function A function node. + * @param varName The name of the variable. + * @param value The initial value of the variable. + */ + private void addVariableToFunction(Node function, Node varName, Node value) { + Preconditions.checkArgument(function.isFunction(), + "Node must be a function."); + + Node block = function.getLastChild(); + Preconditions.checkArgument(block.isBlock(), + "Node must be a block."); + + Preconditions.checkState(value.getParent() == null); + Node stmt; + if (varName != null) { + stmt = NodeUtil.newVarNode(varName.getString(), value); + } else { + stmt = IR.exprResult(value); + } + block.addChildToFront(stmt); + compiler.reportCodeChange(); + } + + /** + * Removes all formal parameters starting at argIndex. + * @return true if a parameter has been removed. + */ + private boolean eliminateParamsAfter(Node function, int argIndex) { + boolean paramRemoved = false; + + Node formalArgPtr = function.getFirstChild().getNext().getFirstChild(); + while (argIndex != 0 && formalArgPtr != null) { + formalArgPtr = formalArgPtr.getNext(); + argIndex--; + } + + return eliminateParamsAfter(function, formalArgPtr); + } + + private boolean eliminateParamsAfter(Node fnNode, Node argNode) { + if (argNode != null) { + // Keep the args in the same order, do the last first. + eliminateParamsAfter(fnNode, argNode.getNext()); + argNode.detachFromParent(); + Node var = IR.var(argNode).copyInformationFrom(argNode); + fnNode.getLastChild().addChildrenToFront(var); + compiler.reportCodeChange(); + return true; + } + return false; + } + + /** + * Eliminates the parameter from a function definition. + * @param function The function node + * @param argIndex The index of the the argument to remove. + * @return The Node of the argument removed. + */ + private Node eliminateFunctionParamAt(Node function, int argIndex) { + Preconditions.checkArgument(function.isFunction(), + "Node must be a function."); + + Node formalArgPtr = NodeUtil.getArgumentForFunction( + function, argIndex); + + if (formalArgPtr != null) { + function.getFirstChild().getNext().removeChild(formalArgPtr); + } + return formalArgPtr; + } + + /** + * Eliminates the parameter from a function call. + * @param defFinder + * @param p + * @param call The function call node + * @param argIndex The index of the the argument to remove. + * @return The Node of the argument removed. + */ + private Node eliminateCallParamAt( + SimpleDefinitionFinder defFinder, Parameter p, Node call, int argIndex) { + Preconditions.checkArgument( + NodeUtil.isCallOrNew(call), "Node must be a call or new."); + + Node formalArgPtr = NodeUtil.getArgumentForCallOrNew( + call, argIndex); + + if (formalArgPtr != null) { + call.removeChild(formalArgPtr); + // The value in the parameter object is the one that is being moved into + // function definition leave that one's references. For everything else, + // remove any references. + if (p.getArg() != formalArgPtr) { + removedNodes.add(formalArgPtr); + } + compiler.reportCodeChange(); + } + return formalArgPtr; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/OptimizeReturns.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/OptimizeReturns.java new file mode 100644 index 0000000..b2d47ed --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/OptimizeReturns.java @@ -0,0 +1,163 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.DefinitionsRemover.Definition; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import java.util.Collection; +import java.util.List; + +/** + * A compiler pass for optimize function return results. Currently this + * pass looks for results that are complete unused and rewrite then to be: + * "return x()" -->"x(); return" + * , but it can easily be + * expanded to look for use context to avoid unneeded type coercion: + * - "return x.toString()" --> "return x" + * - "return !!x" --> "return x" + * @author johnlenz@google.com (John Lenz) + */ +class OptimizeReturns + implements OptimizeCalls.CallGraphCompilerPass, CompilerPass { + + private AbstractCompiler compiler; + + OptimizeReturns(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + @VisibleForTesting + public void process(Node externs, Node root) { + SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); + defFinder.process(externs, root); + process(externs, root, defFinder); + } + + @Override + public void process( + Node externs, Node root, SimpleDefinitionFinder definitions) { + // Find all function nodes whose callers ignore the return values. + List toOptimize = Lists.newArrayList(); + for (DefinitionSite defSite : definitions.getDefinitionSites()) { + if (!defSite.inExterns && !callResultsMaybeUsed(definitions, defSite)) { + toOptimize.add(defSite.definition.getRValue()); + } + } + // Optimize the return statements. + for (Node node : toOptimize) { + rewriteReturns(definitions, node); + } + } + + /** + * Determines if a function result might be used. A result might be use if: + * - Function must is exported. + * - The definition is never accessed outside a function call context. + */ + private boolean callResultsMaybeUsed( + SimpleDefinitionFinder defFinder, DefinitionSite definitionSite) { + + Definition definition = definitionSite.definition; + + // Assume non-function definitions results are used. + Node rValue = definition.getRValue(); + if (rValue == null || !rValue.isFunction()) { + return true; + } + + // Be conservative, don't try to optimize any declaration that isn't as + // simple function declaration or assignment. + if (!SimpleDefinitionFinder.isSimpleFunctionDeclaration(rValue)) { + return true; + } + + if (!defFinder.canModifyDefinition(definition)) { + return true; + } + + Collection useSites = defFinder.getUseSites(definition); + for (UseSite site : useSites) { + // Assume indirect definitions references use the result + Node useNodeParent = site.node.getParent(); + if (isCall(site)) { + Node callNode = useNodeParent; + Preconditions.checkState(callNode.isCall()); + if (NodeUtil.isExpressionResultUsed(callNode)) { + return true; + } + } else { + // Allow a standalone name reference. + // var a; + if (!useNodeParent.isVar()) { + return true; + } + } + + // TODO(johnlenz): Add specialization support. + } + + // No possible use of the definition result + return false; + } + + /** + * For the supplied function node, rewrite all the return expressions so that: + * return foo(); + * becomes: + * foo(); return; + * Useless return will be removed later by the peephole optimization passes. + */ + private void rewriteReturns( + final SimpleDefinitionFinder defFinder, Node fnNode) { + Preconditions.checkState(fnNode.isFunction()); + NodeUtil.visitPostOrder( + fnNode.getLastChild(), + new NodeUtil.Visitor() { + @Override + public void visit(Node node) { + if (node.isReturn() && node.hasOneChild()) { + boolean keepValue = NodeUtil.mayHaveSideEffects( + node.getFirstChild(), compiler); + if (!keepValue) { + defFinder.removeReferences(node.getFirstChild()); + } + Node result = node.removeFirstChild(); + if (keepValue) { + node.getParent().addChildBefore( + IR.exprResult(result).srcref(result), node); + } + compiler.reportCodeChange(); + } + } + }, + new NodeUtil.MatchShallowStatement()); + } + + /** + * Determines if the name node acts as the function name in a call expression. + */ + private static boolean isCall(UseSite site) { + Node node = site.node; + Node parent = node.getParent(); + return (parent.getFirstChild() == node) && parent.isCall(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PassConfig.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PassConfig.java new file mode 100644 index 0000000..adcf17e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PassConfig.java @@ -0,0 +1,312 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; +import com.google.javascript.jscomp.graph.GraphvizGraph; +import com.google.javascript.jscomp.graph.LinkedDirectedGraph; +import com.google.javascript.rhino.Node; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Pass factories and meta-data for native Compiler passes. + * + * @author nicksantos@google.com (Nick Santos) + */ +public abstract class PassConfig { + + // Used by subclasses in this package. + final CompilerOptions options; + + /** + * A memoized version of scopeCreator. It must be memoized so that + * we can make two separate passes over the AST, one for inferring types + * and one for checking types. + */ + private MemoizedScopeCreator typedScopeCreator; + + /** + * This is the scope creator that {@code TypedScopeCreator} delegates to. + */ + private TypedScopeCreator internalScopeCreator; + + /** The global typed scope. */ + Scope topScope = null; + + public PassConfig(CompilerOptions options) { + this.options = options; + } + + /** + * Regenerates the top scope from scratch. + * + * @param compiler The compiler for which the global scope is regenerated. + * @param root The root of the AST. + */ + void regenerateGlobalTypedScope(AbstractCompiler compiler, Node root) { + internalScopeCreator = new TypedScopeCreator(compiler); + typedScopeCreator = new MemoizedScopeCreator(internalScopeCreator); + topScope = typedScopeCreator.createScope(root, null); + } + + void clearTypedScope() { + internalScopeCreator = null; + typedScopeCreator = null; + topScope = null; + } + + /** + * Regenerates the top scope potentially only for a sub-tree of AST and then + * copies information for the old global scope. + * + * @param compiler The compiler for which the global scope is generated. + * @param scriptRoot The root of the AST used to generate global scope. + */ + void patchGlobalTypedScope(AbstractCompiler compiler, Node scriptRoot) { + Preconditions.checkNotNull(internalScopeCreator); + internalScopeCreator.patchGlobalScope(topScope, scriptRoot); + } + + /** + * Gets the scope creator for typed scopes. + */ + MemoizedScopeCreator getTypedScopeCreator() { + return typedScopeCreator; + } + + /** + * Gets the global scope, with type information. + */ + Scope getTopScope() { + return topScope; + } + + /** + * Gets the checking passes to run. + * + * Checking passes revolve around emitting warnings and errors. + * They also may include pre-processor passes needed to do + * error analysis more effectively. + * + * Clients that only want to analyze code (like IDEs) and not emit + * code will only run checks and not optimizations. + */ + abstract protected List getChecks(); + + /** + * Gets the optimization passes to run. + * + * Optimization passes revolve around producing smaller and faster code. + * They should always run after checking passes. + */ + abstract protected List getOptimizations(); + + /** + * Gets a graph of the passes run. For debugging. + */ + GraphvizGraph getPassGraph() { + LinkedDirectedGraph graph = + LinkedDirectedGraph.createWithoutAnnotations(); + Iterable allPasses = + Iterables.concat(getChecks(), getOptimizations()); + String lastPass = null; + String loopStart = null; + for (PassFactory pass : allPasses) { + String passName = pass.getName(); + int i = 1; + while (graph.hasNode(passName)) { + passName = pass.getName() + (i++); + } + graph.createNode(passName); + + if (loopStart == null && !pass.isOneTimePass()) { + loopStart = passName; + } else if (loopStart != null && pass.isOneTimePass()) { + graph.connect(lastPass, "loop", loopStart); + loopStart = null; + } + + if (lastPass != null) { + graph.connect(lastPass, "", passName); + } + lastPass = passName; + } + return graph; + } + + /** + * Create a type inference pass. + */ + final TypeInferencePass makeTypeInference(AbstractCompiler compiler) { + return new TypeInferencePass( + compiler, compiler.getReverseAbstractInterpreter(), + topScope, typedScopeCreator); + } + + final InferJSDocInfo makeInferJsDocInfo(AbstractCompiler compiler) { + return new InferJSDocInfo(compiler); + } + + /** + * Create a type-checking pass. + */ + final TypeCheck makeTypeCheck(AbstractCompiler compiler) { + return new TypeCheck( + compiler, + compiler.getReverseAbstractInterpreter(), + compiler.getTypeRegistry(), + topScope, + typedScopeCreator, + options.reportMissingOverride, + options.reportUnknownTypes) + .reportMissingProperties(options.enables( + DiagnosticGroup.forType(TypeCheck.INEXISTENT_PROPERTY))); + } + + /** + * Insert the given pass factory before the factory of the given name. + */ + final static void addPassFactoryBefore( + List factoryList, PassFactory factory, String passName) { + factoryList.add( + findPassIndexByName(factoryList, passName), factory); + } + + /** + * Find a pass factory with the same name as the given one, and replace it. + */ + final static void replacePassFactory( + List factoryList, PassFactory factory) { + factoryList.set( + findPassIndexByName(factoryList, factory.getName()), factory); + } + + /** + * Throws an exception if no pass with the given name exists. + */ + private static int findPassIndexByName( + List factoryList, String name) { + for (int i = 0; i < factoryList.size(); i++) { + if (factoryList.get(i).getName().equals(name)) { + return i; + } + } + + throw new IllegalArgumentException( + "No factory named '" + name + "' in the factory list"); + } + + /** + * Find the first pass provider that does not have a delegate. + */ + final PassConfig getBasePassConfig() { + PassConfig current = this; + while (current instanceof PassConfigDelegate) { + current = ((PassConfigDelegate) current).delegate; + } + return current; + } + + /** + * Get intermediate state for a running pass config, so it can + * be paused and started again later. + */ + protected abstract State getIntermediateState(); + + /** + * Set the intermediate state for a pass config, to restart + * a compilation process that had been previously paused. + */ + protected abstract void setIntermediateState(State state); + + /** + * An implementation of PassConfig that just proxies all its method calls + * into an inner class. + */ + static class PassConfigDelegate extends PassConfig { + + private final PassConfig delegate; + + PassConfigDelegate(PassConfig delegate) { + super(delegate.options); + this.delegate = delegate; + } + + @Override protected List getChecks() { + return delegate.getChecks(); + } + + @Override protected List getOptimizations() { + return delegate.getOptimizations(); + } + + @Override MemoizedScopeCreator getTypedScopeCreator() { + return delegate.getTypedScopeCreator(); + } + + @Override Scope getTopScope() { + return delegate.getTopScope(); + } + + @Override protected State getIntermediateState() { + return delegate.getIntermediateState(); + } + + @Override protected void setIntermediateState(State state) { + delegate.setIntermediateState(state); + } + } + + /** + * Intermediate state for a running pass configuration. + */ + public static class State implements Serializable { + private static final long serialVersionUID = 1L; + + final Map cssNames; + final Set exportedNames; + final CrossModuleMethodMotion.IdGenerator crossModuleIdGenerator; + final VariableMap variableMap; + final VariableMap propertyMap; + final VariableMap anonymousFunctionNameMap; + final VariableMap stringMap; + final FunctionNames functionNames; + final String idGeneratorMap; + + public State(Map cssNames, Set exportedNames, + CrossModuleMethodMotion.IdGenerator crossModuleIdGenerator, + VariableMap variableMap, VariableMap propertyMap, + VariableMap anonymousFunctionNameMap, + VariableMap stringMap, FunctionNames functionNames, + String idGeneratorMap) { + this.cssNames = cssNames; + this.exportedNames = exportedNames; + this.crossModuleIdGenerator = crossModuleIdGenerator; + this.variableMap = variableMap; + this.propertyMap = propertyMap; + this.anonymousFunctionNameMap = anonymousFunctionNameMap; + this.stringMap = stringMap; + this.idGeneratorMap = idGeneratorMap; + this.functionNames = functionNames; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PassFactory.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PassFactory.java new file mode 100644 index 0000000..74ef6f4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PassFactory.java @@ -0,0 +1,73 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * A factory for creating JSCompiler passes based on the Options + * injected. Contains all meta-data about compiler passes (like + * whether it can be run multiple times, a human-readable name for + * logging, etc.). + * + * @author nicksantos@google.com (Nick Santos) + */ +public abstract class PassFactory { + + private final String name; + private final boolean isOneTimePass; + + /** + * @param name The name of the pass that this factory creates. + * @param isOneTimePass If true, the pass produced by this factory can + * only be run once. + */ + protected PassFactory(String name, boolean isOneTimePass) { + this.name = name; + this.isOneTimePass = isOneTimePass; + } + + /** + * @return The name of this pass. + */ + String getName() { + return name; + } + + /** + * @return Whether the pass produced by this factory can only be run once. + */ + boolean isOneTimePass() { + return isOneTimePass; + } + + /** + * Creates a new compiler pass to be run. + */ + abstract CompilerPass create(AbstractCompiler compiler); + + /** + * Any factory whose CompilerPass has a corresponding hot-swap version should + * override this. + * + * @param compiler The compiler that can has been used to do the full compile. + */ + HotSwapCompilerPass getHotSwapPass(AbstractCompiler compiler) { + // TODO(bashir): If in future most of PassFactory's in DefaultPassConfig + // turns out to be DefaultPassConfig.HotSwapPassFactory, we should probably + // change the implementation here by the one in HotSwapPassFactory. + return null; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeCollectPropertyAssignments.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeCollectPropertyAssignments.java new file mode 100644 index 0000000..decfc10 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeCollectPropertyAssignments.java @@ -0,0 +1,278 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.Node; + +/** + * A pass that looks for assignments to properties of an object or array + * immediately following its creation using the abbreviated syntax. + *

      + * E.g. {@code var a = [];a[0] = 0} is optimized to {@code var a = [0]} and + * similarly for the object constructor. + * + */ +public class PeepholeCollectPropertyAssignments + extends AbstractPeepholeOptimization { + + @Override + Node optimizeSubtree(Node subtree) { + if (!subtree.isScript() && !subtree.isBlock()) { + return subtree; + } + + boolean codeChanged = false; + + // Look for variable declarations or simple assignments + // and start processing there. + for (Node child = subtree.getFirstChild(); + child != null; child = child.getNext()) { + if (!child.isVar() && !NodeUtil.isExprAssign(child)) { + continue; + } + if (!isPropertyAssignmentToName(child.getNext())) { + // Quick check to see if there's anything to collapse. + continue; + } + + Preconditions.checkState(child.hasOneChild()); + Node name = getName(child); + if (!name.isName()) { + // The assignment target is not a simple name. + continue; + } + Node value = getValue(child); + if (value == null || !isInterestingValue(value)) { + // No initializer or not an Object or Array literal. + continue; + } + + Node propertyCandidate; + while ((propertyCandidate = child.getNext()) != null) { + // This does not infinitely loop because collectProperty always + // removes propertyCandidate from its parent when it returns true. + if (!collectProperty(propertyCandidate, name.getString(), value)) { + break; + } + codeChanged = true; + } + } + + if (codeChanged) { + reportCodeChange(); + } + return subtree; + } + + private Node getName(Node n) { + if (n.isVar()) { + return n.getFirstChild(); + } else if (NodeUtil.isExprAssign(n)) { + return n.getFirstChild().getFirstChild(); + } + throw new IllegalStateException(); + } + + private Node getValue(Node n) { + if (n.isVar()) { + return n.getFirstChild().getFirstChild(); + } else if (NodeUtil.isExprAssign(n)) { + return n.getFirstChild().getLastChild(); + } + throw new IllegalStateException(); + } + + boolean isInterestingValue(Node n) { + return n.isObjectLit() || n.isArrayLit(); + } + + private boolean isPropertyAssignmentToName(Node propertyCandidate) { + if (propertyCandidate == null) { return false; } + // Must be an assignment... + if (!NodeUtil.isExprAssign(propertyCandidate)) { + return false; + } + + Node expr = propertyCandidate.getFirstChild(); + + // to a property... + Node lhs = expr.getFirstChild(); + if (!NodeUtil.isGet(lhs)) { + return false; + } + + // of a variable. + Node obj = lhs.getFirstChild(); + if (!obj.isName()) { + return false; + } + + return true; + } + + private boolean collectProperty( + Node propertyCandidate, String name, Node value) { + if (!isPropertyAssignmentToName(propertyCandidate)) { + return false; + } + + Node lhs = propertyCandidate.getFirstChild().getFirstChild(); + // Must be an assignment to the recent variable... + if (!name.equals(lhs.getFirstChild().getString())) { + return false; + } + + Node rhs = lhs.getNext(); + // with a value that cannot change the values of the variables, + if (mayHaveSideEffects(rhs) + || NodeUtil.canBeSideEffected(rhs)) { + return false; + } + // and does not have a reference to a variable initialized after it. + if (!NodeUtil.isLiteralValue(rhs, true) + && mightContainForwardReference(rhs, name)) { + return false; + } + + switch (value.getType()) { + case Token.ARRAYLIT: + if (!collectArrayProperty(value, propertyCandidate)) { + return false; + } + break; + case Token.OBJECTLIT: + if (!collectObjectProperty(value, propertyCandidate)) { + return false; + } + break; + default: + throw new IllegalStateException(); + } + return true; + } + + + private boolean collectArrayProperty( + Node arrayLiteral, Node propertyCandidate) { + Node assignment = propertyCandidate.getFirstChild(); + final int sizeOfArrayAtStart = arrayLiteral.getChildCount(); + int maxIndexAssigned = sizeOfArrayAtStart - 1; + + Node lhs = assignment.getFirstChild(); + Node rhs = lhs.getNext(); + if (!lhs.isGetElem()) { + return false; + } + Node obj = lhs.getFirstChild(); + Node property = obj.getNext(); + // The left hand side must have a numeric index + if (!property.isNumber()) { + return false; + } + // that is a valid array index + double dindex = property.getDouble(); + if (!(dindex >= 0) // Handles NaN and negatives. + || Double.isInfinite(dindex) || dindex > 0x7fffffffL) { + return false; + } + int index = (int) dindex; + if (dindex != index) { + return false; + } + // that would not make the array so sparse that they take more space + // when rendered than x[9]=1. + if (maxIndexAssigned + 4 < index) { + return false; + } + if (index > maxIndexAssigned) { + while (maxIndexAssigned < index - 1) { + // Pad the array if it is sparse. + // So if array is [0] and integer 3 is assigned at index is 2, then + // we want to produce [0,,2]. + Node emptyNode = IR.empty().srcref(arrayLiteral); + arrayLiteral.addChildToBack(emptyNode); + ++maxIndexAssigned; + } + arrayLiteral.addChildToBack(rhs.detachFromParent()); + } else { + // An out of order assignment. Allow it if it's a hole. + Node currentValue = arrayLiteral.getChildAtIndex(index); + if (!currentValue.isEmpty()) { + // We've already collected a value for this index. + return false; + } + arrayLiteral.replaceChild(currentValue, rhs.detachFromParent()); + } + + propertyCandidate.detachFromParent(); + return true; + } + + private boolean collectObjectProperty( + Node objectLiteral, Node propertyCandidate) { + Node assignment = propertyCandidate.getFirstChild(); + Node lhs = assignment.getFirstChild(), rhs = lhs.getNext(); + Node obj = lhs.getFirstChild(); + Node property = obj.getNext(); + + // The property must be statically known. + if (lhs.isGetElem() + && (!property.isString() + && !property.isNumber())) { + return false; + } + + String propertyName; + if (property.isNumber()) { + propertyName = NodeUtil.getStringValue(property); + } else { + propertyName = property.getString(); + } + + Node newProperty = IR.stringKey(propertyName) + .copyInformationFrom(property); + // Preserve the quotedness of a property reference + if (lhs.isGetElem()) { + newProperty.setQuotedString(); + } + Node newValue = rhs.detachFromParent(); + newProperty.addChildToBack(newValue); + objectLiteral.addChildToBack(newProperty); + + propertyCandidate.detachFromParent(); + return true; + } + + + private static boolean mightContainForwardReference( + Node node, String varName) { + if (node.isName()) { + return varName.equals(node.getString()); + } + for (Node child = node.getFirstChild(); child != null; + child = child.getNext()) { + if (mightContainForwardReference(child, varName)) { + return true; + } + } + return false; + } + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeFoldConstants.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeFoldConstants.java new file mode 100644 index 0000000..7378f31 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeFoldConstants.java @@ -0,0 +1,1508 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.head.ScriptRuntime; +import com.google.javascript.rhino.jstype.TernaryValue; + +/** + * Peephole optimization to fold constants (e.g. x + 1 + 7 --> x + 8). + * + */ +class PeepholeFoldConstants extends AbstractPeepholeOptimization { + + // TODO(johnlenz): optimizations should not be emiting errors. Move these to + // a check pass. + static final DiagnosticType INVALID_GETELEM_INDEX_ERROR = + DiagnosticType.warning( + "JSC_INVALID_GETELEM_INDEX_ERROR", + "Array index not integer: {0}"); + + static final DiagnosticType INDEX_OUT_OF_BOUNDS_ERROR = + DiagnosticType.warning( + "JSC_INDEX_OUT_OF_BOUNDS_ERROR", + "Array index out of bounds: {0}"); + + static final DiagnosticType NEGATING_A_NON_NUMBER_ERROR = + DiagnosticType.warning( + "JSC_NEGATING_A_NON_NUMBER_ERROR", + "Can't negate non-numeric value: {0}"); + + static final DiagnosticType BITWISE_OPERAND_OUT_OF_RANGE = + DiagnosticType.warning( + "JSC_BITWISE_OPERAND_OUT_OF_RANGE", + "Operand out of range, bitwise operation will lose information: {0}"); + + static final DiagnosticType SHIFT_AMOUNT_OUT_OF_BOUNDS = + DiagnosticType.warning( + "JSC_SHIFT_AMOUNT_OUT_OF_BOUNDS", + "Shift amount out of bounds: {0}"); + + static final DiagnosticType FRACTIONAL_BITWISE_OPERAND = + DiagnosticType.warning( + "JSC_FRACTIONAL_BITWISE_OPERAND", + "Fractional bitwise operand: {0}"); + + private static final double MAX_FOLD_NUMBER = Math.pow(2, 53); + + private final boolean late; + + /** + * @param late When late is false, this mean we are currently running before + * most of the other optimizations. In this case we would avoid optimizations + * that would make the code harder to analyze. When this is true, we would + * do anything to minimize for size. + */ + PeepholeFoldConstants(boolean late) { + this.late = late; + } + + @Override + Node optimizeSubtree(Node subtree) { + switch(subtree.getType()) { + case Token.NEW: + return tryFoldCtorCall(subtree); + + case Token.TYPEOF: + return tryFoldTypeof(subtree); + + case Token.NOT: + case Token.POS: + case Token.NEG: + case Token.BITNOT: + tryReduceOperandsForOp(subtree); + return tryFoldUnaryOperator(subtree); + + case Token.VOID: + return tryReduceVoid(subtree); + + default: + tryReduceOperandsForOp(subtree); + return tryFoldBinaryOperator(subtree); + } + } + + private Node tryFoldBinaryOperator(Node subtree) { + Node left = subtree.getFirstChild(); + + if (left == null) { + return subtree; + } + + Node right = left.getNext(); + + if (right == null) { + return subtree; + } + + // If we've reached here, node is truly a binary operator. + switch(subtree.getType()) { + case Token.GETPROP: + return tryFoldGetProp(subtree, left, right); + + case Token.GETELEM: + return tryFoldGetElem(subtree, left, right); + + case Token.INSTANCEOF: + return tryFoldInstanceof(subtree, left, right); + + case Token.AND: + case Token.OR: + return tryFoldAndOr(subtree, left, right); + + case Token.LSH: + case Token.RSH: + case Token.URSH: + return tryFoldShift(subtree, left, right); + + case Token.ASSIGN: + return tryFoldAssign(subtree, left, right); + + case Token.ASSIGN_BITOR: + case Token.ASSIGN_BITXOR: + case Token.ASSIGN_BITAND: + case Token.ASSIGN_LSH: + case Token.ASSIGN_RSH: + case Token.ASSIGN_URSH: + case Token.ASSIGN_ADD: + case Token.ASSIGN_SUB: + case Token.ASSIGN_MUL: + case Token.ASSIGN_DIV: + case Token.ASSIGN_MOD: + return tryUnfoldAssignOp(subtree, left, right); + + case Token.ADD: + return tryFoldAdd(subtree, left, right); + + case Token.SUB: + case Token.DIV: + case Token.MOD: + return tryFoldArithmeticOp(subtree, left, right); + + case Token.MUL: + case Token.BITAND: + case Token.BITOR: + case Token.BITXOR: + Node result = tryFoldArithmeticOp(subtree, left, right); + if (result != subtree) { + return result; + } + return tryFoldLeftChildOp(subtree, left, right); + + case Token.LT: + case Token.GT: + case Token.LE: + case Token.GE: + case Token.EQ: + case Token.NE: + case Token.SHEQ: + case Token.SHNE: + return tryFoldComparison(subtree, left, right); + + default: + return subtree; + } + } + + private Node tryReduceVoid(Node n) { + Node child = n.getFirstChild(); + if (!child.isNumber() || child.getDouble() != 0.0) { + if (!mayHaveSideEffects(n)) { + n.replaceChild(child, IR.number(0)); + reportCodeChange(); + } + } + return n; + } + + private void tryReduceOperandsForOp(Node n) { + switch (n.getType()) { + case Token.ADD: + Node left = n.getFirstChild(); + Node right = n.getLastChild(); + if (!NodeUtil.mayBeString(left) && !NodeUtil.mayBeString(right)) { + tryConvertOperandsToNumber(n); + } + break; + case Token.ASSIGN_BITOR: + case Token.ASSIGN_BITXOR: + case Token.ASSIGN_BITAND: + // TODO(johnlenz): convert these to integers. + case Token.ASSIGN_LSH: + case Token.ASSIGN_RSH: + case Token.ASSIGN_URSH: + case Token.ASSIGN_SUB: + case Token.ASSIGN_MUL: + case Token.ASSIGN_MOD: + case Token.ASSIGN_DIV: + tryConvertToNumber(n.getLastChild()); + break; + case Token.BITNOT: + case Token.BITOR: + case Token.BITXOR: + case Token.BITAND: + case Token.LSH: + case Token.RSH: + case Token.URSH: + case Token.SUB: + case Token.MUL: + case Token.MOD: + case Token.DIV: + case Token.POS: + case Token.NEG: + tryConvertOperandsToNumber(n); + break; + } + } + + private void tryConvertOperandsToNumber(Node n) { + Node next; + for (Node c = n.getFirstChild(); c != null; c = next) { + next = c.getNext(); + tryConvertToNumber(c); + } + } + + private void tryConvertToNumber(Node n) { + switch (n.getType()) { + case Token.NUMBER: + // Nothing to do + return; + case Token.AND: + case Token.OR: + case Token.COMMA: + tryConvertToNumber(n.getLastChild()); + return; + case Token.HOOK: + tryConvertToNumber(n.getChildAtIndex(1)); + tryConvertToNumber(n.getLastChild()); + return; + case Token.NAME: + if (!NodeUtil.isUndefined(n)) { + return; + } + break; + } + + Double result = NodeUtil.getNumberValue(n); + if (result == null) { + return; + } + + double value = result; + + Node replacement = NodeUtil.numberNode(value, n); + if (replacement.isEquivalentTo(n)) { + return; + } + + n.getParent().replaceChild(n, replacement); + reportCodeChange(); + } + + /** + * Folds 'typeof(foo)' if foo is a literal, e.g. + * typeof("bar") --> "string" + * typeof(6) --> "number" + */ + private Node tryFoldTypeof(Node originalTypeofNode) { + Preconditions.checkArgument(originalTypeofNode.isTypeOf()); + + Node argumentNode = originalTypeofNode.getFirstChild(); + if (argumentNode == null || !NodeUtil.isLiteralValue(argumentNode, true)) { + return originalTypeofNode; + } + + String typeNameString = null; + + switch (argumentNode.getType()) { + case Token.FUNCTION: + typeNameString = "function"; + break; + case Token.STRING: + typeNameString = "string"; + break; + case Token.NUMBER: + typeNameString = "number"; + break; + case Token.TRUE: + case Token.FALSE: + typeNameString = "boolean"; + break; + case Token.NULL: + case Token.OBJECTLIT: + case Token.ARRAYLIT: + typeNameString = "object"; + break; + case Token.VOID: + typeNameString = "undefined"; + break; + case Token.NAME: + // We assume here that programs don't change the value of the + // keyword undefined to something other than the value undefined. + if ("undefined".equals(argumentNode.getString())) { + typeNameString = "undefined"; + } + break; + } + + if (typeNameString != null) { + Node newNode = IR.string(typeNameString); + originalTypeofNode.getParent().replaceChild(originalTypeofNode, newNode); + reportCodeChange(); + + return newNode; + } + + return originalTypeofNode; + } + + private Node tryFoldUnaryOperator(Node n) { + Preconditions.checkState(n.hasOneChild()); + + Node left = n.getFirstChild(); + Node parent = n.getParent(); + + if (left == null) { + return n; + } + + TernaryValue leftVal = NodeUtil.getPureBooleanValue(left); + if (leftVal == TernaryValue.UNKNOWN) { + return n; + } + + switch (n.getType()) { + case Token.NOT: + // Don't fold !0 and !1 back to false. + if (late && left.isNumber()) { + double numValue = left.getDouble(); + if (numValue == 0 || numValue == 1) { + return n; + } + } + Node replacementNode = NodeUtil.booleanNode(!leftVal.toBoolean(true)); + parent.replaceChild(n, replacementNode); + reportCodeChange(); + return replacementNode; + case Token.POS: + if (NodeUtil.isNumericResult(left)) { + // POS does nothing to numeric values. + parent.replaceChild(n, left.detachFromParent()); + reportCodeChange(); + return left; + } + return n; + case Token.NEG: + if (left.isName()) { + if (left.getString().equals("Infinity")) { + // "-Infinity" is valid and a literal, don't modify it. + return n; + } else if (left.getString().equals("NaN")) { + // "-NaN" is "NaN". + n.removeChild(left); + parent.replaceChild(n, left); + reportCodeChange(); + return left; + } + } + + if (left.isNumber()) { + double negNum = -left.getDouble(); + + Node negNumNode = IR.number(negNum); + parent.replaceChild(n, negNumNode); + reportCodeChange(); + return negNumNode; + } else { + // left is not a number node, so do not replace, but warn the + // user because they can't be doing anything good + report(NEGATING_A_NON_NUMBER_ERROR, left); + return n; + } + case Token.BITNOT: + try { + double val = left.getDouble(); + if (val >= Integer.MIN_VALUE && val <= Integer.MAX_VALUE) { + int intVal = (int) val; + if (intVal == val) { + Node notIntValNode = IR.number(~intVal); + parent.replaceChild(n, notIntValNode); + reportCodeChange(); + return notIntValNode; + } else { + report(FRACTIONAL_BITWISE_OPERAND, left); + return n; + } + } else { + report(BITWISE_OPERAND_OUT_OF_RANGE, left); + return n; + } + } catch (UnsupportedOperationException ex) { + // left is not a number node, so do not replace, but warn the + // user because they can't be doing anything good + report(NEGATING_A_NON_NUMBER_ERROR, left); + return n; + } + default: + return n; + } + } + + /** + * Try to fold {@code left instanceof right} into {@code true} + * or {@code false}. + */ + private Node tryFoldInstanceof(Node n, Node left, Node right) { + Preconditions.checkArgument(n.isInstanceOf()); + + // TODO(johnlenz) Use type information if available to fold + // instanceof. + if (NodeUtil.isLiteralValue(left, true) + && !mayHaveSideEffects(right)) { + + Node replacementNode = null; + + if (NodeUtil.isImmutableValue(left)) { + // Non-object types are never instances. + replacementNode = IR.falseNode(); + } else if (right.isName() + && "Object".equals(right.getString())) { + replacementNode = IR.trueNode(); + } + + if (replacementNode != null) { + n.getParent().replaceChild(n, replacementNode); + reportCodeChange(); + return replacementNode; + } + } + + return n; + } + + private Node tryFoldAssign(Node n, Node left, Node right) { + Preconditions.checkArgument(n.isAssign()); + + if (!late) { + return n; + } + + // Tries to convert x = x + y -> x += y; + if (!right.hasChildren() || + right.getFirstChild().getNext() != right.getLastChild()) { + // RHS must have two children. + return n; + } + + if (mayHaveSideEffects(left)) { + return n; + } + + Node newRight; + if (areNodesEqualForInlining(left, right.getFirstChild())) { + newRight = right.getLastChild(); + } else if (NodeUtil.isCommutative(right.getType()) && + areNodesEqualForInlining(left, right.getLastChild())) { + newRight = right.getFirstChild(); + } else { + return n; + } + + int newType = -1; + switch (right.getType()) { + case Token.ADD: + newType = Token.ASSIGN_ADD; + break; + case Token.BITAND: + newType = Token.ASSIGN_BITAND; + break; + case Token.BITOR: + newType = Token.ASSIGN_BITOR; + break; + case Token.BITXOR: + newType = Token.ASSIGN_BITXOR; + break; + case Token.DIV: + newType = Token.ASSIGN_DIV; + break; + case Token.LSH: + newType = Token.ASSIGN_LSH; + break; + case Token.MOD: + newType = Token.ASSIGN_MOD; + break; + case Token.MUL: + newType = Token.ASSIGN_MUL; + break; + case Token.RSH: + newType = Token.ASSIGN_RSH; + break; + case Token.SUB: + newType = Token.ASSIGN_SUB; + break; + case Token.URSH: + newType = Token.ASSIGN_URSH; + break; + default: + return n; + } + + Node newNode = new Node(newType, + left.detachFromParent(), newRight.detachFromParent()); + n.getParent().replaceChild(n, newNode); + + reportCodeChange(); + + return newNode; + } + + private Node tryUnfoldAssignOp(Node n, Node left, Node right) { + if (late) { + return n; + } + + if (!n.hasChildren() || + n.getFirstChild().getNext() != n.getLastChild()) { + return n; + } + + if (mayHaveSideEffects(left)) { + return n; + } + + // Tries to convert x += y -> x = x + y; + int op = NodeUtil.getOpFromAssignmentOp(n); + Node replacement = IR.assign(left.detachFromParent(), + new Node(op, left.cloneTree(), right.detachFromParent()) + .srcref(n)); + n.getParent().replaceChild(n, replacement); + reportCodeChange(); + + return replacement; + } + + /** + * Try to fold a AND/OR node. + */ + private Node tryFoldAndOr(Node n, Node left, Node right) { + Node parent = n.getParent(); + + Node result = null; + + int type = n.getType(); + + TernaryValue leftVal = NodeUtil.getImpureBooleanValue(left); + + if (leftVal != TernaryValue.UNKNOWN) { + boolean lval = leftVal.toBoolean(true); + + // (TRUE || x) => TRUE (also, (3 || x) => 3) + // (FALSE && x) => FALSE + if (lval && type == Token.OR || + !lval && type == Token.AND) { + result = left; + + } else if (!mayHaveSideEffects(left)) { + // (FALSE || x) => x + // (TRUE && x) => x + result = right; + } + } + + // Note: Right hand side folding is handled by + // PeepholeSubstituteAlternateSyntax#tryMinimizeCondition + + if (result != null) { + // Fold it! + n.removeChild(result); + parent.replaceChild(n, result); + reportCodeChange(); + + return result; + } else { + return n; + } + } + + /** + * Expressions such as [foo() + 'a' + 'b'] generate parse trees + * where no node has two const children ((foo() + 'a') + 'b'), so + * tryFoldAdd() won't fold it -- tryFoldLeftChildAdd() will (for Strings). + * Specifically, it folds Add expressions where: + * - The left child is also and add expression + * - The right child is a constant value + * - The left child's right child is a STRING constant. + */ + private Node tryFoldChildAddString(Node n, Node left, Node right) { + + if (NodeUtil.isLiteralValue(right, false) && + left.isAdd()) { + + Node ll = left.getFirstChild(); + Node lr = ll.getNext(); + + // Left's right child MUST be a string. We would not want to fold + // foo() + 2 + 'a' because we don't know what foo() will return, and + // therefore we don't know if left is a string concat, or a numeric add. + if (lr.isString()) { + String leftString = NodeUtil.getStringValue(lr); + String rightString = NodeUtil.getStringValue(right); + if (leftString != null && rightString != null) { + left.removeChild(ll); + String result = leftString + rightString; + n.replaceChild(left, ll); + n.replaceChild(right, IR.string(result)); + reportCodeChange(); + return n; + } + } + } + + if (NodeUtil.isLiteralValue(left, false) && + right.isAdd()) { + + Node rl = right.getFirstChild(); + Node rr = right.getLastChild(); + + // Left's right child MUST be a string. We would not want to fold + // foo() + 2 + 'a' because we don't know what foo() will return, and + // therefore we don't know if left is a string concat, or a numeric add. + if (rl.isString()) { + String leftString = NodeUtil.getStringValue(left); + String rightString = NodeUtil.getStringValue(rl); + if (leftString != null && rightString != null) { + right.removeChild(rr); + String result = leftString + rightString; + n.replaceChild(right, rr); + n.replaceChild(left, IR.string(result)); + reportCodeChange(); + return n; + } + } + } + + return n; + } + + /** + * Try to fold an ADD node with constant operands + */ + private Node tryFoldAddConstantString(Node n, Node left, Node right) { + if (left.isString() || + right.isString()) { + // Add strings. + String leftString = NodeUtil.getStringValue(left); + String rightString = NodeUtil.getStringValue(right); + if (leftString != null && rightString != null) { + Node newStringNode = IR.string(leftString + rightString); + n.getParent().replaceChild(n, newStringNode); + reportCodeChange(); + return newStringNode; + } + } + + + + return n; + } + + /** + * Try to fold arithmetic binary operators + */ + private Node tryFoldArithmeticOp(Node n, Node left, Node right) { + Node result = performArithmeticOp(n.getType(), left, right); + if (result != null) { + result.copyInformationFromForTree(n); + n.getParent().replaceChild(n, result); + reportCodeChange(); + return result; + } + return n; + } + + /** + * Try to fold arithmetic binary operators + */ + private Node performArithmeticOp(int opType, Node left, Node right) { + // Unlike other operations, ADD operands are not always converted + // to Number. + if (opType == Token.ADD + && (NodeUtil.mayBeString(left, false) + || NodeUtil.mayBeString(right, false))) { + return null; + } + + double result; + + // TODO(johnlenz): Handle NaN with unknown value. BIT ops convert NaN + // to zero so this is a little awkward here. + + Double lValObj = NodeUtil.getNumberValue(left); + if (lValObj == null) { + return null; + } + Double rValObj = NodeUtil.getNumberValue(right); + if (rValObj == null) { + return null; + } + + double lval = lValObj; + double rval = rValObj; + + switch (opType) { + case Token.BITAND: + result = ScriptRuntime.toInt32(lval) & ScriptRuntime.toInt32(rval); + break; + case Token.BITOR: + result = ScriptRuntime.toInt32(lval) | ScriptRuntime.toInt32(rval); + break; + case Token.BITXOR: + result = ScriptRuntime.toInt32(lval) ^ ScriptRuntime.toInt32(rval); + break; + case Token.ADD: + result = lval + rval; + break; + case Token.SUB: + result = lval - rval; + break; + case Token.MUL: + result = lval * rval; + break; + case Token.MOD: + if (rval == 0) { + return null; + } + result = lval % rval; + break; + case Token.DIV: + if (rval == 0) { + return null; + } + result = lval / rval; + break; + default: + throw new Error("Unexpected arithmetic operator"); + } + + // TODO(johnlenz): consider removing the result length check. + // length of the left and right value plus 1 byte for the operator. + if ((String.valueOf(result).length() <= + String.valueOf(lval).length() + String.valueOf(rval).length() + 1 + + // Do not try to fold arithmetic for numbers > 2^53. After that + // point, fixed-point math starts to break down and become inaccurate. + && Math.abs(result) <= MAX_FOLD_NUMBER) + || Double.isNaN(result) + || result == Double.POSITIVE_INFINITY + || result == Double.NEGATIVE_INFINITY) { + return NodeUtil.numberNode(result, null); + } + return null; + } + + /** + * Expressions such as [foo() * 10 * 20] generate parse trees + * where no node has two const children ((foo() * 10) * 20), so + * performArithmeticOp() won't fold it -- tryFoldLeftChildOp() will. + * Specifically, it folds associative expressions where: + * - The left child is also an associative expression of the same time. + * - The right child is a constant NUMBER constant. + * - The left child's right child is a NUMBER constant. + */ + private Node tryFoldLeftChildOp(Node n, Node left, Node right) { + int opType = n.getType(); + Preconditions.checkState( + (NodeUtil.isAssociative(opType) && NodeUtil.isCommutative(opType)) + || n.isAdd()); + + Preconditions.checkState( + !n.isAdd()|| !NodeUtil.mayBeString(n)); + + // Use getNumberValue to handle constants like "NaN" and "Infinity" + // other values are converted to numbers elsewhere. + Double rightValObj = NodeUtil.getNumberValue(right); + if (rightValObj != null && left.getType() == opType) { + Preconditions.checkState(left.getChildCount() == 2); + + Node ll = left.getFirstChild(); + Node lr = ll.getNext(); + + Node valueToCombine = ll; + Node replacement = performArithmeticOp(opType, valueToCombine, right); + if (replacement == null) { + valueToCombine = lr; + replacement = performArithmeticOp(opType, valueToCombine, right); + } + if (replacement != null) { + // Remove the child that has been combined + left.removeChild(valueToCombine); + // Replace the left op with the remaining child. + n.replaceChild(left, left.removeFirstChild()); + // New "-Infinity" node need location info explicitly + // added. + replacement.copyInformationFromForTree(right); + n.replaceChild(right, replacement); + reportCodeChange(); + } + } + + return n; + } + + private Node tryFoldAdd(Node node, Node left, Node right) { + Preconditions.checkArgument(node.isAdd()); + + if (NodeUtil.mayBeString(node, true)) { + if (NodeUtil.isLiteralValue(left, false) && + NodeUtil.isLiteralValue(right, false)) { + // '6' + 7 + return tryFoldAddConstantString(node, left, right); + } else { + // a + 7 or 6 + a + return tryFoldChildAddString(node, left, right); + } + } else { + // Try arithmetic add + Node result = tryFoldArithmeticOp(node, left, right); + if (result != node) { + return result; + } + return tryFoldLeftChildOp(node, left, right); + } + } + + /** + * Try to fold shift operations + */ + private Node tryFoldShift(Node n, Node left, Node right) { + if (left.isNumber() && + right.isNumber()) { + + double result; + double lval = left.getDouble(); + double rval = right.getDouble(); + + // check ranges. We do not do anything that would clip the double to + // a 32-bit range, since the user likely does not intend that. + if (!(lval >= Integer.MIN_VALUE && lval <= Integer.MAX_VALUE)) { + report(BITWISE_OPERAND_OUT_OF_RANGE, left); + return n; + } + + // only the lower 5 bits are used when shifting, so don't do anything + // if the shift amount is outside [0,32) + if (!(rval >= 0 && rval < 32)) { + report(SHIFT_AMOUNT_OUT_OF_BOUNDS, right); + return n; + } + + // Convert the numbers to ints + int lvalInt = (int) lval; + if (lvalInt != lval) { + report(FRACTIONAL_BITWISE_OPERAND, left); + return n; + } + + int rvalInt = (int) rval; + if (rvalInt != rval) { + report(FRACTIONAL_BITWISE_OPERAND, right); + return n; + } + + switch (n.getType()) { + case Token.LSH: + result = lvalInt << rvalInt; + break; + case Token.RSH: + result = lvalInt >> rvalInt; + break; + case Token.URSH: + // JavaScript handles zero shifts on signed numbers differently than + // Java as an Java int can not represent the unsigned 32-bit number + // where JavaScript can so use a long here. + long lvalLong = lvalInt & 0xffffffffL; + result = lvalLong >>> rvalInt; + break; + default: + throw new AssertionError("Unknown shift operator: " + + Token.name(n.getType())); + } + + Node newNumber = IR.number(result); + n.getParent().replaceChild(n, newNumber); + reportCodeChange(); + + return newNumber; + } + + return n; + } + + /** + * Try to fold comparison nodes, e.g == + */ + @SuppressWarnings("fallthrough") + private Node tryFoldComparison(Node n, Node left, Node right) { + TernaryValue result = evaluateComparison(n.getType(), left, right); + if (result == TernaryValue.UNKNOWN) { + return n; + } + + Node newNode = NodeUtil.booleanNode(result.toBoolean(true)); + n.getParent().replaceChild(n, newNode); + reportCodeChange(); + + return newNode; + } + + static TernaryValue evaluateComparison(int op, Node left, Node right) { + boolean leftLiteral = NodeUtil.isLiteralValue(left, true); + boolean rightLiteral = NodeUtil.isLiteralValue(right, true); + + if (!leftLiteral || !rightLiteral) { + // We only handle literal operands for LT and GT. + if (op != Token.GT && op != Token.LT) { + return TernaryValue.UNKNOWN; + } + } + + boolean undefinedRight = NodeUtil.isUndefined(right) && rightLiteral; + boolean nullRight = right.isNull(); + int lhType = getNormalizedNodeType(left); + int rhType = getNormalizedNodeType(right); + switch (lhType) { + case Token.VOID: + if (!leftLiteral) { + return TernaryValue.UNKNOWN; + } else if (!rightLiteral) { + return TernaryValue.UNKNOWN; + } else { + return TernaryValue.forBoolean(compareToUndefined(right, op)); + } + + case Token.NULL: + if (rightLiteral && isEqualityOp(op)) { + return TernaryValue.forBoolean(compareToNull(right, op)); + } + // fallthrough + case Token.TRUE: + case Token.FALSE: + if (undefinedRight) { + return TernaryValue.forBoolean(compareToUndefined(left, op)); + } + if (rhType != Token.TRUE && + rhType != Token.FALSE && + rhType != Token.NULL) { + return TernaryValue.UNKNOWN; + } + switch (op) { + case Token.SHEQ: + case Token.EQ: + return TernaryValue.forBoolean(lhType == rhType); + + case Token.SHNE: + case Token.NE: + return TernaryValue.forBoolean(lhType != rhType); + + case Token.GE: + case Token.LE: + case Token.GT: + case Token.LT: + return compareAsNumbers(op, left, right); + } + return TernaryValue.UNKNOWN; + + case Token.THIS: + if (!right.isThis()) { + return TernaryValue.UNKNOWN; + } + switch (op) { + case Token.SHEQ: + case Token.EQ: + return TernaryValue.TRUE; + + case Token.SHNE: + case Token.NE: + return TernaryValue.FALSE; + } + + // We can only handle == and != here. + // GT, LT, GE, LE depend on the type of "this" and how it will + // be converted to number. The results are different depending on + // whether it is a string, NaN or other number value. + return TernaryValue.UNKNOWN; + + case Token.STRING: + if (undefinedRight) { + return TernaryValue.forBoolean(compareToUndefined(left, op)); + } + if (nullRight && isEqualityOp(op)) { + return TernaryValue.forBoolean(compareToNull(left, op)); + } + if (Token.STRING != right.getType()) { + return TernaryValue.UNKNOWN; // Only eval if they are the same type + } + + switch (op) { + case Token.SHEQ: + case Token.EQ: + return areStringsEqual(left.getString(), right.getString()); + + case Token.SHNE: + case Token.NE: + return areStringsEqual(left.getString(), right.getString()).not(); + } + + return TernaryValue.UNKNOWN; + + case Token.NUMBER: + if (undefinedRight) { + return TernaryValue.forBoolean(compareToUndefined(left, op)); + } + if (nullRight && isEqualityOp(op)) { + return TernaryValue.forBoolean(compareToNull(left, op)); + } + if (Token.NUMBER != right.getType()) { + return TernaryValue.UNKNOWN; // Only eval if they are the same type + } + return compareAsNumbers(op, left, right); + + case Token.NAME: + if (leftLiteral && undefinedRight) { + return TernaryValue.forBoolean(compareToUndefined(left, op)); + } + + if (rightLiteral) { + boolean undefinedLeft = (left.getString().equals("undefined")); + if (undefinedLeft) { + return TernaryValue.forBoolean(compareToUndefined(right, op)); + } + if (leftLiteral && nullRight && isEqualityOp(op)) { + return TernaryValue.forBoolean(compareToNull(left, op)); + } + } + + if (Token.NAME != right.getType()) { + return TernaryValue.UNKNOWN; // Only eval if they are the same type + } + String ln = left.getString(); + String rn = right.getString(); + if (!ln.equals(rn)) { + return TernaryValue.UNKNOWN; // Not the same value name. + } + + switch (op) { + // If we knew the named value wouldn't be NaN, it would be nice + // to handle EQ,NE,LE,GE,SHEQ, and SHNE. + case Token.LT: + case Token.GT: + return TernaryValue.FALSE; + } + + return TernaryValue.UNKNOWN; // don't handle that op + + case Token.NEG: + if (leftLiteral) { + if (undefinedRight) { + return TernaryValue.forBoolean(compareToUndefined(left, op)); + } + if (nullRight && isEqualityOp(op)) { + return TernaryValue.forBoolean(compareToNull(left, op)); + } + } + // Nothing else for now. + return TernaryValue.UNKNOWN; + + case Token.ARRAYLIT: + case Token.OBJECTLIT: + case Token.REGEXP: + case Token.FUNCTION: + if (leftLiteral) { + if (undefinedRight) { + return TernaryValue.forBoolean(compareToUndefined(left, op)); + } + if (nullRight && isEqualityOp(op)) { + return TernaryValue.forBoolean(compareToNull(left, op)); + } + } + // ignore the rest for now. + return TernaryValue.UNKNOWN; + + default: + // assert, this should cover all consts + return TernaryValue.UNKNOWN; + } + } + + /** Returns whether two JS strings are equal. */ + private static TernaryValue areStringsEqual(String a, String b) { + // In JS, browsers parse \v differently. So do not consider strings + // equal if one contains \v. + if (a.indexOf('\u000B') != -1 || + b.indexOf('\u000B') != -1) { + return TernaryValue.UNKNOWN; + } else { + return a.equals(b) ? TernaryValue.TRUE : TernaryValue.FALSE; + } + } + + /** + * @return Translate NOT expressions into TRUE or FALSE when possible. + */ + private static int getNormalizedNodeType(Node n) { + int type = n.getType(); + if (type == Token.NOT) { + TernaryValue value = NodeUtil.getPureBooleanValue(n); + switch (value) { + case TRUE: + return Token.TRUE; + case FALSE: + return Token.FALSE; + case UNKNOWN: + return type; + } + } + return type; + } + + /** + * The result of the comparison, or UNKNOWN if the + * result could not be determined. + */ + private static TernaryValue compareAsNumbers(int op, Node left, Node right) { + Double leftValue = NodeUtil.getNumberValue(left); + if (leftValue == null) { + return TernaryValue.UNKNOWN; + } + Double rightValue = NodeUtil.getNumberValue(right); + if (rightValue == null) { + return TernaryValue.UNKNOWN; + } + + double lv = leftValue; + double rv = rightValue; + + switch (op) { + case Token.SHEQ: + case Token.EQ: + Preconditions.checkState( + left.isNumber() && right.isNumber()); + return TernaryValue.forBoolean(lv == rv); + case Token.SHNE: + case Token.NE: + Preconditions.checkState( + left.isNumber() && right.isNumber()); + return TernaryValue.forBoolean(lv != rv); + case Token.LE: + return TernaryValue.forBoolean(lv <= rv); + case Token.LT: + return TernaryValue.forBoolean(lv < rv); + case Token.GE: + return TernaryValue.forBoolean(lv >= rv); + case Token.GT: + return TernaryValue.forBoolean(lv > rv); + default: + return TernaryValue.UNKNOWN; // don't handle that op + } + } + + /** + * @param value The value to compare to "undefined" + * @param op The boolean op to compare with + * @return Whether the boolean op is true or false + */ + private static boolean compareToUndefined(Node value, int op) { + Preconditions.checkState(NodeUtil.isLiteralValue(value, true)); + boolean valueUndefined = NodeUtil.isUndefined(value); + boolean valueNull = (Token.NULL == value.getType()); + boolean equivalent = valueUndefined || valueNull; + switch (op) { + case Token.EQ: + // undefined is only equal to null or an undefined value + return equivalent; + case Token.NE: + return !equivalent; + case Token.SHEQ: + return valueUndefined; + case Token.SHNE: + return !valueUndefined; + case Token.LT: + case Token.GT: + case Token.LE: + case Token.GE: + return false; + default: + throw new IllegalStateException("unexpected."); + } + } + + private static boolean isEqualityOp(int op) { + switch (op) { + case Token.EQ: + case Token.NE: + case Token.SHEQ: + case Token.SHNE: + return true; + } + return false; + } + + /** + * @param value The value to compare to "null" + * @param op The boolean op to compare with + * @return Whether the boolean op is true or false + */ + private static boolean compareToNull(Node value, int op) { + boolean valueUndefined = NodeUtil.isUndefined(value); + boolean valueNull = (Token.NULL == value.getType()); + boolean equivalent = valueUndefined || valueNull; + switch (op) { + case Token.EQ: + // undefined is only equal to null or an undefined value + return equivalent; + case Token.NE: + return !equivalent; + case Token.SHEQ: + return valueNull; + case Token.SHNE: + return !valueNull; + default: + throw new IllegalStateException("unexpected."); + } + } + + /** + * Try to fold away unnecessary object instantiation. + * e.g. this[new String('eval')] -> this.eval + */ + private Node tryFoldCtorCall(Node n) { + Preconditions.checkArgument(n.isNew()); + + // we can remove this for GETELEM calls (anywhere else?) + if (inForcedStringContext(n)) { + return tryFoldInForcedStringContext(n); + } + return n; + } + + /** Returns whether this node must be coerced to a string. */ + private boolean inForcedStringContext(Node n) { + if (n.getParent().isGetElem() && + n.getParent().getLastChild() == n) { + return true; + } + + // we can fold in the case "" + new String("") + if (n.getParent().isAdd()) { + return true; + } + return false; + } + + private Node tryFoldInForcedStringContext(Node n) { + // For now, we only know how to fold ctors. + Preconditions.checkArgument(n.isNew()); + + Node objectType = n.getFirstChild(); + if (!objectType.isName()) { + return n; + } + + if (objectType.getString().equals("String")) { + Node value = objectType.getNext(); + String stringValue = null; + if (value == null) { + stringValue = ""; + } else { + if (!NodeUtil.isImmutableValue(value)) { + return n; + } + + stringValue = NodeUtil.getStringValue(value); + } + + if (stringValue == null) { + return n; + } + + Node parent = n.getParent(); + Node newString = IR.string(stringValue); + + parent.replaceChild(n, newString); + newString.copyInformationFrom(parent); + reportCodeChange(); + + return newString; + } + return n; + } + + /** + * Try to fold array-element. e.g [1, 2, 3][10]; + */ + private Node tryFoldGetElem(Node n, Node left, Node right) { + Preconditions.checkArgument(n.isGetElem()); + + if (left.isObjectLit()) { + return tryFoldObjectPropAccess(n, left, right); + } + + if (left.isArrayLit()) { + return tryFoldArrayAccess(n, left, right); + } + return n; + } + + /** + * Try to fold array-length. e.g [1, 2, 3].length ==> 3, [x, y].length ==> 2 + */ + private Node tryFoldGetProp(Node n, Node left, Node right) { + Preconditions.checkArgument(n.isGetProp()); + + if (left.isObjectLit()) { + return tryFoldObjectPropAccess(n, left, right); + } + + if (right.isString() && + right.getString().equals("length")) { + int knownLength = -1; + switch (left.getType()) { + case Token.ARRAYLIT: + if (mayHaveSideEffects(left)) { + // Nope, can't fold this, without handling the side-effects. + return n; + } + knownLength = left.getChildCount(); + break; + case Token.STRING: + knownLength = left.getString().length(); + break; + default: + // Not a foldable case, forget it. + return n; + } + + Preconditions.checkState(knownLength != -1); + Node lengthNode = IR.number(knownLength); + n.getParent().replaceChild(n, lengthNode); + reportCodeChange(); + + return lengthNode; + } + + return n; + } + + private boolean isAssignmentTarget(Node n) { + Node parent = n.getParent(); + if ((NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n) + || parent.isInc() + || parent.isDec()) { + // If GETPROP/GETELEM is used as assignment target the object literal is + // acting as a temporary we can't fold it here: + // "{a:x}.a += 1" is not "x += 1" + return true; + } + return false; + } + + private Node tryFoldArrayAccess(Node n, Node left, Node right) { + Node parent = n.getParent(); + // If GETPROP/GETELEM is used as assignment target the array literal is + // acting as a temporary we can't fold it here: + // "[][0] += 1" + if (isAssignmentTarget(n)) { + return n; + } + + if (!right.isNumber()) { + // Sometimes people like to use complex expressions to index into + // arrays, or strings to index into array methods. + return n; + } + + double index = right.getDouble(); + int intIndex = (int) index; + if (intIndex != index) { + report(INVALID_GETELEM_INDEX_ERROR, right); + return n; + } + + if (intIndex < 0) { + report(INDEX_OUT_OF_BOUNDS_ERROR, right); + return n; + } + + Node current = left.getFirstChild(); + Node elem = null; + for (int i = 0; current != null; i++) { + if (i != intIndex) { + if (mayHaveSideEffects(current)) { + return n; + } + } else { + elem = current; + } + + current = current.getNext(); + } + + if (elem == null) { + report(INDEX_OUT_OF_BOUNDS_ERROR, right); + return n; + } + + if (elem.isEmpty()) { + elem = NodeUtil.newUndefinedNode(elem); + } else { + left.removeChild(elem); + } + + // Replace the entire GETELEM with the value + n.getParent().replaceChild(n, elem); + reportCodeChange(); + return elem; + } + + private Node tryFoldObjectPropAccess(Node n, Node left, Node right) { + Preconditions.checkArgument(NodeUtil.isGet(n)); + + if (!left.isObjectLit() || !right.isString()) { + return n; + } + + if (isAssignmentTarget(n)) { + // If GETPROP/GETELEM is used as assignment target the object literal is + // acting as a temporary we can't fold it here: + // "{a:x}.a += 1" is not "x += 1" + return n; + } + + // find the last definition in the object literal + Node key = null; + Node value = null; + for (Node c = left.getFirstChild(); c != null; c = c.getNext()) { + if (c.getString().equals(right.getString())) { + switch (c.getType()) { + case Token.SETTER_DEF: + continue; + case Token.GETTER_DEF: + case Token.STRING_KEY: + if (value != null && mayHaveSideEffects(value)) { + // The previously found value had side-effects + return n; + } + key = c; + value = key.getFirstChild(); + break; + default: + throw new IllegalStateException(); + } + } else if (mayHaveSideEffects(c.getFirstChild())) { + // We don't handle the side-effects here as they might need a temporary + // or need to be reordered. + return n; + } + } + + // Didn't find a definition of the name in the object literal, it might + // be coming from the Object prototype + if (value == null) { + return n; + } + + if (value.isFunction() && NodeUtil.referencesThis(value)) { + // 'this' may refer to the object we are trying to remove + return n; + } + + Node replacement = value.detachFromParent(); + if (key.isGetterDef()){ + replacement = IR.call(replacement); + replacement.putBooleanProp(Node.FREE_CALL, true); + } + + n.getParent().replaceChild(n, replacement); + reportCodeChange(); + return n; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeFoldWithTypes.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeFoldWithTypes.java new file mode 100644 index 0000000..3b1a35f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeFoldWithTypes.java @@ -0,0 +1,118 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.JSType; + +/** + * Performs type-aware peephole optimizations. + * + * These peephole optimizations are in their own class because + * type information may not always be available (such as during pre-processing) + * or may not be turned on. + * + * Currently only Token.TYPEOF is folded -- in the future it may be possible to + * fold Token.INSTANCEOF as well. Another possibility is folding when + * non-nullable objects are used in Boolean logic, such as: + * "if (x) {" or "(!x) ? a : b" or "x && foo()" + * + * TODO(dcc): Support folding Token.INSTANCEOF and non-nullable objects + * in Boolean logic. + * + * @author dcc@google.com (Devin Coughlin) + */ +class PeepholeFoldWithTypes extends AbstractPeepholeOptimization { + + @Override + Node optimizeSubtree(Node subtree) { + switch (subtree.getType()) { + case Token.TYPEOF: + return tryFoldTypeof(subtree); + default: + return subtree; + } + } + + /** + * Folds "typeof expression" based on the JSType of "expression" if the + * expression has no side effects. + * + *

      E.g., + *

      +   * var x = 6;
      +   * if (typeof(x) == "number") {
      +   * }
      +   * 
      + * folds to + *
      +   * var x = 6;
      +   * if ("number" == "number") {
      +   * }
      +   * 
      + * + *

      This method doesn't fold literal values -- we leave that to + * PeepholeFoldConstants. + */ + private Node tryFoldTypeof(Node typeofNode) { + Preconditions.checkArgument(typeofNode.isTypeOf()); + Preconditions.checkArgument(typeofNode.getFirstChild() != null); + + Node argumentNode = typeofNode.getFirstChild(); + + // We'll let PeepholeFoldConstants handle folding literals + // and we can't remove arguments with possible side effects. + if (!NodeUtil.isLiteralValue(argumentNode, true) && + !mayHaveSideEffects(argumentNode)) { + JSType argumentType = argumentNode.getJSType(); + + String typeName = null; + + if (argumentType != null) { + // typeof null is "object" in JavaScript + if (argumentType.isObject() || argumentType.isNullType()) { + typeName = "object"; + } else if (argumentType.isStringValueType()) { + typeName = "string"; + } else if (argumentType.isNumberValueType()) { + typeName = "number"; + } else if (argumentType.isBooleanValueType()) { + typeName = "boolean"; + } else if (argumentType.isVoidType()) { + typeName = "undefined"; + } else if (argumentType.isUnionType()) { + // TODO(dcc): We don't handle union types, for now, + // but could support, say, unions of different object types + // in the future. + typeName = null; + } + + if (typeName != null) { + Node newNode = IR.string(typeName); + typeofNode.getParent().replaceChild(typeofNode, newNode); + reportCodeChange(); + + return newNode; + } + } + } + return typeofNode; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeOptimizationsPass.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeOptimizationsPass.java new file mode 100644 index 0000000..d2afc0a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeOptimizationsPass.java @@ -0,0 +1,220 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.javascript.rhino.Node; + +import java.util.ArrayList; + +/** + * A compiler pass to run various peephole optimizations (e.g. constant folding, + * some useless code removal, some minimizations). + * + * @author dcc@google.com (Devin Coughlin) + * @author acleung@google.com (Alan Leung)( + */ +class PeepholeOptimizationsPass + implements CompilerPass { + private AbstractCompiler compiler; + + // Use an array here for faster iteration compared to ImmutableSet + private final AbstractPeepholeOptimization[] peepholeOptimizations; + + // Track whether the a scope has been modified so that it can be revisited + // immediately. + private StateStack traversalState = new StateStack(); + + private boolean retraverseOnChange = true; + + static private class ScopeState { + boolean changed; + boolean traverseChildScopes; + ScopeState() { + reset(); + } + + void reset() { + changed = false; + traverseChildScopes = true; + } + } + + static private class StateStack { + private ArrayList states = Lists.newArrayList(); + private int currentDepth = 0; + + StateStack() { + states.add(new ScopeState()); + } + + ScopeState peek() { + return states.get(currentDepth); + } + + void push() { + currentDepth++; + if (states.size() <= currentDepth) { + states.add(new ScopeState()); + } else { + states.get(currentDepth).reset(); + } + } + + void pop() { + currentDepth--; + } + } + + private class PeepholeChangeHandler implements CodeChangeHandler { + @Override + public void reportChange() { + traversalState.peek().changed = true; + } + } + + /** + * Creates a peephole optimization pass that runs the given + * optimizations. + */ + PeepholeOptimizationsPass(AbstractCompiler compiler, + AbstractPeepholeOptimization... optimizations) { + this.compiler = compiler; + this.peepholeOptimizations = optimizations; + } + + PeepholeOptimizationsPass setRetraverseOnChange(boolean retraverse) { + this.retraverseOnChange = retraverse; + return this; + } + + public AbstractCompiler getCompiler() { + return compiler; + } + + @Override + public void process(Node externs, Node root) { + PeepholeChangeHandler handler = new PeepholeChangeHandler(); + compiler.addChangeHandler(handler); + beginTraversal(); + traverse(root); + endTraversal(); + compiler.removeChangeHandler(handler); + } + + private void traverse(Node node) { + // The goal here is to avoid retraversing + // the entire AST to catch newly created opportunities. + // So we track whether a "unit of code" has changed, + // and revisit immediately. + if (!shouldVisit(node)) { + return; + } + + int visits = 0; + do { + Node c = node.getFirstChild(); + while(c != null) { + Node next = c.getNext(); + traverse(c); + c = next; + } + + visit(node); + visits++; + + Preconditions.checkState(visits < 10000, "too many interations"); + } while (shouldRetraverse(node)); + + exitNode(node); + } + + private boolean shouldRetraverse(Node node) { + if (retraverseOnChange + && node.getParent() != null + && (node.isFunction() || node.isScript())) { + ScopeState state = traversalState.peek(); + if (state.changed) { + // prepare to re-visit the scope: + // when revisiting, only visit the immediate scope + // this reduces the cost of getting to a fixed + // point in global scope. + state.changed = false; + state.traverseChildScopes = false; + return true; + } + } + return false; + } + + private boolean shouldVisit(Node node) { + if (node.isFunction() || node.isScript()) { + ScopeState previous = traversalState.peek(); + if (!previous.traverseChildScopes) { + return false; + } + traversalState.push(); + } + return true; + } + + private void exitNode(Node node) { + if (node.isFunction() || node.isScript()) { + traversalState.pop(); + } + } + + public void visit(Node n) { + Node currentVersionOfNode = n; + boolean somethingChanged = false; + + do { + somethingChanged = false; + for (AbstractPeepholeOptimization optimization : peepholeOptimizations) { + Node newVersionOfNode = + optimization.optimizeSubtree(currentVersionOfNode); + + if (newVersionOfNode != currentVersionOfNode) { + somethingChanged = true; + + currentVersionOfNode = newVersionOfNode; + } + + if (currentVersionOfNode == null) { + return; + } + } + } while(somethingChanged); + } + + /** + * Make sure that all the optimizations have the current traversal so they + * can report errors. + */ + private void beginTraversal() { + for (AbstractPeepholeOptimization optimization : peepholeOptimizations) { + optimization.beginTraversal(compiler); + } + } + + private void endTraversal() { + for (AbstractPeepholeOptimization optimization : peepholeOptimizations) { + optimization.endTraversal(compiler); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeRemoveDeadCode.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeRemoveDeadCode.java new file mode 100644 index 0000000..1e1571d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeRemoveDeadCode.java @@ -0,0 +1,928 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicates; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.TernaryValue; + +import javax.annotation.Nullable; + +/** + * Peephole optimization to remove useless code such as IF's with false + * guard conditions, comma operator left hand sides with no side effects, etc. + * + */ +class PeepholeRemoveDeadCode extends AbstractPeepholeOptimization { + + // TODO(dcc): Some (all) of these can probably be better achieved + // using the control flow graph (like CheckUnreachableCode). + // There is an existing CFG pass (UnreachableCodeElimination) that + // could be changed to use code from CheckUnreachableCode to do this. + + @Override + Node optimizeSubtree(Node subtree) { + switch(subtree.getType()) { + case Token.ASSIGN: + return tryFoldAssignment(subtree); + case Token.COMMA: + return tryFoldComma(subtree); + case Token.SCRIPT: + case Token.BLOCK: + return tryOptimizeBlock(subtree); + case Token.EXPR_RESULT: + subtree = tryFoldExpr(subtree); + return subtree; + case Token.HOOK: + return tryFoldHook(subtree); + case Token.SWITCH: + return tryOptimizeSwitch(subtree); + case Token.IF: + return tryFoldIf(subtree); + case Token.WHILE: + return tryFoldWhile(subtree); + case Token.FOR: { + Node condition = NodeUtil.getConditionExpression(subtree); + if (condition != null) { + tryFoldForCondition(condition); + } + } + return tryFoldFor(subtree); + case Token.DO: + return tryFoldDo(subtree); + case Token.TRY: + return tryFoldTry(subtree); + default: + return subtree; + } + } + + /** + * Remove try blocks without catch blocks and with empty or not + * existent finally blocks. + * Or, only leave the finally blocks if try body blocks are empty + * @return the replacement node, if changed, or the original if not + */ + private Node tryFoldTry(Node n) { + Preconditions.checkState(n.isTry()); + Node body = n.getFirstChild(); + Node catchBlock = body.getNext(); + Node finallyBlock = catchBlock.getNext(); + + // Removes TRYs that had its CATCH removed and/or empty FINALLY. + if (!catchBlock.hasChildren() && + (finallyBlock == null || !finallyBlock.hasChildren())) { + n.removeChild(body); + n.getParent().replaceChild(n, body); + reportCodeChange(); + return body; + } + + // Only leave FINALLYs if TRYs are empty + if (!body.hasChildren()) { + NodeUtil.redeclareVarsInsideBranch(catchBlock); + if (finallyBlock != null) { + n.removeChild(finallyBlock); + n.getParent().replaceChild(n, finallyBlock); + } else { + n.getParent().removeChild(n); + } + reportCodeChange(); + return finallyBlock; + } + + return n; + } + + /** + * Try removing identity assignments + * @return the replacement node, if changed, or the original if not + */ + private Node tryFoldAssignment(Node subtree) { + Preconditions.checkState(subtree.isAssign()); + Node left = subtree.getFirstChild(); + Node right = subtree.getLastChild(); + // Only names + if (left.isName() + && right.isName() + && left.getString().equals(right.getString())) { + subtree.getParent().replaceChild(subtree, right.detachFromParent()); + reportCodeChange(); + return right; + } + return subtree; + } + + /** + * Try folding EXPR_RESULT nodes by removing useless Ops and expressions. + * @return the replacement node, if changed, or the original if not + */ + private Node tryFoldExpr(Node subtree) { + Node result = trySimplifyUnusedResult(subtree.getFirstChild()); + if (result == null) { + Node parent = subtree.getParent(); + // If the EXPR_RESULT no longer has any children, remove it as well. + if (parent.isLabel()) { + Node replacement = IR.block().srcref(subtree); + parent.replaceChild(subtree, replacement); + subtree = replacement; + } else { + subtree.detachFromParent(); + subtree = null; + } + } + return subtree; + } + + /** + * General cascading unused operation node removal. + * @param n The root of the expression to simplify. + * @return The replacement node, or null if the node was is not useful. + */ + private Node trySimplifyUnusedResult(Node n) { + return trySimplifyUnusedResult(n, true); + } + + /** + * General cascading unused operation node removal. + * @param n The root of the expression to simplify. + * @param removeUnused If true, the node is removed from the AST if + * it is not useful, otherwise it replaced with an EMPTY node. + * @return The replacement node, or null if the node was is not useful. + */ + private Node trySimplifyUnusedResult(Node n, boolean removeUnused) { + Node result = n; + + // Simplify the results of conditional expressions + switch (n.getType()) { + case Token.HOOK: + Node trueNode = trySimplifyUnusedResult(n.getFirstChild().getNext()); + Node falseNode = trySimplifyUnusedResult(n.getLastChild()); + // If one or more of the conditional children were removed, + // transform the HOOK to an equivalent operation: + // x() ? foo() : 1 --> x() && foo() + // x() ? 1 : foo() --> x() || foo() + // x() ? 1 : 1 --> x() + // x ? 1 : 1 --> null + if (trueNode == null && falseNode != null) { + n.setType(Token.OR); + Preconditions.checkState(n.getChildCount() == 2); + } else if (trueNode != null && falseNode == null) { + n.setType(Token.AND); + Preconditions.checkState(n.getChildCount() == 2); + } else if (trueNode == null && falseNode == null) { + result = trySimplifyUnusedResult(n.getFirstChild()); + } else { + // The structure didn't change. + result = n; + } + break; + case Token.AND: + case Token.OR: + // Try to remove the second operand from a AND or OR operations: + // x() || f --> x() + // x() && f --> x() + Node conditionalResultNode = trySimplifyUnusedResult( + n.getLastChild()); + if (conditionalResultNode == null) { + Preconditions.checkState(n.hasOneChild()); + // The conditionally executed code was removed, so + // replace the AND/OR with its LHS or remove it if it isn't useful. + result = trySimplifyUnusedResult(n.getFirstChild()); + } + break; + case Token.FUNCTION: + // A function expression isn't useful if it isn't used, remove it and + // don't bother to look at its children. + result = null; + break; + case Token.COMMA: + // We rewrite other operations as COMMA expressions (which will later + // get split into individual EXPR_RESULT statement, if possible), so + // we special case COMMA (we don't want to rewrite COMMAs as new COMMAs + // nodes. + Node left = trySimplifyUnusedResult(n.getFirstChild()); + Node right = trySimplifyUnusedResult(n.getLastChild()); + if (left == null && right == null) { + result = null; + } else if (left == null) { + result = right; + } else if (right == null){ + result = left; + } else { + // The structure didn't change. + result = n; + } + break; + default: + if (!nodeTypeMayHaveSideEffects(n)) { + // This is the meat of this function. The node itself doesn't generate + // any side-effects but preserve any side-effects in the children. + Node resultList = null; + for (Node next, c = n.getFirstChild(); c != null; c = next) { + next = c.getNext(); + c = trySimplifyUnusedResult(c); + if (c != null) { + c.detachFromParent(); + if (resultList == null) { + // The first side-effect can be used stand-alone. + resultList = c; + } else { + // Leave the side-effects in-place, simplifying it to a COMMA + // expression. + resultList = IR.comma(resultList, c).srcref(c); + } + } + } + result = resultList; + } + } + + // Fix up the AST, replace or remove the an unused node (if requested). + if (n != result) { + Node parent = n.getParent(); + if (result == null) { + if (removeUnused) { + parent.removeChild(n); + } else { + result = IR.empty().srcref(n); + parent.replaceChild(n, result); + } + } else { + // A new COMMA expression may not have an existing parent. + if (result.getParent() != null) { + result.detachFromParent(); + } + n.getParent().replaceChild(n, result); + } + reportCodeChange(); + } + + return result; + } + + /** + * Remove useless switches and cases. + */ + private Node tryOptimizeSwitch(Node n) { + Preconditions.checkState(n.isSwitch()); + + Node defaultCase = tryOptimizeDefaultCase(n); + + // Removing cases when there exists a default case is not safe. + if (defaultCase == null) { + Node cond = n.getFirstChild(), prev = null, next = null, cur; + + for (cur = cond.getNext(); cur != null; cur = next) { + next = cur.getNext(); + if (!mayHaveSideEffects(cur.getFirstChild()) && + isUselessCase(cur, prev)) { + removeCase(n, cur); + } else { + prev = cur; + } + } + + // Optimize switches with constant condition + if (NodeUtil.isLiteralValue(cond, false)) { + Node caseLabel; + TernaryValue caseMatches = TernaryValue.TRUE; + // Remove cases until you find one that may match + for (cur = cond.getNext(); cur != null; cur = next) { + next = cur.getNext(); + caseLabel = cur.getFirstChild(); + caseMatches = PeepholeFoldConstants.evaluateComparison( + Token.SHEQ, cond, caseLabel); + if (caseMatches == TernaryValue.TRUE) { + break; + } else if (caseMatches == TernaryValue.UNKNOWN) { + break; + } else { + removeCase(n, cur); + } + } + if (caseMatches != TernaryValue.UNKNOWN) { + Node block, lastStm; + // Skip cases until you find one whose last stm is a break + while (cur != null) { + block = cur.getLastChild(); + lastStm = block.getLastChild(); + cur = cur.getNext(); + if (lastStm != null && lastStm.isBreak()) { + block.removeChild(lastStm); + reportCodeChange(); + break; + } + } + // Remove any remaining cases + for (; cur != null; cur = next) { + next = cur.getNext(); + removeCase(n, cur); + } + // If there is one case left, we may be able to fold it + cur = cond.getNext(); + if (cur != null && cur.getNext() == null) { + block = cur.getLastChild(); + if (!(NodeUtil.containsType(block, Token.BREAK, + NodeUtil.MATCH_NOT_FUNCTION))) { + cur.removeChild(block); + n.getParent().replaceChild(n, block); + reportCodeChange(); + return block; + } + } + } + } + } + + // Remove the switch if there are no remaining cases. + if (n.hasOneChild()) { + Node condition = n.removeFirstChild(); + Node replacement = IR.exprResult(condition).srcref(n); + n.getParent().replaceChild(n, replacement); + reportCodeChange(); + return replacement; + } + + return null; + } + + /** + * @return the default case node or null if there is no default case or + * if the default case is removed. + */ + private Node tryOptimizeDefaultCase(Node n) { + Preconditions.checkState(n.isSwitch()); + + Node lastNonRemovable = n.getFirstChild(); // The switch condition + + // The first child is the switch conditions skip it when looking for cases. + for (Node c = n.getFirstChild().getNext(); c != null; c = c.getNext()) { + if (c.isDefaultCase()) { + // Remove cases that fall-through to the default case + Node caseToRemove = lastNonRemovable.getNext(); + for (Node next; caseToRemove != c; caseToRemove = next) { + next = caseToRemove.getNext(); + removeCase(n, caseToRemove); + } + + // Don't use the switch condition as the previous case. + Node prevCase = (lastNonRemovable == n.getFirstChild()) + ? null : lastNonRemovable; + + // Remove the default case if we can + if (isUselessCase(c, prevCase)) { + removeCase(n, c); + return null; + } + return c; + } else { + Preconditions.checkState(c.isCase()); + if (c.getLastChild().hasChildren() + || mayHaveSideEffects(c.getFirstChild())) { + lastNonRemovable = c; + } + } + } + return null; + } + + /** + * Remove the case from the switch redeclaring any variables declared in it. + * @param caseNode The case to remove. + */ + private void removeCase(Node switchNode, Node caseNode) { + NodeUtil.redeclareVarsInsideBranch(caseNode); + switchNode.removeChild(caseNode); + reportCodeChange(); + } + + /** + * The function assumes that when checking a CASE node there is no + * DEFAULT node in the SWITCH. + * @return Whether the CASE or DEFAULT block does anything useful. + */ + private boolean isUselessCase(Node caseNode, @Nullable Node previousCase) { + Preconditions.checkState( + previousCase == null || previousCase.getNext() == caseNode); + // A case isn't useless can't be useless if a previous case falls + // through to it unless it happens to be the last case in the switch. + Node switchNode = caseNode.getParent(); + if (switchNode.getLastChild() != caseNode + && previousCase != null) { + Node previousBlock = previousCase.getLastChild(); + if (!previousBlock.hasChildren() + || !isExit(previousBlock.getLastChild())) { + return false; + } + } + + Node executingCase = caseNode; + while (executingCase != null) { + Preconditions.checkState(executingCase.isDefaultCase() + || executingCase.isCase()); + // We only expect a DEFAULT case if the case we are checking is the + // DEFAULT case. Otherwise, we assume the DEFAULT case has already + // been removed. + Preconditions.checkState(caseNode == executingCase + || !executingCase.isDefaultCase()); + Node block = executingCase.getLastChild(); + Preconditions.checkState(block.isBlock()); + if (block.hasChildren()) { + for (Node blockChild : block.children()) { + // If this is a block with a labelless break, it is useless. + switch (blockChild.getType()) { + case Token.BREAK: + // A break to a different control structure isn't useless. + return blockChild.getFirstChild() == null; + case Token.VAR: + if (blockChild.hasOneChild() + && blockChild.getFirstChild().getFirstChild() == null) { + // Variable declarations without initializations are OK. + continue; + } + return false; + default: + return false; + } + } + } else { + // Look at the fallthrough case + executingCase = executingCase.getNext(); + } + } + return true; + } + + /** + * @return Whether the node is an obvious control flow exit. + */ + private boolean isExit(Node n) { + switch (n.getType()) { + case Token.BREAK: + case Token.CONTINUE: + case Token.RETURN: + case Token.THROW: + return true; + default: + return false; + } + } + + private Node tryFoldComma(Node n) { + // If the left side does nothing replace the comma with the result. + Node parent = n.getParent(); + Node left = n.getFirstChild(); + Node right = left.getNext(); + + left = trySimplifyUnusedResult(left); + if (left == null || !mayHaveSideEffects(left)) { + // Fold it! + n.removeChild(right); + parent.replaceChild(n, right); + reportCodeChange(); + return right; + } + return n; + } + + /** + * Try removing unneeded block nodes and their useless children + */ + Node tryOptimizeBlock(Node n) { + // Remove any useless children + for (Node c = n.getFirstChild(); c != null; ) { + Node next = c.getNext(); // save c.next, since 'c' may be removed + if (!isUnremovableNode(c) && !mayHaveSideEffects(c)) { + // TODO(johnlenz): determine what this is actually removing. Candidates + // include: EMPTY nodes, control structures without children + // (removing infinite loops), empty try blocks. What else? + n.removeChild(c); // lazy kids + reportCodeChange(); + } else { + tryOptimizeConditionalAfterAssign(c); + } + c = next; + } + + if (n.isSyntheticBlock() || n.getParent() == null) { + return n; + } + + // Try to remove the block. + if (NodeUtil.tryMergeBlock(n)) { + reportCodeChange(); + return null; + } + + return n; + } + + /** + * Some nodes unremovable node don't have side-effects. + */ + private boolean isUnremovableNode(Node n) { + return (n.isBlock() && n.isSyntheticBlock()) || n.isScript(); + } + + // TODO(johnlenz): Consider moving this to a separate peephole pass. + /** + * Attempt to replace the condition of if or hook immediately that is a + * reference to a name that is assigned immediately before. + */ + private void tryOptimizeConditionalAfterAssign(Node n) { + Node next = n.getNext(); + + // Look for patterns like the following and replace the if-condition with + // a constant value so it can later be folded: + // var a = /a/; + // if (a) {foo(a)} + // or + // a = 0; + // a ? foo(a) : c; + // or + // a = 0; + // a || foo(a); + // or + // a = 0; + // a && foo(a) + // + // TODO(johnlenz): This would be better handled by control-flow sensitive + // constant propagation. As the other case that I want to handle is: + // i=0; for(;i<0;i++){} + // as right now nothing facilitates removing a loop like that. + // This is here simply to remove the cruft left behind goog.userAgent and + // similar cases. + + if (isSimpleAssignment(n) && isConditionalStatement(next)) { + Node lhsAssign = getSimpleAssignmentName(n); + + Node condition = getConditionalStatementCondition(next); + if (lhsAssign.isName() && condition.isName() + && lhsAssign.getString().equals(condition.getString())) { + Node rhsAssign = getSimpleAssignmentValue(n); + TernaryValue value = NodeUtil.getImpureBooleanValue(rhsAssign); + if (value != TernaryValue.UNKNOWN) { + Node replacementConditionNode = + NodeUtil.booleanNode(value.toBoolean(true)); + condition.getParent().replaceChild(condition, + replacementConditionNode); + reportCodeChange(); + } + } + } + } + + /** + * @return whether the node is a assignment to a simple name, or simple var + * declaration with initialization. + */ + private boolean isSimpleAssignment(Node n) { + // For our purposes we define a simple assignment to be a assignment + // to a NAME node, or a VAR declaration with one child and a initializer. + if (NodeUtil.isExprAssign(n) + && n.getFirstChild().getFirstChild().isName()) { + return true; + } else if (n.isVar() && n.hasOneChild() && + n.getFirstChild().getFirstChild() != null) { + return true; + } + + return false; + } + + /** + * @return The name being assigned to. + */ + private Node getSimpleAssignmentName(Node n) { + Preconditions.checkState(isSimpleAssignment(n)); + if (NodeUtil.isExprAssign(n)) { + return n.getFirstChild().getFirstChild(); + } else { + // A var declaration. + return n.getFirstChild(); + } + } + + /** + * @return The value assigned in the simple assignment + */ + private Node getSimpleAssignmentValue(Node n) { + Preconditions.checkState(isSimpleAssignment(n)); + return n.getFirstChild().getLastChild(); + } + + /** + * @return Whether the node is a conditional statement. + */ + private boolean isConditionalStatement(Node n) { + // We defined a conditional statement to be a IF or EXPR_RESULT rooted with + // a HOOK, AND, or OR node. + return n != null && (n.isIf() || isExprConditional(n)); + } + + /** + * @return Whether the node is a rooted with a HOOK, AND, or OR node. + */ + private boolean isExprConditional(Node n) { + if (n.isExprResult()) { + switch (n.getFirstChild().getType()) { + case Token.HOOK: + case Token.AND: + case Token.OR: + return true; + } + } + return false; + } + + /** + * @return The condition of a conditional statement. + */ + private Node getConditionalStatementCondition(Node n) { + if (n.isIf()) { + return NodeUtil.getConditionExpression(n); + } else { + Preconditions.checkState(isExprConditional(n)); + return n.getFirstChild().getFirstChild(); + } + } + + /** + * Try folding IF nodes by removing dead branches. + * @return the replacement node, if changed, or the original if not + */ + private Node tryFoldIf(Node n) { + Preconditions.checkState(n.isIf()); + Node parent = n.getParent(); + Preconditions.checkNotNull(parent); + int type = n.getType(); + Node cond = n.getFirstChild(); + Node thenBody = cond.getNext(); + Node elseBody = thenBody.getNext(); + + // if (x) { .. } else { } --> if (x) { ... } + if (elseBody != null && !mayHaveSideEffects(elseBody)) { + n.removeChild(elseBody); + elseBody = null; + reportCodeChange(); + } + + // if (x) { } else { ... } --> if (!x) { ... } + if (!mayHaveSideEffects(thenBody) && elseBody != null) { + n.removeChild(elseBody); + n.replaceChild(thenBody, elseBody); + Node notCond = new Node(Token.NOT); + n.replaceChild(cond, notCond); + notCond.addChildToFront(cond); + cond = notCond; + thenBody = cond.getNext(); + elseBody = null; + reportCodeChange(); + } + + // if (x()) { } + if (!mayHaveSideEffects(thenBody) && elseBody == null) { + if (mayHaveSideEffects(cond)) { + // x() has side effects, just leave the condition on its own. + n.removeChild(cond); + Node replacement = NodeUtil.newExpr(cond); + parent.replaceChild(n, replacement); + reportCodeChange(); + return replacement; + } else { + // x() has no side effects, the whole tree is useless now. + NodeUtil.removeChild(parent, n); + reportCodeChange(); + return null; + } + } + + // Try transforms that apply to both IF and HOOK. + TernaryValue condValue = NodeUtil.getImpureBooleanValue(cond); + if (condValue == TernaryValue.UNKNOWN) { + return n; // We can't remove branches otherwise! + } + + if (mayHaveSideEffects(cond)) { + // Transform "if (a = 2) {x =2}" into "if (true) {a=2;x=2}" + boolean newConditionValue = condValue == TernaryValue.TRUE; + // Add an elseBody if it is needed. + if (!newConditionValue && elseBody == null) { + elseBody = IR.block().srcref(n); + n.addChildToBack(elseBody); + } + Node newCond = NodeUtil.booleanNode(newConditionValue); + n.replaceChild(cond, newCond); + Node branchToKeep = newConditionValue ? thenBody : elseBody; + branchToKeep.addChildToFront(IR.exprResult(cond).srcref(cond)); + reportCodeChange(); + cond = newCond; + } + + boolean condTrue = condValue.toBoolean(true); + if (n.getChildCount() == 2) { + Preconditions.checkState(type == Token.IF); + + if (condTrue) { + // Replace "if (true) { X }" with "X". + Node thenStmt = n.getFirstChild().getNext(); + n.removeChild(thenStmt); + parent.replaceChild(n, thenStmt); + reportCodeChange(); + return thenStmt; + } else { + // Remove "if (false) { X }" completely. + NodeUtil.redeclareVarsInsideBranch(n); + NodeUtil.removeChild(parent, n); + reportCodeChange(); + return null; + } + } else { + // Replace "if (true) { X } else { Y }" with X, or + // replace "if (false) { X } else { Y }" with Y. + Node trueBranch = n.getFirstChild().getNext(); + Node falseBranch = trueBranch.getNext(); + Node branchToKeep = condTrue ? trueBranch : falseBranch; + Node branchToRemove = condTrue ? falseBranch : trueBranch; + NodeUtil.redeclareVarsInsideBranch(branchToRemove); + n.removeChild(branchToKeep); + parent.replaceChild(n, branchToKeep); + reportCodeChange(); + return branchToKeep; + } + } + + /** + * Try folding HOOK (?:) if the condition results of the condition is known. + * @return the replacement node, if changed, or the original if not + */ + private Node tryFoldHook(Node n) { + Preconditions.checkState(n.isHook()); + Node parent = n.getParent(); + Preconditions.checkNotNull(parent); + Node cond = n.getFirstChild(); + Node thenBody = cond.getNext(); + Node elseBody = thenBody.getNext(); + + TernaryValue condValue = NodeUtil.getImpureBooleanValue(cond); + if (condValue == TernaryValue.UNKNOWN) { + // If the result nodes are equivalent, then one of the nodes can be + // removed and it doesn't matter which. + if (!areNodesEqualForInlining(thenBody, elseBody)) { + return n; // We can't remove branches otherwise! + } + } + + // Transform "(a = 2) ? x =2 : y" into "a=2,x=2" + n.detachChildren(); + Node branchToKeep = condValue.toBoolean(true) ? thenBody : elseBody; + Node replacement; + if (mayHaveSideEffects(cond)) { + replacement = IR.comma(cond, branchToKeep).srcref(n); + } else { + replacement = branchToKeep; + } + + parent.replaceChild(n, replacement); + reportCodeChange(); + return replacement; + } + + /** + * Removes WHILEs that always evaluate to false. + */ + Node tryFoldWhile(Node n) { + Preconditions.checkArgument(n.isWhile()); + Node cond = NodeUtil.getConditionExpression(n); + if (NodeUtil.getPureBooleanValue(cond) != TernaryValue.FALSE) { + return n; + } + NodeUtil.redeclareVarsInsideBranch(n); + NodeUtil.removeChild(n.getParent(), n); + reportCodeChange(); + + return null; + } + + /** + * Removes FORs that always evaluate to false. + */ + Node tryFoldFor(Node n) { + Preconditions.checkArgument(n.isFor()); + // If this is a FOR-IN loop skip it. + if (NodeUtil.isForIn(n)) { + return n; + } + + Node init = n.getFirstChild(); + Node cond = init.getNext(); + Node increment = cond.getNext(); + + if (!init.isEmpty() && !init.isVar()) { + init = trySimplifyUnusedResult(init, false); + } + + if (!increment.isEmpty()) { + increment = trySimplifyUnusedResult(increment, false); + } + + // There is an initializer skip it + if (!n.getFirstChild().isEmpty()) { + return n; + } + + if (NodeUtil.getImpureBooleanValue(cond) != TernaryValue.FALSE) { + return n; + } + + NodeUtil.redeclareVarsInsideBranch(n); + if (!mayHaveSideEffects(cond)) { + NodeUtil.removeChild(n.getParent(), n); + } else { + Node statement = IR.exprResult(cond.detachFromParent()) + .copyInformationFrom(cond); + n.getParent().replaceChild(n, statement); + } + reportCodeChange(); + return null; + } + + /** + * Removes DOs that always evaluate to false. This leaves the + * statements that were in the loop in a BLOCK node. + * The block will be removed in a later pass, if possible. + */ + Node tryFoldDo(Node n) { + Preconditions.checkArgument(n.isDo()); + + Node cond = NodeUtil.getConditionExpression(n); + if (NodeUtil.getImpureBooleanValue(cond) != TernaryValue.FALSE) { + return n; + } + + // TODO(johnlenz): The do-while can be turned into a label with + // named breaks and the label optimized away (maybe). + if (hasBreakOrContinue(n)) { + return n; + } + + Preconditions.checkState( + NodeUtil.isControlStructureCodeBlock(n, n.getFirstChild())); + Node block = n.removeFirstChild(); + + Node parent = n.getParent(); + parent.replaceChild(n, block); + if (mayHaveSideEffects(cond)) { + Node condStatement = IR.exprResult(cond.detachFromParent()) + .srcref(cond); + parent.addChildAfter(condStatement, block); + } + reportCodeChange(); + + return n; + } + + /** + * + */ + boolean hasBreakOrContinue(Node n) { + // TODO(johnlenz): This is overkill as named breaks may refer to outer + // loops or labels, and any break my refer to an inner loop. + // More generally, this check may be more expensive than we like. + return NodeUtil.has( + n, + Predicates.or( + new NodeUtil.MatchNodeType(Token.BREAK), + new NodeUtil.MatchNodeType(Token.CONTINUE)), + NodeUtil.MATCH_NOT_FUNCTION); + } + + /** + * Remove always true loop conditions. + */ + private void tryFoldForCondition(Node forCondition) { + if (NodeUtil.getPureBooleanValue(forCondition) == TernaryValue.TRUE) { + forCondition.getParent().replaceChild(forCondition, + IR.empty()); + reportCodeChange(); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeReplaceKnownMethods.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeReplaceKnownMethods.java new file mode 100644 index 0000000..63538e3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeReplaceKnownMethods.java @@ -0,0 +1,795 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import java.util.List; +import java.util.Locale; + +/** + * Just to fold known methods when they are called with constants. + * + */ +class PeepholeReplaceKnownMethods extends AbstractPeepholeOptimization{ + + // The LOCALE independent "locale" + private static final Locale ROOT_LOCALE = new Locale(""); + private final boolean late; + + /** + * @param late When late is true, this mean we are currently running after + * most of the other optimizations. In this case we avoid changes that make + * the code larger (but otherwise easier to analyze - such as using string + * splitting). + */ + PeepholeReplaceKnownMethods(boolean late) { + this.late = late; + } + + @Override + Node optimizeSubtree(Node subtree) { + if (subtree.isCall() ){ + return tryFoldKnownMethods(subtree); + } + return subtree; + } + + private Node tryFoldKnownMethods(Node subtree) { + // For now we only support string methods .join(), + // .indexOf(), .substring() and .substr() + // and numeric methods parseInt() and parseFloat(). + + subtree = tryFoldArrayJoin(subtree); + + if (subtree.isCall()) { + Node callTarget = subtree.getFirstChild(); + if (callTarget == null) { + return subtree; + } + + if (NodeUtil.isGet(callTarget)) { + subtree = tryFoldKnownStringMethods(subtree); + } else { + subtree = tryFoldKnownNumericMethods(subtree); + } + } + + return subtree; + } + + /** + * Try to evaluate known String methods + * .indexOf(), .substr(), .substring() + */ + private Node tryFoldKnownStringMethods(Node subtree) { + Preconditions.checkArgument(subtree.isCall()); + + // check if this is a call on a string method + // then dispatch to specific folding method. + Node callTarget = subtree.getFirstChild(); + if (callTarget == null) { + return subtree; + } + + if (!NodeUtil.isGet(callTarget)) { + return subtree; + } + + Node stringNode = callTarget.getFirstChild(); + Node functionName = stringNode.getNext(); + + if ((!stringNode.isString()) || + (!functionName.isString())) { + return subtree; + } + + String functionNameString = functionName.getString(); + Node firstArg = callTarget.getNext(); + if (functionNameString.equals("split")) { + subtree = tryFoldStringSplit(subtree, stringNode, firstArg); + } else if (firstArg == null) { + if (functionNameString.equals("toLowerCase")) { + subtree = tryFoldStringToLowerCase(subtree, stringNode); + } else if (functionNameString.equals("toUpperCase")) { + subtree = tryFoldStringToUpperCase(subtree, stringNode); + } + return subtree; + } else if (NodeUtil.isImmutableValue(firstArg)) { + if (functionNameString.equals("indexOf") || + functionNameString.equals("lastIndexOf")) { + subtree = tryFoldStringIndexOf(subtree, functionNameString, + stringNode, firstArg); + } else if (functionNameString.equals("substr")) { + subtree = tryFoldStringSubstr(subtree, stringNode, firstArg); + } else if (functionNameString.equals("substring")) { + subtree = tryFoldStringSubstring(subtree, stringNode, firstArg); + } else if (functionNameString.equals("charAt")) { + subtree = tryFoldStringCharAt(subtree, stringNode, firstArg); + } else if (functionNameString.equals("charCodeAt")) { + subtree = tryFoldStringCharCodeAt(subtree, stringNode, firstArg); + } + } + + return subtree; + } + + /** + * Try to evaluate known Numeric methods + * .parseInt(), parseFloat() + */ + private Node tryFoldKnownNumericMethods(Node subtree) { + Preconditions.checkArgument(subtree.isCall()); + + if (isASTNormalized()) { + // check if this is a call on a string method + // then dispatch to specific folding method. + Node callTarget = subtree.getFirstChild(); + + if (!callTarget.isName()) { + return subtree; + } + + String functionNameString = callTarget.getString(); + Node firstArgument = callTarget.getNext(); + if ((firstArgument != null) && + (firstArgument.isString() || + firstArgument.isNumber())) { + if (functionNameString.equals("parseInt") || + functionNameString.equals("parseFloat")) { + subtree = tryFoldParseNumber(subtree, functionNameString, + firstArgument); + } + } + } + return subtree; + } + + /** + * @return The lowered string Node. + */ + private Node tryFoldStringToLowerCase(Node subtree, Node stringNode) { + // From Rhino, NativeString.java. See ECMA 15.5.4.11 + String lowered = stringNode.getString().toLowerCase(ROOT_LOCALE); + Node replacement = IR.string(lowered); + subtree.getParent().replaceChild(subtree, replacement); + reportCodeChange(); + return replacement; + } + + /** + * @return The upped string Node. + */ + private Node tryFoldStringToUpperCase(Node subtree, Node stringNode) { + // From Rhino, NativeString.java. See ECMA 15.5.4.12 + String upped = stringNode.getString().toUpperCase(ROOT_LOCALE); + Node replacement = IR.string(upped); + subtree.getParent().replaceChild(subtree, replacement); + reportCodeChange(); + return replacement; + } + + /** + * @param input string representation of a number + * @return string with leading and trailing zeros removed + */ + private String normalizeNumericString(String input) { + if (input == null || input.length() == 0) { + return input; + } + + int startIndex = 0, endIndex = input.length() - 1; + + // Remove leading zeros + while (startIndex < input.length() && input.charAt(startIndex) == '0' && + input.charAt(startIndex) != '.') { + startIndex++; + } + + // Remove trailing zeros only after the decimal + if (input.indexOf('.') >= 0) { + while (endIndex >= 0 && input.charAt(endIndex) == '0') { + endIndex--; + } + if (input.charAt(endIndex) == '.') { + endIndex--; + } + } + if (startIndex >= endIndex) { + return input; + } + + return input.substring(startIndex, endIndex + 1); + } + + /** + * Try to evaluate parseInt, parseFloat: + * parseInt("1") -> 1 + * parseInt("1", 10) -> 1 + * parseFloat("1.11") -> 1.11 + */ + private Node tryFoldParseNumber( + Node n, String functionName, Node firstArg) { + Preconditions.checkArgument(n.isCall()); + + boolean isParseInt = functionName.equals("parseInt"); + Node secondArg = firstArg.getNext(); + + // Second argument is only used as the radix for parseInt + int radix = 0; + if (secondArg != null) { + if (!isParseInt) { + return n; + } + + // Third-argument and non-numeric second arg are problematic. Discard. + if (secondArg.getNext() != null || !secondArg.isNumber()) { + return n; + } else { + double tmpRadix = secondArg.getDouble(); + if (tmpRadix != (int)tmpRadix) + return n; + radix = (int)tmpRadix; + if (radix < 0 || radix == 1 || radix > 36) { + return n; + } + } + } + + // stringVal must be a valid string. + String stringVal = null; + Double checkVal; + if (firstArg.isNumber()) { + checkVal = NodeUtil.getNumberValue(firstArg); + if (!(radix == 0 || radix == 10) && isParseInt) { + //Convert a numeric first argument to a different base + stringVal = String.valueOf(checkVal.intValue()); + } else { + // If parseFloat is called with a numeric argument, + // replace it with just the number. + // If parseInt is called with a numeric first argument and the radix + // is 10 or omitted, just replace it with the number + Node numericNode; + if (isParseInt) { + numericNode = IR.number(checkVal.intValue()); + } else { + numericNode = IR.number(checkVal); + } + n.getParent().replaceChild(n, numericNode); + reportCodeChange(); + return numericNode; + } + } else { + stringVal = NodeUtil.getStringValue(firstArg); + if (stringVal == null) { + return n; + } + + //Check that the string is in a format we can recognize + checkVal = NodeUtil.getStringNumberValue(stringVal); + if (checkVal == null) { + return n; + } + + stringVal = NodeUtil.trimJsWhiteSpace(stringVal); + if (stringVal.length() == 0) { + return n; + } + } + + Node newNode; + if (stringVal.equals("0")) { + // Special case for parseInt("0") or parseFloat("0") + newNode = IR.number(0); + } else if (isParseInt) { + if (radix == 0 || radix == 16) { + if (stringVal.length() > 1 && + stringVal.substring(0, 2).equalsIgnoreCase("0x")) { + radix = 16; + stringVal = stringVal.substring(2); + } else if (radix == 0) { + // if a radix is not specified or is 0 and the most + // significant digit is "0", the string will parse + // with a radix of 8 on some browsers, so leave + // this case alone. This check does not apply in + // script mode ECMA5 or greater + if (!isEcmaScript5OrGreater() && + stringVal.substring(0, 1).equals("0")) { + return n; + } + + radix = 10; + } + } + int newVal = 0; + try { + newVal = Integer.parseInt(stringVal, radix); + } catch (NumberFormatException e) { + return n; + } + + newNode = IR.number(newVal); + } else { + String normalizedNewVal = "0"; + try { + double newVal = Double.parseDouble(stringVal); + newNode = IR.number(newVal); + normalizedNewVal = normalizeNumericString(String.valueOf(newVal)); + } + catch(NumberFormatException e) { + return n; + } + // Make sure that the parsed number matches the original string + // This prevents rounding differences between the Java implementation + // and native script. + if (!normalizeNumericString(stringVal).equals(normalizedNewVal)) { + return n; + } + } + + n.getParent().replaceChild(n, newNode); + + reportCodeChange(); + + return newNode; + } + + /** + * Try to evaluate String.indexOf/lastIndexOf: + * "abcdef".indexOf("bc") -> 1 + * "abcdefbc".indexOf("bc", 3) -> 6 + */ + private Node tryFoldStringIndexOf( + Node n, String functionName, Node lstringNode, Node firstArg) { + Preconditions.checkArgument(n.isCall()); + Preconditions.checkArgument(lstringNode.isString()); + + String lstring = NodeUtil.getStringValue(lstringNode); + boolean isIndexOf = functionName.equals("indexOf"); + Node secondArg = firstArg.getNext(); + String searchValue = NodeUtil.getStringValue(firstArg); + // searchValue must be a valid string. + if (searchValue == null) { + return n; + } + int fromIndex = isIndexOf ? 0 : lstring.length(); + if (secondArg != null) { + // Third-argument and non-numeric second arg are problematic. Discard. + if (secondArg.getNext() != null || !secondArg.isNumber()) { + return n; + } else { + fromIndex = (int) secondArg.getDouble(); + } + } + int indexVal = isIndexOf ? lstring.indexOf(searchValue, fromIndex) + : lstring.lastIndexOf(searchValue, fromIndex); + Node newNode = IR.number(indexVal); + n.getParent().replaceChild(n, newNode); + + reportCodeChange(); + + return newNode; + } + + /** + * Try to fold an array join: ['a', 'b', 'c'].join('') -> 'abc'; + */ + private Node tryFoldArrayJoin(Node n) { + Node callTarget = n.getFirstChild(); + + if (callTarget == null || !callTarget.isGetProp()) { + return n; + } + + Node right = callTarget.getNext(); + if (right != null) { + if (right.getNext() != null || !NodeUtil.isImmutableValue(right)) { + return n; + } + } + + Node arrayNode = callTarget.getFirstChild(); + Node functionName = arrayNode.getNext(); + + if (!arrayNode.isArrayLit() || + !functionName.getString().equals("join")) { + return n; + } + + if (right != null && right.isString() + && ",".equals(right.getString())) { + // "," is the default, it doesn't need to be explicit + n.removeChild(right); + reportCodeChange(); + } + + String joinString = (right == null) ? "," : NodeUtil.getStringValue(right); + List arrayFoldedChildren = Lists.newLinkedList(); + StringBuilder sb = null; + int foldedSize = 0; + Node prev = null; + Node elem = arrayNode.getFirstChild(); + // Merges adjacent String nodes. + while (elem != null) { + if (NodeUtil.isImmutableValue(elem) || elem.isEmpty()) { + if (sb == null) { + sb = new StringBuilder(); + } else { + sb.append(joinString); + } + sb.append(NodeUtil.getArrayElementStringValue(elem)); + } else { + if (sb != null) { + Preconditions.checkNotNull(prev); + // + 2 for the quotes. + foldedSize += sb.length() + 2; + arrayFoldedChildren.add( + IR.string(sb.toString()).copyInformationFrom(prev)); + sb = null; + } + foldedSize += InlineCostEstimator.getCost(elem); + arrayFoldedChildren.add(elem); + } + prev = elem; + elem = elem.getNext(); + } + + if (sb != null) { + Preconditions.checkNotNull(prev); + // + 2 for the quotes. + foldedSize += sb.length() + 2; + arrayFoldedChildren.add( + IR.string(sb.toString()).copyInformationFrom(prev)); + } + // one for each comma. + foldedSize += arrayFoldedChildren.size() - 1; + + int originalSize = InlineCostEstimator.getCost(n); + switch (arrayFoldedChildren.size()) { + case 0: + Node emptyStringNode = IR.string(""); + n.getParent().replaceChild(n, emptyStringNode); + reportCodeChange(); + return emptyStringNode; + case 1: + Node foldedStringNode = arrayFoldedChildren.remove(0); + if (foldedSize > originalSize) { + return n; + } + arrayNode.detachChildren(); + if (!foldedStringNode.isString()) { + // If the Node is not a string literal, ensure that + // it is coerced to a string. + Node replacement = IR.add( + IR.string("").srcref(n), + foldedStringNode); + foldedStringNode = replacement; + } + n.getParent().replaceChild(n, foldedStringNode); + reportCodeChange(); + return foldedStringNode; + default: + // No folding could actually be performed. + if (arrayFoldedChildren.size() == arrayNode.getChildCount()) { + return n; + } + int kJoinOverhead = "[].join()".length(); + foldedSize += kJoinOverhead; + foldedSize += (right != null) ? InlineCostEstimator.getCost(right) : 0; + if (foldedSize > originalSize) { + return n; + } + arrayNode.detachChildren(); + for (Node node : arrayFoldedChildren) { + arrayNode.addChildToBack(node); + } + reportCodeChange(); + break; + } + + return n; + } + + /** + * Try to fold .substr() calls on strings + */ + private Node tryFoldStringSubstr(Node n, Node stringNode, Node arg1) { + Preconditions.checkArgument(n.isCall()); + Preconditions.checkArgument(stringNode.isString()); + + int start, length; + String stringAsString = stringNode.getString(); + + // TODO(nicksantos): We really need a NodeUtil.getNumberValue + // function. + if (arg1 != null && arg1.isNumber()) { + start = (int) arg1.getDouble(); + } else { + return n; + } + + Node arg2 = arg1.getNext(); + if (arg2 != null) { + if (arg2.isNumber()) { + length = (int) arg2.getDouble(); + } else { + return n; + } + + if (arg2.getNext() != null) { + // If we got more args than we expected, bail out. + return n; + } + } else { + // parameter 2 not passed + length = stringAsString.length() - start; + } + + // Don't handle these cases. The specification actually does + // specify the behavior in some of these cases, but we haven't + // done a thorough investigation that it is correctly implemented + // in all browsers. + if ((start + length) > stringAsString.length() || + (length < 0) || + (start < 0)) { + return n; + } + + String result = stringAsString.substring(start, start + length); + Node resultNode = IR.string(result); + + Node parent = n.getParent(); + parent.replaceChild(n, resultNode); + reportCodeChange(); + return resultNode; + } + + /** + * Try to fold .substring() calls on strings + */ + private Node tryFoldStringSubstring(Node n, Node stringNode, Node arg1) { + Preconditions.checkArgument(n.isCall()); + Preconditions.checkArgument(stringNode.isString()); + + int start, end; + String stringAsString = stringNode.getString(); + + if (arg1 != null && arg1.isNumber()) { + start = (int) arg1.getDouble(); + } else { + return n; + } + + Node arg2 = arg1.getNext(); + if (arg2 != null) { + if (arg2.isNumber()) { + end = (int) arg2.getDouble(); + } else { + return n; + } + + if (arg2.getNext() != null) { + // If we got more args than we expected, bail out. + return n; + } + } else { + // parameter 2 not passed + end = stringAsString.length(); + } + + // Don't handle these cases. The specification actually does + // specify the behavior in some of these cases, but we haven't + // done a thorough investigation that it is correctly implemented + // in all browsers. + if ((end > stringAsString.length()) || + (start > stringAsString.length()) || + (end < 0) || + (start < 0)) { + return n; + } + + String result = stringAsString.substring(start, end); + Node resultNode = IR.string(result); + + Node parent = n.getParent(); + parent.replaceChild(n, resultNode); + reportCodeChange(); + return resultNode; + } + + /** + * Try to fold .charAt() calls on strings + */ + private Node tryFoldStringCharAt(Node n, Node stringNode, Node arg1) { + Preconditions.checkArgument(n.isCall()); + Preconditions.checkArgument(stringNode.isString()); + + int index; + String stringAsString = stringNode.getString(); + + if (arg1 != null && arg1.isNumber() + && arg1.getNext() == null) { + index = (int) arg1.getDouble(); + } else { + return n; + } + + if (index < 0 || stringAsString.length() <= index) { + // http://es5.github.com/#x15.5.4.4 says "" is returned when index is + // out of bounds but we bail. + return n; + } + + Node resultNode = IR.string( + stringAsString.substring(index, index + 1)); + Node parent = n.getParent(); + parent.replaceChild(n, resultNode); + reportCodeChange(); + return resultNode; + } + + /** + * Try to fold .charCodeAt() calls on strings + */ + private Node tryFoldStringCharCodeAt(Node n, Node stringNode, Node arg1) { + Preconditions.checkArgument(n.isCall()); + Preconditions.checkArgument(stringNode.isString()); + + int index; + String stringAsString = stringNode.getString(); + + if (arg1 != null && arg1.isNumber() + && arg1.getNext() == null) { + index = (int) arg1.getDouble(); + } else { + return n; + } + + if (index < 0 || stringAsString.length() <= index) { + // http://es5.github.com/#x15.5.4.5 says NaN is returned when index is + // out of bounds but we bail. + return n; + } + + Node resultNode = IR.number(stringAsString.charAt(index)); + Node parent = n.getParent(); + parent.replaceChild(n, resultNode); + reportCodeChange(); + return resultNode; + } + + /** + * Support function for jsSplit, find the first occurrence of + * separator within stringValue starting at startIndex. + */ + private int jsSplitMatch(String stringValue, int startIndex, + String separator) { + + if (startIndex + separator.length() > stringValue.length()) { + return -1; + } + + int matchIndex = stringValue.indexOf(separator, startIndex); + + if (matchIndex < 0) { + return -1; + } + + return matchIndex; + } + + /** + * Implement the JS String.split method using a string separator. + */ + private String[] jsSplit(String stringValue, String separator, int limit) { + Preconditions.checkArgument(limit >= 0); + Preconditions.checkArgument(stringValue != null); + + // For limits of 0, return an empty array + if (limit == 0) { + return new String[0]; + } + + // If a separator is not specified, return the entire string as + // the only element of an array. + if (separator == null) { + return new String[] {stringValue}; + } + + List splitStrings = Lists.newArrayList(); + + // If an empty string is specified for the separator, split apart each + // character of the string. + if (separator.length() == 0) { + for (int i = 0; i < stringValue.length() && i < limit; i++) { + splitStrings.add(stringValue.substring(i, i + 1)); + } + } else { + int startIndex = 0, matchIndex; + while ((matchIndex = + jsSplitMatch(stringValue, startIndex, separator)) >= 0 && + splitStrings.size() < limit) { + splitStrings.add(stringValue.substring(startIndex, matchIndex)); + + startIndex = matchIndex + separator.length(); + } + + if (splitStrings.size() < limit) { + if (startIndex < stringValue.length()) { + splitStrings.add(stringValue.substring(startIndex)); + } else { + splitStrings.add(""); + } + } + } + + return splitStrings.toArray(new String[splitStrings.size()]); + } + + /** + * Try to fold .split() calls on strings + */ + private Node tryFoldStringSplit(Node n, Node stringNode, Node arg1) { + if (late) { + return n; + } + + Preconditions.checkArgument(n.isCall()); + Preconditions.checkArgument(stringNode.isString()); + + String separator = null; + String stringValue = stringNode.getString(); + + // Maximum number of possible splits + int limit = stringValue.length() + 1; + + if (arg1 != null) { + if (arg1.isString()) { + separator = arg1.getString(); + } else if (!arg1.isNull()) { + return n; + } + + Node arg2 = arg1.getNext(); + if (arg2 != null) { + if (arg2.isNumber()) { + limit = Math.min((int) arg2.getDouble(), limit); + if (limit < 0) { + return n; + } + } else { + return n; + } + } + } + + // Split the string and convert the returned array into JS nodes + String[] stringArray = jsSplit(stringValue, separator, limit); + Node arrayOfStrings = IR.arraylit(); + for (int i = 0; i < stringArray.length; i++) { + arrayOfStrings.addChildToBack( + IR.string(stringArray[i]).srcref(stringNode)); + } + + Node parent = n.getParent(); + parent.replaceChild(n, arrayOfStrings); + reportCodeChange(); + return arrayOfStrings; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeSimplifyRegExp.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeSimplifyRegExp.java new file mode 100644 index 0000000..8812cce --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeSimplifyRegExp.java @@ -0,0 +1,78 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.regex.RegExpTree; +import com.google.javascript.rhino.Node; + +/** + * Simplifies regular expression patterns and flags. + * + * @author Mike Samuel + */ +class PeepholeSimplifyRegExp extends AbstractPeepholeOptimization { + + @Override + Node optimizeSubtree(Node subtree) { + if (subtree.isRegExp()) { + // Split regexp into pattern and flags. + String pattern = subtree.getFirstChild().getString(); + String flags = subtree.getChildCount() == 2 + ? subtree.getLastChild().getString() : ""; + // Parse to an AST and optimize. + RegExpTree regexTree; + try { + regexTree = RegExpTree.parseRegExp(pattern, flags); + } catch (IllegalArgumentException ex) { + // Warnings are propagated in the CheckRegExp pass. + return subtree; + } + regexTree = regexTree.simplify(flags); + // Decompose the AST. + String literal = regexTree.toString(); + String newPattern = literal.substring(1, literal.length() - 1); + // Remove unnecessary flags and order them consistently for gzip. + String newFlags = ( + // The g flags cannot match or replace more than one instance if it is + // anchored at the front and back as in /^foo$/ and if the anchors are + // relative to the whole string. + // But if the regex has capturing groups, then the match operator + // would return capturing groups without the g flag. + (flags.contains("g") + && (!RegExpTree.matchesWholeInput(regexTree, flags) + || regexTree.hasCapturingGroup()) + ? "g" : "") + // Remove the i flag if it doesn't have any effect. + // E.g. /[a-z0-9_]/i -> /\w/ + + (flags.contains("i") && regexTree.isCaseSensitive() ? "i" : "") + // If the regular expression contains no anchors, then the m flag has + // no effect. + + (flags.contains("m") && regexTree.containsAnchor() ? "m" : "")); + // Update the original if something was done. + if (!(newPattern.equals(pattern) && newFlags.equals(flags))) { + subtree.getFirstChild().setString(newPattern); + if (!"".equals(newFlags)) { + subtree.getLastChild().setString(newFlags); + } else if (subtree.getChildCount() == 2) { + subtree.getLastChild().detachFromParent(); + } + reportCodeChange(); + } + } + return subtree; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeSubstituteAlternateSyntax.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeSubstituteAlternateSyntax.java new file mode 100644 index 0000000..ae568e8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PeepholeSubstituteAlternateSyntax.java @@ -0,0 +1,1725 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableSet; +import com.google.javascript.jscomp.CodingConvention.Bind; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.TernaryValue; + +import java.util.regex.Pattern; + +/** + * A peephole optimization that minimizes code by simplifying conditional + * expressions, replacing IFs with HOOKs, replacing object constructors + * with literals, and simplifying returns. + * + */ +class PeepholeSubstituteAlternateSyntax + extends AbstractPeepholeOptimization { + + private static final int AND_PRECEDENCE = NodeUtil.precedence(Token.AND); + private static final int OR_PRECEDENCE = NodeUtil.precedence(Token.OR); + private static final int NOT_PRECEDENCE = NodeUtil.precedence(Token.NOT); + private static final CodeGenerator REGEXP_ESCAPER = + CodeGenerator.forCostEstimation( + null /* blow up if we try to produce code */); + + private final boolean late; + + private final int STRING_SPLIT_OVERHEAD = ".split('.')".length(); + + static final DiagnosticType INVALID_REGULAR_EXPRESSION_FLAGS = + DiagnosticType.warning( + "JSC_INVALID_REGULAR_EXPRESSION_FLAGS", + "Invalid flags to RegExp constructor: {0}"); + + static final Predicate DONT_TRAVERSE_FUNCTIONS_PREDICATE + = new Predicate() { + @Override + public boolean apply(Node input) { + return !input.isFunction(); + } + }; + + /** + * @param late When late is false, this mean we are currently running before + * most of the other optimizations. In this case we would avoid optimizations + * that would make the code harder to analyze (such as using string splitting, + * merging statements with commas, etc). When this is true, we would + * do anything to minimize for size. + */ + PeepholeSubstituteAlternateSyntax(boolean late) { + this.late = late; + } + + /** + * Tries apply our various peephole minimizations on the passed in node. + */ + @Override + @SuppressWarnings("fallthrough") + public Node optimizeSubtree(Node node) { + switch(node.getType()) { + case Token.RETURN: { + Node result = tryRemoveRedundantExit(node); + if (result != node) { + return result; + } + result = tryReplaceExitWithBreak(node); + if (result != node) { + return result; + } + return tryReduceReturn(node); + } + + case Token.THROW: { + Node result = tryRemoveRedundantExit(node); + if (result != node) { + return result; + } + return tryReplaceExitWithBreak(node); + } + + // TODO(johnlenz): Maybe remove redundant BREAK and CONTINUE. Overlaps + // with MinimizeExitPoints. + + case Token.NOT: + tryMinimizeCondition(node.getFirstChild()); + return tryMinimizeNot(node); + + case Token.IF: + tryMinimizeCondition(node.getFirstChild()); + return tryMinimizeIf(node); + + case Token.EXPR_RESULT: + tryMinimizeCondition(node.getFirstChild()); + return node; + + case Token.HOOK: + tryMinimizeCondition(node.getFirstChild()); + return node; + + case Token.WHILE: + case Token.DO: + tryMinimizeCondition(NodeUtil.getConditionExpression(node)); + return node; + + case Token.FOR: + if (!NodeUtil.isForIn(node)) { + tryJoinForCondition(node); + tryMinimizeCondition(NodeUtil.getConditionExpression(node)); + } + return node; + + case Token.TRUE: + case Token.FALSE: + return reduceTrueFalse(node); + + case Token.NEW: + node = tryFoldStandardConstructors(node); + if (!node.isCall()) { + return node; + } + // Fall through on purpose because tryFoldStandardConstructors() may + // convert a NEW node into a CALL node + case Token.CALL: + Node result = tryFoldLiteralConstructor(node); + if (result == node) { + result = tryFoldSimpleFunctionCall(node); + if (result == node) { + result = tryFoldImmediateCallToBoundFunction(node); + } + } + return result; + + case Token.COMMA: + return trySplitComma(node); + + case Token.NAME: + return tryReplaceUndefined(node); + + case Token.BLOCK: + return tryReplaceIf(node); + + case Token.ARRAYLIT: + return tryMinimizeArrayLiteral(node); + + default: + return node; //Nothing changed + } + } + + private void tryJoinForCondition(Node n) { + if (!late) { + return; + } + + Node block = n.getLastChild(); + Node maybeIf = block.getFirstChild(); + if (maybeIf != null && maybeIf.isIf()) { + Node maybeBreak = maybeIf.getChildAtIndex(1).getFirstChild(); + if (maybeBreak != null && maybeBreak.isBreak() + && !maybeBreak.hasChildren()) { + + // Preserve the IF ELSE expression is there is one. + if (maybeIf.getChildCount() == 3) { + block.replaceChild(maybeIf, + maybeIf.getLastChild().detachFromParent()); + } else { + block.removeFirstChild(); + } + + Node ifCondition = maybeIf.removeFirstChild(); + Node fixedIfCondition = IR.not(ifCondition) + .srcref(ifCondition); + + // OK, join the IF expression with the FOR expression + Node forCondition = NodeUtil.getConditionExpression(n); + if (forCondition.isEmpty()) { + n.replaceChild(forCondition, fixedIfCondition); + } else { + Node replacement = new Node(Token.AND); + n.replaceChild(forCondition, replacement); + replacement.addChildToBack(forCondition); + replacement.addChildToBack(fixedIfCondition); + } + + reportCodeChange(); + } + } + } + + private Node tryFoldSimpleFunctionCall(Node n) { + Preconditions.checkState(n.isCall()); + Node callTarget = n.getFirstChild(); + if (callTarget != null && callTarget.isName() && + callTarget.getString().equals("String")) { + // Fold String(a) to '' + (a) on immutable literals, + // which allows further optimizations + // + // We can't do this in the general case, because String(a) has + // slightly different semantics than '' + (a). See + // http://code.google.com/p/closure-compiler/issues/detail?id=759 + Node value = callTarget.getNext(); + if (value != null && value.getNext() == null && + NodeUtil.isImmutableValue(value)) { + Node addition = IR.add( + IR.string("").srcref(callTarget), + value.detachFromParent()); + n.getParent().replaceChild(n, addition); + reportCodeChange(); + return addition; + } + } + return n; + } + + private Node tryFoldImmediateCallToBoundFunction(Node n) { + // Rewriting "(fn.bind(a,b))()" to "fn.call(a,b)" makes it inlinable + Preconditions.checkState(n.isCall()); + Node callTarget = n.getFirstChild(); + Bind bind = getCodingConvention().describeFunctionBind(callTarget, false); + if (bind != null) { + // replace the call target + bind.target.detachFromParent(); + n.replaceChild(callTarget, bind.target); + callTarget = bind.target; + + // push the parameters + addParameterAfter(bind.parameters, callTarget); + + // add the this value before the parameters if necessary + if (bind.thisValue != null && !NodeUtil.isUndefined(bind.thisValue)) { + // rewrite from "fn(a, b)" to "fn.call(thisValue, a, b)" + Node newCallTarget = IR.getprop( + callTarget.cloneTree(), + IR.string("call").srcref(callTarget)); + n.replaceChild(callTarget, newCallTarget); + n.addChildAfter(bind.thisValue.cloneTree(), newCallTarget); + n.putBooleanProp(Node.FREE_CALL, false); + } else { + n.putBooleanProp(Node.FREE_CALL, true); + } + reportCodeChange(); + } + return n; + } + + private void addParameterAfter(Node parameterList, Node after) { + if (parameterList != null) { + // push the last parameter to the head of the list first. + addParameterAfter(parameterList.getNext(), after); + after.getParent().addChildAfter(parameterList.cloneTree(), after); + } + } + + private Node trySplitComma(Node n) { + if (late) { + return n; + } + Node parent = n.getParent(); + Node left = n.getFirstChild(); + Node right = n.getLastChild(); + + if (parent.isExprResult() + && !parent.getParent().isLabel()) { + // split comma + n.detachChildren(); + // Replace the original expression with the left operand. + parent.replaceChild(n, left); + // Add the right expression afterward. + Node newStatement = IR.exprResult(right); + newStatement.copyInformationFrom(n); + + //This modifies outside the subtree, which is not + //desirable in a peephole optimization. + parent.getParent().addChildAfter(newStatement, parent); + reportCodeChange(); + return left; + } else { + return n; + } + } + + /** + * Use "return x?1:2;" in place of "if(x)return 1;return 2;" + */ + private Node tryReplaceIf(Node n) { + + for (Node child = n.getFirstChild(); + child != null; child = child.getNext()){ + if (child.isIf()){ + Node cond = child.getFirstChild(); + Node thenBranch = cond.getNext(); + Node elseBranch = thenBranch.getNext(); + Node nextNode = child.getNext(); + + if (nextNode != null && elseBranch == null + && isReturnBlock(thenBranch) + && nextNode.isIf()) { + Node nextCond = nextNode.getFirstChild(); + Node nextThen = nextCond.getNext(); + Node nextElse = nextThen.getNext(); + if (thenBranch.isEquivalentToTyped(nextThen)) { + // Transform + // if (x) return 1; if (y) return 1; + // to + // if (x||y) return 1; + child.detachFromParent(); + child.detachChildren(); + Node newCond = new Node(Token.OR, cond); + nextNode.replaceChild(nextCond, newCond); + newCond.addChildToBack(nextCond); + reportCodeChange(); + } else if (nextElse != null + && thenBranch.isEquivalentToTyped(nextElse)) { + // Transform + // if (x) return 1; if (y) foo() else return 1; + // to + // if (!x&&y) foo() else return 1; + child.detachFromParent(); + child.detachChildren(); + Node newCond = new Node(Token.AND, + IR.not(cond).srcref(cond)); + nextNode.replaceChild(nextCond, newCond); + newCond.addChildToBack(nextCond); + reportCodeChange(); + } + } else if (nextNode != null && elseBranch == null && + isReturnBlock(thenBranch) && isReturnExpression(nextNode)) { + Node thenExpr = null; + // if(x)return; return 1 -> return x?void 0:1 + if (isReturnExpressBlock(thenBranch)) { + thenExpr = getBlockReturnExpression(thenBranch); + thenExpr.detachFromParent(); + } else { + thenExpr = NodeUtil.newUndefinedNode(child); + } + + Node elseExpr = nextNode.getFirstChild(); + + cond.detachFromParent(); + elseExpr.detachFromParent(); + + Node returnNode = IR.returnNode( + IR.hook(cond, thenExpr, elseExpr) + .srcref(child)); + n.replaceChild(child, returnNode); + n.removeChild(nextNode); + reportCodeChange(); + } else if (elseBranch != null && statementMustExitParent(thenBranch)) { + child.removeChild(elseBranch); + n.addChildAfter(elseBranch, child); + reportCodeChange(); + } + } + } + return n; + } + + private boolean statementMustExitParent(Node n) { + switch (n.getType()) { + case Token.THROW: + case Token.RETURN: + return true; + case Token.BLOCK: + if (n.hasChildren()) { + Node child = n.getLastChild(); + return statementMustExitParent(child); + } + return false; + // TODO(johnlenz): handle TRY/FINALLY + case Token.FUNCTION: + default: + return false; + } + } + + /** + * Use "void 0" in place of "undefined" + */ + private Node tryReplaceUndefined(Node n) { + // TODO(johnlenz): consider doing this as a normalization. + if (isASTNormalized() + && NodeUtil.isUndefined(n) + && !NodeUtil.isLValue(n)) { + Node replacement = NodeUtil.newUndefinedNode(n); + n.getParent().replaceChild(n, replacement); + reportCodeChange(); + return replacement; + } + return n; + } + + /** + * Reduce "return undefined" or "return void 0" to simply "return". + * + * @return The original node, maybe simplified. + */ + private Node tryReduceReturn(Node n) { + Node result = n.getFirstChild(); + + if (result != null) { + switch (result.getType()) { + case Token.VOID: + Node operand = result.getFirstChild(); + if (!mayHaveSideEffects(operand)) { + n.removeFirstChild(); + reportCodeChange(); + } + break; + case Token.NAME: + String name = result.getString(); + if (name.equals("undefined")) { + n.removeFirstChild(); + reportCodeChange(); + } + break; + } + } + + return n; + } + + /** + * Replace duplicate exits in control structures. If the node following + * the exit node expression has the same effect as exit node, the node can + * be replaced or removed. + * For example: + * "while (a) {return f()} return f();" ==> "while (a) {break} return f();" + * "while (a) {throw 'ow'} throw 'ow';" ==> "while (a) {break} throw 'ow';" + * + * @param n An follow control exit expression (a THROW or RETURN node) + * @return The replacement for n, or the original if no change was made. + */ + private Node tryReplaceExitWithBreak(Node n) { + Node result = n.getFirstChild(); + + // Find the enclosing control structure, if any, that a "break" would exit + // from. + Node breakTarget = n; + for (;!ControlFlowAnalysis.isBreakTarget(breakTarget, null /* no label */); + breakTarget = breakTarget.getParent()) { + if (breakTarget.isFunction() || breakTarget.isScript()) { + // No break target. + return n; + } + } + + Node follow = ControlFlowAnalysis.computeFollowNode(breakTarget); + + // Skip pass all the finally blocks because both the break and return will + // also trigger all the finally blocks. However, the order of execution is + // slightly changed. Consider: + // + // return a() -> finally { b() } -> return a() + // + // which would call a() first. However, changing the first return to a + // break will result in calling b(). + + Node prefinallyFollows = follow; + follow = skipFinallyNodes(follow); + + if (prefinallyFollows != follow) { + // There were finally clauses + if (!isPure(result)) { + // Can't defer the exit + return n; + } + } + + if (follow == null && (n.isThrow() || result != null)) { + // Can't complete remove a throw here or a return with a result. + return n; + } + + // When follow is null, this mean the follow of a break target is the + // end of a function. This means a break is same as return. + if (follow == null || areMatchingExits(n, follow)) { + Node replacement = IR.breakNode(); + n.getParent().replaceChild(n, replacement); + this.reportCodeChange(); + return replacement; + } + + return n; + } + + /** + * Remove duplicate exits. If the node following the exit node expression + * has the same effect as exit node, the node can be removed. + * For example: + * "if (a) {return f()} return f();" ==> "if (a) {} return f();" + * "if (a) {throw 'ow'} throw 'ow';" ==> "if (a) {} throw 'ow';" + * + * @param n An follow control exit expression (a THROW or RETURN node) + * @return The replacement for n, or the original if no change was made. + */ + private Node tryRemoveRedundantExit(Node n) { + Node exitExpr = n.getFirstChild(); + + Node follow = ControlFlowAnalysis.computeFollowNode(n); + + // Skip pass all the finally blocks because both the fall through and return + // will also trigger all the finally blocks. + Node prefinallyFollows = follow; + follow = skipFinallyNodes(follow); + if (prefinallyFollows != follow) { + // There were finally clauses + if (!isPure(exitExpr)) { + // Can't replace the return + return n; + } + } + + if (follow == null && (n.isThrow() || exitExpr != null)) { + // Can't complete remove a throw here or a return with a result. + return n; + } + + // When follow is null, this mean the follow of a break target is the + // end of a function. This means a break is same as return. + if (follow == null || areMatchingExits(n, follow)) { + n.detachFromParent(); + reportCodeChange(); + return null; + } + + return n; + } + + /** + * @return Whether the expression does not produces and can not be affected + * by side-effects. + */ + boolean isPure(Node n) { + return n == null + || (!NodeUtil.canBeSideEffected(n) + && !mayHaveSideEffects(n)); + } + + /** + * @return n or the node following any following finally nodes. + */ + Node skipFinallyNodes(Node n) { + while (n != null && NodeUtil.isTryFinallyNode(n.getParent(), n)) { + n = ControlFlowAnalysis.computeFollowNode(n); + } + return n; + } + + /** + * Check whether one exit can be replaced with another. Verify: + * 1) They are identical expressions + * 2) If an exception is possible that the statements, the original + * and the potential replacement are in the same exception handler. + */ + boolean areMatchingExits(Node nodeThis, Node nodeThat) { + return nodeThis.isEquivalentTo(nodeThat) + && (!isExceptionPossible(nodeThis) + || getExceptionHandler(nodeThis) == getExceptionHandler(nodeThat)); + } + + boolean isExceptionPossible(Node n) { + // TODO(johnlenz): maybe use ControlFlowAnalysis.mayThrowException? + Preconditions.checkState(n.isReturn() + || n.isThrow()); + return n.isThrow() + || (n.hasChildren() + && !NodeUtil.isLiteralValue(n.getLastChild(), true)); + } + + Node getExceptionHandler(Node n) { + return ControlFlowAnalysis.getExceptionHandler(n); + } + + /** + * Try to minimize NOT nodes such as !(x==y). + * + * Returns the replacement for n or the original if no change was made + */ + private Node tryMinimizeNot(Node n) { + Node parent = n.getParent(); + + Node notChild = n.getFirstChild(); + // negative operator of the current one : == -> != for instance. + int complementOperator; + switch (notChild.getType()) { + case Token.EQ: + complementOperator = Token.NE; + break; + case Token.NE: + complementOperator = Token.EQ; + break; + case Token.SHEQ: + complementOperator = Token.SHNE; + break; + case Token.SHNE: + complementOperator = Token.SHEQ; + break; + // GT, GE, LT, LE are not handled in this because !(x=NaN. + default: + return n; + } + Node newOperator = n.removeFirstChild(); + newOperator.setType(complementOperator); + parent.replaceChild(n, newOperator); + reportCodeChange(); + return newOperator; + } + + /** + * Try turning IF nodes into smaller HOOKs + * + * Returns the replacement for n or the original if no replacement was + * necessary. + */ + private Node tryMinimizeIf(Node n) { + + Node parent = n.getParent(); + + Node cond = n.getFirstChild(); + + /* If the condition is a literal, we'll let other + * optimizations try to remove useless code. + */ + if (NodeUtil.isLiteralValue(cond, true)) { + return n; + } + + Node thenBranch = cond.getNext(); + Node elseBranch = thenBranch.getNext(); + + if (elseBranch == null) { + if (isFoldableExpressBlock(thenBranch)) { + Node expr = getBlockExpression(thenBranch); + if (!late && isPropertyAssignmentInExpression(expr)) { + // Keep opportunities for CollapseProperties such as + // a.longIdentifier || a.longIdentifier = ... -> var a = ...; + // until CollapseProperties has been run. + return n; + } + + if (cond.isNot()) { + // if(!x)bar(); -> x||bar(); + if (isLowerPrecedenceInExpression(cond, OR_PRECEDENCE) && + isLowerPrecedenceInExpression(expr.getFirstChild(), + OR_PRECEDENCE)) { + // It's not okay to add two sets of parentheses. + return n; + } + + Node or = IR.or( + cond.removeFirstChild(), + expr.removeFirstChild()).srcref(n); + Node newExpr = NodeUtil.newExpr(or); + parent.replaceChild(n, newExpr); + reportCodeChange(); + + return newExpr; + } + + // if(x)foo(); -> x&&foo(); + if (isLowerPrecedenceInExpression(cond, AND_PRECEDENCE) && + isLowerPrecedenceInExpression(expr.getFirstChild(), + AND_PRECEDENCE)) { + // One additional set of parentheses is worth the change even if + // there is no immediate code size win. However, two extra pair of + // {}, we would have to think twice. (unless we know for sure the + // we can further optimize its parent. + return n; + } + + n.removeChild(cond); + Node and = IR.and(cond, expr.removeFirstChild()).srcref(n); + Node newExpr = NodeUtil.newExpr(and); + parent.replaceChild(n, newExpr); + reportCodeChange(); + + return newExpr; + } else { + + // Try to combine two IF-ELSE + if (NodeUtil.isStatementBlock(thenBranch) && + thenBranch.hasOneChild()) { + Node innerIf = thenBranch.getFirstChild(); + + if (innerIf.isIf()) { + Node innerCond = innerIf.getFirstChild(); + Node innerThenBranch = innerCond.getNext(); + Node innerElseBranch = innerThenBranch.getNext(); + + if (innerElseBranch == null && + !(isLowerPrecedenceInExpression(cond, AND_PRECEDENCE) && + isLowerPrecedenceInExpression(innerCond, AND_PRECEDENCE))) { + n.detachChildren(); + n.addChildToBack( + IR.and( + cond, + innerCond.detachFromParent()) + .srcref(cond)); + n.addChildrenToBack(innerThenBranch.detachFromParent()); + reportCodeChange(); + // Not worth trying to fold the current IF-ELSE into && because + // the inner IF-ELSE wasn't able to be folded into && anyways. + return n; + } + } + } + } + + return n; + } + + /* TODO(dcc) This modifies the siblings of n, which is undesirable for a + * peephole optimization. This should probably get moved to another pass. + */ + tryRemoveRepeatedStatements(n); + + // if(!x)foo();else bar(); -> if(x)bar();else foo(); + // An additional set of curly braces isn't worth it. + if (cond.isNot() && !consumesDanglingElse(elseBranch)) { + n.replaceChild(cond, cond.removeFirstChild()); + n.removeChild(thenBranch); + n.addChildToBack(thenBranch); + reportCodeChange(); + return n; + } + + // if(x)return 1;else return 2; -> return x?1:2; + if (isReturnExpressBlock(thenBranch) && isReturnExpressBlock(elseBranch)) { + Node thenExpr = getBlockReturnExpression(thenBranch); + Node elseExpr = getBlockReturnExpression(elseBranch); + n.removeChild(cond); + thenExpr.detachFromParent(); + elseExpr.detachFromParent(); + + // note - we ignore any cases with "return;", technically this + // can be converted to "return undefined;" or some variant, but + // that does not help code size. + Node returnNode = IR.returnNode( + IR.hook(cond, thenExpr, elseExpr) + .srcref(n)); + parent.replaceChild(n, returnNode); + reportCodeChange(); + return returnNode; + } + + boolean thenBranchIsExpressionBlock = isFoldableExpressBlock(thenBranch); + boolean elseBranchIsExpressionBlock = isFoldableExpressBlock(elseBranch); + + if (thenBranchIsExpressionBlock && elseBranchIsExpressionBlock) { + Node thenOp = getBlockExpression(thenBranch).getFirstChild(); + Node elseOp = getBlockExpression(elseBranch).getFirstChild(); + if (thenOp.getType() == elseOp.getType()) { + // if(x)a=1;else a=2; -> a=x?1:2; + if (NodeUtil.isAssignmentOp(thenOp)) { + Node lhs = thenOp.getFirstChild(); + if (areNodesEqualForInlining(lhs, elseOp.getFirstChild()) && + // if LHS has side effects, don't proceed [since the optimization + // evaluates LHS before cond] + // NOTE - there are some circumstances where we can + // proceed even if there are side effects... + !mayEffectMutableState(lhs)) { + + n.removeChild(cond); + Node assignName = thenOp.removeFirstChild(); + Node thenExpr = thenOp.removeFirstChild(); + Node elseExpr = elseOp.getLastChild(); + elseOp.removeChild(elseExpr); + + Node hookNode = IR.hook(cond, thenExpr, elseExpr).srcref(n); + Node assign = new Node(thenOp.getType(), assignName, hookNode) + .srcref(thenOp); + Node expr = NodeUtil.newExpr(assign); + parent.replaceChild(n, expr); + reportCodeChange(); + + return expr; + } + } + } + // if(x)foo();else bar(); -> x?foo():bar() + n.removeChild(cond); + thenOp.detachFromParent(); + elseOp.detachFromParent(); + Node expr = IR.exprResult( + IR.hook(cond, thenOp, elseOp).srcref(n)); + parent.replaceChild(n, expr); + reportCodeChange(); + return expr; + } + + boolean thenBranchIsVar = isVarBlock(thenBranch); + boolean elseBranchIsVar = isVarBlock(elseBranch); + + // if(x)var y=1;else y=2 -> var y=x?1:2 + if (thenBranchIsVar && elseBranchIsExpressionBlock && + getBlockExpression(elseBranch).getFirstChild().isAssign()) { + + Node var = getBlockVar(thenBranch); + Node elseAssign = getBlockExpression(elseBranch).getFirstChild(); + + Node name1 = var.getFirstChild(); + Node maybeName2 = elseAssign.getFirstChild(); + + if (name1.hasChildren() + && maybeName2.isName() + && name1.getString().equals(maybeName2.getString())) { + Node thenExpr = name1.removeChildren(); + Node elseExpr = elseAssign.getLastChild().detachFromParent(); + cond.detachFromParent(); + Node hookNode = IR.hook(cond, thenExpr, elseExpr) + .srcref(n); + var.detachFromParent(); + name1.addChildrenToBack(hookNode); + parent.replaceChild(n, var); + reportCodeChange(); + return var; + } + + // if(x)y=1;else var y=2 -> var y=x?1:2 + } else if (elseBranchIsVar && thenBranchIsExpressionBlock && + getBlockExpression(thenBranch).getFirstChild().isAssign()) { + + Node var = getBlockVar(elseBranch); + Node thenAssign = getBlockExpression(thenBranch).getFirstChild(); + + Node maybeName1 = thenAssign.getFirstChild(); + Node name2 = var.getFirstChild(); + + if (name2.hasChildren() + && maybeName1.isName() + && maybeName1.getString().equals(name2.getString())) { + Node thenExpr = thenAssign.getLastChild().detachFromParent(); + Node elseExpr = name2.removeChildren(); + cond.detachFromParent(); + Node hookNode = IR.hook(cond, thenExpr, elseExpr) + .srcref(n); + var.detachFromParent(); + name2.addChildrenToBack(hookNode); + parent.replaceChild(n, var); + reportCodeChange(); + + return var; + } + } + + return n; + } + + /** + * Try to remove duplicate statements from IF blocks. For example: + * + * if (a) { + * x = 1; + * return true; + * } else { + * x = 2; + * return true; + * } + * + * becomes: + * + * if (a) { + * x = 1; + * } else { + * x = 2; + * } + * return true; + * + * @param n The IF node to examine. + */ + private void tryRemoveRepeatedStatements(Node n) { + Preconditions.checkState(n.isIf()); + + Node parent = n.getParent(); + if (!NodeUtil.isStatementBlock(parent)) { + // If the immediate parent is something like a label, we + // can't move the statement, so bail. + return; + } + + Node cond = n.getFirstChild(); + Node trueBranch = cond.getNext(); + Node falseBranch = trueBranch.getNext(); + Preconditions.checkNotNull(trueBranch); + Preconditions.checkNotNull(falseBranch); + + while (true) { + Node lastTrue = trueBranch.getLastChild(); + Node lastFalse = falseBranch.getLastChild(); + if (lastTrue == null || lastFalse == null + || !areNodesEqualForInlining(lastTrue, lastFalse)) { + break; + } + lastTrue.detachFromParent(); + lastFalse.detachFromParent(); + parent.addChildAfter(lastTrue, n); + reportCodeChange(); + } + } + + /** + * @return Whether the node is a block with a single statement that is + * an expression. + */ + private boolean isFoldableExpressBlock(Node n) { + if (n.isBlock()) { + if (n.hasOneChild()) { + Node maybeExpr = n.getFirstChild(); + if (maybeExpr.isExprResult()) { + // IE has a bug where event handlers behave differently when + // their return value is used vs. when their return value is in + // an EXPR_RESULT. It's pretty freaking weird. See: + // http://code.google.com/p/closure-compiler/issues/detail?id=291 + // We try to detect this case, and not fold EXPR_RESULTs + // into other expressions. + if (maybeExpr.getFirstChild().isCall()) { + Node calledFn = maybeExpr.getFirstChild().getFirstChild(); + + // We only have to worry about methods with an implicit 'this' + // param, or this doesn't happen. + if (calledFn.isGetElem()) { + return false; + } else if (calledFn.isGetProp() && + calledFn.getLastChild().getString().startsWith("on")) { + return false; + } + } + + return true; + } + return false; + } + } + + return false; + } + + /** + * @return The expression node. + */ + private Node getBlockExpression(Node n) { + Preconditions.checkState(isFoldableExpressBlock(n)); + return n.getFirstChild(); + } + + /** + * @return Whether the node is a block with a single statement that is + * an return with or without an expression. + */ + private boolean isReturnBlock(Node n) { + if (n.isBlock()) { + if (n.hasOneChild()) { + Node first = n.getFirstChild(); + return first.isReturn(); + } + } + + return false; + } + + /** + * @return Whether the node is a block with a single statement that is + * an return. + */ + private boolean isReturnExpressBlock(Node n) { + if (n.isBlock()) { + if (n.hasOneChild()) { + Node first = n.getFirstChild(); + if (first.isReturn()) { + return first.hasOneChild(); + } + } + } + + return false; + } + + /** + * @return Whether the node is a single return statement. + */ + private boolean isReturnExpression(Node n) { + if (n.isReturn()) { + return n.hasOneChild(); + } + return false; + } + + /** + * @return The expression that is part of the return. + */ + private Node getBlockReturnExpression(Node n) { + Preconditions.checkState(isReturnExpressBlock(n)); + return n.getFirstChild().getFirstChild(); + } + + /** + * @return Whether the node is a block with a single statement that is + * a VAR declaration of a single variable. + */ + private boolean isVarBlock(Node n) { + if (n.isBlock()) { + if (n.hasOneChild()) { + Node first = n.getFirstChild(); + if (first.isVar()) { + return first.hasOneChild(); + } + } + } + + return false; + } + + /** + * @return The var node. + */ + private Node getBlockVar(Node n) { + Preconditions.checkState(isVarBlock(n)); + return n.getFirstChild(); + } + + /** + * Does a statement consume a 'dangling else'? A statement consumes + * a 'dangling else' if an 'else' token following the statement + * would be considered by the parser to be part of the statement. + */ + private boolean consumesDanglingElse(Node n) { + while (true) { + switch (n.getType()) { + case Token.IF: + if (n.getChildCount() < 3) { + return true; + } + // This IF node has no else clause. + n = n.getLastChild(); + continue; + case Token.WITH: + case Token.WHILE: + case Token.FOR: + n = n.getLastChild(); + continue; + default: + return false; + } + } + } + + /** + * Does the expression contain an operator with lower precedence than + * the argument? + */ + private boolean isLowerPrecedenceInExpression(Node n, + final int precedence) { + Predicate isLowerPrecedencePredicate = new Predicate() { + @Override + public boolean apply(Node input) { + return NodeUtil.precedence(input.getType()) < precedence; + } + }; + + return NodeUtil.has(n, isLowerPrecedencePredicate, + DONT_TRAVERSE_FUNCTIONS_PREDICATE); + } + + /** + * Whether the node type has lower precedence than "precedence" + */ + private boolean isLowerPrecedence(Node n, final int precedence) { + return NodeUtil.precedence(n.getType()) < precedence; + } + + /** + * Whether the node type has higher precedence than "precedence" + */ + private boolean isHigherPrecedence(Node n, final int precedence) { + return NodeUtil.precedence(n.getType()) > precedence; + } + /** + * Does the expression contain a property assignment? + */ + private boolean isPropertyAssignmentInExpression(Node n) { + Predicate isPropertyAssignmentInExpressionPredicate = + new Predicate() { + @Override + public boolean apply(Node input) { + return (input.isGetProp() && + input.getParent().isAssign()); + } + }; + + return NodeUtil.has(n, isPropertyAssignmentInExpressionPredicate, + DONT_TRAVERSE_FUNCTIONS_PREDICATE); + } + + /** + * Try to minimize conditions expressions, as there are additional + * assumptions that can be made when it is known that the final result + * is a boolean. + * + * The following transformations are done recursively: + * !(x||y) --> !x&&!y + * !(x&&y) --> !x||!y + * !!x --> x + * Thus: + * !(x&&!y) --> !x||!!y --> !x||y + * + * Returns the replacement for n, or the original if no change was made + */ + private Node tryMinimizeCondition(Node n) { + Node parent = n.getParent(); + + switch (n.getType()) { + case Token.NOT: + Node first = n.getFirstChild(); + switch (first.getType()) { + case Token.NOT: { + Node newRoot = first.removeFirstChild(); + parent.replaceChild(n, newRoot); + reportCodeChange(); + // No need to traverse, tryMinimizeCondition is called on the + // NOT children are handled below. + return newRoot; + } + case Token.AND: + case Token.OR: { + // !(!x && !y) --> x || y + // !(!x || !y) --> x && y + // !(!x && y) --> x || !y + // !(!x || y) --> x && !y + // !(x && !y) --> !x || y + // !(x || !y) --> !x && y + // !(x && y) --> !x || !y + // !(x || y) --> !x && !y + Node leftParent = first.getFirstChild(); + Node rightParent = first.getLastChild(); + Node left, right; + + // Check special case when such transformation cannot reduce + // due to the added () + // It only occurs when both of expressions are not NOT expressions + if (!leftParent.isNot() + && !rightParent.isNot()) { + // If an expression has higher precedence than && or ||, + // but lower precedence than NOT, an additional () is needed + // Thus we do not preceed + int op_precedence = NodeUtil.precedence(first.getType()); + if ((isLowerPrecedence(leftParent, NOT_PRECEDENCE) + && isHigherPrecedence(leftParent, op_precedence)) + || (isLowerPrecedence(rightParent, NOT_PRECEDENCE) + && isHigherPrecedence(rightParent, op_precedence))) { + return n; + } + } + + if (leftParent.isNot()) { + left = leftParent.removeFirstChild(); + } else { + leftParent.detachFromParent(); + left = IR.not(leftParent).srcref(leftParent); + } + if (rightParent.isNot()) { + right = rightParent.removeFirstChild(); + } else { + rightParent.detachFromParent(); + right = IR.not(rightParent).srcref(rightParent); + } + + int newOp = (first.isAnd()) ? Token.OR : Token.AND; + Node newRoot = new Node(newOp, left, right); + parent.replaceChild(n, newRoot); + reportCodeChange(); + // No need to traverse, tryMinimizeCondition is called on the + // AND and OR children below. + return newRoot; + } + + default: + TernaryValue nVal = NodeUtil.getPureBooleanValue(first); + if (nVal != TernaryValue.UNKNOWN) { + boolean result = nVal.not().toBoolean(true); + int equivalentResult = result ? 1 : 0; + return maybeReplaceChildWithNumber(n, parent, equivalentResult); + } + } + // No need to traverse, tryMinimizeCondition is called on the NOT + // children in the general case in the main post-order traversal. + return n; + + case Token.OR: + case Token.AND: { + Node left = n.getFirstChild(); + Node right = n.getLastChild(); + + // Because the expression is in a boolean context minimize + // the children, this can't be done in the general case. + left = tryMinimizeCondition(left); + right = tryMinimizeCondition(right); + + // Remove useless conditionals + // Handle four cases: + // x || false --> x + // x || true --> true + // x && true --> x + // x && false --> false + TernaryValue rightVal = NodeUtil.getPureBooleanValue(right); + if (NodeUtil.getPureBooleanValue(right) != TernaryValue.UNKNOWN) { + int type = n.getType(); + Node replacement = null; + boolean rval = rightVal.toBoolean(true); + + // (x || FALSE) => x + // (x && TRUE) => x + if (type == Token.OR && !rval || + type == Token.AND && rval) { + replacement = left; + } else if (!mayHaveSideEffects(left)) { + replacement = right; + } + + if (replacement != null) { + n.detachChildren(); + parent.replaceChild(n, replacement); + reportCodeChange(); + return replacement; + } + } + return n; + } + + case Token.HOOK: { + Node condition = n.getFirstChild(); + Node trueNode = n.getFirstChild().getNext(); + Node falseNode = n.getLastChild(); + + // Because the expression is in a boolean context minimize + // the result children, this can't be done in the general case. + // The condition is handled in the general case in #optimizeSubtree + trueNode = tryMinimizeCondition(trueNode); + falseNode = tryMinimizeCondition(falseNode); + + // Handle four cases: + // x ? true : false --> x + // x ? false : true --> !x + // x ? true : y --> x || y + // x ? y : false --> x && y + Node replacement = null; + TernaryValue trueNodeVal = NodeUtil.getPureBooleanValue(trueNode); + TernaryValue falseNodeVal = NodeUtil.getPureBooleanValue(falseNode); + if (trueNodeVal == TernaryValue.TRUE + && falseNodeVal == TernaryValue.FALSE) { + // Remove useless conditionals, keep the condition + condition.detachFromParent(); + replacement = condition; + } else if (trueNodeVal == TernaryValue.FALSE + && falseNodeVal == TernaryValue.TRUE) { + // Remove useless conditionals, keep the condition + condition.detachFromParent(); + replacement = IR.not(condition); + } else if (trueNodeVal == TernaryValue.TRUE) { + // Remove useless true case. + n.detachChildren(); + replacement = IR.or(condition, falseNode); + } else if (falseNodeVal == TernaryValue.FALSE) { + // Remove useless false case + n.detachChildren(); + replacement = IR.and(condition, trueNode); + } + + if (replacement != null) { + parent.replaceChild(n, replacement); + n = replacement; + reportCodeChange(); + } + + return n; + } + + default: + // while(true) --> while(1) + TernaryValue nVal = NodeUtil.getPureBooleanValue(n); + if (nVal != TernaryValue.UNKNOWN) { + boolean result = nVal.toBoolean(true); + int equivalentResult = result ? 1 : 0; + return maybeReplaceChildWithNumber(n, parent, equivalentResult); + } + // We can't do anything else currently. + return n; + } + } + + /** + * Replaces a node with a number node if the new number node is not equivalent + * to the current node. + * + * Returns the replacement for n if it was replaced, otherwise returns n. + */ + private Node maybeReplaceChildWithNumber(Node n, Node parent, int num) { + Node newNode = IR.number(num); + if (!newNode.isEquivalentTo(n)) { + parent.replaceChild(n, newNode); + reportCodeChange(); + + return newNode; + } + + return n; + } + + private static final ImmutableSet STANDARD_OBJECT_CONSTRUCTORS = + // String, Number, and Boolean functions return non-object types, whereas + // new String, new Number, and new Boolean return object types, so don't + // include them here. + ImmutableSet.of( + "Object", + "Array", + "RegExp", + "Error" + ); + + /** + * Fold "new Object()" to "Object()". + */ + private Node tryFoldStandardConstructors(Node n) { + Preconditions.checkState(n.isNew()); + + // If name normalization has been run then we know that + // new Object() does in fact refer to what we think it is + // and not some custom-defined Object(). + if (isASTNormalized()) { + if (n.getFirstChild().isName()) { + String className = n.getFirstChild().getString(); + if (STANDARD_OBJECT_CONSTRUCTORS.contains(className)) { + n.setType(Token.CALL); + n.putBooleanProp(Node.FREE_CALL, true); + reportCodeChange(); + } + } + } + + return n; + } + + /** + * Replaces a new Array or Object node with an object literal, unless the + * call to Array or Object is to a local function with the same name. + */ + private Node tryFoldLiteralConstructor(Node n) { + Preconditions.checkArgument(n.isCall() + || n.isNew()); + + Node constructorNameNode = n.getFirstChild(); + + Node newLiteralNode = null; + + // We require the AST to be normalized to ensure that, say, + // Object() really refers to the built-in Object constructor + // and not a user-defined constructor with the same name. + + if (isASTNormalized() && Token.NAME == constructorNameNode.getType()) { + + String className = constructorNameNode.getString(); + + if ("RegExp".equals(className)) { + // "RegExp("boo", "g")" --> /boo/g + return tryFoldRegularExpressionConstructor(n); + } else { + boolean constructorHasArgs = constructorNameNode.getNext() != null; + + if ("Object".equals(className) && !constructorHasArgs) { + // "Object()" --> "{}" + newLiteralNode = IR.objectlit(); + } else if ("Array".equals(className)) { + // "Array(arg0, arg1, ...)" --> "[arg0, arg1, ...]" + Node arg0 = constructorNameNode.getNext(); + FoldArrayAction action = isSafeToFoldArrayConstructor(arg0); + + if (action == FoldArrayAction.SAFE_TO_FOLD_WITH_ARGS || + action == FoldArrayAction.SAFE_TO_FOLD_WITHOUT_ARGS) { + newLiteralNode = IR.arraylit(); + n.removeChildren(); + if (action == FoldArrayAction.SAFE_TO_FOLD_WITH_ARGS) { + newLiteralNode.addChildrenToFront(arg0); + } + } + } + + if (newLiteralNode != null) { + n.getParent().replaceChild(n, newLiteralNode); + reportCodeChange(); + return newLiteralNode; + } + } + } + return n; + } + + private static enum FoldArrayAction { + NOT_SAFE_TO_FOLD, SAFE_TO_FOLD_WITH_ARGS, SAFE_TO_FOLD_WITHOUT_ARGS} + + /** + * Checks if it is safe to fold Array() constructor into []. It can be + * obviously done, if the initial constructor has either no arguments or + * at least two. The remaining case may be unsafe since Array(number) + * actually reserves memory for an empty array which contains number elements. + */ + private FoldArrayAction isSafeToFoldArrayConstructor(Node arg) { + FoldArrayAction action = FoldArrayAction.NOT_SAFE_TO_FOLD; + + if (arg == null) { + action = FoldArrayAction.SAFE_TO_FOLD_WITHOUT_ARGS; + } else if (arg.getNext() != null) { + action = FoldArrayAction.SAFE_TO_FOLD_WITH_ARGS; + } else { + switch (arg.getType()) { + case Token.STRING: + // "Array('a')" --> "['a']" + action = FoldArrayAction.SAFE_TO_FOLD_WITH_ARGS; + break; + case Token.NUMBER: + // "Array(0)" --> "[]" + if (arg.getDouble() == 0) { + action = FoldArrayAction.SAFE_TO_FOLD_WITHOUT_ARGS; + } + break; + case Token.ARRAYLIT: + // "Array([args])" --> "[[args]]" + action = FoldArrayAction.SAFE_TO_FOLD_WITH_ARGS; + break; + default: + } + } + return action; + } + + private Node tryFoldRegularExpressionConstructor(Node n) { + Node parent = n.getParent(); + Node constructor = n.getFirstChild(); + Node pattern = constructor.getNext(); // e.g. ^foobar$ + Node flags = null != pattern ? pattern.getNext() : null; // e.g. gi + + if (null == pattern || (null != flags && null != flags.getNext())) { + // too few or too many arguments + return n; + } + + if (// is pattern folded + pattern.isString() + // make sure empty pattern doesn't fold to // + && !"".equals(pattern.getString()) + + // NOTE(nicksantos): Make sure that the regexp isn't longer than + // 100 chars, or it blows up the regexp parser in Opera 9.2. + && pattern.getString().length() < 100 + + && (null == flags || flags.isString()) + // don't escape patterns with Unicode escapes since Safari behaves badly + // (read can't parse or crashes) on regex literals with Unicode escapes + && (isEcmaScript5OrGreater() + || !containsUnicodeEscape(pattern.getString()))) { + + // Make sure that / is escaped, so that it will fit safely in /brackets/ + // and make sure that no LineTerminatorCharacters appear literally inside + // the pattern. + // pattern is a string value with \\ and similar already escaped + pattern = makeForwardSlashBracketSafe(pattern); + + Node regexLiteral; + if (null == flags || "".equals(flags.getString())) { + // fold to /foobar/ + regexLiteral = IR.regexp(pattern); + } else { + // fold to /foobar/gi + if (!areValidRegexpFlags(flags.getString())) { + report(INVALID_REGULAR_EXPRESSION_FLAGS, flags); + return n; + } + if (!areSafeFlagsToFold(flags.getString())) { + return n; + } + n.removeChild(flags); + regexLiteral = IR.regexp(pattern, flags); + } + + parent.replaceChild(n, regexLiteral); + reportCodeChange(); + return regexLiteral; + } + + return n; + } + + private Node reduceTrueFalse(Node n) { + if (late) { + Node not = IR.not(IR.number(n.isTrue() ? 0 : 1)); + not.copyInformationFromForTree(n); + n.getParent().replaceChild(n, not); + reportCodeChange(); + return not; + } + return n; + } + + private Node tryMinimizeArrayLiteral(Node n) { + boolean allStrings = true; + for (Node cur = n.getFirstChild(); cur != null; cur = cur.getNext()) { + if (!cur.isString()) { + allStrings = false; + } + } + + if (allStrings) { + return tryMinimizeStringArrayLiteral(n); + } else { + return n; + } + } + + private Node tryMinimizeStringArrayLiteral(Node n) { + if(!late) { + return n; + } + + int numElements = n.getChildCount(); + // We save two bytes per element. + int saving = numElements * 2 - STRING_SPLIT_OVERHEAD; + if (saving <= 0) { + return n; + } + + String[] strings = new String[n.getChildCount()]; + int idx = 0; + for (Node cur = n.getFirstChild(); cur != null; cur = cur.getNext()) { + strings[idx++] = cur.getString(); + } + + // These delimiters are chars that appears a lot in the program therefore + // probably have a small Huffman encoding. + String delimiter = pickDelimiter(strings); + if (delimiter != null) { + String template = Joiner.on(delimiter).join(strings); + Node call = IR.call( + IR.getprop( + IR.string(template), + IR.string("split")), + IR.string("" + delimiter)); + call.copyInformationFromForTree(n); + n.getParent().replaceChild(n, call); + reportCodeChange(); + return call; + } + return n; + } + + /** + * Find a delimiter that does not occur in the given strings + * @param strings The strings that must be separated. + * @return a delimiter string or null + */ + private String pickDelimiter(String[] strings) { + boolean allLength1 = true; + for (String s : strings) { + if (s.length() != 1) { + allLength1 = false; + break; + } + } + + if (allLength1) { + return ""; + } + + String[] delimiters = new String[]{" ", ";", ",", "{", "}", null}; + int i = 0; + NEXT_DELIMITER: for (;delimiters[i] != null; i++) { + for (String cur : strings) { + if (cur.contains(delimiters[i])) { + continue NEXT_DELIMITER; + } + } + break; + } + return delimiters[i]; + } + + private static final Pattern REGEXP_FLAGS_RE = Pattern.compile("^[gmi]*$"); + + /** + * are the given flags valid regular expression flags? + * JavaScript recognizes several suffix flags for regular expressions, + * 'g' - global replace, 'i' - case insensitive, 'm' - multi-line. + * They are case insensitive, and JavaScript does not recognize the extended + * syntax mode, single-line mode, or expression replacement mode from Perl 5. + */ + private static boolean areValidRegexpFlags(String flags) { + return REGEXP_FLAGS_RE.matcher(flags).matches(); + } + + /** + * are the given flags safe to fold? + * We don't fold the regular expression if global ('g') flag is on, + * because in this case it isn't really a constant: its 'lastIndex' + * property contains the state of last execution, so replacing + * 'new RegExp('foobar','g')' with '/foobar/g' may change the behavior of + * the program if the RegExp is used inside a loop, for example. + *

      + * ECMAScript 5 explicitly disallows pooling of regular expression literals so + * in ECMAScript 5, {@code /foo/g} and {@code new RegExp('foo', 'g')} are + * equivalent. + * From section 7.8.5: + * "Then each time the literal is evaluated, a new object is created as if by + * the expression new RegExp(Pattern, Flags) where RegExp is the standard + * built-in constructor with that name." + */ + private boolean areSafeFlagsToFold(String flags) { + return isEcmaScript5OrGreater() || flags.indexOf('g') < 0; + } + + /** + * returns a string node that can safely be rendered inside /brackets/. + */ + private static Node makeForwardSlashBracketSafe(Node n) { + String s = n.getString(); + // sb contains everything in s[0:pos] + StringBuilder sb = null; + int pos = 0; + boolean isEscaped = false, inCharset = false; + for (int i = 0; i < s.length(); ++i) { + char ch = s.charAt(i); + switch (ch) { + case '\\': + isEscaped = !isEscaped; + continue; + case '/': + // Escape a literal forward slash if it is not already escaped and is + // not inside a character set. + // new RegExp('/') -> /\// + // but the following do not need extra escaping + // new RegExp('\\/') -> /\// + // new RegExp('[/]') -> /[/]/ + if (!isEscaped && !inCharset) { + if (null == sb) { sb = new StringBuilder(s.length() + 16); } + sb.append(s, pos, i).append('\\'); + pos = i; + } + break; + case '[': + if (!isEscaped) { + inCharset = true; + } + break; + case ']': + if (!isEscaped) { + inCharset = false; + } + break; + case '\r': case '\n': case '\u2028': case '\u2029': + // LineTerminators cannot appear raw inside a regular + // expression literal. + // They can't appear legally in a quoted string, but when + // the quoted string from + // new RegExp('\n') + // reaches here, the quoting has been removed. + // Requote just these code-points. + if (null == sb) { sb = new StringBuilder(s.length() + 16); } + if (isEscaped) { + sb.append(s, pos, i - 1); + } else { + sb.append(s, pos, i); + } + switch (ch) { + case '\r': sb.append("\\r"); break; + case '\n': sb.append("\\n"); break; + case '\u2028': sb.append("\\u2028"); break; + case '\u2029': sb.append("\\u2029"); break; + } + pos = i + 1; + break; + } + isEscaped = false; + } + + if (null == sb) { return n.cloneTree(); } + + sb.append(s, pos, s.length()); + return IR.string(sb.toString()).srcref(n); + } + + /** + * true if the JavaScript string would contain a Unicode escape when written + * out as the body of a regular expression literal. + */ + static boolean containsUnicodeEscape(String s) { + String esc = REGEXP_ESCAPER.regexpEscape(s); + for (int i = -1; (i = esc.indexOf("\\u", i + 1)) >= 0;) { + int nSlashes = 0; + while (i - nSlashes > 0 && '\\' == esc.charAt(i - nSlashes - 1)) { + ++nSlashes; + } + // if there are an even number of slashes before the \ u then it is a + // Unicode literal. + if (0 == (nSlashes & 1)) { return true; } + } + return false; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PerformanceTracker.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PerformanceTracker.java new file mode 100644 index 0000000..0c2bace --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PerformanceTracker.java @@ -0,0 +1,364 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.CodeChangeHandler.RecentChange; +import com.google.javascript.jscomp.CompilerOptions.TracerMode; +import com.google.javascript.rhino.Node; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Deque; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.zip.GZIPOutputStream; + +/** + */ +public class PerformanceTracker { + + private final Node jsRoot; + private final boolean trackSize; + private final boolean trackGzippedSize; + + // Keeps track of AST changes and computes code size estimation + // if there is any. + private final RecentChange codeChange = new RecentChange(); + + private int curCodeSizeEstimate = -1; + private int curZippedCodeSizeEstimate = -1; + + private Deque currentRunningPass = new ArrayDeque(); + + /** Summary stats by pass name. */ + private final Map summary = Maps.newHashMap(); + + // To share with the rest of the program + private ImmutableMap summaryCopy = null; + + /** Stats for each run of a compiler pass. */ + private final List log = Lists.newArrayList(); + + + public static class Stats { + public Stats(String pass) { + this.pass = pass; + } + public final String pass; + public long runtime = 0; + public int runs = 0; + public int changes = 0; + public int diff = 0; + public int gzDiff = 0; + public int size = 0; + public int gzSize = 0; + } + + PerformanceTracker(Node jsRoot, TracerMode mode) { + this.jsRoot = jsRoot; + switch (mode) { + case TIMING_ONLY: + this.trackSize = false; + this.trackGzippedSize = false; + break; + + case RAW_SIZE: + this.trackSize = true; + this.trackGzippedSize = false; + break; + + case ALL: + this.trackSize = true; + this.trackGzippedSize = true; + break; + + case OFF: + default: + throw new UnsupportedOperationException(); + } + } + + CodeChangeHandler getCodeChangeHandler() { + return codeChange; + } + + void recordPassStart(String passName) { + currentRunningPass.push(passName); + codeChange.reset(); + } + + /** + * Record that a pass has stopped. + * + * @param passName Short name of the pass. + * @param result Execution time. + */ + void recordPassStop(String passName, long result) { + String currentPassName = currentRunningPass.pop(); + if (!passName.equals(currentPassName)) { + throw new RuntimeException(passName + " is not running."); + } + + CodeSizeEstimatePrinter printer = null; + if (codeChange.hasCodeChanged() && (trackSize || trackGzippedSize)) { + printer = estimateCodeSize(jsRoot); + } + + Stats logStats = new Stats(currentPassName); + log.add(logStats); + updateStats(logStats, result, printer); + + Stats summaryStats = summary.get(passName); + if (summaryStats == null) { + summaryStats = new Stats(passName); + summary.put(passName, summaryStats); + } + updateStats(summaryStats, result, printer); + + if (printer != null) { + if (trackSize) { + curCodeSizeEstimate = printer.calcSize(); + } + if (trackGzippedSize) { + curZippedCodeSizeEstimate = printer.calcZippedSize(); + } + } + } + + private void updateStats(Stats stats, + long result, CodeSizeEstimatePrinter printer) { + stats.runtime += result; + stats.runs += 1; + if (codeChange.hasCodeChanged()) { + stats.changes += 1; + } + + if (printer != null) { + recordSizeChange( + curCodeSizeEstimate, printer.calcSize(), stats); + recordGzSizeChange( + curZippedCodeSizeEstimate, printer.calcZippedSize(), stats); + } + } + + /** + * Record the size change in the given record for that given pass. + */ + private static void recordSizeChange(int oldSize, int newSize, Stats record) { + if (oldSize != -1) { + int delta = oldSize - newSize; + if (delta > 0) { + record.diff += delta; + } + } + if (newSize != -1) { + record.size = newSize; + } + } + + /** + * Record the gzip size change in the given record for that given pass. + */ + private static void recordGzSizeChange( + int oldSize, int newSize, Stats record) { + if (oldSize != -1) { + int delta = oldSize - newSize; + if (delta > 0) { + record.gzDiff += delta; + } + } + if (newSize != -1) { + record.gzSize = newSize; + } + } + + private final CodeSizeEstimatePrinter estimateCodeSize(Node root) { + CodeSizeEstimatePrinter cp = new CodeSizeEstimatePrinter(trackGzippedSize); + CodeGenerator cg = CodeGenerator.forCostEstimation(cp); + cg.add(root); + return cp; + } + + public ImmutableMap getStats() { + if (summaryCopy == null) { + summaryCopy = ImmutableMap.copyOf(summary); + } + return summaryCopy; + } + + class CmpEntries implements Comparator> { + @Override + public int compare(Entry e1, Entry e2) { + return (int) (e1.getValue().runtime - e2.getValue().runtime); + } + } + + public void outputTracerReport(PrintStream pstr) { + JvmMetrics.maybeWriteJvmMetrics(pstr, "verbose:pretty:all"); + OutputStreamWriter output = new OutputStreamWriter(pstr); + try { + int runtime = 0; + int runs = 0; + int changes = 0; + int diff = 0; + int gzDiff = 0; + + // header + output.write("Summary:\n"); + output.write("pass,runtime,runs,changingRuns,reduction,gzReduction\n"); + + ArrayList> a = new ArrayList>(); + for (Entry entry : summary.entrySet()) { + a.add(entry); + } + Collections.sort(a, new CmpEntries()); + + for (Entry entry : a) { + String key = entry.getKey(); + Stats stats = entry.getValue(); + + output.write(key); + output.write(","); + output.write(String.valueOf(stats.runtime)); + runtime += stats.runtime; + output.write(","); + output.write(String.valueOf(stats.runs)); + runs += stats.runs; + output.write(","); + output.write(String.valueOf(stats.changes)); + changes += stats.changes; + output.write(","); + output.write(String.valueOf(stats.diff)); + diff += stats.diff; + output.write(","); + output.write(String.valueOf(stats.gzDiff)); + gzDiff += stats.gzDiff; + output.write("\n"); + } + output.write("TOTAL"); + output.write(","); + output.write(String.valueOf(runtime)); + output.write(","); + output.write(String.valueOf(runs)); + output.write(","); + output.write(String.valueOf(changes)); + output.write(","); + output.write(String.valueOf(diff)); + output.write(","); + output.write(String.valueOf(gzDiff)); + output.write("\n"); + output.write("\n"); + + output.write("Log:\n"); + output.write( + "pass,runtime,runs,changingRuns,reduction,gzReduction,size,gzSize\n"); + for (Stats stats : log) { + output.write(stats.pass); + output.write(","); + output.write(String.valueOf(stats.runtime)); + output.write(","); + output.write(String.valueOf(stats.runs)); + output.write(","); + output.write(String.valueOf(stats.changes)); + output.write(","); + output.write(String.valueOf(stats.diff)); + output.write(","); + output.write(String.valueOf(stats.gzDiff)); + output.write(","); + output.write(String.valueOf(stats.size)); + output.write(","); + output.write(String.valueOf(stats.gzSize)); + output.write("\n"); + } + output.write("\n"); + output.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Purely use to get a code size estimate and not generate any code at all. + */ + private static final class CodeSizeEstimatePrinter extends CodeConsumer { + private final boolean trackGzippedSize; + private int size = 0; + private char lastChar = '\0'; + private final ByteArrayOutputStream output = new ByteArrayOutputStream(); + private final GZIPOutputStream stream; + + private CodeSizeEstimatePrinter(boolean trackGzippedSize) { + this.trackGzippedSize = trackGzippedSize; + + try { + stream = new GZIPOutputStream(output); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + void append(String str) { + int len = str.length(); + if (len > 0) { + size += len; + lastChar = str.charAt(len - 1); + if (trackGzippedSize) { + try { + stream.write(str.getBytes()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + } + + @Override + char getLastChar() { + return lastChar; + } + + private int calcSize() { + return size; + } + + private int calcZippedSize() { + if (trackGzippedSize) { + try { + stream.finish(); + stream.flush(); + stream.close(); + return output.size(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + return -1; + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PhaseOptimizer.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PhaseOptimizer.java new file mode 100644 index 0000000..4278d96 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PhaseOptimizer.java @@ -0,0 +1,418 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.rhino.Node; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Logger; + +/** + * Optimizes the order of compiler passes. + * @author nicksantos@google.com (Nick Santos) + */ +class PhaseOptimizer implements CompilerPass { + + // This ordering is computed offline by running with compute_phase_ordering. + @VisibleForTesting + static final List OPTIMAL_ORDER = ImmutableList.of( + "deadAssignmentsElimination", + "inlineFunctions", + "removeUnusedPrototypeProperties", + "removeUnreachableCode", + "removeUnusedVars", + "minimizeExitPoints", + "inlineVariables", + "collapseObjectLiterals", + "peepholeOptimizations"); + + static final int MAX_LOOPS = 100; + static final String OPTIMIZE_LOOP_ERROR = + "Fixed point loop exceeded the maximum number of iterations."; + + // Only used by Loop/process, but enum types can't be local + enum State { + RUN_PASSES_NOT_RUN_IN_PREV_ITER, + RUN_PASSES_THAT_CHANGED_STH_IN_PREV_ITER + } + + private static final Logger logger = + Logger.getLogger(PhaseOptimizer.class.getName()); + + private final List passes = Lists.newArrayList(); + + private final AbstractCompiler compiler; + private final PerformanceTracker tracker; + private final CodeChangeHandler.RecentChange recentChange = + new CodeChangeHandler.RecentChange(); + private boolean loopMutex = false; + private Tracer currentTracer = null; + private String currentPassName = null; + private PassFactory sanityCheck = null; + + private double progress = 0.0; + private double progressStep = 0.0; + + // The following static properties are only used for computing optimal + // phase orderings. They should not be touched by normal compiler runs. + private static boolean randomizeLoops = false; + private static List> loopsRun = Lists.newArrayList(); + + private final ProgressRange progressRange; + + /** + * @param compiler the compiler that owns/creates this. + * @param tracker an optional performance tracker + * @param progressRange the progress range for the process function or null + * if progress should not be reported. + */ + PhaseOptimizer(AbstractCompiler compiler, PerformanceTracker tracker, + ProgressRange progressRange) { + this.compiler = compiler; + this.tracker = tracker; + this.progressRange = progressRange; + compiler.addChangeHandler(recentChange); + } + + /** + * Randomizes loops. This should only be used when computing optimal phase + * orderings. + */ + static void randomizeLoops() { + randomizeLoops = true; + } + + /** + * Get the phase ordering of loops during this run. + * Returns an empty list when the loops are not randomized. + */ + static List> getLoopsRun() { + return loopsRun; + } + + /** + * Clears the phase ordering of loops during this run. + */ + static void clearLoopsRun() { + loopsRun.clear(); + } + + /** + * Add the passes generated by the given factories to the compile sequence. + * + * Automatically pulls multi-run passes into fixed point loops. If there + * are 1 or more multi-run passes in a row, they will run together in + * the same fixed point loop. The passes will run until they are finished + * making changes. + * + * The PhaseOptimizer is free to tweak the order and frequency of multi-run + * passes in a fixed-point loop. + */ + void consume(List factories) { + Loop currentLoop = new Loop(); + boolean isCurrentLoopPopulated = false; + for (PassFactory factory : factories) { + if (factory.isOneTimePass()) { + if (isCurrentLoopPopulated) { + passes.add(currentLoop); + currentLoop = new Loop(); + isCurrentLoopPopulated = false; + } + addOneTimePass(factory); + } else { + currentLoop.addLoopedPass(factory); + isCurrentLoopPopulated = true; + } + } + + if (isCurrentLoopPopulated) { + passes.add(currentLoop); + } + } + + /** + * Add the pass generated by the given factory to the compile sequence. + * This pass will be run once. + */ + void addOneTimePass(PassFactory factory) { + passes.add(new NamedPass(factory)); + } + + /** + * Add a loop to the compile sequence. This loop will continue running + * until the AST stops changing. + * @return The loop structure. Pass suppliers should be added to the loop. + */ + Loop addFixedPointLoop() { + Loop loop = new Loop(); + passes.add(loop); + return loop; + } + + /** + * Adds a sanity checker to be run after every pass. Intended for development. + */ + void setSanityCheck(PassFactory sanityCheck) { + this.sanityCheck = sanityCheck; + } + + /** + * Run all the passes in the optimizer. + */ + @Override + public void process(Node externs, Node root) { + progress = 0.0; + progressStep = 0.0; + if (progressRange != null) { + progressStep = (progressRange.maxValue - progressRange.initialValue) + / passes.size(); + progress = progressRange.initialValue; + } + + for (CompilerPass pass : passes) { + pass.process(externs, root); + if (hasHaltingErrors()) { + return; + } + } + } + + /** + * Marks the beginning of a pass. + */ + private void startPass(String passName) { + Preconditions.checkState(currentTracer == null && currentPassName == null); + currentPassName = passName; + currentTracer = newTracer(passName); + } + + /** + * Marks the end of a pass. + */ + private void endPass(Node externs, Node root) { + Preconditions.checkState(currentTracer != null && currentPassName != null); + + String passToCheck = currentPassName; + try { + if (progressRange == null) { + compiler.setProgress(-1, currentPassName); + } else { + progress += progressStep; + compiler.setProgress(progress, currentPassName); + } + stopTracer(currentTracer, currentPassName); + currentPassName = null; + currentTracer = null; + + maybeSanityCheck(externs, root); + } catch (Exception e) { + // TODO(johnlenz): Remove this once the normalization checks report + // errors instead of exceptions. + throw new RuntimeException("Sanity check failed for " + passToCheck, e); + } + } + + /** + * Runs the sanity check if it is available. + */ + void maybeSanityCheck(Node externs, Node root) { + if (sanityCheck != null) { + sanityCheck.create(compiler).process(externs, root); + } + } + + private boolean hasHaltingErrors() { + return compiler.hasHaltingErrors(); + } + + /** + * Returns a new tracer for the given pass name. + */ + private Tracer newTracer(String passName) { + String comment = passName + + (recentChange.hasCodeChanged() ? " on recently changed AST" : ""); + if (tracker != null) { + tracker.recordPassStart(passName); + } + return new Tracer("JSCompiler", comment); + } + + private void stopTracer(Tracer t, String passName) { + long result = t.stop(); + if (tracker != null) { + tracker.recordPassStop(passName, result); + } + } + + /** + * A single compiler pass. + */ + class NamedPass implements CompilerPass { + final String name; + private final PassFactory factory; + + NamedPass(PassFactory factory) { + this.name = factory.getName(); + this.factory = factory; + } + + @Override + public void process(Node externs, Node root) { + logger.fine(name); + startPass(name); + // Delay the creation of the actual pass until *after* all previous passes + // have been processed. + // Some precondition checks rely on this, eg, in CoalesceVariableNames. + factory.create(compiler).process(externs, root); + endPass(externs, root); + } + } + + /** + * Runs a set of compiler passes until they reach a fixed point. + * + * Notice that this is a non-static class, because it includes the closure + * of PhaseOptimizer. + */ + class Loop implements CompilerPass { + private final List myPasses = Lists.newArrayList(); + private final Set myNames = Sets.newHashSet(); + + void addLoopedPass(PassFactory factory) { + String name = factory.getName(); + Preconditions.checkArgument(!myNames.contains(name), + "Already a pass with name '%s' in this loop", name); + myNames.add(name); + myPasses.add(new NamedPass(factory)); + } + + /** + * Gets the pass names, in order. + */ + private List getPassOrder() { + List order = Lists.newArrayList(); + for (NamedPass pass : myPasses) { + order.add(pass.name); + } + return order; + } + + @Override + public void process(Node externs, Node root) { + Preconditions.checkState(!loopMutex, "Nested loops are forbidden"); + loopMutex = true; + if (randomizeLoops) { + randomizePasses(); + } else { + optimizePasses(); + } + + // Contains a pass iff it made changes the last time it was run. + Set madeChanges = new HashSet(); + // Contains a pass iff it was run during the last inner loop. + Set runInPrevIter = new HashSet(); + State s = State.RUN_PASSES_NOT_RUN_IN_PREV_ITER; + boolean lastIterMadeChanges; + int count = 0; + + try { + while (true) { + if (count++ > MAX_LOOPS) { + compiler.throwInternalError(OPTIMIZE_LOOP_ERROR, null); + } + lastIterMadeChanges = false; + for (NamedPass pass : myPasses) { + recentChange.reset(); + if ((s == State.RUN_PASSES_NOT_RUN_IN_PREV_ITER && + !runInPrevIter.contains(pass)) || + (s == State.RUN_PASSES_THAT_CHANGED_STH_IN_PREV_ITER && + madeChanges.contains(pass))) { + pass.process(externs, root); + runInPrevIter.add(pass); + if (hasHaltingErrors()) { + return; + } else if (recentChange.hasCodeChanged()) { + madeChanges.add(pass); + lastIterMadeChanges = true; + } else { + madeChanges.remove(pass); + } + } else { + runInPrevIter.remove(pass); + } + } + if (s == State.RUN_PASSES_NOT_RUN_IN_PREV_ITER) { + if (lastIterMadeChanges) { + s = State.RUN_PASSES_THAT_CHANGED_STH_IN_PREV_ITER; + } else { + return; + } + } else if (!lastIterMadeChanges) { + s = State.RUN_PASSES_NOT_RUN_IN_PREV_ITER; + } + } + } finally { + loopMutex = false; + } + } + + /** Re-arrange the passes in a random order. */ + private void randomizePasses() { + Collections.shuffle(myPasses); + } + + /** Re-arrange the passes in an optimal order. */ + private void optimizePasses() { + // It's important that this ordering is deterministic, so that + // multiple compiles with the same input produce exactly the same + // results. + // + // To do this, grab any passes we recognize, and move them to the end + // in an "optimal" order. + List optimalPasses = Lists.newArrayList(); + for (String passName : OPTIMAL_ORDER) { + for (NamedPass pass : myPasses) { + if (pass.name.equals(passName)) { + optimalPasses.add(pass); + break; + } + } + } + + myPasses.removeAll(optimalPasses); + myPasses.addAll(optimalPasses); + } + } + + static class ProgressRange { + public final double initialValue; + public final double maxValue; + + public ProgressRange(double initialValue, double maxValue) { + this.initialValue = initialValue; + this.maxValue = maxValue; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PrepareAst.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PrepareAst.java new file mode 100644 index 0000000..eda524e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PrepareAst.java @@ -0,0 +1,214 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * Prepare the AST before we do any checks or optimizations on it. + * + * This pass must run. It should bring the AST into a consistent state, + * and add annotations where necessary. It should not make any transformations + * on the tree that would lose source information, since we need that source + * information for checks. + * + * @author johnlenz@google.com (John Lenz) + */ +class PrepareAst implements CompilerPass { + + private final AbstractCompiler compiler; + private final boolean checkOnly; + + PrepareAst(AbstractCompiler compiler) { + this(compiler, false); + } + + PrepareAst(AbstractCompiler compiler, boolean checkOnly) { + this.compiler = compiler; + this.checkOnly = checkOnly; + } + + private void reportChange() { + if (checkOnly) { + Preconditions.checkState(false, "normalizeNodeType constraints violated"); + } + } + + @Override + public void process(Node externs, Node root) { + if (checkOnly) { + normalizeNodeTypes(root); + } else { + // Don't perform "PrepareAnnotations" when doing checks as + // they currently aren't valid during sanity checks. In particular, + // they DIRECT_EVAL shouldn't be applied after inlining has been + // performed. + if (externs != null) { + NodeTraversal.traverse( + compiler, externs, new PrepareAnnotations()); + } + if (root != null) { + NodeTraversal.traverse( + compiler, root, new PrepareAnnotations()); + } + } + } + + /** + * Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code. + */ + private void normalizeNodeTypes(Node n) { + normalizeBlocks(n); + + for (Node child = n.getFirstChild(); + child != null; child = child.getNext()) { + // This pass is run during the CompilerTestCase validation, so this + // parent pointer check serves as a more general check. + Preconditions.checkState(child.getParent() == n); + + normalizeNodeTypes(child); + } + } + + /** + * Add blocks to IF, WHILE, DO, etc. + */ + private void normalizeBlocks(Node n) { + if (NodeUtil.isControlStructure(n) + && !n.isLabel() + && !n.isSwitch()) { + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + if (NodeUtil.isControlStructureCodeBlock(n,c) && + !c.isBlock()) { + Node newBlock = IR.block().srcref(n); + n.replaceChild(c, newBlock); + if (!c.isEmpty()) { + newBlock.addChildrenToFront(c); + } else { + newBlock.setWasEmptyNode(true); + } + c = newBlock; + reportChange(); + } + } + } + } + + /** + * Normalize where annotations appear on the AST. Copies + * around existing JSDoc annotations as well as internal annotations. + */ + static class PrepareAnnotations + implements NodeTraversal.Callback { + + PrepareAnnotations() { + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + if (n.isObjectLit()) { + normalizeObjectLiteralAnnotations(n); + } + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.CALL: + annotateCalls(n); + break; + + case Token.FUNCTION: + annotateDispatchers(n, parent); + break; + } + } + + private void normalizeObjectLiteralAnnotations(Node objlit) { + Preconditions.checkState(objlit.isObjectLit()); + for (Node key = objlit.getFirstChild(); + key != null; key = key.getNext()) { + Node value = key.getFirstChild(); + normalizeObjectLiteralKeyAnnotations(objlit, key, value); + } + } + + /** + * There are two types of calls we are interested in calls without explicit + * "this" values (what we are call "free" calls) and direct call to eval. + */ + private void annotateCalls(Node n) { + Preconditions.checkState(n.isCall()); + + // Keep track of of the "this" context of a call. A call without an + // explicit "this" is a free call. + Node first = n.getFirstChild(); + if (!NodeUtil.isGet(first)) { + n.putBooleanProp(Node.FREE_CALL, true); + } + + // Keep track of the context in which eval is called. It is important + // to distinguish between "(0, eval)()" and "eval()". + if (first.isName() && + "eval".equals(first.getString())) { + first.putBooleanProp(Node.DIRECT_EVAL, true); + } + } + + /** + * Translate dispatcher info into the property expected node. + */ + private void annotateDispatchers(Node n, Node parent) { + Preconditions.checkState(n.isFunction()); + if (parent.getJSDocInfo() != null + && parent.getJSDocInfo().isJavaDispatch()) { + if (parent.isAssign()) { + Preconditions.checkState(parent.getLastChild() == n); + n.putBooleanProp(Node.IS_DISPATCHER, true); + } + } + } + + /** + * In the AST that Rhino gives us, it needs to make a distinction + * between JsDoc on the object literal node and JsDoc on the object literal + * value. For example, + *

      +     * var x = {
      +     *   / JSDOC /
      +     *   a: 'b',
      +     *   c: / JSDOC / 'd'
      +     * };
      +     * 
      + * + * But in few narrow cases (in particular, function literals), it's + * a lot easier for us if the doc is attached to the value. + */ + private void normalizeObjectLiteralKeyAnnotations( + Node objlit, Node key, Node value) { + Preconditions.checkState(objlit.isObjectLit()); + if (key.getJSDocInfo() != null && + value.isFunction()) { + value.setJSDocInfo(key.getJSDocInfo()); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PreprocessorSymbolTable.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PreprocessorSymbolTable.java new file mode 100644 index 0000000..ce1f2a8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PreprocessorSymbolTable.java @@ -0,0 +1,108 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.SimpleReference; +import com.google.javascript.rhino.jstype.SimpleSlot; +import com.google.javascript.rhino.jstype.StaticScope; +import com.google.javascript.rhino.jstype.StaticSymbolTable; + +import java.util.Collections; +import java.util.Map; + +/** + * A symbol table for references that are removed by preprocessor passes + * (like {@code ProcessClosurePrimitives}). + * + * @author nicksantos@google.com (Nick Santos) + */ +final class PreprocessorSymbolTable + implements StaticScope, + StaticSymbolTable { + + /** + * All preprocessor symbols are globals. + */ + private final Map symbols = Maps.newHashMap(); + + private final Multimap refs = + ArrayListMultimap.create(); + + private final Node root; + + PreprocessorSymbolTable(Node root) { + this.root = root; + } + + @Override + public Node getRootNode() { return root; } + + @Override + public JSType getTypeOfThis() { return null; } + + @Override + public StaticScope getParentScope() { return null; } + + @Override + public SimpleSlot getSlot(String name) { + return symbols.get(name); + } + + @Override + public SimpleSlot getOwnSlot(String name) { + return getSlot(name); + } + + @Override + public Iterable getReferences(SimpleSlot symbol) { + return Collections.unmodifiableCollection(refs.get(symbol.getName())); + } + + @Override + public Iterable getAllSymbols() { + return Collections.unmodifiableCollection(symbols.values()); + } + + @Override + public StaticScope getScope(SimpleSlot slot) { + return this; + } + + void addReference(Node node) { + String name = node.getQualifiedName(); + Preconditions.checkNotNull(name); + + if (!symbols.containsKey(name)) { + symbols.put(name, new SimpleSlot(name, null, true)); + } + + refs.put(name, new Reference(symbols.get(name), node)); + } + + static final class Reference extends SimpleReference { + Reference(SimpleSlot symbol, Node node) { + super(symbol, node); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PrintStreamErrorManager.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PrintStreamErrorManager.java new file mode 100644 index 0000000..cb0d317 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PrintStreamErrorManager.java @@ -0,0 +1,79 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.CheckLevel; + +import java.io.PrintStream; + +/** + *

      An error manager that prints errors and warnings to the print stream + * provided in addition to the functionality of the + * {@link BasicErrorManager}.

      + * + *

      It collaborates with a {@link SourceExcerptProvider} via a + * {@link MessageFormatter} to display error messages with source context.

      + * + */ +public class PrintStreamErrorManager extends BasicErrorManager { + private final MessageFormatter formatter; + private final PrintStream stream; + private int summaryDetailLevel = 1; + + /** + * Creates an error manager. + * @param formatter the message formatter used to format the messages + * @param stream the stream on which the errors and warnings should be + * printed. This class does not close the stream + */ + public PrintStreamErrorManager(MessageFormatter formatter, + PrintStream stream) { + this.formatter = formatter; + this.stream = stream; + } + + /** + * Creates an instance with a source-less error formatter. + */ + public PrintStreamErrorManager(PrintStream stream) { + this(ErrorFormat.SOURCELESS.toFormatter(null, false), stream); + } + + @Override + public void println(CheckLevel level, JSError error) { + stream.println(error.format(level, formatter)); + } + + public void setSummaryDetailLevel(int summaryDetailLevel) { + this.summaryDetailLevel = summaryDetailLevel; + } + + @Override + public void printSummary() { + if (summaryDetailLevel >= 3 || + (summaryDetailLevel >= 1 && getErrorCount() + getWarningCount() > 0) || + (summaryDetailLevel >= 2 && getTypedPercent() > 0.0)) { + if (getTypedPercent() > 0.0) { + stream.format("%d error(s), %d warning(s), %.1f%% typed%n", + getErrorCount(), getWarningCount(), getTypedPercent()); + } else { + stream.format("%d error(s), %d warning(s)%n", getErrorCount(), + getWarningCount()); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ProcessClosurePrimitives.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ProcessClosurePrimitives.java new file mode 100644 index 0000000..2e75bce --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ProcessClosurePrimitives.java @@ -0,0 +1,1124 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.JSDocInfoBuilder; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +/** + * Replaces goog.provide calls, removes goog.require calls, verifies that + * goog.require has a corresponding goog.provide and some closure specific + * simplifications. + * + */ +class ProcessClosurePrimitives extends AbstractPostOrderCallback + implements HotSwapCompilerPass { + + static final DiagnosticType NULL_ARGUMENT_ERROR = DiagnosticType.error( + "JSC_NULL_ARGUMENT_ERROR", + "method \"{0}\" called without an argument"); + + static final DiagnosticType EXPECTED_OBJECTLIT_ERROR = DiagnosticType.error( + "JSC_EXPECTED_OBJECTLIT_ERROR", + "method \"{0}\" expected an object literal argument"); + + static final DiagnosticType EXPECTED_STRING_ERROR = DiagnosticType.error( + "JSC_EXPECTED_STRING_ERROR", + "method \"{0}\" expected an object string argument"); + + static final DiagnosticType INVALID_ARGUMENT_ERROR = DiagnosticType.error( + "JSC_INVALID_ARGUMENT_ERROR", + "method \"{0}\" called with invalid argument"); + + static final DiagnosticType INVALID_STYLE_ERROR = DiagnosticType.error( + "JSC_INVALID_CSS_NAME_MAP_STYLE_ERROR", + "Invalid CSS name map style {0}"); + + static final DiagnosticType TOO_MANY_ARGUMENTS_ERROR = DiagnosticType.error( + "JSC_TOO_MANY_ARGUMENTS_ERROR", + "method \"{0}\" called with more than one argument"); + + static final DiagnosticType DUPLICATE_NAMESPACE_ERROR = DiagnosticType.error( + "JSC_DUPLICATE_NAMESPACE_ERROR", + "namespace \"{0}\" cannot be provided twice"); + + static final DiagnosticType FUNCTION_NAMESPACE_ERROR = DiagnosticType.error( + "JSC_FUNCTION_NAMESPACE_ERROR", + "\"{0}\" cannot be both provided and declared as a function"); + + static final DiagnosticType MISSING_PROVIDE_ERROR = DiagnosticType.error( + "JSC_MISSING_PROVIDE_ERROR", + "required \"{0}\" namespace never provided"); + + static final DiagnosticType LATE_PROVIDE_ERROR = DiagnosticType.error( + "JSC_LATE_PROVIDE_ERROR", + "required \"{0}\" namespace not provided yet"); + + static final DiagnosticType INVALID_PROVIDE_ERROR = DiagnosticType.error( + "JSC_INVALID_PROVIDE_ERROR", + "\"{0}\" is not a valid JS property name"); + + static final DiagnosticType XMODULE_REQUIRE_ERROR = DiagnosticType.warning( + "JSC_XMODULE_REQUIRE_ERROR", + "namespace \"{0}\" provided in module {1} " + + "but required in module {2}"); + + static final DiagnosticType NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR = + DiagnosticType.error( + "JSC_NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR", + "goog.setCssNameMapping only takes an object literal with string values"); + + static final DiagnosticType INVALID_CSS_RENAMING_MAP = DiagnosticType.warning( + "INVALID_CSS_RENAMING_MAP", + "Invalid entries in css renaming map: {0}"); + + static final DiagnosticType BASE_CLASS_ERROR = DiagnosticType.error( + "JSC_BASE_CLASS_ERROR", + "incorrect use of goog.base: {0}"); + + /** The root Closure namespace */ + static final String GOOG = "goog"; + + private final AbstractCompiler compiler; + private final JSModuleGraph moduleGraph; + + // The goog.provides must be processed in a deterministic order. + private final Map providedNames = + Maps.newTreeMap(); + + private final List unrecognizedRequires = + Lists.newArrayList(); + private final Set exportedVariables = Sets.newHashSet(); + private final CheckLevel requiresLevel; + private final PreprocessorSymbolTable preprocessorSymbolTable; + + ProcessClosurePrimitives(AbstractCompiler compiler, + @Nullable PreprocessorSymbolTable preprocessorSymbolTable, + CheckLevel requiresLevel) { + this.compiler = compiler; + this.preprocessorSymbolTable = preprocessorSymbolTable; + this.moduleGraph = compiler.getModuleGraph(); + this.requiresLevel = requiresLevel; + + // goog is special-cased because it is provided in Closure's base library. + providedNames.put(GOOG, + new ProvidedName(GOOG, null, null, false /* implicit */)); + } + + Set getExportedVariableNames() { + return exportedVariables; + } + + @Override + public void process(Node externs, Node root) { + new NodeTraversal(compiler, this).traverse(root); + + for (ProvidedName pn : providedNames.values()) { + pn.replace(); + } + + if (requiresLevel.isOn()) { + for (UnrecognizedRequire r : unrecognizedRequires) { + DiagnosticType error; + ProvidedName expectedName = providedNames.get(r.namespace); + if (expectedName != null && expectedName.firstNode != null) { + // The namespace ended up getting provided after it was required. + error = LATE_PROVIDE_ERROR; + } else { + error = MISSING_PROVIDE_ERROR; + } + + compiler.report(JSError.make( + r.inputName, r.requireNode, requiresLevel, error, r.namespace)); + } + } + } + + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + // TODO(bashir): Implement a real hot-swap version instead and make it fully + // consistent with the full version. + this.compiler.process(this); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.CALL: + boolean isExpr = parent.isExprResult(); + Node left = n.getFirstChild(); + if (left.isGetProp()) { + Node name = left.getFirstChild(); + if (name.isName() && + GOOG.equals(name.getString())) { + // For the sake of simplicity, we report code changes + // when we see a provides/requires, and don't worry about + // reporting the change when we actually do the replacement. + String methodName = name.getNext().getString(); + if ("base".equals(methodName)) { + processBaseClassCall(t, n); + } else if (!isExpr) { + // All other methods must be called in an EXPR. + break; + } else if ("require".equals(methodName)) { + processRequireCall(t, n, parent); + } else if ("provide".equals(methodName)) { + processProvideCall(t, n, parent); + } else if ("exportSymbol".equals(methodName)) { + Node arg = left.getNext(); + if (arg.isString()) { + int dot = arg.getString().indexOf('.'); + if (dot == -1) { + exportedVariables.add(arg.getString()); + } else { + exportedVariables.add(arg.getString().substring(0, dot)); + } + } + } else if ("addDependency".equals(methodName)) { + CodingConvention convention = compiler.getCodingConvention(); + List typeDecls = + convention.identifyTypeDeclarationCall(n); + if (typeDecls != null) { + for (String typeDecl : typeDecls) { + compiler.getTypeRegistry().forwardDeclareType(typeDecl); + } + } + + // We can't modify parent, so just create a node that will + // get compiled out. + parent.replaceChild(n, IR.number(0)); + compiler.reportCodeChange(); + } else if ("setCssNameMapping".equals(methodName)) { + processSetCssNameMapping(t, n, parent); + } + } + } + break; + + case Token.ASSIGN: + case Token.NAME: + // If this is an assignment to a provided name, remove the provided + // object. + handleCandidateProvideDefinition(t, n, parent); + break; + + case Token.EXPR_RESULT: + handleTypedefDefinition(t, n, parent); + break; + + case Token.FUNCTION: + // If this is a declaration of a provided named function, this is an + // error. Hoisted functions will explode if they're provided. + if (t.inGlobalScope() && + !NodeUtil.isFunctionExpression(n)) { + String name = n.getFirstChild().getString(); + ProvidedName pn = providedNames.get(name); + if (pn != null) { + compiler.report(t.makeError(n, FUNCTION_NAMESPACE_ERROR, name)); + } + } + break; + + case Token.GETPROP: + if (n.getFirstChild().isName() && + !parent.isCall() && + !parent.isAssign() && + "goog.base".equals(n.getQualifiedName())) { + reportBadBaseClassUse(t, n, "May only be called directly."); + } + break; + } + } + + /** + * Handles a goog.require call. + */ + private void processRequireCall(NodeTraversal t, Node n, Node parent) { + Node left = n.getFirstChild(); + Node arg = left.getNext(); + if (verifyArgument(t, left, arg)) { + String ns = arg.getString(); + ProvidedName provided = providedNames.get(ns); + if (provided == null || !provided.isExplicitlyProvided()) { + unrecognizedRequires.add( + new UnrecognizedRequire(n, ns, t.getSourceName())); + } else { + JSModule providedModule = provided.explicitModule; + + // This must be non-null, because there was an explicit provide. + Preconditions.checkNotNull(providedModule); + + JSModule module = t.getModule(); + if (moduleGraph != null && + module != providedModule && + !moduleGraph.dependsOn(module, providedModule)) { + compiler.report( + t.makeError(n, XMODULE_REQUIRE_ERROR, ns, + providedModule.getName(), + module.getName())); + } + } + + maybeAddToSymbolTable(left); + maybeAddStringNodeToSymbolTable(arg); + + // Requires should be removed before runtime. The one + // exception is if the type has not been provided yet and + // errors for broken requires are turned off, in which case, + // we will be doing a later pass that may error, so we can + // leave this here this time and let it error next time if it + // is still not provided. + if (provided != null || requiresLevel.isOn()) { + parent.detachFromParent(); + compiler.reportCodeChange(); + } + } + } + + /** + * Handles a goog.provide call. + */ + private void processProvideCall(NodeTraversal t, Node n, Node parent) { + Node left = n.getFirstChild(); + Node arg = left.getNext(); + if (verifyProvide(t, left, arg)) { + String ns = arg.getString(); + + maybeAddToSymbolTable(left); + maybeAddStringNodeToSymbolTable(arg); + + if (providedNames.containsKey(ns)) { + ProvidedName previouslyProvided = providedNames.get(ns); + if (!previouslyProvided.isExplicitlyProvided()) { + previouslyProvided.addProvide(parent, t.getModule(), true); + } else { + compiler.report( + t.makeError(n, DUPLICATE_NAMESPACE_ERROR, ns)); + } + } else { + registerAnyProvidedPrefixes(ns, parent, t.getModule()); + providedNames.put( + ns, new ProvidedName(ns, parent, t.getModule(), true)); + } + } + } + + /** + * Handles a typedef definition for a goog.provided name. + * @param n EXPR_RESULT node. + */ + private void handleTypedefDefinition( + NodeTraversal t, Node n, Node parent) { + JSDocInfo info = n.getFirstChild().getJSDocInfo(); + if (t.inGlobalScope() && info != null && info.hasTypedefType()) { + String name = n.getFirstChild().getQualifiedName(); + if (name != null) { + ProvidedName pn = providedNames.get(name); + if (pn != null) { + pn.addDefinition(n, t.getModule()); + } + } + } + } + + /** + * Handles a candidate definition for a goog.provided name. + */ + private void handleCandidateProvideDefinition( + NodeTraversal t, Node n, Node parent) { + if (t.inGlobalScope()) { + String name = null; + if (n.isName() && parent.isVar()) { + name = n.getString(); + } else if (n.isAssign() && + parent.isExprResult()) { + name = n.getFirstChild().getQualifiedName(); + } + + if (name != null) { + if (parent.getBooleanProp(Node.IS_NAMESPACE)) { + processProvideFromPreviousPass(t, name, parent); + } else { + ProvidedName pn = providedNames.get(name); + if (pn != null) { + pn.addDefinition(parent, t.getModule()); + } + } + } + } + } + + /** + * Processes the base class call. + */ + private void processBaseClassCall(NodeTraversal t, Node n) { + // Two things must hold for every goog.base call: + // 1) We must be calling it on "this". + // 2) We must be calling it on a prototype method of the same name as + // the one we're in, OR we must be calling it from a constructor. + // If both of those things are true, then we can rewrite: + //
      +    // function Foo() {
      +    //   goog.base(this);
      +    // }
      +    // goog.inherits(Foo, BaseFoo);
      +    // Foo.prototype.bar = function() {
      +    //   goog.base(this, 'bar', 1);
      +    // };
      +    // 
      + // as the easy-to-optimize: + //
      +    // function Foo() {
      +    //   BaseFoo.call(this);
      +    // }
      +    // goog.inherits(Foo, BaseFoo);
      +    // Foo.prototype.bar = function() {
      +    //   Foo.superClass_.bar.call(this, 1);
      +    // };
      +    //
      +    // Most of the logic here is just to make sure the AST's
      +    // structure is what we expect it to be.
      +
      +    Node callee = n.getFirstChild();
      +    Node thisArg = callee.getNext();
      +    if (thisArg == null || !thisArg.isThis()) {
      +      reportBadBaseClassUse(t, n, "First argument must be 'this'.");
      +      return;
      +    }
      +
      +    Node enclosingFnNameNode = getEnclosingDeclNameNode(t);
      +    if (enclosingFnNameNode == null) {
      +      reportBadBaseClassUse(t, n, "Could not find enclosing method.");
      +      return;
      +    }
      +
      +    String enclosingQname = enclosingFnNameNode.getQualifiedName();
      +    if (enclosingQname.indexOf(".prototype.") == -1) {
      +      // Handle constructors.
      +      Node enclosingParent = enclosingFnNameNode.getParent();
      +      Node maybeInheritsExpr = (enclosingParent.isAssign() ?
      +          enclosingParent.getParent() : enclosingParent).getNext();
      +      Node baseClassNode = null;
      +      if (maybeInheritsExpr != null &&
      +          maybeInheritsExpr.isExprResult() &&
      +          maybeInheritsExpr.getFirstChild().isCall()) {
      +        Node callNode = maybeInheritsExpr.getFirstChild();
      +        if ("goog.inherits".equals(
      +                callNode.getFirstChild().getQualifiedName()) &&
      +            callNode.getLastChild().isQualifiedName()) {
      +          baseClassNode = callNode.getLastChild();
      +        }
      +      }
      +
      +      if (baseClassNode == null) {
      +        reportBadBaseClassUse(
      +            t, n, "Could not find goog.inherits for base class");
      +        return;
      +      }
      +
      +      // We're good to go.
      +      n.replaceChild(
      +          callee,
      +          NodeUtil.newQualifiedNameNode(
      +            compiler.getCodingConvention(),
      +            String.format("%s.call", baseClassNode.getQualifiedName()),
      +            callee, "goog.base"));
      +      compiler.reportCodeChange();
      +    } else {
      +      // Handle methods.
      +      Node methodNameNode = thisArg.getNext();
      +      if (methodNameNode == null || !methodNameNode.isString()) {
      +        reportBadBaseClassUse(t, n, "Second argument must name a method.");
      +        return;
      +      }
      +
      +      String methodName = methodNameNode.getString();
      +      String ending = ".prototype." + methodName;
      +      if (enclosingQname == null ||
      +          !enclosingQname.endsWith(ending)) {
      +        reportBadBaseClassUse(
      +            t, n, "Enclosing method does not match " + methodName);
      +        return;
      +      }
      +
      +      // We're good to go.
      +      Node className =
      +          enclosingFnNameNode.getFirstChild().getFirstChild();
      +      n.replaceChild(
      +          callee,
      +          NodeUtil.newQualifiedNameNode(
      +            compiler.getCodingConvention(),
      +            String.format("%s.superClass_.%s.call",
      +                className.getQualifiedName(), methodName),
      +            callee, "goog.base"));
      +      n.removeChild(methodNameNode);
      +      compiler.reportCodeChange();
      +    }
      +  }
      +
      +  /**
      +   * Returns the qualified name node of the function whose scope we're in,
      +   * or null if it cannot be found.
      +   */
      +  private Node getEnclosingDeclNameNode(NodeTraversal t) {
      +    Node scopeRoot = t.getScopeRoot();
      +    if (NodeUtil.isFunctionDeclaration(scopeRoot)) {
      +      // function x() {...}
      +      return scopeRoot.getFirstChild();
      +    } else {
      +      Node parent = scopeRoot.getParent();
      +      if (parent != null) {
      +        if (parent.isAssign() ||
      +            parent.getLastChild() == scopeRoot &&
      +            parent.getFirstChild().isQualifiedName()) {
      +          // x.y.z = function() {...};
      +          return parent.getFirstChild();
      +        } else if (parent.isName()) {
      +          // var x = function() {...};
      +          return parent;
      +        }
      +      }
      +    }
      +
      +    return null;
      +  }
      +
      +  /** Reports an incorrect use of super-method calling. */
      +  private void reportBadBaseClassUse(
      +      NodeTraversal t, Node n, String extraMessage) {
      +    compiler.report(t.makeError(n, BASE_CLASS_ERROR, extraMessage));
      +  }
      +
      +  /**
      +   * Processes the output of processed-provide from a previous pass.  This will
      +   * update our data structures in the same manner as if the provide had been
      +   * processed in this pass.
      +   */
      +  private void processProvideFromPreviousPass(
      +      NodeTraversal t, String name, Node parent) {
      +    if (!providedNames.containsKey(name)) {
      +      // Record this provide created on a previous pass, and create a dummy
      +      // EXPR node as a placeholder to simulate an explicit provide.
      +      Node expr = new Node(Token.EXPR_RESULT);
      +      expr.copyInformationFromForTree(parent);
      +      parent.getParent().addChildBefore(expr, parent);
      +      compiler.reportCodeChange();
      +
      +      JSModule module = t.getModule();
      +      registerAnyProvidedPrefixes(name, expr, module);
      +
      +      ProvidedName provided = new ProvidedName(name, expr, module, true);
      +      providedNames.put(name, provided);
      +      provided.addDefinition(parent, module);
      +    } else {
      +      // Remove this provide if it came from a previous pass since we have an
      +      // replacement already.
      +      if (isNamespacePlaceholder(parent)) {
      +        parent.getParent().removeChild(parent);
      +        compiler.reportCodeChange();
      +      }
      +    }
      +  }
      +
      +  /**
      +   * Processes a call to goog.setCssNameMapping(). Either the argument to
      +   * goog.setCssNameMapping() is valid, in which case it will be used to create
      +   * a CssRenamingMap for the compiler of this CompilerPass, or it is invalid
      +   * and a JSCompiler error will be reported.
      +   * @see #visit(NodeTraversal, Node, Node)
      +   */
      +  private void processSetCssNameMapping(NodeTraversal t, Node n, Node parent) {
      +    Node left = n.getFirstChild();
      +    Node arg = left.getNext();
      +    if (verifySetCssNameMapping(t, left, arg)) {
      +      // Translate OBJECTLIT into SubstitutionMap. All keys and
      +      // values must be strings, or an error will be thrown.
      +      final Map cssNames = Maps.newHashMap();
      +
      +      for (Node key = arg.getFirstChild(); key != null;
      +          key = key.getNext()) {
      +        Node value = key.getFirstChild();
      +        if (!key.isStringKey()
      +            || value == null
      +            || !value.isString()) {
      +          compiler.report(
      +              t.makeError(n,
      +                  NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR));
      +          return;
      +        }
      +        cssNames.put(key.getString(), value.getString());
      +      }
      +
      +      String styleStr = "BY_PART";
      +      if (arg.getNext() != null) {
      +        styleStr = arg.getNext().getString();
      +      }
      +
      +      final CssRenamingMap.Style style;
      +      try {
      +        style = CssRenamingMap.Style.valueOf(styleStr);
      +      } catch (IllegalArgumentException e) {
      +        compiler.report(
      +            t.makeError(n, INVALID_STYLE_ERROR, styleStr));
      +        return;
      +      }
      +
      +      if (style == CssRenamingMap.Style.BY_PART) {
      +        // Make sure that no keys contain -'s
      +        List errors = Lists.newArrayList();
      +        for (String key : cssNames.keySet()) {
      +          if (key.contains("-")) {
      +            errors.add(key);
      +          }
      +        }
      +        if (errors.size() != 0) {
      +          compiler.report(
      +            t.makeError(n, INVALID_CSS_RENAMING_MAP, errors.toString()));
      +        }
      +      } else if (style == CssRenamingMap.Style.BY_WHOLE) {
      +        // Verifying things is a lot trickier here. We just do a quick
      +        // n^2 check over the map which makes sure that if "a-b" in
      +        // the map, then map(a-b) = map(a)-map(b).
      +        // To speed things up, only consider cases where len(b) <= 10
      +        List errors = Lists.newArrayList();
      +        for (Map.Entry b : cssNames.entrySet()) {
      +          if (b.getKey().length() > 10) continue;
      +          for (Map.Entry a : cssNames.entrySet()) {
      +            String combined = cssNames.get(a.getKey() + "-" + b.getKey());
      +            if (combined != null &&
      +                !combined.equals(a.getValue() + "-" + b.getValue())) {
      +              errors.add("map(" + a.getKey() + "-" + b.getKey() +") != map(" +
      +                         a.getKey() + ")-map(" + b.getKey() +")");
      +            }
      +          }
      +        }
      +        if (errors.size() != 0) {
      +          compiler.report(
      +            t.makeError(n, INVALID_CSS_RENAMING_MAP, errors.toString()));
      +        }
      +      }
      +
      +      CssRenamingMap cssRenamingMap = new CssRenamingMap() {
      +        @Override
      +        public String get(String value) {
      +          if (cssNames.containsKey(value)) {
      +            return cssNames.get(value);
      +          } else {
      +            return value;
      +          }
      +        }
      +
      +        @Override
      +        public CssRenamingMap.Style getStyle() {
      +          return style;
      +        }
      +      };
      +      compiler.setCssRenamingMap(cssRenamingMap);
      +      parent.getParent().removeChild(parent);
      +      compiler.reportCodeChange();
      +    }
      +  }
      +
      +  /**
      +   * Verifies that a provide method call has exactly one argument,
      +   * and that it's a string literal and that the contents of the string are
      +   * valid JS tokens. Reports a compile error if it doesn't.
      +   *
      +   * @return Whether the argument checked out okay
      +   */
      +  private boolean verifyProvide(NodeTraversal t, Node methodName, Node arg) {
      +    if (!verifyArgument(t, methodName, arg)) {
      +      return false;
      +    }
      +
      +    for (String part : arg.getString().split("\\.")) {
      +      if (!NodeUtil.isValidPropertyName(part)) {
      +        compiler.report(t.makeError(arg, INVALID_PROVIDE_ERROR, part));
      +        return false;
      +      }
      +    }
      +    return true;
      +  }
      +
      +  /**
      +   * Verifies that a method call has exactly one argument, and that it's a
      +   * string literal. Reports a compile error if it doesn't.
      +   *
      +   * @return Whether the argument checked out okay
      +   */
      +  private boolean verifyArgument(NodeTraversal t, Node methodName, Node arg) {
      +    return verifyArgument(t, methodName, arg, Token.STRING);
      +  }
      +
      +  /**
      +   * Verifies that a method call has exactly one argument, and that it is of the
      +   * desired type. Reports a compile error if it doesn't.
      +   *
      +   * @return Whether the argument checked out okay
      +   */
      +  private boolean verifyArgument(NodeTraversal t, Node methodName, Node arg,
      +      int desiredType) {
      +    DiagnosticType diagnostic = null;
      +    if (arg == null) {
      +      diagnostic = NULL_ARGUMENT_ERROR;
      +    } else if (arg.getType() != desiredType) {
      +      diagnostic = INVALID_ARGUMENT_ERROR;
      +    } else if (arg.getNext() != null) {
      +      diagnostic = TOO_MANY_ARGUMENTS_ERROR;
      +    }
      +    if (diagnostic != null) {
      +      compiler.report(
      +          t.makeError(methodName,
      +              diagnostic, methodName.getQualifiedName()));
      +      return false;
      +    }
      +    return true;
      +  }
      +
      +  /**
      +   * Verifies that setCssNameMapping is called with the correct methods.
      +   *
      +   * @return Whether the arguments checked out okay
      +   */
      +  private boolean verifySetCssNameMapping(NodeTraversal t, Node methodName,
      +      Node firstArg) {
      +    DiagnosticType diagnostic = null;
      +    if (firstArg == null) {
      +      diagnostic = NULL_ARGUMENT_ERROR;
      +    } else if (!firstArg.isObjectLit()) {
      +      diagnostic = EXPECTED_OBJECTLIT_ERROR;
      +    } else if (firstArg.getNext() != null) {
      +      Node secondArg = firstArg.getNext();
      +      if (!secondArg.isString()) {
      +        diagnostic = EXPECTED_STRING_ERROR;
      +      } else if (secondArg.getNext() != null) {
      +        diagnostic = TOO_MANY_ARGUMENTS_ERROR;
      +      }
      +    }
      +    if (diagnostic != null) {
      +      compiler.report(
      +          t.makeError(methodName,
      +              diagnostic, methodName.getQualifiedName()));
      +      return false;
      +    }
      +    return true;
      +  }
      +
      +  /**
      +   * Registers ProvidedNames for prefix namespaces if they haven't
      +   * already been defined. The prefix namespaces must be registered in
      +   * order from shortest to longest.
      +   *
      +   * @param ns The namespace whose prefixes may need to be provided.
      +   * @param node The EXPR of the provide call.
      +   * @param module The current module.
      +   */
      +  private void registerAnyProvidedPrefixes(
      +      String ns, Node node, JSModule module) {
      +    int pos = ns.indexOf('.');
      +    while (pos != -1) {
      +      String prefixNs = ns.substring(0, pos);
      +      pos = ns.indexOf('.', pos + 1);
      +      if (providedNames.containsKey(prefixNs)) {
      +        providedNames.get(prefixNs).addProvide(
      +            node, module, false /* implicit */);
      +      } else {
      +        providedNames.put(
      +            prefixNs,
      +            new ProvidedName(prefixNs, node, module, false /* implicit */));
      +      }
      +    }
      +  }
      +
      +  // -------------------------------------------------------------------------
      +
      +  /**
      +   * Information required to replace a goog.provide call later in the traversal.
      +   */
      +  private class ProvidedName {
      +    private final String namespace;
      +
      +    // The node and module where the call was explicitly or implicitly
      +    // goog.provided.
      +    private final Node firstNode;
      +    private final JSModule firstModule;
      +
      +    // The node where the call was explicitly goog.provided. May be null
      +    // if the namespace is always provided implicitly.
      +    private Node explicitNode = null;
      +    private JSModule explicitModule = null;
      +
      +    // The candidate definition.
      +    private Node candidateDefinition = null;
      +
      +    // The minimum module where the provide must appear.
      +    private JSModule minimumModule = null;
      +
      +    // The replacement declaration.
      +    private Node replacementNode = null;
      +
      +    ProvidedName(String namespace, Node node, JSModule module,
      +        boolean explicit) {
      +      Preconditions.checkArgument(
      +          node == null /* The base case */ ||
      +          node.isExprResult());
      +      this.namespace = namespace;
      +      this.firstNode = node;
      +      this.firstModule = module;
      +
      +      addProvide(node, module, explicit);
      +    }
      +
      +    /**
      +     * Add an implicit or explicit provide.
      +     */
      +    void addProvide(Node node, JSModule module, boolean explicit) {
      +      if (explicit) {
      +        Preconditions.checkState(explicitNode == null);
      +        Preconditions.checkArgument(node.isExprResult());
      +        explicitNode = node;
      +        explicitModule = module;
      +      }
      +      updateMinimumModule(module);
      +    }
      +
      +    boolean isExplicitlyProvided() {
      +      return explicitNode != null;
      +    }
      +
      +    /**
      +     * Record function declaration, variable declaration or assignment that
      +     * refers to the same name as the provide statement.  Give preference to
      +     * declarations; if no declaration exists, record a reference to an
      +     * assignment so it repurposed later.
      +     */
      +    void addDefinition(Node node, JSModule module) {
      +      Preconditions.checkArgument(node.isExprResult() || // assign
      +                                  node.isFunction() ||
      +                                  node.isVar());
      +      Preconditions.checkArgument(explicitNode != node);
      +      if ((candidateDefinition == null) || !node.isExprResult()) {
      +        candidateDefinition = node;
      +        updateMinimumModule(module);
      +      }
      +    }
      +
      +    private void updateMinimumModule(JSModule newModule) {
      +      if (minimumModule == null) {
      +        minimumModule = newModule;
      +      } else if (moduleGraph != null) {
      +        minimumModule = moduleGraph.getDeepestCommonDependencyInclusive(
      +            minimumModule, newModule);
      +      } else {
      +        // If there is no module graph, then there must be exactly one
      +        // module in the program.
      +        Preconditions.checkState(newModule == minimumModule,
      +                                 "Missing module graph");
      +      }
      +    }
      +
      +    /**
      +     * Replace the provide statement.
      +     *
      +     * If we're providing a name with no definition, then create one.
      +     * If we're providing a name with a duplicate definition, then make sure
      +     * that definition becomes a declaration.
      +     */
      +    void replace() {
      +      if (firstNode == null) {
      +        // Don't touch the base case ('goog').
      +        replacementNode = candidateDefinition;
      +        return;
      +      }
      +
      +      // Handle the case where there is a duplicate definition for an explicitly
      +      // provided symbol.
      +      if (candidateDefinition != null && explicitNode != null) {
      +        explicitNode.detachFromParent();
      +        compiler.reportCodeChange();
      +
      +        // Does this need a VAR keyword?
      +        replacementNode = candidateDefinition;
      +        if (candidateDefinition.isExprResult() &&
      +            !candidateDefinition.getFirstChild().isQualifiedName()) {
      +          candidateDefinition.putBooleanProp(Node.IS_NAMESPACE, true);
      +          Node assignNode = candidateDefinition.getFirstChild();
      +          Node nameNode = assignNode.getFirstChild();
      +          if (nameNode.isName()) {
      +            // Need to convert this assign to a var declaration.
      +            Node valueNode = nameNode.getNext();
      +            assignNode.removeChild(nameNode);
      +            assignNode.removeChild(valueNode);
      +            nameNode.addChildToFront(valueNode);
      +            Node varNode = IR.var(nameNode);
      +            varNode.copyInformationFrom(candidateDefinition);
      +            candidateDefinition.getParent().replaceChild(
      +                candidateDefinition, varNode);
      +            nameNode.setJSDocInfo(assignNode.getJSDocInfo());
      +            compiler.reportCodeChange();
      +            replacementNode = varNode;
      +          }
      +        }
      +      } else {
      +        // Handle the case where there's not a duplicate definition.
      +        replacementNode = createDeclarationNode();
      +        if (firstModule == minimumModule) {
      +          firstNode.getParent().addChildBefore(replacementNode, firstNode);
      +        } else {
      +          // In this case, the name was implicitly provided by two independent
      +          // modules. We need to move this code up to a common module.
      +          int indexOfDot = namespace.lastIndexOf('.');
      +          if (indexOfDot == -1) {
      +            // Any old place is fine.
      +            compiler.getNodeForCodeInsertion(minimumModule)
      +                .addChildToBack(replacementNode);
      +          } else {
      +            // Add it after the parent namespace.
      +            ProvidedName parentName =
      +                providedNames.get(namespace.substring(0, indexOfDot));
      +            Preconditions.checkNotNull(parentName);
      +            Preconditions.checkNotNull(parentName.replacementNode);
      +            parentName.replacementNode.getParent().addChildAfter(
      +                replacementNode, parentName.replacementNode);
      +          }
      +        }
      +        if (explicitNode != null) {
      +          explicitNode.detachFromParent();
      +        }
      +        compiler.reportCodeChange();
      +      }
      +    }
      +
      +    /**
      +     * Create the declaration node for this name, without inserting it
      +     * into the AST.
      +     */
      +    private Node createDeclarationNode() {
      +      if (namespace.indexOf('.') == -1) {
      +        return makeVarDeclNode();
      +      } else {
      +        return makeAssignmentExprNode();
      +      }
      +    }
      +
      +    /**
      +     * Creates a simple namespace variable declaration
      +     * (e.g. var foo = {};).
      +     */
      +    private Node makeVarDeclNode() {
      +      Node name = IR.name(namespace);
      +      name.addChildToFront(createNamespaceLiteral());
      +
      +      Node decl = IR.var(name);
      +      decl.putBooleanProp(Node.IS_NAMESPACE, true);
      +
      +      // TODO(nicksantos): ew ew ew. Create a mutator package.
      +      if (compiler.getCodingConvention().isConstant(namespace)) {
      +        name.putBooleanProp(Node.IS_CONSTANT_NAME, true);
      +      }
      +      if (candidateDefinition == null) {
      +        name.setJSDocInfo(createConstantJsDoc());
      +      }
      +
      +      Preconditions.checkState(isNamespacePlaceholder(decl));
      +      setSourceInfo(decl);
      +      return decl;
      +    }
      +
      +    /**
      +     * There are some special cases where clients of the compiler
      +     * do not run TypedScopeCreator after running this pass.
      +     * So always give the namespace literal a type.
      +     */
      +    private Node createNamespaceLiteral() {
      +      Node objlit = IR.objectlit();
      +      objlit.setJSType(
      +          compiler.getTypeRegistry().createAnonymousObjectType(null));
      +      return objlit;
      +    }
      +
      +    /**
      +     * Creates a dotted namespace assignment expression
      +     * (e.g. foo.bar = {};).
      +     */
      +    private Node makeAssignmentExprNode() {
      +      Node decl = IR.exprResult(
      +          IR.assign(
      +              NodeUtil.newQualifiedNameNode(
      +                  compiler.getCodingConvention(), namespace,
      +                  firstNode /* real source info will be filled in below */,
      +                  namespace),
      +              createNamespaceLiteral()));
      +      decl.putBooleanProp(Node.IS_NAMESPACE, true);
      +      if (candidateDefinition == null) {
      +        decl.getFirstChild().setJSDocInfo(createConstantJsDoc());
      +      }
      +      Preconditions.checkState(isNamespacePlaceholder(decl));
      +      setSourceInfo(decl);
      +      return decl;
      +    }
      +
      +    private JSDocInfo createConstantJsDoc() {
      +      JSDocInfoBuilder builder = new JSDocInfoBuilder(false);
      +      builder.recordConstancy();
      +      return builder.build(null);
      +    }
      +
      +    /**
      +     * Copy source info to the new node.
      +     */
      +    private void setSourceInfo(Node newNode) {
      +      Node provideStringNode = getProvideStringNode();
      +      int offset = getSourceInfoOffset(provideStringNode);
      +      Node sourceInfoNode = provideStringNode == null
      +          ? firstNode : provideStringNode;
      +      newNode.copyInformationFromForTree(sourceInfoNode);
      +      if (offset != 0) {
      +        newNode.setSourceEncodedPositionForTree(
      +            sourceInfoNode.getSourcePosition() + offset);
      +      }
      +    }
      +
      +    /**
      +     * Get the offset into the provide node where the symbol appears.
      +     */
      +    private int getSourceInfoOffset(Node provideStringNode) {
      +      if (provideStringNode == null) {
      +        return 0;
      +      }
      +
      +      int indexOfLastDot = namespace.lastIndexOf('.');
      +
      +      // +1 for the opening quote
      +      // +1 for the dot
      +      // if there's no dot, then the -1 index cancels it out
      +      // so elegant!
      +      return 2 + indexOfLastDot;
      +    }
      +
      +    private Node getProvideStringNode() {
      +      return (firstNode.getFirstChild() != null &&
      +              NodeUtil.isExprCall(firstNode)) ?
      +          firstNode.getFirstChild().getLastChild() :
      +          null;
      +    }
      +  }
      +
      +  /**
      +   * @return Whether the node is namespace placeholder.
      +   */
      +  private static boolean isNamespacePlaceholder(Node n) {
      +    if (!n.getBooleanProp(Node.IS_NAMESPACE)) {
      +      return false;
      +    }
      +
      +    Node value = null;
      +    if (n.isExprResult()) {
      +      Node assign = n.getFirstChild();
      +      value = assign.getLastChild();
      +    } else if (n.isVar()) {
      +      Node name = n.getFirstChild();
      +      value = name.getFirstChild();
      +    }
      +
      +    return value != null
      +      && value.isObjectLit()
      +      && !value.hasChildren();
      +  }
      +
      +  /**
      +   * The string in {@code n} is a reference name. Create a synthetic
      +   * node for it with all the proper source info, and add it to the symbol
      +   * table.
      +   */
      +  private void maybeAddStringNodeToSymbolTable(Node n) {
      +    if (preprocessorSymbolTable == null) {
      +      return;
      +    }
      +
      +    String name = n.getString();
      +    Node syntheticRef = NodeUtil.newQualifiedNameNode(
      +        compiler.getCodingConvention(), name,
      +        n /* real source offsets will be filled in below */,
      +        name);
      +
      +    // Offsets to add to source. Named for documentation purposes.
      +    final int FOR_QUOTE = 1;
      +    final int FOR_DOT = 1;
      +
      +    Node current = null;
      +    for (current = syntheticRef;
      +         current.isGetProp();
      +         current = current.getFirstChild()) {
      +      int fullLen = current.getQualifiedName().length();
      +      int namespaceLen = current.getFirstChild().getQualifiedName().length();
      +
      +      current.setSourceEncodedPosition(n.getSourcePosition() + FOR_QUOTE);
      +      current.setLength(fullLen);
      +
      +      current.getLastChild().setSourceEncodedPosition(
      +          n.getSourcePosition() + namespaceLen + FOR_QUOTE + FOR_DOT);
      +      current.getLastChild().setLength(
      +          current.getLastChild().getString().length());
      +    }
      +
      +    current.setSourceEncodedPosition(n.getSourcePosition() + FOR_QUOTE);
      +    current.setLength(current.getString().length());
      +
      +    maybeAddToSymbolTable(syntheticRef);
      +  }
      +
      +  /**
      +   * Add the given qualified name node to the symbol table.
      +   */
      +  private void maybeAddToSymbolTable(Node n) {
      +    if (preprocessorSymbolTable != null) {
      +      preprocessorSymbolTable.addReference(n);
      +    }
      +  }
      +
      +  // -------------------------------------------------------------------------
      +
      +  /**
      +   * Information required to create a {@code MISSING_PROVIDE_ERROR} warning.
      +   */
      +  private class UnrecognizedRequire {
      +    final Node requireNode;
      +    final String namespace;
      +    final String inputName;
      +
      +    UnrecognizedRequire(Node requireNode, String namespace, String inputName) {
      +      this.requireNode = requireNode;
      +      this.namespace = namespace;
      +      this.inputName = inputName;
      +    }
      +  }
      +}
      diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ProcessCommonJSModules.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ProcessCommonJSModules.java
      new file mode 100644
      index 0000000..6777957
      --- /dev/null
      +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ProcessCommonJSModules.java
      @@ -0,0 +1,287 @@
      +/*
      + * Copyright 2011 The Closure Compiler Authors.
      + *
      + * Licensed under the Apache License, Version 2.0 (the "License");
      + * you may not use this file except in compliance with the License.
      + * You may obtain a copy of the License at
      + *
      + *     http://www.apache.org/licenses/LICENSE-2.0
      + *
      + * Unless required by applicable law or agreed to in writing, software
      + * distributed under the License is distributed on an "AS IS" BASIS,
      + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      + * See the License for the specific language governing permissions and
      + * limitations under the License.
      + */
      +package com.google.javascript.jscomp;
      +
      +import com.google.common.base.Preconditions;
      +import com.google.common.collect.Sets;
      +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
      +import com.google.javascript.rhino.IR;
      +import com.google.javascript.rhino.Node;
      +
      +import java.net.URI;
      +import java.net.URISyntaxException;
      +import java.util.Set;
      +import java.util.regex.Pattern;
      +
      +/**
      + * Rewrites a CommonJS module http://wiki.commonjs.org/wiki/Modules/1.1.1
      + * into a form that can be safely concatenated.
      + * Does not add a function around the module body but instead adds suffixes
      + * to global variables to avoid conflicts.
      + * Calls to require are changed to reference the required module directly.
      + * goog.provide and goog.require are emitted for closure compiler automatic
      + * ordering.
      + */
      +public class ProcessCommonJSModules implements CompilerPass {
      +  // According to the spec, the forward slash should be the delimite on
      +  // all platforms.
      +  private static final String MODULE_SLASH = "/";
      +
      +  public static final String DEFAULT_FILENAME_PREFIX = "." + MODULE_SLASH;
      +
      +  private static final String MODULE_NAME_SEPARATOR = "\\$";
      +  private static final String MODULE_NAME_PREFIX = "module$";
      +
      +  private final AbstractCompiler compiler;
      +  private final String filenamePrefix;
      +  private final boolean reportDependencies;
      +  private JSModule module;
      +
      +  ProcessCommonJSModules(AbstractCompiler compiler, String filenamePrefix) {
      +    this(compiler, filenamePrefix, true);
      +  }
      +
      +  ProcessCommonJSModules(AbstractCompiler compiler, String filenamePrefix,
      +      boolean reportDependencies) {
      +    this.compiler = compiler;
      +    this.filenamePrefix = filenamePrefix.endsWith(MODULE_SLASH) ?
      +        filenamePrefix : filenamePrefix + MODULE_SLASH;
      +    this.reportDependencies = reportDependencies;
      +  }
      +
      +  @Override
      +  public void process(Node externs, Node root) {
      +    NodeTraversal
      +        .traverse(compiler, root, new ProcessCommonJsModulesCallback());
      +  }
      +
      +  String guessCJSModuleName(String filename) {
      +    return toModuleName(normalizeSourceName(filename));
      +  }
      +
      +  /**
      +   * For every file that is being processed this returns the module that
      +   * created for it.
      +   */
      +  JSModule getModule() {
      +    return module;
      +  }
      +
      +  /**
      +   * Turns a filename into a JS identifier that is used for moduleNames in
      +   * rewritten code. Removes leading ./, replaces / with $, removes trailing .js
      +   * and replaces - with _. All moduleNames get a "module$" prefix.
      +   */
      +  public static String toModuleName(String filename) {
      +    return MODULE_NAME_PREFIX +
      +        filename.replaceAll("^\\." + Pattern.quote(MODULE_SLASH), "")
      +            .replaceAll(Pattern.quote(MODULE_SLASH), MODULE_NAME_SEPARATOR)
      +            .replaceAll("\\.js$", "").replaceAll("-", "_");
      +  }
      +
      +  /**
      +   * Turn a filename into a moduleName with support for relative addressing
      +   * with ./ and ../ based on currentFilename;
      +   */
      +  public static String toModuleName(String requiredFilename,
      +      String currentFilename) {
      +    requiredFilename = requiredFilename.replaceAll("\\.js$", "");
      +    currentFilename = currentFilename.replaceAll("\\.js$", "");
      +
      +    if (requiredFilename.startsWith("." + MODULE_SLASH) ||
      +        requiredFilename.startsWith(".." + MODULE_SLASH)) {
      +      try {
      +        requiredFilename = (new URI(currentFilename)).resolve(new URI(requiredFilename))
      +            .toString();
      +      } catch (URISyntaxException e) {
      +        throw new RuntimeException(e);
      +      }
      +    }
      +    return toModuleName(requiredFilename);
      +  }
      +
      +  private String normalizeSourceName(String filename) {
      +    // The DOS command shell will normalize "/" to "\", so we have to
      +    // wrestle it back.
      +    filename = filename.replace("\\", "/");
      +
      +    if (filename.indexOf(filenamePrefix) == 0) {
      +      filename = filename.substring(filenamePrefix.length());
      +    }
      +
      +    return filename;
      +  }
      +
      +  /**
      +   * Visits require, every "script" and special module.exports assignments.
      +   */
      +  private class ProcessCommonJsModulesCallback extends
      +      AbstractPostOrderCallback {
      +
      +    private int scriptNodeCount = 0;
      +    private Set modulesWithExports = Sets.newHashSet();
      +
      +    @Override
      +    public void visit(NodeTraversal t, Node n, Node parent) {
      +      if (n.isCall() && n.getChildCount() == 2 &&
      +          "require".equals(n.getFirstChild().getQualifiedName()) &&
      +          n.getChildAtIndex(1).isString()) {
      +        visitRequireCall(t, n, parent);
      +      }
      +
      +      if (n.isScript()) {
      +        scriptNodeCount++;
      +        visitScript(t, n);
      +      }
      +
      +      if (n.isGetProp() &&
      +          "module.exports".equals(n.getQualifiedName())) {
      +        visitModuleExports(n);
      +      }
      +    }
      +
      +    /**
      +     * Visit require calls. Emit corresponding goog.require and rewrite require
      +     * to be a direct reference to name of require module.
      +     */
      +    private void visitRequireCall(NodeTraversal t, Node require, Node parent) {
      +      String moduleName = toModuleName(require.getChildAtIndex(1).getString(),
      +          normalizeSourceName(t.getSourceName()));
      +      Node moduleRef = IR.name(moduleName).srcref(require);
      +      parent.replaceChild(require, moduleRef);
      +      Node script = getCurrentScriptNode(parent);
      +      if (reportDependencies) {
      +        t.getInput().addRequire(moduleName);
      +      }
      +      // Rewrite require("name").
      +      script.addChildToFront(IR.exprResult(
      +          IR.call(IR.getprop(IR.name("goog"), IR.string("require")),
      +              IR.string(moduleName))).copyInformationFromForTree(require));
      +      compiler.reportCodeChange();
      +    }
      +
      +    /**
      +     * Emit goog.provide and add suffix to all global vars to avoid conflicts
      +     * with other modules.
      +     */
      +    private void visitScript(NodeTraversal t, Node script) {
      +      Preconditions.checkArgument(scriptNodeCount == 1,
      +          "ProcessCommonJSModules supports only one invocation per " +
      +          "CompilerInput / script node");
      +      String moduleName = guessCJSModuleName(script.getSourceFileName());
      +      script.addChildToFront(IR.var(IR.name(moduleName), IR.objectlit())
      +          .copyInformationFromForTree(script));
      +      if (reportDependencies) {
      +        CompilerInput ci = t.getInput();
      +        ci.addProvide(moduleName);
      +        JSModule m = new JSModule(moduleName);
      +        m.addAndOverrideModule(ci);
      +        module = m;
      +      }
      +      script.addChildToFront(IR.exprResult(
      +          IR.call(IR.getprop(IR.name("goog"), IR.string("provide")),
      +              IR.string(moduleName))).copyInformationFromForTree(script));
      +
      +      emitOptionalModuleExportsOverride(script, moduleName);
      +
      +      // Rename vars to not conflict in global scope.
      +      NodeTraversal.traverse(compiler, script, new SuffixVarsCallback(
      +          moduleName));
      +
      +      compiler.reportCodeChange();
      +    }
      +
      +    /**
      +     * Emit if (moduleName.module$exports) {
      +     *    moduleName = moduleName.module$export;
      +     * } at end of file.
      +     */
      +    private void emitOptionalModuleExportsOverride(Node script,
      +        String moduleName) {
      +      if (!modulesWithExports.contains(moduleName)) {
      +        return;
      +      }
      +
      +      Node moduleExportsProp = IR.getprop(IR.name(moduleName),
      +          IR.string("module$exports"));
      +      script.addChildToBack(IR.ifNode(
      +          moduleExportsProp,
      +          IR.block(IR.exprResult(IR.assign(IR.name(moduleName),
      +              moduleExportsProp.cloneTree())))).copyInformationFromForTree(
      +          script));
      +    }
      +
      +    /**
      +     * Rewrite module.exports to moduleName.module$exports.
      +     */
      +    private void visitModuleExports(Node prop) {
      +      String moduleName = guessCJSModuleName(prop.getSourceFileName());
      +      Node module = prop.getChildAtIndex(0);
      +      module.putProp(Node.ORIGINALNAME_PROP, "module");
      +      module.setString(moduleName);
      +      Node exports = prop.getChildAtIndex(1);
      +      exports.putProp(Node.ORIGINALNAME_PROP, "exports");
      +      exports.setString("module$exports");
      +      modulesWithExports.add(moduleName);
      +    }
      +
      +    /**
      +     * Returns next script node in parents.
      +     */
      +    private Node getCurrentScriptNode(Node n) {
      +      while (true) {
      +        if (n.isScript()) {
      +          return n;
      +        }
      +        n = n.getParent();
      +      }
      +    }
      +  }
      +
      +  /**
      +   * Traverses a node tree and appends a suffix to all global variable names.
      +   */
      +  private class SuffixVarsCallback extends AbstractPostOrderCallback {
      +
      +    private static final String EXPORTS = "exports";
      +
      +    private final String suffix;
      +
      +    SuffixVarsCallback(String suffix) {
      +      this.suffix = suffix;
      +    }
      +
      +    @Override
      +    public void visit(NodeTraversal t, Node n, Node parent) {
      +      if (n.isName()) {
      +        String name = n.getString();
      +        if (suffix.equals(name)) {
      +          return;
      +        }
      +        if (EXPORTS.equals(name)) {
      +          n.setString(suffix);
      +          n.putProp(Node.ORIGINALNAME_PROP, EXPORTS);
      +        } else {
      +          Scope.Var var = t.getScope().getVar(name);
      +          if (var != null && var.isGlobal()) {
      +            n.setString(name + "$$" + suffix);
      +            n.putProp(Node.ORIGINALNAME_PROP, name);
      +          }
      +        }
      +      }
      +    }
      +  }
      +}
      diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ProcessDefines.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ProcessDefines.java
      new file mode 100644
      index 0000000..e86001d
      --- /dev/null
      +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ProcessDefines.java
      @@ -0,0 +1,543 @@
      +/*
      + * Copyright 2007 The Closure Compiler Authors.
      + *
      + * Licensed under the Apache License, Version 2.0 (the "License");
      + * you may not use this file except in compliance with the License.
      + * You may obtain a copy of the License at
      + *
      + *     http://www.apache.org/licenses/LICENSE-2.0
      + *
      + * Unless required by applicable law or agreed to in writing, software
      + * distributed under the License is distributed on an "AS IS" BASIS,
      + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      + * See the License for the specific language governing permissions and
      + * limitations under the License.
      + */
      +
      +package com.google.javascript.jscomp;
      +
      +import com.google.common.base.Preconditions;
      +import com.google.common.collect.Lists;
      +import com.google.common.collect.Maps;
      +import com.google.common.collect.Sets;
      +import com.google.javascript.jscomp.GlobalNamespace.Name;
      +import com.google.javascript.jscomp.GlobalNamespace.Ref;
      +import com.google.javascript.jscomp.NodeTraversal.Callback;
      +import com.google.javascript.rhino.JSDocInfo;
      +import com.google.javascript.rhino.JSTypeExpression;
      +import com.google.javascript.rhino.Node;
      +import com.google.javascript.rhino.Token;
      +import com.google.javascript.rhino.jstype.JSType;
      +import com.google.javascript.rhino.jstype.JSTypeNative;
      +
      +import java.text.MessageFormat;
      +import java.util.ArrayDeque;
      +import java.util.Deque;
      +import java.util.List;
      +import java.util.Map;
      +import java.util.Set;
      +
      +/**
      + * Process variables annotated as {@code @define}. A define is
      + * a special constant that may be overridden by later files and
      + * manipulated by the compiler, much like C preprocessor {@code #define}s.
      + *
      + * @author nicksantos@google.com (Nick Santos)
      + */
      +class ProcessDefines implements CompilerPass {
      +
      +  /**
      +   * Defines in this set will not be flagged with "unknown define" warnings.
      +   * There are legacy flags that always set these defines, even when they
      +   * might not be in the binary.
      +   */
      +  private static final Set KNOWN_DEFINES =
      +      Sets.newHashSet("COMPILED");
      +
      +  private final AbstractCompiler compiler;
      +  private final Map dominantReplacements;
      +
      +  private GlobalNamespace namespace = null;
      +
      +  // Warnings
      +  static final DiagnosticType UNKNOWN_DEFINE_WARNING = DiagnosticType.warning(
      +      "JSC_UNKNOWN_DEFINE_WARNING",
      +      "unknown @define variable {0}");
      +
      +  // Errors
      +  static final DiagnosticType INVALID_DEFINE_TYPE_ERROR =
      +    DiagnosticType.error(
      +        "JSC_INVALID_DEFINE_TYPE_ERROR",
      +        "@define tag only permits literal types");
      +
      +  static final DiagnosticType INVALID_DEFINE_INIT_ERROR =
      +      DiagnosticType.error(
      +          "JSC_INVALID_DEFINE_INIT_ERROR",
      +          "illegal initialization of @define variable {0}");
      +
      +  static final DiagnosticType NON_GLOBAL_DEFINE_INIT_ERROR =
      +      DiagnosticType.error(
      +          "JSC_NON_GLOBAL_DEFINE_INIT_ERROR",
      +          "@define variable {0} assignment must be global");
      +
      +  static final DiagnosticType DEFINE_NOT_ASSIGNABLE_ERROR =
      +      DiagnosticType.error(
      +          "JSC_DEFINE_NOT_ASSIGNABLE_ERROR",
      +          "@define variable {0} cannot be reassigned due to code at {1}.");
      +
      +  private static final MessageFormat REASON_DEFINE_NOT_ASSIGNABLE =
      +      new MessageFormat("line {0} of {1}");
      +
      +  /**
      +   * Create a pass that overrides define constants.
      +   *
      +   * TODO(nicksantos): Write a builder to help JSCompiler induce
      +   *    {@code replacements} from command-line flags
      +   *
      +   * @param replacements A hash table of names of defines to their replacements.
      +   *   All replacements must be literals.
      +   */
      +  ProcessDefines(AbstractCompiler compiler, Map replacements) {
      +    this.compiler = compiler;
      +    dominantReplacements = replacements;
      +  }
      +
      +  /**
      +   * Injects a pre-computed global namespace, so that the same namespace
      +   * can be re-used for multiple check passes. Returns {@code this} for
      +   * easy chaining.
      +   */
      +  ProcessDefines injectNamespace(GlobalNamespace namespace) {
      +    this.namespace = namespace;
      +    return this;
      +  }
      +
      +  @Override
      +  public void process(Node externs, Node root) {
      +    if (namespace == null) {
      +      namespace = new GlobalNamespace(compiler, root);
      +    }
      +    overrideDefines(collectDefines(root, namespace));
      +  }
      +
      +  private void overrideDefines(Map allDefines) {
      +    boolean changed = false;
      +    for (Map.Entry def : allDefines.entrySet()) {
      +      String defineName = def.getKey();
      +      DefineInfo info = def.getValue();
      +      Node inputValue = dominantReplacements.get(defineName);
      +      Node finalValue = inputValue != null ?
      +          inputValue : info.getLastValue();
      +      if (finalValue != info.initialValue) {
      +        info.initialValueParent.replaceChild(
      +            info.initialValue, finalValue.cloneTree());
      +        compiler.addToDebugLog("Overriding @define variable " + defineName);
      +        changed = changed ||
      +            finalValue.getType() != info.initialValue.getType() ||
      +            !finalValue.isEquivalentTo(info.initialValue);
      +      }
      +    }
      +
      +    if (changed) {
      +      compiler.reportCodeChange();
      +    }
      +
      +    Set unusedReplacements = dominantReplacements.keySet();
      +    unusedReplacements.removeAll(allDefines.keySet());
      +    unusedReplacements.removeAll(KNOWN_DEFINES);
      +    for (String unknownDefine : unusedReplacements) {
      +      compiler.report(JSError.make(UNKNOWN_DEFINE_WARNING, unknownDefine));
      +    }
      +  }
      +
      +  private static String format(MessageFormat format, Object... params) {
      +    return format.format(params);
      +  }
      +
      +  /**
      +   * Only defines of literal number, string, or boolean are supported.
      +   */
      +  private boolean isValidDefineType(JSTypeExpression expression) {
      +    JSType type = expression.evaluate(null, compiler.getTypeRegistry());
      +    return !type.isUnknownType() && type.isSubtype(
      +        compiler.getTypeRegistry().getNativeType(
      +            JSTypeNative.NUMBER_STRING_BOOLEAN));
      +  }
      +
      +  /**
      +   * Finds all defines, and creates a {@link DefineInfo} data structure for
      +   * each one.
      +   * @return A map of {@link DefineInfo} structures, keyed by name.
      +   */
      +  private Map collectDefines(Node root,
      +      GlobalNamespace namespace) {
      +    // Find all the global names with a @define annotation
      +    List allDefines = Lists.newArrayList();
      +    for (Name name : namespace.getNameIndex().values()) {
      +      Ref decl = name.getDeclaration();
      +      if (name.docInfo != null && name.docInfo.isDefine()) {
      +        // Process defines should not depend on check types being enabled,
      +        // so we look for the JSDoc instead of the inferred type.
      +        if (isValidDefineType(name.docInfo.getType())) {
      +          allDefines.add(name);
      +        } else {
      +          JSError error = JSError.make(
      +              decl.getSourceName(),
      +              decl.node, INVALID_DEFINE_TYPE_ERROR);
      +          compiler.report(error);
      +        }
      +      } else {
      +        for (Ref ref : name.getRefs()) {
      +          if (ref == decl) {
      +            // Declarations were handled above.
      +            continue;
      +          }
      +
      +          Node n = ref.node;
      +          Node parent = ref.node.getParent();
      +          JSDocInfo info = n.getJSDocInfo();
      +          if (info == null &&
      +              parent.isVar() && parent.hasOneChild()) {
      +            info = parent.getJSDocInfo();
      +          }
      +
      +          if (info != null && info.isDefine()) {
      +            allDefines.add(name);
      +            break;
      +          }
      +        }
      +      }
      +    }
      +
      +    CollectDefines pass = new CollectDefines(compiler, allDefines);
      +    NodeTraversal.traverse(compiler, root, pass);
      +    return pass.getAllDefines();
      +  }
      +
      +  /**
      +   * Finds all assignments to @defines, and figures out the last value of
      +   * the @define.
      +   */
      +  private static final class CollectDefines implements Callback {
      +
      +    private final AbstractCompiler compiler;
      +    private final Map assignableDefines;
      +    private final Map allDefines;
      +    private final Map allRefInfo;
      +
      +    // A hack that allows us to remove ASSIGN/VAR statements when
      +    // we're currently visiting one of the children of the assign.
      +    private Node lvalueToRemoveLater = null;
      +
      +    // A stack tied to the node traversal, to keep track of whether
      +    // we're in a conditional block. If 1 is at the top, assignment to
      +    // a define is allowed. Otherwise, it's not allowed.
      +    private final Deque assignAllowed;
      +
      +    CollectDefines(AbstractCompiler compiler, List listOfDefines) {
      +      this.compiler = compiler;
      +      this.allDefines = Maps.newHashMap();
      +
      +      assignableDefines = Maps.newHashMap();
      +      assignAllowed = new ArrayDeque();
      +      assignAllowed.push(1);
      +
      +      // Create a map of references to defines keyed by node for easy lookup
      +      allRefInfo = Maps.newHashMap();
      +      for (Name name : listOfDefines) {
      +        Ref decl = name.getDeclaration();
      +        if (decl != null) {
      +          allRefInfo.put(decl.node,
      +                         new RefInfo(decl, name));
      +        }
      +        for (Ref ref : name.getRefs()) {
      +          if (ref == decl) {
      +            // Declarations were handled above.
      +            continue;
      +          }
      +
      +          // If there's a TWIN def, only put one of the twins in.
      +          if (ref.getTwin() == null || !ref.getTwin().isSet()) {
      +            allRefInfo.put(ref.node, new RefInfo(ref, name));
      +          }
      +        }
      +      }
      +    }
      +
      +    /**
      +     * Get a map of {@link DefineInfo} structures, keyed by the name of
      +     * the define.
      +     */
      +    Map getAllDefines() {
      +      return allDefines;
      +    }
      +
      +    /**
      +     * Keeps track of whether the traversal is in a conditional branch.
      +     * We traverse all nodes of the parse tree.
      +     */
      +    @Override
      +    public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
      +        Node parent) {
      +      updateAssignAllowedStack(n, true);
      +      return true;
      +    }
      +
      +    @Override
      +    public void visit(NodeTraversal t, Node n, Node parent) {
      +      RefInfo refInfo = allRefInfo.get(n);
      +      if (refInfo != null) {
      +        Ref ref = refInfo.ref;
      +        Name name = refInfo.name;
      +        String fullName = name.getFullName();
      +        switch (ref.type) {
      +          case SET_FROM_GLOBAL:
      +          case SET_FROM_LOCAL:
      +            Node valParent = getValueParent(ref);
      +            Node val = valParent.getLastChild();
      +            if (valParent.isAssign() && name.isSimpleName() &&
      +                name.getDeclaration() == ref) {
      +              // For defines, it's an error if a simple name is assigned
      +              // before it's declared
      +              compiler.report(
      +                  t.makeError(val, INVALID_DEFINE_INIT_ERROR, fullName));
      +            } else if (processDefineAssignment(t, fullName, val, valParent)) {
      +              // remove the assignment so that the variable is still declared,
      +              // but no longer assigned to a value, e.g.,
      +              // DEF_FOO = 5; // becomes "5;"
      +
      +              // We can't remove the ASSIGN/VAR when we're still visiting its
      +              // children, so we'll have to come back later to remove it.
      +              refInfo.name.removeRef(ref);
      +              lvalueToRemoveLater = valParent;
      +            }
      +            break;
      +          default:
      +            if (t.inGlobalScope()) {
      +              // Treat this as a reference to a define in the global scope.
      +              // After this point, the define must not be reassigned,
      +              // or it's an error.
      +              DefineInfo info = assignableDefines.get(fullName);
      +              if (info != null) {
      +                setDefineInfoNotAssignable(info, t);
      +                assignableDefines.remove(fullName);
      +              }
      +            }
      +            break;
      +        }
      +      }
      +
      +      if (!t.inGlobalScope() &&
      +          n.getJSDocInfo() != null && n.getJSDocInfo().isDefine()) {
      +        // warn about @define annotations in local scopes
      +        compiler.report(
      +            t.makeError(n, NON_GLOBAL_DEFINE_INIT_ERROR, ""));
      +      }
      +
      +      if (lvalueToRemoveLater == n) {
      +        lvalueToRemoveLater = null;
      +        if (n.isAssign()) {
      +          Node last = n.getLastChild();
      +          n.removeChild(last);
      +          parent.replaceChild(n, last);
      +        } else {
      +          Preconditions.checkState(n.isName());
      +          n.removeChild(n.getFirstChild());
      +        }
      +        compiler.reportCodeChange();
      +      }
      +
      +      if (n.isCall()) {
      +        if (t.inGlobalScope()) {
      +          // If there's a function call in the global scope,
      +          // we just say it's unsafe and freeze all the defines.
      +          //
      +          // NOTE(nicksantos): We could be a lot smarter here. For example,
      +          // ReplaceOverriddenVars keeps a call graph of all functions and
      +          // which functions/variables that they reference, and tries
      +          // to statically determine which functions are "safe" and which
      +          // are not. But this would be overkill, especially because
      +          // the intended use of defines is with config_files, where
      +          // all the defines are at the top of the bundle.
      +          for (DefineInfo info : assignableDefines.values()) {
      +            setDefineInfoNotAssignable(info, t);
      +          }
      +
      +          assignableDefines.clear();
      +        }
      +      }
      +
      +      updateAssignAllowedStack(n, false);
      +    }
      +
      +    /**
      +     * Determines whether assignment to a define should be allowed
      +     * in the subtree of the given node, and if not, records that fact.
      +     *
      +     * @param n The node whose subtree we're about to enter or exit.
      +     * @param entering True if we're entering the subtree, false otherwise.
      +     */
      +    private void updateAssignAllowedStack(Node n, boolean entering) {
      +      switch (n.getType()) {
      +        case Token.CASE:
      +        case Token.FOR:
      +        case Token.FUNCTION:
      +        case Token.HOOK:
      +        case Token.IF:
      +        case Token.SWITCH:
      +        case Token.WHILE:
      +          if (entering) {
      +            assignAllowed.push(0);
      +          } else {
      +            assignAllowed.remove();
      +          }
      +          break;
      +      }
      +    }
      +
      +    /**
      +     * Determines whether assignment to a define should be allowed
      +     * at the current point of the traversal.
      +     */
      +    private boolean isAssignAllowed() {
      +      return assignAllowed.element() == 1;
      +    }
      +
      +    /**
      +     * Tracks the given define.
      +     *
      +     * @param t The current traversal, for context.
      +     * @param name The full name for this define.
      +     * @param value The value assigned to the define.
      +     * @param valueParent The parent node of value.
      +     * @return Whether we should remove this assignment from the parse tree.
      +     */
      +    private boolean processDefineAssignment(NodeTraversal t,
      +        String name, Node value, Node valueParent) {
      +      if (value == null || !NodeUtil.isValidDefineValue(value,
      +                                                        allDefines.keySet())) {
      +        compiler.report(
      +            t.makeError(value, INVALID_DEFINE_INIT_ERROR, name));
      +      } else if (!isAssignAllowed()) {
      +        compiler.report(
      +            t.makeError(valueParent, NON_GLOBAL_DEFINE_INIT_ERROR, name));
      +      } else {
      +        DefineInfo info = allDefines.get(name);
      +        if (info == null) {
      +          // First declaration of this define.
      +          info = new DefineInfo(value, valueParent);
      +          allDefines.put(name, info);
      +          assignableDefines.put(name, info);
      +        } else if (info.recordAssignment(value)) {
      +          // The define was already initialized, but this is a safe
      +          // re-assignment.
      +          return true;
      +        } else {
      +          // The define was already initialized, and this is an unsafe
      +          // re-assignment.
      +          compiler.report(
      +              t.makeError(valueParent, DEFINE_NOT_ASSIGNABLE_ERROR,
      +                  name, info.getReasonWhyNotAssignable()));
      +        }
      +      }
      +
      +      return false;
      +    }
      +
      +    /**
      +     * Gets the parent node of the value for any assignment to a Name.
      +     * For example, in the assignment
      +     * {@code var x = 3;}
      +     * the parent would be the NAME node.
      +     */
      +    private static Node getValueParent(Ref ref) {
      +      // there are two types of declarations: VARs and ASSIGNs
      +      return ref.node.getParent() != null &&
      +          ref.node.getParent().isVar() ?
      +          ref.node : ref.node.getParent();
      +    }
      +
      +    /**
      +     * Records the fact that because of the current node in the node traversal,
      +     * the define can't ever be assigned again.
      +     *
      +     * @param info Represents the define variable.
      +     * @param t The current traversal.
      +     */
      +    private void setDefineInfoNotAssignable(DefineInfo info, NodeTraversal t) {
      +      info.setNotAssignable(format(REASON_DEFINE_NOT_ASSIGNABLE,
      +                                t.getLineNumber(), t.getSourceName()));
      +    }
      +
      +    /**
      +     * A simple data structure for associating a Ref with the name
      +     * that it references.
      +     */
      +    private static class RefInfo {
      +      final Ref ref;
      +      final Name name;
      +
      +      RefInfo(Ref ref, Name name) {
      +        this.ref = ref;
      +        this.name = name;
      +      }
      +    }
      +  }
      +
      +  /**
      +   * A simple class for storing information about a define.
      +   * Gathers the initial value, the last assigned value, and whether
      +   * the define can be safely assigned a new value.
      +   */
      +  private static final class DefineInfo {
      +    public final Node initialValueParent;
      +    public final Node initialValue;
      +    private Node lastValue;
      +    private boolean isAssignable;
      +    private String reasonNotAssignable;
      +
      +    /**
      +     * Initializes a define.
      +     */
      +    public DefineInfo(Node initialValue, Node initialValueParent) {
      +      this.initialValueParent = initialValueParent;
      +      this.initialValue = initialValue;
      +      lastValue = initialValue;
      +      isAssignable = true;
      +    }
      +
      +    /**
      +     * Records the fact that this define can't be assigned a value anymore.
      +     *
      +     * @param reason A message describing the reason why it can't be assigned.
      +     */
      +    public void setNotAssignable(String reason) {
      +      isAssignable = false;
      +      reasonNotAssignable = reason;
      +    }
      +
      +    /**
      +     * Gets the reason why a define is not assignable.
      +     */
      +    public String getReasonWhyNotAssignable() {
      +      return reasonNotAssignable;
      +    }
      +
      +    /**
      +     * Records an assigned value.
      +     *
      +     * @return False if there was an error.
      +     */
      +    public boolean recordAssignment(Node value) {
      +      lastValue = value;
      +      return isAssignable;
      +    }
      +
      +    /**
      +     * Gets the last assigned value.
      +     */
      +    public Node getLastValue() {
      +      return lastValue;
      +    }
      +  }
      +}
      diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ProcessTweaks.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ProcessTweaks.java
      new file mode 100644
      index 0000000..e20b902
      --- /dev/null
      +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ProcessTweaks.java
      @@ -0,0 +1,558 @@
      +/*
      + * Copyright 2011 The Closure Compiler Authors.
      + *
      + * Licensed under the Apache License, Version 2.0 (the "License");
      + * you may not use this file except in compliance with the License.
      + * You may obtain a copy of the License at
      + *
      + *     http://www.apache.org/licenses/LICENSE-2.0
      + *
      + * Unless required by applicable law or agreed to in writing, software
      + * distributed under the License is distributed on an "AS IS" BASIS,
      + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      + * See the License for the specific language governing permissions and
      + * limitations under the License.
      + */
      +
      +package com.google.javascript.jscomp;
      +
      +import com.google.common.base.CharMatcher;
      +import com.google.common.base.Preconditions;
      +import com.google.common.collect.Lists;
      +import com.google.common.collect.Maps;
      +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
      +import com.google.javascript.rhino.IR;
      +import com.google.javascript.rhino.Node;
      +import com.google.javascript.rhino.Token;
      +
      +import java.util.List;
      +import java.util.Map;
      +import java.util.Map.Entry;
      +import java.util.SortedMap;
      +
      +/**
      + * Process goog.tweak primitives. Checks that:
      + * 
        + *
      • parameters to goog.tweak.register* are literals of the correct type. + *
      • the parameter to goog.tweak.get* is a string literal. + *
      • parameters to goog.tweak.overrideDefaultValue are literals of the correct + * type. + *
      • tweak IDs passed to goog.tweak.get* and goog.tweak.overrideDefaultValue + * correspond to registered tweaks. + *
      • all calls to goog.tweak.register* and goog.tweak.overrideDefaultValue are + * within the top-level context. + *
      • each tweak is registered only once. + *
      • calls to goog.tweak.overrideDefaultValue occur before the call to the + * corresponding goog.tweak.register* function. + *
      + * @author agrieve@google.com (Andrew Grieve) + */ +class ProcessTweaks implements CompilerPass { + + private final AbstractCompiler compiler; + private final boolean stripTweaks; + private final SortedMap compilerDefaultValueOverrides; + + private static final CharMatcher ID_MATCHER = CharMatcher.inRange('a', 'z'). + or(CharMatcher.inRange('A', 'Z')).or(CharMatcher.anyOf("0123456789_.")); + + // Warnings and Errors. + static final DiagnosticType UNKNOWN_TWEAK_WARNING = + DiagnosticType.warning( + "JSC_UNKNOWN_TWEAK_WARNING", + "no tweak registered with ID {0}"); + + static final DiagnosticType TWEAK_MULTIPLY_REGISTERED_ERROR = + DiagnosticType.error( + "JSC_TWEAK_MULTIPLY_REGISTERED_ERROR", + "Tweak {0} has already been registered."); + + static final DiagnosticType NON_LITERAL_TWEAK_ID_ERROR = + DiagnosticType.error( + "JSC_NON_LITERAL_TWEAK_ID_ERROR", + "tweak ID must be a string literal"); + + static final DiagnosticType INVALID_TWEAK_DEFAULT_VALUE_WARNING = + DiagnosticType.warning( + "JSC_INVALID_TWEAK_DEFAULT_VALUE_WARNING", + "tweak {0} registered with {1} must have a default value that is a " + + "literal of type {2}"); + + static final DiagnosticType NON_GLOBAL_TWEAK_INIT_ERROR = + DiagnosticType.error( + "JSC_NON_GLOBAL_TWEAK_INIT_ERROR", + "tweak declaration {0} must occur in the global scope"); + + static final DiagnosticType TWEAK_OVERRIDE_AFTER_REGISTERED_ERROR = + DiagnosticType.error( + "JSC_TWEAK_OVERRIDE_AFTER_REGISTERED_ERROR", + "Cannot override the default value of tweak {0} after it has been " + + "registered"); + + static final DiagnosticType TWEAK_WRONG_GETTER_TYPE_WARNING = + DiagnosticType.warning( + "JSC_TWEAK_WRONG_GETTER_TYPE_WARNING", + "tweak getter function {0} used for tweak registered using {1}"); + + static final DiagnosticType INVALID_TWEAK_ID_ERROR = + DiagnosticType.error( + "JSC_INVALID_TWEAK_ID_ERROR", + "tweak ID contains illegal characters. Only letters, numbers, _ " + + "and . are allowed"); + + /** + * An enum of goog.tweak functions. + */ + private static enum TweakFunction { + REGISTER_BOOLEAN("goog.tweak.registerBoolean", "boolean", Token.TRUE, + Token.FALSE), + REGISTER_NUMBER("goog.tweak.registerNumber", "number", Token.NUMBER), + REGISTER_STRING("goog.tweak.registerString", "string", Token.STRING), + OVERRIDE_DEFAULT_VALUE("goog.tweak.overrideDefaultValue"), + GET_COMPILER_OVERRIDES("goog.tweak.getCompilerOverrides_"), + GET_BOOLEAN("goog.tweak.getBoolean", REGISTER_BOOLEAN), + GET_NUMBER("goog.tweak.getNumber", REGISTER_NUMBER), + GET_STRING("goog.tweak.getString", REGISTER_STRING); + + final String name; + final String expectedTypeName; + final int validNodeTypeA; + final int validNodeTypeB; + final TweakFunction registerFunction; + + TweakFunction(String name) { + this(name, null, Token.ERROR, Token.ERROR, null); + } + + TweakFunction(String name, String expectedTypeName, + int validNodeTypeA) { + this(name, expectedTypeName, validNodeTypeA, Token.ERROR, null); + } + + TweakFunction(String name, String expectedTypeName, + int validNodeTypeA, int validNodeTypeB) { + this(name, expectedTypeName, validNodeTypeA, validNodeTypeB, null); + } + + TweakFunction(String name, TweakFunction registerFunction) { + this(name, null, Token.ERROR, Token.ERROR, registerFunction); + } + + TweakFunction(String name, String expectedTypeName, + int validNodeTypeA, int validNodeTypeB, + TweakFunction registerFunction) { + this.name = name; + this.expectedTypeName = expectedTypeName; + this.validNodeTypeA = validNodeTypeA; + this.validNodeTypeB = validNodeTypeB; + this.registerFunction = registerFunction; + } + + boolean isValidNodeType(int type) { + return type == validNodeTypeA || type == validNodeTypeB; + } + + boolean isCorrectRegisterFunction(TweakFunction registerFunction) { + Preconditions.checkNotNull(registerFunction); + return this.registerFunction == registerFunction; + } + + boolean isGetterFunction() { + return registerFunction != null; + } + + String getName() { + return name; + } + + String getExpectedTypeName() { + return expectedTypeName; + } + + Node createDefaultValueNode() { + switch (this) { + case REGISTER_BOOLEAN: + return IR.falseNode(); + case REGISTER_NUMBER: + return IR.number(0); + case REGISTER_STRING: + return IR.string(""); + default: + throw new IllegalStateException(); + } + } + } + + // A map of function name -> TweakFunction. + private static final Map TWEAK_FUNCTIONS_MAP; + static { + TWEAK_FUNCTIONS_MAP = Maps.newHashMap(); + for (TweakFunction func : TweakFunction.values()) { + TWEAK_FUNCTIONS_MAP.put(func.getName(), func); + } + } + + ProcessTweaks(AbstractCompiler compiler, boolean stripTweaks, + Map compilerDefaultValueOverrides) { + this.compiler = compiler; + this.stripTweaks = stripTweaks; + // Having the map sorted is required for the unit tests to be deterministic. + this.compilerDefaultValueOverrides = Maps.newTreeMap(); + this.compilerDefaultValueOverrides.putAll(compilerDefaultValueOverrides); + } + + @Override + public void process(Node externs, Node root) { + CollectTweaksResult result = collectTweaks(root); + applyCompilerDefaultValueOverrides(result.tweakInfos); + + boolean changed = false; + + if (stripTweaks) { + changed = stripAllCalls(result.tweakInfos); + } else if (!compilerDefaultValueOverrides.isEmpty()) { + changed = replaceGetCompilerOverridesCalls(result.getOverridesCalls); + } + if (changed) { + compiler.reportCodeChange(); + } + } + + /** + * Passes the compiler default value overrides to the JS by replacing calls + * to goog.tweak.getCompilerOverrids_ with a map of tweak ID->default value; + */ + private boolean replaceGetCompilerOverridesCalls( + List calls) { + for (TweakFunctionCall call : calls) { + Node callNode = call.callNode; + Node objNode = createCompilerDefaultValueOverridesVarNode(callNode); + callNode.getParent().replaceChild(callNode, objNode); + } + return !calls.isEmpty(); + } + + /** + * Removes all CALL nodes in the given TweakInfos, replacing calls to getter + * functions with the tweak's default value. + */ + private boolean stripAllCalls(Map tweakInfos) { + for (TweakInfo tweakInfo : tweakInfos.values()) { + boolean isRegistered = tweakInfo.isRegistered(); + for (TweakFunctionCall functionCall : tweakInfo.functionCalls) { + Node callNode = functionCall.callNode; + Node parent = callNode.getParent(); + if (functionCall.tweakFunc.isGetterFunction()) { + Node newValue; + if (isRegistered) { + newValue = tweakInfo.getDefaultValueNode().cloneNode(); + } else { + // When we find a getter of an unregistered tweak, there has + // already been a warning about it, so now just use a default + // value when stripping. + TweakFunction registerFunction = + functionCall.tweakFunc.registerFunction; + newValue = registerFunction.createDefaultValueNode(); + } + parent.replaceChild(callNode, newValue); + } else { + Node voidZeroNode = IR.voidNode(IR.number(0).srcref(callNode)) + .srcref(callNode); + parent.replaceChild(callNode, voidZeroNode); + } + } + } + return !tweakInfos.isEmpty(); + } + + /** + * Creates a JS object that holds a map of tweakId -> default value override. + */ + private Node createCompilerDefaultValueOverridesVarNode( + Node sourceInformationNode) { + Node objNode = IR.objectlit().srcref(sourceInformationNode); + for (Entry entry : compilerDefaultValueOverrides.entrySet()) { + Node objKeyNode = IR.stringKey(entry.getKey()) + .copyInformationFrom(sourceInformationNode); + Node objValueNode = entry.getValue().cloneNode() + .copyInformationFrom(sourceInformationNode); + objKeyNode.addChildToBack(objValueNode); + objNode.addChildToBack(objKeyNode); + } + return objNode; + } + + /** Sets the default values of tweaks based on compiler options. */ + private void applyCompilerDefaultValueOverrides( + Map tweakInfos) { + for (Entry entry : compilerDefaultValueOverrides.entrySet()) { + String tweakId = entry.getKey(); + TweakInfo tweakInfo = tweakInfos.get(tweakId); + if (tweakInfo == null) { + compiler.report(JSError.make(UNKNOWN_TWEAK_WARNING, tweakId)); + } else { + TweakFunction registerFunc = tweakInfo.registerCall.tweakFunc; + Node value = entry.getValue(); + if (!registerFunc.isValidNodeType(value.getType())) { + compiler.report(JSError.make(INVALID_TWEAK_DEFAULT_VALUE_WARNING, + tweakId, registerFunc.getName(), + registerFunc.getExpectedTypeName())); + } else { + tweakInfo.defaultValueNode = value; + } + } + } + } + + /** + * Finds all calls to goog.tweak functions and emits warnings/errors if any + * of the calls have issues. + * @return A map of {@link TweakInfo} structures, keyed by tweak ID. + */ + private CollectTweaksResult collectTweaks(Node root) { + CollectTweaks pass = new CollectTweaks(); + NodeTraversal.traverse(compiler, root, pass); + + Map tweakInfos = pass.allTweaks; + for (TweakInfo tweakInfo: tweakInfos.values()) { + tweakInfo.emitAllWarnings(); + } + return new CollectTweaksResult(tweakInfos, pass.getOverridesCalls); + } + + private final static class CollectTweaksResult { + final Map tweakInfos; + final List getOverridesCalls; + + CollectTweaksResult(Map tweakInfos, + List getOverridesCalls) { + this.tweakInfos = tweakInfos; + this.getOverridesCalls = getOverridesCalls; + } + } + + /** + * Processes all calls to goog.tweak functions. + */ + private final class CollectTweaks extends AbstractPostOrderCallback { + final Map allTweaks = Maps.newHashMap(); + final List getOverridesCalls = Lists.newArrayList(); + + @SuppressWarnings("incomplete-switch") + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isCall()) { + return; + } + + String callName = n.getFirstChild().getQualifiedName(); + TweakFunction tweakFunc = TWEAK_FUNCTIONS_MAP.get(callName); + if (tweakFunc == null) { + return; + } + + if (tweakFunc == TweakFunction.GET_COMPILER_OVERRIDES) { + getOverridesCalls.add( + new TweakFunctionCall(t.getSourceName(), tweakFunc, n)); + return; + } + + // Ensure the first parameter (the tweak ID) is a string literal. + Node tweakIdNode = n.getFirstChild().getNext(); + if (!tweakIdNode.isString()) { + compiler.report(t.makeError(tweakIdNode, NON_LITERAL_TWEAK_ID_ERROR)); + return; + } + String tweakId = tweakIdNode.getString(); + + // Make sure there is a TweakInfo structure for it. + TweakInfo tweakInfo = allTweaks.get(tweakId); + if (tweakInfo == null) { + tweakInfo = new TweakInfo(tweakId); + allTweaks.put(tweakId, tweakInfo); + } + + switch (tweakFunc) { + case REGISTER_BOOLEAN: + case REGISTER_NUMBER: + case REGISTER_STRING: + // Ensure the ID contains only valid characters. + if (!ID_MATCHER.matchesAllOf(tweakId)) { + compiler.report(t.makeError(tweakIdNode, INVALID_TWEAK_ID_ERROR)); + } + + // Ensure tweaks are registered in the global scope. + if (!t.inGlobalScope()) { + compiler.report( + t.makeError(n, NON_GLOBAL_TWEAK_INIT_ERROR, tweakId)); + break; + } + + // Ensure tweaks are registered only once. + if (tweakInfo.isRegistered()) { + compiler.report( + t.makeError(n, TWEAK_MULTIPLY_REGISTERED_ERROR, tweakId)); + break; + } + + Node tweakDefaultValueNode = tweakIdNode.getNext().getNext(); + tweakInfo.addRegisterCall(t.getSourceName(), tweakFunc, n, + tweakDefaultValueNode); + break; + case OVERRIDE_DEFAULT_VALUE: + // Ensure tweaks overrides occur in the global scope. + if (!t.inGlobalScope()) { + compiler.report( + t.makeError(n, NON_GLOBAL_TWEAK_INIT_ERROR, tweakId)); + break; + } + // Ensure tweak overrides occur before the tweak is registered. + if (tweakInfo.isRegistered()) { + compiler.report( + t.makeError(n, TWEAK_OVERRIDE_AFTER_REGISTERED_ERROR, tweakId)); + break; + } + + tweakDefaultValueNode = tweakIdNode.getNext(); + tweakInfo.addOverrideDefaultValueCall(t.getSourceName(), tweakFunc, n, + tweakDefaultValueNode); + break; + case GET_BOOLEAN: + case GET_NUMBER: + case GET_STRING: + tweakInfo.addGetterCall(t.getSourceName(), tweakFunc, n); + } + } + } + + /** + * Holds information about a call to a goog.tweak function. + */ + private static final class TweakFunctionCall { + final String sourceName; + final TweakFunction tweakFunc; + final Node callNode; + final Node valueNode; + + TweakFunctionCall(String sourceName, TweakFunction tweakFunc, + Node callNode) { + this(sourceName, tweakFunc, callNode, null); + } + + TweakFunctionCall(String sourceName, TweakFunction tweakFunc, Node callNode, + Node valueNode) { + this.sourceName = sourceName; + this.callNode = callNode; + this.tweakFunc = tweakFunc; + this.valueNode = valueNode; + } + + Node getIdNode() { + return callNode.getFirstChild().getNext(); + } + } + + /** + * Stores information about a single tweak. + */ + private final class TweakInfo { + final String tweakId; + final List functionCalls; + TweakFunctionCall registerCall; + Node defaultValueNode; + + TweakInfo(String tweakId) { + this.tweakId = tweakId; + functionCalls = Lists.newArrayList(); + } + + /** + * If this tweak is registered, then looks for type warnings in default + * value parameters and getter functions. If it is not registered, emits an + * error for each function call. + */ + void emitAllWarnings() { + if (isRegistered()) { + emitAllTypeWarnings(); + } else { + emitUnknownTweakErrors(); + } + } + + /** + * Emits a warning for each default value parameter that has the wrong type + * and for each getter function that was used for the wrong type of tweak. + */ + void emitAllTypeWarnings() { + for (TweakFunctionCall call : functionCalls) { + Node valueNode = call.valueNode; + TweakFunction tweakFunc = call.tweakFunc; + TweakFunction registerFunc = registerCall.tweakFunc; + if (valueNode != null) { + // For register* and overrideDefaultValue calls, ensure the default + // value is a literal of the correct type. + if (!registerFunc.isValidNodeType(valueNode.getType())) { + compiler.report(JSError.make(call.sourceName, + valueNode, INVALID_TWEAK_DEFAULT_VALUE_WARNING, + tweakId, registerFunc.getName(), + registerFunc.getExpectedTypeName())); + } + } else if (tweakFunc.isGetterFunction()) { + // For getter calls, ensure the correct getter was used. + if (!tweakFunc.isCorrectRegisterFunction(registerFunc)) { + compiler.report(JSError.make(call.sourceName, + call.callNode, TWEAK_WRONG_GETTER_TYPE_WARNING, + tweakFunc.getName(), registerFunc.getName())); + } + } + } + } + + /** + * Emits an error for each function call that was found. + */ + void emitUnknownTweakErrors() { + for (TweakFunctionCall call : functionCalls) { + compiler.report(JSError.make(call.sourceName, + call.getIdNode(), UNKNOWN_TWEAK_WARNING, tweakId)); + } + } + + void addRegisterCall(String sourceName, TweakFunction tweakFunc, + Node callNode, Node defaultValueNode) { + registerCall = new TweakFunctionCall(sourceName, tweakFunc, callNode, + defaultValueNode); + functionCalls.add(registerCall); + } + + void addOverrideDefaultValueCall(String sourceName, + TweakFunction tweakFunc, Node callNode, Node defaultValueNode) { + functionCalls.add(new TweakFunctionCall(sourceName, tweakFunc, callNode, + defaultValueNode)); + this.defaultValueNode = defaultValueNode; + } + + void addGetterCall(String sourceName, TweakFunction tweakFunc, + Node callNode) { + functionCalls.add(new TweakFunctionCall(sourceName, tweakFunc, callNode)); + } + + boolean isRegistered() { + return registerCall != null; + } + + Node getDefaultValueNode() { + Preconditions.checkState(isRegistered()); + // Use calls to goog.tweak.overrideDefaultValue() first. + if (defaultValueNode != null) { + return defaultValueNode; + } + // Use the value passed to the register function next. + if (registerCall.valueNode != null) { + return registerCall.valueNode; + } + // Otherwise, use the default value for the tweak's type. + return registerCall.tweakFunc.createDefaultValueNode(); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PropertyRenamingPolicy.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PropertyRenamingPolicy.java new file mode 100644 index 0000000..6494b03 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PropertyRenamingPolicy.java @@ -0,0 +1,50 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Policies to determine how properties should be renamed. + */ +public enum PropertyRenamingPolicy { + /** + * Rename no properties. + */ + OFF, + + /** + * Rename properties heuristically. + * @see RenamePrototypes + */ + HEURISTIC, + + /** + * Rename properties more heuristically. + * @see RenamePrototypes + */ + AGGRESSIVE_HEURISTIC, + + /** + * Rename all properties that aren't explicitly quoted and aren't + * externally defined (i.e. declared in an externs file). This policy + * achieves better compaction than the others. + * @see RenameProperties + */ + ALL_UNQUOTED, + + // for transitioning off old flags. not for public consumption. + UNSPECIFIED +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PureFunctionIdentifier.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PureFunctionIdentifier.java new file mode 100644 index 0000000..171eb51 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/PureFunctionIdentifier.java @@ -0,0 +1,1123 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.common.io.Files; +import com.google.javascript.jscomp.DefinitionsRemover.Definition; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.graph.DiGraph; +import com.google.javascript.jscomp.graph.FixedPointGraphTraversal; +import com.google.javascript.jscomp.graph.FixedPointGraphTraversal.EdgeCallback; +import com.google.javascript.jscomp.graph.LinkedDirectedGraph; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Compiler pass that computes function purity. A function is pure if + * it has no outside visible side effects, and the result of the + * computation does not depend on external factors that are beyond the + * control of the application; repeated calls to the function should + * return the same value as long as global state hasn't changed. + * + * Date.now is an example of a function that has no side effects but + * is not pure. + * + * + * We will prevail, in peace and freedom from fear, and in true + * health, through the purity and essence of our natural... fluids. + * - General Turgidson + */ +class PureFunctionIdentifier implements CompilerPass { + static final DiagnosticType INVALID_NO_SIDE_EFFECT_ANNOTATION = + DiagnosticType.error( + "JSC_INVALID_NO_SIDE_EFFECT_ANNOTATION", + "@nosideeffects may only appear in externs files."); + + static final DiagnosticType INVALID_MODIFIES_ANNOTATION = + DiagnosticType.error( + "JSC_INVALID_MODIFIES_ANNOTATION", + "@modifies may only appear in externs files."); + + private final AbstractCompiler compiler; + private final DefinitionProvider definitionProvider; + + // Function node -> function side effects map + private final Map functionSideEffectMap; + + // List of all function call sites; used to iterate in markPureFunctionCalls. + private final List allFunctionCalls; + + // Externs and ast tree root, for use in getDebugReport. These two + // fields are null until process is called. + private Node externs; + private Node root; + + public PureFunctionIdentifier(AbstractCompiler compiler, + DefinitionProvider definitionProvider) { + this.compiler = compiler; + this.definitionProvider = definitionProvider; + this.functionSideEffectMap = Maps.newHashMap(); + this.allFunctionCalls = Lists.newArrayList(); + this.externs = null; + this.root = null; + } + + @Override + public void process(Node externsAst, Node srcAst) { + if (externs != null || root != null) { + throw new IllegalStateException( + "It is illegal to call PureFunctionIdentifier.process " + + "twice the same instance. Please use a new " + + "PureFunctionIdentifier instance each time."); + } + + externs = externsAst; + root = srcAst; + + NodeTraversal.traverse(compiler, externs, new FunctionAnalyzer(true)); + NodeTraversal.traverse(compiler, root, new FunctionAnalyzer(false)); + + propagateSideEffects(); + + markPureFunctionCalls(); + } + + /** + * Compute debug report that includes: + * - List of all pure functions. + * - Reasons we think the remaining functions have side effects. + */ + String getDebugReport() { + Preconditions.checkNotNull(externs); + Preconditions.checkNotNull(root); + + StringBuilder sb = new StringBuilder(); + + FunctionNames functionNames = new FunctionNames(compiler); + functionNames.process(null, externs); + functionNames.process(null, root); + + sb.append("Pure functions:\n"); + for (Map.Entry entry : + functionSideEffectMap.entrySet()) { + Node function = entry.getKey(); + FunctionInformation functionInfo = entry.getValue(); + + boolean isPure = + functionInfo.mayBePure() && !functionInfo.mayHaveSideEffects(); + if (isPure) { + sb.append(" " + functionNames.getFunctionName(function) + "\n"); + } + } + sb.append("\n"); + + for (Map.Entry entry : + functionSideEffectMap.entrySet()) { + Node function = entry.getKey(); + FunctionInformation functionInfo = entry.getValue(); + + Set depFunctionNames = Sets.newHashSet(); + for (Node callSite : functionInfo.getCallsInFunctionBody()) { + Collection defs = + getCallableDefinitions(definitionProvider, + callSite.getFirstChild()); + + if (defs == null) { + depFunctionNames.add(""); + continue; + } + + for (Definition def : defs) { + depFunctionNames.add( + functionNames.getFunctionName(def.getRValue())); + } + } + + sb.append(functionNames.getFunctionName(function) + " " + + functionInfo.toString() + + " Calls: " + depFunctionNames + "\n"); + } + + return sb.toString(); + } + + /** + * Query the DefinitionProvider for the list of definitions that + * correspond to a given qualified name subtree. Return null if + * DefinitionProvider does not contain an entry for a given name, + * one or more of the values returned by getDeclarations is not + * callable, or the "name" node is not a GETPROP or NAME. + * + * @param definitionProvider The name reference graph + * @param name Query node + * @return non-empty definition list or null + */ + private static Collection getCallableDefinitions( + DefinitionProvider definitionProvider, Node name) { + if (name.isGetProp() || name.isName()) { + List result = Lists.newArrayList(); + + Collection decls = + definitionProvider.getDefinitionsReferencedAt(name); + if (decls == null) { + return null; + } + + for (Definition current : decls) { + Node rValue = current.getRValue(); + if ((rValue != null) && rValue.isFunction()) { + result.add(current); + } else { + return null; + } + } + + return result; + } else if (name.isOr() || name.isHook()) { + Node firstVal; + if (name.isHook()) { + firstVal = name.getFirstChild().getNext(); + } else { + firstVal = name.getFirstChild(); + } + + Collection defs1 = getCallableDefinitions(definitionProvider, + firstVal); + Collection defs2 = getCallableDefinitions(definitionProvider, + firstVal.getNext()); + if (defs1 != null && defs2 != null) { + defs1.addAll(defs2); + return defs1; + } else { + return null; + } + } else if (NodeUtil.isFunctionExpression(name)) { + // The anonymous function reference is also the definition. + // TODO(user) Change SimpleDefinitionFinder so it is possible to query for + // function expressions by function node. + + // isExtern is false in the call to the constructor for the + // FunctionExpressionDefinition below because we know that + // getCallableDefinitions() will only be called on the first + // child of a call and thus the function expression + // definition will never be an extern. + return Lists.newArrayList( + (Definition) + new DefinitionsRemover.FunctionExpressionDefinition(name, false)); + } else { + return null; + } + } + + /** + * Propagate side effect information by building a graph based on + * call site information stored in FunctionInformation and the + * DefinitionProvider and then running GraphReachability to + * determine the set of functions that have side effects. + */ + private void propagateSideEffects() { + // Nodes are function declarations; Edges are function call sites. + DiGraph sideEffectGraph = + LinkedDirectedGraph.createWithoutAnnotations(); + + // create graph nodes + for (FunctionInformation functionInfo : functionSideEffectMap.values()) { + sideEffectGraph.createNode(functionInfo); + } + + // add connections to called functions and side effect root. + for (FunctionInformation functionInfo : functionSideEffectMap.values()) { + if (!functionInfo.mayHaveSideEffects()) { + continue; + } + + for (Node callSite : functionInfo.getCallsInFunctionBody()) { + Node callee = callSite.getFirstChild(); + Collection defs = + getCallableDefinitions(definitionProvider, callee); + if (defs == null) { + // Definition set is not complete or eligible. Possible + // causes include: + // * "callee" is not of type NAME or GETPROP. + // * One or more definitions are not functions. + // * One or more definitions are complex. + // (e.i. return value of a call that returns a function). + functionInfo.setTaintsUnknown(); + break; + } + + for (Definition def : defs) { + Node defValue = def.getRValue(); + FunctionInformation dep = functionSideEffectMap.get(defValue); + Preconditions.checkNotNull(dep); + sideEffectGraph.connect(dep, callSite, functionInfo); + } + } + } + + // Propagate side effect information to a fixed point. + FixedPointGraphTraversal.newTraversal(new SideEffectPropagationCallback()) + .computeFixedPoint(sideEffectGraph); + + // Mark remaining functions "pure". + for (FunctionInformation functionInfo : functionSideEffectMap.values()) { + if (functionInfo.mayBePure()) { + functionInfo.setIsPure(); + } + } + } + + /** + * Set no side effect property at pure-function call sites. + */ + private void markPureFunctionCalls() { + for (Node callNode : allFunctionCalls) { + Node name = callNode.getFirstChild(); + Collection defs = + getCallableDefinitions(definitionProvider, name); + // Default to side effects, non-local results + Node.SideEffectFlags flags = new Node.SideEffectFlags(); + if (defs == null) { + flags.setMutatesGlobalState(); + flags.setThrows(); + flags.setReturnsTainted(); + } else { + flags.clearAllFlags(); + for (Definition def : defs) { + FunctionInformation functionInfo = + functionSideEffectMap.get(def.getRValue()); + Preconditions.checkNotNull(functionInfo); + // TODO(johnlenz): set the arguments separately from the + // global state flag. + if (functionInfo.mutatesGlobalState()) { + flags.setMutatesGlobalState(); + } + + if (functionInfo.functionThrows) { + flags.setThrows(); + } + + if (!callNode.isNew()) { + if (functionInfo.taintsThis) { + flags.setMutatesThis(); + } + } + + if (functionInfo.taintsReturn) { + flags.setReturnsTainted(); + } + + if (flags.areAllFlagsSet()) { + break; + } + } + } + + // Handle special cases (Math, RegExp) + if (callNode.isCall()) { + Preconditions.checkState(compiler != null); + if (!NodeUtil.functionCallHasSideEffects(callNode, compiler)) { + flags.clearSideEffectFlags(); + } + } else if (callNode.isNew()) { + // Handle known cases now (Object, Date, RegExp, etc) + if (!NodeUtil.constructorCallHasSideEffects(callNode)) { + flags.clearSideEffectFlags(); + } + } + + callNode.setSideEffectFlags(flags.valueOf()); + } + } + + /** + * Gather list of functions, functions with @nosideeffects + * annotations, call sites, and functions that may mutate variables + * not defined in the local scope. + */ + private class FunctionAnalyzer implements ScopedCallback { + private final boolean inExterns; + + FunctionAnalyzer(boolean inExterns) { + this.inExterns = inExterns; + } + + @Override + public boolean shouldTraverse(NodeTraversal traversal, + Node node, + Node parent) { + + // Functions need to be processed as part of pre-traversal so an + // entry for the enclosing function exists in the + // FunctionInformation map when processing assignments and calls + // inside visit. + if (node.isFunction()) { + Node gramp = parent.getParent(); + visitFunction(traversal, node, parent, gramp); + } + + return true; + } + + @Override + public void visit(NodeTraversal traversal, Node node, Node parent) { + + if (inExterns) { + return; + } + + if (!NodeUtil.nodeTypeMayHaveSideEffects(node) + && !node.isReturn()) { + return; + } + + if (node.isCall() || node.isNew()) { + allFunctionCalls.add(node); + } + + Node enclosingFunction = traversal.getEnclosingFunction(); + if (enclosingFunction != null) { + FunctionInformation sideEffectInfo = + functionSideEffectMap.get(enclosingFunction); + Preconditions.checkNotNull(sideEffectInfo); + + if (NodeUtil.isAssignmentOp(node)) { + visitAssignmentOrUnaryOperator( + sideEffectInfo, traversal.getScope(), + node, node.getFirstChild(), node.getLastChild()); + } else { + switch(node.getType()) { + case Token.CALL: + case Token.NEW: + visitCall(sideEffectInfo, node); + break; + case Token.DELPROP: + case Token.DEC: + case Token.INC: + visitAssignmentOrUnaryOperator( + sideEffectInfo, traversal.getScope(), + node, node.getFirstChild(), null); + break; + case Token.NAME: + // Variable definition are not side effects. + // Just check that the name appears in the context of a + // variable declaration. + Preconditions.checkArgument( + NodeUtil.isVarDeclaration(node)); + Node value = node.getFirstChild(); + // Assignment to local, if the value isn't a safe local value, + // new object creation or literal or known primitive result + // value, add it to the local blacklist. + if (value != null && !NodeUtil.evaluatesToLocalValue(value)) { + Scope scope = traversal.getScope(); + Var var = scope.getVar(node.getString()); + sideEffectInfo.blacklistLocal(var); + } + break; + case Token.THROW: + visitThrow(sideEffectInfo); + break; + case Token.RETURN: + if (node.hasChildren() + && !NodeUtil.evaluatesToLocalValue(node.getFirstChild())) { + sideEffectInfo.setTaintsReturn(); + } + break; + default: + throw new IllegalArgumentException( + "Unhandled side effect node type " + + Token.name(node.getType())); + } + } + } + } + + @Override + public void enterScope(NodeTraversal t) { + // Nothing to do. + } + + @Override + public void exitScope(NodeTraversal t) { + if (t.inGlobalScope()) { + return; + } + + // Handle deferred local variable modifications: + // + FunctionInformation sideEffectInfo = + functionSideEffectMap.get(t.getScopeRoot()); + if (sideEffectInfo.mutatesGlobalState()){ + sideEffectInfo.resetLocalVars(); + return; + } + + for (Iterator i = t.getScope().getVars(); i.hasNext();) { + Var v = i.next(); + boolean localVar = false; + // Parameters and catch values come can from other scopes. + if (v.getParentNode().isVar()) { + // TODO(johnlenz): create a useful parameter list + sideEffectInfo.knownLocals.add(v.getName()); + localVar = true; + } + + // Take care of locals that might have been tainted. + if (!localVar || sideEffectInfo.blacklisted.contains(v)) { + if (sideEffectInfo.taintedLocals.contains(v)) { + // If the function has global side-effects + // don't bother with the local side-effects. + sideEffectInfo.setTaintsUnknown(); + sideEffectInfo.resetLocalVars(); + break; + } + } + } + + sideEffectInfo.taintedLocals = null; + sideEffectInfo.blacklisted = null; + } + + + /** + * Record information about the side effects caused by an + * assignment or mutating unary operator. + * + * If the operation modifies this or taints global state, mark the + * enclosing function as having those side effects. + * @param op operation being performed. + * @param lhs The store location (name or get) being operated on. + * @param rhs The right have value, if any. + */ + private void visitAssignmentOrUnaryOperator( + FunctionInformation sideEffectInfo, + Scope scope, Node op, Node lhs, Node rhs) { + if (lhs.isName()) { + Var var = scope.getVar(lhs.getString()); + if (var == null || var.scope != scope) { + sideEffectInfo.setTaintsGlobalState(); + } else { + // Assignment to local, if the value isn't a safe local value, + // a literal or new object creation, add it to the local blacklist. + // parameter values depend on the caller. + + // Note: other ops result in the name or prop being assigned a local + // value (x++ results in a number, for instance) + Preconditions.checkState( + NodeUtil.isAssignmentOp(op) + || isIncDec(op) || op.isDelProp()); + if (rhs != null + && op.isAssign() + && !NodeUtil.evaluatesToLocalValue(rhs)) { + sideEffectInfo.blacklistLocal(var); + } + } + } else if (NodeUtil.isGet(lhs)) { + if (lhs.getFirstChild().isThis()) { + sideEffectInfo.setTaintsThis(); + } else { + Var var = null; + Node objectNode = lhs.getFirstChild(); + if (objectNode.isName()) { + var = scope.getVar(objectNode.getString()); + } + if (var == null || var.scope != scope) { + sideEffectInfo.setTaintsUnknown(); + } else { + // Maybe a local object modification. We won't know for sure until + // we exit the scope and can validate the value of the local. + // + sideEffectInfo.addTaintedLocalObject(var); + } + } + } else { + // TODO(johnlenz): track down what is inserting NULL on the LHS + // of an assign. + + // The only valid LHS expressions are NAME, GETELEM, or GETPROP. + // throw new IllegalStateException( + // "Unexpected LHS expression:" + lhs.toStringTree() + // + ", parent: " + op.toStringTree() ); + sideEffectInfo.setTaintsUnknown(); + } + } + + /** + * Record information about a call site. + */ + private void visitCall(FunctionInformation sideEffectInfo, Node node) { + // Handle special cases (Math, RegExp) + if (node.isCall() + && !NodeUtil.functionCallHasSideEffects(node, compiler)) { + return; + } + + // Handle known cases now (Object, Date, RegExp, etc) + if (node.isNew() + && !NodeUtil.constructorCallHasSideEffects(node)) { + return; + } + + sideEffectInfo.appendCall(node); + } + + /** + * Record function and check for @nosideeffects annotations. + */ + private void visitFunction(NodeTraversal traversal, + Node node, + Node parent, + Node gramp) { + Preconditions.checkArgument(!functionSideEffectMap.containsKey(node)); + + FunctionInformation sideEffectInfo = new FunctionInformation(inExterns); + functionSideEffectMap.put(node, sideEffectInfo); + + if (inExterns) { + JSType jstype = node.getJSType(); + boolean knownLocalResult = false; + FunctionType functionType = JSType.toMaybeFunctionType(jstype); + if (functionType != null) { + JSType jstypeReturn = functionType.getReturnType(); + if (isLocalValueType(jstypeReturn, true)) { + knownLocalResult = true; + } + } + if (!knownLocalResult) { + sideEffectInfo.setTaintsReturn(); + } + } + + JSDocInfo info = getJSDocInfoForFunction(node, parent, gramp); + if (info != null) { + boolean hasSpecificSideEffects = false; + if (hasSideEffectsThisAnnotation(info)) { + if (inExterns) { + hasSpecificSideEffects = true; + sideEffectInfo.setTaintsThis(); + } else { + traversal.report(node, INVALID_MODIFIES_ANNOTATION); + } + } + + if (hasSideEffectsArgumentsAnnotation(info)) { + if (inExterns) { + hasSpecificSideEffects = true; + sideEffectInfo.setTaintsArguments(); + } else { + traversal.report(node, INVALID_MODIFIES_ANNOTATION); + } + } + + if (inExterns && !info.getThrownTypes().isEmpty()) { + hasSpecificSideEffects = true; + sideEffectInfo.setFunctionThrows(); + } + + if (!hasSpecificSideEffects) { + if (hasNoSideEffectsAnnotation(info)) { + if (inExterns) { + sideEffectInfo.setIsPure(); + } else { + traversal.report(node, INVALID_NO_SIDE_EFFECT_ANNOTATION); + } + } else if (inExterns) { + sideEffectInfo.setTaintsGlobalState(); + } + } + } else { + if (inExterns) { + sideEffectInfo.setTaintsGlobalState(); + } + } + } + + /** + * @return Whether the jstype is something known to be a local value. + */ + private boolean isLocalValueType(JSType jstype, boolean recurse) { + Preconditions.checkNotNull(jstype); + JSType subtype = jstype.getGreatestSubtype( + compiler.getTypeRegistry().getNativeType(JSTypeNative.OBJECT_TYPE)); + // If the type includes anything related to a object type, don't assume + // anything about the locality of the value. + return subtype.isNoType(); + } + + /** + * Record that the enclosing function throws. + */ + private void visitThrow(FunctionInformation sideEffectInfo) { + sideEffectInfo.setFunctionThrows(); + } + + /** + * Get the doc info associated with the function. + */ + private JSDocInfo getJSDocInfoForFunction( + Node node, Node parent, Node gramp) { + JSDocInfo info = node.getJSDocInfo(); + if (info != null) { + return info; + } else if (parent.isName()) { + return gramp.hasOneChild() ? gramp.getJSDocInfo() : null; + } else if (parent.isAssign()) { + return parent.getJSDocInfo(); + } else { + return null; + } + } + + /** + * Get the value of the @nosideeffects annotation stored in the + * doc info. + */ + private boolean hasNoSideEffectsAnnotation(JSDocInfo docInfo) { + Preconditions.checkNotNull(docInfo); + return docInfo.isNoSideEffects(); + } + + /** + * Get the value of the @modifies{this} annotation stored in the + * doc info. + */ + private boolean hasSideEffectsThisAnnotation(JSDocInfo docInfo) { + Preconditions.checkNotNull(docInfo); + return (docInfo.getModifies().contains("this")); + } + + /** + * @returns Whether the @modifies annotation includes "arguments" + * or any named parameters. + */ + private boolean hasSideEffectsArgumentsAnnotation(JSDocInfo docInfo) { + Preconditions.checkNotNull(docInfo); + Set modifies = docInfo.getModifies(); + // TODO(johnlenz): if we start tracking parameters individually + // this should simply be a check for "arguments". + return (modifies.size() > 1 + || (modifies.size() == 1 && !modifies.contains("this"))); + } + } + + private static boolean isIncDec(Node n) { + int type = n.getType(); + return (type == Token.INC || type == Token.DEC); + } + + /** + * @return Whether the node is known to be a value that is not a reference + * outside the local scope. + */ + @SuppressWarnings("unused") + private static boolean isKnownLocalValue(final Node value) { + Predicate taintingPredicate = new Predicate() { + @Override + public boolean apply(Node value) { + switch (value.getType()) { + case Token.ASSIGN: + // The assignment might cause an alias, look at the LHS. + return false; + case Token.THIS: + // TODO(johnlenz): maybe redirect this to be a tainting list for 'this'. + return false; + case Token.NAME: + // TODO(johnlenz): add to local tainting list, if the NAME + // is known to be a local. + return false; + case Token.GETELEM: + case Token.GETPROP: + // There is no information about the locality of object properties. + return false; + case Token.CALL: + // TODO(johnlenz): add to local tainting list, if the call result + // is not known to be a local result. + return false; + } + return false; + } + }; + + return NodeUtil.evaluatesToLocalValue(value, taintingPredicate); + } + + /** + * Callback that propagates side effect information across call sites. + */ + private static class SideEffectPropagationCallback + implements EdgeCallback { + @Override + public boolean traverseEdge(FunctionInformation callee, + Node callSite, + FunctionInformation caller) { + Preconditions.checkArgument(callSite.isCall() || + callSite.isNew()); + + boolean changed = false; + if (!caller.mutatesGlobalState() && callee.mutatesGlobalState()) { + caller.setTaintsGlobalState(); + changed = true; + } + + if (!caller.functionThrows() && callee.functionThrows()) { + caller.setFunctionThrows(); + changed = true; + } + + if (callee.mutatesThis()) { + // Side effects only propagate via regular calls. + // Calling a constructor that modifies "this" has no side effects. + if (!callSite.isNew()) { + Node objectNode = getCallThisObject(callSite); + if (objectNode != null && objectNode.isName() + && !isCallOrApply(callSite)) { + // Exclude ".call" and ".apply" as the value may still be + // null or undefined. We don't need to worry about this with a + // direct method call because null and undefined don't have any + // properties. + String name = objectNode.getString(); + + // TODO(nicksantos): Turn this back on when locals-tracking + // is fixed. See testLocalizedSideEffects11. + //if (!caller.knownLocals.contains(name)) { + if (!caller.mutatesGlobalState()) { + caller.setTaintsGlobalState(); + changed = true; + } + //} + } else if (objectNode != null && objectNode.isThis()) { + if (!caller.mutatesThis()) { + caller.setTaintsThis(); + changed = true; + } + } else if (objectNode != null + && NodeUtil.evaluatesToLocalValue(objectNode) + && !isCallOrApply(callSite)) { + // Modifying 'this' on a known local object doesn't change any + // significant state. + // TODO(johnlenz): We can improve this by including literal values + // that we know for sure are not null. + } else if (!caller.mutatesGlobalState()) { + caller.setTaintsGlobalState(); + changed = true; + } + } + } + + return changed; + } + } + + /** + * Analyze a call site and extract the node that will be act as + * "this" inside the call, which is either the object part of the + * qualified function name, the first argument to the call in the + * case of ".call" and ".apply" or null if object is not specified + * in either of those ways. + * + * @return node that will act as "this" for the call. + */ + private static Node getCallThisObject(Node callSite) { + Node callTarget = callSite.getFirstChild(); + if (!NodeUtil.isGet(callTarget)) { + + // "this" is not specified explicitly; call modifies global "this". + return null; + } + + String propString = callTarget.getLastChild().getString(); + if (propString.equals("call") || propString.equals("apply")) { + return callTarget.getNext(); + } else { + return callTarget.getFirstChild(); + } + } + + private static boolean isCallOrApply(Node callSite) { + Node callTarget = callSite.getFirstChild(); + if (NodeUtil.isGet(callTarget)) { + String propString = callTarget.getLastChild().getString(); + if (propString.equals("call") || propString.equals("apply")) { + return true; + } + } + return false; + } + + /** + * Keeps track of a function's known side effects by type and the + * list of calls that appear in a function's body. + */ + private static class FunctionInformation { + private final boolean extern; + private final List callsInFunctionBody = Lists.newArrayList(); + private Set blacklisted = Sets.newHashSet(); + private Set taintedLocals = Sets.newHashSet(); + private Set knownLocals = Sets.newHashSet(); + private boolean pureFunction = false; + private boolean functionThrows = false; + private boolean taintsGlobalState = false; + private boolean taintsThis = false; + private boolean taintsArguments = false; + private boolean taintsUnknown = false; + private boolean taintsReturn = false; + + FunctionInformation(boolean extern) { + this.extern = extern; + checkInvariant(); + } + + /** + * @param var + */ + void addTaintedLocalObject(Var var) { + taintedLocals.add(var); + } + + void resetLocalVars() { + blacklisted = null; + taintedLocals = null; + knownLocals = Collections.emptySet(); + } + + /** + * @param var + */ + public void blacklistLocal(Var var) { + blacklisted.add(var); + } + + /** + * @returns false if function known to have side effects. + */ + boolean mayBePure() { + return !(functionThrows || + taintsGlobalState || + taintsThis || + taintsArguments || + taintsUnknown); + } + + /** + * @returns false if function known to be pure. + */ + boolean mayHaveSideEffects() { + return !pureFunction; + } + + /** + * Mark the function as being pure. + */ + void setIsPure() { + pureFunction = true; + checkInvariant(); + } + + /** + * Marks the function as having "modifies globals" side effects. + */ + void setTaintsGlobalState() { + taintsGlobalState = true; + checkInvariant(); + } + + /** + * Marks the function as having "modifies this" side effects. + */ + void setTaintsThis() { + taintsThis = true; + checkInvariant(); + } + + /** + * Marks the function as having "modifies arguments" side effects. + */ + void setTaintsArguments() { + taintsArguments = true; + checkInvariant(); + } + + /** + * Marks the function as having "throw" side effects. + */ + void setFunctionThrows() { + functionThrows = true; + checkInvariant(); + } + + /** + * Marks the function as having "complex" side effects that are + * not otherwise explicitly tracked. + */ + void setTaintsUnknown() { + taintsUnknown = true; + checkInvariant(); + } + + /** + * Marks the function as having non-local return result. + */ + void setTaintsReturn() { + taintsReturn = true; + checkInvariant(); + } + + + /** + * Returns true if function mutates global state. + */ + boolean mutatesGlobalState() { + // TODO(johnlenz): track arguments separately. + return taintsGlobalState || taintsArguments || taintsUnknown; + } + + /** + * Returns true if function mutates "this". + */ + boolean mutatesThis() { + return taintsThis; + } + + /** + * Returns true if function has an explicit "throw". + */ + boolean functionThrows() { + return functionThrows; + } + + /** + * Verify internal consistency. Should be called at the end of + * every method that mutates internal state. + */ + private void checkInvariant() { + boolean invariant = mayBePure() || mayHaveSideEffects(); + if (!invariant) { + throw new IllegalStateException("Invariant failed. " + toString()); + } + } + + /** + * Add a CALL or NEW node to the list of calls this function makes. + */ + void appendCall(Node callNode) { + callsInFunctionBody.add(callNode); + } + + /** + * Gets the list of CALL and NEW nodes. + */ + List getCallsInFunctionBody() { + return callsInFunctionBody; + } + + @Override + public String toString() { + List status = Lists.newArrayList(); + if (extern) { + status.add("extern"); + } + + if (pureFunction) { + status.add("pure"); + } + + if (taintsThis) { + status.add("this"); + } + + if (taintsGlobalState) { + status.add("global"); + } + + if (functionThrows) { + status.add("throw"); + } + + if (taintsUnknown) { + status.add("complex"); + } + + return "Side effects: " + status.toString(); + } + } + + /** + * A compiler pass that constructs a reference graph and drives + * the PureFunctionIdentifier across it. + */ + static class Driver implements CompilerPass { + private final AbstractCompiler compiler; + private final String reportPath; + private final boolean useNameReferenceGraph; + + Driver(AbstractCompiler compiler, String reportPath, + boolean useNameReferenceGraph) { + this.compiler = compiler; + this.reportPath = reportPath; + this.useNameReferenceGraph = useNameReferenceGraph; + } + + @Override + public void process(Node externs, Node root) { + DefinitionProvider definitionProvider = null; + if (useNameReferenceGraph) { + NameReferenceGraphConstruction graphBuilder = + new NameReferenceGraphConstruction(compiler); + graphBuilder.process(externs, root); + definitionProvider = graphBuilder.getNameReferenceGraph(); + } else { + SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); + defFinder.process(externs, root); + definitionProvider = defFinder; + } + + PureFunctionIdentifier pureFunctionIdentifier = + new PureFunctionIdentifier(compiler, definitionProvider); + pureFunctionIdentifier.process(externs, root); + + if (reportPath != null) { + try { + Files.write(pureFunctionIdentifier.getDebugReport(), + new File(reportPath), + Charsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RecordFunctionInformation.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RecordFunctionInformation.java new file mode 100644 index 0000000..345d8e3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RecordFunctionInformation.java @@ -0,0 +1,132 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.Node; +import java.util.Comparator; +import java.util.TreeSet; + +/** + * Records information about functions and modules. + * + */ +class RecordFunctionInformation extends AbstractPostOrderCallback + implements CompilerPass { + private final Compiler compiler; + private final FunctionNames functionNames; + private final JSModuleGraph moduleGraph; + + /** + * Protocol buffer builder. + */ + private final FunctionInformationMap.Builder mapBuilder; + + /** + * Creates a record function information compiler pass. + * + * @param compiler The JSCompiler + * @param functionNames Assigned function identifiers. + */ + RecordFunctionInformation(Compiler compiler, + FunctionNames functionNames) { + this.compiler = compiler; + this.moduleGraph = compiler.getModuleGraph(); + this.functionNames = functionNames; + this.mapBuilder = FunctionInformationMap.newBuilder(); + } + + /** + * Returns the built-out map. + */ + FunctionInformationMap getMap() { + return mapBuilder.build(); + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + + if (moduleGraph == null) { + addModuleInformation(null); + } else { + // The test expects a consistent module order. + TreeSet modules = Sets.newTreeSet(new Comparator() { + @Override + public int compare(JSModule o1, JSModule o2) { + return o1.getName().compareTo(o2.getName()); + } + }); + Iterables.addAll(modules, moduleGraph.getAllModules()); + for (JSModule m : modules) { + addModuleInformation(m); + } + } + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isFunction()) { + return; + } + + int id = functionNames.getFunctionId(n); + if (id < 0) { + // Function node was added during compilation; don't instrument. + return; + } + + String compiledSource = compiler.toSource(n); + JSModule module = t.getModule(); + mapBuilder.addEntry(FunctionInformationMap.Entry.newBuilder() + .setId(id) + .setSourceName(NodeUtil.getSourceName(n)) + .setLineNumber(n.getLineno()) + .setModuleName(moduleGraph == null ? "" : module.getName()) + .setSize(compiledSource.length()) + .setName(functionNames.getFunctionName(n)) + .setCompiledSource(compiledSource).build()); + } + + /** + * Record a module's compiled source. The view of the source we get + * from function sources alone is not complete; it doesn't contain + * assignments and function calls in the global scope which are + * crucial to understanding how the application works. + * + * This version of the source is also written out to js_output_file, + * module_output_path_prefix or other places. Duplicating it here + * simplifies the process of writing tools that combine and present + * module and function for debugging purposes. + */ + private void addModuleInformation(JSModule module) { + String name; + String source; + if (module != null) { + name = module.getName(); + source = compiler.toSource(module); + } else { + name = ""; + source = compiler.toSource(); + } + + mapBuilder.addModule(FunctionInformationMap.Module.newBuilder() + .setName(name) + .setCompiledSource(source).build()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReferenceCollectingCallback.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReferenceCollectingCallback.java new file mode 100644 index 0000000..497be42 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReferenceCollectingCallback.java @@ -0,0 +1,724 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.StaticReference; +import com.google.javascript.rhino.jstype.StaticSourceFile; +import com.google.javascript.rhino.jstype.StaticSymbolTable; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * A helper class for passes that want to access all information about where a + * variable is referenced and declared at once and then make a decision as to + * how it should be handled, possibly inlining, reordering, or generating + * warnings. Callers do this by providing {@link Behavior} and then + * calling {@link #process(Node, Node)}. + * + * @author kushal@google.com (Kushal Dave) + */ +class ReferenceCollectingCallback implements ScopedCallback, + HotSwapCompilerPass, + StaticSymbolTable { + + /** + * Maps a given variable to a collection of references to that name. Note that + * Var objects are not stable across multiple traversals (unlike scope root or + * name). + */ + private final Map referenceMap = + Maps.newHashMap(); + + /** + * The stack of basic blocks and scopes the current traversal is in. + */ + private final Deque blockStack = new ArrayDeque(); + + /** + * Source of behavior at various points in the traversal. + */ + private final Behavior behavior; + + /** + * JavaScript compiler to use in traversing. + */ + private final AbstractCompiler compiler; + + /** + * Only collect references for filtered variables. + */ + private final Predicate varFilter; + + /** + * Constructor initializes block stack. + */ + ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior) { + this(compiler, behavior, Predicates.alwaysTrue()); + } + + /** + * Constructor only collects references that match the given variable. + * + * The test for Var equality uses reference equality, so it's necessary to + * inject a scope when you traverse. + */ + ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior, + Predicate varFilter) { + this.compiler = compiler; + this.behavior = behavior; + this.varFilter = varFilter; + } + + /** + * Convenience method for running this pass over a tree with this + * class as a callback. + */ + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverseRoots( + compiler, Lists.newArrayList(externs, root), this); + } + + /** + * Same as process but only runs on a part of AST associated to one script. + */ + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + NodeTraversal.traverse(compiler, scriptRoot, this); + } + + /** + * Gets the variables that were referenced in this callback. + */ + @Override + public Iterable getAllSymbols() { + return referenceMap.keySet(); + } + + @Override + public Scope getScope(Var var) { + return var.scope; + } + + /** + * Gets the reference collection for the given variable. + */ + @Override + public ReferenceCollection getReferences(Var v) { + return referenceMap.get(v); + } + + /** + * For each node, update the block stack and reference collection + * as appropriate. + */ + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isName()) { + Var v; + if (n.getString().equals("arguments")) { + v = t.getScope().getArgumentsVar(); + } else { + v = t.getScope().getVar(n.getString()); + } + if (v != null && varFilter.apply(v)) { + addReference(t, v, new Reference(n, t, blockStack.peek())); + } + } + + if (isBlockBoundary(n, parent)) { + blockStack.pop(); + } + } + + /** + * Updates block stack and invokes any additional behavior. + */ + @Override + public void enterScope(NodeTraversal t) { + Node n = t.getScope().getRootNode(); + BasicBlock parent = blockStack.isEmpty() ? null : blockStack.peek(); + blockStack.push(new BasicBlock(parent, n)); + } + + /** + * Updates block stack and invokes any additional behavior. + */ + @Override + public void exitScope(NodeTraversal t) { + blockStack.pop(); + if (t.getScope().isGlobal()) { + // Update global scope reference lists when we are done with it. + compiler.updateGlobalVarReferences(referenceMap, t.getScopeRoot()); + behavior.afterExitScope(t, compiler.getGlobalVarReferences()); + } else { + behavior.afterExitScope(t, new ReferenceMapWrapper(referenceMap)); + } + } + + /** + * Updates block stack. + */ + @Override + public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, + Node parent) { + // If node is a new basic block, put on basic block stack + if (isBlockBoundary(n, parent)) { + blockStack.push(new BasicBlock(blockStack.peek(), n)); + } + return true; + } + + /** + * @return true if this node marks the start of a new basic block + */ + private static boolean isBlockBoundary(Node n, Node parent) { + if (parent != null) { + switch (parent.getType()) { + case Token.DO: + case Token.FOR: + case Token.TRY: + case Token.WHILE: + case Token.WITH: + // NOTE: TRY has up to 3 child blocks: + // TRY + // BLOCK + // BLOCK + // CATCH + // BLOCK + // Note that there is an explicit CATCH token but no explicit + // FINALLY token. For simplicity, we consider each BLOCK + // a separate basic BLOCK. + return true; + case Token.AND: + case Token.HOOK: + case Token.IF: + case Token.OR: + // The first child of a conditional is not a boundary, + // but all the rest of the children are. + return n != parent.getFirstChild(); + + } + } + + return n.isCase(); + } + + private void addReference(NodeTraversal t, Var v, Reference reference) { + // Create collection if none already + ReferenceCollection referenceInfo = referenceMap.get(v); + if (referenceInfo == null) { + referenceInfo = new ReferenceCollection(); + referenceMap.put(v, referenceInfo); + } + + // Add this particular reference + referenceInfo.add(reference, t, v); + } + + interface ReferenceMap { + ReferenceCollection getReferences(Var var); + } + + private static class ReferenceMapWrapper implements ReferenceMap { + private final Map referenceMap; + + public ReferenceMapWrapper(Map referenceMap) { + this.referenceMap = referenceMap; + } + + @Override + public ReferenceCollection getReferences(Var var) { + return referenceMap.get(var); + } + } + + /** + * Way for callers to add specific behavior during traversal that + * utilizes the built-up reference information. + */ + interface Behavior { + /** + * Called after we finish with a scope. + */ + void afterExitScope(NodeTraversal t, ReferenceMap referenceMap); + } + + static Behavior DO_NOTHING_BEHAVIOR = new Behavior() { + @Override + public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {} + }; + + /** + * A collection of references. Can be subclassed to apply checks or + * store additional state when adding. + */ + static class ReferenceCollection implements Iterable { + + List references = Lists.newArrayList(); + + @Override + public Iterator iterator() { + return references.iterator(); + } + + void add(Reference reference, NodeTraversal t, Var v) { + references.add(reference); + } + + /** + * Determines if the variable for this reference collection is + * "well-defined." A variable is well-defined if we can prove at + * compile-time that it's assigned a value before it's used. + * + * Notice that if this function returns false, this doesn't imply that the + * variable is used before it's assigned. It just means that we don't + * have enough information to make a definitive judgment. + */ + protected boolean isWellDefined() { + int size = references.size(); + if (size == 0) { + return false; + } + + // If this is a declaration that does not instantiate the variable, + // it's not well-defined. + Reference init = getInitializingReference(); + if (init == null) { + return false; + } + + Preconditions.checkState(references.get(0).isDeclaration()); + BasicBlock initBlock = init.getBasicBlock(); + for (int i = 1; i < size; i++) { + if (!initBlock.provablyExecutesBefore( + references.get(i).getBasicBlock())) { + return false; + } + } + + return true; + } + + /** + * Whether the variable is escaped into an inner scope. + */ + boolean isEscaped() { + Scope scope = null; + for (Reference ref : references) { + if (scope == null) { + scope = ref.scope; + } else if (scope != ref.scope) { + return true; + } + } + return false; + } + + /** + * @param index The index into the references array to look for an + * assigning declaration. + * + * This is either the declaration if a value is assigned (such as + * "var a = 2", "function a()...", "... catch (a)..."). + */ + private boolean isInitializingDeclarationAt(int index) { + Reference maybeInit = references.get(index); + if (maybeInit.isInitializingDeclaration()) { + // This is a declaration that represents the initial value. + // Specifically, var declarations without assignments such as "var a;" + // are not. + return true; + } + return false; + } + + /** + * @param index The index into the references array to look for an + * initialized assignment reference. That is, an assignment immediately + * follow a variable declaration that itself does not initialize the + * variable. + */ + private boolean isInitializingAssignmentAt(int index) { + if (index < references.size() && index > 0) { + Reference maybeDecl = references.get(index-1); + if (maybeDecl.isVarDeclaration()) { + Preconditions.checkState(!maybeDecl.isInitializingDeclaration()); + Reference maybeInit = references.get(index); + if (maybeInit.isSimpleAssignmentToName()) { + return true; + } + } + } + return false; + } + + /** + * @return The reference that provides the value for the variable at the + * time of the first read, if known, otherwise null. + * + * This is either the variable declaration ("var a = ...") or first + * reference following the declaration if it is an assignment. + */ + Reference getInitializingReference() { + if (isInitializingDeclarationAt(0)) { + return references.get(0); + } else if (isInitializingAssignmentAt(1)) { + return references.get(1); + } + return null; + } + + /** + * Constants are allowed to be defined after their first use. + */ + Reference getInitializingReferenceForConstants() { + int size = references.size(); + for (int i = 0; i < size; i++) { + if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { + return references.get(i); + } + } + return null; + } + + /** + * @return Whether the variable is only assigned a value once for its + * lifetime. + */ + boolean isAssignedOnceInLifetime() { + Reference ref = getOneAndOnlyAssignment(); + if (ref == null) { + return false; + } + + // Make sure this assignment is not in a loop. + for (BasicBlock block = ref.getBasicBlock(); + block != null; block = block.getParent()) { + if (block.isFunction) { + break; + } else if (block.isLoop) { + return false; + } + } + + return true; + } + + /** + * @return The one and only assignment. Returns if there are 0 or 2+ + * assignments. + */ + private Reference getOneAndOnlyAssignment() { + Reference assignment = null; + int size = references.size(); + for (int i = 0; i < size; i++) { + Reference ref = references.get(i); + if (ref.isLvalue() || ref.isInitializingDeclaration()) { + if (assignment == null) { + assignment = ref; + } else { + return null; + } + } + } + return assignment; + } + + /** + * @return Whether the variable is never assigned a value. + */ + boolean isNeverAssigned() { + int size = references.size(); + for (int i = 0; i < size; i++) { + Reference ref = references.get(i); + if (ref.isLvalue() || ref.isInitializingDeclaration()) { + return false; + } + } + return true; + } + + boolean firstReferenceIsAssigningDeclaration() { + int size = references.size(); + if (size > 0 && references.get(0).isInitializingDeclaration()) { + return true; + } + return false; + } + } + + /** + * Represents a single declaration or reference to a variable. + */ + static final class Reference implements StaticReference { + + private static final Set DECLARATION_PARENTS = + ImmutableSet.of(Token.VAR, Token.FUNCTION, Token.CATCH); + + private final Node nameNode; + private final BasicBlock basicBlock; + private final Scope scope; + private final InputId inputId; + private final StaticSourceFile sourceFile; + + Reference(Node nameNode, NodeTraversal t, + BasicBlock basicBlock) { + this(nameNode, basicBlock, t.getScope(), t.getInput().getInputId()); + } + + // Bleeding functions are weird, because the declaration does + // not appear inside their scope. So they need their own constructor. + static Reference newBleedingFunction(NodeTraversal t, + BasicBlock basicBlock, Node func) { + return new Reference(func.getFirstChild(), + basicBlock, t.getScope(), t.getInput().getInputId()); + } + + /** + * Creates a variable reference in a given script file name, used in tests. + * + * @return The created reference. + */ + @VisibleForTesting + static Reference createRefForTest(CompilerInput input) { + return new Reference(new Node(Token.NAME), null, null, + input.getInputId()); + } + + private Reference(Node nameNode, + BasicBlock basicBlock, Scope scope, InputId inputId) { + this.nameNode = nameNode; + this.basicBlock = basicBlock; + this.scope = scope; + this.inputId = inputId; + this.sourceFile = nameNode.getStaticSourceFile(); + } + + /** + * Makes a copy of the current reference using a new Scope instance. + */ + Reference cloneWithNewScope(Scope newScope) { + return new Reference(nameNode, basicBlock, newScope, inputId); + } + + @Override + public Var getSymbol() { + return scope.getVar(nameNode.getString()); + } + + @Override + public Node getNode() { + return nameNode; + } + + public InputId getInputId() { + return inputId; + } + + @Override + public StaticSourceFile getSourceFile() { + return sourceFile; + } + + boolean isDeclaration() { + Node parent = getParent(); + Node grandparent = parent.getParent(); + return DECLARATION_PARENTS.contains(parent.getType()) || + parent.isParamList() && + grandparent.isFunction(); + } + + boolean isVarDeclaration() { + return getParent().isVar(); + } + + boolean isHoistedFunction() { + return NodeUtil.isHoistedFunctionDeclaration(getParent()); + } + + /** + * Determines whether the variable is initialized at the declaration. + */ + boolean isInitializingDeclaration() { + // VAR is the only type of variable declaration that may not initialize + // its variable. Catch blocks, named functions, and parameters all do. + return isDeclaration() && + !getParent().isVar() || + nameNode.getFirstChild() != null; + } + + /** + * @return For an assignment, variable declaration, or function declaration + * return the assigned value, otherwise null. + */ + Node getAssignedValue() { + Node parent = getParent(); + return (parent.isFunction()) + ? parent : NodeUtil.getAssignedValue(nameNode); + } + + BasicBlock getBasicBlock() { + return basicBlock; + } + + Node getParent() { + return getNode().getParent(); + } + + Node getGrandparent() { + Node parent = getParent(); + return parent == null ? null : parent.getParent(); + } + + private static boolean isLhsOfForInExpression(Node n) { + Node parent = n.getParent(); + if (parent.isVar()) { + return isLhsOfForInExpression(parent); + } + return NodeUtil.isForIn(parent) && parent.getFirstChild() == n; + } + + boolean isSimpleAssignmentToName() { + Node parent = getParent(); + return parent.isAssign() + && parent.getFirstChild() == nameNode; + } + + boolean isLvalue() { + Node parent = getParent(); + int parentType = parent.getType(); + return (parentType == Token.VAR && nameNode.getFirstChild() != null) + || parentType == Token.INC + || parentType == Token.DEC + || (NodeUtil.isAssignmentOp(parent) + && parent.getFirstChild() == nameNode) + || isLhsOfForInExpression(nameNode); + } + + Scope getScope() { + return scope; + } + } + + /** + * Represents a section of code that is uninterrupted by control structures + * (conditional or iterative logic). + */ + static final class BasicBlock { + + private final BasicBlock parent; + + /** + * Determines whether the block may not be part of the normal control flow, + * but instead "hoisted" to the top of the scope. + */ + private final boolean isHoisted; + + /** + * Whether this block denotes a function scope. + */ + private final boolean isFunction; + + /** + * Whether this block denotes a loop. + */ + private final boolean isLoop; + + /** + * Creates a new block. + * @param parent The containing block. + * @param root The root node of the block. + */ + BasicBlock(BasicBlock parent, Node root) { + this.parent = parent; + + // only named functions may be hoisted. + this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); + + this.isFunction = root.isFunction(); + + if (root.getParent() != null) { + int pType = root.getParent().getType(); + this.isLoop = pType == Token.DO || + pType == Token.WHILE || + pType == Token.FOR; + } else { + this.isLoop = false; + } + } + + BasicBlock getParent() { + return parent; + } + + /** + * Determines whether this block is equivalent to the very first block that + * is created when reference collection traversal enters global scope. Note + * that when traversing a single script in a hot-swap fashion a new instance + * of {@code BasicBlock} is created. + * + * @return true if this is global scope block. + */ + boolean isGlobalScopeBlock() { + return getParent() == null; + } + + /** + * Determines whether this block is guaranteed to begin executing before + * the given block does. + */ + boolean provablyExecutesBefore(BasicBlock thatBlock) { + // If thatBlock is a descendant of this block, and there are no hoisted + // blocks between them, then this block must start before thatBlock. + BasicBlock currentBlock; + for (currentBlock = thatBlock; + currentBlock != null && currentBlock != this; + currentBlock = currentBlock.getParent()) { + if (currentBlock.isHoisted) { + return false; + } + } + + if (currentBlock == this) { + return true; + } + if (isGlobalScopeBlock() && thatBlock.isGlobalScopeBlock()) { + return true; + } + return false; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Region.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Region.java new file mode 100644 index 0000000..f293fc5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Region.java @@ -0,0 +1,38 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Source code region. + * + */ +public interface Region { + /** + * Get the source region. + */ + String getSourceExcerpt(); + + /** + * Get the beginning line number. + */ + int getBeginningLineNumber(); + + /** + * Get the ending line number. + */ + int getEndingLineNumber(); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RemoveTryCatch.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RemoveTryCatch.java new file mode 100644 index 0000000..962ff57 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RemoveTryCatch.java @@ -0,0 +1,114 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.HashSet; +import java.util.Set; + +/** + * Removes try catch finally blocks from a parse tree for easier debugging + * (these statements impact both debugging in IE and sometimes even in FF). + * + */ +class RemoveTryCatch implements CompilerPass { + private final AbstractCompiler compiler; + private final Set tryNodesContainingReturnStatements; + + RemoveTryCatch(AbstractCompiler compiler) { + this.compiler = compiler; + this.tryNodesContainingReturnStatements = new HashSet(); + } + + /** + * Do all processing on the root node. + */ + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, new RemoveTryCatchCode()); + } + + private class RemoveTryCatchCode extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.TRY: + // Ignore the try statement if it has the @preserveTry annotation + // (for expected exceptions). + JSDocInfo info = n.getJSDocInfo(); + if (info != null && info.shouldPreserveTry()) { + return; + } + + Node tryBlock = n.getFirstChild(); + Node catchBlock = tryBlock.getNext(); // may be null or empty + Node finallyBlock = catchBlock != null ? catchBlock.getNext() : null; + + // Ignore the try statement if it has a finally part and the try + // block contains an early return. + if (finallyBlock != null && + tryNodesContainingReturnStatements.contains(n)) { + return; + } + + // Redeclare vars declared in the catch node to be removed. + if (catchBlock.hasOneChild()) { + NodeUtil.redeclareVarsInsideBranch(catchBlock); + } + + // Disconnect the try/catch/finally nodes from the parent + // and each other. + n.detachChildren(); + + // try node + Node block; + if (!NodeUtil.isStatementBlock(parent)) { + block = IR.block(); + parent.replaceChild(n, block); + block.addChildToFront(tryBlock); + } else { + parent.replaceChild(n, tryBlock); + block = parent; + } + + // finally node + if (finallyBlock != null) { + block.addChildAfter(finallyBlock, tryBlock); + } + compiler.reportCodeChange(); + break; + + case Token.RETURN: + boolean isInTryBlock = false; + for (Node anc = parent; + anc != null && !anc.isFunction(); + anc = anc.getParent()) { + if (anc.isTry()) { + tryNodesContainingReturnStatements.add(anc); + break; + } + } + break; + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RemoveUnusedClassProperties.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RemoveUnusedClassProperties.java new file mode 100644 index 0000000..4328f22 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RemoveUnusedClassProperties.java @@ -0,0 +1,146 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.List; +import java.util.Set; + +/** + * Look for internal properties set using "this" but never read. Explicitly + * ignored is the possibility that these properties + * may be indirectly referenced using "for-in" or "Object.keys". This is the + * same assumption used with RemoveUnusedPrototypeProperties but is by slightly + * wider in scope. + * + * @author johnlenz@google.com (John Lenz) + */ +class RemoveUnusedClassProperties + implements CompilerPass, NodeTraversal.Callback { + final AbstractCompiler compiler; + private boolean inExterns; + private Set used = Sets.newHashSet(); + private List candidates = Lists.newArrayList(); + + RemoveUnusedClassProperties(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverseRoots(compiler, this, externs, root); + removeUnused(); + } + + private void removeUnused() { + for (Node n : candidates) { + Preconditions.checkState(n.isGetProp()); + if (!used.contains(n.getLastChild().getString())) { + Node parent = n.getParent(); + if (NodeUtil.isAssignmentOp(parent)) { + Node assign = parent; + Preconditions.checkState(assign != null + && NodeUtil.isAssignmentOp(assign) + && assign.getFirstChild() == n); + // 'this.x = y' to 'y' + assign.getParent().replaceChild(assign, + assign.getLastChild().detachFromParent()); + } else if (parent.isInc() || parent.isDec()) { + parent.getParent().replaceChild(parent, IR.number(0)); + } else { + throw new IllegalStateException("unexpected: "+ parent); + } + compiler.reportCodeChange(); + } + } + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + if (n.isScript()) { + this.inExterns = n.getStaticSourceFile().isExtern(); + } + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.GETPROP: { + String propName = n.getLastChild().getString(); + if (inExterns || isPinningPropertyUse(n)) { + used.add(propName); + } else { + // This is a definition of a property but it is only removable + // if it is defined on "this". + if (n.getFirstChild().isThis()) { + candidates.add(n); + } + } + break; + } + + case Token.CALL: + // Look for properties referenced through "JSCompiler_propertyRename". + Node target = n.getFirstChild(); + if (n.hasMoreThanOneChild() + && target.isName() + && target.getString().equals(NodeUtil.JSC_PROPERTY_NAME_FN)) { + Node propName = target.getNext(); + if (propName.isString()) { + used.add(propName.getString()); + } + } + break; + } + } + + /** + * @return Whether the property is used in a way that prevents its removal. + */ + private boolean isPinningPropertyUse(Node n) { + // Rather than looking for cases that are uses, we assume all references are + // pinning uses unless they are: + // - a simple assignment (x.a = 1) + // - a compound assignment or increment (x++, x += 1) whose result is + // otherwise unused + + Node parent = n.getParent(); + if (n == parent.getFirstChild()) { + if (parent.isAssign()) { + // A simple assignment doesn't pin the property. + return false; + } else if (NodeUtil.isAssignmentOp(parent) + || parent.isInc() || parent.isDec()) { + // In general, compound assignments are both reads and writes, but + // if the property is never otherwise read we can consider it simply + // a write. + // However if the assign expression is used as part of a larger + // expression, we much consider it a read. For example: + // x = (y.a += 1); + return NodeUtil.isExpressionResultUsed(parent); + } + } + return true; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RemoveUnusedNames.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RemoveUnusedNames.java new file mode 100644 index 0000000..3ce2427 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RemoveUnusedNames.java @@ -0,0 +1,76 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.AnalyzeNameReferences.NameInfo; +import com.google.javascript.jscomp.NameReferenceGraph.Name; +import com.google.javascript.jscomp.NameReferenceGraph.Reference; +import com.google.javascript.jscomp.graph.GraphNode; +import com.google.javascript.rhino.Node; + +import java.util.logging.Logger; + +/** + * Removes unused names. + * + */ +class RemoveUnusedNames implements CompilerPass { + + private static final Logger logger = + Logger.getLogger(RemoveUnusedNames.class.getName()); + + private final AbstractCompiler compiler; + + private final boolean canModifyExterns; + + /** + * Creates a new pass for removing unused prototype properties, based + * on the uniqueness of property names. + * @param compiler The compiler. + */ + RemoveUnusedNames(AbstractCompiler compiler, + boolean canModifyExterns) { + this.compiler = compiler; + this.canModifyExterns = canModifyExterns; + } + + @Override + public void process(Node externRoot, Node root) { + AnalyzeNameReferences analyzer = + new AnalyzeNameReferences(compiler); + analyzer.process(externRoot, root); + removeUnusedProperties(analyzer.getGraph()); + } + + /** + * Remove all properties under a given name if the property name is + * never referenced. + */ + private void removeUnusedProperties(NameReferenceGraph graph) { + for (GraphNode node : graph.getNodes()) { + Name name = node.getValue(); + NameInfo nameInfo = node.getAnnotation(); + if (nameInfo == null || !nameInfo.isReferenced()) { + if (canModifyExterns || !name.isExtern()) { + name.remove(); + compiler.reportCodeChange(); + logger.fine("Removed unused name" + name); + } + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RemoveUnusedPrototypeProperties.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RemoveUnusedPrototypeProperties.java new file mode 100644 index 0000000..214d76c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RemoveUnusedPrototypeProperties.java @@ -0,0 +1,148 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.AnalyzePrototypeProperties.AssignmentProperty; +import com.google.javascript.jscomp.AnalyzePrototypeProperties.GlobalFunction; +import com.google.javascript.jscomp.AnalyzePrototypeProperties.LiteralProperty; +import com.google.javascript.jscomp.AnalyzePrototypeProperties.NameInfo; +import com.google.javascript.jscomp.AnalyzePrototypeProperties.Symbol; +import com.google.javascript.rhino.Node; + +import java.util.Collection; +import java.util.logging.Logger; + +/** + * Removes unused properties from prototypes. + * + */ +class RemoveUnusedPrototypeProperties implements + SpecializationAwareCompilerPass { + + private static final Logger logger = + Logger.getLogger(RemoveUnusedPrototypeProperties.class.getName()); + + private final AbstractCompiler compiler; + private final boolean canModifyExterns; + private final boolean anchorUnusedVars; + private SpecializeModule.SpecializationState specializationState; + + /** + * Creates a new pass for removing unused prototype properties, based + * on the uniqueness of property names. + * @param compiler The compiler. + * @param canModifyExterns If true, then we can remove prototype + * properties that are declared in the externs file. + * @param anchorUnusedVars If true, then we must keep unused variables + * and the prototype properties they reference, even if they are + * never used. + */ + RemoveUnusedPrototypeProperties(AbstractCompiler compiler, + boolean canModifyExterns, + boolean anchorUnusedVars) { + this.compiler = compiler; + this.canModifyExterns = canModifyExterns; + this.anchorUnusedVars = anchorUnusedVars; + } + + @Override + public void enableSpecialization(SpecializeModule.SpecializationState state) { + this.specializationState = state; + } + + @Override + public void process(Node externRoot, Node root) { + AnalyzePrototypeProperties analyzer = + new AnalyzePrototypeProperties(compiler, + null /* no module graph */, canModifyExterns, anchorUnusedVars); + analyzer.process(externRoot, root); + removeUnusedSymbols(analyzer.getAllNameInfo()); + } + + /** + * Remove all properties under a given name if the property name is + * never referenced. + */ + private void removeUnusedSymbols(Collection allNameInfo) { + boolean changed = false; + for (NameInfo nameInfo : allNameInfo) { + if (!nameInfo.isReferenced()) { + for (Symbol declaration : nameInfo.getDeclarations()) { + boolean canRemove = false; + + if (specializationState == null) { + canRemove = true; + } else { + Node specializableFunction = + getSpecializableFunctionFromSymbol(declaration); + + if (specializableFunction != null) { + specializationState.reportRemovedFunction( + specializableFunction, null); + canRemove = true; + } + } + + if (canRemove) { + declaration.remove(); + changed = true; + } + } + + logger.fine("Removed unused prototype property: " + nameInfo.name); + } + } + + if (changed) { + compiler.reportCodeChange(); + } + } + + /** + * Attempts to find a specializable function from the Symbol. + */ + private Node getSpecializableFunctionFromSymbol(Symbol symbol) { + Preconditions.checkNotNull(specializationState); + + Node specializableFunction = null; + + if (symbol instanceof GlobalFunction) { + specializableFunction = ((GlobalFunction) symbol).getFunctionNode(); + } else if (symbol instanceof AssignmentProperty) { + Node propertyValue = ((AssignmentProperty) symbol).getValue(); + if (propertyValue.isFunction()) { + specializableFunction = propertyValue; + } + } else if (symbol instanceof LiteralProperty) { + // Module specialization doesn't know how to handle these + // because the "name" of the function isn't the name + // it needs to add an unspecialized version of. + + return null; + } else { + Preconditions.checkState(false, "Should be unreachable."); + } + + if (specializableFunction != null && + specializationState.canFixupFunction(specializableFunction)) { + return specializableFunction; + } else { + return null; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RemoveUnusedVars.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RemoveUnusedVars.java new file mode 100644 index 0000000..4c84735 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RemoveUnusedVars.java @@ -0,0 +1,998 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.CodingConvention.SubclassRelationship; +import com.google.javascript.jscomp.DefinitionsRemover.Definition; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.*; + +/** + * Garbage collection for variable and function definitions. Basically performs + * a mark-and-sweep type algorithm over the JavaScript parse tree. + * + * For each scope: + * (1) Scan the variable/function declarations at that scope. + * (2) Traverse the scope for references, marking all referenced variables. + * Unlike other compiler passes, this is a pre-order traversal, not a + * post-order traversal. + * (3) If the traversal encounters an assign without other side-effects, + * create a continuation. Continue the continuation iff the assigned + * variable is referenced. + * (4) When the traversal completes, remove all unreferenced variables. + * + * If it makes it easier, you can think of the continuations of the traversal + * as a reference graph. Each continuation represents a set of edges, where the + * source node is a known variable, and the destination nodes are lazily + * evaluated when the continuation is executed. + * + * This algorithm is similar to the algorithm used by {@code SmartNameRemoval}. + * {@code SmartNameRemoval} maintains an explicit graph of dependencies + * between global symbols. However, {@code SmartNameRemoval} cannot handle + * non-trivial edges in the reference graph ("A is referenced iff both B and C + * are referenced"), or local variables. {@code SmartNameRemoval} is also + * substantially more complicated because it tries to handle namespaces + * (which is largely unnecessary in the presence of {@code CollapseProperties}. + * + * This pass also uses a more complex analysis of assignments, where + * an assignment to a variable or a property of that variable does not + * necessarily count as a reference to that variable, unless we can prove + * that it modifies external state. This is similar to + * {@code FlowSensitiveInlineVariables}, except that it works for variables + * used across scopes. + * + */ +class RemoveUnusedVars + implements CompilerPass, OptimizeCalls.CallGraphCompilerPass { + + private final AbstractCompiler compiler; + + private final CodingConvention codingConvention; + + private final boolean removeGlobals; + + private boolean preserveFunctionExpressionNames; + + /** + * Keep track of variables that we've referenced. + */ + private final Set referenced = Sets.newHashSet(); + + /** + * Keep track of variables that might be unreferenced. + */ + private final List maybeUnreferenced = Lists.newArrayList(); + + /** + * Keep track of scopes that we've traversed. + */ + private final List allFunctionScopes = Lists.newArrayList(); + + /** + * Keep track of assigns to variables that we haven't referenced. + */ + private final Multimap assignsByVar = + ArrayListMultimap.create(); + + /** + * The assigns, indexed by the NAME node that they assign to. + */ + private final Map assignsByNode = Maps.newHashMap(); + + /** + * Subclass name -> class-defining call EXPR node. (like inherits) + */ + private final Multimap classDefiningCalls = + ArrayListMultimap.create(); + + /** + * Keep track of continuations that are finished iff the variable they're + * indexed by is referenced. + */ + private final Multimap continuations = + ArrayListMultimap.create(); + + private boolean modifyCallSites; + + private CallSiteOptimizer callSiteOptimizer; + + RemoveUnusedVars( + AbstractCompiler compiler, + boolean removeGlobals, + boolean preserveFunctionExpressionNames, + boolean modifyCallSites) { + this.compiler = compiler; + this.codingConvention = compiler.getCodingConvention(); + this.removeGlobals = removeGlobals; + this.preserveFunctionExpressionNames = preserveFunctionExpressionNames; + this.modifyCallSites = modifyCallSites; + } + + /** + * Traverses the root, removing all unused variables. Multiple traversals + * may occur to ensure all unused variables are removed. + */ + @Override + public void process(Node externs, Node root) { + Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); + SimpleDefinitionFinder defFinder = null; + + if (modifyCallSites) { + // For testing, allow the SimpleDefinitionFinder to be build now. + defFinder = new SimpleDefinitionFinder(compiler); + defFinder.process(externs, root); + } + process(externs, root, defFinder); + } + + @Override + public void process( + Node externs, Node root, SimpleDefinitionFinder defFinder) { + if (modifyCallSites) { + Preconditions.checkNotNull(defFinder); + callSiteOptimizer = new CallSiteOptimizer(compiler, defFinder); + } + traverseAndRemoveUnusedReferences(root); + if (callSiteOptimizer != null) { + callSiteOptimizer.applyChanges(); + } + } + + /** + * Traverses a node recursively. Call this once per pass. + */ + private void traverseAndRemoveUnusedReferences(Node root) { + Scope scope = new SyntacticScopeCreator(compiler).createScope(root, null); + traverseNode(root, null, scope); + + if (removeGlobals) { + collectMaybeUnreferencedVars(scope); + } + + interpretAssigns(); + removeUnreferencedVars(); + for (Scope fnScope : allFunctionScopes) { + removeUnreferencedFunctionArgs(fnScope); + } + } + + /** + * Traverses everything in the current scope and marks variables that + * are referenced. + * + * During traversal, we identify subtrees that will only be + * referenced if their enclosing variables are referenced. Instead of + * traversing those subtrees, we create a continuation for them, + * and traverse them lazily. + */ + private void traverseNode(Node n, Node parent, Scope scope) { + int type = n.getType(); + Var var = null; + switch (type) { + case Token.FUNCTION: + // If this function is a removable var, then create a continuation + // for it instead of traversing immediately. + if (NodeUtil.isFunctionDeclaration(n)) { + var = scope.getVar(n.getFirstChild().getString()); + } + + if (var != null && isRemovableVar(var)) { + continuations.put(var, new Continuation(n, scope)); + } else { + traverseFunction(n, scope); + } + return; + + case Token.ASSIGN: + Assign maybeAssign = Assign.maybeCreateAssign(n); + if (maybeAssign != null) { + // Put this in the assign map. It might count as a reference, + // but we won't know that until we have an index of all assigns. + var = scope.getVar(maybeAssign.nameNode.getString()); + if (var != null) { + assignsByVar.put(var, maybeAssign); + assignsByNode.put(maybeAssign.nameNode, maybeAssign); + + if (isRemovableVar(var) && + !maybeAssign.mayHaveSecondarySideEffects) { + // If the var is unreferenced and performing this assign has + // no secondary side effects, then we can create a continuation + // for it instead of traversing immediately. + continuations.put(var, new Continuation(n, scope)); + return; + } + } + } + break; + + case Token.CALL: + Var modifiedVar = null; + + // Look for calls to inheritance-defining calls (such as goog.inherits). + SubclassRelationship subclassRelationship = + codingConvention.getClassesDefinedByCall(n); + if (subclassRelationship != null) { + modifiedVar = scope.getVar(subclassRelationship.subclassName); + } else { + // Look for calls to addSingletonGetter calls. + String className = codingConvention.getSingletonGetterClassName(n); + if (className != null) { + modifiedVar = scope.getVar(className); + } + } + + // Don't try to track the inheritance calls for non-globals. It would + // be more correct to only not track when the subclass does not + // reference a constructor, but checking that it is a global is + // easier and mostly the same. + if (modifiedVar != null && modifiedVar.isGlobal() + && !referenced.contains(modifiedVar)) { + // Save a reference to the EXPR node. + classDefiningCalls.put(modifiedVar, parent); + continuations.put(modifiedVar, new Continuation(n, scope)); + return; + } + break; + + case Token.NAME: + var = scope.getVar(n.getString()); + if (parent.isVar()) { + Node value = n.getFirstChild(); + if (value != null && var != null && isRemovableVar(var) && + !NodeUtil.mayHaveSideEffects(value, compiler)) { + // If the var is unreferenced and creating its value has no side + // effects, then we can create a continuation for it instead + // of traversing immediately. + continuations.put(var, new Continuation(n, scope)); + return; + } + } else { + + // If arguments is escaped, we just assume the worst and continue + // on all the parameters. + if ("arguments".equals(n.getString()) && scope.isLocal()) { + Node lp = scope.getRootNode().getFirstChild().getNext(); + for (Node a = lp.getFirstChild(); a != null; a = a.getNext()) { + markReferencedVar(scope.getVar(a.getString())); + } + } + + // All name references that aren't declarations or assigns + // are references to other vars. + if (var != null) { + // If that var hasn't already been marked referenced, then + // start tracking it. If this is an assign, do nothing + // for now. + if (isRemovableVar(var)) { + if (!assignsByNode.containsKey(n)) { + markReferencedVar(var); + } + } else { + markReferencedVar(var); + } + } + } + break; + } + + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + traverseNode(c, n, scope); + } + } + + private boolean isRemovableVar(Var var) { + // Global variables are off-limits if the user might be using them. + if (!removeGlobals && var.isGlobal()) { + return false; + } + + // Referenced variables are off-limits. + if (referenced.contains(var)) { + return false; + } + + // Exported variables are off-limits. + if (codingConvention.isExported(var.getName())) { + return false; + } + + return true; + } + + /** + * Traverses a function, which creates a new scope in JavaScript. + * + * Note that CATCH blocks also create a new scope, but only for the + * catch variable. Declarations within the block actually belong to the + * enclosing scope. Because we don't remove catch variables, there's + * no need to treat CATCH blocks differently like we do functions. + */ + private void traverseFunction(Node n, Scope parentScope) { + Preconditions.checkState(n.getChildCount() == 3); + Preconditions.checkState(n.isFunction()); + + final Node body = n.getLastChild(); + Preconditions.checkState(body.getNext() == null && + body.isBlock()); + + Scope fnScope = + new SyntacticScopeCreator(compiler).createScope(n, parentScope); + traverseNode(body, n, fnScope); + + collectMaybeUnreferencedVars(fnScope); + allFunctionScopes.add(fnScope); + } + + /** + * For each variable in this scope that we haven't found a reference + * for yet, add it to the list of variables to check later. + */ + private void collectMaybeUnreferencedVars(Scope scope) { + for (Iterator it = scope.getVars(); it.hasNext(); ) { + Var var = it.next(); + if (isRemovableVar(var)) { + maybeUnreferenced.add(var); + } + } + } + + /** + * Removes unreferenced arguments from a function declaration and when + * possible the function's callSites. + * + * @param fnScope The scope inside the function + */ + private void removeUnreferencedFunctionArgs(Scope fnScope) { + // Notice that removing unreferenced function args breaks + // Function.prototype.length. In advanced mode, we don't really care + // about this: we consider "length" the equivalent of reflecting on + // the function's lexical source. + // + // Rather than create a new option for this, we assume that if the user + // is removing globals, then it's OK to remove unused function args. + // + // See http://code.google.com/p/closure-compiler/issues/detail?id=253 + if (!removeGlobals) { + return; + } + + Node function = fnScope.getRootNode(); + + Preconditions.checkState(function.isFunction()); + if (NodeUtil.isGetOrSetKey(function.getParent())) { + // The parameters object literal setters can not be removed. + return; + } + + Node argList = getFunctionArgList(function); + boolean modifyCallers = modifyCallSites + && callSiteOptimizer.canModifyCallers(function); + if (!modifyCallers) { + // Strip unreferenced args off the end of the function declaration. + Node lastArg; + while ((lastArg = argList.getLastChild()) != null) { + Var var = fnScope.getVar(lastArg.getString()); + if (!referenced.contains(var)) { + argList.removeChild(lastArg); + compiler.reportCodeChange(); + } else { + break; + } + } + } else { + callSiteOptimizer.optimize(fnScope, referenced); + } + } + + + /** + * @return the LP node containing the function parameters. + */ + private static Node getFunctionArgList(Node function) { + return function.getFirstChild().getNext(); + } + + private static class CallSiteOptimizer { + private final AbstractCompiler compiler; + private final SimpleDefinitionFinder defFinder; + private final List toRemove = Lists.newArrayList(); + private final List toReplaceWithZero = Lists.newArrayList(); + + CallSiteOptimizer( + AbstractCompiler compiler, + SimpleDefinitionFinder defFinder) { + this.compiler = compiler; + this.defFinder = defFinder; + } + + public void optimize(Scope fnScope, Set referenced) { + Node function = fnScope.getRootNode(); + Preconditions.checkState(function.isFunction()); + Node argList = getFunctionArgList(function); + + // In this path we try to modify all the call sites to remove unused + // function parameters. + boolean changeCallSignature = canChangeSignature(function); + markUnreferencedFunctionArgs( + fnScope, function, referenced, + argList.getFirstChild(), 0, changeCallSignature); + } + + /** + * Applies optimizations to all previously marked nodes. + */ + public void applyChanges() { + for (Node n : toRemove) { + n.getParent().removeChild(n); + compiler.reportCodeChange(); + } + for (Node n : toReplaceWithZero) { + n.getParent().replaceChild(n, IR.number(0).srcref(n)); + compiler.reportCodeChange(); + } + } + + /** + * For each unused function parameter, determine if it can be removed + * from all the call sites, if so, remove it from the function signature + * and the call sites otherwise replace the unused value where possible + * with a constant (0). + * + * @param scope The function scope + * @param function The function + * @param param The current parameter node in the parameter list. + * @param paramIndex The index of the current parameter + * @param canChangeSignature Whether function signature can be change. + * @return Whether there is a following function parameter. + */ + private boolean markUnreferencedFunctionArgs( + Scope scope, Node function, Set referenced, + Node param, int paramIndex, + boolean canChangeSignature) { + if (param != null) { + // Take care of the following siblings first. + boolean hasFollowing = markUnreferencedFunctionArgs( + scope, function, referenced, param.getNext(), paramIndex+1, + canChangeSignature); + + Var var = scope.getVar(param.getString()); + if (!referenced.contains(var)) { + Preconditions.checkNotNull(var); + + // Remove call parameter if we can generally change the signature + // or if it is the last parameter in the parameter list. + boolean modifyAllCallSites = canChangeSignature || !hasFollowing; + if (modifyAllCallSites) { + modifyAllCallSites = canRemoveArgFromCallSites( + function, paramIndex); + } + + tryRemoveArgFromCallSites(function, paramIndex, modifyAllCallSites); + + // Remove an unused function parameter if all the call sites can + // be modified to remove it, or if it is the last parameter. + if (modifyAllCallSites || !hasFollowing) { + toRemove.add(param); + return hasFollowing; + } + } + return true; + } else { + // Anything past the last formal parameter can be removed from the call + // sites. + tryRemoveAllFollowingArgs(function, paramIndex-1); + return false; + } + } + + /** + * Remove all references to a parameter, otherwise simplify the known + * references. + * @return Whether all the references were removed. + */ + private boolean canRemoveArgFromCallSites(Node function, int argIndex) { + Definition definition = getFunctionDefinition(function); + + // Check all the call sites. + for (UseSite site : defFinder.getUseSites(definition)) { + if (isModifiableCallSite(site)) { + Node arg = getArgumentForCallOrNewOrDotCall(site, argIndex); + // TODO(johnlenz): try to remove parameters with side-effects by + // decomposing the call expression. + if (arg != null && NodeUtil.mayHaveSideEffects(arg, compiler)) { + return false; + } + } else { + return false; + } + } + + return true; + } + + /** + * Remove all references to a parameter if possible otherwise simplify the + * side-effect free parameters. + */ + private void tryRemoveArgFromCallSites( + Node function, int argIndex, boolean canModifyAllSites) { + Definition definition = getFunctionDefinition(function); + + for (UseSite site : defFinder.getUseSites(definition)) { + if (isModifiableCallSite(site)) { + Node arg = getArgumentForCallOrNewOrDotCall(site, argIndex); + if (arg != null) { + Node argParent = arg.getParent(); + // Even if we can't change the signature in general we can always + // remove an unused value off the end of the parameter list. + if (canModifyAllSites + || (arg.getNext() == null + && !NodeUtil.mayHaveSideEffects(arg, compiler))) { + toRemove.add(arg); + } else { + // replace the node in the arg with 0 + if (!NodeUtil.mayHaveSideEffects(arg, compiler) + && (!arg.isNumber() || arg.getDouble() != 0)) { + toReplaceWithZero.add(arg); + } + } + } + } + } + } + + /** + * Remove all the following parameters without side-effects + */ + private void tryRemoveAllFollowingArgs(Node function, final int argIndex) { + Definition definition = getFunctionDefinition(function); + for (UseSite site : defFinder.getUseSites(definition)) { + if (!isModifiableCallSite(site)) { + continue; + } + Node arg = getArgumentForCallOrNewOrDotCall(site, argIndex + 1); + while (arg != null) { + if (!NodeUtil.mayHaveSideEffects(arg)) { + toRemove.add(arg); + } + arg = arg.getNext(); + } + } + } + + /** + * Returns the nth argument node given a usage site for a direct function + * call or for a func.call() node. + */ + private static Node getArgumentForCallOrNewOrDotCall(UseSite site, + final int argIndex) { + int adjustedArgIndex = argIndex; + Node parent = site.node.getParent(); + if (NodeUtil.isFunctionObjectCall(parent)) { + adjustedArgIndex++; + } + return NodeUtil.getArgumentForCallOrNew(parent, adjustedArgIndex); + } + + /** + * @param function + * @return Whether the callers to this function can be modified in any way. + */ + boolean canModifyCallers(Node function) { + if (NodeUtil.isVarArgsFunction(function)) { + return false; + } + + DefinitionSite defSite = defFinder.getDefinitionForFunction(function); + if (defSite == null) { + return false; + } + + Definition definition = defSite.definition; + + // Be conservative, don't try to optimize any declaration that isn't as + // simple function declaration or assignment. + if (!SimpleDefinitionFinder.isSimpleFunctionDeclaration(function)) { + return false; + } + + return defFinder.canModifyDefinition(definition); + } + + /** + * @param site The site to inspect + * @return Whether the call site is suitable for modification + */ + private static boolean isModifiableCallSite(UseSite site) { + return SimpleDefinitionFinder.isCallOrNewSite(site) + && !NodeUtil.isFunctionObjectApply(site.node.getParent()); + } + + /** + * @return Whether the definitionSite represents a function whose call + * signature can be modified. + */ + private boolean canChangeSignature(Node function) { + Definition definition = getFunctionDefinition(function); + CodingConvention convention = compiler.getCodingConvention(); + + Preconditions.checkState(!definition.isExtern()); + + Collection useSites = defFinder.getUseSites(definition); + for (UseSite site : useSites) { + Node parent = site.node.getParent(); + + // This was a use site removed by something else before we run. + // 1. By another pass before us which means the definition graph is + // no updated properly. + // 2. By the continuations algorithm above. + if (parent == null) { + continue; // Ignore it. + } + + // Ignore references within goog.inherits calls. + if (parent.isCall() && + convention.getClassesDefinedByCall(parent) != null) { + continue; + } + + // Accessing the property directly prevents rewrite. + if (!SimpleDefinitionFinder.isCallOrNewSite(site)) { + if (!(parent.isGetProp() && + NodeUtil.isFunctionObjectCall(parent.getParent()))) { + return false; + } + } + + if (NodeUtil.isFunctionObjectApply(parent)) { + return false; + } + + // TODO(johnlenz): support specialization + + // Multiple definitions prevent rewrite. + // Attempt to validate the state of the simple definition finder. + Node nameNode = site.node; + Collection singleSiteDefinitions = + defFinder.getDefinitionsReferencedAt(nameNode); + Preconditions.checkState(singleSiteDefinitions.size() == 1); + Preconditions.checkState(singleSiteDefinitions.contains(definition)); + } + + return true; + } + + /** + * @param function + * @return the Definition object for the function. + */ + private Definition getFunctionDefinition(Node function) { + DefinitionSite definitionSite = defFinder.getDefinitionForFunction( + function); + Preconditions.checkNotNull(definitionSite); + Definition definition = definitionSite.definition; + Preconditions.checkState(!definitionSite.inExterns); + Preconditions.checkState(definition.getRValue() == function); + return definition; + } + } + + + /** + * Look at all the property assigns to all variables. + * These may or may not count as references. For example, + * + * + * var x = {}; + * x.foo = 3; // not a reference. + * var y = foo(); + * y.foo = 3; // is a reference. + * + * + * Interpreting assignments could mark a variable as referenced that + * wasn't referenced before, in order to keep it alive. Because we find + * references by lazily traversing subtrees, marking a variable as + * referenced could trigger new traversals of new subtrees, which could + * find new references. + * + * Therefore, this interpretation needs to be run to a fixed point. + */ + private void interpretAssigns() { + boolean changes = false; + do { + changes = false; + + // We can't use traditional iterators and iterables for this list, + // because our lazily-evaluated continuations will modify it while + // we traverse it. + for (int current = 0; current < maybeUnreferenced.size(); current++) { + Var var = maybeUnreferenced.get(current); + if (referenced.contains(var)) { + maybeUnreferenced.remove(current); + current--; + } else { + boolean assignedToUnknownValue = false; + boolean hasPropertyAssign = false; + + if (var.getParentNode().isVar() && + !NodeUtil.isForIn(var.getParentNode().getParent())) { + Node value = var.getInitialValue(); + assignedToUnknownValue = value != null && + !NodeUtil.isLiteralValue(value, true); + } else { + // This was initialized to a function arg or a catch param + // or a for...in variable. + assignedToUnknownValue = true; + } + + boolean maybeEscaped = false; + for (Assign assign : assignsByVar.get(var)) { + if (assign.isPropertyAssign) { + hasPropertyAssign = true; + } else if (!NodeUtil.isLiteralValue( + assign.assignNode.getLastChild(), true)) { + assignedToUnknownValue = true; + } + if (assign.maybeAliased) { + maybeEscaped = true; + } + } + + if ((assignedToUnknownValue || maybeEscaped) && hasPropertyAssign) { + changes = markReferencedVar(var) || changes; + maybeUnreferenced.remove(current); + current--; + } + } + } + } while (changes); + } + + /** + * Remove all assigns to a var. + */ + private void removeAllAssigns(Var var) { + for (Assign assign : assignsByVar.get(var)) { + assign.remove(); + compiler.reportCodeChange(); + } + } + + /** + * Marks a var as referenced, recursing into any values of this var + * that we skipped. + * @return True if this variable had not been referenced before. + */ + private boolean markReferencedVar(Var var) { + if (referenced.add(var)) { + for (Continuation c : continuations.get(var)) { + c.apply(); + } + return true; + } + return false; + } + + /** + * Removes any vars in the scope that were not referenced. Removes any + * assignments to those variables as well. + */ + private void removeUnreferencedVars() { + CodingConvention convention = codingConvention; + + for (Iterator it = maybeUnreferenced.iterator(); it.hasNext(); ) { + Var var = it.next(); + + // Remove calls to inheritance-defining functions where the unreferenced + // class is the subclass. + for (Node exprCallNode : classDefiningCalls.get(var)) { + NodeUtil.removeChild(exprCallNode.getParent(), exprCallNode); + compiler.reportCodeChange(); + } + + // Regardless of what happens to the original declaration, + // we need to remove all assigns, because they may contain references + // to other unreferenced variables. + removeAllAssigns(var); + + compiler.addToDebugLog("Unreferenced var: " + var.name); + Node nameNode = var.nameNode; + Node toRemove = nameNode.getParent(); + Node parent = toRemove.getParent(); + + Preconditions.checkState( + toRemove.isVar() || + toRemove.isFunction() || + toRemove.isParamList() && + parent.isFunction(), + "We should only declare vars and functions and function args"); + + if (toRemove.isParamList() && + parent.isFunction()) { + // Don't remove function arguments here. That's a special case + // that's taken care of in removeUnreferencedFunctionArgs. + } else if (NodeUtil.isFunctionExpression(toRemove)) { + if (!preserveFunctionExpressionNames) { + toRemove.getFirstChild().setString(""); + compiler.reportCodeChange(); + } + // Don't remove bleeding functions. + } else if (parent != null && + parent.isFor() && + parent.getChildCount() < 4) { + // foreach iterations have 3 children. Leave them alone. + } else if (toRemove.isVar() && + nameNode.hasChildren() && + NodeUtil.mayHaveSideEffects(nameNode.getFirstChild(), compiler)) { + // If this is a single var declaration, we can at least remove the + // declaration itself and just leave the value, e.g., + // var a = foo(); => foo(); + if (toRemove.getChildCount() == 1) { + parent.replaceChild(toRemove, + IR.exprResult(nameNode.removeFirstChild())); + compiler.reportCodeChange(); + } + } else if (toRemove.isVar() && + toRemove.getChildCount() > 1) { + // For var declarations with multiple names (i.e. var a, b, c), + // only remove the unreferenced name + toRemove.removeChild(nameNode); + compiler.reportCodeChange(); + } else if (parent != null) { + NodeUtil.removeChild(parent, toRemove); + compiler.reportCodeChange(); + } + } + } + + /** + * Our progress in a traversal can be expressed completely as the + * current node and scope. The continuation lets us save that + * information so that we can continue the traversal later. + */ + private class Continuation { + private final Node node; + private final Scope scope; + + Continuation(Node node, Scope scope) { + this.node = node; + this.scope = scope; + } + + void apply() { + if (NodeUtil.isFunctionDeclaration(node)) { + traverseFunction(node, scope); + } else { + for (Node child = node.getFirstChild(); + child != null; child = child.getNext()) { + traverseNode(child, node, scope); + } + } + } + } + + private static class Assign { + + final Node assignNode; + + final Node nameNode; + + // If false, then this is an assign to the normal variable. Otherwise, + // this is an assign to a property of that variable. + final boolean isPropertyAssign; + + // Secondary side effects are any side effects in this assign statement + // that aren't caused by the assignment operation itself. For example, + // a().b = 3; + // a = b(); + // var foo = (a = b); + // In the first two cases, the sides of the assignment have side-effects. + // In the last one, the result of the assignment is used, so we + // are conservative and assume that it may be used in a side-effecting + // way. + final boolean mayHaveSecondarySideEffects; + + // If true, the value may have escaped and any modification is a use. + final boolean maybeAliased; + + Assign(Node assignNode, Node nameNode, boolean isPropertyAssign) { + Preconditions.checkState(NodeUtil.isAssignmentOp(assignNode)); + this.assignNode = assignNode; + this.nameNode = nameNode; + this.isPropertyAssign = isPropertyAssign; + + this.maybeAliased = NodeUtil.isExpressionResultUsed(assignNode); + this.mayHaveSecondarySideEffects = + maybeAliased || + NodeUtil.mayHaveSideEffects(assignNode.getFirstChild()) || + NodeUtil.mayHaveSideEffects(assignNode.getLastChild()); + } + + /** + * If this is an assign to a variable or its property, return it. + * Otherwise, return null. + */ + static Assign maybeCreateAssign(Node assignNode) { + Preconditions.checkState(NodeUtil.isAssignmentOp(assignNode)); + + // Skip one level of GETPROPs or GETELEMs. + // + // Don't skip more than one level, because then we get into + // situations where assigns to properties of properties will always + // trigger side-effects, and the variable they're on cannot be removed. + boolean isPropAssign = false; + Node current = assignNode.getFirstChild(); + if (NodeUtil.isGet(current)) { + current = current.getFirstChild(); + isPropAssign = true; + + if (current.isGetProp() && + current.getLastChild().getString().equals("prototype")) { + // Prototype properties sets should be considered like normal + // property sets. + current = current.getFirstChild(); + } + } + + if (current.isName()) { + return new Assign(assignNode, current, isPropAssign); + } + return null; + } + + /** + * Replace the current assign with its right hand side. + */ + void remove() { + Node parent = assignNode.getParent(); + if (mayHaveSecondarySideEffects) { + Node replacement = assignNode.getLastChild().detachFromParent(); + + // Aggregate any expressions in GETELEMs. + for (Node current = assignNode.getFirstChild(); + !current.isName(); + current = current.getFirstChild()) { + if (current.isGetElem()) { + replacement = IR.comma( + current.getLastChild().detachFromParent(), replacement); + replacement.copyInformationFrom(current); + } + } + + parent.replaceChild(assignNode, replacement); + } else { + Node gramps = parent.getParent(); + if (parent.isExprResult()) { + gramps.removeChild(parent); + } else { + parent.replaceChild(assignNode, + assignNode.getLastChild().detachFromParent()); + } + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RenameLabels.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RenameLabels.java new file mode 100644 index 0000000..321e236 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RenameLabels.java @@ -0,0 +1,278 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + + +/** + * RenameLabels renames all the labels so that they have short names, to reduce + * code size and also to obfuscate the code. + * + * Label names have a unique namespace, so variable or function names clashes + * are not a concern, but keywords clashes are. + * + * Additionally, labels names are only within the statements include in the + * label and do not cross function boundaries. This means that it is possible to + * create one label name that is used for labels at any given depth of label + * nesting. Typically, the name "a" will be used for all top-level labels, "b" + * for the next nested label, and so on. For example: + * + * + * function bar() { + * a: { + * b: { + * foo(); + * } + * } + * + * a: { + * b: break a; + * } + * } + * + * + * The general processes is as follows: process() is the entry point for the + * CompilerPass, and from there a standard "ScopedCallback" traversal is done, + * where "shouldTraverse" is called when descending the tree, and the "visit" is + * called in a depth first manner. The name for the label is selected during the + * decent in "shouldTraverse", and the references to the label name are renamed + * as they are encountered during the "visit". This means that if the label is + * unreferenced, it is known when the label node is visited, and, if so, can be + * safely removed. + * + * @author johnlenz@google.com (John Lenz) + */ +final class RenameLabels implements CompilerPass { + private final AbstractCompiler compiler; + private final Supplier nameSupplier; + private final boolean removeUnused; + + RenameLabels(AbstractCompiler compiler) { + this(compiler, new DefaultNameSupplier(), true); + } + + RenameLabels( + AbstractCompiler compiler, + Supplier supplier, + boolean removeUnused) { + this.compiler = compiler; + this.nameSupplier = supplier; + this.removeUnused = removeUnused; + } + + static class DefaultNameSupplier implements Supplier { + // NameGenerator is used to create safe label names. + final NameGenerator nameGenerator = + new NameGenerator(new HashSet(), "", null); + + @Override + public String get() { + return nameGenerator.generateNextName(); + } + } + + /** + * Iterate through the nodes, renaming all the labels. + */ + class ProcessLabels implements ScopedCallback { + + ProcessLabels() { + // Create a entry for global scope. + namespaceStack.push(new LabelNamespace()); + } + + // A stack of labels namespaces. Labels in an outer scope aren't part of an + // inner scope, so a new namespace is created each time a scope is entered. + final Deque namespaceStack = Lists.newLinkedList(); + + // The list of generated names. Typically, the first name will be "a", + // the second "b", etc. + final ArrayList names = new ArrayList(); + + + @Override + public void enterScope(NodeTraversal nodeTraversal) { + // Start a new namespace for label names. + namespaceStack.push(new LabelNamespace()); + } + + @Override + public void exitScope(NodeTraversal nodeTraversal) { + namespaceStack.pop(); + } + + /** + * shouldTraverse is call when descending into the Node tree, so it is used + * here to build the context for label renames. + * + * {@inheritDoc} + */ + @Override + public boolean shouldTraverse(NodeTraversal nodeTraversal, Node node, + Node parent) { + if (node.isLabel()) { + // Determine the new name for this label. + LabelNamespace current = namespaceStack.peek(); + int currentDepth = current.renameMap.size() + 1; + String name = node.getFirstChild().getString(); + + // Store the context for this label name. + LabelInfo li = new LabelInfo(currentDepth); + Preconditions.checkState(!current.renameMap.containsKey(name)); + current.renameMap.put(name, li); + + // Create a new name, if needed, for this depth. + if (names.size() < currentDepth) { + names.add(nameSupplier.get()); + } + + String newName = getNameForId(currentDepth); + compiler.addToDebugLog("label renamed: " + name + " => " + newName); + } + + return true; + } + + /** + * Delegate the actual processing of the node to visitLabel and + * visitBreakOrContinue. + * + * {@inheritDoc} + */ + @Override + public void visit(NodeTraversal nodeTraversal, Node node, Node parent) { + switch (node.getType()) { + case Token.LABEL: + visitLabel(node, parent); + break; + + case Token.BREAK: + case Token.CONTINUE: + visitBreakOrContinue(node); + break; + } + } + + /** + * Rename label references in breaks and continues. + * @param node The break or continue node. + */ + private void visitBreakOrContinue(Node node) { + Node nameNode = node.getFirstChild(); + if (nameNode != null) { + // This is a named break or continue; + String name = nameNode.getString(); + Preconditions.checkState(name.length() != 0); + LabelInfo li = getLabelInfo(name); + if (li != null) { + String newName = getNameForId(li.id); + // Mark the label as referenced so it isn't removed. + li.referenced = true; + if (!name.equals(newName)) { + // Give it the short name. + nameNode.setString(newName); + compiler.reportCodeChange(); + } + } + } + } + + /** + * Rename or remove labels. + * @param node The label node. + * @param parent The parent of the label node. + */ + private void visitLabel(Node node, Node parent) { + Node nameNode = node.getFirstChild(); + Preconditions.checkState(nameNode != null); + String name = nameNode.getString(); + LabelInfo li = getLabelInfo(name); + // This is a label... + if (li.referenced || !removeUnused) { + String newName = getNameForId(li.id); + if (!name.equals(newName)) { + // ... and it is used, give it the short name. + nameNode.setString(newName); + compiler.reportCodeChange(); + } + } else { + // ... and it is not referenced, just remove it. + Node newChild = node.getLastChild(); + node.removeChild(newChild); + parent.replaceChild(node, newChild); + if (newChild.isBlock()) { + NodeUtil.tryMergeBlock(newChild); + } + compiler.reportCodeChange(); + } + + // Remove the label from the current stack of labels. + namespaceStack.peek().renameMap.remove(name); + } + + /** + * @param id The id, which is the depth of the label in the current context, + * for which to get a short name. + * @return The short name of the identified label. + */ + String getNameForId(int id) { + return names.get(id - 1); + } + + /** + * @param name The name to retrieve information about. + * @return The structure representing the name in the current context. + */ + LabelInfo getLabelInfo(String name) { + return namespaceStack.peek().renameMap.get(name); + } + } + + @Override + public void process(Node externs, Node root) { + // Do variable reference counting. + NodeTraversal.traverse(compiler, root, new ProcessLabels()); + } + + + private static class LabelInfo { + boolean referenced = false; + final int id; + + LabelInfo(int id) { + this.id = id; + } + } + + + private static class LabelNamespace { + final Map renameMap = new HashMap(); + } + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RenameProperties.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RenameProperties.java new file mode 100644 index 0000000..7753a36 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RenameProperties.java @@ -0,0 +1,592 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.graph.Graph.GraphEdge; +import com.google.javascript.jscomp.graph.LinkedUndirectedGraph; +import com.google.javascript.jscomp.graph.UndiGraph; +import com.google.javascript.jscomp.graph.UndiGraph.UndiGraphEdge; +import com.google.javascript.jscomp.graph.UndiGraph.UndiGraphNode; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.TokenStream; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import javax.annotation.Nullable; + +/** + * RenameProperties renames properties (including methods) of all JavaScript + * objects. This includes prototypes, functions, object literals, etc. + * + *

      If provided a VariableMap of previously used names, it tries to reuse + * those names. + * + *

      To prevent a property from getting renamed you may extern it (add it to + * your externs file) or put it in quotes. + * + *

      To avoid run-time JavaScript errors, use quotes when accessing properties + * that are defined using quotes. + * + *

      + *   var a = {'myprop': 0}, b = a['myprop'];  // correct
      + *   var x = {'myprop': 0}, y = x.myprop;     // incorrect
      + * 
      + * + */ +class RenameProperties implements CompilerPass { + + private final AbstractCompiler compiler; + private final boolean generatePseudoNames; + + /** Property renaming map from a previous compilation. */ + private final VariableMap prevUsedPropertyMap; + + private final List stringNodesToRename = new ArrayList(); + private final Map callNodeToParentMap = + new HashMap(); + private final char[] reservedCharacters; + + // Map from property name to Property object + private final Map propertyMap = + new HashMap(); + + /** + * A graph of property affinity information. + * + * Suppose property X and Y are access in the same function N times. + * + * The graph would have X -> Y with the edge of N. + */ + private final UndiGraph affinityGraph; + + // Property names that don't get renamed + private final Set externedNames = new HashSet( + Arrays.asList("prototype")); + + // Names to which properties shouldn't be renamed, to avoid name conflicts + private final Set quotedNames = new HashSet(); + + private static final Comparator FREQUENCY_COMPARATOR = + new Comparator() { + @Override + public int compare(Property p1, Property p2) { + + /** + * First a frequently used names would always be picked first. + */ + if (p1.numOccurrences != p2.numOccurrences) { + return p2.numOccurrences - p1.numOccurrences; + + /** + * If both properties are used equally frequent. We'll let the property + * with a high affinity score get a name first. + * + * see #computeAffinityScores() for how the score is computed. + */ + } else if (p1.affinityScore != p2.affinityScore) { + return p2.affinityScore - p1.affinityScore; + } + + /** + * Finally, for determinism, we compare them based on the old name. + */ + return p1.oldName.compareTo(p2.oldName); + } + }; + + /** + * The name of a special function that this pass replaces. It takes one + * argument: a string literal containing one or more dot-separated JS + * identifiers. This pass will replace them as though they were JS property + * references. Here are two examples: + * JSCompiler_renameProperty('propertyName') -> 'jYq' + * JSCompiler_renameProperty('myProp.nestedProp.innerProp') -> 'e4.sW.C$' + */ + static final String RENAME_PROPERTY_FUNCTION_NAME = + "JSCompiler_renameProperty"; + + static final DiagnosticType BAD_CALL = DiagnosticType.error( + "JSC_BAD_RENAME_PROPERTY_FUNCTION_NAME_CALL", + "Bad " + RENAME_PROPERTY_FUNCTION_NAME + " call - " + + "argument must be a string literal"); + + static final DiagnosticType BAD_ARG = DiagnosticType.error( + "JSC_BAD_RENAME_PROPERTY_FUNCTION_NAME_ARG", + "Bad " + RENAME_PROPERTY_FUNCTION_NAME + " argument - " + + "'{0}' is not a valid JavaScript identifier"); + + /** + * Creates an instance. + * + * @param compiler The JSCompiler + * @param affinity Optimize for affinity information. + * @param generatePseudoNames Generate pseudo names. e.g foo -> $foo$ instead + * of compact obfuscated names. This is used for debugging. + */ + RenameProperties(AbstractCompiler compiler, boolean affinity, + boolean generatePseudoNames) { + this(compiler, affinity, generatePseudoNames, null, null); + } + + /** + * Creates an instance. + * + * @param compiler The JSCompiler. + * @param affinity Optimize for affinity information. + * @param generatePseudoNames Generate pseudo names. e.g foo -> $foo$ instead + * of compact obfuscated names. This is used for debugging. + * @param prevUsedPropertyMap The property renaming map used in a previous + * compilation. + */ + RenameProperties(AbstractCompiler compiler, boolean affinity, + boolean generatePseudoNames, VariableMap prevUsedPropertyMap) { + this(compiler, affinity, generatePseudoNames, prevUsedPropertyMap, null); + } + + /** + * Creates an instance. + * + * @param compiler The JSCompiler. + * @param affinity Optimize for affinity information. + * @param generatePseudoNames Generate pseudo names. e.g foo -> $foo$ instead + * of compact obfuscated names. This is used for debugging. + * @param prevUsedPropertyMap The property renaming map used in a previous + * compilation. + * @param reservedCharacters If specified these characters won't be used in + * generated names + */ + RenameProperties(AbstractCompiler compiler, + boolean affinity, + boolean generatePseudoNames, + VariableMap prevUsedPropertyMap, + @Nullable char[] reservedCharacters) { + this.compiler = compiler; + this.generatePseudoNames = generatePseudoNames; + this.prevUsedPropertyMap = prevUsedPropertyMap; + this.reservedCharacters = reservedCharacters; + if (affinity) { + this.affinityGraph = LinkedUndirectedGraph.createWithoutAnnotations(); + } else { + this.affinityGraph = null; + } + } + + @Override + public void process(Node externs, Node root) { + Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); + + NodeTraversal.traverse(compiler, externs, new ProcessExterns()); + NodeTraversal.traverse(compiler, root, new ProcessProperties()); + + Set reservedNames = + new HashSet(externedNames.size() + quotedNames.size()); + reservedNames.addAll(externedNames); + reservedNames.addAll(quotedNames); + + // First, try and reuse as many property names from the previous compilation + // as possible. + if (prevUsedPropertyMap != null) { + reusePropertyNames(reservedNames, propertyMap.values()); + } + + compiler.addToDebugLog("JS property assignments:"); + if (affinityGraph != null) { + computeAffinityScores(); + } + + // Assign names, sorted by descending frequency to minimize code size. + Set propsByFreq = new TreeSet(FREQUENCY_COMPARATOR); + propsByFreq.addAll(propertyMap.values()); + generateNames(propsByFreq, reservedNames); + + // Update the string nodes. + boolean changed = false; + for (Node n : stringNodesToRename) { + String oldName = n.getString(); + Property p = propertyMap.get(oldName); + if (p != null && p.newName != null) { + Preconditions.checkState(oldName.equals(p.oldName)); + n.setString(p.newName); + changed = changed || !p.newName.equals(oldName); + } + } + + // Update the call nodes. + for (Map.Entry nodeEntry : callNodeToParentMap.entrySet()) { + Node parent = nodeEntry.getValue(); + Node firstArg = nodeEntry.getKey().getFirstChild().getNext(); + StringBuilder sb = new StringBuilder(); + for (String oldName : firstArg.getString().split("[.]")) { + Property p = propertyMap.get(oldName); + String replacement; + if (p != null && p.newName != null) { + Preconditions.checkState(oldName.equals(p.oldName)); + replacement = p.newName; + } else { + replacement = oldName; + } + if (sb.length() > 0) { + sb.append('.'); + } + sb.append(replacement); + } + parent.replaceChild(nodeEntry.getKey(), IR.string(sb.toString())); + changed = true; + } + + if (changed) { + compiler.reportCodeChange(); + } + + compiler.setLifeCycleStage(LifeCycleStage.NORMALIZED_OBFUSCATED); + } + + /** + * Runs through the list of properties and renames as many as possible with + * names from the previous compilation. Also, updates reservedNames with the + * set of reused names. + * @param reservedNames Reserved names to use during renaming. + * @param allProps Properties to rename. + */ + private void reusePropertyNames(Set reservedNames, + Collection allProps) { + for (Property prop : allProps) { + // Check if this node can reuse a name from a previous compilation - if + // it can set the newName for the property too. + String prevName = prevUsedPropertyMap.lookupNewName(prop.oldName); + if (!generatePseudoNames && prevName != null) { + // We can reuse prevName if it's not reserved. + if (reservedNames.contains(prevName)) { + continue; + } + + prop.newName = prevName; + reservedNames.add(prevName); + } + } + } + + /** + * A X property gets an affinity score: + * + * score = sum (# of times X appears Y * frequency(Y)) for all Y where + * frequency(Y) > frequency (X). + * + * This way a property would have a name closer to all high frequency names. + * Also two property of the same frequency would have very close names if + * they always appear together. + */ + private void computeAffinityScores() { + for (Property p : propertyMap.values()) { + UndiGraphNode node = + affinityGraph.getUndirectedGraphNode(p); + + int affinityScore = 0; + for (Iterator> edgeIterator = + node.getNeighborEdgesIterator(); edgeIterator.hasNext();) { + UndiGraphEdge edge = edgeIterator.next(); + affinityScore += edge.getValue().affinity + + (node == edge.getNodeA() ? + edge.getNodeB().getValue().numOccurrences : + edge.getNodeA().getValue().numOccurrences); + } + node.getValue().affinityScore = affinityScore; + } + } + + /** + * Generates new names for properties. + * + * @param props Properties to generate new names for + * @param reservedNames A set of names to which properties should not be + * renamed + */ + private void generateNames(Set props, Set reservedNames) { + NameGenerator nameGen = new NameGenerator( + reservedNames, "", reservedCharacters); + for (Property p : props) { + if (generatePseudoNames) { + p.newName = "$" + p.oldName + "$"; + } else { + // If we haven't already given this property a reusable name. + if (p.newName == null) { + p.newName = nameGen.generateNextName(); + } + } + reservedNames.add(p.newName); + compiler.addToDebugLog(p.oldName + " => " + p.newName); + } + } + + /** + * Gets the property renaming map (the "answer key"). + * + * @return A mapping from original names to new names + */ + VariableMap getPropertyMap() { + ImmutableMap.Builder map = ImmutableMap.builder(); + for (Property p : propertyMap.values()) { + if (p.newName != null) { + map.put(p.oldName, p.newName); + } + } + return new VariableMap(map.build()); + } + + // ------------------------------------------------------------------------- + + /** + * A traversal callback that collects externed property names. + */ + private class ProcessExterns extends AbstractPostOrderCallback { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.GETPROP: + Node dest = n.getFirstChild().getNext(); + if (dest.isString()) { + externedNames.add(dest.getString()); + } + break; + case Token.OBJECTLIT: + for (Node child = n.getFirstChild(); + child != null; + child = child.getNext()) { + externedNames.add(child.getString()); + } + break; + } + } + } + + + // ------------------------------------------------------------------------- + + /** + * A traversal callback that collects property names and counts how + * frequently each property name occurs. + */ + private class ProcessProperties extends AbstractPostOrderCallback implements + ScopedCallback { + + private Set currentHighAffinityProperties = null; + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.GETPROP: + Node propNode = n.getFirstChild().getNext(); + if (propNode.isString()) { + maybeMarkCandidate(propNode); + } + break; + case Token.OBJECTLIT: + for (Node key = n.getFirstChild(); key != null; key = key.getNext()) { + if (!key.isQuotedString()) { + maybeMarkCandidate(key); + } else { + // Ensure that we never rename some other property in a way + // that could conflict with this quoted key. + quotedNames.add(key.getString()); + } + } + break; + case Token.GETELEM: + // If this is a quoted property access (e.g. x['myprop']), we need to + // ensure that we never rename some other property in a way that + // could conflict with this quoted name. + Node child = n.getLastChild(); + if (child != null && child.isString()) { + quotedNames.add(child.getString()); + } + break; + case Token.CALL: + // We replace a JSCompiler_renameProperty function call with a string + // containing the renamed property. + Node fnName = n.getFirstChild(); + if (fnName.isName() && + RENAME_PROPERTY_FUNCTION_NAME.equals(fnName.getString())) { + callNodeToParentMap.put(n, parent); + countCallCandidates(t, n); + } + break; + case Token.FUNCTION: + // We eliminate any stub implementations of JSCompiler_renameProperty + // that we encounter. + if (NodeUtil.isFunctionDeclaration(n)) { + String name = n.getFirstChild().getString(); + if (RENAME_PROPERTY_FUNCTION_NAME.equals(name)) { + if (parent.isExprResult()) { + parent.detachFromParent(); + } else { + parent.removeChild(n); + } + compiler.reportCodeChange(); + } + } else if (parent.isName() && + RENAME_PROPERTY_FUNCTION_NAME.equals(parent.getString())) { + Node varNode = parent.getParent(); + if (varNode.isVar()) { + varNode.removeChild(parent); + if (!varNode.hasChildren()) { + varNode.detachFromParent(); + } + compiler.reportCodeChange(); + } + } + break; + } + } + + /** + * If a property node is eligible for renaming, stashes a reference to it + * and increments the property name's access count. + * + * @param n The STRING node for a property + */ + private void maybeMarkCandidate(Node n) { + String name = n.getString(); + if (!externedNames.contains(name)) { + stringNodesToRename.add(n); + countPropertyOccurrence(name); + } + } + + /** + * Counts references to property names that occur in a special function + * call. + * + * @param callNode The CALL node for a property + * @param t The traversal + */ + private void countCallCandidates(NodeTraversal t, Node callNode) { + Node firstArg = callNode.getFirstChild().getNext(); + if (!firstArg.isString()) { + t.report(callNode, BAD_CALL); + return; + } + + for (String name : firstArg.getString().split("[.]")) { + if (!TokenStream.isJSIdentifier(name)) { + t.report(callNode, BAD_ARG, name); + continue; + } + if (!externedNames.contains(name)) { + countPropertyOccurrence(name); + } + } + } + + /** + * Increments the occurrence count for a property name. + * + * @param name The property name + */ + private void countPropertyOccurrence(String name) { + Property prop = propertyMap.get(name); + if (prop == null) { + prop = new Property(name); + propertyMap.put(name, prop); + if (affinityGraph != null) { + affinityGraph.createNode(prop); + } + } + prop.numOccurrences++; + if (currentHighAffinityProperties != null) { + currentHighAffinityProperties.add(prop); + } + } + + @Override + public void enterScope(NodeTraversal t) { + if (!t.inGlobalScope() && t.getScope().getParent().isGlobal()) { + currentHighAffinityProperties = Sets.newHashSet(); + } + } + + @Override + public void exitScope(NodeTraversal t) { + if (affinityGraph == null) { + return; + } + if (!t.inGlobalScope() && t.getScope().getParent().isGlobal()) { + for (Property p1 : currentHighAffinityProperties) { + for (Property p2 : currentHighAffinityProperties) { + if (p1.oldName.compareTo(p2.oldName) < 0) { + GraphEdge edge = + affinityGraph.getFirstEdge(p1, p2); + if (edge == null) { + affinityGraph.connect(p1, new PropertyAffinity(1), p2); + } else { + edge.getValue().increase(); + } + } + } + } + currentHighAffinityProperties = null; + } + } + } + + // ------------------------------------------------------------------------- + + /** + * Encapsulates the information needed for renaming a property. + */ + private class Property { + final String oldName; + String newName; + int numOccurrences; + int affinityScore = 0; + + Property(String name) { + this.oldName = name; + } + } + + private class PropertyAffinity { + // This will forever be zero if no affinity information was gathered. + private int affinity = 0; + + private PropertyAffinity(int affinity) { + this.affinity = affinity; + } + + private void increase() { + affinity++; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RenamePrototypes.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RenamePrototypes.java new file mode 100644 index 0000000..c051306 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RenamePrototypes.java @@ -0,0 +1,456 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.TokenStream; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import javax.annotation.Nullable; + +/** + * RenamePrototypes renames custom properties (including methods) of custom + * prototypes and object literals. Externed property names are never renamed. + * + * To ensure that a prototype property or object literal property gets renamed, + * end it with an underscore. + * + * To ensure that a prototype property is not renamed, give it a leading + * underscore. + * + * For custom prototype property names that lack leading and trailing + * underscores: + * - To always rename these, use aggressive renaming. + * - If aggressive renaming is off, we use a heuristic to decide whether to + * rename (to avoid most built-in JS methods). We rename if the original name + * contains at least one character that is not a lowercase letter. + * + * When a property name is used both in a prototype definition and as an object + * literal key, we rename it only if it satisfies both renaming policies. + * + */ +class RenamePrototypes implements CompilerPass { + + private final AbstractCompiler compiler; + private final boolean aggressiveRenaming; + private final char[] reservedCharacters; + + /** Previously used prototype renaming map. */ + private final VariableMap prevUsedRenameMap; + + /** + * The Property class encapsulates the information needed for renaming + * a method or member. + */ + private class Property { + String oldName; + String newName; + int prototypeCount; + int objLitCount; + int refCount; + + Property(String name) { + this.oldName = name; + this.newName = null; + this.prototypeCount = 0; + this.objLitCount = 0; + this.refCount = 0; + } + + int count() { + return prototypeCount + objLitCount + refCount; + } + + boolean canRename() { + if (this.prototypeCount > 0 && this.objLitCount == 0) { + return canRenamePrototypeProperty(); + } + if (this.objLitCount > 0 && this.prototypeCount == 0) { + return canRenameObjLitProperty(); + } + // We're not sure what kind of property this is, so we're conservative. + // Note that we still want to try renaming the property even when both + // counts are zero. It may be a property added to an object at runtime, + // like: o.newProp = x; + return canRenamePrototypeProperty() && canRenameObjLitProperty(); + } + + private boolean canRenamePrototypeProperty() { + if (compiler.getCodingConvention().isExported(oldName)) { + // an externally visible name should not be renamed. + return false; + } + + if (compiler.getCodingConvention().isPrivate(oldName)) { + // private names can be safely renamed. Rename! + return true; + } + + if (aggressiveRenaming) { + return true; + } + + for (int i = 0, n = oldName.length(); i < n; i++) { + char ch = oldName.charAt(i); + + if (Character.isUpperCase(ch) || !Character.isLetter(ch)) { + return true; + } + } + return false; + } + + private boolean canRenameObjLitProperty() { + if (compiler.getCodingConvention().isExported(oldName)) { + // an externally visible name should not be renamed. + return false; + } + + if (compiler.getCodingConvention().isPrivate(oldName)) { + // private names can be safely renamed. Rename! + return true; + } + + // NOTE(user): We should probably have more aggressive options, like + // renaming all obj lit properties that are not quoted. + return false; + } + } + + /** + * Sorts Property objects by their count, breaking ties alphabetically to + * ensure a deterministic total ordering. + */ + private static final Comparator FREQUENCY_COMPARATOR = + new Comparator() { + @Override + public int compare(Property a1, Property a2) { + int n1 = a1.count(); + int n2 = a2.count(); + if (n1 != n2) { + return n2 - n1; + } + return a1.oldName.compareTo(a2.oldName); + } + }; + + + // Set of String nodes to rename + private final Set stringNodes = new HashSet(); + + // Mapping of property names to Property objects + private final Map properties = + new HashMap(); + + // Set of names not to rename. Externed properties/methods are added later. + private final Set reservedNames = + new HashSet(Arrays.asList( + "indexOf", "lastIndexOf", "toString", "valueOf")); + + // Set of OBJLIT nodes that are assigned to prototypes + private final Set prototypeObjLits = new HashSet(); + + /** + * Creates an instance. + * + * @param compiler The JSCompiler + * @param aggressiveRenaming Whether to rename aggressively + * @param reservedCharacters If specified these characters won't be used in + * generated names + * @param prevUsedRenameMap The rename map used in the previous compilation + */ + RenamePrototypes(AbstractCompiler compiler, boolean aggressiveRenaming, + @Nullable char[] reservedCharacters, + @Nullable VariableMap prevUsedRenameMap) { + this.compiler = compiler; + this.aggressiveRenaming = aggressiveRenaming; + this.reservedCharacters = reservedCharacters; + this.prevUsedRenameMap = prevUsedRenameMap; + } + + /** + * Does property/method renaming. + * + * @param externs The root of the externs parse tree + * @param root The root of the main code parse tree + */ + @Override + public void process(Node externs, Node root) { + Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); + + NodeTraversal.traverse(compiler, externs, + new ProcessExternedProperties()); + NodeTraversal.traverse(compiler, root, new ProcessProperties()); + + // Gather the properties to rename, sorted by count. + SortedSet propsByFrequency = + new TreeSet(FREQUENCY_COMPARATOR); + + for (Iterator> it = + properties.entrySet().iterator(); it.hasNext(); ) { + Property a = it.next().getValue(); + if (a.canRename() && !reservedNames.contains(a.oldName)) { + propsByFrequency.add(a); + } else { + it.remove(); + + // If we're not renaming this, make sure we don't name something + // else to this name. + reservedNames.add(a.oldName); + } + } + + // Try and reuse as many names from the previous compilation as possible. + if (prevUsedRenameMap != null) { + reusePrototypeNames(propsByFrequency); + } + + // Generate new names. + NameGenerator nameGen = new NameGenerator(reservedNames, "", + reservedCharacters); + StringBuilder debug = new StringBuilder(); + for (Property a : propsByFrequency) { + if (a.newName == null) { + a.newName = nameGen.generateNextName(); + reservedNames.add(a.newName); + } + + debug.append(a.oldName).append(" => ").append(a.newName).append('\n'); + } + + compiler.addToDebugLog("JS property assignments:\n" + debug); + + // Update the string nodes. + boolean changed = false; + for (Node n : stringNodes) { + String oldName = n.getString(); + Property a = properties.get(oldName); + if (a != null && a.newName != null) { + n.setString(a.newName); + changed = changed || !a.newName.equals(oldName); + } + } + + if (changed) { + compiler.reportCodeChange(); + } + + compiler.setLifeCycleStage(LifeCycleStage.NORMALIZED_OBFUSCATED); + } + + /** + * Runs through the list of properties and tries to rename as many as possible + * with names that were used for them in the previous compilation. + * {@code reservedNames} is updated with the set of reused names. + * @param properties The set of properties to attempt to rename. + */ + private void reusePrototypeNames(Set properties) { + for (Property prop : properties) { + String prevName = prevUsedRenameMap.lookupNewName(prop.oldName); + if (prevName != null) { + if (reservedNames.contains(prevName)) { + continue; + } + + prop.newName = prevName; + reservedNames.add(prevName); + } + } + } + + /** + * Iterate through the nodes, collect all of the STRING nodes that are + * children of GETPROP or GETELEM and mark them as externs. + */ + private class ProcessExternedProperties extends AbstractPostOrderCallback { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.GETPROP: + case Token.GETELEM: + Node dest = n.getFirstChild().getNext(); + if (dest.isString()) { + reservedNames.add(dest.getString()); + } + } + } + } + + /** + * Iterate through the nodes, collect all of the STRING nodes that are + * children of GETPROP, GETELEM, or OBJLIT, and also count the number of + * times each STRING is referenced. + * + * Also collects OBJLIT assignments of prototypes as candidates for renaming. + */ + private class ProcessProperties extends AbstractPostOrderCallback { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.GETPROP: + case Token.GETELEM: + Node dest = n.getFirstChild().getNext(); + if (dest.isString()) { + String s = dest.getString(); + if (s.equals("prototype")) { + processPrototypeParent(parent, t.getInput()); + } else { + markPropertyAccessCandidate(dest, t.getInput()); + } + } + break; + case Token.OBJECTLIT: + if (!prototypeObjLits.contains(n)) { + // Object literals have their property name/value pairs as a flat + // list as their children. We want every other node in order to get + // only the property names. + for (Node child = n.getFirstChild(); + child != null; + child = child.getNext()) { + + if (TokenStream.isJSIdentifier(child.getString())) { + markObjLitPropertyCandidate(child, t.getInput()); + } + } + } + break; + } + } + + /** + * Processes the parent of a GETPROP prototype, which can either be + * another GETPROP (in the case of Foo.prototype.bar), or can be + * an assignment (in the case of Foo.prototype = ...). + */ + private void processPrototypeParent(Node n, CompilerInput input) { + switch (n.getType()) { + // Foo.prototype.getBar = function() { ... } + case Token.GETPROP: + case Token.GETELEM: + Node dest = n.getFirstChild().getNext(); + if (dest.isString()) { + markPrototypePropertyCandidate(dest, input); + } + break; + + // Foo.prototype = { "getBar" : function() { ... } } + case Token.ASSIGN: + case Token.CALL: + Node map; + if (n.isAssign()) { + map = n.getFirstChild().getNext(); + } else { + map = n.getLastChild(); + } + if (map.isObjectLit()) { + // Remember this node so that we can avoid processing it again when + // the traversal reaches it. + prototypeObjLits.add(map); + + for (Node key = map.getFirstChild(); + key != null; key = key.getNext()) { + if (TokenStream.isJSIdentifier(key.getString())) { + // May be STRING, GET, or SET + markPrototypePropertyCandidate(key, input); + } + } + } + break; + } + } + + /** + * Remembers the given String node and increments the property name's + * access count. + * + * @param n A STRING node + * @param input The Input that the node came from + */ + private void markPrototypePropertyCandidate(Node n, CompilerInput input) { + stringNodes.add(n); + getProperty(n.getString()).prototypeCount++; + } + + /** + * Remembers the given String node and increments the property name's + * access count. + * + * @param n A STRING node + * @param input The Input that the node came from + */ + private void markObjLitPropertyCandidate(Node n, CompilerInput input) { + stringNodes.add(n); + getProperty(n.getString()).objLitCount++; + } + + /** + * Remembers the given String node and increments the property name's + * access count. + * + * @param n A STRING node + * @param input The Input that the node came from + */ + private void markPropertyAccessCandidate(Node n, CompilerInput input) { + stringNodes.add(n); + getProperty(n.getString()).refCount++; + } + + /** + * Gets the current property for the given name, creating a new one if + * none exists. + */ + private Property getProperty(String name) { + Property prop = properties.get(name); + if (prop == null) { + prop = new Property(name); + properties.put(name, prop); + } + return prop; + } + } + + /** + * Gets the property renaming map. + * + * @return A mapping from original names to new names + */ + VariableMap getPropertyMap() { + ImmutableMap.Builder map = ImmutableMap.builder(); + for (Property p : properties.values()) { + if (p.newName != null) { + map.put(p.oldName, p.newName); + } + } + return new VariableMap(map.build()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RenameVars.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RenameVars.java new file mode 100644 index 0000000..27c69c3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RenameVars.java @@ -0,0 +1,567 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.Node; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import javax.annotation.Nullable; + +/** + * RenameVars renames all the variables names into short names, to reduce code + * size and also to obfuscate the code. + * + */ +final class RenameVars implements CompilerPass { + private final AbstractCompiler compiler; + + /** List of global NAME nodes */ + private final ArrayList globalNameNodes = new ArrayList(); + + /** List of local NAME nodes */ + private final ArrayList localNameNodes = new ArrayList(); + + /** + * Maps a name node to its pseudo name, null if we are not generating so + * there will not no overhead unless we are debugging. + */ + private final Map pseudoNameMap; + + /** Set of extern variable names */ + private final Set externNames = new HashSet(); + + /** Set of reserved variable names */ + private final Set reservedNames; + + /** The renaming map */ + private final Map renameMap = new HashMap(); + + /** The previously used rename map. */ + private final VariableMap prevUsedRenameMap; + + /** The global name prefix */ + private final String prefix; + + /** Counter for each assignment */ + private int assignmentCount = 0; + + /** Logs all name assignments */ + private StringBuilder assignmentLog; + + // Logic for bleeding functions, where the name leaks into the outer + // scope on IE but not on other browsers. + private Set localBleedingFunctions = Sets.newHashSet(); + private ArrayListMultimap localBleedingFunctionsPerScope = + ArrayListMultimap.create(); + + class Assignment { + final String oldName; + final int orderOfOccurrence; + String newName; + int count; // Number of times this is referenced + + Assignment(String name) { + this.oldName = name; + this.newName = null; + this.count = 0; + + // Represents the order at which a symbol appears in the source. + this.orderOfOccurrence = assignmentCount++; + } + + /** + * Assigns the new name. + */ + void setNewName(String newName) { + Preconditions.checkState(this.newName == null); + this.newName = newName; + } + } + + /** Maps an old name to a new name assignment */ + private final Map assignments = + new HashMap(); + + /** Whether renaming should apply to local variables only. */ + private final boolean localRenamingOnly; + + /** + * Whether function expression names should be preserved. Typically, for + * debugging purposes. + * + * @see NameAnonymousFunctions + */ + private boolean preserveFunctionExpressionNames; + + private final boolean shouldShadow; + + /** Characters that shouldn't be used in variable names. */ + private final char[] reservedCharacters; + + /** A prefix to distinguish temporary local names from global names */ + // TODO(user): No longer needs to be public when shadowing doesn't use it. + public static final String LOCAL_VAR_PREFIX = "L "; + + RenameVars(AbstractCompiler compiler, String prefix, + boolean localRenamingOnly, boolean preserveFunctionExpressionNames, + boolean generatePseudoNames, boolean shouldShadow, + VariableMap prevUsedRenameMap, + @Nullable char[] reservedCharacters, + @Nullable Set reservedNames) { + this.compiler = compiler; + this.prefix = prefix == null ? "" : prefix; + this.localRenamingOnly = localRenamingOnly; + this.preserveFunctionExpressionNames = preserveFunctionExpressionNames; + if (generatePseudoNames) { + this.pseudoNameMap = Maps.newHashMap(); + } else { + this.pseudoNameMap = null; + } + this.prevUsedRenameMap = prevUsedRenameMap; + this.reservedCharacters = reservedCharacters; + this.shouldShadow = shouldShadow; + if (reservedNames == null) { + this.reservedNames = Sets.newHashSet(); + } else { + this.reservedNames = Sets.newHashSet(reservedNames); + } + } + + /** + * Iterate through the nodes, collect all the NAME nodes that need to be + * renamed, and count how many times each variable name is referenced. + * + * There are 2 passes: + * - externs: keep track of the global vars in the externNames_ map. + * - source: keep track of all name references in globalNameNodes_, and + * localNameNodes_. + * + * To get shorter local variable renaming, we rename local variables to a + * temporary name "LOCAL_VAR_PREFIX + index" where index is the index of the + * variable declared in the local scope stack. + * e.g. + * Foo(fa, fb) { + * var c = function(d, e) { return fa; } + * } + * The indexes are: fa:0, fb:1, c:2, d:3, e:4 + * + * In that way, local variable names are reused in each global function. + * e.g. the final code might look like + * function x(a,b) { ... } + * function y(a,b,c) { ... } + */ + class ProcessVars extends AbstractPostOrderCallback + implements ScopedCallback { + private final boolean isExternsPass_; + + ProcessVars(boolean isExterns) { + isExternsPass_ = isExterns; + } + + @Override + public void enterScope(NodeTraversal t) { + if (t.inGlobalScope()) return; + + Iterator it = t.getScope().getVars(); + while (it.hasNext()) { + Var current = it.next(); + if (current.isBleedingFunction()) { + localBleedingFunctions.add(current); + localBleedingFunctionsPerScope.put( + t.getScope().getParent(), current); + } + } + } + + @Override + public void exitScope(NodeTraversal t) {} + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isName()) { + return; + } + + String name = n.getString(); + + // Ignore anonymous functions + if (name.length() == 0) { + return; + } + + // Is this local or Global? + // Bleeding functions should be treated as part of their outer + // scope, because IE has bugs in how it handles bleeding + // functions. + Scope.Var var = t.getScope().getVar(name); + boolean local = (var != null) && var.isLocal() && + (!var.scope.getParent().isGlobal() || + !var.isBleedingFunction()); + + // Are we renaming global variables? + if (!local && localRenamingOnly) { + reservedNames.add(name); + return; + } + + // Are we renaming function expression names? + if (preserveFunctionExpressionNames && var != null + && NodeUtil.isFunctionExpression(var.getParentNode())) { + reservedNames.add(name); + return; + } + + // Check if we can rename this. + if (!okToRenameVar(name, local)) { + if (local) { + // Blindly de-uniquify for the Prototype library for issue 103. + String newName = MakeDeclaredNamesUnique.ContextualRenameInverter + .getOrginalName(name); + if (!newName.equals(name)) { + n.setString(newName); + } + } + return; + } + + if (isExternsPass_) { + // Keep track of extern globals. + if (!local) { + externNames.add(name); + } + return; + } + + if (pseudoNameMap != null) { + recordPseudoName(n); + } + + if (local) { + // Local var: assign a new name + String tempName = LOCAL_VAR_PREFIX + getLocalVarIndex(var); + incCount(tempName); + localNameNodes.add(n); + n.setString(tempName); + } else if (var != null) { // Not an extern + // If it's global, increment global count + incCount(name); + globalNameNodes.add(n); + } + } + + // Increment count of an assignment + void incCount(String name) { + Assignment s = assignments.get(name); + if (s == null) { + s = new Assignment(name); + assignments.put(name, s); + } + s.count++; + } + } + + /** + * Sorts Assignment objects by their count, breaking ties by their order of + * occurrence in the source to ensure a deterministic total ordering. + */ + private static final Comparator FREQUENCY_COMPARATOR = + new Comparator() { + @Override + public int compare(Assignment a1, Assignment a2) { + if (a1.count != a2.count) { + return a2.count - a1.count; + } + // Break a tie using the order in which the variable first appears in + // the source. + return ORDER_OF_OCCURRENCE_COMPARATOR.compare(a1, a2); + } + }; + + /** + * Sorts Assignment objects by the order the variable name first appears in + * the source. + */ + private static final Comparator ORDER_OF_OCCURRENCE_COMPARATOR = + new Comparator() { + @Override + public int compare(Assignment a1, Assignment a2) { + return a1.orderOfOccurrence - a2.orderOfOccurrence; + } + }; + + @Override + public void process(Node externs, Node root) { + assignmentLog = new StringBuilder(); + + // Do variable reference counting. + NodeTraversal.traverse(compiler, externs, new ProcessVars(true)); + NodeTraversal.traverse(compiler, root, new ProcessVars(false)); + + // Make sure that new names don't overlap with extern names. + reservedNames.addAll(externNames); + + // Rename vars, sorted by frequency of occurrence to minimize code size. + SortedSet varsByFrequency = + new TreeSet(FREQUENCY_COMPARATOR); + varsByFrequency.addAll(assignments.values()); + + if (shouldShadow) { + new ShadowVariables( + compiler, assignments, varsByFrequency, pseudoNameMap).process( + externs, root); + } + + // First try to reuse names from an earlier compilation. + if (prevUsedRenameMap != null) { + reusePreviouslyUsedVariableMap(); + } + + // Assign names, sorted by descending frequency to minimize code size. + assignNames(varsByFrequency); + + boolean changed = false; + + // Rename the globals! + for (Node n : globalNameNodes) { + String newName = getNewGlobalName(n); + // Note: if newName is null, then oldName is an extern. + if (newName != null) { + n.setString(newName); + changed = true; + } + } + + // Rename the locals! + int count = 0; + for (Node n : localNameNodes) { + String newName = getNewLocalName(n); + if (newName != null) { + n.setString(newName); + changed = true; + } + count++; + } + + if (changed) { + compiler.reportCodeChange(); + } + + // Lastly, write the name assignments to the debug log. + compiler.addToDebugLog("JS var assignments:\n" + assignmentLog); + assignmentLog = null; + } + + private String getNewGlobalName(Node n) { + String oldName = n.getString(); + Assignment a = assignments.get(oldName); + if (a.newName != null && !a.newName.equals(oldName)) { + if (pseudoNameMap != null) { + return pseudoNameMap.get(n); + } + return a.newName; + } else { + return null; + } + } + + private String getNewLocalName(Node n) { + String oldTempName = n.getString(); + Assignment a = assignments.get(oldTempName); + if (!a.newName.equals(oldTempName)) { + if (pseudoNameMap != null) { + return pseudoNameMap.get(n); + } + return a.newName; + } + return null; + } + + private void recordPseudoName(Node n) { + // Variable names should be in a different name space than + // property pseudo names. + pseudoNameMap.put(n, '$' + n.getString() + "$$" ); + } + + /** + * Runs through the assignments and reuses as many names as possible from the + * previously used variable map. Updates reservedNames with the set of names + * that were reused. + */ + private void reusePreviouslyUsedVariableMap() { + for (Assignment a : assignments.values()) { + String prevNewName = prevUsedRenameMap.lookupNewName(a.oldName); + if (prevNewName == null || reservedNames.contains(prevNewName)) { + continue; + } + + if (a.oldName.startsWith(LOCAL_VAR_PREFIX) + || (!externNames.contains(a.oldName) + && prevNewName.startsWith(prefix))) { + reservedNames.add(prevNewName); + finalizeNameAssignment(a, prevNewName); + } + } + } + + /** + * Determines which new names to substitute for the original names. + */ + private void assignNames(Set varsToRename) { + NameGenerator globalNameGenerator = + new NameGenerator(reservedNames, prefix, reservedCharacters); + + // Local variables never need a prefix. + NameGenerator localNameGenerator = + prefix.isEmpty() ? globalNameGenerator : new NameGenerator( + reservedNames, "", reservedCharacters); + + // Generated names and the assignments for non-local vars. + List pendingAssignments = new ArrayList(); + List generatedNamesForAssignments = new ArrayList(); + + for (Assignment a : varsToRename) { + if (a.newName != null) { + continue; + } + + if (externNames.contains(a.oldName)) { + continue; + } + + String newName; + if (a.oldName.startsWith(LOCAL_VAR_PREFIX)) { + // For local variable, we make the assignment right away. + newName = localNameGenerator.generateNextName(); + finalizeNameAssignment(a, newName); + } else { + // For non-local variable, delay finalizing the name assignment + // until we know how many new names we'll have of length 2, 3, etc. + newName = globalNameGenerator.generateNextName(); + pendingAssignments.add(a); + generatedNamesForAssignments.add(newName); + } + reservedNames.add(newName); + } + + // Now that we have a list of generated names, and a list of variable + // Assignment objects, we assign the generated names to the vars as + // follows: + // 1) The most frequent vars get the shorter names. + // 2) If N number of vars are going to be assigned names of the same + // length, we assign the N names based on the order at which the vars + // first appear in the source. This makes the output somewhat less + // random, because symbols declared close together are assigned names + // that are quite similar. With this heuristic, the output is more + // compressible. + // For instance, the output may look like: + // var da = "..", ea = ".."; + // function fa() { .. } function ga() { .. } + + int numPendingAssignments = generatedNamesForAssignments.size(); + for (int i = 0; i < numPendingAssignments;) { + SortedSet varsByOrderOfOccurrence = + new TreeSet(ORDER_OF_OCCURRENCE_COMPARATOR); + + // Add k number of Assignment to the set, where k is the number of + // generated names of the same length. + int len = generatedNamesForAssignments.get(i).length(); + for (int j = i; j < numPendingAssignments + && generatedNamesForAssignments.get(j).length() == len; j++) { + varsByOrderOfOccurrence.add(pendingAssignments.get(j)); + } + + // Now, make the assignments + for (Assignment a : varsByOrderOfOccurrence) { + finalizeNameAssignment(a, generatedNamesForAssignments.get(i)); + ++i; + } + } + } + + /** + * Makes a final name assignment. + */ + private void finalizeNameAssignment(Assignment a, String newName) { + a.setNewName(newName); + + // Keep track of the mapping + renameMap.put(a.oldName, newName); + + // Log the mapping + assignmentLog.append(a.oldName).append(" => ").append(newName).append('\n'); + } + + /** + * Gets the variable map. + */ + VariableMap getVariableMap() { + return new VariableMap(ImmutableMap.copyOf(renameMap)); + } + + /** + * Determines whether a variable name is okay to rename. + */ + private boolean okToRenameVar(String name, boolean isLocal) { + return !compiler.getCodingConvention().isExported(name, isLocal); + } + + /** + * Returns the index within the scope stack. + * e.g. function Foo(a) { var b; function c(d) { } } + * a = 0, b = 1, c = 2, d = 3 + */ + private int getLocalVarIndex(Var v) { + int num = v.index; + Scope s = v.scope.getParent(); + if (s == null) { + throw new IllegalArgumentException("Var is not local"); + } + + boolean isBleedingIntoScope = s.getParent() != null && + localBleedingFunctions.contains(v); + + while (s.getParent() != null) { + if (isBleedingIntoScope) { + num += localBleedingFunctionsPerScope.get(s).indexOf(v) + 1; + isBleedingIntoScope = false; + } else { + num += localBleedingFunctionsPerScope.get(s).size(); + } + + num += s.getVarCount(); + s = s.getParent(); + } + return num; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReorderConstantExpression.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReorderConstantExpression.java new file mode 100644 index 0000000..394e390 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReorderConstantExpression.java @@ -0,0 +1,58 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + * Reorder constant expression hoping for a better compression. + * ex. x === 0 -> 0 === x + * After reordering, expressions like 0 === x and 0 === y may have higher + * compression together than their original counterparts. + * + */ +class ReorderConstantExpression extends AbstractPeepholeOptimization { + + // TODO(user): Rename this pass to PeepholeReorderConstantExpression + // to follow our naming convention. + @Override + Node optimizeSubtree(Node subtree) { + // if the operator is symmetric + if (NodeUtil.isSymmetricOperation(subtree) + || NodeUtil.isRelationalOperation(subtree)) { + // right value is immutable and left is not + if (NodeUtil.isImmutableValue(subtree.getLastChild()) + && !NodeUtil.isImmutableValue(subtree.getFirstChild())) { + + // if relational, get the inverse operator. + if (NodeUtil.isRelationalOperation(subtree)){ + int inverseOperator = NodeUtil.getInverseOperator(subtree.getType()); + subtree.setType(inverseOperator); + } + + // swap them + Node firstNode = subtree.getFirstChild().detachFromParent(); + Node lastNode = subtree.getLastChild().detachFromParent(); + + subtree.addChildrenToFront(lastNode); + subtree.addChildrenToBack(firstNode); + reportCodeChange(); + } + } + return subtree; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReplaceCssNames.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReplaceCssNames.java new file mode 100644 index 0000000..caaaf08 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReplaceCssNames.java @@ -0,0 +1,263 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_TYPE; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.JSType; + +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +/** + * ReplaceCssNames replaces occurrences of goog.getCssName('foo') with + * a shorter version from the passed in renaming map. There are two + * styles of operation: for 'BY_WHOLE' we look up the whole string in the + * renaming map. For 'BY_PART', all the class name's components, + * separated by '-', are renamed individually and then recombined. + * + * Given the renaming map: + * { + * once: 'a', + * upon: 'b', + * atime: 'c', + * long: 'd', + * time: 'e', + * ago: 'f' + * } + * + * The following outputs are expected with the 'BY_PART' renaming style: + * + * goog.getCssName('once') -> 'a' + * goog.getCssName('once-upon-atime') -> 'a-b-c' + * + * var baseClass = goog.getCssName('long-time'); + * el.className = goog.getCssName(baseClass, 'ago'); + * -> + * var baseClass = 'd-e'; + * el.className = baseClass + '-f'; + * + * However if we have the following renaming map with the 'BY_WHOLE' renaming style: + * { + * once: 'a', + * upon-atime: 'b', + * long-time: 'c', + * ago: 'd' + * } + * + * Then we would expect: + * + * goog.getCssName('once') -> 'a' + * + * var baseClass = goog.getCssName('long-time'); + * el.className = goog.getCssName(baseClass, 'ago'); + * -> + * var baseClass = 'c'; + * el.className = baseClass + '-d'; + * + * In addition, the CSS names before replacement can optionally be gathered. + * + */ +class ReplaceCssNames implements CompilerPass { + + static final String GET_CSS_NAME_FUNCTION = "goog.getCssName"; + + static final DiagnosticType INVALID_NUM_ARGUMENTS_ERROR = + DiagnosticType.error("JSC_GETCSSNAME_NUM_ARGS", + "goog.getCssName called with \"{0}\" arguments, expected 1 or 2."); + + static final DiagnosticType STRING_LITERAL_EXPECTED_ERROR = + DiagnosticType.error("JSC_GETCSSNAME_STRING_LITERAL_EXPECTED", + "goog.getCssName called with invalid argument, string literal " + + "expected. Was \"{0}\"."); + + static final DiagnosticType UNEXPECTED_STRING_LITERAL_ERROR = + DiagnosticType.error("JSC_GETCSSNAME_UNEXPECTED_STRING_LITERAL", + "goog.getCssName called with invalid arguments, string literal " + + "passed as first of two arguments. Did you mean " + + "goog.getCssName(\"{0}-{1}\")?"); + + static final DiagnosticType UNKNOWN_SYMBOL_WARNING = + DiagnosticType.warning("JSC_GETCSSNAME_UNKNOWN_CSS_SYMBOL", + "goog.getCssName called with unrecognized symbol \"{0}\" in class " + + "\"{1}\"."); + + + private final AbstractCompiler compiler; + + private final Map cssNames; + + private CssRenamingMap symbolMap; + + private final Set whitelist; + + private final JSType nativeStringType; + + ReplaceCssNames(AbstractCompiler compiler, + @Nullable Map cssNames, + @Nullable Set whitelist) { + this.compiler = compiler; + this.cssNames = cssNames; + this.whitelist = whitelist; + this.nativeStringType = compiler.getTypeRegistry() + .getNativeType(STRING_TYPE); + } + + @Override + public void process(Node externs, Node root) { + // The CssRenamingMap may not have been available from the compiler when + // this ReplaceCssNames pass was constructed, so getCssRenamingMap() should + // only be called before this pass is actually run. + symbolMap = getCssRenamingMap(); + + NodeTraversal.traverse(compiler, root, new Traversal()); + } + + @VisibleForTesting + protected CssRenamingMap getCssRenamingMap() { + return compiler.getCssRenamingMap(); + } + + private class Traversal extends AbstractPostOrderCallback { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isCall() && + GET_CSS_NAME_FUNCTION.equals(n.getFirstChild().getQualifiedName())) { + int count = n.getChildCount(); + Node first = n.getFirstChild().getNext(); + switch (count) { + case 2: + // Replace the function call with the processed argument. + if (first.isString()) { + processStringNode(t, first); + n.removeChild(first); + parent.replaceChild(n, first); + compiler.reportCodeChange(); + } else { + compiler.report(t.makeError(n, STRING_LITERAL_EXPECTED_ERROR, + Token.name(first.getType()))); + } + break; + + case 3: + // Replace function call with concatenation of two args. It's + // assumed the first arg has already been processed. + + Node second = first.getNext(); + + if (!second.isString()) { + compiler.report(t.makeError(n, STRING_LITERAL_EXPECTED_ERROR, + Token.name(second.getType()))); + } else if (first.isString()) { + compiler.report(t.makeError( + n, UNEXPECTED_STRING_LITERAL_ERROR, + first.getString(), second.getString())); + } else { + processStringNode(t, second); + n.removeChild(first); + Node replacement = IR.add(first, + IR.string("-" + second.getString()) + .copyInformationFrom(second)) + .copyInformationFrom(n); + replacement.setJSType(nativeStringType); + parent.replaceChild(n, replacement); + compiler.reportCodeChange(); + } + break; + + default: + compiler.report(t.makeError( + n, INVALID_NUM_ARGUMENTS_ERROR, String.valueOf(count))); + } + } + } + + /** + * Processes a string argument to goog.getCssName(). The string will be + * renamed based off the symbol map. If there is no map or any part of the + * name can't be renamed, a warning is reported to the compiler and the node + * is left unchanged. + * + * If the type is unexpected then an error is reported to the compiler. + * + * @param t The node traversal. + * @param n The string node to process. + */ + private void processStringNode(NodeTraversal t, Node n) { + String name = n.getString(); + if (whitelist != null && whitelist.contains(name)) { + // We apply the whitelist before splitting on dashes, and not after. + // External substitution maps should do the same. + return; + } + String[] parts = name.split("-"); + if (symbolMap != null) { + String replacement = null; + switch (symbolMap.getStyle()) { + case BY_WHOLE: + replacement = symbolMap.get(name); + if (replacement == null) { + compiler.report( + t.makeError(n, UNKNOWN_SYMBOL_WARNING, name, name)); + return; + } + break; + case BY_PART: + String[] replaced = new String[parts.length]; + for (int i = 0; i < parts.length; i++) { + String part = symbolMap.get(parts[i]); + if (part == null) { + // If we can't encode all parts, don't encode any of it. + compiler.report( + t.makeError(n, UNKNOWN_SYMBOL_WARNING, parts[i], name)); + return; + } + replaced[i] = part; + } + replacement = Joiner.on("-").join(replaced); + break; + default: + throw new IllegalStateException( + "Unknown replacement style: " + symbolMap.getStyle()); + } + n.setString(replacement); + } + if (cssNames != null) { + // We still want to collect statistics even if we've already + // done the full replace. The statistics are collected on a + // per-part basis. + for (int i = 0; i < parts.length; i++) { + Integer count = cssNames.get(parts[i]); + if (count == null) { + count = Integer.valueOf(0); + } + cssNames.put(parts[i], count.intValue() + 1); + } + } + } + } + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReplaceIdGenerators.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReplaceIdGenerators.java new file mode 100644 index 0000000..c6e4271 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReplaceIdGenerators.java @@ -0,0 +1,390 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.ImmutableBiMap; +import com.google.common.collect.Maps; +import com.google.debugging.sourcemap.Base64; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * Replaces calls to id generators with ids. + * + * Use this to get unique and short ids. + * + */ +class ReplaceIdGenerators implements CompilerPass { + static final DiagnosticType NON_GLOBAL_ID_GENERATOR_CALL = + DiagnosticType.error( + "JSC_NON_GLOBAL_ID_GENERATOR_CALL", + "Id generator call must be in the global scope"); + + static final DiagnosticType CONDITIONAL_ID_GENERATOR_CALL = + DiagnosticType.error( + "JSC_CONDITIONAL_ID_GENERATOR_CALL", + "Id generator call must be unconditional"); + + static final DiagnosticType CONFLICTING_GENERATOR_TYPE = + DiagnosticType.error( + "JSC_CONFLICTING_ID_GENERATOR_TYPE", + "Id generator can only be one of consistent, inconsistent, or stable."); + + static final DiagnosticType INVALID_GENERATOR_ID_MAPPING = + DiagnosticType.error( + "JSC_INVALID_GENERATOR_ID_MAPPING", + "Invalid generator id mapping. {0}"); + + private final AbstractCompiler compiler; + private final Map nameGenerators; + private final Map> consistNameMap; + + private final Map> idGeneratorMaps; + private final Map> previousMap; + + private final boolean generatePseudoNames; + + public ReplaceIdGenerators( + AbstractCompiler compiler, Set idGens, + boolean generatePseudoNames, + String previousMapSerialized) { + this.compiler = compiler; + this.generatePseudoNames = generatePseudoNames; + nameGenerators = Maps.newLinkedHashMap(); + idGeneratorMaps = Maps.newLinkedHashMap(); + consistNameMap = Maps.newLinkedHashMap(); + + Map> previousMap; + previousMap = parsePreviousResults(previousMapSerialized); + this.previousMap = previousMap; + + if (idGens != null) { + for (String gen : idGens) { + nameGenerators.put( + gen, createNameSupplier(RenameStrategy.INCONSISTENT, previousMap.get(gen))); + idGeneratorMaps.put(gen, Maps.newLinkedHashMap()); + } + } + } + + private enum RenameStrategy { + CONSISTENT, + INCONSISTENT, + STABLE + } + + private static interface NameSupplier { + String getName(String id, String name); + RenameStrategy getRenameStrategy(); + } + + private static class ObfuscatedNameSuppier implements NameSupplier { + private final NameGenerator generator; + private final Map previousMappings; + private RenameStrategy renameStrategy; + + public ObfuscatedNameSuppier( + RenameStrategy renameStrategy, BiMap previousMappings) { + this.previousMappings = previousMappings.inverse(); + this.generator = + new NameGenerator(previousMappings.keySet(), "", null); + this.renameStrategy = renameStrategy; + } + + @Override + public String getName(String id, String name) { + String newName = previousMappings.get(id); + if (newName == null) { + newName = generator.generateNextName(); + } + return newName; + } + + @Override + public RenameStrategy getRenameStrategy() { + return renameStrategy; + } + } + + private static class PseudoNameSuppier implements NameSupplier { + private int counter = 0; + private RenameStrategy renameStrategy; + + public PseudoNameSuppier(RenameStrategy renameStrategy) { + this.renameStrategy = renameStrategy; + } + + @Override + public String getName(String id, String name) { + if (renameStrategy == RenameStrategy.INCONSISTENT) { + return name + "$" + counter++; + } + return name + "$0"; + } + + @Override + public RenameStrategy getRenameStrategy() { + return renameStrategy; + } + } + + private static class StableNameSupplier implements NameSupplier { + @Override + public String getName(String id, String name) { + return Base64.base64EncodeInt(name.hashCode()); + } + @Override + public RenameStrategy getRenameStrategy() { + return RenameStrategy.STABLE; + } + } + + private NameSupplier createNameSupplier( + RenameStrategy renameStrategy, BiMap previousMappings) { + previousMappings = previousMappings != null ? + previousMappings : + ImmutableBiMap.of(); + if (renameStrategy == RenameStrategy.STABLE) { + return new StableNameSupplier(); + } else if (generatePseudoNames) { + return new PseudoNameSuppier(renameStrategy); + } else { + return new ObfuscatedNameSuppier(renameStrategy, previousMappings); + } + } + + private class GatherGenerators extends AbstractPostOrderCallback { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + JSDocInfo doc = n.getJSDocInfo(); + if (doc == null) { + return; + } + + int numGeneratorAnnotations = + (doc.isConsistentIdGenerator() ? 1 : 0) + + (doc.isIdGenerator() ? 1 : 0) + + (doc.isStableIdGenerator() ? 1 : 0); + if (numGeneratorAnnotations == 0) { + return; + } else if (numGeneratorAnnotations > 1) { + compiler.report(t.makeError(n, CONFLICTING_GENERATOR_TYPE)); + } + + String name = null; + if (n.isAssign()) { + name = n.getFirstChild().getQualifiedName(); + } else if (n.isVar()) { + name = n.getFirstChild().getString(); + } else if (n.isFunction()){ + name = n.getFirstChild().getString(); + if (name.isEmpty()) { + return; + } + } + + if (doc.isConsistentIdGenerator()) { + consistNameMap.put(name, Maps.newLinkedHashMap()); + nameGenerators.put( + name, createNameSupplier(RenameStrategy.CONSISTENT, previousMap.get(name))); + } else if (doc.isStableIdGenerator()) { + nameGenerators.put( + name, createNameSupplier(RenameStrategy.STABLE, previousMap.get(name))); + } else { + nameGenerators.put( + name, createNameSupplier(RenameStrategy.INCONSISTENT, previousMap.get(name))); + } + idGeneratorMaps.put(name, Maps.newLinkedHashMap()); + } + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, new GatherGenerators()); + if (!nameGenerators.isEmpty()) { + NodeTraversal.traverse(compiler, root, new ReplaceGenerators()); + } + } + + private class ReplaceGenerators extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isCall()) { + return; + } + + String callName = n.getFirstChild().getQualifiedName(); + NameSupplier nameGenerator = nameGenerators.get(callName); + if (nameGenerator == null) { + return; + } + + if (!t.inGlobalScope() && + nameGenerator.getRenameStrategy() == RenameStrategy.INCONSISTENT) { + // Warn about calls not in the global scope. + compiler.report(t.makeError(n, NON_GLOBAL_ID_GENERATOR_CALL)); + return; + } + + if (nameGenerator.getRenameStrategy() == RenameStrategy.INCONSISTENT) { + for (Node ancestor : n.getAncestors()) { + if (NodeUtil.isControlStructure(ancestor)) { + // Warn about conditional calls. + compiler.report(t.makeError(n, CONDITIONAL_ID_GENERATOR_CALL)); + return; + } + } + } + + Node id = n.getFirstChild().getNext(); + + // TODO(user): Error on id not a string literal. + if (!id.isString()) { + return; + } + + Map idGeneratorMap = idGeneratorMaps.get(callName); + String rename = null; + + String name = id.getString(); + String instanceId = getIdForGeneratorNode( + nameGenerator.getRenameStrategy() == RenameStrategy.CONSISTENT, id); + if (nameGenerator.getRenameStrategy() == RenameStrategy.CONSISTENT) { + Map entry = consistNameMap.get(callName); + rename = entry.get(instanceId); + if (rename == null) { + rename = nameGenerator.getName(instanceId, name); + entry.put(instanceId, rename); + } + } else { + rename = nameGenerator.getName(instanceId, name); + } + + parent.replaceChild(n, IR.string(rename)); + idGeneratorMap.put(rename, instanceId); + + compiler.reportCodeChange(); + } + } + + /** + * @return The serialize map of generators and their ids and their + * replacements. + */ + public String getSerializedIdMappings() { + StringBuilder sb = new StringBuilder(); + for (Map.Entry> replacements : + idGeneratorMaps.entrySet()) { + if (!replacements.getValue().isEmpty()) { + sb.append("["); + sb.append(replacements.getKey()); + sb.append("]\n\n"); + for (Map.Entry replacement : + replacements.getValue().entrySet()) { + sb.append(replacement.getKey()); + sb.append(':'); + sb.append(replacement.getValue()); + sb.append("\n"); + } + sb.append("\n"); + } + } + return sb.toString(); + } + + private Map> parsePreviousResults( + String serializedMap) { + + // + // The expected format looks like this: + // + // [generatorName] + // someId:someFile:theLine:theColumn + // + // + + if (serializedMap == null || serializedMap.isEmpty()) { + return Collections.emptyMap(); + } + + Map> resultMap = Maps.newHashMap(); + BufferedReader reader = new BufferedReader(new StringReader(serializedMap)); + BiMap currentSectionMap = null; + + String line; + int lineIndex = 0; + try { + while ((line = reader.readLine()) != null) { + lineIndex++; + if (line.isEmpty()) { + continue; + } + if (line.charAt(0) == '[') { + String currentSection = line.substring(1, line.length() - 1); + currentSectionMap = resultMap.get(currentSection); + if (currentSectionMap == null) { + currentSectionMap = HashBiMap.create(); + resultMap.put(currentSection, currentSectionMap); + } else { + reportInvalidLine(line, lineIndex); + return Collections.emptyMap(); + } + } else { + int split = line.indexOf(':'); + if (split != -1) { + String name = line.substring(0, split); + String location = line.substring(split + 1, line.length()); + currentSectionMap.put(name, location); + } else { + reportInvalidLine(line, lineIndex); + return Collections.emptyMap(); + } + } + } + } catch (IOException e) { + JSError.make(INVALID_GENERATOR_ID_MAPPING, e.getMessage()); + } + return resultMap; + } + + private void reportInvalidLine(String line, int lineIndex) { + JSError.make(INVALID_GENERATOR_ID_MAPPING, + "line(" + line + "): " + lineIndex); + } + + String getIdForGeneratorNode(boolean consistent, Node n) { + Preconditions.checkState(n.isString()); + if (consistent) { + return n.getString(); + } else { + return n.getSourceFileName() + ':' + n.getLineno() + ":" + n.getCharno(); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReplaceMessages.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReplaceMessages.java new file mode 100644 index 0000000..bd435ec --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReplaceMessages.java @@ -0,0 +1,376 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Iterator; + +import javax.annotation.Nullable; + +/** + * ReplaceMessages replaces user-visible messages with alternatives. + * It uses Google specific JsMessageVisitor implementation. + * + */ +class ReplaceMessages extends JsMessageVisitor { + private final MessageBundle bundle; + private final boolean strictReplacement; + + static final DiagnosticType BUNDLE_DOES_NOT_HAVE_THE_MESSAGE = + DiagnosticType.error("JSC_BUNDLE_DOES_NOT_HAVE_THE_MESSAGE", + "Message with id = {0} could not be found in replacement bundle"); + + ReplaceMessages(AbstractCompiler compiler, MessageBundle bundle, + boolean checkDuplicatedMessages, JsMessage.Style style, + boolean strictReplacement) { + + super(compiler, checkDuplicatedMessages, style, bundle.idGenerator()); + + this.bundle = bundle; + this.strictReplacement = strictReplacement; + } + + @Override + void processMessageFallback( + Node callNode, JsMessage message1, JsMessage message2) { + boolean isFirstMessageTranslated = + (bundle.getMessage(message1.getId()) != null); + boolean isSecondMessageTranslated = + (bundle.getMessage(message2.getId()) != null); + Node replacementNode = + isSecondMessageTranslated && !isFirstMessageTranslated ? + callNode.getChildAtIndex(2) : callNode.getChildAtIndex(1); + callNode.getParent().replaceChild(callNode, + replacementNode.detachFromParent()); + } + + @Override + void processJsMessage(JsMessage message, + JsMessageDefinition definition) { + + // Get the replacement. + JsMessage replacement = bundle.getMessage(message.getId()); + if (replacement == null) { + if (strictReplacement) { + compiler.report(JSError.make(message.getSourceName(), + definition.getMessageNode(), BUNDLE_DOES_NOT_HAVE_THE_MESSAGE, + message.getId())); + // Fallback to the default message + return; + } else { + // In case if it is not a strict replacement we could leave original + // message. + replacement = message; + } + } + + // Replace the message. + Node newValue; + Node msgNode = definition.getMessageNode(); + try { + newValue = getNewValueNode(replacement, msgNode); + } catch (MalformedException e) { + compiler.report(JSError.make(message.getSourceName(), + e.getNode(), MESSAGE_TREE_MALFORMED, e.getMessage())); + newValue = msgNode; + } + + if (newValue != msgNode) { + newValue.copyInformationFromForTree(msgNode); + definition.getMessageParentNode().replaceChild(msgNode, newValue); + compiler.reportCodeChange(); + } + } + + /** + * Constructs a node representing a message's value, or, if possible, just + * modifies {@code origValueNode} so that it accurately represents the + * message's value. + * + * @param message a message + * @param origValueNode the message's original value node + * @return a Node that can replace {@code origValueNode} + * + * @throws MalformedException if the passed node's subtree structure is + * not as expected + */ + private Node getNewValueNode(JsMessage message, Node origValueNode) + throws MalformedException { + switch (origValueNode.getType()) { + case Token.FUNCTION: + // The message is a function. Modify the function node. + updateFunctionNode(message, origValueNode); + return origValueNode; + case Token.STRING: + // The message is a simple string. Modify the string node. + String newString = message.toString(); + if (!origValueNode.getString().equals(newString)) { + origValueNode.setString(newString); + compiler.reportCodeChange(); + } + return origValueNode; + case Token.ADD: + // The message is a simple string. Create a string node. + return IR.string(message.toString()); + case Token.CALL: + // The message is a function call. Replace it with a string expression. + return replaceCallNode(message, origValueNode); + default: + throw new MalformedException( + "Expected FUNCTION, STRING, or ADD node; found: " + + origValueNode.getType(), origValueNode); + } + } + + /** + * Updates the descendants of a FUNCTION node to represent a message's value. + *

      + * The tree looks something like: + *

      +   * function
      +   *  |-- name
      +   *  |-- lp
      +   *  |   |-- name 
      +   *  |    -- name 
      +   *   -- block
      +   *      |
      +   *       --return
      +   *           |
      +   *            --add
      +   *               |-- string foo
      +   *                -- name 
      +   * 
      + * + * @param message a message + * @param functionNode the message's original FUNCTION value node + * + * @throws MalformedException if the passed node's subtree structure is + * not as expected + */ + private void updateFunctionNode(JsMessage message, Node functionNode) + throws MalformedException { + checkNode(functionNode, Token.FUNCTION); + Node nameNode = functionNode.getFirstChild(); + checkNode(nameNode, Token.NAME); + Node argListNode = nameNode.getNext(); + checkNode(argListNode, Token.PARAM_LIST); + Node oldBlockNode = argListNode.getNext(); + checkNode(oldBlockNode, Token.BLOCK); + + Iterator iterator = message.parts().iterator(); + Node valueNode = iterator.hasNext() + ? constructAddOrStringNode(iterator, argListNode) + : IR.string(""); + Node newBlockNode = IR.block(IR.returnNode(valueNode)); + + // TODO(user): checkTreeEqual is overkill. I am in process of rewriting + // these functions. + if (newBlockNode.checkTreeEquals(oldBlockNode) != null) { + newBlockNode.copyInformationFromForTree(oldBlockNode); + functionNode.replaceChild(oldBlockNode, newBlockNode); + compiler.reportCodeChange(); + } + } + + /** + * Creates a parse tree corresponding to the remaining message parts in + * an iteration. The result will contain only STRING nodes, NAME nodes + * (corresponding to placeholder references), and/or ADD nodes used to + * combine the other two types. + * + * @param partsIterator an iterator over message parts + * @param argListNode an LP node whose children are valid placeholder names + * @return the root of the constructed parse tree + * + * @throws MalformedException if {@code partsIterator} contains a + * placeholder reference that does not correspond to a valid argument in + * the arg list + */ + private Node constructAddOrStringNode(Iterator partsIterator, + Node argListNode) + throws MalformedException { + CharSequence part = partsIterator.next(); + Node partNode = null; + if (part instanceof JsMessage.PlaceholderReference) { + JsMessage.PlaceholderReference phRef = + (JsMessage.PlaceholderReference) part; + + for (Node node : argListNode.children()) { + if (node.isName()) { + String arg = node.getString(); + + // We ignore the case here because the transconsole only supports + // uppercase placeholder names, but function arguments in JavaScript + // code can have mixed case. + if (arg.equalsIgnoreCase(phRef.getName())) { + partNode = IR.name(arg); + } + } + } + + if (partNode == null) { + throw new MalformedException( + "Unrecognized message placeholder referenced: " + phRef.getName(), + argListNode); + } + } else { + // The part is just a string literal. + partNode = IR.string(part.toString()); + } + + if (partsIterator.hasNext()) { + return IR.add(partNode, + constructAddOrStringNode(partsIterator, argListNode)); + } else { + return partNode; + } + } + + /** + * Replaces a CALL node with an inlined message value. + *

      + * The call tree looks something like: + *

      +   * call
      +   *  |-- getprop
      +   *  |   |-- name 'goog'
      +   *  |   +-- string 'getMsg'
      +   *  |
      +   *  |-- string 'Hi {$userName}! Welcome to {$product}.'
      +   *  +-- objlit
      +   *      |-- string 'userName'
      +   *      |-- name 'someUserName'
      +   *      |-- string 'product'
      +   *      +-- call
      +   *          +-- name 'getProductName'
      +   * 
      +   * 

      + * For that example, we'd return: + *

      +   * add
      +   *  |-- string 'Hi '
      +   *  +-- add
      +   *      |-- name someUserName
      +   *      +-- add
      +   *          |-- string '! Welcome to '
      +   *          +-- add
      +   *              |-- call
      +   *              |   +-- name 'getProductName'
      +   *              +-- string '.'
      +   * 
      + * @param message a message + * @param callNode the message's original CALL value node + * @return a STRING node, or an ADD node that does string concatenation, if + * the message has one or more placeholders + * + * @throws MalformedException if the passed node's subtree structure is + * not as expected + */ + private Node replaceCallNode(JsMessage message, Node callNode) + throws MalformedException { + checkNode(callNode, Token.CALL); + Node getPropNode = callNode.getFirstChild(); + checkNode(getPropNode, Token.GETPROP); + Node stringExprNode = getPropNode.getNext(); + checkStringExprNode(stringExprNode); + Node objLitNode = stringExprNode.getNext(); + + // Build the replacement tree. + return constructStringExprNode(message.parts().iterator(), objLitNode); + } + + /** + * Creates a parse tree corresponding to the remaining message parts in an + * iteration. The result consists of one or more STRING nodes, placeholder + * replacement value nodes (which can be arbitrary expressions), and ADD + * nodes. + * + * @param parts an iterator over message parts + * @param objLitNode an OBJLIT node mapping placeholder names to values + * @return the root of the constructed parse tree + * + * @throws MalformedException if {@code parts} contains a placeholder + * reference that does not correspond to a valid placeholder name + */ + private Node constructStringExprNode(Iterator parts, + Node objLitNode) throws MalformedException { + + CharSequence part = parts.next(); + Node partNode = null; + if (part instanceof JsMessage.PlaceholderReference) { + JsMessage.PlaceholderReference phRef = + (JsMessage.PlaceholderReference) part; + + // The translated message is null + if (objLitNode == null) { + throw new MalformedException("Empty placeholder value map " + + "for a translated message with placeholders.", objLitNode); + } + + for (Node key = objLitNode.getFirstChild(); key != null; + key = key.getNext()) { + if (key.getString().equals(phRef.getName())) { + Node valueNode = key.getFirstChild(); + partNode = valueNode.cloneTree(); + } + } + + if (partNode == null) { + throw new MalformedException( + "Unrecognized message placeholder referenced: " + phRef.getName(), + objLitNode); + } + } else { + // The part is just a string literal. + partNode = IR.string(part.toString()); + } + + if (parts.hasNext()) { + return IR.add(partNode, + constructStringExprNode(parts, objLitNode)); + } else { + return partNode; + } + } + + /** + * Checks that a node is a valid string expression (either a string literal + * or a concatenation of string literals). + * + * @throws IllegalArgumentException if the node is null or the wrong type + */ + private void checkStringExprNode(@Nullable Node node) { + if (node == null) { + throw new IllegalArgumentException("Expected a string; found: null"); + } + switch (node.getType()) { + case Token.STRING: + break; + case Token.ADD: + Node c = node.getFirstChild(); + checkStringExprNode(c); + checkStringExprNode(c.getNext()); + break; + default: + throw new IllegalArgumentException( + "Expected a string; found: " + node.getType()); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReplaceMessagesForChrome.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReplaceMessagesForChrome.java new file mode 100644 index 0000000..add629a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReplaceMessagesForChrome.java @@ -0,0 +1,113 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.JsMessage.PlaceholderReference; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Collections; +import java.util.List; + + +/** + * Replaces user-visible messages with appropriate calls to + * chrome.i18n.getMessage. The first argument to getMessage is the id of the + * message, as a string. If the message contains placeholders, the second + * argument is an array of the values being used for the placeholders, in the + * order they appear in the source code. + * + */ +class ReplaceMessagesForChrome extends JsMessageVisitor { + + ReplaceMessagesForChrome(AbstractCompiler compiler, + JsMessage.IdGenerator idGenerator, + boolean checkDuplicatedMessages, JsMessage.Style style) { + + super(compiler, checkDuplicatedMessages, style, idGenerator); + } + + private static Node getChromeI18nGetMessageNode(String messageId) { + Node chromeI18n = IR.getprop(IR.name("chrome"), IR.string("i18n")); + Node getMessage = IR.getprop(chromeI18n, IR.string("getMessage")); + return IR.call(getMessage, IR.string(messageId)); + } + + @Override + protected void processJsMessage( + JsMessage message, JsMessageDefinition definition) { + try { + Node msgNode = definition.getMessageNode(); + Node newValue = getNewValueNode(msgNode, message); + newValue.copyInformationFromForTree(msgNode); + + definition.getMessageParentNode().replaceChild(msgNode, newValue); + compiler.reportCodeChange(); + } catch (MalformedException e) { + compiler.report(JSError.make(message.getSourceName(), e.getNode(), + MESSAGE_TREE_MALFORMED, e.getMessage())); + } + } + + private Node getNewValueNode(Node origNode, JsMessage message) + throws MalformedException { + Node newValueNode = getChromeI18nGetMessageNode(message.getId()); + + if (!message.placeholders().isEmpty()) { + Node placeholderValues = origNode.getLastChild(); + checkNode(placeholderValues, Token.OBJECTLIT); + + // Output the placeholders, sorted alphabetically by placeholder name, + // regardless of what order they appear in the original message. + List placeholderNames = Lists.newArrayList(); + for (CharSequence cs : message.parts()) { + if (cs instanceof PlaceholderReference) { + String placeholderName = ((PlaceholderReference) cs).getName(); + placeholderNames.add(placeholderName); + } + } + Collections.sort(placeholderNames); + + Node placeholderValueArray = IR.arraylit(); + for (String name : placeholderNames) { + Node value = getPlaceholderValue(placeholderValues, name); + if (value == null) { + throw new MalformedException( + "No value was provided for placeholder " + name, + origNode); + } + placeholderValueArray.addChildToBack(value); + } + newValueNode.addChildToBack(placeholderValueArray); + } + + newValueNode.copyInformationFromForTree(origNode); + return newValueNode; + } + + private Node getPlaceholderValue( + Node placeholderValues, String placeholderName) { + for (Node key : placeholderValues.children()) { + if (key.getString().equals(placeholderName)) { + return key.getFirstChild().cloneTree(); + } + } + return null; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReplaceStrings.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReplaceStrings.java new file mode 100644 index 0000000..1d4ac50 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ReplaceStrings.java @@ -0,0 +1,499 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeRegistry; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Replaces JavaScript strings in the list of supplied methods with shortened + * forms. Useful for replacing debug message such as: throw new + * Error("Something bad happened"); with generated codes like: throw new + * Error("a"); This makes the compiled JavaScript smaller and prevents us from + * leaking details about the source code. + * + * Based in concept on the work by Jared Jacobs. + */ +class ReplaceStrings extends AbstractPostOrderCallback + implements CompilerPass { + static final DiagnosticType BAD_REPLACEMENT_CONFIGURATION = + DiagnosticType.warning( + "JSC_BAD_REPLACEMENT_CONFIGURATION", + "Bad replacement configuration."); + + private final String DEFAULT_PLACEHOLDER_TOKEN = "`"; + private final String placeholderToken; + private static final String REPLACE_ONE_MARKER = "?"; + private static final String REPLACE_ALL_MARKER = "*"; + + private final AbstractCompiler compiler; + private final JSTypeRegistry registry; + + // + private final Map functions = Maps.newHashMap(); + private final Multimap methods = HashMultimap.create(); + private final NameGenerator nameGenerator; + private final Map results = Maps.newLinkedHashMap(); + + /** + * Describes a function to look for a which parameters to replace. + */ + private class Config { + // TODO(johnlenz): Support name "groups" so that unrelated strings can + // reuse strings. For example, event-id can reuse the names used for logger + // classes. + final String name; + final int parameter; + static final int REPLACE_ALL_VALUE = 0; + + Config(String name, int parameter) { + this.name = name; + this.parameter = parameter; + } + } + + /** + * Describes a replacement that occurred. + */ + class Result { + // The original message with non-static content replaced with + // {@code placeholderToken}. + public final String original; + public final String replacement; + public final List replacementLocations = Lists.newLinkedList(); + + Result(String original, String replacement) { + this.original = original; + this.replacement = replacement; + } + + void addLocation(Node n) { + replacementLocations.add(new Location( + n.getSourceFileName(), + n.getLineno(), n.getCharno())); + } + } + + /** Represent a source location where a replacement occurred. */ + class Location { + public final String sourceFile; + public final int line; + public final int column; + Location(String sourceFile, int line, int column) { + this.sourceFile = sourceFile; + this.line = line; + this.column = column; + } + } + + /** + * @param placeholderToken Separator to use between string parts. Used to replace + * non-static string content. + * @param functionsToInspect A list of function configurations in the form of + * function($,,,) + * or + * class.prototype.method($,,,) + * @param blacklisted A set of names that should not be used as replacement + * strings. Useful to prevent unwanted strings for appearing in the + * final output. + * where '$' is used to indicate which parameter should be replaced. + */ + ReplaceStrings( + AbstractCompiler compiler, String placeholderToken, + List functionsToInspect, + Set blacklisted, + VariableMap previousMappings) { + this.compiler = compiler; + this.placeholderToken = placeholderToken.isEmpty() + ? DEFAULT_PLACEHOLDER_TOKEN : placeholderToken; + this.registry = compiler.getTypeRegistry(); + + Iterable reservedNames = blacklisted; + if (previousMappings != null) { + Set previous = + previousMappings.getOriginalNameToNewNameMap().keySet(); + reservedNames = Iterables.concat(blacklisted, previous); + initMapping(previousMappings, blacklisted); + } + this.nameGenerator = createNameGenerator(reservedNames); + + // Initialize the map of functions to inspect for renaming candidates. + parseConfiguration(functionsToInspect); + } + + private void initMapping( + VariableMap previousVarMap, Set reservedNames) { + Map previous = previousVarMap.getOriginalNameToNewNameMap(); + for (Map.Entry entry : previous.entrySet()) { + String key = entry.getKey(); + if (!reservedNames.contains(key)) { + String value = entry.getValue(); + results.put(value, new Result(value, key)); + } + } + } + + static final Predicate USED_RESULTS = new Predicate() { + @Override + public boolean apply(Result result) { + // The list of locations may be empty if the map + // was pre-populated from a previous map. + return !result.replacementLocations.isEmpty(); + } + }; + + // Get the list of all replacements performed. + List getResult() { + return ImmutableList.copyOf( + Iterables.filter(results.values(), USED_RESULTS)); + } + + // Get the list of replaces as a VariableMap + VariableMap getStringMap() { + ImmutableMap.Builder map = ImmutableMap.builder(); + for (Result result : Iterables.filter(results.values(), USED_RESULTS)) { + map.put(result.replacement, result.original); + } + + VariableMap stringMap = new VariableMap(map.build()); + return stringMap; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + // TODO(johnlenz): Determine if it is necessary to support ".call" or + // ".apply". + switch (n.getType()) { + case Token.NEW: // e.g. new Error('msg'); + case Token.CALL: // e.g. Error('msg'); + Node calledFn = n.getFirstChild(); + + // Look for calls to static functions. + String name = calledFn.getQualifiedName(); + if (name != null) { + Config config = findMatching(name); + if (config != null) { + doSubstitutions(t, config, n); + return; + } + } + + // Look for calls to class methods. + if (NodeUtil.isGet(calledFn)) { + Node rhs = calledFn.getLastChild(); + if (rhs.isName() || rhs.isString()) { + String methodName = rhs.getString(); + Collection classes = methods.get(methodName); + if (classes != null) { + Node lhs = calledFn.getFirstChild(); + if (lhs.getJSType() != null) { + JSType type = lhs.getJSType().restrictByNotNullOrUndefined(); + Config config = findMatchingClass(type, classes); + if (config != null) { + doSubstitutions(t, config, n); + return; + } + } + } + } + } + break; + } + } + + /** + * @param name The function name to find. + * @return The Config object for the name or null if no match was found. + */ + private Config findMatching(String name) { + Config config = functions.get(name); + if (config == null) { + name = name.replace('$', '.'); + config = functions.get(name); + } + return config; + } + + /** + * @return The Config object for the class match the specified type or null + * if no match was found. + */ + private Config findMatchingClass( + JSType callClassType, Collection declarationNames) { + if (!callClassType.isNoObjectType() && !callClassType.isUnknownType()) { + for (String declarationName : declarationNames) { + String className = getClassFromDeclarationName(declarationName); + JSType methodClassType = registry.getType(className); + if (methodClassType != null + && callClassType.isSubtype(methodClassType)) { + return functions.get(declarationName); + } + } + } + return null; + } + + /** + * Replace the parameters specified in the config, if possible. + */ + private void doSubstitutions(NodeTraversal t, Config config, Node n) { + Preconditions.checkState( + n.isNew() || n.isCall()); + + if (config.parameter != Config.REPLACE_ALL_VALUE) { + // Note: the first child is the function, but the parameter id is 1 based. + Node arg = n.getChildAtIndex(config.parameter); + if (arg != null) { + replaceExpression(t, arg, n); + } + } else { + // Replace all parameters. + Node firstParam = n.getFirstChild().getNext(); + for (Node arg = firstParam; arg != null; arg = arg.getNext()) { + arg = replaceExpression(t, arg, n); + } + } + } + + /** + * Replaces a string expression with a short encoded string expression. + * + * @param t The traversal + * @param expr The expression node + * @param parent The expression node's parent + * @return The replacement node (or the original expression if no replacement + * is made) + */ + private Node replaceExpression(NodeTraversal t, Node expr, Node parent) { + Node replacement; + String key = null; + String replacementString; + switch (expr.getType()) { + case Token.STRING: + key = expr.getString(); + replacementString = getReplacement(key); + replacement = IR.string(replacementString); + break; + case Token.ADD: + StringBuilder keyBuilder = new StringBuilder(); + Node keyNode = IR.string(""); + replacement = buildReplacement(expr, keyNode, keyBuilder); + key = keyBuilder.toString(); + replacementString = getReplacement(key); + keyNode.setString(replacementString); + break; + case Token.NAME: + // If the referenced variable is a constant, use its value. + Scope.Var var = t.getScope().getVar(expr.getString()); + if (var != null && var.isConst()) { + Node value = var.getInitialValue(); + if (value != null && value.isString()) { + key = value.getString(); + replacementString = getReplacement(key); + replacement = IR.string(replacementString); + break; + } + } + return expr; + default: + // This may be a function call or a variable reference. We don't + // replace these. + return expr; + } + + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(replacementString); + recordReplacement(expr, key, replacementString); + + parent.replaceChild(expr, replacement); + compiler.reportCodeChange(); + return replacement; + } + + /** + * Get a replacement string for the provide key text. + */ + private String getReplacement(String key) { + Result result = results.get(key); + if (result != null) { + return result.replacement; + } + + String replacement = nameGenerator.generateNextName(); + result = new Result(key, replacement); + results.put(key, result); + return replacement; + } + + /** + * Record the location the replacement was made. + */ + private void recordReplacement(Node n, String key, String replacement) { + Result result = results.get(key); + Preconditions.checkState(result != null); + + result.addLocation(n); + } + + /** + * Builds a replacement abstract syntax tree for the string expression {@code + * expr}. Appends any string literal values that are encountered to + * {@code keyBuilder}, to build the expression's replacement key. + * + * @param expr A JS expression that evaluates to a string value + * @param prefix The JS expression to which {@code expr}'s replacement is + * logically being concatenated. It is a partial solution to the + * problem at hand and will either be this method's return value or a + * descendant of it. + * @param keyBuilder A builder of the string expression's replacement key + * @return The abstract syntax tree that should replace {@code expr} + */ + private Node buildReplacement( + Node expr, Node prefix, StringBuilder keyBuilder) { + switch (expr.getType()) { + case Token.ADD: + Node left = expr.getFirstChild(); + Node right = left.getNext(); + prefix = buildReplacement(left, prefix, keyBuilder); + return buildReplacement(right, prefix, keyBuilder); + case Token.STRING: + keyBuilder.append(expr.getString()); + return prefix; + default: + keyBuilder.append(placeholderToken); + prefix = IR.add(prefix, IR.string(placeholderToken)); + return IR.add(prefix, expr.cloneTree()); + } + } + + /** + * From a provide name extract the method name. + */ + private String getMethodFromDeclarationName(String fullDeclarationName) { + String[] parts = fullDeclarationName.split("\\.prototype\\."); + Preconditions.checkState(parts.length == 1 || parts.length == 2); + if (parts.length == 2) { + return parts[1]; + } + return null; + } + + /** + * From a provide name extract the class name. + */ + private String getClassFromDeclarationName(String fullDeclarationName) { + String[] parts = fullDeclarationName.split("\\.prototype\\."); + Preconditions.checkState(parts.length == 1 || parts.length == 2); + if (parts.length == 2) { + return parts[0]; + } + return null; + } + + /** + * Build the data structures need by this pass from the provided + * list of functions and methods. + */ + private void parseConfiguration(List functionsToInspect) { + for (String function : functionsToInspect) { + Config config = parseConfiguration(function); + functions.put(config.name, config); + + String method = getMethodFromDeclarationName(config.name); + if (method != null) { + methods.put(method, config.name); + } + } + } + + /** + * Convert the provide string into a Config. The string can be a static function: + * foo(,,?) + * foo.bar(?) + * or a class method: + * foo.prototype.bar(?) + * And is allowed to either replace all parameters using "*" or one parameter "?". + * "," is used as a placeholder for ignored parameters. + */ + private Config parseConfiguration(String function) { + // Looks like this function_name(,$,) + int first = function.indexOf('('); + int last = function.indexOf(')'); + + // TODO(johnlenz): Make parsing precondition checks JSErrors reports. + Preconditions.checkState(first != -1 && last != -1); + + String name = function.substring(0, first); + String params = function.substring(first+1, last); + + int paramCount = 0; + int replacementParameter = -1; + String[] parts = params.split(","); + for (String param : parts) { + paramCount++; + if (param.equals(REPLACE_ALL_MARKER)) { + Preconditions.checkState(paramCount == 1 && parts.length == 1); + replacementParameter = Config.REPLACE_ALL_VALUE; + } else if (param.equals(REPLACE_ONE_MARKER)) { + // TODO(johnlenz): Support multiple. + Preconditions.checkState(replacementParameter == -1); + replacementParameter = paramCount; + } else { + // TODO(johnlenz): report an error. + Preconditions.checkState(param.isEmpty(), "Unknown marker", param); + } + } + + Preconditions.checkState(replacementParameter != -1); + return new Config(name, replacementParameter); + } + + /** + * Use a name generate to create names so the names overlap with the names + * used for variable and properties. + */ + private static NameGenerator createNameGenerator(Iterable reserved) { + final String namePrefix = ""; + final char[] reservedChars = new char[0]; + return new NameGenerator( + ImmutableSet.copyOf(reserved), namePrefix, reservedChars); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RescopeGlobalSymbols.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RescopeGlobalSymbols.java new file mode 100644 index 0000000..72b9653 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RescopeGlobalSymbols.java @@ -0,0 +1,302 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableSet; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.AbstractShallowStatementCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Finds all references to global symbols and rewrites them to be property + * accesses to a special object with the same name as the global symbol. + * + * Given the name of the global object is NS + *
       var a = 1; function b() { return a }
      + * becomes + *
       NS.a = 1; NS.b = function b() { return NS.a }
      + * + * This allows splitting code into modules that depend on each other's + * global symbols, without using polluting JavaScript's global scope with those + * symbols. + * + *

      This compile step requires moveFunctionDeclarations to be turned on + * to guarantee semantics. + * + *

      For lots of examples, see the unit test. + * + * + */ +class RescopeGlobalSymbols implements CompilerPass { + + // Appended to variables names that conflict with globalSymbolNamespace. + private static final String DISAMBIGUATION_SUFFIX = "$"; + private static final String WINDOW = "window"; + private static final Set SPECIAL_EXTERNS = + ImmutableSet.of(WINDOW, "eval", "arguments"); + + private final AbstractCompiler compiler; + private final String globalSymbolNamespace; + private final boolean addExtern; + + RescopeGlobalSymbols(AbstractCompiler compiler, String globalSymbolNamespace, + boolean addExtern) { + this.compiler = compiler; + this.globalSymbolNamespace = globalSymbolNamespace; + this.addExtern = addExtern; + } + + RescopeGlobalSymbols(AbstractCompiler compiler, + String globalSymbolNamespace) { + this(compiler, globalSymbolNamespace, true); + } + + private void addExternForGlobalSymbolNamespace() { + Node varNode = IR.var(IR.name(globalSymbolNamespace)); + CompilerInput input = compiler.newExternInput( + "{RescopeGlobalSymbolsNamespaceVar}"); + input.getAstRoot(compiler).addChildrenToBack(varNode); + compiler.reportCodeChange(); + } + + @Override + public void process(Node externs, Node root) { + // Make the name of the globalSymbolNamespace an extern. + if (addExtern) { + addExternForGlobalSymbolNamespace(); + } + // Rewrite all references to global symbols to properties of a + // single symbol by: + // (If necessary the 3 traversals could be combined. They are left + // separate for readability reasons.) + // 1. turning global named function statements into var assignments. + NodeTraversal.traverse(compiler, root, + new RewriteGlobalFunctionStatementsToVarAssignmentsCallback()); + // 2. rewriting all references to be property accesses of the single symbol. + NodeTraversal.traverse(compiler, root, new RewriteScopeCallback()); + // 3. removing the var from every statement in global scope. + NodeTraversal.traverse(compiler, root, new RemoveGlobalVarCallback()); + + // Extra pass which makes all extern global symbols reference window + // explicitly. + NodeTraversal.traverse(compiler, root, + new MakeExternsReferenceWindowExplicitly()); + } + + /** + * Rewrites function statements to var statements + assignment. + * + *

      function test(){}
      + * becomes + *
      var test = function (){}
      + * + * After this traversal, the special case of global function statements + * can be ignored. + */ + private class RewriteGlobalFunctionStatementsToVarAssignmentsCallback + extends AbstractShallowStatementCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (NodeUtil.isFunctionDeclaration(n)) { + String name = NodeUtil.getFunctionName(n); + n.getFirstChild().setString(""); + Node prev = parent.getChildBefore(n); + n.detachFromParent(); + Node var = NodeUtil.newVarNode(name, n); + if (prev == null) { + parent.addChildToFront(var); + } else { + parent.addChildAfter(var, prev); + } + compiler.reportCodeChange(); + } + } + } + + /** + * Visits each NAME token and checks whether it refers to a global variable. + * If yes, rewrites the name to be a property access on the + * "globalSymbolNamespace". + * + *
      var a = 1, b = 2, c = 3;
      + * becomes + *
      var NS.a = 1, NS.b = 2, NS.c = 4
      + * (The var token is removed in a later traversal.) + * + *
      a + b
      + * becomes + *
      NS.a + NS.b
      + * + *
      a()
      + * becomes + *
      (0,NS.a)()
      + * Notice the special syntax here to preserve the *this* semantics in the + * function call. + */ + private class RewriteScopeCallback extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isName()) { + return; + } + String name = n.getString(); + // Ignore anonymous functions + if (parent.isFunction() && name.length() == 0) { + return; + } + Scope.Var var = t.getScope().getVar(name); + if (var == null) { + return; + } + // Don't touch externs. + if (var.isExtern()) { + return; + } + // When the globalSymbolNamespace is used as a local variable name + // add suffix to avoid shadowing the namespace. Also add a suffix + // if a name starts with the name of the globalSymbolNamespace and + // the suffix. + if (!var.isExtern() && (name.equals(globalSymbolNamespace) || + name.indexOf(globalSymbolNamespace + DISAMBIGUATION_SUFFIX) == 0)) { + n.setString(name + DISAMBIGUATION_SUFFIX); + compiler.reportCodeChange(); + } + // We only care about global vars. + if (!var.isGlobal()) { + return; + } + Node nameNode = var.getNameNode(); + // The exception variable (e in try{}catch(e){}) should not be rewritten. + if (nameNode != null && nameNode.getParent() != null && + nameNode.getParent().isCatch()) { + return; + } + replaceSymbol(n, name); + } + + private void replaceSymbol(Node node, String name) { + Node parent = node.getParent(); + Node replacement = IR.getprop( + IR.name(globalSymbolNamespace).srcref(node), + IR.string(name).srcref(node)); + replacement.srcref(node); + if (node.hasChildren()) { + // var declaration list: var a = 1, b = 2; + Node assign = IR.assign(replacement, + node.removeFirstChild()); + parent.replaceChild(node, assign); + } else { + parent.replaceChild(node, replacement); + } + compiler.reportCodeChange(); + } + } + + /** + * Removes every occurrence of var that declares a global variable. + * + *
      var NS.a = 1, NS.b = 2;
      + * becomes + *
      NS.a = 1; NS.b = 2;
      + * + *
      for (var a = 0, b = 0;;)
      + * becomes + *
      for (NS.a = 0, NS.b = 0;;)
      + * + * Declarations without assignments are optimized away: + *
      var a = 1, b;
      + * becomes + *
      NS.a = 1
      + */ + private class RemoveGlobalVarCallback extends + AbstractShallowStatementCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isVar()) { + return; + } + List commas = new ArrayList(); + List interestingChildren = new ArrayList(); + // Filter out declarations without assignments. + // As opposed to regular var nodes, there are always assignments + // because the previous traversal in RewriteScopeCallback creates + // them. + for (Node c : n.children()) { + if (c.isAssign() || + parent.isFor()) { + interestingChildren.add(c); + } + } + for (Node c : interestingChildren) { + if (parent.isFor() && parent.getFirstChild() == n) { + commas.add(c.cloneTree()); + } else { + // Var statement outside of for-loop. + Node expr = IR.exprResult(c.cloneTree()).srcref(c); + parent.addChildBefore(expr, n); + } + } + if (commas.size() > 0) { + Node comma = joinOnComma(commas, n); + parent.addChildBefore(comma, n); + } + // Remove the var node. + parent.removeChild(n); + compiler.reportCodeChange(); + } + + private Node joinOnComma(List commas, Node source) { + Node comma = commas.get(0); + for (int i = 1; i < commas.size(); i++) { + Node nextComma = IR.comma(comma, commas.get(i)); + nextComma.copyInformationFrom(source); + comma = nextComma; + } + return comma; + } + } + + /** + * Rewrites extern names to be explicit children of window instead of only + * implicitly referencing it. + * This enables injecting window into a scope and make all global symbol + * depend on the injected object. + */ + private class MakeExternsReferenceWindowExplicitly extends + AbstractPostOrderCallback { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isName()) { + return; + } + String name = n.getString(); + Scope.Var var = t.getScope().getVar(name); + if (name.length() > 0 && (var == null || var.isExtern()) && + !globalSymbolNamespace.equals(name) && + !SPECIAL_EXTERNS.contains(name)) { + parent.replaceChild(n, IR.getprop(IR.name(WINDOW), IR.string(name)) + .srcrefTree(n)); + compiler.reportCodeChange(); + } + } + + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Result.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Result.java new file mode 100644 index 0000000..a191320 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Result.java @@ -0,0 +1,71 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import java.util.Map; + +/** + * Compilation results + */ +public class Result { + public final boolean success; + public final JSError[] errors; + public final JSError[] warnings; + public final String debugLog; + public final VariableMap variableMap; + public final VariableMap propertyMap; + public final VariableMap namedAnonFunctionMap; + public final VariableMap stringMap; + public final FunctionInformationMap functionInformationMap; + public final SourceMap sourceMap; + public final Map cssNames; + public final String externExport; + public final String idGeneratorMap; + + Result(JSError[] errors, JSError[] warnings, String debugLog, + VariableMap variableMap, VariableMap propertyMap, + VariableMap namedAnonFunctionMap, + VariableMap stringMap, + FunctionInformationMap functionInformationMap, + SourceMap sourceMap, String externExport, + Map cssNames, String idGeneratorMap) { + this.success = errors.length == 0; + this.errors = errors; + this.warnings = warnings; + this.debugLog = debugLog; + this.variableMap = variableMap; + this.propertyMap = propertyMap; + this.namedAnonFunctionMap = namedAnonFunctionMap; + this.stringMap = stringMap; + this.functionInformationMap = functionInformationMap; + this.sourceMap = sourceMap; + this.externExport = externExport; + this.cssNames = cssNames; + this.idGeneratorMap = idGeneratorMap; + } + + // Visible for testing only. + public Result(JSError[] errors, JSError[] warnings, String debugLog, + VariableMap variableMap, VariableMap propertyMap, + VariableMap namedAnonFunctionMap, + FunctionInformationMap functionInformationMap, + SourceMap sourceMap, String externExport) { + this(errors, warnings, debugLog, variableMap, propertyMap, + namedAnonFunctionMap, null, functionInformationMap, sourceMap, + externExport, null, null); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RhinoErrorReporter.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RhinoErrorReporter.java new file mode 100644 index 0000000..88ea3fa --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RhinoErrorReporter.java @@ -0,0 +1,213 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableMap; +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.rhino.ErrorReporter; +import com.google.javascript.rhino.ScriptRuntime; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Pattern; + +/** + * An error reporter for serializing Rhino errors into our error format. + * @author nicksantos@google.com (Nick Santos) + */ +class RhinoErrorReporter { + + static final DiagnosticType PARSE_ERROR = + DiagnosticType.error("JSC_PARSE_ERROR", "Parse error. {0}"); + + static final DiagnosticType TYPE_PARSE_ERROR = + DiagnosticType.warning("JSC_TYPE_PARSE_ERROR", "{0}"); + + // Special-cased errors, so that they can be configured via the + // warnings API. + static final DiagnosticType TRAILING_COMMA = + DiagnosticType.error("JSC_TRAILING_COMMA", + "Parse error. IE8 (and below) will parse trailing commas in " + + "array and object literals incorrectly. " + + "If you are targeting newer versions of JS, " + + "set the appropriate language_in option."); + + static final DiagnosticType DUPLICATE_PARAM = + DiagnosticType.error("JSC_DUPLICATE_PARAM", "Parse error. {0}"); + + static final DiagnosticType BAD_JSDOC_ANNOTATION = + DiagnosticType.warning("JSC_BAD_JSDOC_ANNOTATION", "Parse error. {0}"); + + static final DiagnosticType MISPLACED_TYPE_ANNOTATION = + DiagnosticType.warning("JSC_MISPLACED_TYPE_ANNOTATION", + "Type annotations are not allowed here. " + + "Are you missing parentheses?"); + + // A map of Rhino messages to their DiagnosticType. + private final Map typeMap; + + final AbstractCompiler compiler; + + /** + * For each message such as "Not a good use of {0}", replace the place + * holder {0} with a wild card that matches all possible strings. + * Also put the any non-place-holder in quotes for regex matching later. + */ + private Pattern replacePlaceHolders(String s) { + s = Pattern.quote(s); + return Pattern.compile(s.replaceAll("\\{\\d+\\}", "\\\\E.*\\\\Q")); + } + + private RhinoErrorReporter(AbstractCompiler compiler) { + this.compiler = compiler; + typeMap = ImmutableMap.of( + // Trailing comma + replacePlaceHolders( + com.google.javascript.rhino.head.ScriptRuntime + .getMessage0("msg.extra.trailing.comma")), + TRAILING_COMMA, + + // Duplicate parameter + replacePlaceHolders( + com.google.javascript.rhino.head.ScriptRuntime + .getMessage0("msg.dup.parms")), + DUPLICATE_PARAM, + + // Unknown @annotations. + replacePlaceHolders(ScriptRuntime.getMessage0("msg.bad.jsdoc.tag")), + BAD_JSDOC_ANNOTATION, + + Pattern.compile("^Type annotations are not allowed here.*"), + MISPLACED_TYPE_ANNOTATION, + + // Type annotation errors. + Pattern.compile("^Bad type annotation.*"), + TYPE_PARSE_ERROR + ); + } + + public static com.google.javascript.rhino.head.ErrorReporter + forNewRhino(AbstractCompiler compiler) { + return new NewRhinoErrorReporter(compiler); + } + + public static ErrorReporter forOldRhino(AbstractCompiler compiler) { + return new OldRhinoErrorReporter(compiler); + } + + void warningAtLine(String message, String sourceName, int line, + int lineOffset) { + compiler.report( + makeError(message, sourceName, line, lineOffset, CheckLevel.WARNING)); + } + + void errorAtLine(String message, String sourceName, int line, + int lineOffset) { + compiler.report( + makeError(message, sourceName, line, lineOffset, CheckLevel.ERROR)); + } + + private JSError makeError(String message, String sourceName, int line, + int lineOffset, CheckLevel defaultLevel) { + + // Try to see if the message is one of the rhino errors we want to + // expose as DiagnosticType by matching it with the regex key. + for (Entry entry : typeMap.entrySet()) { + if (entry.getKey().matcher(message).matches()) { + return JSError.make( + sourceName, line, lineOffset, entry.getValue(), message); + } + } + + return JSError.make(sourceName, line, lineOffset, defaultLevel, + PARSE_ERROR, message); + } + + private static class OldRhinoErrorReporter extends RhinoErrorReporter + implements ErrorReporter { + + private OldRhinoErrorReporter(AbstractCompiler compiler) { + super(compiler); + } + + @Override + public void error(String message, String sourceName, int line, + int lineOffset) { + super.errorAtLine(message, sourceName, line, lineOffset); + } + + @Override + public void warning(String message, String sourceName, int line, + int lineOffset) { + super.warningAtLine(message, sourceName, line, lineOffset); + } + } + + private static class NewRhinoErrorReporter extends RhinoErrorReporter + implements com.google.javascript.rhino.head.ast.IdeErrorReporter { + + private NewRhinoErrorReporter(AbstractCompiler compiler) { + super(compiler); + } + + @Override + public com.google.javascript.rhino.head.EvaluatorException + runtimeError(String message, String sourceName, int line, + String lineSource, int lineOffset) { + return new com.google.javascript.rhino.head.EvaluatorException( + message, sourceName, line, lineSource, lineOffset); + } + + @Override + public void error(String message, String sourceName, int line, + String sourceLine, int lineOffset) { + super.errorAtLine(message, sourceName, line, lineOffset); + } + + @Override + public void error(String message, String sourceName, + int offset, int length) { + int line = 1; + int column = 0; + SourceFile file = this.compiler.getSourceFileByName(sourceName); + if (file != null) { + line = file.getLineOfOffset(offset); + column = file.getColumnOfOffset(offset); + } + super.errorAtLine(message, sourceName, line, column); + } + + @Override + public void warning(String message, String sourceName, int line, + String sourceLine, int lineOffset) { + super.warningAtLine(message, sourceName, line, lineOffset); + } + + @Override + public void warning(String message, String sourceName, + int offset, int length) { + int line = 1; + int column = 0; + SourceFile file = this.compiler.getSourceFileByName(sourceName); + if (file != null) { + line = file.getLineOfOffset(offset); + column = file.getColumnOfOffset(offset); + } + super.errorAtLine(message, sourceName, line, column); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RuntimeTypeCheck.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RuntimeTypeCheck.java new file mode 100644 index 0000000..773b7ad --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/RuntimeTypeCheck.java @@ -0,0 +1,385 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.StaticSourceFile; + +import java.util.Collection; +import java.util.Comparator; +import java.util.TreeSet; + +import javax.annotation.Nullable; + +/** + * Inserts run-time type assertions. + * + *

      We add markers to user-defined interfaces and classes in order to check if + * an object conforms to that type. + * + *

      For each function, we insert a run-time type assertion for each parameter + * and return value for which the compiler has a type. + * + *

      The JavaScript code which implements the type assertions is in + * js/runtime-type-check.js. + * + */ +class RuntimeTypeCheck implements CompilerPass { + + private static final Comparator ALPHA = new Comparator() { + @Override + public int compare(JSType t1, JSType t2) { + return getName(t1).compareTo(getName(t2)); + } + + private String getName(JSType type) { + if (type.isInstanceType()) { + return ((ObjectType) type).getReferenceName(); + } else if (type.isNullType() + || type.isBooleanValueType() + || type.isNumberValueType() + || type.isStringValueType() + || type.isVoidType()) { + return type.toString(); + } else { + // Type unchecked at runtime, so we don't care about the sorting order. + return ""; + } + } + }; + + private final AbstractCompiler compiler; + private final String logFunction; + + RuntimeTypeCheck(AbstractCompiler compiler, @Nullable String logFunction) { + this.compiler = compiler; + this.logFunction = logFunction; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, new AddMarkers(compiler)); + NodeTraversal.traverse(compiler, root, new AddChecks()); + addBoilerplateCode(); + } + + /** + * Inserts marker properties for user-defined interfaces and classes. + * + *

      For example, for a class C, we add + * {@code C.prototype['instance_of__C']}, and for each interface I it + * implements , we add {@code C.prototype['implements__I']}. + * + *

      Since interfaces are not a run-time JS concept, we use these markers to + * recognize an interface implementation at runtime. We also use markers for + * user-defined classes, so that we can easily recognize them independently of + * which module they are defined in and whether the module is loaded. + */ + private static class AddMarkers + extends NodeTraversal.AbstractPostOrderCallback { + + private final AbstractCompiler compiler; + + private AddMarkers(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isFunction()) { + visitFunction(t, n); + } + } + + private void visitFunction(NodeTraversal t, Node n) { + FunctionType funType = n.getJSType().toMaybeFunctionType(); + if (funType != null && !funType.isConstructor()) { + return; + } + + Node nodeToInsertAfter = findNodeToInsertAfter(n); + + nodeToInsertAfter = addMarker(funType, nodeToInsertAfter, null); + + TreeSet stuff = Sets.newTreeSet(ALPHA); + Iterables.addAll(stuff, funType.getAllImplementedInterfaces()); + for (ObjectType interfaceType : stuff) { + nodeToInsertAfter = + addMarker(funType, nodeToInsertAfter, interfaceType); + } + } + + private Node addMarker( + FunctionType funType, + Node nodeToInsertAfter, + @Nullable ObjectType interfaceType) { + + if (funType.getSource() == null) { + return nodeToInsertAfter; + } + + String className = NodeUtil.getFunctionName(funType.getSource()); + + // This can happen with anonymous classes declared with the type + // {@code Function}. + if (className == null) { + return nodeToInsertAfter; + } + + Node classNode = NodeUtil.newQualifiedNameNode( + compiler.getCodingConvention(), className); + + Node marker = IR.string( + interfaceType == null ? + "instance_of__" + className : + "implements__" + interfaceType.getReferenceName()); + + Node assign = IR.exprResult(IR.assign( + IR.getelem( + IR.getprop( + classNode, + IR.string("prototype")), marker), + IR.trueNode())); + + nodeToInsertAfter.getParent().addChildAfter(assign, nodeToInsertAfter); + compiler.reportCodeChange(); + nodeToInsertAfter = assign; + return nodeToInsertAfter; + } + + /** + * Find the node to insert the markers after. Typically, this node + * corresponds to the constructor declaration, but we want to skip any of + * the white-listed function calls. + * + * @param n the constructor function node + * @return the node to insert after + */ + private Node findNodeToInsertAfter(Node n) { + Node nodeToInsertAfter = findEnclosingConstructorDeclaration(n); + + Node next = nodeToInsertAfter.getNext(); + while (next != null && isClassDefiningCall(next)) { + nodeToInsertAfter = next; + next = nodeToInsertAfter.getNext(); + } + + return nodeToInsertAfter; + } + + private Node findEnclosingConstructorDeclaration(Node n) { + while (!n.getParent().isScript() && !n.getParent().isBlock()) { + n = n.getParent(); + } + return n; + } + + private boolean isClassDefiningCall(Node next) { + return NodeUtil.isExprCall(next) && + compiler.getCodingConvention().getClassesDefinedByCall( + next.getFirstChild()) != null; + } + } + + /** + * Insert calls to the run-time type checking function {@code checkType}, which + * takes an expression to check and a list of checkers (one of which must + * match). It returns the expression back to facilitate checking of return + * values. We have checkers for value types, class types (user-defined and + * externed), and interface types. + */ + private class AddChecks + extends NodeTraversal.AbstractPostOrderCallback { + + private AddChecks() { + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isFunction()) { + visitFunction(t, n); + } else if (n.isReturn()) { + visitReturn(t, n); + } + } + + /** + * Insert checks for the parameters of the function. + */ + private void visitFunction(NodeTraversal t, Node n) { + FunctionType funType = JSType.toMaybeFunctionType(n.getJSType()); + Node block = n.getLastChild(); + Node paramName = NodeUtil.getFunctionParameters(n).getFirstChild(); + Node insertionPoint = null; + + // To satisfy normalization constraints, the type checking must be + // added after any inner function declarations. + for (Node next = block.getFirstChild(); + next != null && NodeUtil.isFunctionDeclaration(next); + next = next.getNext()) { + insertionPoint = next; + } + + for (Node paramType : funType.getParameters()) { + // Can this ever happen? + if (paramName == null) { + return; + } + + Node checkNode = createCheckTypeCallNode( + paramType.getJSType(), paramName.cloneTree()); + + if (checkNode == null) { + // We don't know how to check this parameter type. + paramName = paramName.getNext(); + continue; + } + + checkNode = IR.exprResult(checkNode); + if (insertionPoint == null) { + block.addChildToFront(checkNode); + } else { + block.addChildAfter(checkNode, insertionPoint); + } + + compiler.reportCodeChange(); + paramName = paramName.getNext(); + insertionPoint = checkNode; + } + } + + private void visitReturn(NodeTraversal t, Node n) { + Node function = t.getEnclosingFunction(); + FunctionType funType = function.getJSType().toMaybeFunctionType(); + + Node retValue = n.getFirstChild(); + if (retValue == null) { + return; + } + + Node checkNode = createCheckTypeCallNode( + funType.getReturnType(), retValue.cloneTree()); + + if (checkNode == null) { + return; + } + + n.replaceChild(retValue, checkNode); + compiler.reportCodeChange(); + } + + /** + * Creates a function call to check that the given expression matches the + * given type at runtime. + * + *

      For example, if the type is {@code (string|Foo)}, the function call is + * {@code checkType(expr, [valueChecker('string'), classChecker('Foo')])}. + * + * @return the function call node or {@code null} if the type is not checked + */ + private Node createCheckTypeCallNode(JSType type, Node expr) { + Node arrayNode = IR.arraylit(); + Collection alternates; + if (type.isUnionType()) { + alternates = Sets.newTreeSet(ALPHA); + Iterables.addAll(alternates, type.toMaybeUnionType().getAlternates()); + } else { + alternates = ImmutableList.of(type); + } + for (JSType alternate : alternates) { + Node checkerNode = createCheckerNode(alternate); + if (checkerNode == null) { + return null; + } + arrayNode.addChildToBack(checkerNode); + } + return IR.call(jsCode("checkType"), expr, arrayNode); + } + + /** + * Creates a node which evaluates to a checker for the given type (which + * must not be a union). We have checkers for value types, classes and + * interfaces. + * + * @return the checker node or {@code null} if the type is not checked + */ + private Node createCheckerNode(JSType type) { + if (type.isNullType()) { + return jsCode("nullChecker"); + + } else if (type.isBooleanValueType() + || type.isNumberValueType() + || type.isStringValueType() + || type.isVoidType()) { + return IR.call( + jsCode("valueChecker"), + IR.string(type.toString())); + + } else if (type.isInstanceType()) { + ObjectType objType = (ObjectType) type; + + String refName = objType.getReferenceName(); + + StaticSourceFile sourceFile = + NodeUtil.getSourceFile(objType.getConstructor().getSource()); + if (sourceFile == null || sourceFile.isExtern()) { + return IR.call( + jsCode("externClassChecker"), + IR.string(refName)); + } + + return IR.call( + jsCode(objType.getConstructor().isInterface() ? + "interfaceChecker" : "classChecker"), + IR.string(refName)); + + } else { + // We don't check this type (e.g. unknown & all types). + return null; + } + } + } + + private void addBoilerplateCode() { + Node newNode = compiler.ensureLibraryInjected("runtime_type_check"); + if (newNode != null && logFunction != null) { + // Inject the custom log function. + Node logOverride = IR.exprResult( + IR.assign( + NodeUtil.newQualifiedNameNode( + compiler.getCodingConvention(), + "$jscomp.typecheck.log"), + NodeUtil.newQualifiedNameNode( + compiler.getCodingConvention(), + logFunction))); + newNode.getParent().addChildAfter(logOverride, newNode); + compiler.reportCodeChange(); + } + } + + private Node jsCode(String prop) { + return NodeUtil.newQualifiedNameNode( + compiler.getCodingConvention(), "$jscomp.typecheck." + prop); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SanityCheck.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SanityCheck.java new file mode 100644 index 0000000..3aa9784 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SanityCheck.java @@ -0,0 +1,135 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + * A compiler pass that verifies the structure of the AST conforms + * to a number of invariants. Because this can add a lot of overhead, + * we only run this in development mode. + * + */ +class SanityCheck implements CompilerPass { + + static final DiagnosticType CANNOT_PARSE_GENERATED_CODE = + DiagnosticType.error("JSC_CANNOT_PARSE_GENERATED_CODE", + "Internal compiler error. Cannot parse generated code: {0}"); + + static final DiagnosticType GENERATED_BAD_CODE = DiagnosticType.error( + "JSC_GENERATED_BAD_CODE", + "Internal compiler error. Generated bad code." + + "----------------------------------------\n" + + "Expected:\n{0}\n" + + "----------------------------------------\n" + + "Actual:\n{1}"); + + private final AbstractCompiler compiler; + + private final AstValidator astValidator = new AstValidator(); + + SanityCheck(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + sanityCheckAst(externs, root); + sanityCheckNormalization(externs, root); + sanityCheckCodeGeneration(root); + sanityCheckVars(externs, root); + } + + /** + * Sanity check the AST is structurally accurate. + */ + private void sanityCheckAst(Node externs, Node root) { + astValidator.validateCodeRoot(externs); + astValidator.validateCodeRoot(root); + } + + private void sanityCheckVars(Node externs, Node root) { + if (compiler.getLifeCycleStage().isNormalized()) { + (new VarCheck(compiler, true)).process(externs, root); + } + } + + /** + * Sanity checks code generation by performing it once, parsing the result, + * then generating code from the second parse tree to verify that it matches + * the code generated from the first parse tree. + * + * @return The regenerated parse tree. Null on error. + */ + private Node sanityCheckCodeGeneration(Node root) { + if (compiler.hasHaltingErrors()) { + // Don't even bother checking code generation if we already know the + // the code is bad. + return null; + } + + String source = compiler.toSource(root); + Node root2 = compiler.parseSyntheticCode(source); + if (compiler.hasHaltingErrors()) { + compiler.report(JSError.make(CANNOT_PARSE_GENERATED_CODE, + Strings.truncateAtMaxLength(source, 100, true))); + + // Throw an exception, so that the infrastructure will tell us + // which pass violated the sanity check. + throw new IllegalStateException("Sanity Check failed"); + } + + String source2 = compiler.toSource(root2); + if (!source.equals(source2)) { + compiler.report(JSError.make(GENERATED_BAD_CODE, source, source2)); + + // Throw an exception, so that the infrastructure will tell us + // which pass violated the sanity check. + throw new IllegalStateException("Sanity Check failed"); + } + + return root2; + } + + /** + * Sanity checks the AST. This is by verifying the normalization passes do + * nothing. + */ + private void sanityCheckNormalization(Node externs, Node root) { + // Verify nothing has inappropriately denormalize the AST. + CodeChangeHandler handler = + new CodeChangeHandler.ForbiddenChange(); + compiler.addChangeHandler(handler); + + // TODO(johnlenz): Change these normalization checks Preconditions and + // Exceptions into Errors so that it is easier to find the root cause + // when there are cascading issues. + new PrepareAst(compiler, true).process(null, root); + if (compiler.getLifeCycleStage().isNormalized()) { + (new Normalize(compiler, true)).process(externs, root); + + if (compiler.getLifeCycleStage().isNormalizedUnobfuscated()) { + boolean checkUserDeclarations = true; + CompilerPass pass = new Normalize.VerifyConstants( + compiler, checkUserDeclarations); + pass.process(externs, root); + } + } + + compiler.removeChangeHandler(handler); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Scope.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Scope.java new file mode 100644 index 0000000..7ed2b6f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Scope.java @@ -0,0 +1,620 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterators; +import com.google.javascript.rhino.ErrorReporter; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.StaticReference; +import com.google.javascript.rhino.jstype.StaticScope; +import com.google.javascript.rhino.jstype.StaticSlot; +import com.google.javascript.rhino.jstype.StaticSourceFile; +import com.google.javascript.rhino.jstype.StaticSymbolTable; + +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Scope contains information about a variable scope in JavaScript. + * Scopes can be nested, a scope points back to its parent scope. + * A Scope contains information about variables defined in that scope. + *

      + * A Scope is also used as a lattice element for flow-sensitive type inference. + * As a lattice element, a Scope is viewed as a map from names to types. A name + * not in the map is considered to have the bottom type. The join of two maps m1 + * and m2 is the map of the union of names with {@link JSType#getLeastSupertype} + * to meet the m1 type and m2 type. + * + * @see NodeTraversal + * @see DataFlowAnalysis + * + */ +public class Scope + implements StaticScope, StaticSymbolTable { + private final Map vars = new LinkedHashMap(); + private final Scope parent; + private final int depth; + private final Node rootNode; + + /** Whether this is a bottom scope for the purposes of type inference. */ + private final boolean isBottom; + + private Var arguments; + + private static final Predicate DECLARATIVELY_UNBOUND_VARS_WITHOUT_TYPES = + new Predicate() { + @Override public boolean apply(Var var) { + return var.getParentNode() != null && + var.getType() == null && // no declared type + var.getParentNode().isVar() && + !var.isExtern(); + } + }; + + /** Stores info about a variable */ + public static class Var + implements StaticSlot, StaticReference { + /** name */ + final String name; + + /** Var node */ + final Node nameNode; + + /** + * The variable's type. + */ + private JSType type; + + /** + * Whether the variable's type has been inferred or is declared. An inferred + * type may change over time (as more code is discovered), whereas a + * declared type is a static contract that must be matched. + */ + private final boolean typeInferred; + + /** Input source */ + final CompilerInput input; + + /** + * The index at which the var is declared. e..g if it's 0, it's the first + * declared variable in that scope + */ + final int index; + + /** The enclosing scope */ + final Scope scope; + + /** @see #isMarkedEscaped */ + private boolean markedEscaped = false; + + /** @see #isMarkedAssignedExactlyOnce */ + private boolean markedAssignedExactlyOnce = false; + + /** + * Creates a variable. + * + * @param inferred whether its type is inferred (as opposed to declared) + */ + private Var(boolean inferred, String name, Node nameNode, JSType type, + Scope scope, int index, CompilerInput input) { + this.name = name; + this.nameNode = nameNode; + this.type = type; + this.scope = scope; + this.index = index; + this.input = input; + this.typeInferred = inferred; + } + + /** + * Gets the name of the variable. + */ + @Override + public String getName() { + return name; + } + + /** + * Gets the node for the name of the variable. + */ + @Override + public Node getNode() { + return nameNode; + } + + CompilerInput getInput() { + return input; + } + + @Override + public StaticSourceFile getSourceFile() { + return nameNode.getStaticSourceFile(); + } + + @Override + public Var getSymbol() { + return this; + } + + @Override + public Var getDeclaration() { + return nameNode == null ? null : this; + } + + /** + * Gets the parent of the name node. + */ + public Node getParentNode() { + return nameNode == null ? null : nameNode.getParent(); + } + + /** + * Whether this is a bleeding function (an anonymous named function + * that bleeds into the inner scope). + */ + public boolean isBleedingFunction() { + return NodeUtil.isFunctionExpression(getParentNode()); + } + + /** + * Gets the scope where this variable is declared. + */ + Scope getScope() { + return scope; + } + + /** + * Returns whether this is a global variable. + */ + public boolean isGlobal() { + return scope.isGlobal(); + } + + /** + * Returns whether this is a local variable. + */ + public boolean isLocal() { + return scope.isLocal(); + } + + /** + * Returns whether this is defined in an extern file. + */ + boolean isExtern() { + return input == null || input.isExtern(); + } + + /** + * Returns {@code true} if the variable is declared as a constant, + * based on the value reported by {@code NodeUtil}. + */ + public boolean isConst() { + return nameNode != null && NodeUtil.isConstantName(nameNode); + } + + /** + * Returns {@code true} if the variable is declared as a define. + * A variable is a define if it is annotated by {@code @define}. + */ + public boolean isDefine() { + JSDocInfo info = getJSDocInfo(); + return info != null && info.isDefine(); + } + + public Node getInitialValue() { + return NodeUtil.getRValueOfLValue(nameNode); + } + + /** + * Gets this variable's type. To know whether this type has been inferred, + * see {@code #isTypeInferred()}. + */ + @Override + public JSType getType() { + return type; + } + + /** + * Returns the name node that produced this variable. + */ + public Node getNameNode() { + return nameNode; + } + + /** + * Gets the JSDocInfo for the variable. + */ + @Override + public JSDocInfo getJSDocInfo() { + return nameNode == null ? null : NodeUtil.getBestJSDocInfo(nameNode); + } + + /** + * Sets this variable's type. + * @throws IllegalStateException if the variable's type is not inferred + */ + void setType(JSType type) { + Preconditions.checkState(isTypeInferred()); + this.type = type; + } + + /** + * Resolve this variable's type. + */ + void resolveType(ErrorReporter errorReporter) { + if (type != null) { + type = type.resolve(errorReporter, scope); + } + } + + /** + * Returns whether this variable's type is inferred. To get the variable's + * type, see {@link #getType()}. + */ + @Override + public boolean isTypeInferred() { + return typeInferred; + } + + public String getInputName() { + if (input == null) + return ""; + else + return input.getName(); + } + + public boolean isNoShadow() { + JSDocInfo info = getJSDocInfo(); + return info != null && info.isNoShadow(); + } + + @Override public boolean equals(Object other) { + if (!(other instanceof Var)) { + return false; + } + + Var otherVar = (Var) other; + return otherVar.nameNode == nameNode; + } + + @Override public int hashCode() { + return nameNode.hashCode(); + } + + @Override + public String toString() { + return "Scope.Var " + name + "{" + type + "}"; + } + + /** + * Record that this is escaped by an inner scope. + * + * In other words, it's assigned in an inner scope so that it's much harder + * to make assertions about its value at a given point. + */ + void markEscaped() { + markedEscaped = true; + } + + /** + * Whether this is escaped by an inner scope. + * Notice that not all scope creators record this information. + */ + boolean isMarkedEscaped() { + return markedEscaped; + } + + /** + * Record that this is assigned exactly once.. + * + * In other words, it's assigned in an inner scope so that it's much harder + * to make assertions about its value at a given point. + */ + void markAssignedExactlyOnce() { + markedAssignedExactlyOnce = true; + } + + /** + * Whether this is assigned exactly once. + * Notice that not all scope creators record this information. + */ + boolean isMarkedAssignedExactlyOnce() { + return markedAssignedExactlyOnce; + } + } + + /** + * A special subclass of Var used to distinguish "arguments" in the current + * scope. + */ + // TODO(johnlenz): Include this the list of Vars for the scope. + public static class Arguments extends Var { + Arguments(Scope scope) { + super( + false, // no inferred + "arguments", // always arguments + null, // no declaration node + // TODO(johnlenz): provide the type of "Arguments". + null, // no type info + scope, + -1, // no variable index + null // input + ); + } + + @Override public boolean equals(Object other) { + if (!(other instanceof Arguments)) { + return false; + } + + Arguments otherVar = (Arguments) other; + return otherVar.scope.getRootNode() == scope.getRootNode(); + } + + @Override public int hashCode() { + return System.identityHashCode(this); + } + } + + /** + * Creates a Scope given the parent Scope and the root node of the scope. + * @param parent The parent Scope. Cannot be null. + * @param rootNode Typically the FUNCTION node. + */ + Scope(Scope parent, Node rootNode) { + Preconditions.checkNotNull(parent); + Preconditions.checkArgument(rootNode != parent.rootNode); + + this.parent = parent; + this.rootNode = rootNode; + this.isBottom = false; + this.depth = parent.depth + 1; + } + + /** + * Creates a empty Scope (bottom of the lattice). + * @param rootNode Typically a FUNCTION node or the global BLOCK node. + * @param isBottom Whether this is the bottom of a lattice. Otherwise, + * it must be a global scope. + */ + private Scope(Node rootNode, boolean isBottom) { + this.parent = null; + this.rootNode = rootNode; + this.isBottom = isBottom; + this.depth = 0; + } + + static Scope createGlobalScope(Node rootNode) { + return new Scope(rootNode, false); + } + + static Scope createLatticeBottom(Node rootNode) { + return new Scope(rootNode, true); + } + + /** The depth of the scope. The global scope has depth 0. */ + int getDepth() { + return depth; + } + + /** Whether this is the bottom of the lattice. */ + boolean isBottom() { + return isBottom; + } + + /** + * Gets the container node of the scope. This is typically the FUNCTION + * node or the global BLOCK/SCRIPT node. + */ + @Override + public Node getRootNode() { + return rootNode; + } + + public Scope getParent() { + return parent; + } + + Scope getGlobalScope() { + Scope result = this; + while (result.getParent() != null) { + result = result.getParent(); + } + return result; + } + + @Override + public StaticScope getParentScope() { + return parent; + } + + /** + * Gets the type of {@code this} in the current scope. + */ + @Override + public JSType getTypeOfThis() { + if (isGlobal()) { + return ObjectType.cast(rootNode.getJSType()); + } + + Preconditions.checkState(rootNode.isFunction()); + JSType nodeType = rootNode.getJSType(); + if (nodeType != null && nodeType.isFunctionType()) { + return nodeType.toMaybeFunctionType().getTypeOfThis(); + } else { + return parent.getTypeOfThis(); + } + } + + /** + * Declares a variable whose type is inferred. + * + * @param name name of the variable + * @param nameNode the NAME node declaring the variable + * @param type the variable's type + * @param input the input in which this variable is defined. + */ + Var declare(String name, Node nameNode, JSType type, CompilerInput input) { + return declare(name, nameNode, type, input, true); + } + + /** + * Declares a variable. + * + * @param name name of the variable + * @param nameNode the NAME node declaring the variable + * @param type the variable's type + * @param input the input in which this variable is defined. + * @param inferred Whether this variable's type is inferred (as opposed + * to declared). + */ + Var declare(String name, Node nameNode, + JSType type, CompilerInput input, boolean inferred) { + Preconditions.checkState(name != null && name.length() > 0); + + // Make sure that it's declared only once + Preconditions.checkState(vars.get(name) == null); + + Var var = new Var(inferred, name, nameNode, type, this, vars.size(), input); + vars.put(name, var); + return var; + } + + /** + * Undeclares a variable, to be used when the compiler optimizes out + * a variable and removes it from the scope. + */ + void undeclare(Var var) { + Preconditions.checkState(var.scope == this); + Preconditions.checkState(vars.get(var.name) == var); + vars.remove(var.name); + } + + @Override + public Var getSlot(String name) { + return getVar(name); + } + + @Override + public Var getOwnSlot(String name) { + return vars.get(name); + } + + /** + * Returns the variable, may be null + */ + public Var getVar(String name) { + Var var = vars.get(name); + if (var != null) { + return var; + } else if (parent != null) { // Recurse up the parent Scope + return parent.getVar(name); + } else { + return null; + } + } + + /** + * Get a unique VAR object to represents "arguments" within this scope + */ + public Var getArgumentsVar() { + if (arguments == null) { + arguments = new Arguments(this); + } + return arguments; + } + + /** + * Returns true if a variable is declared. + */ + public boolean isDeclared(String name, boolean recurse) { + Scope scope = this; + if (scope.vars.containsKey(name)) + return true; + + if (scope.parent != null && recurse) { + return scope.parent.isDeclared(name, recurse); + } + return false; + } + + /** + * Return an iterator over all of the variables declared in this scope. + */ + public Iterator getVars() { + return vars.values().iterator(); + } + + /** + * Return an iterable over all of the variables declared in this scope. + */ + Iterable getVarIterable() { + return vars.values(); + } + + @Override + public Iterable getReferences(Var var) { + return ImmutableList.of(var); + } + + @Override + public StaticScope getScope(Var var) { + return var.scope; + } + + @Override + public Iterable getAllSymbols() { + return Collections.unmodifiableCollection(vars.values()); + } + + /** + * Returns number of variables in this scope + */ + public int getVarCount() { + return vars.size(); + } + + /** + * Returns whether this is the global scope. + */ + public boolean isGlobal() { + return parent == null; + } + + /** + * Returns whether this is a local scope (i.e. not the global scope). + */ + public boolean isLocal() { + return !isGlobal(); + } + + /** + * Gets all variables declared with "var" but without declared types attached. + */ + public Iterator getDeclarativelyUnboundVarsWithoutTypes() { + return Iterators.filter( + getVars(), DECLARATIVELY_UNBOUND_VARS_WITHOUT_TYPES); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ScopeCreator.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ScopeCreator.java new file mode 100644 index 0000000..feb433a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ScopeCreator.java @@ -0,0 +1,35 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + * This interface defines how objects capable of creating scopes from the parse + * tree behave. + * + */ +interface ScopeCreator { + /** + * Creates a {@link Scope} object. + * + * @param n the root node (either a FUNCTION node, a SCRIPT node, or a + * synthetic block node whose children are all SCRIPT nodes) + * @param parent the parent Scope object (may be null) + */ + Scope createScope(Node n, Scope parent); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ScopedAliases.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ScopedAliases.java new file mode 100644 index 0000000..e2f0538 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ScopedAliases.java @@ -0,0 +1,484 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.CompilerOptions.AliasTransformation; +import com.google.javascript.jscomp.CompilerOptions.AliasTransformationHandler; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.SourcePosition; +import com.google.javascript.rhino.Token; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +/** + * Process aliases in goog.scope blocks. + * + * goog.scope(function() { + * var dom = goog.dom; + * var DIV = dom.TagName.DIV; + * + * dom.createElement(DIV); + * }); + * + * should become + * + * goog.dom.createElement(goog.dom.TagName.DIV); + * + * @author robbyw@google.com (Robby Walker) + */ +class ScopedAliases implements HotSwapCompilerPass { + /** Name used to denote an scoped function block used for aliasing. */ + static final String SCOPING_METHOD_NAME = "goog.scope"; + + private final AbstractCompiler compiler; + private final PreprocessorSymbolTable preprocessorSymbolTable; + private final AliasTransformationHandler transformationHandler; + + // Errors + static final DiagnosticType GOOG_SCOPE_USED_IMPROPERLY = DiagnosticType.error( + "JSC_GOOG_SCOPE_USED_IMPROPERLY", + "The call to goog.scope must be alone in a single statement."); + + static final DiagnosticType GOOG_SCOPE_HAS_BAD_PARAMETERS = + DiagnosticType.error( + "JSC_GOOG_SCOPE_HAS_BAD_PARAMETERS", + "The call to goog.scope must take only a single parameter. It must" + + " be an anonymous function that itself takes no parameters."); + + static final DiagnosticType GOOG_SCOPE_REFERENCES_THIS = DiagnosticType.error( + "JSC_GOOG_SCOPE_REFERENCES_THIS", + "The body of a goog.scope function cannot reference 'this'."); + + static final DiagnosticType GOOG_SCOPE_USES_RETURN = DiagnosticType.error( + "JSC_GOOG_SCOPE_USES_RETURN", + "The body of a goog.scope function cannot use 'return'."); + + static final DiagnosticType GOOG_SCOPE_USES_THROW = DiagnosticType.error( + "JSC_GOOG_SCOPE_USES_THROW", + "The body of a goog.scope function cannot use 'throw'."); + + static final DiagnosticType GOOG_SCOPE_ALIAS_REDEFINED = DiagnosticType.error( + "JSC_GOOG_SCOPE_ALIAS_REDEFINED", + "The alias {0} is assigned a value more than once."); + + static final DiagnosticType GOOG_SCOPE_NON_ALIAS_LOCAL = DiagnosticType.error( + "JSC_GOOG_SCOPE_NON_ALIAS_LOCAL", + "The local variable {0} is in a goog.scope and is not an alias."); + + ScopedAliases(AbstractCompiler compiler, + @Nullable PreprocessorSymbolTable preprocessorSymbolTable, + AliasTransformationHandler transformationHandler) { + this.compiler = compiler; + this.preprocessorSymbolTable = preprocessorSymbolTable; + this.transformationHandler = transformationHandler; + } + + @Override + public void process(Node externs, Node root) { + hotSwapScript(root, null); + } + + @Override + public void hotSwapScript(Node root, Node originalRoot) { + Traversal traversal = new Traversal(); + NodeTraversal.traverse(compiler, root, traversal); + + if (!traversal.hasErrors()) { + + // Apply the aliases. + for (AliasUsage aliasUsage : traversal.getAliasUsages()) { + aliasUsage.applyAlias(); + } + + // Remove the alias definitions. + for (Node aliasDefinition : traversal.getAliasDefinitionsInOrder()) { + if (aliasDefinition.getParent().isVar() && + aliasDefinition.getParent().hasOneChild()) { + aliasDefinition.getParent().detachFromParent(); + } else { + aliasDefinition.detachFromParent(); + } + } + + // Collapse the scopes. + for (Node scopeCall : traversal.getScopeCalls()) { + Node expressionWithScopeCall = scopeCall.getParent(); + Node scopeClosureBlock = scopeCall.getLastChild().getLastChild(); + scopeClosureBlock.detachFromParent(); + expressionWithScopeCall.getParent().replaceChild( + expressionWithScopeCall, + scopeClosureBlock); + NodeUtil.tryMergeBlock(scopeClosureBlock); + } + + if (traversal.getAliasUsages().size() > 0 || + traversal.getAliasDefinitionsInOrder().size() > 0 || + traversal.getScopeCalls().size() > 0) { + compiler.reportCodeChange(); + } + } + } + + private interface AliasUsage { + public void applyAlias(); + } + + private class AliasedNode implements AliasUsage { + private final Node aliasReference; + + private final Node aliasDefinition; + + AliasedNode(Node aliasReference, Node aliasDefinition) { + this.aliasReference = aliasReference; + this.aliasDefinition = aliasDefinition; + } + + @Override + public void applyAlias() { + aliasReference.getParent().replaceChild( + aliasReference, aliasDefinition.cloneTree()); + } + } + + private class AliasedTypeNode implements AliasUsage { + private final Node typeReference; + private final Node aliasDefinition; + private final String aliasName; + + AliasedTypeNode(Node typeReference, Node aliasDefinition, + String aliasName) { + this.typeReference = typeReference; + this.aliasDefinition = aliasDefinition; + this.aliasName = aliasName; + } + + @Override + public void applyAlias() { + String typeName = typeReference.getString(); + String aliasExpanded = + Preconditions.checkNotNull(aliasDefinition.getQualifiedName()); + Preconditions.checkState(typeName.startsWith(aliasName)); + typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded)); + } + } + + + private class Traversal implements NodeTraversal.ScopedCallback { + // The job of this class is to collect these three data sets. + + // The order of this list determines the order that aliases are applied. + private final List aliasDefinitionsInOrder = Lists.newArrayList(); + + private final List scopeCalls = Lists.newArrayList(); + + private final List aliasUsages = Lists.newArrayList(); + + // This map is temporary and cleared for each scope. + private final Map aliases = Maps.newHashMap(); + + // Suppose you create an alias. + // var x = goog.x; + // As a side-effect, this means you can shadow the namespace 'goog' + // in inner scopes. When we inline the namespaces, we have to rename + // these shadows. + // + // Fortunately, we already have a name uniquifier that runs during tree + // normalization (before optimizations). We run it here on a limited + // set of variables, but only as a last resort (because this will screw + // up warning messages downstream). + private final Set forbiddenLocals = Sets.newHashSet(); + private boolean hasNamespaceShadows = false; + + private boolean hasErrors = false; + + private AliasTransformation transformation = null; + + Collection getAliasDefinitionsInOrder() { + return aliasDefinitionsInOrder; + } + + private List getAliasUsages() { + return aliasUsages; + } + + List getScopeCalls() { + return scopeCalls; + } + + boolean hasErrors() { + return hasErrors; + } + + private boolean isCallToScopeMethod(Node n) { + return n.isCall() && + SCOPING_METHOD_NAME.equals(n.getFirstChild().getQualifiedName()); + } + + @Override + public void enterScope(NodeTraversal t) { + Node n = t.getCurrentNode().getParent(); + if (n != null && isCallToScopeMethod(n)) { + transformation = transformationHandler.logAliasTransformation( + n.getSourceFileName(), getSourceRegion(n)); + findAliases(t); + } + } + + @Override + public void exitScope(NodeTraversal t) { + if (t.getScopeDepth() > 2) { + findNamespaceShadows(t); + } + + if (t.getScopeDepth() == 2) { + renameNamespaceShadows(t); + aliases.clear(); + forbiddenLocals.clear(); + transformation = null; + hasNamespaceShadows = false; + } + } + + @Override + public final boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + if (n.isFunction() && t.inGlobalScope()) { + // Do not traverse in to functions except for goog.scope functions. + if (parent == null || !isCallToScopeMethod(parent)) { + return false; + } + } + return true; + } + + private SourcePosition getSourceRegion(Node n) { + Node testNode = n; + Node next = null; + for (; next != null || testNode.isScript();) { + next = testNode.getNext(); + testNode = testNode.getParent(); + } + + int endLine = next == null ? Integer.MAX_VALUE : next.getLineno(); + int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); + SourcePosition pos = + new SourcePosition() {}; + pos.setPositionInformation( + n.getLineno(), n.getCharno(), endLine, endChar); + return pos; + } + + private void report(NodeTraversal t, Node n, DiagnosticType error, + String... arguments) { + compiler.report(t.makeError(n, error, arguments)); + hasErrors = true; + } + + private void findAliases(NodeTraversal t) { + Scope scope = t.getScope(); + for (Var v : scope.getVarIterable()) { + Node n = v.getNode(); + int type = n.getType(); + Node parent = n.getParent(); + if (parent.isVar() && + n.hasChildren() && n.getFirstChild().isQualifiedName()) { + String name = n.getString(); + Var aliasVar = scope.getVar(name); + aliases.put(name, aliasVar); + + String qualifiedName = + aliasVar.getInitialValue().getQualifiedName(); + transformation.addAlias(name, qualifiedName); + + int rootIndex = qualifiedName.indexOf("."); + if (rootIndex != -1) { + String qNameRoot = qualifiedName.substring(0, rootIndex); + if (!aliases.containsKey(qNameRoot)) { + forbiddenLocals.add(qNameRoot); + } + } + } else if (v.isBleedingFunction()) { + // Bleeding functions already get a BAD_PARAMETERS error, so just + // do nothing. + } else if (parent.getType() == Token.LP) { + // Parameters of the scope function also get a BAD_PARAMETERS + // error. + } else { + // TODO(robbyw): Support using locals for private variables. + report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); + } + } + } + + /** Find out if there are any local shadows of namespaces. */ + private void findNamespaceShadows(NodeTraversal t) { + if (hasNamespaceShadows) { + return; + } + + Scope scope = t.getScope(); + for (Var v : scope.getVarIterable()) { + if (forbiddenLocals.contains(v.getName())) { + hasNamespaceShadows = true; + return; + } + } + } + + /** + * Rename any local shadows of namespaces. + * This should be a very rare occurrence, so only do this traversal + * if we know that we need it. + */ + private void renameNamespaceShadows(NodeTraversal t) { + if (hasNamespaceShadows) { + MakeDeclaredNamesUnique.Renamer renamer = + new MakeDeclaredNamesUnique.WhitelistedRenamer( + new MakeDeclaredNamesUnique.ContextualRenamer(), + forbiddenLocals); + for (String s : forbiddenLocals) { + renamer.addDeclaredName(s); + } + MakeDeclaredNamesUnique uniquifier = + new MakeDeclaredNamesUnique(renamer); + NodeTraversal.traverse(compiler, t.getScopeRoot(), uniquifier); + } + } + + private void validateScopeCall(NodeTraversal t, Node n, Node parent) { + if (preprocessorSymbolTable != null) { + preprocessorSymbolTable.addReference(n.getFirstChild()); + } + if (!parent.isExprResult()) { + report(t, n, GOOG_SCOPE_USED_IMPROPERLY); + } + if (n.getChildCount() != 2) { + // The goog.scope call should have exactly 1 parameter. The first + // child is the "goog.scope" and the second should be the parameter. + report(t, n, GOOG_SCOPE_HAS_BAD_PARAMETERS); + } else { + Node anonymousFnNode = n.getChildAtIndex(1); + if (!anonymousFnNode.isFunction() || + NodeUtil.getFunctionName(anonymousFnNode) != null || + NodeUtil.getFunctionParameters(anonymousFnNode).hasChildren()) { + report(t, anonymousFnNode, GOOG_SCOPE_HAS_BAD_PARAMETERS); + } else { + scopeCalls.add(n); + } + } + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (isCallToScopeMethod(n)) { + validateScopeCall(t, n, n.getParent()); + } + + if (t.getScopeDepth() < 2) { + return; + } + + int type = n.getType(); + Var aliasVar = null; + if (type == Token.NAME) { + String name = n.getString(); + Var lexicalVar = t.getScope().getVar(n.getString()); + if (lexicalVar != null && lexicalVar == aliases.get(name)) { + aliasVar = lexicalVar; + } + } + + // Validate the top-level of the goog.scope block. + if (t.getScopeDepth() == 2) { + if (aliasVar != null && NodeUtil.isLValue(n)) { + if (aliasVar.getNode() == n) { + aliasDefinitionsInOrder.add(n); + + // Return early, to ensure that we don't record a definition + // twice. + return; + } else { + report(t, n, GOOG_SCOPE_ALIAS_REDEFINED, n.getString()); + } + } + + if (type == Token.RETURN) { + report(t, n, GOOG_SCOPE_USES_RETURN); + } else if (type == Token.THIS) { + report(t, n, GOOG_SCOPE_REFERENCES_THIS); + } else if (type == Token.THROW) { + report(t, n, GOOG_SCOPE_USES_THROW); + } + } + + // Validate all descendent scopes of the goog.scope block. + if (t.getScopeDepth() >= 2) { + // Check if this name points to an alias. + if (aliasVar != null) { + // Note, to support the transitive case, it's important we don't + // clone aliasedNode here. For example, + // var g = goog; var d = g.dom; d.createElement('DIV'); + // The node in aliasedNode (which is "g") will be replaced in the + // changes pass above with "goog". If we cloned here, we'd end up + // with g.dom.createElement('DIV'). + Node aliasedNode = aliasVar.getInitialValue(); + aliasUsages.add(new AliasedNode(n, aliasedNode)); + } + + JSDocInfo info = n.getJSDocInfo(); + if (info != null) { + for (Node node : info.getTypeNodes()) { + fixTypeNode(node); + } + } + + // TODO(robbyw): Error for goog.scope not at root. + } + } + + private void fixTypeNode(Node typeNode) { + if (typeNode.isString()) { + String name = typeNode.getString(); + int endIndex = name.indexOf('.'); + if (endIndex == -1) { + endIndex = name.length(); + } + String baseName = name.substring(0, endIndex); + Var aliasVar = aliases.get(baseName); + if (aliasVar != null) { + Node aliasedNode = aliasVar.getInitialValue(); + aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName)); + } + } + + for (Node child = typeNode.getFirstChild(); child != null; + child = child.getNext()) { + fixTypeNode(child); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ShadowVariables.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ShadowVariables.java new file mode 100644 index 0000000..f627aaf --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ShadowVariables.java @@ -0,0 +1,296 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.RenameVars.Assignment; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.Node; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.SortedSet; + +/** + * Tries to compute a list of variables that can shadow a variable in the + * outer scope. + * + * For example: + * + * + * var a = function() { + * var b = getB(); + * b(); + * return function(y) {}; + * }; + * + * + * Normally, b would be mapped to variable L0, y would be L1. + * + * Instead we are going to make y shadows L0 in hope of using less variables + * and reusing frequently used local names. + * + */ +class ShadowVariables implements CompilerPass { + + // Keep a map of Upward Referencing name nodes of each scope. + // A name is upward referencing name of a scope if: + // + // 1) It refers to (or defines) a name that is defined in the current + // scope or any scope above the current scope that isn't the + // global scope. + // + // 2) It is a upward referencing name of a child scope of this scope. + // + // Example: + // var x; var y; function foo(a) { function bar(b) { x, a } } + // The upward referencing names in scope 'foo' is bar, b, x and a; + // The key to this map is the root node of the scope. + // + // We can see that for any variable x in the current scope, we can shadow + // a variable y in an outer scope given that y is not a upward referencing + // name of the current scope. + + // TODO(user): Maps scope to string instead of Node to string. + // Make sure of scope memorization to minimize scope creation cost. + private final Multimap scopeUpRefMap = HashMultimap.create(); + + // Maps all local Scope.Var to all of its referencing NAME node + // in any scope. + private final Multimap varToNameUsage = HashMultimap.create(); + + private final AbstractCompiler compiler; + + // All the information used for renaming. + private final SortedSet varsByFrequency; + private final Map assignments; + private final Map oldPseudoNameMap; + private final Map deltaPseudoNameMap; + + + /** + * @param assignments Map of old variable names to its assignment Objects. + * @param varsByFrequency Sorted variable assignments by Frequency. + * @param pseudoNameMap The current pseudo name map so this pass can update + * it accordingly. + */ + ShadowVariables( + AbstractCompiler compiler, + Map assignments, + SortedSet varsByFrequency, + Map pseudoNameMap) { + this.compiler = compiler; + this.assignments = assignments; + this.varsByFrequency = varsByFrequency; + this.oldPseudoNameMap = pseudoNameMap; + this.deltaPseudoNameMap = Maps.newLinkedHashMap(); + } + + @Override + public void process(Node externs, Node root) { + + // The algorithm is divided into two stages: + // + // 1. Information gathering (variable usage, upward referencing) + // + // 2. Tries to find shadows for each variables, updates the + // variable usage frequency map. + // + // 3. Updates the pseudo naming map if needed. + NodeTraversal.traverse(compiler, root, new GatherReferenceInfo()); + NodeTraversal.traverse(compiler, root, new DoShadowVariables()); + + if (oldPseudoNameMap != null) { + oldPseudoNameMap.putAll(deltaPseudoNameMap); + } + } + + private class GatherReferenceInfo extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + // Skipping over non-name nodes and empty function names. + if (!NodeUtil.isReferenceName(n)) { + return; + } + + // We focus on shadowing local variables as their name occurs much more + // than global names. + // TODO(user): Alternatively, we could experiment with using a local + // name to shadow a global variable. + if (t.inGlobalScope()) { + return; + } + + Var var = t.getScope().getVar(n.getString()); + if (var == null) { + // extern name or undefined name. + return; + } + + if (var.getScope().isGlobal()) { + // We will not shadow a global variable name. + return; + } + + // Using the definition of upward referencing, fill in the map. + if (var.getScope() != t.getScope()) { + for (Scope s = t.getScope(); + s != var.getScope() && s.isLocal(); s = s.getParent()) { + scopeUpRefMap.put(s.getRootNode(), var.name); + } + } + + if (var.getScope() == t.getScope()) { + scopeUpRefMap.put(t.getScopeRoot(), var.name); + } + + // Find in the usage map that tracks a var and all of its usage. + varToNameUsage.put(var, n); + } + } + + private class DoShadowVariables extends AbstractPostOrderCallback + implements ScopedCallback { + + @Override + public void enterScope(NodeTraversal t) { + Scope s = t.getScope(); + if (!s.isLocal()) { + return; + } + + // Since we don't shadow global, there is nothing to be done in the + // first immediate local scope as well. + if (s.getParent().isGlobal()) { + return; + } + + for (Iterator vars = s.getVars(); vars.hasNext();) { + Var var = vars.next(); + + // Don't shadow variables that is bleed-out to fix an IE bug. + if (var.isBleedingFunction()) { + continue; + } + + // Don't shadow an exported local. + if (compiler.getCodingConvention().isExported(var.name, s.isLocal())) { + continue; + } + + // Try to look for the best shadow for the current candidate. + Assignment bestShadow = findBestShadow(s, var); + if (bestShadow == null) { + continue; + } + + // The name assignment being shadowed. + Assignment localAssignment = assignments.get(var.getName()); + + // Only shadow if this increases the number of occurrences of the + // shadowed variable. + if (bestShadow.count < localAssignment.count) { + continue; // Hope the next local variable would have a smaller count. + } + + doShadow(localAssignment, bestShadow, var); + + if (oldPseudoNameMap != null) { + String targetPseudoName = + oldPseudoNameMap.get(s.getVar(bestShadow.oldName).nameNode); + for (Node use : varToNameUsage.get(var)) { + deltaPseudoNameMap.put(use, targetPseudoName); + } + } + } + } + + @Override + public void exitScope(NodeTraversal t) {} + + @Override + public void visit(NodeTraversal t, Node n, Node parent) {} + + /** + * @returns An assignment that can be used as a shadow for a local variable + * in the scope defined by curScopeRoot. + */ + private Assignment findBestShadow(Scope curScope, Var candidate) { + // Search for the candidate starting from the most used local. + for (Assignment assignment : varsByFrequency) { + if (assignment.oldName.startsWith(RenameVars.LOCAL_VAR_PREFIX)) { + if (!scopeUpRefMap.get(curScope.getRootNode()).contains( + assignment.oldName)) { + if (curScope.isDeclared(assignment.oldName, true)) { + return assignment; + } + } + } + } + return null; + } + + private void doShadow(Assignment original, Assignment toShadow, Var var) { + Scope s = var.getScope(); + // We are now shadowing 'bestShadow' with localAssignment. + // All of the reference NAME node of this variable. + Collection references = varToNameUsage.get(var); + + // First remove both assignments from the sorted list since they need + // to be re-sorted. + varsByFrequency.remove(original); + varsByFrequency.remove(toShadow); + + // Adjust the count offset by the inner scope variable. + original.count -= references.size(); + toShadow.count += references.size(); + + // Add it back to the sorted list after re-adjustment. + varsByFrequency.add(original); + varsByFrequency.add(toShadow); + + // This is an important step. If variable L7 is going to be renamed to + // L1, by definition of upward referencing, The name L1 is now in the + // set of upward referencing names of the current scope up to the + // declaring scope of the best shadow variable. + Var shadowed = s.getVar(toShadow.oldName); + if (shadowed != null) { + for (Scope curScope = s; curScope != shadowed.scope; + curScope = curScope.getParent()) { + scopeUpRefMap.put(curScope.getRootNode(), toShadow.oldName); + } + } + + // Mark all the references as shadowed. + for (Node n : references) { + n.setString(toShadow.oldName); + Node cur = n; + while(cur != s.getRootNode()) { + cur = cur.getParent(); + if (cur.isFunction()) { + scopeUpRefMap.put(cur, toShadow.oldName); + } + } + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ShowByPathWarningsGuard.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ShowByPathWarningsGuard.java new file mode 100644 index 0000000..5b6fe06 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ShowByPathWarningsGuard.java @@ -0,0 +1,76 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; + +import java.util.List; + +/** + * Control whether warnings should be restricted or suppressed for specified + * paths. + * + * @author anatol@google.com (Anatol Pomazau) + */ +public class ShowByPathWarningsGuard extends WarningsGuard { + private static final long serialVersionUID = 1L; + + /** + * Controls whether warnings should be restricted to a specified path or + * suppressed within the specified path. + */ + public enum ShowType { + INCLUDE, // Suppress warnings outside the path. + EXCLUDE; // Suppress warnings within the path. + } + + private final ByPathWarningsGuard warningsGuard; + + public ShowByPathWarningsGuard(String checkWarningsOnlyForPath) { + this(checkWarningsOnlyForPath, ShowType.INCLUDE); + } + + public ShowByPathWarningsGuard(String[] checkWarningsOnlyForPath) { + this(checkWarningsOnlyForPath, ShowType.INCLUDE); + } + + public ShowByPathWarningsGuard(String path, ShowType showType) { + this(new String[] { path }, showType); + } + + public ShowByPathWarningsGuard(String[] paths, ShowType showType) { + Preconditions.checkArgument(paths != null); + Preconditions.checkArgument(showType != null); + List pathList = Lists.newArrayList(paths); + if (showType == ShowType.INCLUDE) { + warningsGuard = ByPathWarningsGuard.exceptPath(pathList, CheckLevel.OFF); + } else { + warningsGuard = ByPathWarningsGuard.forPath(pathList, CheckLevel.OFF); + } + } + + @Override + public CheckLevel level(JSError error) { + return warningsGuard.level(error); + } + + @Override + protected int getPriority() { + return warningsGuard.getPriority(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SideEffectsAnalysis.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SideEffectsAnalysis.java new file mode 100644 index 0000000..8e717e5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SideEffectsAnalysis.java @@ -0,0 +1,992 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback; +import com.google.javascript.jscomp.ReferenceCollectingCallback.Reference; +import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.VariableVisibilityAnalysis.VariableVisibility; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.ArrayList; +import java.util.Map; +import java.util.Set; + +/** + * A pass that analyzes side effects to determine when it is safe to move + * code from one program point to another. + * + * In its current form, SideEffectsAnalysis is very incomplete; this is + * mostly a sketch to prototype the interface and the broad strokes of + * a possible implementation based on flow-insensitive MOD and REF sets. + * + * See: + * + * Banning, John. "An efficient way to find the side effects of procedure + * calls and the aliases of variables." POPL '79. + * + * For an introduction to MOD and REF sets. + * + * @author dcc@google.com (Devin Coughlin) + */ + class SideEffectsAnalysis implements CompilerPass { + + /** + * The type of location abstraction to use for this analysis. + */ + enum LocationAbstractionMode { + /** See {@link DegenerateLocationAbstraction} for details. */ + DEGENERATE, + /** See {@link VisibilityLocationAbstraction} for details. */ + VISIBILITY_BASED + } + + private static final Predicate NOT_FUNCTION_PREDICATE = + new Predicate() { + @Override + public boolean apply(Node input) { + return !input.isFunction(); + } + }; + + private AbstractCompiler compiler; + + /** The location abstraction used to calculate the effects of code */ + private LocationAbstraction locationAbstraction; + + /** The kind of location abstraction to use */ + private final LocationAbstractionMode locationAbstractionIdentifier; + + /** + * Constructs a new SideEffectsAnalysis with the given location abstraction. + * + * @param compiler A compiler instance + * @param locationAbstractionMode The location abstraction to use. {@code + * DEGENERATE} will use {@link DegenerateLocationAbstraction} while + * {@code VISIBILITY_BASED} will use {@link VisibilityLocationAbstraction} + * + */ + public SideEffectsAnalysis(AbstractCompiler compiler, + LocationAbstractionMode locationAbstractionMode) { + this.compiler = compiler; + + this.locationAbstractionIdentifier = locationAbstractionMode; + } + + public SideEffectsAnalysis(AbstractCompiler compiler) { + this(compiler, LocationAbstractionMode.DEGENERATE); + } + + @Override + public void process(Node externs, Node root) { + switch(locationAbstractionIdentifier) { + case DEGENERATE: + locationAbstraction = new DegenerateLocationAbstraction(); + break; + case VISIBILITY_BASED: + locationAbstraction = createVisibilityAbstraction(externs, root); + break; + default: + throw new IllegalStateException("Unrecognized location abstraction " + + "identifier: " + locationAbstractionIdentifier); + } + + // In the future, this method + // will construct a callgraph and calculate side effects summaries + // for all functions. + // TODO(dcc): Add per-function side effects summaries. + } + + private LocationAbstraction createVisibilityAbstraction(Node externs, + Node root) { + VariableVisibilityAnalysis variableVisibility = + new VariableVisibilityAnalysis(compiler); + + variableVisibility.process(externs, root); + + VariableUseDeclarationMap variableMap = + new VariableUseDeclarationMap(compiler); + + variableMap.mapUses(root); + + return new VisibilityLocationAbstraction(compiler, + variableVisibility, variableMap); + } + + /** + * Determines whether it is safe to move code ({@code source}) across + * an environment to another program point (immediately preceding + * {@code destination}). + * + *

      The notion of "environment" is optimization-specific, but it should + * include any code that could be executed between the source program point + * and the destination program point. + * + * {@code destination} must not be a descendant of {@code source}. + * + * @param source The node that would be moved + * @param environment An environment representing the code across which + * the source will be moved. + * @param destination The node before which the source would be moved + * @return Whether it is safe to move the source to the destination + */ + public boolean safeToMoveBefore(Node source, + AbstractMotionEnvironment environment, + Node destination) { + Preconditions.checkNotNull(locationAbstraction); + Preconditions.checkArgument(!nodeHasAncestor(destination, source)); + + // It is always safe to move pure code. + if (isPure(source)) { + return true; + } + + // Don't currently support interprocedural analysis + if (nodeHasCall(source)) { + return false; + } + + LocationSummary sourceLocationSummary = + locationAbstraction.calculateLocationSummary(source); + + EffectLocation sourceModSet = sourceLocationSummary.getModSet(); + + // If the source has side effects, then we require that the source + // is executed exactly as many times as the destination. + if (!sourceModSet.isEmpty() && + !nodesHaveSameControlFlow(source, destination)) { + return false; + } + + EffectLocation sourceRefSet = sourceLocationSummary.getRefSet(); + + Set environmentNodes = environment.calculateEnvironment(); + + for (Node environmentNode : environmentNodes) { + if (nodeHasCall(environmentNode)) { + return false; + } + } + + LocationSummary environmentLocationSummary = + locationAbstraction.calculateLocationSummary(environmentNodes); + + EffectLocation environmentModSet = environmentLocationSummary.getModSet(); + + EffectLocation environmentRefSet = environmentLocationSummary.getRefSet(); + + // If MOD(environment) intersects REF(source) then moving the + // source across the environment could cause the source + // to read an incorrect value. + // If REF(environment) intersects MOD(source) then moving the + // source across the environment could cause the environment + // to read an incorrect value. + // If MOD(environment) intersects MOD(source) then moving the + // source across the environment could cause some later code that reads + // a modified location to get an incorrect value. + + if (!environmentModSet.intersectsLocation(sourceRefSet) + && !environmentRefSet.intersectsLocation(sourceModSet) + && !environmentModSet.intersectsLocation(sourceModSet)) { + return true; + } + + return false; + } + + /** + * Returns true if the node is pure, that is it side effect free and does it + * not depend on its environment? + */ + private boolean isPure(Node node) { + // For now, we conservatively assume all code is not pure. + // TODO(dcc): Implement isPure(). + return false; + } + + /** + * Returns true if the two nodes have the same control flow properties, + * that is, is node1 be executed every time node2 is executed and vice versa? + */ + private static boolean nodesHaveSameControlFlow(Node node1, Node node2) { + /* + * We conservatively approximate this with the following criteria: + * + * Define the "deepest control dependent block" for a node to be the + * closest ancestor whose *parent* is a control structure and where that + * ancestor may or may be executed depending on the parent. + * + * So, for example, in: + * if (a) { + * b; + * } else { + * c; + * } + * + * a has not deepest control dependent block. + * b's deepest control dependent block is the "then" block of the IF. + * c's deepest control dependent block is the "else" block of the IF. + * + * We'll say two nodes have the same control flow if + * + * 1) they have the same deepest control dependent block + * 2) that block is either a CASE (which can't have early exits) or it + * doesn't have any early exits (e.g. breaks, continues, returns.) + * + */ + + Node node1DeepestControlDependentBlock = + closestControlDependentAncestor(node1); + + Node node2DeepestControlDependentBlock = + closestControlDependentAncestor(node2); + + if (node1DeepestControlDependentBlock == + node2DeepestControlDependentBlock) { + + if (node2DeepestControlDependentBlock != null) { + // CASE is complicated because we have to deal with fall through and + // because some BREAKs are early exits and some are not. + // For now, we don't allow movement within a CASE. + // + // TODO(dcc): be less conservative about movement within CASE + if (node2DeepestControlDependentBlock.isCase()) { + return false; + } + + // Don't allow breaks, continues, returns in control dependent + // block because we don't actually create a control-flow graph + // and so don't know if early exits site between the source + // and the destination. + // + // This is overly conservative as it doesn't allow, for example, + // moving in the following case: + // while (a) { + // source(); + // + // while(b) { + // break; + // } + // + // destination(); + // } + // + // To fully support this kind of movement, we'll probably have to use + // a CFG-based analysis rather than just looking at the AST. + // + // TODO(dcc): have nodesHaveSameControlFlow() use a CFG + Predicate isEarlyExitPredicate = new Predicate() { + @Override + public boolean apply(Node input) { + int nodeType = input.getType(); + + return nodeType == Token.RETURN + || nodeType == Token.BREAK + || nodeType == Token.CONTINUE; + } + }; + + return !NodeUtil.has(node2DeepestControlDependentBlock, + isEarlyExitPredicate, NOT_FUNCTION_PREDICATE); + } else { + return true; + } + } else { + return false; + } + } + + /** + * Returns true if the number of times the child executes depends on the + * parent. + * + * For example, the guard of an IF is not control dependent on the + * IF, but its two THEN/ELSE blocks are. + * + * Also, the guard of WHILE and DO are control dependent on the parent + * since the number of times it executes depends on the parent. + */ + private static boolean isControlDependentChild(Node child) { + Node parent = child.getParent(); + + if (parent == null) { + return false; + } + + ArrayList siblings = Lists.newArrayList(parent.children()); + + int indexOfChildInParent = siblings.indexOf(child); + + switch(parent.getType()) { + case Token.IF: + case Token.HOOK: + return (indexOfChildInParent == 1 || indexOfChildInParent == 2); + case Token.WHILE: + case Token.DO: + return true; + case Token.FOR: + // Only initializer is not control dependent + return indexOfChildInParent != 0; + case Token.SWITCH: + return indexOfChildInParent > 0; + case Token.AND: + return true; + case Token.OR: + return true; + case Token.FUNCTION: + return true; + + default: + return false; + } + } + + private static Node closestControlDependentAncestor(Node node) { + if (isControlDependentChild(node)) { + return node; + } + + // Note: node is not considered one of its ancestors + for (Node ancestor : node.getAncestors()) { + if (isControlDependentChild(ancestor)) { + return ancestor; + } + } + + return null; + } + + /** + * Returns true if {@code possibleAncestor} is an ancestor of{@code node}. + * A node is not considered to be an ancestor of itself. + */ + private static boolean nodeHasAncestor(Node node, Node possibleAncestor) { + // Note node is not in node.getAncestors() + + for (Node ancestor : node.getAncestors()) { + if (ancestor == possibleAncestor) { + return true; + } + } + + return false; + } + + /** + * Returns true if a node has a CALL or a NEW descendant. + */ + private boolean nodeHasCall(Node node) { + return NodeUtil.has(node, new Predicate() { + @Override + public boolean apply(Node input) { + return input.isCall() || input.isNew(); + }}, + NOT_FUNCTION_PREDICATE); + } + + /** + * Represents an environment across which code might be moved, i.e. the set + * of code that could be run in between the source and the destination. + * + * SideEffectAnalysis characterizes the code to be moved and the environment + * in order to determine if they interact in such a way as to make the move + * unsafe. + * + * Since determining the environment for an optimization can be tricky, + * we provide several concrete subclasses that common classes of optimizations + * may be able to reuse. + */ + public abstract static class AbstractMotionEnvironment { + + /** + * Calculates the set of nodes that this environment represents. + */ + public abstract Set calculateEnvironment(); + } + + /** + * An environment for motion within a function. Given a + * control flow graph and a source and destination node in the control + * flow graph, instances of this object will calculate the environment between + * the source and destination. + */ + public static class IntraproceduralMotionEnvironment + extends AbstractMotionEnvironment { + + /** + * Creates an intraprocedural motion environment. + * + * @param controlFlowGraph A control flow graph for function in which + * code will be moved + * @param cfgSource The code to be moved + * @param cfgDestination The node immediately before which cfgSource + * will be moved + */ + public IntraproceduralMotionEnvironment( + ControlFlowGraph controlFlowGraph, + Node cfgSource, + Node cfgDestination) { + + } + + @Override + public Set calculateEnvironment() { + // TODO(dcc): Implement IntraproceduralMotionEnvironment + return null; + } + } + + /** + * An environment for motion between modules. Given a + * module graph and as well as source and destination nodes and modules, + * instances of this object will calculate the environment between the source + * and destination. + */ + public static class CrossModuleMotionEnvironment + extends AbstractMotionEnvironment { + + /** + * Creates a cross module code motion environment. + * + * @param sourceNode The code to be moved + * @param sourceModule The module for the code to be moved + * @param destinationNode The node before which sourceNode will be inserted + * @param destinationModule The module that destination is in + * @param moduleGraph The module graph of the entire program + */ + public CrossModuleMotionEnvironment(Node sourceNode, + JSModule sourceModule, + Node destinationNode, + JSModule destinationModule, + JSModuleGraph moduleGraph) { + + } + + @Override + public Set calculateEnvironment() { + // TODO(dcc): Implement CrossModuleMotionEnvironment + return null; + } + } + /** + * A low-level concrete environment that allows the client to specify + * the environment nodes directly. Clients may wish to use this environment + * if none of the higher-level environments fit their needs. + */ + public static class RawMotionEnvironment + extends AbstractMotionEnvironment { + Set environment; + + public RawMotionEnvironment(Set environment) { + this.environment = environment; + } + + @Override + public Set calculateEnvironment() { + return environment; + } + } + + /* + * A combined representation for location set summaries. + * + * Basically, it is often easier to shuffle MOD/REF around together; this is + * a value class for that purpose. + */ + private static class LocationSummary { + + private EffectLocation modSet; + private EffectLocation refSet; + + public LocationSummary(EffectLocation modSet, EffectLocation refSet) { + this.modSet = modSet; + this.refSet = refSet; + } + + public EffectLocation getModSet() { + return modSet; + } + + public EffectLocation getRefSet() { + return refSet; + } + } + + /** + * Interface representing the notion of an effect location -- an abstract + * location that can be modified or referenced. + * + *

      Since there are an infinite number of possible concrete locations + * in a running program, this abstraction must be imprecise (i.e. there + * will be some distinct concrete locations that are indistinguishable + * under the abstraction). + * + *

      Different location abstractions will provide their + * own implementations of this interface, based on the level and kind + * of precision they provide. + */ + private static interface EffectLocation { + + /** + * Does the receiver's effect location intersect a given effect location? + * That is, could any of the concrete storage locations (fields, variables, + * etc.) represented by the receiver be contained in the set of concrete + * storage locations represented by the given abstract effect location. + */ + public boolean intersectsLocation(EffectLocation otherLocation); + + /** + * Returns the result of merging the given effect location with + * the receiver. The concrete locations represented by the result must + * include all the concrete locations represented by each of the merged + * locations and may also possibly include more (i.e., a join may + * introduce a loss of precision). + */ + public EffectLocation join(EffectLocation otherLocation); + + /** + * Does the effect location represent any possible concrete locations? + */ + public boolean isEmpty(); + } + + /** + * An abstract class representing a location abstraction. (Here "abstraction" + * means an imprecise representation of concrete side effects.) + * + *

      Implementations of this class will each provide own their + * implementation(s) of SideEffectLocation and methods to determine the side + * effect locations of a given piece of code. + */ + private abstract static class LocationAbstraction { + + /** Calculates the abstraction-specific side effects + * for the node. + */ + abstract LocationSummary calculateLocationSummary(Node node); + + /** + * Returns an abstraction-specific EffectLocation representing + * no location. + * + *

      The bottom location joined with any location should return + * that location. + */ + abstract EffectLocation getBottomLocation(); + + /** + * Calculates the abstraction-specific side effects + * for the node. + */ + public LocationSummary calculateLocationSummary(Set nodes) { + EffectLocation modAccumulator = getBottomLocation(); + EffectLocation refAccumulator = getBottomLocation(); + + for (Node node : nodes) { + LocationSummary nodeLocationSummary = calculateLocationSummary(node); + + modAccumulator = modAccumulator.join(nodeLocationSummary.getModSet()); + refAccumulator = refAccumulator.join(nodeLocationSummary.getRefSet()); + } + + return new LocationSummary(modAccumulator, refAccumulator); + } + } + /** + * A very imprecise location abstraction in which there are only two abstract + * locations: one representing all concrete locations and one for bottom + * (no concrete locations). + * + * This implementation is a thin wrapper on NodeUtil.mayHaveSideEffects() + * and NodeUtil.canBeSideEffected() -- it doesn't add any real value other + * than to prototype the LocationAbstraction interface. + */ + private static class DegenerateLocationAbstraction + extends LocationAbstraction { + + private static final EffectLocation EVERY_LOCATION = + new DegenerateEffectLocation(); + + private static final EffectLocation NO_LOCATION = + new DegenerateEffectLocation(); + + @Override + EffectLocation getBottomLocation() { + return NO_LOCATION; + } + + @Override + public LocationSummary calculateLocationSummary(Node node) { + return new LocationSummary(calculateModSet(node), calculateRefSet(node)); + } + + EffectLocation calculateRefSet(Node node) { + if (NodeUtil.canBeSideEffected(node)) { + return EVERY_LOCATION; + } else { + return NO_LOCATION; + } + } + + EffectLocation calculateModSet(Node node) { + if (NodeUtil.mayHaveSideEffects(node)) { + return EVERY_LOCATION; + } else { + return NO_LOCATION; + } + } + + private static class DegenerateEffectLocation implements EffectLocation { + @Override + public EffectLocation join(EffectLocation otherLocation) { + if (otherLocation == EVERY_LOCATION) { + return otherLocation; + } else { + return this; + } + } + + @Override + public boolean intersectsLocation(EffectLocation otherLocation) { + return this == EVERY_LOCATION && otherLocation == EVERY_LOCATION; + } + + @Override + public boolean isEmpty() { + return this == NO_LOCATION; + } + } + } + + /** + * A location abstraction based on the visibility of concrete locations. + * + * A global variables are treated as one common location, as are all heap + * storage locations. + * + * Local variables are broken up into two classes, one for truly local + * variables and one for local variables captured by an inner scope. Each + * of these classes has their own separate location representing the + * variables in the class. + * + * Parameter variables are considered to be heap locations since they + * can be accessed via the arguments object which itself can be aliased. + * + * A more precise analysis could: + * 1) put parameters on the heap only when "arguments" is actually used + * in a method + * 2) recognize that GETPROPs cannot access or modify parameters, only + * GETELEMs + * + * TODO(dcc): Don't merge parameters with the heap unless necessary. + * + * Internally, abstract locations are represented as integers + * with bits set (masks) representing the storage classes in the location, so + * that joining is bit-wise ORing and intersection is bitwise AND. + */ + private static class VisibilityLocationAbstraction + extends LocationAbstraction { + + /** The "bottom" location. Used to signify an empty location set */ + private static final int VISIBILITY_LOCATION_NONE = 0; + + /** The "top" location. Used to signify the set containing all locations */ + private static final int UNKNOWN_LOCATION_MASK = 0xFFFFFFFF; + + private static final int LOCAL_VARIABLE_LOCATION_MASK = 1 << 1; + + private static final int CAPTURED_LOCAL_VARIABLE_LOCATION_MASK = 1 << 2; + + private static final int GLOBAL_VARIABLE_LOCATION_MASK = 1 << 3; + + private static final int HEAP_LOCATION_MASK = 1 << 4; + + AbstractCompiler compiler; + + VariableVisibilityAnalysis variableVisibilityAnalysis; + VariableUseDeclarationMap variableUseMap; + + private VisibilityLocationAbstraction(AbstractCompiler compiler, + VariableVisibilityAnalysis variableVisibilityAnalysis, + VariableUseDeclarationMap variableUseMap) { + this.compiler = compiler; + this.variableVisibilityAnalysis = variableVisibilityAnalysis; + this.variableUseMap = variableUseMap; + } + + /** + * Calculates the MOD/REF summary for the given node. + */ + @Override + LocationSummary calculateLocationSummary(Node node) { + int visibilityRefLocations = VISIBILITY_LOCATION_NONE; + int visibilityModLocations = VISIBILITY_LOCATION_NONE; + + for (Node reference : findStorageLocationReferences(node)) { + int effectMask; + + if (reference.isName()) { + // Variable access + effectMask = effectMaskForVariableReference(reference); + } else { + // Heap access + effectMask = HEAP_LOCATION_MASK; + } + + if (storageNodeIsLValue(reference)) { + visibilityModLocations |= effectMask; + } + + if (storageNodeIsRValue(reference)) { + visibilityRefLocations |= effectMask; + } + } + + VisibilityBasedEffectLocation modSet = + new VisibilityBasedEffectLocation(visibilityModLocations); + + VisibilityBasedEffectLocation refSet = + new VisibilityBasedEffectLocation(visibilityRefLocations); + + return new LocationSummary(modSet, refSet); + } + + /** + * Returns the set of references to storage locations (both variables + * and the heap) under {@code root}. + */ + private Set findStorageLocationReferences(Node root) { + final Set references = Sets.newHashSet(); + + NodeTraversal.traverse(compiler, root, new AbstractShallowCallback() { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (NodeUtil.isGet(n) + || (n.isName() && !parent.isFunction())) { + references.add(n); + } + } + }); + + return references; + } + + /** + * Calculates the effect mask for a variable reference. + */ + private int effectMaskForVariableReference(Node variableReference) { + Preconditions.checkArgument(variableReference.isName()); + + int effectMask = VISIBILITY_LOCATION_NONE; + + Node declaringNameNode = + variableUseMap.findDeclaringNameNodeForUse(variableReference); + + if (declaringNameNode != null) { + VariableVisibility visibility = + variableVisibilityAnalysis.getVariableVisibility(declaringNameNode); + + switch (visibility) { + case LOCAL: + effectMask = LOCAL_VARIABLE_LOCATION_MASK; + break; + case CAPTURED_LOCAL: + effectMask = CAPTURED_LOCAL_VARIABLE_LOCATION_MASK; + break; + case PARAMETER: + // Parameters are considered to be on the heap since they + // can be accessed via the arguments object. + effectMask = HEAP_LOCATION_MASK; + break; + case GLOBAL: + effectMask = GLOBAL_VARIABLE_LOCATION_MASK; + break; + default: + throw new IllegalStateException("Unrecognized variable" + + " visibility: " + visibility); + } + } else { + // Couldn't find a variable for the reference + effectMask = UNKNOWN_LOCATION_MASK; + } + + return effectMask; + } + + @Override + EffectLocation getBottomLocation() { + return new VisibilityBasedEffectLocation(VISIBILITY_LOCATION_NONE); + } + + /** + * Returns true if the node is a storage node. + * + * Only NAMEs, GETPROPs, and GETELEMs are storage nodes. + */ + private static boolean isStorageNode(Node node) { + return node.isName() || NodeUtil.isGet(node); + } + + /** + * Return true if the storage node is an r-value. + */ + private static boolean storageNodeIsRValue(Node node) { + Preconditions.checkArgument(isStorageNode(node)); + + // We consider all names to be r-values unless + // LHS of Token.ASSIGN + // LHS of of for in expression + // Child of VAR + + Node parent = node.getParent(); + + if (storageNodeIsLValue(node)) { + // Assume l-value is NOT an r-value + // unless it is a non-simple assign + // or an increment/decrement + + boolean nonSimpleAssign = + NodeUtil.isAssignmentOp(parent) && !parent.isAssign(); + + return (nonSimpleAssign + || parent.isDec() + || parent.isInc()); + } + + return true; + } + + /** + * Return true if the storage node is an l-value. + */ + private static boolean storageNodeIsLValue(Node node) { + Preconditions.checkArgument(isStorageNode(node)); + return NodeUtil.isLValue(node); + } + + /** + * An abstract effect location based the visibility of the + * concrete storage location. + * + * See {@link VisibilityLocationAbstraction} for deeper description + * of this abstraction. + * + * The effect locations are stored as bits set on an integer, so + * intersect, join, etc. are the standard bitwise operations. + */ + private static class VisibilityBasedEffectLocation + implements EffectLocation { + int visibilityMask = VISIBILITY_LOCATION_NONE; + + public VisibilityBasedEffectLocation(int visibilityMask) { + this.visibilityMask = visibilityMask; + } + + @Override + public boolean intersectsLocation(EffectLocation otherLocation) { + Preconditions.checkArgument(otherLocation instanceof + VisibilityBasedEffectLocation); + + int otherMask = + ((VisibilityBasedEffectLocation) otherLocation).visibilityMask; + + return (visibilityMask & otherMask) > 0; + } + + @Override + public boolean isEmpty() { + return visibilityMask == VISIBILITY_LOCATION_NONE; + } + + @Override + public EffectLocation join(EffectLocation otherLocation) { + Preconditions.checkArgument(otherLocation instanceof + VisibilityBasedEffectLocation); + + int otherMask = + ((VisibilityBasedEffectLocation) otherLocation).visibilityMask; + + int joinedMask = visibilityMask | otherMask; + + return new VisibilityBasedEffectLocation(joinedMask); + } + } + } + + /** + * Maps NAME nodes that refer to variables to the NAME + * nodes that declared them. + */ + private static class VariableUseDeclarationMap { + + private AbstractCompiler compiler; + + // Maps a using name to its declaring name + private Map referencesByNameNode; + + public VariableUseDeclarationMap(AbstractCompiler compiler) { + this.compiler = compiler; + } + + /** + * Adds a map from each use NAME in {@code root} to its corresponding + * declaring name, *provided the declaration is also under root*. + * + * If the declaration is not under root, then the reference will + * not be added to the map. + */ + public void mapUses(Node root) { + referencesByNameNode = Maps.newHashMap(); + + ReferenceCollectingCallback callback = + new ReferenceCollectingCallback(compiler, + ReferenceCollectingCallback.DO_NOTHING_BEHAVIOR); + + NodeTraversal.traverse(compiler, root, callback); + + for (Var variable : callback.getAllSymbols()) { + ReferenceCollection referenceCollection = + callback.getReferences(variable); + + for (Reference reference : referenceCollection.references) { + Node referenceNameNode = reference.getNode(); + + // Note that this counts a declaration as a reference to itself + referencesByNameNode.put(referenceNameNode, variable.getNameNode()); + } + } + } + + /** + * Returns the NAME node for the declaration of the variable + * that {@code usingNameNode} refers to, if it is in the map, + * or {@code null} otherwise. + */ + public Node findDeclaringNameNodeForUse(Node usingNameNode) { + Preconditions.checkArgument(usingNameNode.isName()); + + return referencesByNameNode.get(usingNameNode); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SimpleDefinitionFinder.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SimpleDefinitionFinder.java new file mode 100644 index 0000000..fb9b5c2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SimpleDefinitionFinder.java @@ -0,0 +1,465 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.javascript.jscomp.DefinitionsRemover.Definition; +import com.google.javascript.jscomp.DefinitionsRemover.ExternalNameOnlyDefinition; +import com.google.javascript.jscomp.DefinitionsRemover.UnknownDefinition; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Simple name-based definition gatherer that implements + * {@link DefinitionProvider}. + * + * It treats all variable writes as happening in the global scope and + * treats all objects as capable of having the same set of properties. + * The current implementation only handles definitions whose right + * hand side is an immutable value or function expression. All + * complex definitions are treated as unknowns. + * + */ +class SimpleDefinitionFinder implements CompilerPass, DefinitionProvider { + private final AbstractCompiler compiler; + private final Map definitionSiteMap; + private final Multimap nameDefinitionMultimap; + private final Multimap nameUseSiteMultimap; + + public SimpleDefinitionFinder(AbstractCompiler compiler) { + this.compiler = compiler; + this.definitionSiteMap = Maps.newLinkedHashMap(); + this.nameDefinitionMultimap = LinkedHashMultimap.create(); + this.nameUseSiteMultimap = LinkedHashMultimap.create(); + } + + /** + * Returns the collection of definition sites found during traversal. + * + * @return definition site collection. + */ + public Collection getDefinitionSites() { + return definitionSiteMap.values(); + } + + private DefinitionSite getDefinitionAt(Node node) { + return definitionSiteMap.get(node); + } + + DefinitionSite getDefinitionForFunction(Node function) { + Preconditions.checkState(function.isFunction()); + return getDefinitionAt(getNameNodeFromFunctionNode(function)); + } + + @Override + public Collection getDefinitionsReferencedAt(Node useSite) { + if (definitionSiteMap.containsKey(useSite)) { + return null; + } + + if (useSite.isGetProp()) { + String propName = useSite.getLastChild().getString(); + if (propName.equals("apply") || propName.equals("call")) { + useSite = useSite.getFirstChild(); + } + } + + String name = getSimplifiedName(useSite); + if (name != null) { + Collection defs = nameDefinitionMultimap.get(name); + if (!defs.isEmpty()) { + return defs; + } else { + return null; + } + } else { + return null; + } + } + + @Override + public void process(Node externs, Node source) { + NodeTraversal.traverse( + compiler, externs, new DefinitionGatheringCallback(true)); + NodeTraversal.traverse( + compiler, source, new DefinitionGatheringCallback(false)); + NodeTraversal.traverse( + compiler, source, new UseSiteGatheringCallback()); + } + + /** + * Returns a collection of use sites that may refer to provided + * definition. Returns an empty collection if the definition is not + * used anywhere. + * + * @param definition Definition of interest. + * @return use site collection. + */ + Collection getUseSites(Definition definition) { + String name = getSimplifiedName(definition.getLValue()); + return nameUseSiteMultimap.get(name); + } + + /** + * Extract a name from a node. In the case of GETPROP nodes, + * replace the namespace or object expression with "this" for + * simplicity and correctness at the expense of inefficiencies due + * to higher chances of name collisions. + * + * TODO(user) revisit. it would be helpful to at least use fully + * qualified names in the case of namespaces. Might not matter as + * much if this pass runs after "collapsing properties". + */ + private static String getSimplifiedName(Node node) { + if (node.isName()) { + String name = node.getString(); + if (name != null && !name.isEmpty()) { + return name; + } else { + return null; + } + } else if (node.isGetProp()) { + return "this." + node.getLastChild().getString(); + } + return null; + } + + private class DefinitionGatheringCallback extends AbstractPostOrderCallback { + private boolean inExterns; + + DefinitionGatheringCallback(boolean inExterns) { + this.inExterns = inExterns; + } + + @Override + public void visit(NodeTraversal traversal, Node node, Node parent) { + // Arguments of external functions should not count as name + // definitions. They are placeholder names for documentation + // purposes only which are not reachable from anywhere. + if (inExterns && node.isName() && parent.isParamList()) { + return; + } + + Definition def = + DefinitionsRemover.getDefinition(node, inExterns); + if (def != null) { + String name = getSimplifiedName(def.getLValue()); + if (name != null) { + Node rValue = def.getRValue(); + if ((rValue != null) && + !NodeUtil.isImmutableValue(rValue) && + !rValue.isFunction()) { + + // Unhandled complex expression + Definition unknownDef = + new UnknownDefinition(def.getLValue(), inExterns); + def = unknownDef; + } + + // TODO(johnlenz) : remove this stub dropping code if it becomes + // illegal to have untyped stubs in the externs definitions. + if (inExterns) { + // We need special handling of untyped externs stubs here: + // the stub should be dropped if the name is provided elsewhere. + + List stubsToRemove = Lists.newArrayList(); + String qualifiedName = node.getQualifiedName(); + + // If there is no qualified name for this, then there will be + // no stubs to remove. This will happen if node is an object + // literal key. + if (qualifiedName != null) { + for (Definition prevDef : nameDefinitionMultimap.get(name)) { + if (prevDef instanceof ExternalNameOnlyDefinition + && !jsdocContainsDeclarations(node)) { + String prevName = prevDef.getLValue().getQualifiedName(); + if (qualifiedName.equals(prevName)) { + // Drop this stub, there is a real definition. + stubsToRemove.add(prevDef); + } + } + } + + for (Definition prevDef : stubsToRemove) { + nameDefinitionMultimap.remove(name, prevDef); + } + } + } + + nameDefinitionMultimap.put(name, def); + definitionSiteMap.put(node, + new DefinitionSite(node, + def, + traversal.getModule(), + traversal.inGlobalScope(), + inExterns)); + } + } + + if (inExterns && (parent != null) && parent.isExprResult()) { + String name = getSimplifiedName(node); + if (name != null) { + + // TODO(johnlenz) : remove this code if it becomes illegal to have + // stubs in the externs definitions. + + // We need special handling of untyped externs stubs here: + // the stub should be dropped if the name is provided elsewhere. + // We can't just drop the stub now as it needs to be used as the + // externs definition if no other definition is provided. + + boolean dropStub = false; + if (!jsdocContainsDeclarations(node)) { + String qualifiedName = node.getQualifiedName(); + if (qualifiedName != null) { + for (Definition prevDef : nameDefinitionMultimap.get(name)) { + String prevName = prevDef.getLValue().getQualifiedName(); + if (qualifiedName.equals(prevName)) { + dropStub = true; + break; + } + } + } + } + + if (!dropStub) { + // Incomplete definition + Definition definition = new ExternalNameOnlyDefinition(node); + nameDefinitionMultimap.put(name, definition); + definitionSiteMap.put(node, + new DefinitionSite(node, + definition, + traversal.getModule(), + traversal.inGlobalScope(), + inExterns)); + } + } + } + } + + /** + * @return Whether the node has a JSDoc that actually declares something. + */ + private boolean jsdocContainsDeclarations(Node node) { + JSDocInfo info = node.getJSDocInfo(); + return (info != null && info.containsDeclaration()); + } + } + + private class UseSiteGatheringCallback extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal traversal, Node node, Node parent) { + + Collection defs = getDefinitionsReferencedAt(node); + if (defs == null) { + return; + } + + Definition first = defs.iterator().next(); + + String name = getSimplifiedName(first.getLValue()); + Preconditions.checkNotNull(name); + nameUseSiteMultimap.put( + name, + new UseSite(node, traversal.getScope(), traversal.getModule())); + } + } + + /** + * @param use A use site to check. + * @return Whether the use is a call or new. + */ + static boolean isCallOrNewSite(UseSite use) { + Node call = use.node.getParent(); + if (call == null) { + // The node has been removed from the AST. + return false; + } + // We need to make sure we're dealing with a call to the function we're + // optimizing. If the the first child of the parent is not the site, this + // is a nested call and it's a call to another function. + return NodeUtil.isCallOrNew(call) && call.getFirstChild() == use.node; + } + + boolean canModifyDefinition(Definition definition) { + if (isExported(definition)) { + return false; + } + + // Don't modify unused definitions for two reasons: + // 1) It causes unnecessary churn + // 2) Other definitions might be used to reflect on this one using + // goog.reflect.object (the check for definitions with uses is below). + Collection useSites = getUseSites(definition); + if (useSites.isEmpty()) { + return false; + } + + for (UseSite site : useSites) { + // This catches the case where an object literal in goog.reflect.object + // and a prototype method have the same property name. + + // NOTE(nicksantos): Maps and trogedit both do this by different + // mechanisms. + + Node nameNode = site.node; + Collection singleSiteDefinitions = + getDefinitionsReferencedAt(nameNode); + if (singleSiteDefinitions.size() > 1) { + return false; + } + + Preconditions.checkState(!singleSiteDefinitions.isEmpty()); + Preconditions.checkState(singleSiteDefinitions.contains(definition)); + } + + return true; + } + + /** + * @return Whether the definition is directly exported. + */ + private boolean isExported(Definition definition) { + // Assume an exported method result is used. + Node lValue = definition.getLValue(); + if (lValue == null) { + return true; + } + + String partialName; + if (lValue.isGetProp()) { + partialName = lValue.getLastChild().getString(); + } else if (lValue.isName()) { + partialName = lValue.getString(); + } else { + // GETELEM is assumed to be an export or other expression are unknown + // uses. + return true; + } + + CodingConvention codingConvention = compiler.getCodingConvention(); + if (codingConvention.isExported(partialName)) { + return true; + } + + return false; + } + + /** + * @return Whether the function is defined in a non-aliasing expression. + */ + static boolean isSimpleFunctionDeclaration(Node fn) { + Node parent = fn.getParent(); + Node gramps = parent.getParent(); + + // Simple definition finder doesn't provide useful results in some + // cases, specifically: + // - functions with recursive definitions + // - functions defined in object literals + // - functions defined in array literals + // Here we defined a set of known function declaration that are 'ok'. + + // Some projects seem to actually define "JSCompiler_renameProperty" + // rather than simply having an extern definition. Don't mess with it. + Node nameNode = SimpleDefinitionFinder.getNameNodeFromFunctionNode(fn); + if (nameNode != null + && nameNode.isName()) { + String name = nameNode.getString(); + if (name.equals(NodeUtil.JSC_PROPERTY_NAME_FN) || + name.equals( + ObjectPropertyStringPreprocess.EXTERN_OBJECT_PROPERTY_STRING)) { + return false; + } + } + + // example: function a(){}; + if (NodeUtil.isFunctionDeclaration(fn)) { + return true; + } + + // example: a = function(){}; + // example: var a = function(){}; + if (fn.getFirstChild().getString().isEmpty() + && (NodeUtil.isExprAssign(gramps) || parent.isName())) { + return true; + } + + return false; + } + + /** + * @return the node defining the name for this function (if any). + */ + static Node getNameNodeFromFunctionNode(Node function) { + Preconditions.checkState(function.isFunction()); + if (NodeUtil.isFunctionDeclaration(function)) { + return function.getFirstChild(); + } else { + Node parent = function.getParent(); + if (NodeUtil.isVarDeclaration(parent)) { + return parent; + } else if (parent.isAssign()) { + return parent.getFirstChild(); + } else if (NodeUtil.isObjectLitKey(parent, parent.getParent())) { + return parent; + } + } + return null; + } + + /** + * Traverse a node and its children and remove any references to from + * the structures. + */ + void removeReferences(Node node) { + if (DefinitionsRemover.isDefinitionNode(node)) { + DefinitionSite defSite = definitionSiteMap.get(node); + if (defSite != null) { + Definition def = defSite.definition; + String name = getSimplifiedName(def.getLValue()); + if (name != null) { + this.definitionSiteMap.remove(node); + this.nameDefinitionMultimap.remove(name, node); + } + } + } else { + Node useSite = node; + if (useSite.isGetProp()) { + String propName = useSite.getLastChild().getString(); + if (propName.equals("apply") || propName.equals("call")) { + useSite = useSite.getFirstChild(); + } + } + String name = getSimplifiedName(useSite); + if (name != null) { + this.nameUseSiteMultimap.remove(name, new UseSite(useSite, null, null)); + } + } + + for (Node child : node.children()) { + removeReferences(child); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SimpleFunctionAliasAnalysis.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SimpleFunctionAliasAnalysis.java new file mode 100644 index 0000000..f1d6f7e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SimpleFunctionAliasAnalysis.java @@ -0,0 +1,119 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.DefinitionsRemover.Definition; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Set; + +/** + * Uses {@link SimpleDefinitionFinder} to determine if a function has been + * aliased or exposed to .call() or .apply(). + * + * @author dcc@google.com (Devin Coughlin) + */ +class SimpleFunctionAliasAnalysis { + private Set aliasedFunctions; + + private Set functionsExposedToCallOrApply; + + /** + * Returns true if the function is aliased. + * + * Must only be called after {@link #analyze(SimpleDefinitionFinder)} + * has been called. + */ + public boolean isAliased(Node functionNode) { + Preconditions.checkNotNull(aliasedFunctions); + Preconditions.checkArgument(functionNode.isFunction()); + + return aliasedFunctions.contains(functionNode); + } + + /** + * Returns true if the function ever exposed to .call() or .apply(). + * + * Must only be called after {@link #analyze(SimpleDefinitionFinder)} + * has been called. + */ + public boolean isExposedToCallOrApply(Node functionNode) { + Preconditions.checkNotNull(functionsExposedToCallOrApply); + Preconditions.checkArgument(functionNode.isFunction()); + + return functionsExposedToCallOrApply.contains(functionNode); + } + + /** + * Uses the provided {@link SimpleDefinitionFinder} to determine + * which functions are aliased or exposed to .call() or .apply(). + */ + public void analyze(SimpleDefinitionFinder finder) { + Preconditions.checkState(aliasedFunctions == null); + + aliasedFunctions = Sets.newHashSet(); + functionsExposedToCallOrApply = Sets.newHashSet(); + + for (DefinitionSite definitionSite : finder.getDefinitionSites()) { + Definition definition = definitionSite.definition; + + if (!definition.isExtern()) { + Node rValue = definition.getRValue(); + + if (rValue != null && rValue.isFunction()) { + // rValue is a Token.FUNCTION from a definition + + for (UseSite useSite : finder.getUseSites(definition)) { + updateFunctionForUse(rValue, useSite.node); + } + } + } + } + } + + /** + * Updates alias and exposure information based a site where the function is + * used. + * + * Note: this method may be called multiple times per Function, each time + * with a different useNode. + */ + private void updateFunctionForUse(Node function, Node useNode) { + Node useParent = useNode.getParent(); + int parentType = useParent.getType(); + + if ((parentType == Token.CALL || parentType == Token.NEW) + && useParent.getFirstChild() == useNode) { + // Regular call sites don't count as aliases + } else if (NodeUtil.isGet(useParent)) { + // GET{PROP,ELEM} don't count as aliases + // but we have to check for using them in .call and .apply. + + if (useParent.isGetProp()) { + Node gramps = useParent.getParent(); + if (NodeUtil.isFunctionObjectApply(gramps) || + NodeUtil.isFunctionObjectCall(gramps)) { + functionsExposedToCallOrApply.add(function); + } + } + } else { + aliasedFunctions.add(function); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SimpleRegion.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SimpleRegion.java new file mode 100644 index 0000000..9d18e13 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SimpleRegion.java @@ -0,0 +1,47 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +/** + * Simple region. + */ +public class SimpleRegion implements Region { + private final int beginningLineNumber; + private final int endingLineNumber; + private final String source; + + public SimpleRegion(int beginningLineNumber, int endingLineNumber, + String source) { + this.beginningLineNumber = beginningLineNumber; + this.endingLineNumber = endingLineNumber; + this.source = source; + } + + @Override + public int getBeginningLineNumber() { + return beginningLineNumber; + } + + @Override + public int getEndingLineNumber() { + return endingLineNumber; + } + + @Override + public String getSourceExcerpt() { + return source; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SourceAst.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SourceAst.java new file mode 100644 index 0000000..5fd27f5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SourceAst.java @@ -0,0 +1,55 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.Node; + +import java.io.Serializable; + +/** + * An interface for accessing the AST root of an input. + * + */ +public interface SourceAst extends Serializable { + /** + * Gets the root node of the AST for the source file this represents. The AST + * is lazily instantiated and cached. + */ + public Node getAstRoot(AbstractCompiler compiler); + + /** + * Removes any references to root node of the AST. If it is requested again, + * another parse will be performed. This method is needed to allow the ASTs + * to be garbage collected if the inputs are still around after compilation. + */ + public void clearAst(); + + /** @return The input id associated with this AST */ + public InputId getInputId(); + + /** Returns the source file the generated AST represents. */ + public SourceFile getSourceFile(); + + /** + * Sets the source file the generated AST represents. This can be called after + * deserializing if access to the source file is needed. If a different file + * is provided than that with which this was created, an IllegalStateException + * will be thrown. + */ + public void setSourceFile(SourceFile file); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SourceExcerptProvider.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SourceExcerptProvider.java new file mode 100644 index 0000000..8971c3b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SourceExcerptProvider.java @@ -0,0 +1,93 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +/** + * A source excerpt provider is responsible for building source code excerpt + * of specific locations, such as a specific line or a region around a + * given line number. + * + */ +public interface SourceExcerptProvider { + /** + * Source excerpt variety. + */ + enum SourceExcerpt { + /** + * Line excerpt. + */ + LINE { + @Override + public String get(SourceExcerptProvider source, String sourceName, + int lineNumber, ExcerptFormatter formatter) { + return formatter.formatLine( + source.getSourceLine(sourceName, lineNumber), lineNumber); + } + }, + /** + * Region excerpt. + */ + REGION { + @Override + public String get(SourceExcerptProvider source, String sourceName, + int lineNumber, ExcerptFormatter formatter) { + return formatter.formatRegion( + source.getSourceRegion(sourceName, lineNumber)); + } + }; + + /** + * Get a source excerpt string based on the type of the source excerpt. + */ + public abstract String get(SourceExcerptProvider source, String sourceName, + int lineNumber, ExcerptFormatter formatter); + } + + /** + * Get the line indicated by the line number. This call will return only the + * specific line. + * + * @param lineNumber the line number, 1 being the first line of the file + * @return the line indicated, or {@code null} if it does not exist + */ + String getSourceLine(String sourceName, int lineNumber); + + /** + * Get a region around the indicated line number. The exact definition of a + * region is implementation specific, but it must contain the line indicated + * by the line number. A region must not start or end by a carriage return. + * + * @param lineNumber the line number, 1 being the first line of the file + * @return the region around the line number indicated, or null + * if it does not exist + */ + Region getSourceRegion(String sourceName, int lineNumber); + + /** + * A excerpt formatter is responsible of formatting source excerpts. + */ + interface ExcerptFormatter { + /** + * Format a line excerpt. + */ + String formatLine(String line, int lineNumber); + + /** + * Format a region excerpt. + */ + String formatRegion(Region region); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SourceFile.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SourceFile.java new file mode 100644 index 0000000..2fd501a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SourceFile.java @@ -0,0 +1,536 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Charsets; +import com.google.common.io.CharStreams; +import com.google.common.io.Files; +import com.google.javascript.rhino.jstype.StaticSourceFile; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.Serializable; +import java.io.StringReader; +import java.nio.charset.Charset; +import java.util.Arrays; + +/** + * An abstract representation of a source file that provides access to + * language-neutral features. The source file can be loaded from various + * locations, such as from disk or from a preloaded string. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class SourceFile implements StaticSourceFile, Serializable { + private static final long serialVersionUID = 1L; + + /** A JavaScript source code provider. The value should + * be cached so that the source text stays consistent throughout a single + * compile. */ + public interface Generator { + public String getCode(); + } + + /** + * Number of lines in the region returned by {@link #getRegion(int)}. + * This length must be odd. + */ + private static final int SOURCE_EXCERPT_REGION_LENGTH = 5; + + private final String fileName; + private boolean isExternFile = false; + + // The fileName may not always identify the original file - for example, + // supersourced Java inputs, or Java inputs that come from Jar files. This + // is an optional field that the creator of an AST or SourceFile can set. + // It could be a path to the original file, or in case this SourceFile came + // from a Jar, it could be the path to the Jar. + private String originalPath = null; + + // Source Line Information + private int[] lineOffsets = null; + + private String code = null; + + /** + * Construct a new abstract source file. + * + * @param fileName The file name of the source file. It does not necessarily + * need to correspond to a real path. But it should be unique. Will + * appear in warning messages emitted by the compiler. + */ + public SourceFile(String fileName) { + if (fileName == null || fileName.isEmpty()) { + throw new IllegalArgumentException("a source must have a name"); + } + this.fileName = fileName; + } + + @Override + public int getLineOffset(int lineno) { + findLineOffsets(); + if (lineno < 1 || lineno > lineOffsets.length) { + throw new IllegalArgumentException( + "Expected line number between 1 and " + lineOffsets.length + + "\nActual: " + lineno); + } + return lineOffsets[lineno - 1]; + } + + /** @return The number of lines in this source file. */ + int getNumLines() { + findLineOffsets(); + return lineOffsets.length; + } + + + private void findLineOffsets() { + if (lineOffsets != null) { + return; + } + try { + String[] sourceLines = getCode().split("\n"); + lineOffsets = new int[sourceLines.length]; + for (int ii = 1; ii < sourceLines.length; ++ii) { + lineOffsets[ii] = + lineOffsets[ii - 1] + sourceLines[ii - 1].length() + 1; + } + } catch (IOException e) { + lineOffsets = new int[1]; + lineOffsets[0] = 0; + } + } + + + ////////////////////////////////////////////////////////////////////////////// + // Implementation + + /** + * Gets all the code in this source file. + * @throws IOException + */ + public String getCode() throws IOException { + return code; + } + + /** + * Gets a reader for the code in this source file. + */ + public Reader getCodeReader() throws IOException { + return new StringReader(getCode()); + } + + @VisibleForTesting + String getCodeNoCache() { + return code; + } + + private void setCode(String sourceCode) { + code = sourceCode; + } + + public String getOriginalPath() { + return originalPath != null ? originalPath : fileName; + } + + public void setOriginalPath(String originalPath) { + this.originalPath = originalPath; + } + + // For SourceFile types which cache source code that can be regenerated + // easily, flush the cache. We maintain the cache mostly to speed up + // generating source when displaying error messages, so dumping the file + // contents after the compile is a fine thing to do. + public void clearCachedSource() { + // By default, do nothing. Not all kinds of SourceFiles can regenerate + // code. + } + + boolean hasSourceInMemory() { + return code != null; + } + + /** Returns a unique name for the source file. */ + @Override + public String getName() { + return fileName; + } + + /** Returns whether this is an extern. */ + @Override + public boolean isExtern() { + return isExternFile; + } + + /** Sets that this is an extern. */ + void setIsExtern(boolean newVal) { + isExternFile = newVal; + } + + @Override + public int getLineOfOffset(int offset) { + findLineOffsets(); + int search = Arrays.binarySearch(lineOffsets, offset); + if (search >= 0) { + return search + 1; // lines are 1-based. + } else { + int insertionPoint = -1 * (search + 1); + return Math.min(insertionPoint - 1, lineOffsets.length - 1) + 1; + } + } + + @Override + public int getColumnOfOffset(int offset) { + int line = getLineOfOffset(offset); + return offset - lineOffsets[line - 1]; + } + + /** + * Gets the source line for the indicated line number. + * + * @param lineNumber the line number, 1 being the first line of the file. + * @return The line indicated. Does not include the newline at the end + * of the file. Returns {@code null} if it does not exist, + * or if there was an IO exception. + */ + public String getLine(int lineNumber) { + findLineOffsets(); + if (lineNumber > lineOffsets.length) { + return null; + } + + if (lineNumber < 1) { + lineNumber = 1; + } + + int pos = lineOffsets[lineNumber - 1]; + String js = ""; + try { + // NOTE(nicksantos): Right now, this is optimized for few warnings. + // This is probably the right trade-off, but will be slow if there + // are lots of warnings in one file. + js = getCode(); + } catch (IOException e) { + return null; + } + + if (js.indexOf('\n', pos) == -1) { + // If next new line cannot be found, there are two cases + // 1. pos already reaches the end of file, then null should be returned + // 2. otherwise, return the contents between pos and the end of file. + if (pos >= js.length()) { + return null; + } else { + return js.substring(pos, js.length()); + } + } else { + return js.substring(pos, js.indexOf('\n', pos)); + } + } + + /** + * Get a region around the indicated line number. The exact definition of a + * region is implementation specific, but it must contain the line indicated + * by the line number. A region must not start or end by a carriage return. + * + * @param lineNumber the line number, 1 being the first line of the file. + * @return The line indicated. Returns {@code null} if it does not exist, + * or if there was an IO exception. + */ + public Region getRegion(int lineNumber) { + String js = ""; + try { + js = getCode(); + } catch (IOException e) { + return null; + } + int pos = 0; + int startLine = Math.max(1, + lineNumber - (SOURCE_EXCERPT_REGION_LENGTH + 1) / 2 + 1); + for (int n = 1; n < startLine; n++) { + int nextpos = js.indexOf('\n', pos); + if (nextpos == -1) { + break; + } + pos = nextpos + 1; + } + int end = pos; + int endLine = startLine; + for (int n = 0; n < SOURCE_EXCERPT_REGION_LENGTH; n++, endLine++) { + end = js.indexOf('\n', end); + if (end == -1) { + break; + } + end++; + } + if (lineNumber >= endLine) { + return null; + } + if (end == -1) { + int last = js.length() - 1; + if (js.charAt(last) == '\n') { + return + new SimpleRegion(startLine, endLine, js.substring(pos, last)); + } else { + return new SimpleRegion(startLine, endLine, js.substring(pos)); + } + } else { + return new SimpleRegion(startLine, endLine, js.substring(pos, end)); + } + } + + @Override + public String toString() { + return fileName; + } + + public static SourceFile fromFile(String fileName, Charset c) { + return builder().withCharset(c).buildFromFile(fileName); + } + + public static SourceFile fromFile(String fileName) { + return builder().buildFromFile(fileName); + } + + public static SourceFile fromFile(File file, Charset c) { + return builder().withCharset(c).buildFromFile(file); + } + + public static SourceFile fromFile(File file) { + return builder().buildFromFile(file); + } + + public static SourceFile fromCode(String fileName, String code) { + return builder().buildFromCode(fileName, code); + } + + public static SourceFile fromCode(String fileName, + String originalPath, String code) { + return builder().withOriginalPath(originalPath) + .buildFromCode(fileName, code); + } + + public static SourceFile fromInputStream(String fileName, InputStream s) + throws IOException { + return builder().buildFromInputStream(fileName, s); + } + + public static SourceFile fromInputStream(String fileName, + String originalPath, InputStream s) throws IOException { + return builder().withOriginalPath(originalPath) + .buildFromInputStream(fileName, s); + } + + public static SourceFile fromReader(String fileName, Reader r) + throws IOException { + return builder().buildFromReader(fileName, r); + } + + public static SourceFile fromGenerator(String fileName, + Generator generator) { + return builder().buildFromGenerator(fileName, generator); + } + + /** Create a new builder for source files. */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder interface for source files. + * + * Allows users to customize the Charset, and the original path of + * the source file (if it differs from the path on disk). + */ + public static class Builder { + private Charset charset = Charsets.UTF_8; + private String originalPath = null; + + public Builder() {} + + /** Set the charset to use when reading from an input stream or file. */ + public Builder withCharset(Charset charset) { + this.charset = charset; + return this; + } + + /** Set the original path to use. */ + public Builder withOriginalPath(String originalPath) { + this.originalPath = originalPath; + return this; + } + + public SourceFile buildFromFile(String fileName) { + return buildFromFile(new File(fileName)); + } + + public SourceFile buildFromFile(File file) { + return new OnDisk(file, originalPath, charset); + } + + public SourceFile buildFromCode(String fileName, String code) { + return new Preloaded(fileName, originalPath, code); + } + + public SourceFile buildFromInputStream(String fileName, InputStream s) + throws IOException { + return buildFromCode(fileName, + CharStreams.toString(new InputStreamReader(s, charset))); + } + + public SourceFile buildFromReader(String fileName, Reader r) + throws IOException { + return buildFromCode(fileName, CharStreams.toString(r)); + } + + public SourceFile buildFromGenerator(String fileName, + Generator generator) { + return new Generated(fileName, originalPath, generator); + } + } + + + ////////////////////////////////////////////////////////////////////////////// + // Implementations + + /** + * A source file where the code has been preloaded. + */ + static class Preloaded extends SourceFile { + private static final long serialVersionUID = 1L; + + Preloaded(String fileName, String originalPath, String code) { + super(fileName); + super.setOriginalPath(originalPath); + super.setCode(code); + } + } + + /** + * A source file where the code will be dynamically generated + * from the injected interface. + */ + static class Generated extends SourceFile { + private static final long serialVersionUID = 1L; + private final Generator generator; + + // Not private, so that LazyInput can extend it. + Generated(String fileName, String originalPath, Generator generator) { + super(fileName); + super.setOriginalPath(originalPath); + this.generator = generator; + } + + @Override + public synchronized String getCode() throws IOException { + String cachedCode = super.getCode(); + + if (cachedCode == null) { + cachedCode = generator.getCode(); + super.setCode(cachedCode); + } + return cachedCode; + } + + // Clear out the generated code when finished with a compile; we can + // regenerate it if we ever need it again. + @Override + public void clearCachedSource() { + super.setCode(null); + } + } + + /** + * A source file where the code is only read into memory if absolutely + * necessary. We will try to delay loading the code into memory as long as + * possible. + */ + static class OnDisk extends SourceFile { + private static final long serialVersionUID = 1L; + private final File file; + + // This is stored as a String, but passed in and out as a Charset so that + // we can serialize the class. + // Default input file format for JSCompiler has always been UTF_8. + private String inputCharset = Charsets.UTF_8.name(); + + OnDisk(File file, String originalPath, Charset c) { + super(file.getPath()); + this.file = file; + super.setOriginalPath(originalPath); + if (c != null) { + this.setCharset(c); + } + } + + @Override + public synchronized String getCode() throws IOException { + String cachedCode = super.getCode(); + + if (cachedCode == null) { + cachedCode = Files.toString(file, this.getCharset()); + super.setCode(cachedCode); + } + return cachedCode; + } + + /** + * Gets a reader for the code in this source file. + */ + @Override + public Reader getCodeReader() throws IOException { + if (hasSourceInMemory()) { + return super.getCodeReader(); + } else { + // If we haven't pulled the code into memory yet, don't. + return new FileReader(file); + } + } + + // Flush the cached code after the compile; we can read it off disk + // if we need it again. + @Override + public void clearCachedSource() { + super.setCode(null); + } + + /** + * Store the Charset specification as the string version of the name, + * rather than the Charset itself. This allows us to serialize the + * SourceFile class. + * @param c charset to use when reading the input. + */ + public void setCharset(Charset c) { + inputCharset = c.name(); + } + + /** + * Get the Charset specifying how we're supposed to read the file + * in off disk and into UTF-16. This is stored as a strong to allow + * SourceFile to be serialized. + * @return Charset object representing charset to use. + */ + public Charset getCharset() { + return Charset.forName(inputCharset); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SourceInformationAnnotator.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SourceInformationAnnotator.java new file mode 100755 index 0000000..ffbf384 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SourceInformationAnnotator.java @@ -0,0 +1,96 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * Annotates nodes with information from their original input file + * before the compiler performs work that changes this information (such + * as its original location, its original name, etc). + * + * Information saved: + * + * - Annotates all NAME nodes with an ORIGINALNAME_PROP indicating its original + * name. + * + * - Annotates all string GET_PROP nodes with an ORIGINALNAME_PROP. + * + * - Annotates all OBJECT_LITERAL unquoted string key nodes with an + * ORIGINALNAME_PROP. + * + * - Annotates all FUNCTION nodes with an ORIGINALNAME_PROP indicating its + * nearest original name. + * + */ +class SourceInformationAnnotator extends + NodeTraversal.AbstractPostOrderCallback { + private final String sourceFile; + private final boolean doSanityChecks; + + public SourceInformationAnnotator( + String sourceFile, boolean doSanityChecks) { + this.sourceFile = sourceFile; + this.doSanityChecks = doSanityChecks; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + // Verify the source file is annotated. + if (doSanityChecks && sourceFile != null) { + Preconditions.checkState(sourceFile.equals( + n.getSourceFileName())); + } + + // Annotate the original name. + switch (n.getType()) { + case Token.GETPROP: + Node propNode = n.getLastChild(); + setOriginalName(n, propNode.getString()); + break; + + case Token.FUNCTION: + String functionName = NodeUtil.getNearestFunctionName(n); + if (functionName != null) { + setOriginalName(n, functionName); + } + break; + + case Token.NAME: + setOriginalName(n, n.getString()); + break; + + case Token.OBJECTLIT: + for (Node key = n.getFirstChild(); key != null; + key = key.getNext()) { + // We only want keys were unquoted. + if (!key.isQuotedString()) { + setOriginalName(key, key.getString()); + } + } + break; + } + } + + void setOriginalName(Node n, String name) { + if (!name.isEmpty() && n.getProp(Node.ORIGINALNAME_PROP) == null) { + n.putProp(Node.ORIGINALNAME_PROP, name); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SourceMap.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SourceMap.java new file mode 100644 index 0000000..7192408 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SourceMap.java @@ -0,0 +1,209 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Predicate; +import com.google.common.collect.Maps; +import com.google.debugging.sourcemap.FilePosition; +import com.google.debugging.sourcemap.SourceMapFormat; +import com.google.debugging.sourcemap.SourceMapGenerator; +import com.google.debugging.sourcemap.SourceMapGeneratorFactory; +import com.google.debugging.sourcemap.SourceMapGeneratorV1; +import com.google.debugging.sourcemap.SourceMapGeneratorV2; +import com.google.javascript.rhino.Node; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Collects information mapping the generated (compiled) source back to + * its original source for debugging purposes. + * + * @see CodeConsumer + * @see CodeGenerator + * @see CodePrinter + * + */ +public class SourceMap { + + public static enum Format { + V1 { + @Override SourceMap getInstance() { + return new SourceMap( + SourceMapGeneratorFactory.getInstance(SourceMapFormat.V1)); + } + }, + DEFAULT { + @Override SourceMap getInstance() { + return new SourceMap( + SourceMapGeneratorFactory.getInstance(SourceMapFormat.DEFAULT)); + } + }, + V2 { + @Override SourceMap getInstance() { + return new SourceMap( + SourceMapGeneratorFactory.getInstance(SourceMapFormat.V2)); + } + }, + V3 { + @Override SourceMap getInstance() { + return new SourceMap( + SourceMapGeneratorFactory.getInstance(SourceMapFormat.V3)); + } + }; + abstract SourceMap getInstance(); + } + + /** + * Source maps can be very large different levels of detail can be specified. + */ + public static enum DetailLevel implements Predicate { + // ALL is best when the fullest details are needed for debugging or for + // code-origin analysis. + ALL { + @Override public boolean apply(Node node) { + return true; + } + }, + // SYMBOLS is intended to be used for stack trace deobfuscation when full + // detail is not needed. + SYMBOLS { + @Override public boolean apply(Node node) { + return node.isCall() + || node.isNew() + || node.isFunction() + || node.isName() + || NodeUtil.isGet(node) + || NodeUtil.isObjectLitKey(node, node.getParent()) + || (node.isString() && NodeUtil.isGet(node.getParent())); + } + }; + } + + public static class LocationMapping { + final String prefix; + final String replacement; + public LocationMapping(String prefix, String replacement) { + this.prefix = prefix; + this.replacement = replacement; + } + } + + private final SourceMapGenerator generator; + private List prefixMappings = Collections.emptyList(); + private final Map sourceLocationFixupCache = + Maps.newHashMap(); + + private SourceMap(SourceMapGenerator generator) { + this.generator = generator; + } + + public void addMapping( + Node node, + FilePosition outputStartPosition, + FilePosition outputEndPosition) { + String sourceFile = node.getSourceFileName(); + + // If the node does not have an associated source file or + // its line number is -1, then the node does not have sufficient + // information for a mapping to be useful. + if (sourceFile == null || node.getLineno() < 0) { + return; + } + + sourceFile = fixupSourceLocation(sourceFile); + + String originalName = (String) node.getProp(Node.ORIGINALNAME_PROP); + + // Strangely, Rhino source lines are one based but columns are + // zero based. + // We don't change this for the v1 or v2 source maps but for + // v3 we make them both 0 based. + int lineBaseOffset = 1; + if (generator instanceof SourceMapGeneratorV1 + || generator instanceof SourceMapGeneratorV2) { + lineBaseOffset = 0; + } + + generator.addMapping( + sourceFile, originalName, + new FilePosition(node.getLineno() - lineBaseOffset, node.getCharno()), + outputStartPosition, outputEndPosition); + } + + /** + * @param sourceFile The source file location to fixup. + * @return a remapped source file. + */ + private String fixupSourceLocation(String sourceFile) { + if (prefixMappings.isEmpty()) { + return sourceFile; + } + + String fixed = sourceLocationFixupCache.get(sourceFile); + if (fixed != null) { + return fixed; + } + + // Replace the first prefix found with its replacement + for (LocationMapping mapping : prefixMappings) { + if (sourceFile.startsWith(mapping.prefix)) { + fixed = mapping.replacement + sourceFile.substring( + mapping.prefix.length()); + break; + } + } + + // If none of the mappings match then use the original file path. + if (fixed == null) { + fixed = sourceFile; + } + + sourceLocationFixupCache.put(sourceFile, fixed); + return fixed; + } + + public void appendTo(Appendable out, String name) throws IOException { + generator.appendTo(out, name); + } + + public void reset() { + generator.reset(); + sourceLocationFixupCache.clear(); + } + + public void setStartingPosition(int offsetLine, int offsetIndex) { + generator.setStartingPosition(offsetLine, offsetIndex); + } + + public void setWrapperPrefix(String prefix) { + generator.setWrapperPrefix(prefix); + } + + public void validate(boolean validate) { + generator.validate(validate); + } + + /** + * @param sourceMapLocationMappings + */ + public void setPrefixMappings(List sourceMapLocationMappings) { + this.prefixMappings = sourceMapLocationMappings; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SpecializationAwareCompilerPass.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SpecializationAwareCompilerPass.java new file mode 100644 index 0000000..1f31bec --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SpecializationAwareCompilerPass.java @@ -0,0 +1,28 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +/** + * Interface indicating a CompilerPass is specialization aware. + * + * See {@link SpecializeModule} for details of module specialization. + * + * @author dcc@google.com (Devin Coughlin) + * + */ +interface SpecializationAwareCompilerPass extends CompilerPass { + public void enableSpecialization(SpecializeModule.SpecializationState state); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SpecializeModule.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SpecializeModule.java new file mode 100644 index 0000000..8c0b12c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SpecializeModule.java @@ -0,0 +1,753 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Beginnings of an optimization to specialize the initial module at the cost of + * increasing code in later modules. This is still very experimental. + * + * High-level overview: + * + * This optimization replaces functions in the initial module with specialized + * versions that are only safe in the initial module. The original, general, + * versions of the functions are "fixed up" in later modules. This optimization + * can shrink the initial module significantly but the fixup code in later + * modules increases overall code size. + * + * Implementation approach: + * + * We take a ridiculously naive approach: remove the initial module + * from the rest of the AST, optimize it with existing optimization passes + * (recording which functions have been specialized), put it back in the AST, + * and add fixups restoring the general versions of the functions in each module + * that depends on the initial module. + * + * Since it is only safe to specialize functions that can be fixed up, we + * don't allow specialization of local functions and functions that + * are aliased. + * + * We currently run three optimizations on the isolated AST: InlineFunctions, + * DevirtualizePrototypeMethods, and RemoveUnusedPrototypeProperties. + * + * These optimizations rely on a coarse-grained name-based analysis to + * maintain safety properties and thus are likely to see some benefit when + * applied in isolation. + * + * InlineFunctions is truly specializing -- it replaces functions with + * versions that have calls to other functions inlined into them, while + * RemoveUnusedPrototypeProperties is really just removing properties that + * aren't used in the initial module and adding copies further down in the + * module graph. It would probably be more elegant to give + * CrossModuleMethodMotion permission to make copies of methods instead. + * + * There are additional passes that might benefit from being made + * specialization-aware: + * + * - OptimizeParameters + * + * - Any pass that is too slow to run over the entire AST but might + * be acceptable on only the initial module: + * - RemoveUnusedNames + * + * - Also, any pass that uses the results of PureFunctionIdentifier to + * determine when it is safe to remove code might benefit (e.g. the peephole + * passes), since PureFunctionIdentifier relies on SimpleDefinitionFinder, + * which would be more precise when running on only the initial module. + * + * @author dcc@google.com (Devin Coughlin) + */ +class SpecializeModule implements CompilerPass { + private AbstractCompiler compiler; + + private Map specializedInputRootsByOriginal; + + private Map + functionInfoBySpecializedFunctionNode; + + private SpecializationState specializationState; + + private final PassFactory[] specializationPassFactories; + + public SpecializeModule(AbstractCompiler compiler, + PassFactory ...specializationPassFactories) { + this.compiler = compiler; + this.specializationPassFactories = specializationPassFactories; + } + + /** + * Performs initial module specialization. + * + * The process is as follows: + * + * 1) Make a copy of each of the inputs in the initial root and put them + * in a fake AST that looks like it is the whole program. + * + * 2) Run the specializing compiler passes over the fake initial module AST + * until it reaches a fixed point, recording which functions are specialized + * or removed. + * + * 3) Replace the original input roots with the specialized input roots + * + * 4) For each module that directly depends on the initial module, add + * fixups for the specialized and removed functions. Right now we add + * fixups for for every function that was specialized or removed -- we could + * be smarter about this and for each dependent module only add the functions + * that it needs. + * + * 5) Add dummy variables declaring the removed function to the end of + * the now-specialized initial module. This is needed to keep + * {@link VarCheck} from complaining. + */ + @Override + public void process(Node externs, Node root) { + JSModuleGraph moduleGraph = compiler.getModuleGraph(); + + // Can't perform optimization without a module graph! + if (moduleGraph == null) { + return; + } + + JSModule module = moduleGraph.getRootModule(); + + Node fakeModuleRoot = copyModuleInputs(module); + + SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); + + defFinder.process(externs, fakeModuleRoot); + + SimpleFunctionAliasAnalysis initialModuleFunctionAliasAnalysis = + new SimpleFunctionAliasAnalysis(); + + initialModuleFunctionAliasAnalysis.analyze(defFinder); + + specializationState = + new SpecializationState(initialModuleFunctionAliasAnalysis); + + do { + specializationState.resetHasChanged(); + + for (SpecializationAwareCompilerPass pass : createSpecializingPasses()) { + pass.enableSpecialization(specializationState); + pass.process(externs, fakeModuleRoot); + } + } while(specializationState.hasChanged()); + + // We must always add dummy variables before replacing the original module. + addDummyVarDeclarationsToInitialModule(module); + replaceOriginalModuleInputsWithSpecialized(); + addOriginalFunctionVersionsToDependentModules(module); + } + + /** + * Returns a collection of new instances of specializing passes. + */ + private Collection + createSpecializingPasses() { + + Collection passes = Lists.newLinkedList(); + + for (PassFactory passFactory : specializationPassFactories) { + CompilerPass pass = passFactory.create(compiler); + + Preconditions.checkState(pass instanceof + SpecializationAwareCompilerPass); + + passes.add((SpecializationAwareCompilerPass) pass); + } + + return passes; + } + + /** + * Creates an AST that consists solely of copies of the input roots for the + * passed in module. + * + * Also records a map in {@link #functionInfoBySpecializedFunctionNode} + * of information about the original function keyed on the copies of the + * functions to specialized. + */ + private Node copyModuleInputs(JSModule module) { + + specializedInputRootsByOriginal = Maps.newLinkedHashMap(); + + functionInfoBySpecializedFunctionNode = Maps.newLinkedHashMap(); + + Node syntheticModuleJsRoot = IR.block(); + syntheticModuleJsRoot.setIsSyntheticBlock(true); + + for (CompilerInput input : module.getInputs()) { + Node originalInputRoot = input.getAstRoot(compiler); + + Node copiedInputRoot = originalInputRoot.cloneTree(); + copiedInputRoot.copyInformationFromForTree(originalInputRoot); + + specializedInputRootsByOriginal.put(originalInputRoot, + copiedInputRoot); + + matchTopLevelFunctions(originalInputRoot, copiedInputRoot); + + syntheticModuleJsRoot.addChildToBack(copiedInputRoot); + } + + // The jsRoot needs a parent (in a normal compilation this would be the + // node that contains jsRoot and the externs). + Node syntheticExternsAndJsRoot = IR.block(); + syntheticExternsAndJsRoot.addChildToBack(syntheticModuleJsRoot); + + return syntheticModuleJsRoot; + } + + /** + * Records information about original functions and creates a map from + * the specialized functions to this information. + * + * This information is only recorded for global functions since non-global + * functions cannot be inlined. + * + * @param original An original input root. + * @param toBeSpecialized A copy of the input root (the copy to be + * specialized) + */ + private void matchTopLevelFunctions(Node original, Node toBeSpecialized) { + new NodeMatcher() { + @Override + public void reportMatch(Node original, Node specialized) { + if (original.isFunction()) { + OriginalFunctionInformation functionInfo = + new OriginalFunctionInformation(original); + + functionInfoBySpecializedFunctionNode.put(specialized, + functionInfo); + } + } + + @Override + public boolean shouldTraverse(Node n1, Node n2) { + return !n1.isFunction(); + } + }.match(original, toBeSpecialized); + } + + /** + * Replaces the original input roots of the initial module with + * their specialized versions. + * + * (Since {@link JsAst} holds a pointer to original inputs roots, we actually + * replace the all the children of the root rather than swapping the + * root pointers). + */ + private void replaceOriginalModuleInputsWithSpecialized() { + for (Node original : specializedInputRootsByOriginal.keySet()) { + Node specialized = specializedInputRootsByOriginal.get(original); + + original.removeChildren(); + + List specializedChildren = Lists.newLinkedList(); + + while (specialized.getFirstChild() != null) { + original.addChildToBack(specialized.removeFirstChild()); + } + } + } + + /** + * Adds dummy variable declarations for all the function declarations we've + * removed to the end of the initial module. + * + * We do this to make {@link VarCheck} happy, since it requires variables to + * be declared before they are used in the whole program AST and doesn't + * like it when they are declared multiple times. + * + * TODO(dcc): Be smarter about whether we need a VAR here or not. + */ + private void addDummyVarDeclarationsToInitialModule(JSModule module) { + for (Node modifiedFunction : + functionInfoBySpecializedFunctionNode.keySet()) { + if (specializationState.getRemovedFunctions().contains(modifiedFunction)) { + OriginalFunctionInformation originalInfo = + functionInfoBySpecializedFunctionNode.get(modifiedFunction); + + if (originalInfo.name != null && originalInfo.originalWasDeclaration()) { + Node block = specializationState.removedFunctionToBlock.get( + modifiedFunction); + + // Declaring block might be null if no fix-up declarations is needed. + // For example, InlineFunction can inline an anonymous function call or + // anything with prototype property requires no dummy declaration + // fix-ups afterward. + if (block != null) { + Node originalRoot = specializedInputRootsByOriginal.get(block); + block.addChildrenToBack(originalInfo.generateDummyDeclaration()); + } + } + } + } + } + + /** + * Adds a copy of the original versions of specialized/removed functions + * to each of the dependents of module. + * + * Currently we add all of these functions to all dependents; it + * would be more efficient to only add the functions that could be used. + * + * TODO(dcc): Only add fixup functions where needed. + */ + private void addOriginalFunctionVersionsToDependentModules(JSModule module) { + for (JSModule directDependent : getDirectDependents(module)) { + CompilerInput firstInput = directDependent.getInputs().get(0); + Node firstInputRootNode = firstInput.getAstRoot(compiler); + + // We don't iterate specializedFunctions directly because want to maintain + // and specializedFunctions in source order, rather than + // in the order that some optimization specialized the function. + + // So since we're adding to the front of the module each time, we + // have to iterate in reverse source order. + + List possiblyModifiedFunctions = + Lists.newArrayList(functionInfoBySpecializedFunctionNode.keySet()); + + Collections.reverse(possiblyModifiedFunctions); + + for (Node modifiedFunction : possiblyModifiedFunctions) { + boolean declarationWasSpecialized = + specializationState.getSpecializedFunctions() + .contains(modifiedFunction); + + boolean declarationWasRemoved = + specializationState.getRemovedFunctions() + .contains(modifiedFunction); + + if (declarationWasSpecialized || declarationWasRemoved) { + OriginalFunctionInformation originalInfo = + functionInfoBySpecializedFunctionNode.get(modifiedFunction); + + // Don't add unspecialized versions of anonymous functions + if (originalInfo.name != null) { + Node newDefinition = + originalInfo.generateFixupDefinition(); + + firstInputRootNode.addChildrenToFront(newDefinition); + } + } + } + } + } + + /** + * Returns a list of modules that directly depend on the given module. + * + * This probably deserves to be in JSModuleGraph. + */ + public Collection getDirectDependents(JSModule module) { + Set directDependents = Sets.newHashSet(); + + for (JSModule possibleDependent : + compiler.getModuleGraph().getAllModules()) { + if (possibleDependent.getDependencies().contains(module)) { + directDependents.add(possibleDependent); + } + } + + return directDependents; + } + + /** + * A simple abstract classes that takes two isomorphic ASTs and walks + * each of them together, reporting matches to subclasses. + * + * This could probably be hardened and moved to NodeUtil + */ + private abstract static class NodeMatcher { + + /** + * Calls {@link #reportMatch(Node, Node)} for each pair of matching nodes + * from the two ASTs. + * + * The two ASTs must be isomorphic. Currently no error checking is + * performed to ensure that this is the case. + */ + public void match(Node ast1, Node ast2) { + // Just blunder ahead and assume that the two nodes actually match + + reportMatch(ast1, ast2); + + if (shouldTraverse(ast1, ast2)) { + Node childOf1 = ast1.getFirstChild(); + Node childOf2 = ast2.getFirstChild(); + + while (childOf1 != null) { + match(childOf1, childOf2); + childOf1 = childOf1.getNext(); + childOf2 = childOf2.getNext(); + } + } + + } + + /** + * Subclasses should override to add their own behavior when two nodes + * are matched. + * @param n1 A node from the AST passed as ast1 in + * {@link #match(Node, Node)}. + * @param n2 A node from the AST passed as ast1 in + * {@link #match(Node, Node)}. + */ + public abstract void reportMatch(Node n1, Node n2); + + /** + * Subclasses should override to determine whether matching should proceed + * under a subtree. + */ + public boolean shouldTraverse(Node node1, Node n2) { + return true; + } + } + + /** + * A class that stores information about the original version of a + * function that will be/was specialized or removed. + * + * This class stores: + * - how the function was defined + * - a copy of the original function + */ + private class OriginalFunctionInformation { + private String name; + + /** + * a = function() {} if true; + * function a() {} otherwise + */ + private boolean isAssignFunction; + + private boolean assignHasVar; + + private Node originalFunctionCopy; + + public OriginalFunctionInformation(Node originalFunction) { + name = NodeUtil.getFunctionName(originalFunction); + + originalFunctionCopy = originalFunction.cloneTree(); + originalFunctionCopy.copyInformationFromForTree(originalFunction); + + Node originalParent = originalFunction.getParent(); + + isAssignFunction = originalParent.isAssign() || + originalParent.isName(); + + assignHasVar = + isAssignFunction && originalParent.getParent().isVar(); + } + + private Node copiedOriginalFunction() { + // Copy of a copy + Node copy = originalFunctionCopy.cloneTree(); + copy.copyInformationFromForTree(originalFunctionCopy); + + return copy; + } + + /** + * Did the original function add its name to scope? + * (If so, and specialization removes it, then we'll have to + * add a VAR for it so VarCheck doesn't complain). + */ + private boolean originalWasDeclaration() { + return (!isAssignFunction) || (assignHasVar); + } + + /** + * Generates a definition of the original function that can be added as + * a fixup in the modules that directly depend on the specialized module. + * + *

      +     * The trick here is that even if the original function is declared as:
      +     *
      +     * function foo() {
      +     *   // stuff
      +     * }
      +     *
      +     * the fixup will have to be of the form
      +     *
      +     * foo = function() {
      +     *   // stuff
      +     * }
      +     * 
      + * + */ + private Node generateFixupDefinition() { + Node functionCopy = copiedOriginalFunction(); + + Node nameNode; + + if (isAssignFunction) { + nameNode = + NodeUtil.newQualifiedNameNode( + compiler.getCodingConvention(), name, functionCopy, name); + } else { + // Grab the name node from the original function and make that + // function anonymous. + nameNode = functionCopy.getFirstChild(); + functionCopy.replaceChild(nameNode, + NodeUtil.newName(compiler.getCodingConvention(), "", nameNode)); + } + + Node assignment = IR.assign(nameNode, functionCopy); + assignment.copyInformationFrom(functionCopy); + + return NodeUtil.newExpr(assignment); + } + + /** + * Returns a new dummy var declaration for the function with no initial + * value: + * + * var name; + */ + private Node generateDummyDeclaration() { + Node declaration = NodeUtil.newVarNode(name, null); + declaration.copyInformationFromForTree(originalFunctionCopy); + + return declaration; + } + } + + /** + * A class to hold state during SpecializeModule. An instance of this class + * is passed to specialization-aware compiler passes -- they use it to + * communicate with SpecializeModule. + * + * SpecializationAware optimizations are required to keep track of the + * functions they remove and the functions that they modify so that the fixups + * can be added. However, not all functions can be fixed up. + * + * Specialization-aware classes *must* call + * {@link #reportSpecializedFunction} when a function is modified during + * specialization and {@link #reportRemovedFunction} when one is removed. + * + * Also, when specializing, they must query {@link #canFixupFunction} + * before modifying a function. + * + * This two-way communication, is the reason we don't use + * {@link AstChangeProxy} to report code changes. + */ + public static class SpecializationState { + + /** + * The functions that the pass has specialized. These functions will + * be fixed up in non-specialized modules to their more general versions. + * + * This field is also used to determine whether specialization is enabled. + * If not null, specialization is enabled, otherwise it is disabled. + */ + private Set specializedFunctions; + + /** + * The functions that the pass has removed. These functions will be + * redefined in non-specialized modules. + */ + private Set removedFunctions; + + private Map removedFunctionToBlock; + + private SimpleFunctionAliasAnalysis initialModuleAliasAnalysis; + + /** Will be true if any new functions have been removed or specialized since + * {@link #resetHasChanged}. + */ + private boolean hasChanged = false; + + public SpecializationState(SimpleFunctionAliasAnalysis + initialModuleAliasAnalysis) { + + this.initialModuleAliasAnalysis = initialModuleAliasAnalysis; + + specializedFunctions = Sets.newLinkedHashSet(); + removedFunctions = Sets.newLinkedHashSet(); + removedFunctionToBlock = Maps.newLinkedHashMap(); + } + + /** + * Returns true if any new functions have been reported as removed or + * specialized since {@link #resetHasChanged()} was last called. + */ + private boolean hasChanged() { + return hasChanged; + } + + private void resetHasChanged() { + hasChanged = false; + } + + /** + * Returns the functions specialized by this compiler pass. + */ + public Set getSpecializedFunctions() { + return specializedFunctions; + } + + /** + * Reports that a function has been specialized. + * + * @param functionNode A specialized AST node with type Token.FUNCTION + */ + public void reportSpecializedFunction(Node functionNode) { + if (specializedFunctions.add(functionNode)) { + hasChanged = true; + } + } + + /** + * Reports that the function containing the node has been specialized. + */ + public void reportSpecializedFunctionContainingNode(Node node) { + Node containingFunction = containingFunction(node); + + if (containingFunction != null) { + reportSpecializedFunction(containingFunction); + } + } + + /** + * The functions removed by this compiler pass. + */ + public Set getRemovedFunctions() { + return removedFunctions; + } + + /** + * Reports that a function has been removed. + * + * @param functionNode A removed AST node with type Token.FUNCTION + * @param declaringBlock If the function declaration puts a variable in the + * scope, we need to have a VAR statement in the scope where the + * function is declared. Null if the function does not put a name + * in the scope. + */ + public void reportRemovedFunction(Node functionNode, Node declaringBlock) { + // Depends when we were notified, functionNode.getParent might or might + // not be null. We are going to force the user to tell us the parent + // instead. + if (removedFunctions.add(functionNode)) { + hasChanged = true; + removedFunctionToBlock.put(functionNode, declaringBlock); + } + } + + /** + * Returns true if the function can be fixed up (that is, if it can be + * safely removed or specialized). + * + *

      In order to be safely fixed up, a function must be: + *

      +     * - in the global scope
      +     * - not aliased in the initial module
      +     * - of one of the following forms:
      +     *    function f() {}
      +     *    var f = function() {}
      +     *    f = function(){}
      +     *    var ns = {}; ns.f = function() {}
      +     *    SomeClass.prototype.foo = function() {};
      +     * 
      + * + *

      Anonymous functions cannot be safely fixed up, nor can functions + * that have been aliased. + * + *

      Some functions declared as object literals could be safely fixed up, + * however we do not currently support this. + */ + public boolean canFixupFunction(Node functionNode) { + Preconditions.checkArgument(functionNode.isFunction()); + + if (!nodeIsInGlobalScope(functionNode) || + initialModuleAliasAnalysis.isAliased(functionNode)) { + return false; + } + + if (NodeUtil.isStatement(functionNode)) { + // function F() {} + return true; + } + + Node parent = functionNode.getParent(); + Node gramps = parent.getParent(); + + if (parent.isName() && gramps.isVar()) { + // var f = function() {} + return true; + } + + if (NodeUtil.isExprAssign(gramps) + && parent.getChildAtIndex(1) == functionNode) { + // f = function() {} + // ns.f = function() {} + return true; + } + + return false; + } + + /** + * Returns true if the function containing n can be fixed up. + * Also returns true if n is in the global scope -- since it is always safe + * to specialize the global scope. + */ + public boolean canFixupSpecializedFunctionContainingNode(Node n) { + Node containingFunction = containingFunction(n); + if (containingFunction != null) { + return canFixupFunction(containingFunction); + } else { + // Always safe to specialize the global scope + return true; + } + } + + /** + * Returns true if a node is in the global scope; false otherwise. + */ + private boolean nodeIsInGlobalScope(Node node) { + return containingFunction(node) == null; + } + + /** + * Returns the function containing the node, or null if none exists. + */ + private Node containingFunction(Node node) { + for (Node ancestor : node.getAncestors()) { + if (ancestor.isFunction()) { + return ancestor; + } + } + + return null; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/StatementFusion.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/StatementFusion.java new file mode 100644 index 0000000..381d8b3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/StatementFusion.java @@ -0,0 +1,155 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * Tries to fuse all the statements in a block into a one statement by using + * COMMAs. + * + * Because COMMAs has the lowest precedence, we never need to insert + * extra () around. Once we have only one statement in a block, we can then + * eliminate a pair of {}'s. Further more, we can also fold a single + * statement IF into && or create further opportunities for all the other + * goodies in {@link PeepholeSubstituteAlternateSyntax}. + * + */ +public class StatementFusion extends AbstractPeepholeOptimization { + + @Override + Node optimizeSubtree(Node n) { + // The block of a function body always need { }. + if (!n.getParent().isFunction() && canFuseIntoOneStatement(n)) { + fuseIntoOneStatement(n); + reportCodeChange(); + } + return n; + } + + private boolean canFuseIntoOneStatement(Node block) { + // Fold only statement block. NOT scripts block. + if (!block.isBlock()) { + return false; + } + + // Nothing to do here. + if (!block.hasChildren() || block.hasOneChild()) { + return false; + } + + Node last = block.getLastChild(); + + for (Node c = block.getFirstChild(); c != null; c = c.getNext()) { + if (!c.isExprResult() && c != last) { + return false; + } + } + + // TODO(user): Support more control statement for fusion. + // FOR + switch(last.getType()) { + case Token.IF: + case Token.THROW: + case Token.SWITCH: + case Token.EXPR_RESULT: + return true; + case Token.RETURN: + // We don't want to add a new return value. + return last.hasChildren(); + case Token.FOR: + return NodeUtil.isForIn(last) && + // Avoid cases where we have for(var x = foo() in a) { .... + !mayHaveSideEffects(last.getFirstChild()); + } + + return false; + } + + private void fuseIntoOneStatement(Node block) { + Node cur = block.removeFirstChild(); + + // Starts building a tree. + Node commaTree = cur.removeFirstChild(); + + + while (block.hasMoreThanOneChild()) { + Node next = block.removeFirstChild().removeFirstChild(); + commaTree = fuseExpressionIntoExpression(commaTree, next); + } + + Preconditions.checkState(block.hasOneChild()); + Node last = block.getLastChild(); + + // Now we are just left with two statements. The comma tree of the first + // n - 1 statements (which can be used in an expression) and the last + // statement. We perform specific fusion based on the last statement's type. + switch(last.getType()) { + case Token.IF: + case Token.RETURN: + case Token.THROW: + case Token.SWITCH: + case Token.EXPR_RESULT: + fuseExpresssonIntoFirstChild(commaTree, last); + return; + case Token.FOR: + if (NodeUtil.isForIn(last)) { + fuseExpresssonIntoSecondChild(commaTree, last); + } + return ; + default: + throw new IllegalStateException("Statement fusion missing."); + } + } + + // exp1, exp1 + private static Node fuseExpressionIntoExpression(Node exp1, Node exp2) { + Node comma = new Node(Token.COMMA, exp1); + comma.copyInformationFrom(exp2); + + // We can just join the new comma expression with another comma but + // lets keep all the comma's in a straight line. That way we can use + // tree comparison. + if (exp2.isComma()) { + Node leftMostChild = exp2; + while(leftMostChild.isComma()) { + leftMostChild = leftMostChild.getFirstChild(); + } + Node parent = leftMostChild.getParent(); + comma.addChildToBack(leftMostChild.detachFromParent()); + parent.addChildToFront(comma); + return exp2; + } else { + comma.addChildToBack(exp2); + return comma; + } + } + + private static void fuseExpresssonIntoFirstChild(Node exp, Node stmt) { + Node val = stmt.removeFirstChild(); + Node comma = fuseExpressionIntoExpression(exp, val); + stmt.addChildToFront(comma); + } + + private static void fuseExpresssonIntoSecondChild(Node exp, Node stmt) { + Node val = stmt.removeChildAfter(stmt.getFirstChild()); + Node comma = fuseExpressionIntoExpression(exp, val); + stmt.addChildAfter(comma, stmt.getFirstChild()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/StrictModeCheck.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/StrictModeCheck.java new file mode 100644 index 0000000..b22f3ed --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/StrictModeCheck.java @@ -0,0 +1,260 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Set; + +/** + * Checks that the code obeys the static restrictions of strict mode: + *

        + *
      1. No use of "with". + *
      2. No deleting variables, functions, or arguments. + *
      3. No re-declarations or assignments of "eval" or arguments. + *
      4. No use of "eval" (optional check for Caja). + *
      + * + */ +class StrictModeCheck extends AbstractPostOrderCallback + implements CompilerPass { + + static final DiagnosticType UNKNOWN_VARIABLE = DiagnosticType.warning( + "JSC_UNKNOWN_VARIABLE", "unknown variable {0}"); + + static final DiagnosticType EVAL_USE = DiagnosticType.error( + "JSC_EVAL_USE", "\"eval\" cannot be used in Caja"); + + static final DiagnosticType EVAL_DECLARATION = DiagnosticType.warning( + "JSC_EVAL_DECLARATION", + "\"eval\" cannot be redeclared in ES5 strict mode"); + + static final DiagnosticType EVAL_ASSIGNMENT = DiagnosticType.warning( + "JSC_EVAL_ASSIGNMENT", + "the \"eval\" object cannot be reassigned in ES5 strict mode"); + + static final DiagnosticType ARGUMENTS_DECLARATION = DiagnosticType.warning( + "JSC_ARGUMENTS_DECLARATION", + "\"arguments\" cannot be redeclared in ES5 strict mode"); + + static final DiagnosticType ARGUMENTS_ASSIGNMENT = DiagnosticType.warning( + "JSC_ARGUMENTS_ASSIGNMENT", + "the \"arguments\" object cannot be reassigned in ES5 strict mode"); + + static final DiagnosticType DELETE_VARIABLE = DiagnosticType.warning( + "JSC_DELETE_VARIABLE", + "variables, functions, and arguments cannot be deleted in " + + "ES5 strict mode"); + + static final DiagnosticType ILLEGAL_NAME = DiagnosticType.error( + "JSC_ILLEGAL_NAME", + "identifiers ending in '__' cannot be used in Caja"); + + static final DiagnosticType DUPLICATE_OBJECT_KEY = DiagnosticType.warning( + "JSC_DUPLICATE_OBJECT_KEY", + "object literals cannot contain duplicate keys in ES5 strict mode"); + + static final DiagnosticType BAD_FUNCTION_DECLARATION = DiagnosticType.error( + "JSC_BAD_FUNCTION_DECLARATION", + "functions can only be declared at top level or immediately within " + + "another function in ES5 strict mode"); + + private final AbstractCompiler compiler; + private final boolean noVarCheck; + private final boolean noCajaChecks; + + StrictModeCheck(AbstractCompiler compiler) { + this(compiler, false, false); + } + + StrictModeCheck( + AbstractCompiler compiler, boolean noVarCheck, boolean noCajaChecks) { + this.compiler = compiler; + this.noVarCheck = noVarCheck; + this.noCajaChecks = noCajaChecks; + } + + @Override public void process(Node externs, Node root) { + NodeTraversal.traverseRoots( + compiler, Lists.newArrayList(externs, root), this); + NodeTraversal.traverse(compiler, root, new NonExternChecks()); + } + + @Override public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isFunction()) { + checkFunctionUse(t, n); + } else if (n.isName()) { + if (!isDeclaration(n)) { + checkNameUse(t, n); + } + } else if (n.isAssign()) { + checkAssignment(t, n); + } else if (n.isDelProp()) { + checkDelete(t, n); + } else if (n.isObjectLit()) { + checkObjectLiteral(t, n); + } else if (n.isLabel()) { + checkLabel(t, n); + } + } + + /** Checks that the function is used legally. */ + private void checkFunctionUse(NodeTraversal t, Node n) { + if (NodeUtil.isFunctionDeclaration(n) && !NodeUtil.isHoistedFunctionDeclaration(n)) { + t.report(n, BAD_FUNCTION_DECLARATION); + } + } + + /** + * Determines if the given name is a declaration, which can be a declaration + * of a variable, function, or argument. + */ + private static boolean isDeclaration(Node n) { + switch (n.getParent().getType()) { + case Token.VAR: + case Token.FUNCTION: + case Token.CATCH: + return true; + + case Token.PARAM_LIST: + return n.getParent().getParent().isFunction(); + + default: + return false; + } + } + + /** Checks that the given name is used legally. */ + private void checkNameUse(NodeTraversal t, Node n) { + Var v = t.getScope().getVar(n.getString()); + if (v == null) { + // In particular, this prevents creating a global variable by assigning + // to it without a declaration. + if (!noVarCheck) { + t.report(n, UNKNOWN_VARIABLE, n.getString()); + } + } + + if (!noCajaChecks) { + if ("eval".equals(n.getString())) { + t.report(n, EVAL_USE); + } else if (n.getString().endsWith("__")) { + t.report(n, ILLEGAL_NAME); + } + } + } + + /** Checks that an assignment is not to the "arguments" object. */ + private void checkAssignment(NodeTraversal t, Node n) { + if (n.getFirstChild().isName()) { + if ("arguments".equals(n.getFirstChild().getString())) { + t.report(n, ARGUMENTS_ASSIGNMENT); + } else if ("eval".equals(n.getFirstChild().getString())) { + // Note that assignment to eval is already illegal because any use of + // that name is illegal. + if (noCajaChecks) { + t.report(n, EVAL_ASSIGNMENT); + } + } + } + } + + /** Checks that variables, functions, and arguments are not deleted. */ + private void checkDelete(NodeTraversal t, Node n) { + if (n.getFirstChild().isName()) { + Var v = t.getScope().getVar(n.getFirstChild().getString()); + if (v != null) { + t.report(n, DELETE_VARIABLE); + } + } + } + + /** Checks that object literal keys are valid. */ + private void checkObjectLiteral(NodeTraversal t, Node n) { + Set getters = Sets.newHashSet(); + Set setters = Sets.newHashSet(); + for (Node key = n.getFirstChild(); + key != null; + key = key.getNext()) { + if (!noCajaChecks && key.getString().endsWith("__")) { + t.report(key, ILLEGAL_NAME); + } + if (!key.isSetterDef()) { + // normal property and getter cases + if (getters.contains(key.getString())) { + t.report(key, DUPLICATE_OBJECT_KEY); + } else { + getters.add(key.getString()); + } + } + if (!key.isGetterDef()) { + // normal property and setter cases + if (setters.contains(key.getString())) { + t.report(key, DUPLICATE_OBJECT_KEY); + } else { + setters.add(key.getString()); + } + } + } + } + + /** Checks that label names are valid. */ + private void checkLabel(NodeTraversal t, Node n) { + if (n.getFirstChild().getString().endsWith("__")) { + if (!noCajaChecks) { + t.report(n.getFirstChild(), ILLEGAL_NAME); + } + } + } + + /** Checks that are performed on non-extern code only. */ + private class NonExternChecks extends AbstractPostOrderCallback { + @Override public void visit(NodeTraversal t, Node n, Node parent) { + if ((n.isName()) && isDeclaration(n)) { + checkDeclaration(t, n); + } else if (n.isGetProp()) { + checkProperty(t, n); + } + } + + /** Checks for illegal declarations. */ + private void checkDeclaration(NodeTraversal t, Node n) { + if ("eval".equals(n.getString())) { + t.report(n, EVAL_DECLARATION); + } else if ("arguments".equals(n.getString())) { + t.report(n, ARGUMENTS_DECLARATION); + } else if (n.getString().endsWith("__")) { + if (!noCajaChecks) { + t.report(n, ILLEGAL_NAME); + } + } + } + + /** Checks for illegal property accesses. */ + private void checkProperty(NodeTraversal t, Node n) { + if (n.getLastChild().getString().endsWith("__")) { + if (!noCajaChecks) { + t.report(n.getLastChild(), ILLEGAL_NAME); + } + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/StrictWarningsGuard.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/StrictWarningsGuard.java new file mode 100644 index 0000000..c1a13fe --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/StrictWarningsGuard.java @@ -0,0 +1,42 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * All warnings should be reported as errors. + * + * @author anatol@google.com (Anatol Pomazau) + */ +public class StrictWarningsGuard extends WarningsGuard { + private static final long serialVersionUID = 1L; + + static final DiagnosticType UNRAISABLE_WARNING = + DiagnosticType.warning("JSC_UNRAISABLE_WARNING", "{0}"); + + @Override + public CheckLevel level(JSError error) { + if (error.getType() == UNRAISABLE_WARNING) { + return null; + } + return error.getDefaultLevel().isOn() ? CheckLevel.ERROR : null; + } + + @Override + protected int getPriority() { + return WarningsGuard.Priority.STRICT.value; // applied last + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Strings.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Strings.java new file mode 100644 index 0000000..ffea880 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Strings.java @@ -0,0 +1,119 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Guava code that will eventually be open-sourced properly. Package-private + * until they're able to do that. A lot of these methods are discouraged + * anyways. + * + * @author nicksantos@google.com (Nick Santos) + */ +class Strings { + private Strings() {} // All static + + /** + * If this given string is of length {@code maxLength} or less, it will + * be returned as-is. + * Otherwise, it will be truncated to {@code maxLength}, regardless of whether + * there are any space characters in the String. If an ellipsis is requested + * to be appended to the truncated String, the String will be truncated so + * that the ellipsis will also fit within maxLength. + * If no truncation was necessary, no ellipsis will be added. + * @param source the String to truncate if necessary + * @param maxLength the maximum number of characters to keep + * @param addEllipsis if true, and if the String had to be truncated, + * add "..." to the end of the String before returning. Additionally, + * the ellipsis will only be added if maxLength is greater than 3. + * @return the original string if it's length is less than or equal to + * maxLength, otherwise a truncated string as mentioned above + */ + static String truncateAtMaxLength(String source, int maxLength, + boolean addEllipsis) { + + if (source.length() <= maxLength) { + return source; + } + if (addEllipsis && maxLength > 3) { + return unicodePreservingSubstring(source, 0, maxLength - 3) + "..."; + } + return unicodePreservingSubstring(source, 0, maxLength); + } + + /** + * Normalizes {@code index} such that it respects Unicode character + * boundaries in {@code str}. + * + *

      If {@code index} is the low surrogate of a Unicode character, + * the method returns {@code index - 1}. Otherwise, {@code index} is + * returned. + * + *

      In the case in which {@code index} falls in an invalid surrogate pair + * (e.g. consecutive low surrogates, consecutive high surrogates), or if + * if it is not a valid index into {@code str}, the original value of + * {@code index} is returned. + * + * @param str the String + * @param index the index to be normalized + * @return a normalized index that does not split a Unicode character + */ + private static int unicodePreservingIndex(String str, int index) { + if (index > 0 && index < str.length()) { + if (Character.isHighSurrogate(str.charAt(index - 1)) && + Character.isLowSurrogate(str.charAt(index))) { + return index - 1; + } + } + return index; + } + + /** + * Returns a substring of {@code str} that respects Unicode character + * boundaries. + * + *

      The string will never be split between a [high, low] surrogate pair, + * as defined by {@link Character#isHighSurrogate} and + * {@link Character#isLowSurrogate}. + * + *

      If {@code begin} or {@code end} are the low surrogate of a Unicode + * character, it will be offset by -1. + * + *

      This behavior guarantees that + * {@code str.equals(StringUtil.unicodePreservingSubstring(str, 0, n) + + * StringUtil.unicodePreservingSubstring(str, n, str.length())) } is + * true for all {@code n}. + *

      + * + *

      This means that unlike {@link String#substring(int, int)}, the length of + * the returned substring may not necessarily be equivalent to + * {@code end - begin}. + * + * @param str the original String + * @param begin the beginning index, inclusive + * @param end the ending index, exclusive + * @return the specified substring, possibly adjusted in order to not + * split Unicode surrogate pairs + * @throws IndexOutOfBoundsException if the {@code begin} is negative, + * or {@code end} is larger than the length of {@code str}, or + * {@code begin} is larger than {@code end} + */ + private static String unicodePreservingSubstring( + String str, int begin, int end) { + return str.substring(unicodePreservingIndex(str, begin), + unicodePreservingIndex(str, end)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/StripCode.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/StripCode.java new file mode 100644 index 0000000..94c384a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/StripCode.java @@ -0,0 +1,608 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import javax.annotation.Nullable; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.CodingConvention.SubclassRelationship; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Set; + +/** + * A pass for stripping a list of provided JavaScript object types. + * + * The stripping strategy is as follows: + * - Provide: 1) a list of types that should be stripped, and 2) a list of + * suffixes of field/variable names that should be stripped. + * - Remove declarations of variables that are initialized using static + * methods of strip types (e.g. var x = goog.debug.Logger.getLogger(...);). + * - Remove all references to variables that are stripped. + * - Remove all object literal keys with strip names. + * - Remove all assignments to 1) field names that are strip names and + * 2) qualified names that begin with strip types. + * - Remove all statements containing calls to static methods of strip types. + * + */ +class StripCode implements CompilerPass { + + // TODO(user): Try eliminating the need for a list of strip names by instead + // recording which field names are assigned to debug types in each JS input. + private final AbstractCompiler compiler; + private final Set stripTypes; + private final Set stripNameSuffixes; + private final Set stripTypePrefixes; + private final Set stripNamePrefixes; + private final Set varsToRemove; + + static final DiagnosticType STRIP_TYPE_INHERIT_ERROR = DiagnosticType.error( + "JSC_STRIP_TYPE_INHERIT_ERROR", + "Non-strip type {0} cannot inherit from strip type {1}"); + + static final DiagnosticType STRIP_ASSIGNMENT_ERROR = DiagnosticType.error( + "JSC_STRIP_ASSIGNMENT_ERROR", + "Unable to strip assignment to {0}"); + + /** + * Creates an instance. + * + * @param compiler The compiler + */ + StripCode(AbstractCompiler compiler, + Set stripTypes, + Set stripNameSuffixes, + Set stripTypePrefixes, + Set stripNamePrefixes) { + + this.compiler = compiler; + this.stripTypes = Sets.newHashSet(stripTypes); + this.stripNameSuffixes = Sets.newHashSet(stripNameSuffixes); + this.stripTypePrefixes = Sets.newHashSet(stripTypePrefixes); + this.stripNamePrefixes = Sets.newHashSet(stripNamePrefixes); + this.varsToRemove = Sets.newHashSet(); + } + + /** + * Enables stripping of goog.tweak functions. + */ + public void enableTweakStripping() { + stripTypes.add("goog.tweak"); + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, new Strip()); + } + + // ------------------------------------------------------------------------- + + /** + * A callback that strips debug code from a JavaScript parse tree. + */ + private class Strip extends AbstractPostOrderCallback { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.VAR: + removeVarDeclarationsByNameOrRvalue(t, n, parent); + break; + + case Token.NAME: + maybeRemoveReferenceToRemovedVariable(t, n, parent); + break; + + case Token.ASSIGN: + case Token.ASSIGN_BITOR: + case Token.ASSIGN_BITXOR: + case Token.ASSIGN_BITAND: + case Token.ASSIGN_LSH: + case Token.ASSIGN_RSH: + case Token.ASSIGN_URSH: + case Token.ASSIGN_ADD: + case Token.ASSIGN_SUB: + case Token.ASSIGN_MUL: + case Token.ASSIGN_DIV: + case Token.ASSIGN_MOD: + maybeEliminateAssignmentByLvalueName(t, n, parent); + break; + + case Token.CALL: + case Token.NEW: + maybeRemoveCall(t, n, parent); + break; + + case Token.OBJECTLIT: + eliminateKeysWithStripNamesFromObjLit(t, n); + break; + + case Token.EXPR_RESULT: + maybeEliminateExpressionByName(t, n, parent); + break; + } + } + + /** + * Removes declarations of any variables whose names are strip names or + * whose whose r-values are static method calls on strip types. Builds a set + * of removed variables so that all references to them can be removed. + * + * @param t The traversal + * @param n A VAR node + * @param parent {@code n}'s parent + */ + void removeVarDeclarationsByNameOrRvalue(NodeTraversal t, Node n, + Node parent) { + for (Node nameNode = n.getFirstChild(); nameNode != null; + nameNode = nameNode.getNext()) { + String name = nameNode.getString(); + if (isStripName(name) || + isCallWhoseReturnValueShouldBeStripped(nameNode.getFirstChild())) { + // Remove the NAME. + Scope scope = t.getScope(); + varsToRemove.add(scope.getVar(name)); + n.removeChild(nameNode); + compiler.reportCodeChange(); + } + } + if (!n.hasChildren()) { + // Must also remove the VAR. + replaceWithEmpty(n, parent); + compiler.reportCodeChange(); + } + } + + /** + * Removes a reference if it is a reference to a removed variable. + * + * @param t The traversal + * @param n A NAME node + * @param parent {@code n}'s parent + */ + void maybeRemoveReferenceToRemovedVariable(NodeTraversal t, Node n, + Node parent) { + switch (parent.getType()) { + case Token.VAR: + // This is a variable declaration, not a reference. + break; + + case Token.GETPROP: + // GETPROP + // NAME + // STRING (property name) + case Token.GETELEM: + // GETELEM + // NAME + // NUMBER|STRING|NAME|... + if (parent.getFirstChild() == n && isReferenceToRemovedVar(t, n)) { + replaceHighestNestedCallWithNull(parent, parent.getParent()); + } + break; + + case Token.ASSIGN: + case Token.ASSIGN_BITOR: + case Token.ASSIGN_BITXOR: + case Token.ASSIGN_BITAND: + case Token.ASSIGN_LSH: + case Token.ASSIGN_RSH: + case Token.ASSIGN_URSH: + case Token.ASSIGN_ADD: + case Token.ASSIGN_SUB: + case Token.ASSIGN_MUL: + case Token.ASSIGN_DIV: + case Token.ASSIGN_MOD: + if (isReferenceToRemovedVar(t, n)) { + if (parent.getFirstChild() == n) { + Node gramps = parent.getParent(); + if (gramps.isExprResult()) { + // Remove the assignment. + Node greatGramps = gramps.getParent(); + replaceWithEmpty(gramps, greatGramps); + compiler.reportCodeChange(); + } else { + // Substitute the r-value for the assignment. + Node rvalue = n.getNext(); + parent.removeChild(rvalue); + gramps.replaceChild(parent, rvalue); + compiler.reportCodeChange(); + } + } else { + // The var reference is the r-value. Replace it with null. + replaceWithNull(n, parent); + compiler.reportCodeChange(); + } + } + break; + + default: + if (isReferenceToRemovedVar(t, n)) { + replaceWithNull(n, parent); + compiler.reportCodeChange(); + } + break; + } + } + + /** + * Use a while loop to get up out of any nested calls. For example, + * if we have just detected that we need to remove the a.b() call + * in a.b().c().d(), we'll have to remove all of the calls, and it + * will take a few iterations through this loop to get up to d(). + */ + void replaceHighestNestedCallWithNull(Node node, Node parent) { + Node ancestor = parent; + Node ancestorChild = node; + while (true) { + if (ancestor.getFirstChild() != ancestorChild) { + replaceWithNull(ancestorChild, ancestor); + break; + } + if (ancestor.isExprResult()) { + // Remove the entire expression statement. + Node ancParent = ancestor.getParent(); + replaceWithEmpty(ancestor, ancParent); + break; + } + int type = ancestor.getType(); + if (type != Token.GETPROP && + type != Token.GETELEM && + type != Token.CALL) { + replaceWithNull(ancestorChild, ancestor); + break; + } + ancestorChild = ancestor; + ancestor = ancestor.getParent(); + } + compiler.reportCodeChange(); + } + + /** + * Eliminates an assignment if the l-value is: + * - A field name that's a strip name + * - A qualified name that begins with a strip type + * + * @param t The traversal + * @param n An ASSIGN node + * @param parent {@code n}'s parent + */ + void maybeEliminateAssignmentByLvalueName(NodeTraversal t, Node n, + Node parent) { + // ASSIGN + // l-value + // r-value + Node lvalue = n.getFirstChild(); + if (nameEndsWithFieldNameToStrip(lvalue) || + qualifiedNameBeginsWithStripType(lvalue)) { + + // Limit to EXPR_RESULT because it is not + // safe to eliminate assignment in complex expressions, + // e.g. in ((x = 7) + 8) + if (parent.isExprResult()) { + Node gramps = parent.getParent(); + replaceWithEmpty(parent, gramps); + compiler.reportCodeChange(); + } else { + t.report(n, STRIP_ASSIGNMENT_ERROR, lvalue.getQualifiedName()); + } + } + } + + /** + * Eliminates an expression if it refers to: + * - A field name that's a strip name + * - A qualified name that begins with a strip type + * This gets rid of construct like: + * a.prototype.logger; (used instead of a.prototype.logger = null;) + * This expression is not an assignment and so will not be caught by + * maybeEliminateAssignmentByLvalueName. + * @param t The traversal + * @param n An EXPR_RESULT node + * @param parent {@code n}'s parent + */ + void maybeEliminateExpressionByName(NodeTraversal t, Node n, + Node parent) { + // EXPR_RESULT + // expression + Node expression = n.getFirstChild(); + if (nameEndsWithFieldNameToStrip(expression) || + qualifiedNameBeginsWithStripType(expression)) { + if (parent.isExprResult()) { + Node gramps = parent.getParent(); + replaceWithEmpty(parent, gramps); + } else { + replaceWithEmpty(n, parent); + } + compiler.reportCodeChange(); + } + } + + /** + * Removes a method call if {@link #isMethodOrCtorCallThatTriggersRemoval} + * indicates that it should be removed. + * + * @param t The traversal + * @param n A CALL node + * @param parent {@code n}'s parent + */ + void maybeRemoveCall(NodeTraversal t, Node n, Node parent) { + // CALL/NEW + // function + // arguments + if (isMethodOrCtorCallThatTriggersRemoval(t, n, parent)) { + replaceHighestNestedCallWithNull(n, parent); + } + } + + /** + * Eliminates any object literal keys in an object literal declaration that + * have strip names. + * + * @param t The traversal + * @param n An OBJLIT node + */ + void eliminateKeysWithStripNamesFromObjLit(NodeTraversal t, Node n) { + // OBJLIT + // key1 + // value1 + // key2 + // ... + Node key = n.getFirstChild(); + while (key != null) { + if (isStripName(key.getString())) { + Node value = key.getFirstChild(); + Node next = key.getNext(); + n.removeChild(key); + key = next; + compiler.reportCodeChange(); + } else { + key = key.getNext(); + } + } + } + + /** + * Gets whether a node is a CALL node whose return value should be + * stripped. A call's return value should be stripped if the function + * getting called is a static method in a class that gets stripped. For + * example, if "goog.debug.Logger" is a strip name, then this function + * returns true for a call such as "goog.debug.Logger.getLogger(...)". It + * may also simply be a function that is getting stripped. For example, + * if "getLogger" is a strip name, but not "goog.debug.Logger", this will + * still return true. + * + * @param n A node (typically a CALL node) + * @return Whether the call's return value should be stripped + */ + boolean isCallWhoseReturnValueShouldBeStripped(@Nullable Node n) { + return n != null && + (n.isCall() || + n.isNew()) && + n.hasChildren() && + (qualifiedNameBeginsWithStripType(n.getFirstChild()) || + nameEndsWithFieldNameToStrip(n.getFirstChild())); + } + + /** + * Gets whether a qualified name begins with a strip name. The names + * "goog.debug", "goog.debug.Logger", and "goog.debug.Logger.Level" are + * examples of strip names that would result in this function returning + * true for a node representing the name "goog.debug.Logger.Level". + * + * @param n A node (typically a NAME or GETPROP node) + * @return Whether the name begins with a strip name + */ + boolean qualifiedNameBeginsWithStripType(Node n) { + String name = n.getQualifiedName(); + return qualifiedNameBeginsWithStripType(name); + } + + /** + * Gets whether a qualified name begins with a strip name. The names + * "goog.debug", "goog.debug.Logger", and "goog.debug.Logger.Level" are + * examples of strip names that would result in this function returning + * true for a node representing the name "goog.debug.Logger.Level". + * + * @param name A qualified class name + * @return Whether the name begins with a strip name + */ + boolean qualifiedNameBeginsWithStripType(String name) { + if (name != null) { + for (String type : stripTypes) { + if (name.equals(type) || name.startsWith(type + ".")) { + return true; + } + } + for (String type : stripTypePrefixes) { + if (name.startsWith(type)) { + return true; + } + } + } + return false; + } + + /** + * Determines whether a NAME node represents a reference to a variable that + * has been removed. + * + * @param t The traversal + * @param n A NAME node + * @return Whether the variable was removed + */ + boolean isReferenceToRemovedVar(NodeTraversal t, Node n) { + String name = n.getString(); + Scope scope = t.getScope(); + Scope.Var var = scope.getVar(name); + return varsToRemove.contains(var); + } + + /** + * Gets whether a CALL node triggers statement removal, based on the name + * of the object whose method is being called, or the name of the method. + * Checks whether the name begins with a strip type, ends with a field name + * that's a strip name, or belongs to the set of global class-defining + * functions (e.g. goog.inherits). + * + * @param t The traversal + * @param n A CALL node + * @return Whether the node triggers statement removal + */ + boolean isMethodOrCtorCallThatTriggersRemoval( + NodeTraversal t, Node n, Node parent) { + // CALL/NEW + // GETPROP (function) <-- we're interested in this, the function + // GETPROP (callee object) <-- or the object on which it is called + // ... + // STRING (field name) + // STRING (method name) + // ... (arguments) + + Node function = n.getFirstChild(); + if (function == null || !function.isGetProp()) { + // We are only interested in calls on object references that are + // properties. We don't need to eliminate method calls on variables + // that are getting removed, since that's already done by the code + // that removes all references to those variables. + return false; + } + + if (parent != null && parent.isName()) { + Node gramps = parent.getParent(); + if (gramps != null && gramps.isVar()) { + // The call's return value is being used to initialize a newly + // declared variable. We should leave the call intact for now. + // That way, when the traversal reaches the variable declaration, + // we'll recognize that the variable and all references to it need + // to be eliminated. + return false; + } + } + + Node callee = function.getFirstChild(); + return nameEndsWithFieldNameToStrip(callee) || + nameEndsWithFieldNameToStrip(function) || + qualifiedNameBeginsWithStripType(function) || + actsOnStripType(t, n); + } + + /** + * Gets whether a name ends with a field name that should be stripped. For + * example, this function would return true when passed "this.logger" or + * "a.b.c.myLogger" if "logger" is a strip name. + * + * @param n A node (typically a GETPROP node) + * @return Whether the name ends with a field name that should be stripped + */ + boolean nameEndsWithFieldNameToStrip(@Nullable Node n) { + if (n != null && n.isGetProp()) { + Node propNode = n.getLastChild(); + return propNode != null && propNode.isString() && + isStripName(propNode.getString()); + } + return false; + } + + /** + * Determines whether the given node helps to define a + * strip type. For example, goog.inherits(stripType, Object) + * would be such a call. + * + * Also reports an error if a non-strip type inherits from a strip type. + * + * @param t The current traversal + * @param callNode The CALL node + */ + private boolean actsOnStripType(NodeTraversal t, Node callNode) { + SubclassRelationship classes = + compiler.getCodingConvention().getClassesDefinedByCall(callNode); + if (classes != null) { + // It's okay to strip a type that inherits from a non-stripped type + // e.g. goog.inherits(goog.debug.Logger, Object) + if (qualifiedNameBeginsWithStripType(classes.subclassName)) { + return true; + } + + // report an error if a non-strip type inherits from a + // strip type. + if (qualifiedNameBeginsWithStripType(classes.superclassName)) { + t.report(callNode, STRIP_TYPE_INHERIT_ERROR, + classes.subclassName, classes.superclassName); + } + } + + return false; + } + + /** + * Gets whether a JavaScript identifier is the name of a variable or + * property that should be stripped. + * + * @param name A JavaScript identifier + * @return Whether {@code name} is a name that triggers removal + */ + boolean isStripName(String name) { + if (stripNameSuffixes.contains(name) || + stripNamePrefixes.contains(name)) { + return true; + } + + if ((name.length() == 0) || Character.isUpperCase(name.charAt(0))) { + return false; + } + + String lcName = name.toLowerCase(); + for (String stripName : stripNamePrefixes) { + if (lcName.startsWith(stripName.toLowerCase())) { + return true; + } + } + + for (String stripName : stripNameSuffixes) { + if (lcName.endsWith(stripName.toLowerCase())) { + return true; + } + } + + return false; + } + + /** + * Replaces a node with a NULL node. This is useful where a value is + * expected. + * + * @param n A node + * @param parent {@code n}'s parent + */ + void replaceWithNull(Node n, Node parent) { + parent.replaceChild(n, IR.nullNode()); + } + + /** + * Replaces a node with an EMPTY node. This is useful where a statement is + * expected. + * + * @param n A node + * @param parent {@code n}'s parent + */ + void replaceWithEmpty(Node n, Node parent) { + NodeUtil.removeChild(parent, n); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SuppressDocWarningsGuard.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SuppressDocWarningsGuard.java new file mode 100644 index 0000000..f9ff72a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SuppressDocWarningsGuard.java @@ -0,0 +1,101 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Maps; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Map; + +/** + * Filters warnings based on in-code {@code @suppress} annotations. + * @author nicksantos@google.com (Nick Santos) + */ +class SuppressDocWarningsGuard extends WarningsGuard { + private static final long serialVersionUID = 1L; + + /** Warnings guards for each suppressible warnings group, indexed by name. */ + private final Map suppressors = + Maps.newHashMap(); + + /** + * The suppressible groups, indexed by name. + */ + SuppressDocWarningsGuard(Map suppressibleGroups) { + for (Map.Entry entry : + suppressibleGroups.entrySet()) { + suppressors.put( + entry.getKey(), + new DiagnosticGroupWarningsGuard( + entry.getValue(), + CheckLevel.OFF)); + } + } + + @Override + public CheckLevel level(JSError error) { + Node node = error.node; + if (node != null) { + for (Node current = node; + current != null; + current = current.getParent()) { + int type = current.getType(); + JSDocInfo info = null; + + // We only care about function annotations at the FUNCTION and SCRIPT + // level. Otherwise, the @suppress annotation has an implicit + // dependency on the exact structure of our AST, and that seems like + // a bad idea. + if (type == Token.FUNCTION) { + info = NodeUtil.getFunctionJSDocInfo(current); + } else if (type == Token.SCRIPT) { + info = current.getJSDocInfo(); + } else if (type == Token.ASSIGN) { + Node rhs = current.getLastChild(); + if (rhs.isFunction()) { + info = NodeUtil.getFunctionJSDocInfo(rhs); + } + } + + if (info != null) { + for (String suppressor : info.getSuppressions()) { + WarningsGuard guard = suppressors.get(suppressor); + + // Some @suppress tags are for other tools, and + // may not have a warnings guard. + if (guard != null) { + CheckLevel newLevel = guard.level(error); + if (newLevel != null) { + return newLevel; + } + } + } + } + } + } + return null; + } + + @Override + public int getPriority() { + // Happens after path-based filtering, but before other times + // of filtering. + return WarningsGuard.Priority.SUPPRESS_DOC.value; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SymbolTable.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SymbolTable.java new file mode 100644 index 0000000..5f0a14c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SymbolTable.java @@ -0,0 +1,1622 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicates; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Ordering; +import com.google.common.collect.Sets; +import com.google.common.collect.Table; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.JSDocInfo.Marker; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.SourcePosition; +import com.google.javascript.rhino.jstype.EnumType; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.SimpleReference; +import com.google.javascript.rhino.jstype.SimpleSlot; +import com.google.javascript.rhino.jstype.StaticReference; +import com.google.javascript.rhino.jstype.StaticScope; +import com.google.javascript.rhino.jstype.StaticSlot; +import com.google.javascript.rhino.jstype.StaticSymbolTable; +import com.google.javascript.rhino.jstype.UnionType; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.logging.Logger; + +import javax.annotation.Nullable; + +/** + * A symbol table for people that want to use Closure Compiler as an indexer. + * + * Contains an index of all the symbols in the code within a compilation + * job. The API is designed for people who want to visit all the symbols, rather + * than people who want to lookup a specific symbol by a certain key. + * + * We can use this to combine different types of symbol tables. For example, + * one class might have a {@code StaticSymbolTable} of all variable references, + * and another class might have a {@code StaticSymbolTable} of all type names + * in JSDoc comments. This class allows you to combine them into a unified + * index. + * + * Most passes build their own "partial" symbol table that implements the same + * interface (StaticSymbolTable, StaticSlot, and friends). Individual compiler + * passes usually need more or less metadata about the certainty of symbol + * information. Building a complete symbol table with all the necessary metadata + * for all passes would be too slow. However, as long as these "partial" symbol + * tables implement the proper interfaces, we should be able to add them to this + * symbol table to make it more complete. + * + * If clients want fast lookup, they should build their own wrapper around + * this symbol table that indexes symbols or references by the desired lookup + * key. + * + * By design, when this symbol table creates symbols for types, it tries + * to mimic the symbol table you would get in an OO language. For example, + * the "type Foo" and "the constructor that creates objects of type Foo" + * are the same symbol. The types of "Foo.prototype" and "new Foo()" also + * have the same symbol. Although JSCompiler internally treats these as + * distinct symbols, we assume that most clients will not care about + * the distinction. + * + * @see #addSymbolsFrom For more information on how to write plugins for this + * symbol table. + * + * @author nicksantos@google.com (Nick Santos) + */ +public final class SymbolTable + implements StaticSymbolTable { + private static final Logger logger = + Logger.getLogger(SymbolTable.class.getName()); + + /** + * The name we use for the JavaScript built-in Global object. It's + * anonymous in JavaScript, so we have to give it an invalid identifier + * to avoid conflicts with user-defined property names. + */ + public static final String GLOBAL_THIS = "*global*"; + + /** + * All symbols in the program, uniquely identified by the node where + * they're declared and their name. + */ + private final Table symbols = HashBasedTable.create(); + + /** + * All syntactic scopes in the program, uniquely identified by the node where + * they're declared. + */ + private final Map scopes = Maps.newLinkedHashMap(); + + /** + * All JSDocInfo in the program. + */ + private final List docInfos = Lists.newArrayList(); + + private SymbolScope globalScope = null; + + private final JSTypeRegistry registry; + + /** + * Clients should get a symbol table by asking the compiler at the end + * of a compilation job. + */ + SymbolTable(JSTypeRegistry registry) { + this.registry = registry; + } + + @Override + public Iterable getReferences(Symbol symbol) { + return Collections.unmodifiableCollection(symbol.references.values()); + } + + public List getReferenceList(Symbol symbol) { + return ImmutableList.copyOf(symbol.references.values()); + } + + @Override + public Iterable getAllSymbols() { + return Collections.unmodifiableCollection(symbols.values()); + } + + /** + * Get the symbols in their natural ordering. + * Always returns a mutable list. + */ + public List getAllSymbolsSorted() { + List sortedSymbols = Lists.newArrayList(symbols.values()); + Collections.sort(sortedSymbols, getNaturalSymbolOrdering()); + return sortedSymbols; + } + + /** + * Gets the 'natural' ordering of symbols. + * + * Right now, we only guarantee that symbols in the global scope will come + * before symbols in local scopes. After that, the order is deterministic but + * undefined. + */ + public Ordering getNaturalSymbolOrdering() { + return SYMBOL_ORDERING; + } + + @Override + public SymbolScope getScope(Symbol slot) { + return slot.scope; + } + + public Collection getAllJSDocInfo() { + return Collections.unmodifiableList(docInfos); + } + + /** + * Declare a symbol after the main symbol table was constructed. + * Throws an exception if you try to declare a symbol twice. + */ + public Symbol declareInferredSymbol( + SymbolScope scope, String name, Node declNode) { + return declareSymbol(name, null, true, scope, declNode, null); + } + + /** + * Gets the scope that contains the given node. + * If {@code n} is a function name, we return the scope that contains the + * function, not the function itself. + */ + public SymbolScope getEnclosingScope(Node n) { + Node current = n.getParent(); + if (n.isName() && + n.getParent().isFunction()) { + current = current.getParent(); + } + + for (; current != null; current = current.getParent()) { + if (scopes.containsKey(current)) { + return scopes.get(current); + } + } + return null; + } + + /** + * If {@code sym} is a function, try to find a Symbol for + * a parameter with the given name. + * + * Returns null if we couldn't find one. + * + * Notice that this just makes a best effort, and may not be able + * to find parameters for non-conventional function definitions. + * For example, we would not be able to find "y" in this code: + * + * var x = x() ? function(y) {} : function(y) {}; + * + */ + public Symbol getParameterInFunction(Symbol sym, String paramName) { + SymbolScope scope = getScopeInFunction(sym); + if (scope != null) { + Symbol param = scope.getSlot(paramName); + if (param != null && param.scope == scope) { + return param; + } + } + return null; + } + + private SymbolScope getScopeInFunction(Symbol sym) { + FunctionType type = sym.getFunctionType(); + if (type == null) { + return null; + } + + Node functionNode = type.getSource(); + if (functionNode == null) { + return null; + } + + return scopes.get(functionNode); + } + + /** + * All local scopes are associated with a function, and some functions + * are associated with a symbol. Returns the symbol associated with the given + * scope. + */ + public Symbol getSymbolForScope(SymbolScope scope) { + if (scope.getSymbolForScope() == null) { + scope.setSymbolForScope(findSymbolForScope(scope)); + } + return scope.getSymbolForScope(); + } + + /** + * Find the symbol associated with the given scope. + * Notice that we won't always be able to figure out this association + * dynamically, so sometimes we'll just create the association when we + * create the scope. + */ + private Symbol findSymbolForScope(SymbolScope scope) { + Node rootNode = scope.getRootNode(); + if (rootNode.getParent() == null) { + return globalScope.getSlot(GLOBAL_THIS); + } + + if (!rootNode.isFunction()) { + return null; + } + + String name = NodeUtil.getBestLValueName( + NodeUtil.getBestLValue(rootNode)); + return name == null ? null : scope.getParentScope().getQualifiedSlot(name); + } + + /** + * Get all symbols associated with the type of the given symbol. + * + * For example, given a variable x declared as + * /* @type {Array|Date} / + * var x = f(); + * this will return the constructors for Array and Date. + */ + public Iterable getAllSymbolsForTypeOf(Symbol sym) { + return getAllSymbolsForType(sym.getType()); + } + + /** + * Returns the global scope. + */ + public SymbolScope getGlobalScope() { + return globalScope; + } + + /** + * Gets the symbol for the given constructor or interface. + */ + public Symbol getSymbolDeclaredBy(FunctionType fn) { + Preconditions.checkState(fn.isConstructor() || fn.isInterface()); + ObjectType instanceType = fn.getInstanceType(); + return getSymbolForName(fn.getSource(), instanceType.getReferenceName()); + } + + /** + * Gets the symbol for the given enum. + */ + public Symbol getSymbolDeclaredBy(EnumType enumType) { + return getSymbolForName(null, + enumType.getElementsType().getReferenceName()); + } + + /** + * Gets the symbol for the prototype if this is the symbol for a constructor + * or interface. + */ + public Symbol getSymbolForInstancesOf(Symbol sym) { + FunctionType fn = sym.getFunctionType(); + if (fn != null && fn.isNominalConstructor()) { + return getSymbolForInstancesOf(fn); + } + return null; + } + + /** + * Gets the symbol for the prototype of the given constructor or interface. + */ + public Symbol getSymbolForInstancesOf(FunctionType fn) { + Preconditions.checkState(fn.isConstructor() || fn.isInterface()); + ObjectType pType = fn.getPrototype(); + return getSymbolForName(fn.getSource(), pType.getReferenceName()); + } + + private Symbol getSymbolForName(Node source, String name) { + if (name == null || globalScope == null) { + return null; + } + + SymbolScope scope = source == null ? + globalScope : getEnclosingScope(source); + + // scope will sometimes be null if one of the type-stripping passes + // was run, and the symbol isn't in the AST anymore. + return scope == null ? null : scope.getQualifiedSlot(name); + } + + /** + * Gets all symbols associated with the given type. + * For union types, this may be multiple symbols. + * For instance types, this will return the constructor of + * that instance. + */ + public List getAllSymbolsForType(JSType type) { + if (type == null) { + return ImmutableList.of(); + } + + UnionType unionType = type.toMaybeUnionType(); + if (unionType != null) { + List result = Lists.newArrayListWithExpectedSize(2); + for (JSType alt : unionType.getAlternates()) { + // Our type system never has nested unions. + Symbol altSym = getSymbolForTypeHelper(alt, true); + if (altSym != null) { + result.add(altSym); + } + } + return result; + } + Symbol result = getSymbolForTypeHelper(type, true); + return result == null + ? ImmutableList.of() : ImmutableList.of(result); + } + + /** + * Gets all symbols associated with the given type. + * If there is more that one symbol associated with the given type, + * return null. + * @param type The type. + * @param linkToCtor If true, we should link instance types back + * to their constructor function. If false, we should link + * instance types back to their prototype. See the comments + * at the top of this file for more information on how + * our internal type system is more granular than Symbols. + */ + private Symbol getSymbolForTypeHelper(JSType type, boolean linkToCtor) { + if (type == null) { + return null; + } + + if (type.isGlobalThisType()) { + return globalScope.getSlot(GLOBAL_THIS); + } else if (type.isNominalConstructor()) { + return linkToCtor ? + globalScope.getSlot("Function") : + getSymbolDeclaredBy(type.toMaybeFunctionType()); + } else if (type.isFunctionPrototypeType()) { + FunctionType ownerFn = ((ObjectType) type).getOwnerFunction(); + if (!ownerFn.isConstructor() && !ownerFn.isInterface()) { + return null; + } + return linkToCtor ? + getSymbolDeclaredBy(ownerFn) : + getSymbolForInstancesOf(ownerFn); + } else if (type.isInstanceType()) { + FunctionType ownerFn = ((ObjectType) type).getConstructor(); + return linkToCtor ? + getSymbolDeclaredBy(ownerFn) : + getSymbolForInstancesOf(ownerFn); + } else if (type.isFunctionType()) { + return linkToCtor ? + globalScope.getSlot("Function") : + globalScope.getQualifiedSlot("Function.prototype"); + } else if (type.autoboxesTo() != null) { + return getSymbolForTypeHelper(type.autoboxesTo(), linkToCtor); + } else { + return null; + } + } + + public String toDebugString() { + StringBuilder builder = new StringBuilder(); + for (Symbol symbol : getAllSymbols()) { + toDebugString(builder, symbol); + } + return builder.toString(); + } + + private void toDebugString(StringBuilder builder, Symbol symbol) { + SymbolScope scope = symbol.scope; + if (scope.isGlobalScope()) { + builder.append( + String.format("'%s' : in global scope:\n", symbol.getName())); + } else if (scope.getRootNode() != null) { + builder.append( + String.format("'%s' : in scope %s:%d\n", + symbol.getName(), + scope.getRootNode().getSourceFileName(), + scope.getRootNode().getLineno())); + } else if (scope.getSymbolForScope() != null) { + builder.append( + String.format("'%s' : in scope %s\n", symbol.getName(), + scope.getSymbolForScope().getName())); + } else { + builder.append( + String.format("'%s' : in unknown scope\n", symbol.getName())); + } + + int refCount = 0; + for (Reference ref : getReferences(symbol)) { + builder.append( + String.format(" Ref %d: %s:%d\n", + refCount, + ref.getNode().getSourceFileName(), + ref.getNode().getLineno())); + refCount++; + } + } + + /** + * Make sure all the given scopes in {@code otherSymbolTable} + * are in this symbol table. + */ + > + void addScopes(Collection scopes) { + for (S scope : scopes) { + createScopeFrom(scope); + } + } + + /** Finds all the scopes and adds them to this symbol table. */ + void findScopes(AbstractCompiler compiler, Node externs, Node root) { + NodeTraversal.traverseRoots( + compiler, + Lists.newArrayList(externs, root), + new NodeTraversal.AbstractScopedCallback() { + @Override + public void enterScope(NodeTraversal t) { + createScopeFrom(t.getScope()); + } + + @Override + public void visit(NodeTraversal t, Node n, Node p) {} + }); + } + + /** Gets all the scopes in this symbol table. */ + public Collection getAllScopes() { + return Collections.unmodifiableCollection(scopes.values()); + } + + /** + * Finds anonymous functions in local scopes, and gives them names + * and symbols. They will show up as local variables with names + * "function%0", "function%1", etc. + */ + public void addAnonymousFunctions() { + TreeSet scopes = Sets.newTreeSet(LEXICAL_SCOPE_ORDERING); + for (SymbolScope scope : getAllScopes()) { + if (scope.isLexicalScope()) { + scopes.add(scope); + } + } + + for (SymbolScope scope : scopes) { + addAnonymousFunctionsInScope(scope); + } + } + + private void addAnonymousFunctionsInScope(SymbolScope scope) { + Symbol sym = getSymbolForScope(scope); + if (sym == null) { + // JSCompiler has no symbol for this scope. Check to see if it's a + // local function. If it is, give it a name. + if (scope.isLexicalScope() && + !scope.isGlobalScope() && + scope.getRootNode() != null && + !scope.getRootNode().isFromExterns() && + scope.getParentScope() != null) { + SymbolScope parent = scope.getParentScope(); + + int count = parent.innerAnonFunctionsWithNames++; + String innerName = "function%" + count; + scope.setSymbolForScope( + declareInferredSymbol( + parent, innerName, scope.getRootNode())); + } + } + } + + /** + * Make sure all the symbols and references in {@code otherSymbolTable} + * are in this symbol table. + * + * Uniqueness of symbols and references is determined by the associated + * node. + * + * If multiple symbol tables are mixed in, we do not check for consistency + * between symbol tables. The first symbol we see dictates the type + * information for that symbol. + */ + , R extends StaticReference> + void addSymbolsFrom(StaticSymbolTable otherSymbolTable) { + for (S otherSymbol : otherSymbolTable.getAllSymbols()) { + String name = otherSymbol.getName(); + SymbolScope myScope = createScopeFrom( + otherSymbolTable.getScope(otherSymbol)); + + StaticReference decl = + findBestDeclToAdd(otherSymbolTable, otherSymbol); + Symbol mySymbol = null; + if (decl != null) { + Node declNode = decl.getNode(); + + // If we have a declaration node, we can ensure the symbol is declared. + mySymbol = isAnySymbolDeclared(name, declNode, myScope); + if (mySymbol == null) { + mySymbol = copySymbolTo(otherSymbol, declNode, myScope); + } + } else { + // If we don't have a declaration node, we won't be able to declare + // a symbol in this symbol table. But we may be able to salvage the + // references if we already have a symbol. + mySymbol = myScope.getOwnSlot(name); + } + + if (mySymbol != null) { + for (R otherRef : otherSymbolTable.getReferences(otherSymbol)) { + if (isGoodRefToAdd(otherRef)) { + mySymbol.defineReferenceAt(otherRef.getNode()); + } + } + } + } + } + + /** + * Checks if any symbol is already declared at the given node and scope + * for the given name. If so, returns it. + */ + private Symbol isAnySymbolDeclared( + String name, Node declNode, SymbolScope scope) { + Symbol sym = symbols.get(declNode, name); + if (sym == null) { + // Sometimes, our symbol tables will disagree on where the + // declaration node should be. In the rare case where this happens, + // trust the existing symbol. + // See SymbolTableTest#testDeclarationDisagreement. + return scope.ownSymbols.get(name); + } + return sym; + } + + /** Helper for addSymbolsFrom, to determine the best declaration spot. */ + private , R extends StaticReference> + StaticReference findBestDeclToAdd( + StaticSymbolTable otherSymbolTable, S slot) { + StaticReference decl = slot.getDeclaration(); + if (isGoodRefToAdd(decl)) { + return decl; + } + + for (R ref : otherSymbolTable.getReferences(slot)) { + if (isGoodRefToAdd(ref)) { + return ref; + } + } + + return null; + } + + /** + * Helper for addSymbolsFrom, to determine whether a reference is + * acceptable. A reference must be in the normal source tree. + */ + private boolean isGoodRefToAdd(@Nullable StaticReference ref) { + return ref != null && ref.getNode() != null + && ref.getNode().getStaticSourceFile() != null + && !Compiler.SYNTHETIC_EXTERNS.equals( + ref.getNode().getStaticSourceFile().getName()); + } + + private Symbol copySymbolTo(StaticSlot sym, SymbolScope scope) { + return copySymbolTo(sym, sym.getDeclaration().getNode(), scope); + } + + private Symbol copySymbolTo( + StaticSlot sym, Node declNode, SymbolScope scope) { + // All symbols must have declaration nodes. + Preconditions.checkNotNull(declNode); + return declareSymbol( + sym.getName(), sym.getType(), sym.isTypeInferred(), scope, declNode, + sym.getJSDocInfo()); + } + + private Symbol addSymbol( + String name, JSType type, boolean inferred, SymbolScope scope, + Node declNode) { + Symbol symbol = new Symbol(name, type, inferred, scope); + Symbol replacedSymbol = symbols.put(declNode, name, symbol); + Preconditions.checkState( + replacedSymbol == null, + "Found duplicate symbol %s in global index. Type %s", name, type); + + replacedSymbol = scope.ownSymbols.put(name, symbol); + Preconditions.checkState( + replacedSymbol == null, + "Found duplicate symbol %s in its scope. Type %s", name, type); + return symbol; + } + + private Symbol declareSymbol( + String name, JSType type, boolean inferred, + SymbolScope scope, Node declNode, JSDocInfo info) { + Symbol symbol = addSymbol(name, type, inferred, scope, declNode); + symbol.setJSDocInfo(info); + symbol.setDeclaration(symbol.defineReferenceAt(declNode)); + return symbol; + } + + private void removeSymbol(Symbol s) { + SymbolScope scope = getScope(s); + if (scope.ownSymbols.remove(s.getName()) != s) { + throw new IllegalStateException("Symbol not found in scope " + s); + } + if (symbols.remove(s.getDeclaration().getNode(), s.getName()) != s) { + throw new IllegalStateException("Symbol not found in table " + s); + } + } + + /** + * Not all symbol tables record references to "namespace" objects. + * For example, if you have: + * goog.dom.DomHelper = function() {}; + * The symbol table may not record that as a reference to "goog.dom", + * because that would be redundant. + */ + void fillNamespaceReferences() { + for (Symbol symbol : getAllSymbolsSorted()) { + String qName = symbol.getName(); + int rootIndex = qName.indexOf('.'); + if (rootIndex == -1) { + continue; + } + + Symbol root = symbol.scope.getQualifiedSlot( + qName.substring(0, rootIndex)); + if (root == null) { + // In theory, this should never happen, but we fail quietly anyway + // just to be safe. + continue; + } + + for (Reference ref : getReferences(symbol)) { + Node currentNode = ref.getNode(); + if (!currentNode.isQualifiedName()) { + continue; + } + + while (currentNode.isGetProp()) { + currentNode = currentNode.getFirstChild(); + + String name = currentNode.getQualifiedName(); + if (name != null) { + Symbol namespace = + isAnySymbolDeclared(name, currentNode, root.scope); + if (namespace == null) { + namespace = root.scope.getQualifiedSlot(name); + } + + if (namespace == null && root.scope.isGlobalScope()) { + namespace = declareSymbol(name, + registry.getNativeType(JSTypeNative.UNKNOWN_TYPE), + true, + root.scope, + currentNode, + null /* JsDoc info */); + } + + if (namespace != null) { + namespace.defineReferenceAt(currentNode); + } + } + } + } + } + } + + void fillPropertyScopes() { + // Collect all object symbols. + List types = Lists.newArrayList(); + + // Create a property scope for each named type and each anonymous object, + // and populate it with that object's properties. + // + // We notably don't want to create a property scope for 'x' in + // var x = new Foo(); + // where x is just an instance of another type. + for (Symbol sym : getAllSymbols()) { + if (needsPropertyScope(sym)) { + types.add(sym); + } + } + + // The order of operations here is significant. + // + // When we add properties to Foo, we'll remove Foo.prototype from + // the symbol table and replace it with a fresh symbol in Foo's + // property scope. So the symbol for Foo.prototype in + // {@code instances} will be stale. + // + // To prevent this, we sort the list by the reverse of the + // default symbol order, which will do the right thing. + Collections.sort(types, + Collections.reverseOrder(getNaturalSymbolOrdering())); + for (Symbol s : types) { + createPropertyScopeFor(s); + } + + pruneOrphanedNames(); + } + + private boolean needsPropertyScope(Symbol sym) { + ObjectType type = ObjectType.cast(sym.getType()); + if (type == null) { + return false; + } + + // Anonymous objects + if (type.getReferenceName() == null) { + return true; + } + + // Constructors/prototypes + // Should this check for + // (type.isNominalConstructor() || type.isFunctionPrototypeType()) + // ? + if (sym.getName().equals(type.getReferenceName())) { + return true; + } + + // Enums + if (type.isEnumType() && + sym.getName().equals( + type.toMaybeEnumType().getElementsType().getReferenceName())) { + return true; + } + + return false; + } + + /** + * Removes symbols where the namespace they're on has been removed. + * + * After filling property scopes, we may have two symbols represented + * in different ways. For example, "A.superClass_.foo" and B.prototype.foo". + * + * This resolves that ambiguity by pruning the duplicates. + * If we have a lexical symbol with a constructor in its property + * chain, then we assume there's also a property path to this symbol. + * In other words, we can remove "A.superClass_.foo" because it's rooted + * at "A", and we built a property scope for "A" above. + */ + void pruneOrphanedNames() { + nextSymbol: for (Symbol s : getAllSymbolsSorted()) { + if (s.isProperty()) { + continue; + } + + String currentName = s.getName(); + int dot = -1; + while (-1 != (dot = currentName.lastIndexOf('.'))) { + currentName = currentName.substring(0, dot); + + Symbol owner = s.scope.getQualifiedSlot(currentName); + if (owner != null + && owner.getType() != null + && (owner.getType().isNominalConstructor() || + owner.getType().isFunctionPrototypeType() || + owner.getType().isEnumType())) { + removeSymbol(s); + continue nextSymbol; + } + } + } + } + + /** + * Create symbols and references for all properties of types in + * this symbol table. + * + * This gets a little bit tricky, because of the way this symbol table + * conflates "type Foo" and "the constructor of type Foo". So if you + * have: + * + * + * SymbolTable symbolTable = for("var x = new Foo();"); + * Symbol x = symbolTable.getGlobalScope().getSlot("x"); + * Symbol type = symbolTable.getAllSymbolsForType(x.getType()).get(0); + * + * + * Then type.getPropertyScope() will have the properties of the + * constructor "Foo". To get the properties of instances of "Foo", + * you will need to call: + * + * + * Symbol instance = symbolTable.getSymbolForInstancesOf(type); + * + * + * As described at the top of this file, notice that "new Foo()" and + * "Foo.prototype" are represented by the same symbol. + */ + void fillPropertySymbols( + AbstractCompiler compiler, Node externs, Node root) { + (new PropertyRefCollector(compiler)).process(externs, root); + } + + /** Index JSDocInfo. */ + void fillJSDocInfo( + AbstractCompiler compiler, Node externs, Node root) { + NodeTraversal.traverseRoots( + compiler, Lists.newArrayList(externs, root), + new JSDocInfoCollector(compiler.getTypeRegistry())); + + // Create references to parameters in the JSDoc. + for (Symbol sym : getAllSymbolsSorted()) { + JSDocInfo info = sym.getJSDocInfo(); + if (info == null) { + continue; + } + + for (Marker marker : info.getMarkers()) { + SourcePosition pos = marker.getNameNode(); + if (pos == null) { + continue; + } + + Node paramNode = pos.getItem(); + String name = paramNode.getString(); + Symbol param = getParameterInFunction(sym, name); + if (param == null) { + // There is no reference to this parameter in the actual JavaScript + // code, so we'll try to create a special JsDoc-only symbol in + // a JsDoc-only scope. + SourcePosition typePos = marker.getType(); + JSType type = null; + if (typePos != null) { + type = typePos.getItem().getJSType(); + } + + if (sym.docScope == null) { + sym.docScope = new SymbolScope(null /* root */, + null /* parent scope */, null /* type of this */, sym); + } + + // Check to make sure there's no existing symbol. In theory, this + // should never happen, but we check anyway and fail silently + // if our assumptions are wrong. (We do not want to put the symbol + // table into an invalid state). + Symbol existingSymbol = + isAnySymbolDeclared(name, paramNode, sym.docScope); + if (existingSymbol == null) { + declareSymbol(name, type, type == null, sym.docScope, paramNode, + null /* info */); + } + } else { + param.defineReferenceAt(paramNode); + } + } + } + } + + /** + * Build a property scope for the given symbol. Any properties of the symbol + * will be added to the property scope. + * + * It is important that property scopes are created in order from the leaves + * up to the root, so this should only be called from #fillPropertyScopes. + * If you try to create a property scope for a parent before its leaf, + * then the leaf will get cut and re-added to the parent property scope, + * and weird things will happen. + */ + private void createPropertyScopeFor(Symbol s) { + // In order to build a property scope for s, we will need to build + // a property scope for all its implicit prototypes first. This means + // that sometimes we will already have built its property scope + // for a previous symbol. + if (s.propertyScope != null) { + return; + } + + SymbolScope parentPropertyScope = null; + ObjectType type = s.getType() == null ? null : s.getType().toObjectType(); + if (type == null) { + return; + } + + ObjectType proto = type.getParentScope(); + if (proto != null && proto != type && proto.getConstructor() != null) { + Symbol parentSymbol = getSymbolForInstancesOf(proto.getConstructor()); + if (parentSymbol != null) { + createPropertyScopeFor(parentSymbol); + parentPropertyScope = parentSymbol.getPropertyScope(); + } + } + + ObjectType instanceType = type; + Iterable propNames = type.getOwnPropertyNames(); + if (instanceType.isFunctionPrototypeType()) { + // Merge the properties of "Foo.prototype" and "new Foo()" together. + instanceType = instanceType.getOwnerFunction().getInstanceType(); + Set set = Sets.newHashSet(propNames); + Iterables.addAll(set, instanceType.getOwnPropertyNames()); + propNames = set; + } + + s.setPropertyScope(new SymbolScope(null, parentPropertyScope, type, s)); + for (String propName : propNames) { + StaticSlot newProp = instanceType.getSlot(propName); + if (newProp.getDeclaration() == null) { + // Skip properties without declarations. We won't know how to index + // them, because we index things by node. + continue; + } + + // We have symbol tables that do not do type analysis. They just try + // to build a complete index of all objects in the program. So we might + // already have symbols for things like "Foo.bar". If this happens, + // throw out the old symbol and use the type-based symbol. + Symbol oldProp = symbols.get(newProp.getDeclaration().getNode(), + s.getName() + "." + propName); + if (oldProp != null) { + removeSymbol(oldProp); + } + + // If we've already have an entry in the table for this symbol, + // then skip it. This should only happen if we screwed up, + // and declared multiple distinct properties with the same name + // at the same node. We bail out here to be safe. + if (symbols.get(newProp.getDeclaration().getNode(), + newProp.getName()) != null) { + logger.warning("Found duplicate symbol " + newProp); + continue; + } + + Symbol newSym = copySymbolTo(newProp, s.propertyScope); + if (oldProp != null) { + if (newSym.getJSDocInfo() == null) { + newSym.setJSDocInfo(oldProp.getJSDocInfo()); + } + newSym.setPropertyScope(oldProp.propertyScope); + for (Reference ref : oldProp.references.values()) { + newSym.defineReferenceAt(ref.getNode()); + } + } + } + } + + /** + * Fill in references to "this" variables. + */ + void fillThisReferences( + AbstractCompiler compiler, Node externs, Node root) { + (new ThisRefCollector(compiler)).process(externs, root); + } + + /** + * Given a scope from another symbol table, returns the {@code SymbolScope} + * rooted at the same node. Creates one if it doesn't exist yet. + */ + private SymbolScope createScopeFrom(StaticScope otherScope) { + Node otherScopeRoot = otherScope.getRootNode(); + SymbolScope myScope = scopes.get(otherScopeRoot); + if (myScope == null) { + StaticScope otherScopeParent = otherScope.getParentScope(); + + // If otherScope is a global scope, and we already have a global scope, + // then something has gone seriously wrong. + // + // Not all symbol tables are rooted at the same global node, and + // we do not want to mix and match symbol tables that are rooted + // differently. + + if (otherScopeParent == null) { + // The global scope must be created before any local scopes. + Preconditions.checkState( + globalScope == null, "Global scopes found at different roots"); + } + + myScope = new SymbolScope( + otherScopeRoot, + otherScopeParent == null ? null : createScopeFrom(otherScopeParent), + otherScope.getTypeOfThis(), + null); + scopes.put(otherScopeRoot, myScope); + if (myScope.isGlobalScope()) { + globalScope = myScope; + } + } + return myScope; + } + + public static final class Symbol extends SimpleSlot { + // Use a linked hash map, so that the results are deterministic + // (and so the declaration always comes first). + private final Map references = Maps.newLinkedHashMap(); + + private final SymbolScope scope; + + private SymbolScope propertyScope = null; + + private Reference declaration = null; + + private JSDocInfo docInfo = null; + + // A scope for symbols that are only documented in JSDoc. + private SymbolScope docScope = null; + + Symbol(String name, JSType type, boolean inferred, SymbolScope scope) { + super(name, type, inferred); + this.scope = scope; + } + + @Override + public Reference getDeclaration() { + return declaration; + } + + public FunctionType getFunctionType() { + return JSType.toMaybeFunctionType(getType()); + } + + public Reference defineReferenceAt(Node n) { + Reference result = references.get(n); + if (result == null) { + result = new Reference(this, n); + references.put(n, result); + } + return result; + } + + /** Sets the declaration node. May only be called once. */ + void setDeclaration(Reference ref) { + Preconditions.checkState(this.declaration == null); + this.declaration = ref; + } + + public boolean inGlobalScope() { + return scope.isGlobalScope(); + } + + public boolean inExterns() { + Node n = getDeclarationNode(); + return n == null ? false : n.isFromExterns(); + } + + public Node getDeclarationNode() { + return declaration == null ? null : declaration.getNode(); + } + + public String getSourceFileName() { + Node n = getDeclarationNode(); + return n == null ? null : n.getSourceFileName(); + } + + public SymbolScope getPropertyScope() { + return propertyScope; + } + + void setPropertyScope(SymbolScope scope) { + this.propertyScope = scope; + if (scope != null) { + this.propertyScope.setSymbolForScope(this); + } + } + + @Override + public JSDocInfo getJSDocInfo() { + return docInfo; + } + + void setJSDocInfo(JSDocInfo info) { + this.docInfo = info; + } + + /** Whether this is a property of another variable. */ + public boolean isProperty() { + return scope.isPropertyScope(); + } + + /** Whether this is a variable in a lexical scope. */ + public boolean isLexicalVariable() { + return scope.isLexicalScope(); + } + + /** Whether this is a variable that's only in JSDoc. */ + public boolean isDocOnlyParameter() { + return scope.isDocScope(); + } + + @Override + public String toString() { + Node n = getDeclarationNode(); + int lineNo = n == null ? -1 : n.getLineno(); + return getName() + "@" + getSourceFileName() + ":" + lineNo; + } + } + + public static final class Reference extends SimpleReference { + Reference(Symbol symbol, Node node) { + super(symbol, node); + } + } + + public static final class SymbolScope implements StaticScope { + private final Node rootNode; + private final SymbolScope parent; + private final JSType typeOfThis; + private final Map ownSymbols = Maps.newLinkedHashMap(); + private final int scopeDepth; + + // The number of inner anonymous functions that we've given names to. + private int innerAnonFunctionsWithNames = 0; + + // The symbol associated with a property scope or doc scope. + private Symbol mySymbol; + + SymbolScope( + Node rootNode, + @Nullable SymbolScope parent, + JSType typeOfThis, + Symbol mySymbol) { + this.rootNode = rootNode; + this.parent = parent; + this.typeOfThis = typeOfThis; + this.scopeDepth = parent == null ? 0 : (parent.getScopeDepth() + 1); + this.mySymbol = mySymbol; + } + + Symbol getSymbolForScope() { + return mySymbol; + } + + void setSymbolForScope(Symbol sym) { + this.mySymbol = sym; + } + + /** Gets a unique index for the symbol in this scope. */ + public int getIndexOfSymbol(Symbol sym) { + return Iterables.indexOf( + ownSymbols.values(), Predicates.equalTo(sym)); + } + + @Override + public Node getRootNode() { + return rootNode; + } + + @Override + public SymbolScope getParentScope() { + return parent; + } + + /** + * Get the slot for a fully-qualified name (e.g., "a.b.c") by trying + * to find property scopes at each part of the path. + */ + public Symbol getQualifiedSlot(String name) { + Symbol fullyNamedSym = getSlot(name); + if (fullyNamedSym != null) { + return fullyNamedSym; + } + + int dot = name.lastIndexOf("."); + if (dot != -1) { + Symbol owner = getQualifiedSlot(name.substring(0, dot)); + if (owner != null && owner.getPropertyScope() != null) { + return owner.getPropertyScope().getSlot(name.substring(dot + 1)); + } + } + + return null; + } + + @Override + public Symbol getSlot(String name) { + Symbol own = getOwnSlot(name); + if (own != null) { + return own; + } + + Symbol ancestor = parent == null ? null : parent.getSlot(name); + if (ancestor != null) { + return ancestor; + } + return null; + } + + @Override + public Symbol getOwnSlot(String name) { + return ownSymbols.get(name); + } + + @Override + public JSType getTypeOfThis() { + return typeOfThis; + } + + public boolean isGlobalScope() { + return getParentScope() == null && getRootNode() != null; + } + + /** + * Returns whether this is a doc scope. A doc scope is a table for symbols + * that are documented solely within a JSDoc comment. + */ + public boolean isDocScope() { + return getRootNode() == null && mySymbol != null && + mySymbol.docScope == this; + } + + public boolean isPropertyScope() { + return getRootNode() == null && !isDocScope(); + } + + public boolean isLexicalScope() { + return getRootNode() != null; + } + + public int getScopeDepth() { + return scopeDepth; + } + + @Override + public String toString() { + Node n = getRootNode(); + if (n != null) { + return "Scope@" + n.getSourceFileName() + ":" + n.getLineno(); + } else { + return "PropertyScope@" + getSymbolForScope(); + } + } + } + + private class PropertyRefCollector + extends NodeTraversal.AbstractPostOrderCallback + implements CompilerPass { + private final AbstractCompiler compiler; + + PropertyRefCollector(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverseRoots( + compiler, + Lists.newArrayList(externs, root), + this); + } + + private boolean maybeDefineReference( + Node n, String propName, Symbol ownerSymbol) { + // getPropertyScope() will be null in some rare cases where there + // are no extern declarations for built-in types (like Function). + if (ownerSymbol != null && ownerSymbol.getPropertyScope() != null) { + Symbol prop = ownerSymbol.getPropertyScope().getSlot(propName); + if (prop != null) { + prop.defineReferenceAt(n); + return true; + } + } + return false; + } + + // Try to find the symbol by its fully qualified name. + private boolean tryDefineLexicalQualifiedNameRef(String name, Node n) { + if (name != null) { + Symbol lexicalSym = getEnclosingScope(n).getQualifiedSlot(name); + if (lexicalSym != null) { + lexicalSym.defineReferenceAt(n); + return true; + } + } + return false; + } + + // Try to remove a reference by its fully qualified name. + // If the symbol has no references left, remove it completely. + private void tryRemoveLexicalQualifiedNameRef(String name, Node n) { + if (name != null) { + Symbol lexicalSym = getEnclosingScope(n).getQualifiedSlot(name); + if (lexicalSym != null && + lexicalSym.isLexicalVariable() && + lexicalSym.getDeclaration().getNode() == n) { + removeSymbol(lexicalSym); + } + } + } + + private boolean maybeDefineTypedReference( + Node n, String propName, JSType owner) { + if (owner.isGlobalThisType()) { + Symbol sym = globalScope.getSlot(propName); + if (sym != null) { + sym.defineReferenceAt(n); + return true; + } + } else if (owner.isNominalConstructor()) { + return maybeDefineReference( + n, propName, getSymbolDeclaredBy(owner.toMaybeFunctionType())); + } else if (owner.isEnumType()) { + return maybeDefineReference( + n, propName, getSymbolDeclaredBy(owner.toMaybeEnumType())); + } else { + boolean defined = false; + for (Symbol ctor : getAllSymbolsForType(owner)) { + if (maybeDefineReference( + n, propName, getSymbolForInstancesOf(ctor))) { + defined = true; + } + } + return defined; + } + return false; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + // There are two ways to define a property reference: + // 1) As a fully qualified lexical symbol (e.g., x.y) + // 2) As a property of another object (e.g., x's y) + // Property definitions should take precedence over lexical + // definitions. e.g., for "a.b", it's more useful to record + // this as "property b of the type of a", than as "symbol a.b". + + if (n.isGetProp()) { + JSType owner = n.getFirstChild().getJSType(); + if (owner != null) { + boolean defined = maybeDefineTypedReference( + n, n.getLastChild().getString(), owner); + + if (defined) { + tryRemoveLexicalQualifiedNameRef(n.getQualifiedName(), n); + return; + } + } + + tryDefineLexicalQualifiedNameRef(n.getQualifiedName(), n); + } else if (n.isStringKey()) { + JSType owner = parent.getJSType(); + if (owner != null) { + boolean defined = + maybeDefineTypedReference(n, n.getString(), owner); + + if (defined) { + tryRemoveLexicalQualifiedNameRef( + NodeUtil.getBestLValueName(n), n); + return; + } + } + + tryDefineLexicalQualifiedNameRef( + NodeUtil.getBestLValueName(n), n); + } + } + } + + private class ThisRefCollector + extends NodeTraversal.AbstractScopedCallback + implements CompilerPass { + private final AbstractCompiler compiler; + + // The 'this' symbols in the current scope chain. + // + // If we don't know how to declare 'this' in a scope chain, + // then null should be on the stack. But this should be a rare + // occurrence. We should strive to always be able to come up + // with some symbol for 'this'. + private final List thisStack = Lists.newArrayList(); + + ThisRefCollector(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverseRoots( + compiler, + Lists.newArrayList(externs, root), + this); + } + + @Override + public void enterScope(NodeTraversal t) { + Symbol symbol = null; + if (t.inGlobalScope()) { + // Declare the global this at the first input root. + // This is a bizarre place to put it, but we need some + // location with a real file path (because all symbols + // must have a path). + // Note that root.lastChild.firstChild is the first non-extern input. + Node firstInputRoot = t.getScopeRoot().getLastChild().getFirstChild(); + if (firstInputRoot != null) { + symbol = addSymbol( + GLOBAL_THIS, + registry.getNativeType(JSTypeNative.GLOBAL_THIS), + false /* declared */, + globalScope, + firstInputRoot); + symbol.setDeclaration(new Reference(symbol, firstInputRoot)); + } + } else { + // Otherwise, declare a "this" property when possible. + SymbolScope scope = scopes.get(t.getScopeRoot()); + Preconditions.checkNotNull(scope); + Symbol scopeSymbol = getSymbolForScope(scope); + if (scopeSymbol != null) { + SymbolScope propScope = scopeSymbol.getPropertyScope(); + if (propScope != null) { + // If a function is assigned multiple times, we only want + // one addressable "this" symbol. + symbol = propScope.getOwnSlot("this"); + if (symbol == null) { + JSType rootType = t.getScopeRoot().getJSType(); + FunctionType fnType = rootType == null + ? null : rootType.toMaybeFunctionType(); + JSType type = fnType == null + ? null : fnType.getTypeOfThis(); + symbol = addSymbol( + "this", + type, + false /* declared */, + scope, + t.getScopeRoot()); + } + + // TODO(nicksantos): It's non-obvious where the declaration of + // the 'this' symbol should be. Figure this out later. + } + } + } + + thisStack.add(symbol); + } + + @Override + public void exitScope(NodeTraversal t) { + thisStack.remove(thisStack.size() - 1); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isThis()) { + return; + } + + Symbol symbol = thisStack.get(thisStack.size() - 1); + if (symbol != null) { + Reference ref = symbol.defineReferenceAt(n); + if (symbol.getDeclaration() == null) { + symbol.setDeclaration(ref); + } + } + } + } + + /** Collects references to types in JSDocInfo. */ + private class JSDocInfoCollector + extends NodeTraversal.AbstractPostOrderCallback { + private final JSTypeRegistry typeRegistry; + + private JSDocInfoCollector(JSTypeRegistry registry) { + this.typeRegistry = registry; + } + + @Override public void visit(NodeTraversal t, Node n, Node parent) { + if (n.getJSDocInfo() != null) { + + // Find references in the JSDocInfo. + JSDocInfo info = n.getJSDocInfo(); + docInfos.add(info); + + for (Node typeAst : info.getTypeNodes()) { + SymbolScope scope = scopes.get(t.getScopeRoot()); + visitTypeNode(scope == null ? globalScope : scope, typeAst); + } + } + } + + public void visitTypeNode(SymbolScope scope, Node n) { + if (n.isString()) { + Symbol symbol = scope.getSlot(n.getString()); + if (symbol == null) { + // If we can't find this type, it might be a reference to a + // primitive type (like {string}). Autobox it to check. + JSType type = typeRegistry.getType(n.getString()); + JSType autobox = type == null ? null : type.autoboxesTo(); + symbol = autobox == null + ? null : getSymbolForTypeHelper(autobox, true); + } + if (symbol != null) { + symbol.defineReferenceAt(n); + } + } + + for (Node child = n.getFirstChild(); + child != null; child = child.getNext()) { + visitTypeNode(scope, child); + } + } + } + + // Comparators + private final Ordering SOURCE_NAME_ORDERING = + Ordering.natural().nullsFirst(); + + private final Ordering NODE_ORDERING = new Ordering() { + @Override + public int compare(Node a, Node b) { + int result = SOURCE_NAME_ORDERING.compare( + a.getSourceFileName(), b.getSourceFileName()); + if (result != 0) { + return result; + } + + // Source position is a bit mask of line in the top 4 bits, so this + // is a quick way to compare order without computing absolute position. + return a.getSourcePosition() - b.getSourcePosition(); + } + }; + + private final Ordering LEXICAL_SCOPE_ORDERING = + new Ordering() { + @Override + public int compare(SymbolScope a, SymbolScope b) { + Preconditions.checkState(a.isLexicalScope() && b.isLexicalScope(), + "We can only sort lexical scopes"); + return NODE_ORDERING.compare(a.getRootNode(), b.getRootNode()); + } + }; + + private final Ordering SYMBOL_ORDERING = new Ordering() { + @Override + public int compare(Symbol a, Symbol b) { + SymbolScope scopeA = getScope(a); + SymbolScope scopeB = getScope(b); + + // More deeply nested symbols should go later. + int result = getLexicalScopeDepth(scopeA) - getLexicalScopeDepth(scopeB); + if (result != 0) { + return result; + } + + // After than, just use lexicographic ordering. + // This ensures "a.b" comes before "a.b.c". + return a.getName().compareTo(b.getName()); + } + }; + + /** + * For a lexical scope, just returns the normal scope depth. + * + * For a property scope, returns the number of scopes we have to search + * to find the nearest lexical scope, plus that lexical scope's depth. + * + * For a doc info scope, returns 0. + */ + private int getLexicalScopeDepth(SymbolScope scope) { + if (scope.isLexicalScope() || scope.isDocScope()) { + return scope.getScopeDepth(); + } else { + Preconditions.checkState(scope.isPropertyScope()); + Symbol sym = scope.getSymbolForScope(); + Preconditions.checkNotNull(sym); + return getLexicalScopeDepth(getScope(sym)) + 1; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SyntacticScopeCreator.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SyntacticScopeCreator.java new file mode 100644 index 0000000..e1caa71 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SyntacticScopeCreator.java @@ -0,0 +1,287 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + + +/** + *

      The syntactic scope creator scans the parse tree to create a Scope object + * containing all the variable declarations in that scope.

      + * + *

      This implementation is not thread-safe.

      + * + */ +class SyntacticScopeCreator implements ScopeCreator { + private final AbstractCompiler compiler; + private Scope scope; + private InputId inputId; + private final RedeclarationHandler redeclarationHandler; + + // The arguments variable is special, in that it's declared in every local + // scope, but not explicitly declared. + private static final String ARGUMENTS = "arguments"; + + public static final DiagnosticType VAR_MULTIPLY_DECLARED_ERROR = + DiagnosticType.error( + "JSC_VAR_MULTIPLY_DECLARED_ERROR", + "Variable {0} first declared in {1}"); + + public static final DiagnosticType VAR_ARGUMENTS_SHADOWED_ERROR = + DiagnosticType.error( + "JSC_VAR_ARGUMENTS_SHADOWED_ERROR", + "Shadowing \"arguments\" is not allowed"); + + /** + * Creates a ScopeCreator. + */ + SyntacticScopeCreator(AbstractCompiler compiler) { + this.compiler = compiler; + this.redeclarationHandler = new DefaultRedeclarationHandler(); + } + + SyntacticScopeCreator( + AbstractCompiler compiler, RedeclarationHandler redeclarationHandler) { + this.compiler = compiler; + this.redeclarationHandler = redeclarationHandler; + } + + @Override + public Scope createScope(Node n, Scope parent) { + inputId = null; + if (parent == null) { + scope = Scope.createGlobalScope(n); + } else { + scope = new Scope(parent, n); + } + + scanRoot(n, parent); + + inputId = null; + Scope returnedScope = scope; + scope = null; + return returnedScope; + } + + private void scanRoot(Node n, Scope parent) { + if (n.isFunction()) { + if (inputId == null) { + inputId = NodeUtil.getInputId(n); + // TODO(johnlenz): inputId maybe null if the FUNCTION node is detached + // from the AST. + // Is it meaningful to build a scope for detached FUNCTION node? + } + + final Node fnNameNode = n.getFirstChild(); + final Node args = fnNameNode.getNext(); + final Node body = args.getNext(); + + // Bleed the function name into the scope, if it hasn't + // been declared in the outer scope. + String fnName = fnNameNode.getString(); + if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) { + declareVar(fnNameNode); + } + + // Args: Declare function variables + Preconditions.checkState(args.isParamList()); + for (Node a = args.getFirstChild(); a != null; + a = a.getNext()) { + Preconditions.checkState(a.isName()); + declareVar(a); + } + + // Body + scanVars(body, n); + } else { + // It's the global block + Preconditions.checkState(scope.getParent() == null); + scanVars(n, null); + } + } + + /** + * Scans and gather variables declarations under a Node + */ + private void scanVars(Node n, Node parent) { + switch (n.getType()) { + case Token.VAR: + // Declare all variables. e.g. var x = 1, y, z; + for (Node child = n.getFirstChild(); + child != null;) { + Node next = child.getNext(); + declareVar(child); + child = next; + } + return; + + case Token.FUNCTION: + if (NodeUtil.isFunctionExpression(n)) { + return; + } + + String fnName = n.getFirstChild().getString(); + if (fnName.isEmpty()) { + // This is invalid, but allow it so the checks can catch it. + return; + } + declareVar(n.getFirstChild()); + return; // should not examine function's children + + case Token.CATCH: + Preconditions.checkState(n.getChildCount() == 2); + Preconditions.checkState(n.getFirstChild().isName()); + // the first child is the catch var and the third child + // is the code block + + final Node var = n.getFirstChild(); + final Node block = var.getNext(); + + declareVar(var); + scanVars(block, n); + return; // only one child to scan + + case Token.SCRIPT: + inputId = n.getInputId(); + Preconditions.checkNotNull(inputId); + break; + } + + // Variables can only occur in statement-level nodes, so + // we only need to traverse children in a couple special cases. + if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { + for (Node child = n.getFirstChild(); + child != null;) { + Node next = child.getNext(); + scanVars(child, n); + child = next; + } + } + } + + /** + * Interface for injectable duplicate handling. + */ + interface RedeclarationHandler { + void onRedeclaration( + Scope s, String name, Node n, CompilerInput input); + } + + /** + * The default handler for duplicate declarations. + */ + private class DefaultRedeclarationHandler implements RedeclarationHandler { + @Override + public void onRedeclaration( + Scope s, String name, Node n, CompilerInput input) { + Node parent = n.getParent(); + + // Don't allow multiple variables to be declared at the top-level scope + if (scope.isGlobal()) { + Scope.Var origVar = scope.getVar(name); + Node origParent = origVar.getParentNode(); + if (origParent.isCatch() && + parent.isCatch()) { + // Okay, both are 'catch(x)' variables. + return; + } + + boolean allowDupe = hasDuplicateDeclarationSuppression(n, origVar); + + if (!allowDupe) { + compiler.report( + JSError.make(NodeUtil.getSourceName(n), n, + VAR_MULTIPLY_DECLARED_ERROR, + name, + (origVar.input != null + ? origVar.input.getName() + : "??"))); + } + } else if (name.equals(ARGUMENTS) && !NodeUtil.isVarDeclaration(n)) { + // Disallow shadowing "arguments" as we can't handle with our current + // scope modeling. + compiler.report( + JSError.make(NodeUtil.getSourceName(n), n, + VAR_ARGUMENTS_SHADOWED_ERROR)); + } + } + } + + /** + * Declares a variable. + * + * @param n The node corresponding to the variable name. + */ + private void declareVar(Node n) { + Preconditions.checkState(n.isName()); + + CompilerInput input = compiler.getInput(inputId); + String name = n.getString(); + if (scope.isDeclared(name, false) + || (scope.isLocal() && name.equals(ARGUMENTS))) { + redeclarationHandler.onRedeclaration( + scope, name, n, input); + } else { + scope.declare(name, n, null, input); + } + } + + + /** + * @param n The name node to check. + * @param origVar The associated Var. + * @return Whether duplicated declarations warnings should be suppressed + * for the given node. + */ + static boolean hasDuplicateDeclarationSuppression(Node n, Scope.Var origVar) { + Preconditions.checkState(n.isName()); + Node parent = n.getParent(); + Node origParent = origVar.getParentNode(); + + JSDocInfo info = n.getJSDocInfo(); + if (info == null) { + info = parent.getJSDocInfo(); + } + if (info != null && info.getSuppressions().contains("duplicate")) { + return true; + } + + info = origVar.nameNode.getJSDocInfo(); + if (info == null) { + info = origParent.getJSDocInfo(); + } + return (info != null && info.getSuppressions().contains("duplicate")); + } + + /** + * Generates an untyped global scope from the root of AST of compiler (which + * includes externs). + * + * @param compiler The compiler for which the scope is generated. + * @return The new untyped global scope generated as a result of this call. + */ + static Scope generateUntypedTopScope(AbstractCompiler compiler) { + return new SyntacticScopeCreator(compiler).createScope(compiler.getRoot(), + null); + } + + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SyntheticAst.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SyntheticAst.java new file mode 100644 index 0000000..ebd350a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/SyntheticAst.java @@ -0,0 +1,70 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.Node; + + +/** + * An AST generated totally by the compiler. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class SyntheticAst implements SourceAst { + private static final long serialVersionUID = 1L; + + private final InputId inputId; + private final SourceFile sourceFile; + + private Node root; + + SyntheticAst(String sourceName) { + this.inputId = new InputId(sourceName); + this.sourceFile = new SourceFile(sourceName); + clearAst(); + } + + @Override + public Node getAstRoot(AbstractCompiler compiler) { + return root; + } + + @Override + public void clearAst() { + root = IR.script(); + root.setInputId(inputId); + root.setStaticSourceFile(sourceFile); + } + + @Override + public InputId getInputId() { + return inputId; + } + + @Override + public SourceFile getSourceFile() { + return sourceFile; + } + + @Override + public void setSourceFile(SourceFile file) { + throw new IllegalStateException( + "Cannot set a source file for a synthetic AST"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TightenTypes.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TightenTypes.java new file mode 100644 index 0000000..2f1ab6a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TightenTypes.java @@ -0,0 +1,1414 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import static com.google.javascript.rhino.jstype.JSTypeNative.U2U_CONSTRUCTOR_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.ConcreteType.ConcreteFunctionType; +import com.google.javascript.jscomp.ConcreteType.ConcreteInstanceType; +import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.StaticReference; +import com.google.javascript.rhino.jstype.StaticScope; +import com.google.javascript.rhino.jstype.StaticSlot; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Computes the set of possible concrete types for every variable, property, + * function argument, and function return value in the program. Unlike a normal + * reference type annotation, a concrete type of A indicates that an instance of + * A -- not a subclass of A -- is a possible value. + * + * Also unlike normal type checking, this pass does not assume that all defined + * functions are actually called. Instead, it assumes only that the top-level + * code is executed plus any implicit calls detected, such as calls to functions + * exported via goog.exportSymbol or Element.addEventListener. Hence, this pass + * also performs a very strict form of dead code detection. Elimination of dead + * code will occur because the disambiguation pass can rename all uncalled + * functions to have distinct names, which will then appear to be uncalled to + * the normal unused property remover. + * + * Since concrete types are all reference types, we only care about the limited + * set of actions that apply to them: assignments to variables/properties, + * method calls, and return statements. To speed up and simplify the + * implementation, the first time a scope is processed, we make one pass through + * it {@link CreateScope} to translate it into a list of Actions. Each Action + * can translate itself into a list of assignments: method calls are just + * assignments to the parameter variables, while return statements are + * assignments to a special $return slot. Each time a scope is (re-)processed, + * we iterate over the assignments produced by the actions and update the types + * of the target slots. Once we complete a pass through all scopes with no + * changes, we are done. + * + */ +class TightenTypes implements CompilerPass, ConcreteType.Factory { + public static final String NON_HALTING_ERROR_MSG = + "TightenTypes pass appears to be stuck in an infinite loop."; + + /** The compiler that invoked this pass. */ + private final AbstractCompiler compiler; + + /** + * Map of function type information to their concrete wrappers. These must be + * reused so that each declaration has only a single concrete type, which will + * hold all the known types that flow to its arguments and return value. + */ + private final Map functionFromDeclaration = + Maps.newHashMap(); + + /** + * Secondary index of concrete functions by JSType. This is necessary for + * retrieving the concrete type of a superclass, where the actual declaration + * is not at hand. Note that we must use an identity hash map here because + * functions are compared using the signature only. + */ + private final Map functionFromJSType = + Maps.newIdentityHashMap(); + + /** + * Map of instance type information to their concrete wrappers. These must be + * reused so that each property has only one variable, which will store all + * known types that flow to that variable. + */ + private final Map instanceFromJSType = + Maps.newHashMap(); + + /** + * Memoized results of "createTypeIntersection" calls. + */ + private final Map typeIntersectionMemos = + Maps.newHashMap(); + + /** Scope storing the top-level variables and functions. */ + private ConcreteScope topScope; + + TightenTypes(AbstractCompiler compiler) { + this.compiler = compiler; + } + + /** Returns the top scope computed during the pass. */ + ConcreteScope getTopScope() { return topScope; } + + /** Convenience method to get the type registry of the compiler. */ + @Override + public JSTypeRegistry getTypeRegistry() { return compiler.getTypeRegistry(); } + + /** All concrete instance types encountered during flow analysis. */ + private Set allInstantiatedTypes = Sets.newHashSet(); + + @Override + public void process(Node externRoot, Node jsRoot) { + // Create the scope of top-level variables and functions. + topScope = new ConcreteScope(null); + topScope.initForExternRoot(externRoot); + topScope.initForScopeRoot(jsRoot); + + // Process the assignments in each scope in the working set until no more + // changes are detected. Each time a new scope is discovered (starting with + // the top-level scope), it is added to the working set to be processed. + // Since changes in almost any scope can affect another, we iterate over all + // discovered scopes until no further changes occur. + + long maxIterations = 1000; + long iterations = 0; + + Set workSet = Sets.newHashSet(topScope); + List workList = Lists.newArrayList(topScope); + + boolean changed; + do { + changed = false; + for (int i = 0; i < workList.size(); ++i) { + ConcreteScope scope = workList.get(i); + for (Action action : scope.getActions()) { + for (Assignment assign : action.getAssignments(scope)) { + if (assign.slot.addConcreteType(assign.type)) { + changed = true; + ConcreteScope varScope = assign.slot.getScope(); + if ((varScope != scope) && !workSet.contains(varScope)) { + workSet.add(varScope); + workList.add(varScope); + } + } + } + } + } + Preconditions.checkState(++iterations != maxIterations, + NON_HALTING_ERROR_MSG); + } while (changed); + } + + /** + * Represents a scope in which a set of slots are declared. The scope also + * includes code, which is normalized to a set of actions (which may affect + * slots in other scopes as well). + */ + class ConcreteScope implements StaticScope { + private final ConcreteScope parent; + private final Map slots; + private final List actions; + + ConcreteScope(ConcreteScope parent) { + this.parent = parent; + this.slots = Maps.newHashMap(); + this.actions = Lists.newArrayList(); + } + + @Override + public Node getRootNode() { return null; } + + @Override + public StaticScope getParentScope() { return parent; } + + @Override + public StaticSlot getOwnSlot(String name) { + return slots.get(name); + } + + @Override + public StaticSlot getSlot(String name) { + StaticSlot var = getOwnSlot(name); + if (var != null) { + return var; + } else if (parent != null) { + return parent.getSlot(name); + } else { + return null; + } + } + + /** Returns all the slots in this scope. */ + Collection getSlots() { return slots.values(); } + + @Override + public ConcreteType getTypeOfThis() { + // Since the slot doesn't have a reference to its ConcreteType, we can't + // reference the ConcreteFunctionType directly to get the typeOfThis. + ConcreteSlot thisVar = slots.get(ConcreteFunctionType.THIS_SLOT_NAME); + return (thisVar != null) ? thisVar.getType() : ConcreteType.NONE; + } + + /** Add a declaration for the given variable. */ + void declareSlot(String name, Node declaration) { + slots.put(name, new ConcreteSlot(this, name)); + } + + /** Add a declaration for the given variable with the given type. */ + void declareSlot(String name, Node declaration, ConcreteType type) { + ConcreteSlot var = new ConcreteSlot(this, name); + var.addConcreteType(type); + slots.put(name, var); + } + + /** Returns all the actions performed in the code of this scope. */ + List getActions() { return actions; } + + /** Finds assignments and variables from the function body. */ + void initForScopeRoot(Node decl) { + Preconditions.checkNotNull(decl); + if (decl.isFunction()) { + decl = decl.getLastChild(); + } + Preconditions.checkArgument(decl.isBlock()); + + NodeTraversal.traverse(compiler, decl, new CreateScope(this, false)); + } + + /** Finds assignments and variables from the given externs. */ + void initForExternRoot(Node decl) { + Preconditions.checkNotNull(decl); + Preconditions.checkArgument(decl.isBlock()); + + NodeTraversal.traverse(compiler, decl, new CreateScope(this, true)); + } + + /** Adds the given action to the list for the code in this scope. */ + void addAction(Action action) { actions.add(action); } + + @Override public String toString() { + return getTypeOfThis().toString() + " " + getSlots(); + } + } + + /** Represents a variable or function declared in a scope. */ + static class ConcreteSlot implements StaticSlot { + private final ConcreteScope scope; + private final String name; + private ConcreteType type; + + ConcreteSlot(ConcreteScope scope, String name) { + this.scope = scope; + this.name = name; + this.type = ConcreteType.NONE; + } + + /** Returns the scope in which this slot exists. */ + ConcreteScope getScope() { return scope; } + + /** Returns the name of this slot in its scope. */ + @Override public String getName() { return name; } + + @Override public ConcreteType getType() { return type; } + + /** Whether this type was inferred rather than declared (always true). */ + @Override public boolean isTypeInferred() { return true; } + + @Override public StaticReference getDeclaration() { + return null; + } + + @Override public JSDocInfo getJSDocInfo() { + return null; + } + + /** + * Adds the given type to the possible concrete types for this slot. + * Returns whether the added type was not already known. + */ + boolean addConcreteType(ConcreteType type) { + ConcreteType origType = this.type; + this.type = origType.unionWith(type); + return !this.type.equals(origType); + } + + @Override public String toString() { + return getName() + ": " + getType(); + } + } + + /** + * Represents a type of action performed in the body of scope that may affect + * the concrete types of slot. Example actions are a function call, a + * variable assignment, and a property assignment. The function call will + * create assignments for each of the function parameters, for the "this" + * slot, and for the "call" slot. Property and variable assignment actions + * create assignments for the property or variable they represent. + */ + private static interface Action { + /** Returns all assignments that may occur by this action. */ + Collection getAssignments(ConcreteScope scope); + } + + /** Represents an assignment to a variable of a set of possible types. */ + private static class Assignment { + private final ConcreteSlot slot; + private final ConcreteType type; + + Assignment(ConcreteSlot slot, ConcreteType type) { + this.slot = slot; + this.type = type; + + Preconditions.checkNotNull(slot); + Preconditions.checkNotNull(type); + } + } + + /** Records an assignment of an expression to a variable. */ + private class VariableAssignAction implements Action { + private final ConcreteSlot slot; + private final Node expression; + + VariableAssignAction(ConcreteSlot slot, Node expr) { + this.slot = slot; + this.expression = expr; + + Preconditions.checkNotNull(slot); + Preconditions.checkNotNull(expr); + } + + @Override + public Collection getAssignments(ConcreteScope scope) { + return Lists.newArrayList( + new Assignment(slot, inferConcreteType(scope, expression))); + } + } + + /** Records an assignment of an expression to a property of an object. */ + private class PropertyAssignAction implements Action { + private final Node receiver; + private final String propName; + private final Node expression; + + PropertyAssignAction(Node receiver, Node expr) { + this.receiver = receiver; + this.propName = receiver.getNext().getString(); + this.expression = expr; + + Preconditions.checkNotNull(receiver); + Preconditions.checkNotNull(propName); + Preconditions.checkNotNull(expr); + } + + /** + * Returns all assignments that could occur as a result of this property + * assign action. Each type in the receiver is checked for a property + * {@code propName}, and if that property exists, it is assigned the type + * of {@code expression}. + */ + @Override + public Collection getAssignments(ConcreteScope scope) { + ConcreteType recvType = inferConcreteType(scope, receiver); + ConcreteType exprType = inferConcreteType(scope, expression); + + List assigns = Lists.newArrayList(); + for (StaticSlot prop + : recvType.getPropertySlots(propName)) { + assigns.add(new Assignment((ConcreteSlot) prop, exprType)); + } + return assigns; + } + } + + /** Helper class to build a FunctionCall object. */ + private class FunctionCallBuilder { + private boolean isNewCall = false; + private boolean isCallFunction = false; + private final Node receiver; + private final Node firstArgument; + private String propName = null; + + FunctionCallBuilder(Node receiver, Node firstArgument) { + this.receiver = receiver; + this.firstArgument = firstArgument; + } + + FunctionCallBuilder setPropName(String propName) { + this.propName = propName; + return this; + } + + /** Should be called iff this is a new call, e.g. new Object(); */ + FunctionCallBuilder setIsNewCall(boolean isNew) { + Preconditions.checkState(!(isCallFunction && isNew), + "A function call cannot be of the form: new Object.call()"); + + isNewCall = isNew; + return this; + } + + /** + * Should be called iff this is a {@code call()} function call, + * e.g. Array.prototype.slice.call(arguments, 0); + */ + FunctionCallBuilder setIsCallFunction() { + Preconditions.checkState(!isNewCall, + "A function call cannot be of the form: new Object.call()"); + + isCallFunction = true; + return this; + } + + Action build() { + if (isCallFunction) { + return new NativeCallFunctionCall(receiver, propName, firstArgument); + } else { + return new FunctionCall(isNewCall, receiver, propName, firstArgument); + } + } + } + + /** + * Returns a list of assignments that will result from a function call with + * the given concrete types. + */ + private List getFunctionCallAssignments(ConcreteType recvType, + ConcreteType thisType, List argTypes) { + List assigns = Lists.newArrayList(); + for (ConcreteFunctionType fType : recvType.getFunctions()) { + assigns.add(new Assignment((ConcreteSlot) fType.getCallSlot(), fType)); + assigns.add(new Assignment((ConcreteSlot) fType.getThisSlot(), thisType)); + for (int i = 0; i < argTypes.size(); ++i) { + ConcreteSlot variable = (ConcreteSlot) fType.getParameterSlot(i); + // TODO(johnlenz): Support "arguments" references in function bodies. + // For now, ignore anonymous arguments. + if (variable != null) { + assigns.add(new Assignment(variable, argTypes.get(i))); + } + } + } + return assigns; + } + + /** + * Records a call to a function with a given set of concrete types. This is + * used for function calls that originate outside the scope of the user code. + * E.g. callbacks from an extern function. + */ + private class ExternFunctionCall implements Action { + private Node receiver; + private ConcreteType thisType; + private List argTypes; + + ExternFunctionCall(Node receiver, ConcreteType thisType, + List argTypes) { + this.receiver = receiver; + this.thisType = thisType; + this.argTypes = argTypes; + } + + @Override + public Collection getAssignments(ConcreteScope scope) { + return getFunctionCallAssignments(inferConcreteType(scope, receiver), + thisType, argTypes); + } + } + + /** Records a call to a function with a given set of arguments. */ + private class FunctionCall implements Action { + private final boolean isNewCall; + private final Node receiver; + private final String propName; + private final Node firstArgument; + + /** + * The function called is {@code receiver} or, if {@code propName} is + * non-null, the {@propName} field of {@code receiver}. + */ + FunctionCall(boolean isNewCall, Node receiver, String propName, + Node firstArgument) { + this.isNewCall = isNewCall; + this.receiver = receiver; + this.propName = propName; + this.firstArgument = firstArgument; + + Preconditions.checkNotNull(receiver); + } + + @Override + public Collection getAssignments(ConcreteScope scope) { + ConcreteType thisType = ConcreteType.NONE; + ConcreteType recvType = inferConcreteType(scope, receiver); + + // If a property name was specified, then the receiver is actually the + // type of this and the actual receiver is the type of that property. + if (propName != null) { + thisType = recvType; + recvType = thisType.getPropertyType(propName); + } + + if (recvType.isAll()) { + // TODO(user): ensure that this will trigger for code like + // functions[3](); + throw new AssertionError( + "Found call on all type, which makes tighten types useless."); + } + + // If this is a call to new, then a new instance of the receiver is + // created and passed in as the value of this. + if (isNewCall) { + thisType = ConcreteType.NONE; + for (ConcreteInstanceType instType + : recvType.getFunctionInstanceTypes()) { + thisType = thisType.unionWith(instType); + } + boolean added = allInstantiatedTypes.add(thisType); + if (added) { + // A new type instance invalidates the cached type intersections. + typeIntersectionMemos.clear(); + } + } + + List argTypes = Lists.newArrayList(); + for (Node arg = firstArgument; arg != null; arg = arg.getNext()) { + argTypes.add(inferConcreteType(scope, arg)); + } + + return getFunctionCallAssignments(recvType, thisType, argTypes); + } + } + + /** Records a call to the native call() function. */ + private class NativeCallFunctionCall implements Action { + private final Node receiver; + private final String propName; + private final Node firstArgument; + + NativeCallFunctionCall(Node receiver, String propName, Node firstArgument) { + this.receiver = receiver; + this.propName = propName; + this.firstArgument = firstArgument; + + Preconditions.checkNotNull(receiver); + } + + @Override + public Collection getAssignments(ConcreteScope scope) { + ConcreteType thisType = (firstArgument != null) + ? inferConcreteType(scope, firstArgument) + : getTopScope().getTypeOfThis(); + ConcreteType recvType = inferConcreteType(scope, receiver); + + if (recvType instanceof ConcreteInstanceType && + ((ConcreteInstanceType) recvType).isFunctionPrototype()) { + recvType = thisType.getPropertyType(propName); + } + List argTypes = Lists.newArrayList(); + // Skip the first argument for call() as it is the 'this' object. + for (Node arg = firstArgument.getNext(); + arg != null; + arg = arg.getNext()) { + argTypes.add(inferConcreteType(scope, arg)); + } + return getFunctionCallAssignments(recvType, thisType, argTypes); + } + } + + /** Adds all the variables and assignments to a given scope from the code. */ + private class CreateScope extends AbstractShallowCallback { + private final ConcreteScope scope; + private final boolean inExterns; + + CreateScope(ConcreteScope scope, boolean inExterns) { + this.scope = scope; + this.inExterns = inExterns; + } + + // TODO(user): handle object literals like { a: new Foo }; + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getType()) { + case Token.VAR: + // Variable declaration, e.g. var a = b; + Node name; + for (name = n.getFirstChild(); name != null; name = name.getNext()) { + if (inExterns) { + // In externs, we have to trust the type information because there + // are not necessarily assignments to the variables, calls to the + // functions, etc. + scope.declareSlot(name.getString(), n, createType(name, scope)); + } else { + scope.declareSlot(name.getString(), n); + if (name.getFirstChild() != null) { + addActions(createAssignmentActions( + name, name.getFirstChild(), n)); + } + } + } + break; + + case Token.GETPROP: + // Property access, e.g. a.b = c; + if (inExterns) { + ConcreteType type = inferConcreteType(getTopScope(), n); + // We only need to set a type if one hasn't been assigned by + // something else, e.g. an ASSIGN node. + if (type.isNone()) { + ConcreteScope scope = + (ConcreteScope) inferConcreteType(getTopScope(), + n.getFirstChild()).getScope(); + if (scope != null) { + type = createType(n.getJSType()); + if (type.isNone() || type.isAll()) { + break; + } + type = createUnionWithSubTypes(type); + Node nameNode = n.getLastChild(); + scope.declareSlot(nameNode.getString(), n, type); + } + } + } + break; + + case Token.FUNCTION: + // Function declaration, e.g. function Foo() {}; + if (NodeUtil.isFunctionDeclaration(n)) { + if (!n.getJSType().isNoObjectType()) { + ConcreteFunctionType type = createConcreteFunction(n, scope); + scope.declareSlot(n.getFirstChild().getString(), n, type); + + if (inExterns && type.getInstanceType() != null) { + // We must assume all extern types are instantiated since they + // can be created by the browser itself. + allInstantiatedTypes.add(type.getInstanceType()); + } + } + } + break; + + case Token.ASSIGN: + // Variable assignment, e.g. a = b; + Node lhs = n.getFirstChild(); + if (inExterns) { + // Again, we have to trust the externs. + ConcreteScope scope; + if (lhs.isGetProp()) { + ConcreteType type = inferConcreteType(getTopScope(), + lhs.getFirstChild()); + scope = (ConcreteScope) type.getScope(); + } else { + scope = getTopScope(); + } + + if (scope == null) break; + + ConcreteType type = inferConcreteType(getTopScope(), n); + if (type.isNone() || type.isAll()) { + break; + } + + if (type.isFunction()) { + JSType lhsType = lhs.getJSType(); + if (lhsType == null) { + break; + } + FunctionType funType = + lhsType.restrictByNotNullOrUndefined().toMaybeFunctionType(); + if (funType == null) { + break; + } + ConcreteType retType = createType(funType.getReturnType()); + retType = createUnionWithSubTypes(retType); + ConcreteType newret = type.toFunction().getReturnSlot() + .getType().unionWith(retType); + ((ConcreteScope) type.getScope()).declareSlot( + ConcreteFunctionType.RETURN_SLOT_NAME, n, newret); + } + scope.declareSlot(lhs.getLastChild().getString(), n, type); + } else { + addActions(createAssignmentActions(lhs, n.getLastChild(), n)); + } + break; + + case Token.NEW: + case Token.CALL: + Node receiver = n.getFirstChild(); + if (receiver.isGetProp()) { + Node first = receiver.getFirstChild(); + // Special case the call() function. + if ("call".equals(first.getNext().getString())) { + if (first.isGetProp()) { + // foo.bar.call() + addAction(new FunctionCallBuilder(first, receiver.getNext()) + .setPropName(first.getFirstChild().getNext().getString()) + .setIsCallFunction() + .build()); + } else { + // bar.call() + addAction(new FunctionCallBuilder( + first, receiver.getNext()).setIsCallFunction() + .build()); + } + } else { + // foo.bar() + addAction(new FunctionCallBuilder(first, receiver.getNext()) + .setPropName(first.getNext().getString()) + .build()); + } + } else { + // foo() or new Foo() + addAction(new FunctionCallBuilder(receiver, receiver.getNext()) + .setIsNewCall(n.isNew()) + .build()); + } + break; + + case Token.NAME: + if (parent.isCatch() && parent.getFirstChild() == n) { + // The variable in a catch statement gets defined in the scope of + // the catch block. We approximate that, as does the normal type + // system, by declaring a variable for it in the scope in which the + // catch is declared. + scope.declareSlot(n.getString(), n, + createUnionWithSubTypes( + createType(getTypeRegistry().getType("Error")).toInstance())); + } + break; + + case Token.RETURN: + if (n.getFirstChild() != null) { + addAction(new VariableAssignAction( + (ConcreteSlot) scope.getOwnSlot( + ConcreteFunctionType.RETURN_SLOT_NAME), n.getFirstChild())); + } + break; + } + + Collection actions = getImplicitActions(n); + if (actions != null) { + for (Action action : actions) { + addAction(action); + } + } + } + + /** Adds the given action to the scope (in non-externs only). */ + private void addAction(Action action) { + Preconditions.checkState(!inExterns, "Unexpected action in externs."); + scope.addAction(action); + } + + /** Adds the given action to the scope (in non-externs only). */ + private void addActions(List actions) { + Preconditions.checkState(!inExterns, "Unexpected action in externs."); + for (Action action : actions) { + scope.addAction(action); + } + } + + /** + * Returns an action for assigning the right-hand-side to the left or null + * if this assignment should be ignored. + */ + private List createAssignmentActions( + Node lhs, Node rhs, Node parent) { + switch (lhs.getType()) { + case Token.NAME: + ConcreteSlot var = (ConcreteSlot) scope.getSlot(lhs.getString()); + Preconditions.checkState(var != null, + "Type tightener could not find variable with name %s", + lhs.getString()); + return Lists.newArrayList( + new VariableAssignAction(var, rhs)); + + case Token.GETPROP: + Node receiver = lhs.getFirstChild(); + return Lists.newArrayList( + new PropertyAssignAction(receiver, rhs)); + + case Token.GETELEM: + return Lists.newArrayList(); + + default: + throw new AssertionError( + "Bad LHS for assignment: " + parent.toStringTree()); + } + } + + private ExternFunctionCall createExternFunctionCall( + Node receiver, JSType jsThisType, FunctionType fun) { + List argTypes = Lists.newArrayList(); + ConcreteType thisType; + if (fun != null) { + thisType = createType(jsThisType); + for (Node arg : fun.getParameters()) { + argTypes.add(createType(arg, scope)); + } + } else { + thisType = ConcreteType.NONE; + } + return new ExternFunctionCall(receiver, thisType, argTypes); + } + + private JSType getJSType(Node n) { + if (n.getJSType() != null) { + return n.getJSType(); + } else { + return getTypeRegistry().getNativeType(UNKNOWN_TYPE); + } + } + + /** + * Returns any actions that are implicit in the given code. This can return + * null instead of an empty collection if none are found. + */ + private Collection getImplicitActions(Node n) { + switch (n.getType()) { + case Token.CALL: + // Functions passed to externs functions are considered called. + // E.g. window.setTimeout(callback, 100); + // TODO(user): support global extern function calls if necessary + // TODO(user): handle addEventListener for the case of an object + // implementing the EventListener interface. + Node receiver = n.getFirstChild(); + if (!inExterns && receiver.isGetProp()) { + return getImplicitActionsFromCall(n, receiver.getJSType()); + } + break; + + case Token.ASSIGN: + Node lhs = n.getFirstChild(); + // Functions assigned to externs properties are considered called. + // E.g. element.onclick = function handle(evt) {}; + if (!inExterns && lhs.isGetProp()) { + return getImplicitActionsFromProp(lhs.getFirstChild().getJSType(), + lhs.getLastChild().getString(), n.getLastChild()); + } + break; + } + return null; + } + + private Collection getImplicitActionsFromCall( + Node n, JSType recvType) { + Node receiver = n.getFirstChild(); + if (recvType.isUnionType()) { + List actions = Lists.newArrayList(); + for (JSType alt : recvType.toMaybeUnionType().getAlternates()) { + actions.addAll(getImplicitActionsFromCall(n, alt)); + } + return actions; + } else if (!(recvType.isFunctionType())) { + return Lists.newArrayList(); + } + + ObjectType objType = ObjectType.cast( + getJSType(receiver.getFirstChild()) + .restrictByNotNullOrUndefined()); + String prop = receiver.getLastChild().getString(); + if (objType != null && + (objType.isPropertyInExterns(prop)) && + (recvType.toMaybeFunctionType()).getParameters() != null) { + List actions = Lists.newArrayList(); + + // Look for a function type in the argument list. + Iterator paramIter = + (recvType.toMaybeFunctionType()).getParameters().iterator(); + Iterator argumentIter = n.children().iterator(); + argumentIter.next(); // Skip the function name. + while (paramIter.hasNext() && argumentIter.hasNext()) { + Node arg = argumentIter.next(); + Node param = paramIter.next(); + if (arg.getJSType() != null && arg.getJSType().isFunctionType()) { + actions.addAll(getImplicitActionsFromArgument( + arg, + arg.getJSType().toMaybeFunctionType().getTypeOfThis() + .toObjectType(), + param.getJSType())); + } + } + return actions; + } + return Lists.newArrayList(); + } + + private Collection getImplicitActionsFromArgument( + Node arg, ObjectType thisType, JSType paramType) { + if (paramType.isUnionType()) { + List actions = Lists.newArrayList(); + for (JSType paramAlt : paramType.toMaybeUnionType().getAlternates()) { + actions.addAll( + getImplicitActionsFromArgument(arg, thisType, paramAlt)); + } + return actions; + } else if (paramType.isFunctionType()) { + return Lists.newArrayList(createExternFunctionCall( + arg, thisType, paramType.toMaybeFunctionType())); + } else { + return Lists.newArrayList(createExternFunctionCall( + arg, thisType, null)); + } + } + + private Collection getImplicitActionsFromProp( + JSType jsType, String prop, Node fnNode) { + List actions = Lists.newArrayList(); + if (jsType.isUnionType()) { + boolean found = false; + for (JSType alt : jsType.toMaybeUnionType().getAlternates()) { + ObjectType altObj = ObjectType.cast(alt); + if (altObj != null) { + actions.addAll(getImplicitActionsFromPropNonUnion( + altObj, prop, fnNode)); + if (altObj.hasProperty(prop)) { + found = true; + } + } + } + if (found) { + return actions; + } + } else { + ObjectType objType = ObjectType.cast(jsType); + if (objType != null && + !objType.isUnknownType() && objType.hasProperty(prop)) { + return getImplicitActionsFromPropNonUnion(objType, prop, fnNode); + } + } + + // If we didn't find a type that has the property, then check if there + // exists a property with this name anywhere in the externs. + for (ObjectType type : + getTypeRegistry().getEachReferenceTypeWithProperty(prop)) { + actions.addAll( + getImplicitActionsFromPropNonUnion( + type, prop, fnNode)); + } + return actions; + } + + private Collection getImplicitActionsFromPropNonUnion( + ObjectType jsType, String prop, Node fnNode) { + JSType propType = jsType.getPropertyType(prop) + .restrictByNotNullOrUndefined(); + if (jsType.isPropertyInExterns(prop) && propType.isFunctionType()) { + ObjectType thisType = jsType; + if (jsType.isFunctionPrototypeType()) { + thisType = thisType.getOwnerFunction().getInstanceType(); + } + FunctionType callType = propType.toMaybeFunctionType(); + Action action = createExternFunctionCall( + fnNode, thisType, callType); + return Lists.newArrayList(action); + } + return Lists.newArrayList(); + } + } + + /** Returns a concrete type from the JSType of the given variable. */ + private ConcreteType createType(Node name, ConcreteScope scope) { + Preconditions.checkNotNull(name); + Preconditions.checkArgument(name.isName()); + + if (name.getJSType() == null) { + return ConcreteType.ALL; + } + + if ((name.getFirstChild() != null) + && (name.getFirstChild().isFunction())) { + return createConcreteFunction(name.getFirstChild(), scope); + } + + return createType(name.getJSType()); + } + + /** Returns a concrete type from the given JSType. */ + private ConcreteType createType(JSType jsType) { + if (jsType.isUnknownType() || jsType.isEmptyType()) { + return ConcreteType.ALL; + } + + if (jsType.isUnionType()) { + ConcreteType type = ConcreteType.NONE; + for (JSType alt : jsType.toMaybeUnionType().getAlternates()) { + type = type.unionWith(createType(alt)); + } + return type; + } + + if (jsType.isFunctionType()) { + if (getConcreteFunction(jsType.toMaybeFunctionType()) != null) { + return getConcreteFunction(jsType.toMaybeFunctionType()); + } + // Since we don't have a declaration, it's not concrete. + return ConcreteType.ALL; + } + + if (jsType.isObject()) { + return createConcreteInstance(jsType.toObjectType()); + } + + return ConcreteType.NONE; // Not a reference type. + } + + /** + * Returns a concrete type from the given JSType that includes the concrete + * types for subtypes and implementing types for any interfaces. + */ + private ConcreteType createTypeWithSubTypes(JSType jsType) { + ConcreteType ret = ConcreteType.NONE; + if (jsType.isUnionType()) { + for (JSType alt : jsType.toMaybeUnionType().getAlternates()) { + ret = ret.unionWith(createTypeWithSubTypes(alt)); + } + } else { + ObjectType instType = ObjectType.cast(jsType); + if (instType != null && + instType.getConstructor() != null && + instType.getConstructor().isInterface()) { + Collection implementors = + getTypeRegistry().getDirectImplementors(instType); + + for (FunctionType implementor : implementors) { + ret = ret.unionWith(createTypeWithSubTypes( + implementor.getInstanceType())); + } + } else { + ret = ret.unionWith(createUnionWithSubTypes(createType(jsType))); + } + } + return ret; + } + + /** Computes the concrete types that can result from the given expression. */ + ConcreteType inferConcreteType(ConcreteScope scope, Node expr) { + Preconditions.checkNotNull(scope); + Preconditions.checkNotNull(expr); + ConcreteType ret; + switch (expr.getType()) { + case Token.NAME: + StaticSlot slot = scope.getSlot(expr.getString()); + + if (slot != null) { + ret = slot.getType(); + } else { + // This should occur only for extern variables, which we are assuming + // do not ever get assigned instances of user types. + ret = ConcreteType.ALL; + } + break; + + case Token.THIS: + ret = scope.getTypeOfThis(); + break; + + case Token.ASSIGN: + // Using the right-hand side is more specific since the left hand side + // is a variable of some sort that can be assigned elsewhere. + ret = inferConcreteType(scope, expr.getLastChild()); + break; + + case Token.COMMA: + ret = inferConcreteType(scope, expr.getLastChild()); + break; + + case Token.AND: + // Since a reference type is always true, only the right hand side could + // actually be returned. + ret = inferConcreteType(scope, expr.getLastChild()); + break; + + case Token.OR: + ret = inferConcreteType(scope, expr.getFirstChild()).unionWith( + inferConcreteType(scope, expr.getLastChild())); + break; + + case Token.HOOK: + ret = inferConcreteType(scope, + expr.getFirstChild().getNext()).unionWith( + inferConcreteType(scope, expr.getLastChild())); + break; + + case Token.GETPROP: + ConcreteType recvType = inferConcreteType(scope, expr.getFirstChild()); + if (recvType.isAll()) { + ret = recvType; + break; + } + Node prop = expr.getLastChild(); + String propName = prop.getString(); + ConcreteType type = recvType.getPropertyType(propName); + if ("prototype".equals(propName)) { + for (ConcreteFunctionType funType : recvType.getFunctions()) { + type = type.unionWith(funType.getPrototypeType()); + } + } else if (compiler.getCodingConvention() + .isSuperClassReference(propName)) { + for (ConcreteFunctionType superType : recvType.getSuperclassTypes()) { + type = type.unionWith(superType.getPrototypeType()); + } + } else if ("call".equals(propName)) { + type = recvType; + } + ret = type; + break; + + case Token.GETELEM: + ret = ConcreteType.ALL; + break; + + case Token.CALL: + // TODO(user): Support apply on functions. + // TODO(user): Create goog.bind that curries some arguments. + ConcreteType targetType = + inferConcreteType(scope, expr.getFirstChild()); + if (targetType.isAll()) { + ret = targetType; + break; + } + ret = ConcreteType.NONE; + for (ConcreteFunctionType funType : targetType.getFunctions()) { + ret = ret.unionWith(funType.getReturnSlot().getType()); + } + break; + + case Token.NEW: + ConcreteType constructorType = + inferConcreteType(scope, expr.getFirstChild()); + if (constructorType.isAll()) { + throw new AssertionError("Attempted new call on all type!"); + } + ret = ConcreteType.NONE; + for (ConcreteInstanceType instType + : constructorType.getFunctionInstanceTypes()) { + ret = ret.unionWith(instType); + } + allInstantiatedTypes.add(ret); + break; + + case Token.FUNCTION: + ret = createConcreteFunction(expr, scope); + break; + + case Token.OBJECTLIT: + if ((expr.getJSType() != null) && !expr.getJSType().isUnknownType()) { + JSType exprType = expr.getJSType().restrictByNotNullOrUndefined(); + ConcreteType inst = createConcreteInstance(exprType.toObjectType()); + allInstantiatedTypes.add(inst); + ret = inst; + } else { + ret = ConcreteType.ALL; + } + break; + + case Token.ARRAYLIT: + ObjectType arrayType = (ObjectType) getTypeRegistry() + .getNativeType(JSTypeNative.ARRAY_TYPE); + ConcreteInstanceType inst = createConcreteInstance(arrayType); + allInstantiatedTypes.add(inst); + ret = inst; + break; + + default: + ret = ConcreteType.NONE; + } + return createTypeIntersection(ret, expr.getJSType()); + } + + private ConcreteType createTypeIntersection( + ConcreteType concreteType, JSType jsType) { + // TODO(johnlenz): Even with memoizing all the time of this pass is still + // spent in this function (due to invalidation caused by changes to + // allInstantiatedTypes), specifically calls to ConcreteUnionType.unionWith + ConcreteJSTypePair key = new ConcreteJSTypePair(concreteType, jsType); + ConcreteType ret = typeIntersectionMemos.get(key); + if (ret != null) { + return ret; + } + + if (jsType == null || jsType.isUnknownType() || concreteType.isNone()) { + ret = concreteType; + } else if (concreteType.isUnion() || concreteType.isSingleton()) { + ret = concreteType.intersectWith(createTypeWithSubTypes(jsType)); + } else { + Preconditions.checkState(concreteType.isAll()); + ret = createTypeWithSubTypes(jsType); + } + ret = ret.intersectWith(ConcreteType.createForTypes(allInstantiatedTypes)); + + // Keep all function types, as restricting to instantiated types will only + // keep instance types. + // TODO(user): only keep functions that match the JS type. + for (ConcreteFunctionType functionType : concreteType.getFunctions()) { + ret = ret.unionWith(functionType); + } + + // The prototype type is special as it should only appear from a direct + // reference to Foo.prototype, and not via a type cast, thus, do not filter + // them out. We do not include them in the list of instantiated types. + for (ConcreteInstanceType prototype : concreteType.getPrototypeTypes()) { + ret = ret.unionWith(prototype); + } + + // Anonymous object types and enums will get removed in the createForTypes + // call, so add them back in as well. + for (ConcreteInstanceType instance : concreteType.getInstances()) { + if (!instance.instanceType.isInstanceType() + && !instance.isFunctionPrototype()) { + ret = ret.unionWith(instance); + } + } + + typeIntersectionMemos.put(key, ret); + return ret; + } + + @Override + public ConcreteFunctionType createConcreteFunction( + Node decl, StaticScope parent) { + ConcreteFunctionType funType = functionFromDeclaration.get(decl); + if (funType == null) { + functionFromDeclaration.put(decl, + funType = new ConcreteFunctionType(this, decl, parent)); + if (decl.getJSType() != null) { + functionFromJSType.put(decl.getJSType().toMaybeFunctionType(), funType); + } + } + return funType; + } + + @Override + public ConcreteInstanceType createConcreteInstance(ObjectType instanceType) { + // This should be an instance or function prototype object, not a function. + Preconditions.checkArgument( + !instanceType.isFunctionType() || + instanceType == getTypeRegistry().getNativeType(U2U_CONSTRUCTOR_TYPE)); + ConcreteInstanceType instType = instanceFromJSType.get(instanceType); + if (instType == null) { + instanceFromJSType.put(instanceType, + instType = new ConcreteInstanceType(this, instanceType)); + } + return instType; + } + + /** Returns the (already created) function with the given declaration. */ + ConcreteFunctionType getConcreteFunction(Node decl) { + return functionFromDeclaration.get(decl); + } + + /** Returns the function (if any) for the given node. */ + @Override + public ConcreteFunctionType getConcreteFunction(FunctionType functionType) { + return functionFromJSType.get(functionType); + } + + /** Returns the function (if any) for the given node. */ + @Override + public ConcreteInstanceType getConcreteInstance(ObjectType instanceType) { + return instanceFromJSType.get(instanceType); + } + + @Override + public StaticScope createFunctionScope( + Node decl, StaticScope parent) { + ConcreteScope scope = new ConcreteScope((ConcreteScope) parent); + scope.declareSlot(ConcreteFunctionType.CALL_SLOT_NAME, decl); + scope.declareSlot(ConcreteFunctionType.THIS_SLOT_NAME, decl); + scope.declareSlot(ConcreteFunctionType.RETURN_SLOT_NAME, decl); + for (Node n = decl.getFirstChild().getNext().getFirstChild(); + n != null; + n = n.getNext()) { + scope.declareSlot(n.getString(), n); + } + // TODO(user): Create an 'arguments' variable that returns the union + // of the concrete types of all parameters. + scope.initForScopeRoot(decl.getLastChild()); + return scope; + } + + @Override + public StaticScope createInstanceScope( + ObjectType instanceType) { + ConcreteScope parentScope = null; + ObjectType implicitProto = instanceType.getImplicitPrototype(); + if (implicitProto != null && !implicitProto.isUnknownType()) { + ConcreteInstanceType prototype = createConcreteInstance(implicitProto); + parentScope = (ConcreteScope) prototype.getScope(); + } + ConcreteScope scope = new ConcreteScope(parentScope); + for (String propName : instanceType.getOwnPropertyNames()) { + scope.declareSlot(propName, null); + } + return scope; + } + + /** + * Returns a ConcreteType that is the union of the given type and all of its + * subtypes. This assumes that the passed in type is an instance type, + * otherwise an empty set is returned. The returned set will be instance + * types. + */ + ConcreteType createUnionWithSubTypes(ConcreteType type) { + Set set = null; + if (type.isInstance()) { + set = getSubTypes(type.toInstance()); + } + + return ConcreteType.createForTypes(set).unionWith(type); + } + + /** Returns the set of subtypes of the given type. */ + private Set getSubTypes(ConcreteInstanceType type) { + if (type.getConstructorType() == null) { + return null; + } + + Set set = Sets.newHashSet(); + getSubTypes(type.getConstructorType().getJSType(), set); + return set; + } + + /** + * Adds all subtypes of the given type to the provided set. + * @return false if the all type was encountered, else true. + */ + private boolean getSubTypes(FunctionType type, Set set) { + if (type.getSubTypes() != null) { + for (FunctionType sub : type.getSubTypes()) { + ConcreteType concrete = createType(sub); + if (concrete.isFunction() + && concrete.toFunction().getInstanceType() != null) { + concrete = concrete.toFunction().getInstanceType(); + if (!set.contains(concrete)) { + set.add(concrete); + if (!getSubTypes(sub, set)) { + return false; + } + } + } else { + // The only time we should find a subtype that doesn't have an + // instance type is for the odd case of ActiveXObject, which is + // of the NoObject type and will be returned as a subtype of Object. + set.clear(); + set.add(ConcreteType.ALL); + return false; + } + } + } + return true; + } + + /** + * A simple class used to pair a concrete type and a JS type. Used to + * memoize the results of a "createTypeIntersection" call. + */ + static class ConcreteJSTypePair { + final ConcreteType concrete; + final JSType jstype; + final int hashcode; + + ConcreteJSTypePair(ConcreteType concrete, JSType jstype) { + this.concrete = concrete; + this.jstype = jstype; + this.hashcode = concrete.hashCode() + getJSTypeHashCode(); + } + + private int getJSTypeHashCode() { + return jstype != null ? jstype.hashCode() : 0; + } + + private boolean equalsJSType(JSType jsType) { + if (jsType == null || jstype == null) { + return jstype == jsType; + } else { + return jsType.equals(this.jstype); + } + } + + @Override + public boolean equals(Object o) { + if (o instanceof ConcreteJSTypePair) { + ConcreteJSTypePair pair = (ConcreteJSTypePair) o; + if ((pair.concrete.equals(this.concrete) + && equalsJSType(pair.jstype))) { + return true; + } + } + return false; + } + + @Override + public int hashCode() { + return hashcode; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Tracer.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Tracer.java new file mode 100644 index 0000000..5298e98 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/Tracer.java @@ -0,0 +1,1078 @@ +/* + * Copyright 2002 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.annotation.Nullable; + +/** + * Tracer provides a simple way to trace the handling of a request. + * + * By timing likely slow points in the code you can quickly pinpoint + * why a request is slow. + * + *

      Example usage: + *

      + * Tracer.initCurrentThreadTrace(); // must be called in each Thread
      + * Tracer wholeRequest = new Tracer(null, "Request " + params);
      + * try {
      + *   ...
      + *   t = new Tracer("Database", "getName()");
      + *   try {
      + *     name = database.getName();
      + *   } finally {
      + *     t.stop();
      + *   }
      + *   ...
      + *   t = new Tracer(null, "call sendmail");
      + *   try {
      + *     sendMessage();
      + *   } finally {
      + *     t.stop();
      + *   }
      + *   ...
      + *   t = new Tracer("Database", "updateinfo()");
      + *   try {
      + *     database.updateinfo("new info");
      + *   } finally {
      + *     t.stop();
      + *   }
      + *   ...
      + * } finally {
      + *   if (wholeRequest.stop() > 1000) {
      + *     // more than a second, better log
      + *     Tracer.logAndClearCurrentThreadTrace();
      + *   } else {
      + *     Tracer.clearCurrentThreadTrace();
      + *   }
      + * }
      + * 
      + * + * Now slow requests will produce a report like this: + *
      + *       10.452 Start        Request cmd=dostuff
      + *     3 10.455 Start        [Database] getName()
      + *    34 10.489 Done   34 ms [Database] getName()
      + *     3 10.491 Start        call sendmail
      + *  1042 11.533 Done 1042 ms call sendmail
      + *     0 11.533 Start        [Database] updateinfo()
      + *     3 11.536 Done    3 ms [Database] updateinfo()
      + *    64 11.600 Done 1148 ms Request cmd=dostuff
      + *   TOTAL Database 2 (37 ms)
      + * 
      + * + * If you enabled pretty-printing by calling {@link Tracer#setPrettyPrint}, + * it will print more easily readable reports that use indentation to visualize + * the tracer hierarchy and dynamically adjusts the padding to handle large + * durations. Like: + *
      + *       10.452 Start        Request cmd=dostuff
      + *     3 10.455 Start        | [Database] getName()
      + *    34 10.489 Done   34 ms | [Database] getName()
      + *     3 10.491 Start        | call sendmail
      + *  1042 11.533 Done 1042 ms | call sendmail
      + *     0 11.533 Start        | [Database] updateinfo()
      + *     3 11.536 Done    3 ms | [Database] updateinfo()
      + *    64 11.600 Done 1148 ms Request cmd=dostuff
      + *   TOTAL Database 2 (37 ms)
      + * 
      + * Pretty-printing is an application global setting and should only be called + * in the main setup of an application, not in library code. + * + * Now you can easily see that sendmail is causing your problems, not + * the two database calls. + * + * You can easily add additional tracing statistics to your Trace output by + * adding additional tracing statistics. Simply add to your initialization code: + *
      + *    Tracer.addTracingStatistic(myTracingStatistic)
      + * 
      + * where myTracingStatistic implements the {@link TracingStatistic} interface. + * The class com.google.monitoring.tracing.TracingStatistics contains + * several useful statistics such as CPU time, wait time, and memory usage. + * If you add your own tracing statistics, the output is not quite as pretty, + * but includes additional useful information. + + *

      If a Trace is given a type (the first argument to the constructor) and + * multiple Traces are done on that type then a "TOTAL line will be + * produced showing the total number of traces and the sum of the time + * ("TOTAL Database 2 (37 ms)" in our example). These traces should be + * mutually exclusive or else the sum won't make sense (the time will + * be double counted if the second starts before the first ends). + * + *

      It is also possible to have a "silent" Tracer which does not appear + * in the trace because it was faster than the silence threshold. This + * threshold can be set for the for the current ThreadTrace with + * setDefaultSilenceThreshold(threshold), or on a per-Tracer basis with + * t.stop(threshold). Silent tracers are still counted in the type + * totals, so these events are not completely lost. + * + *

      WARNING: This code makes a big assumption that + * everything for a given trace is done within a single thread. + * It uses threads to identify requests. It is fine to have multiple + * requests traced in multiple simultaneous threads but it is not ok + * to have any given request traced in multiple threads. (the results + * will be scattered across reports). + * + * Java objects do not support destructors (as in C++) so Tracer is not robust + * when exceptions are thrown. Each Tracer object should be wrapped in a + * try/finally block so that if an exception is thrown, the Tracer.stop() + * method is guaranteed to be called. + * + *

      A thread must call {@link Tracer#initCurrentThreadTrace()} to enable the + * Tracer logging, otherwise Tracer does nothing. The requirement to call + * {@code initCurrentThreadTrace} avoids the situation where Tracer is called + * without the explicit knowledge of the application authors because they + * happen to use a class in another package that uses Tracer. If {@link + * Tracer#logCurrentThreadTrace} is called without calling {@link + * Tracer#initCurrentThreadTrace()}, then a Third Eye WARNING message is logged, + * which should help track down the problem. + * + */ +final class Tracer { + // package-private for access from unit tests + static final Logger logger = + Logger.getLogger(Tracer.class.getName()); + + /** + * Whether pretty printing is enabled. This is intended to be set once + * at application startup. + */ + private static volatile boolean defaultPrettyPrint; + + /* This list is guaranteed to only increase in length. It contains + * a list of additional statistics that the user wants to keep track + * of. + */ + private static List extraTracingStatistics = + new CopyOnWriteArrayList(); + + /** Values returned by extraTracingStatistics */ + private long[] extraTracingValues; + + /** The type for grouping traces, may be null */ + private final @Nullable String type; + + /** A comment string for the report */ + private final String comment; + + /** Start time of the trace */ + private final long startTimeMs; + + /** Stop time of the trace, non-final */ + private long stopTimeMs; + + /** + * Record our starter thread in order to trap Traces that are started in one + * thread and stopped in another + */ + final Thread startThread; + + /** + * We limit the number of events in a Trace in order to catch memory + * leaks (a thread that keeps logging events and never clears them). + * This number is arbitrary and can be increased if necessary (though + * if there are more than 1000 events then the Tracer is probably being + * misused). + */ + static final int MAX_TRACE_SIZE = 1000; + + /** + * For unit testing. Can't use {@link com.google.common.time.Clock} because + * this code is in base and has minimal dependencies. + */ + static interface InternalClock { + long currentTimeMillis(); + } + + /** + * Default clock that calls through to the system clock. Can be overridden + * in unit tests. + */ + static InternalClock clock = new InternalClock() { + @Override + public long currentTimeMillis() { + return System.currentTimeMillis(); + } + }; + + /** + * Create and start a tracer. + * Both type and comment may be null. See class comment for usage. + * + * @param type The type for totaling + * @param comment Comment about this tracer + */ + Tracer(@Nullable String type, @Nullable String comment) { + this.type = type; + this.comment = comment == null ? "" : comment; + startTimeMs = clock.currentTimeMillis(); + startThread = Thread.currentThread(); + if (!extraTracingStatistics.isEmpty()) { + int size = extraTracingStatistics.size(); + extraTracingValues = new long[size]; + int i = 0; + for (TracingStatistic tracingStatistic : extraTracingStatistics) { + extraTracingValues[i] = tracingStatistic.start(startThread); + i++; + } + } + + ThreadTrace trace = getThreadTrace(); + + // Do nothing if the current thread trace wasn't initialized. + if (!trace.isInitialized()) { + return; + } + + // Check if we are creating too many Tracers. + if (trace.events.size() >= MAX_TRACE_SIZE) { + logger.log(Level.WARNING, + "Giant thread trace. Too many Tracers created. " + + "Clearing to avoid memory leak.", + new Throwable(trace.toString())); + trace.truncateEvents(); + } + + // Check if we forgot to close the Tracers. + if (trace.outstandingEvents.size() >= MAX_TRACE_SIZE) { + logger.log(Level.WARNING, + "Too many outstanding Tracers. Tracer.stop() is missing " + + "or Tracer.stop() is not wrapped in a " + + "try/finally block. " + + "Clearing to avoid memory leak.", + new Throwable(trace.toString())); + trace.truncateOutstandingEvents(); + } + + trace.startEvent(this); + } + + /** + * Create a tracer that isn't summed as part of a total + * + * @param comment Comment about this tracer + */ + Tracer(String comment) { + this(null, comment); + } + + /** + * Construct a tracer whose type is based on the short name of the object + * @param object Object to use as type name + * @param comment A comment + * @return new Tracer. + */ + static Tracer shortName(Object object, String comment) { + if (object == null) { + return new Tracer(comment); + } + return new Tracer(object.getClass().getSimpleName(), comment); + } + + /** + * Converts 'v' to a string and pads it with up to 16 spaces for + * improved alignment. + * @param v The value to convert. + * @param digits_column_width The desired with of the string. + */ + private static String longToPaddedString(long v, int digits_column_width) { + int digit_width = numDigits(v); + StringBuilder sb = new StringBuilder(); + appendSpaces(sb, digits_column_width - digit_width); + sb.append(v); + return sb.toString(); + } + + /** + * Gets the number of digits in an integer when printed in base 10. Assumes + * a positive integer. + * @param n The value. + * @return The number of digits in the string. + */ + private static int numDigits(long n) { + int i = 0; + do { + i++; + n = n / 10; + } while (n > 0); + return i; + } + + /** + * Gets a string of spaces of the length specified. + * @param sb The string builder to append to. + * @param numSpaces The number of spaces in the string. + */ + @VisibleForTesting + static void appendSpaces(StringBuilder sb, int numSpaces) { + if (numSpaces > 16) { + logger.warning("Tracer.appendSpaces called with large numSpaces"); + // Avoid long loop in case some bug in the caller + numSpaces = 16; + } + while (numSpaces >= 5) { + sb.append(" "); + numSpaces -= 5; + } + + // We know it's less than 5 now + switch (numSpaces) { + case 1: + sb.append(" "); + break; + case 2: + sb.append(" "); + break; + case 3: + sb.append(" "); + break; + case 4: + sb.append(" "); + break; + } + } + + /** + * Adds a new tracing statistic to a trace + * + * @param tracingStatistic to enable a run + * @return The index of this statistic (for use with stat.extraInfo()), or + * -1 if the statistic is not enabled. + */ + static int addTracingStatistic(TracingStatistic tracingStatistic) { + // Check to see if we can enable the tracing statistic before actually + // adding it. + if (tracingStatistic.enable()) { + // No synchronization needed, since this is a copy-on-write array. + extraTracingStatistics.add(tracingStatistic); + // 99.9% of the time, this will be O(1) and return + // extraTracingStatistics.length - 1 + return extraTracingStatistics.lastIndexOf(tracingStatistic); + } else { + return -1; + } + } + + /** + * For testing purposes only. These removes all current tracers. + * Severe errors can occur if there are any active tracers going on + * when this is called. + * + * The test suite uses this to remove any tracers that it has added. + */ + @VisibleForTesting + static void clearTracingStatisticsTestingOnly() { + extraTracingStatistics.clear(); + } + + /** + * Stop the trace. + * This may only be done once and must be done from the same thread + * that started it. + * @param silence_threshold Traces for time less than silence_threshold + * ms will be left out of the trace report. A value of -1 indicates + * that the current ThreadTrace silence_threshold should be used. + * @return The time that this trace actually ran + */ + long stop(int silence_threshold) { + Preconditions.checkState(Thread.currentThread() == startThread); + + ThreadTrace trace = getThreadTrace(); + // Do nothing if the thread trace was not initialized. + if (!trace.isInitialized()) { + return 0; + } + + stopTimeMs = clock.currentTimeMillis(); + if (extraTracingValues != null) { + // We use extraTracingValues.length rather than + // extraTracingStatistics.size() because a new statistic may + // have been added + for (int i = 0; i < extraTracingValues.length; i++) { + long value = extraTracingStatistics.get(i).stop(startThread); + extraTracingValues[i] = value - extraTracingValues[i]; + } + } + + // Do nothing if the thread trace was not initialized. + if (!trace.isInitialized()) { + return 0; + } + + trace.endEvent(this, silence_threshold); + return stopTimeMs - startTimeMs; + } + + /** Stop the trace using the default silence_threshold + * + * @return The time that this trace actually ran. + */ + long stop() { + return stop(-1); + } + + @Override public String toString() { + if (type == null) { + return comment; + } else { + return "[" + type + "] " + comment; + } + } + + static void setDefaultSilenceThreshold(int threshold) { + getThreadTrace().defaultSilenceThreshold = threshold; + } + + /** + * Initialize the trace associated with the current thread by clearing + * out any existing trace. There shouldn't be a trace so if one is + * found we log it as an error. + */ + static void initCurrentThreadTrace() { + ThreadTrace events = getThreadTrace(); + if (!events.isEmpty()) { + logger.log(Level.WARNING, + "Non-empty timer log:\n" + events, + new Throwable()); + clearThreadTrace(); + + // Grab a new thread trace if we find a previous non-empty ThreadTrace. + events = getThreadTrace(); + } + + // Mark the thread trace as initialized. + events.init(); + } + + static void initCurrentThreadTrace(int default_silence_threshold) { + initCurrentThreadTrace(); + setDefaultSilenceThreshold(default_silence_threshold); + } + + /** + * Returns a timer report similar to the one described in the class comment. + * + * @return The timer report as a string + */ + static String getCurrentThreadTraceReport() { + return getThreadTrace().toString(); + } + + /** + * Logs a timer report similar to the one described in the class comment. + */ + static void logCurrentThreadTrace() { + ThreadTrace trace = getThreadTrace(); + + // New threads must call Tracer.initCurrentThreadTrace() before Tracer + // statistics are gathered. This is a recent change (Jun 2007) that + // prevents spurious Third Eye messages when an application uses a class in + // a different package that happens to call Tracer without knowledge of the + // application authors. + if (!trace.isInitialized()) { + logger.log(Level.WARNING, + "Tracer log requested for this thread but was not " + + "initialized using Tracer.initCurrentThreadTrace().", + new Throwable()); + return; + } + + if (!trace.isEmpty()) { + logger.log(Level.WARNING, "timers:\n{0}", getCurrentThreadTraceReport()); + } + } + + /** + * Throw away any Trace associated with the current thread. + */ + static void clearCurrentThreadTrace() { + clearThreadTrace(); + } + + /** + * logCurrentThreadTrace() then clearCurrentThreadTrace() + */ + static void logAndClearCurrentThreadTrace() { + logCurrentThreadTrace(); + clearThreadTrace(); + } + + /** + * Sets whether pretty printing is enabled. See class-level comment. This + * only affects tracers created after this is called. + * @param enabled Whether to enable pretty printing. + */ + static void setPrettyPrint(boolean enabled) { + defaultPrettyPrint = enabled; + } + + /** Statistics for a given tracer type */ + static final class Stat { + private int count; + private int silent; + private int clockTime; + private int[] extraInfo; + + /** total count of tracers of a type, including silent + * + * @return total count of tracers, including silent tracers + */ + int getCount() { return count; } + + /** total count of silent tracers of a type + * + * @return total count of silent tracers + */ + int getSilentCount() { return silent; } + + /** total time spent in tracers of a type, in ms + * + * @return total time spent in tracer, in ms + */ + int getTotalTime() { return clockTime; } + + /** total time spent doing additional things that we are clocking */ + @VisibleForTesting + int getExtraInfo(int index) { + return index >= extraInfo.length ? 0 : extraInfo[index]; + } + + } + + /** + * This map tracks counts of tracers for each type over all time. + */ + private static @Nullable AtomicTracerStatMap typeToCountMap; + + /** + * This map tracks counts of silent tracers for each type over all time. + */ + private static @Nullable AtomicTracerStatMap typeToSilentMap; + + /** + * This map tracks time (ms) for each type over all time. + */ + private static @Nullable AtomicTracerStatMap typeToTimeMap; + + /** + * This method MUST be called before getTypeToCountMap (and friends) + * will return a valid map. This is because computing this information + * imposes a synchronization penalty on every Tracer that is stopped. + */ + static synchronized void enableTypeMaps() { + if (typeToCountMap == null) { + typeToCountMap = new AtomicTracerStatMap(); + typeToSilentMap = new AtomicTracerStatMap(); + typeToTimeMap = new AtomicTracerStatMap(); + } + } + + /** + * Used for exporting this data via varz. Accesses to this + * map must be synchronized on the map. If enableTypeMaps has not + * been called, this will return null. + */ + static @Nullable Map getTypeToCountMap() { + return typeToCountMap != null ? typeToCountMap.getMap() : null; + } + + /** + * Used for exporting this data via varz. Accesses to this + * map must be synchronized on the map. If enableTypeMaps has not + * been called, this will return null. + */ + static @Nullable Map getTypeToSilentMap() { + return typeToSilentMap != null ? typeToSilentMap.getMap() : null; + } + + /** + * Used for exporting this data via varz. Accesses to this + * map must be synchronized on the map. If enableTypeMaps has not + * been called, this will return null. + */ + static @Nullable Map getTypeToTimeMap() { + return typeToTimeMap != null ? typeToTimeMap.getMap() : null; + } + + /** Gets the Stat for a tracer type; never returns null */ + static Stat getStatsForType(String type) { + Stat stat = getThreadTrace().stats.get(type); + return stat != null ? stat : ZERO_STAT; + } + + private static final Stat ZERO_STAT = new Stat(); + + /** Return the sec.ms part of time (if time = "20:06:11.566", "11.566") */ + private static String formatTime(long time) { + int sec = (int) ((time / 1000) % 60); + int ms = (int) (time % 1000); + return String.format("%02d.%03d", sec, ms); + } + + /** An event is created every time a Tracer is created or stopped */ + private static final class Event { + boolean isStart; // else is_stop + Tracer tracer; + + Event(boolean start, Tracer t) { + isStart = start; + tracer = t; + } + + long eventTime() { + return isStart ? tracer.startTimeMs : tracer.stopTimeMs; + } + + /** + * Converts the event to a formatted string. + * @param prevEventTime The time of the previous event which appears at + * the left most part of the trace line. + * @param indent The indentation to put before the tracer to show the + * hierarchy. + * @param digitsColWidth How many characters the digits should use. + * @return The formatted string. + */ + String toString(long prevEventTime, String indent, int digitsColWidth) { + StringBuilder sb = new StringBuilder(120); + + if (prevEventTime == -1) { + appendSpaces(sb, digitsColWidth); + } else { + sb.append(longToPaddedString( + eventTime() - prevEventTime, digitsColWidth)); + } + + sb.append(' '); + sb.append(formatTime(eventTime())); + if (isStart) { + sb.append(" Start "); + appendSpaces(sb, digitsColWidth); + sb.append(" "); + } else { + sb.append(" Done "); + long delta = tracer.stopTimeMs - tracer.startTimeMs; + sb.append(longToPaddedString(delta, digitsColWidth)); + sb.append(" ms "); + if (tracer.extraTracingValues != null) { + for (int i = 0; i < tracer.extraTracingValues.length; i++) { + delta = tracer.extraTracingValues[i]; + sb.append(String.format("%4d", delta)); + sb.append(extraTracingStatistics.get(i).getUnits()); + sb.append("; "); + } + } + } + sb.append(indent); + sb.append(tracer.toString()); + return sb.toString(); + } + } + + /** Stores a thread's Trace */ + static final class ThreadTrace { + + /** Events taking less than this number of milliseconds are not reported. */ + int defaultSilenceThreshold; // non-final + + /** The Events corresponding to each startEvent/stopEvent */ + final ArrayList events = new ArrayList(); + + /** Tracers that have not had their .stop() called */ + final HashSet outstandingEvents = new HashSet(); + + /** Map from type to Stat object */ + final Map stats = new HashMap(); + + /** + * True if {@code outstandingEvents} has been cleared because we exceeded + * the max trace limit. + */ + boolean isOutstandingEventsTruncated = false; + + /** + * True if {@code events} has been cleared because we exceeded the max + * trace limit. + */ + boolean isEventsTruncated = false; + + /** + * Set to true if {@link Tracer#initCurrentThreadTrace()} was called by + * the current thread. + */ + boolean isInitialized = false; + + /** + * Whether pretty printing is enabled for the trace. + */ + boolean prettyPrint = false; + + /** Initialize the trace. */ + void init() { + isInitialized = true; + } + + /** Is initialized? */ + boolean isInitialized() { + return isInitialized; + } + + /** + * Called by the constructor {@link Tracer#Tracer(String, String)} to create + * a start event. + */ + void startEvent(Tracer t) { + events.add(new Event(true, t)); + boolean notAlreadyOutstanding = outstandingEvents.add(t); + Preconditions.checkState(notAlreadyOutstanding); + } + + /** + * Called by {@link Tracer#stop()} to create a stop event. + */ + void endEvent(Tracer t, int silenceThreshold) { + boolean wasOutstanding = outstandingEvents.remove(t); + if (!wasOutstanding) { + if (isOutstandingEventsTruncated) { + // The events stack overflowed and was truncated, so just log a + // warning. Otherwise, we get an exception which is extremely + // confusing. + logger.log(Level.WARNING, + "event not found, probably because the event stack " + + "overflowed and was truncated", + new Throwable()); + } else { + // throw an exception if the event was not found and the events stack + // is pristine + throw new IllegalStateException(); + } + } + + long elapsed = t.stopTimeMs - t.startTimeMs; + + if (silenceThreshold == -1) { // use default + silenceThreshold = defaultSilenceThreshold; + } + + if (elapsed < silenceThreshold) { + // If this one is silent then we need to remove the start Event + boolean removed = false; + for (int i = 0; i < events.size(); i++) { + Event e = events.get(i); + if (e.tracer == t) { + Preconditions.checkState(e.isStart); + events.remove(i); + removed = true; + break; + } + } + + // Only assert if we didn't find the original and the events + // weren't truncated. + Preconditions.checkState(removed || isEventsTruncated); + } else { + events.add(new Event(false, t)); + } + + if (t.type != null) { + Stat stat = stats.get(t.type); + if (stat == null) { + stat = new Stat(); + if (!extraTracingStatistics.isEmpty()) { + stat.extraInfo = new int[extraTracingStatistics.size()]; + } + stats.put(t.type, stat); + } + + stat.count++; + if (typeToCountMap != null) { + typeToCountMap.incrementBy(t.type, 1); + } + + stat.clockTime += elapsed; + if (typeToTimeMap != null) { + typeToTimeMap.incrementBy(t.type, elapsed); + } + + if (stat.extraInfo != null && t.extraTracingValues != null) { + int overlapLength = + Math.min(stat.extraInfo.length, t.extraTracingValues.length); + for (int i = 0; i < overlapLength; i++) { + stat.extraInfo[i] += t.extraTracingValues[i]; + AtomicTracerStatMap map = + extraTracingStatistics.get(i).getTracingStat(); + if (map != null) { + map.incrementBy(t.type, t.extraTracingValues[i]); + } + } + } + + if (elapsed < silenceThreshold) { + stat.silent++; + if (typeToSilentMap != null) { + typeToSilentMap.incrementBy(t.type, 1); + } + } + } + } + + boolean isEmpty() { + return events.size() == 0 && outstandingEvents.size() == 0; + } + + void truncateOutstandingEvents() { + isOutstandingEventsTruncated = true; + outstandingEvents.clear(); + } + + void truncateEvents() { + isEventsTruncated = true; + events.clear(); + } + + /** Produces the lovely Trace seen in the class comments */ + // Nullness checker does not understand that prettyPrint => indent != null + @SuppressWarnings("nullness") + @Override public String toString() { + + int numDigits = getMaxDigits(); + StringBuilder sb = new StringBuilder(); + long etime = -1; + LinkedList indent = prettyPrint ? new LinkedList() : null; + for (Event e : events) { + if (prettyPrint && !e.isStart && !indent.isEmpty()) { + indent.pop(); + } + sb.append(" "); + if (prettyPrint) { + sb.append(e.toString(etime, Joiner.on("").join(indent), numDigits)); + } else { + sb.append(e.toString(etime, "", 4)); + } + etime = e.eventTime(); + sb.append('\n'); + if (prettyPrint && e.isStart) { + indent.push("| "); + } + } + + if (outstandingEvents.size() != 0) { + long now = clock.currentTimeMillis(); + + sb.append(" Unstopped timers:\n"); + for (Tracer t : outstandingEvents) { + sb.append(" "). + append(t). + append(" ("). + append(now - t.startTimeMs). + append(" ms, started at "). + append(formatTime(t.startTimeMs)). + append(")\n"); + } + } + + for (String key : stats.keySet()) { + Stat stat = stats.get(key); + if (stat.count > 1) { + sb.append(" TOTAL "). + append(key). + append(" "). + append(stat.count). + append(" ("). + append(stat.clockTime). + append(" ms"); + if (stat.extraInfo != null) { + for (int i = 0; i < stat.extraInfo.length; i++) { + sb.append("; "); + sb.append(stat.extraInfo[i]). + append(' '). + append(extraTracingStatistics.get(i).getUnits()); + } + } + sb.append(")\n"); + } + } + return sb.toString(); + } + + /** + * Gets the maximum number of digits that can appear in the tracer output + * in the gaps between tracers or the duration of a tracer. This is used + * by the pretty printing case so that all of the tracers are aligned. + */ + private int getMaxDigits() { + long etime = -1; + long max_time = 0; + for (Event e : events) { + if (etime != -1) { + long time = e.eventTime() - etime; + max_time = Math.max(max_time, time); + } + if (!e.isStart) { + long time = e.tracer.stopTimeMs - e.tracer.startTimeMs; + max_time = Math.max(max_time, time); + } + etime = e.eventTime(); + } + // Minimum is 3 to preserve an indent even when max is small. + return Math.max(3, numDigits(max_time)); + } + } + + /** Holds the ThreadTrace for each thread. */ + private static ThreadLocal traces = + new ThreadLocal(); + + /** + * Get the ThreadTrace for the current thread, creating one if necessary. + */ + static ThreadTrace getThreadTrace() { + ThreadTrace t = traces.get(); + if (t == null) { + t = new ThreadTrace(); + t.prettyPrint = defaultPrettyPrint; + traces.set(t); + } + return t; + } + + /** Remove any ThreadTrace associated with the current thread */ + static void clearThreadTrace() { + traces.remove(); + } + + /** + * A TracingStatistic allows the program to add additional optional + * statistics to the trace output. + * + * The class com.google.monitoring.tracing.TracingStatistics + * contains several useful tracing statistics + * + */ + static interface TracingStatistic { + /** + * This method is called at the start of the trace. It should + * return a numeric result indicating the amount of the specific + * resource in use before the call started + * @param thread The current thread + * @return A numeric value indicating the amount of the resource + * already used. + */ + long start(Thread thread); + + /** + * This method is called at the end of the trace. It should + * return a numeric result indicating the amount of the specific + * resource in use after the call ends. The actual reported result + * will be the result end() - start() + * @param thread The current thread + * @return A numeric value indicating the amount of the resource + * currently used. + */ + long stop(Thread thread); + + /** + * Called when this tracing statistic is first enabled. A return + * value of True indicates that this statistic can successfully + * run in the current JVM. + * + * @return An indication of whether this statistic can be + * implemented in the current JVM. + */ + boolean enable(); + + /** Returns this tracing statistic's trace map. + * + * @return This tracing statistic's trace map. + */ + AtomicTracerStatMap getTracingStat(); + + /** A string that should be appended to the numeric output + * indicating what this is. + * + * @return A string indicating the units of this statistic and what it is. + */ + String getUnits(); + } + + /** + * This class encapsulates a map for keeping track of tracing statistics. + * It allows the caller to atomically increment named fields. + * + */ + static final class AtomicTracerStatMap { + private ConcurrentMap map = + new ConcurrentHashMap(); + + /** + * Atomically increment the specified field by the specified amount. + * + * @param key the name of the field + * @param delta the amount by which to increment the field + */ + // Nullness checker is not powerful enough to prove null-safety of + // this method + @SuppressWarnings("nullness") + void incrementBy(String key, long delta) { + // We use a compareAndSet strategy to update the map, which is much + // faster when there isn't too much contention. Look at a value, and + // conditionally update the map if the value hasn't changed. + // If it has changed, repeat. + Long oldValue = map.get(key); + if (oldValue == null) { + // Currently, the slot is empty + oldValue = map.putIfAbsent(key, delta); + if (oldValue == null) { + // The slot was still empty when we set it + return; + } else { + // Someone filled in the slot behind our back. oldValue has + // its current value + } + } + while (true) { + if (map.replace(key, oldValue, oldValue + delta)) { + break; + } + // Nullness checker doesn't understand that this cannot return null. + oldValue = map.get(key); + } + } + + /** + * Returns a map of key:value pairs. + */ + Map getMap() { + return map; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TransformAMDToCJSModule.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TransformAMDToCJSModule.java new file mode 100644 index 0000000..709e6f1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TransformAMDToCJSModule.java @@ -0,0 +1,302 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import java.util.Iterator; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Iterators; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; + +/** + * Rewrites an AMD module https://github.com/amdjs/amdjs-api/wiki/AMD to a + * CommonJS module. See {@link ProcessCommonJSModules} for follow up processing + * step. + */ +class TransformAMDToCJSModule implements CompilerPass { + + @VisibleForTesting + final static DiagnosticType UNSUPPORTED_DEFINE_SIGNATURE_ERROR = + DiagnosticType.error( + "UNSUPPORTED_DEFINE_SIGNATURE", + "Only define(function() ...), define(OBJECT_LITERAL) and define(" + + "['dep', 'dep1'], function(d0, d2, [exports, module]) ...) forms " + + "are currently supported."); + final static DiagnosticType NON_TOP_LEVEL_STATEMENT_DEFINE_ERROR = + DiagnosticType.error( + "NON_TOP_LEVEL_STATEMENT_DEFINE", + "The define function must be called as a top-level statement."); + final static DiagnosticType REQUIREJS_PLUGINS_NOT_SUPPORTED_WARNING = + DiagnosticType.warning( + "REQUIREJS_PLUGINS_NOT_SUPPORTED", + "Plugins in define requirements are not supported: {0}"); + + final static String VAR_RENAME_SUFFIX = "__alias"; + + + private final AbstractCompiler compiler; + private int renameIndex = 0; + + TransformAMDToCJSModule(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, new TransformAMDModulesCallback()); + } + + private void unsupportedDefineError(NodeTraversal t, Node n) { + t.report(n, UNSUPPORTED_DEFINE_SIGNATURE_ERROR); + } + + /** + * The modules "exports", "require" and "module" are virtual in terms of + * existing implicitly in CommonJS. + */ + private boolean isVirtualModuleName(String moduleName) { + return "exports".equals(moduleName) || "require".equals(moduleName) || + "module".equals(moduleName); + } + + /** + * Rewrites calls to define which has to be in void context just below the + * current script node. + */ + private class TransformAMDModulesCallback extends AbstractPostOrderCallback { + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isCall() && n.getFirstChild() != null && + n.getFirstChild().isName() && + "define".equals(n.getFirstChild().getString())) { + Scope.Var define = t.getScope().getVar(n.getFirstChild(). + getString()); + if (define != null && !define.isGlobal()) { + // Ignore non-global define. + return; + } + if (!(parent.isExprResult() && parent.getParent().isScript())) { + t.report(n, NON_TOP_LEVEL_STATEMENT_DEFINE_ERROR); + return; + } + Node script = parent.getParent(); + Node requiresNode = null; + Node callback = null; + int defineArity = n.getChildCount() - 1; + if (defineArity == 0) { + unsupportedDefineError(t, n); + return; + } else if (defineArity == 1) { + callback = n.getChildAtIndex(1); + if (callback.isObjectLit()) { + handleDefineObjectLiteral(parent, callback, script); + return; + } + } else if (defineArity == 2) { + requiresNode = n.getChildAtIndex(1); + callback = n.getChildAtIndex(2); + } else if (defineArity >= 3) { + unsupportedDefineError(t, n); + return; + } + + if (!callback.isFunction() || + (requiresNode != null && !requiresNode.isArrayLit())) { + unsupportedDefineError(t, n); + return; + } + + handleRequiresAndParamList(t, n, script, requiresNode, callback); + + Node callbackBlock = callback.getChildAtIndex(2); + NodeTraversal.traverse(compiler, callbackBlock, + new DefineCallbackReturnCallback()); + + moveCallbackContentToTopLevel(parent, script, callbackBlock); + compiler.reportCodeChange(); + } + } + + /** + * When define is called with an object literal, assign it to exports and + * we're done. + */ + private void handleDefineObjectLiteral(Node parent, Node onlyExport, + Node script) { + onlyExport.getParent().removeChild(onlyExport); + script.replaceChild(parent, + IR.exprResult(IR.assign(IR.name("exports"), onlyExport)) + .copyInformationFromForTree(onlyExport)); + compiler.reportCodeChange(); + } + + /** + * Rewrites the required modules to + * var nameInParamList = require("nameFromRequireList"); + */ + private void handleRequiresAndParamList(NodeTraversal t, Node defineNode, + Node script, Node requiresNode, Node callback) { + Iterator paramList = callback.getChildAtIndex(1).children(). + iterator(); + Iterator requires = requiresNode != null ? + requiresNode.children().iterator() : Iterators.emptyIterator(); + while (paramList.hasNext() || requires.hasNext()) { + Node aliasNode = paramList.hasNext() ? paramList.next() : null; + Node modNode = requires.hasNext() ? requires.next() : null; + handleRequire(t, defineNode, script, callback, aliasNode, modNode); + } + } + + /** + * Rewrite a single require call. + */ + private void handleRequire(NodeTraversal t, Node defineNode, Node script, + Node callback, Node aliasNode, Node modNode) { + String moduleName = null; + if (modNode != null) { + moduleName = handlePlugins(t, script, modNode.getString(), modNode); + } + + if (isVirtualModuleName(moduleName)) { + return; + } + + String aliasName = aliasNode != null ? aliasNode.getString() : null; + Scope globalScope = t.getScope(); + if (aliasName != null && + globalScope.isDeclared(aliasName, true)) { + while (true) { + String renamed = aliasName + VAR_RENAME_SUFFIX + renameIndex; + if (!globalScope.isDeclared(renamed, true)) { + NodeTraversal.traverse(compiler, callback, + new RenameCallback(aliasName, renamed)); + aliasName = renamed; + break; + } + renameIndex++; + } + } + + Node requireNode; + if (moduleName != null) { + Node call = IR.call(IR.name("require"), IR.string(moduleName)); + call.putBooleanProp(Node.FREE_CALL, true); + if (aliasName != null) { + requireNode = IR.var(IR.name(aliasName), call) + .copyInformationFromForTree(aliasNode); + } else { + requireNode = IR.exprResult(call). + copyInformationFromForTree(modNode); + } + } else { + // ignore exports, require and module (because they are implicit + // in CommonJS); + if (isVirtualModuleName(aliasName)) { + return; + } + requireNode = IR.var(IR.name(aliasName), IR.nullNode()) + .copyInformationFromForTree(aliasNode); + } + + script.addChildBefore(requireNode, + defineNode.getParent()); + } + + /** + * Require.js supports a range of plugins that are hard to support + * statically. Generally none are supported right now with the + * exception of a simple hack to support condition loading. This + * was added to make compilation of Dojo work better but will + * probably break, so just don't use them :) + */ + private String handlePlugins(NodeTraversal t, Node script, + String moduleName, Node modNode) { + if (moduleName.contains("!")) { + t.report(modNode, REQUIREJS_PLUGINS_NOT_SUPPORTED_WARNING, moduleName); + int condition = moduleName.indexOf('?'); + if (condition > 0) { + if (moduleName.contains(":")) { + return null; + } + return handlePlugins(t, script, moduleName.substring(condition + 1), + modNode); + } + moduleName = null; + } + return moduleName; + } + + /** + * Moves the statements in the callback to be direct children of the + * current script. + */ + private void moveCallbackContentToTopLevel(Node defineParent, Node script, + Node callbackBlock) { + int curIndex = script.getIndexOfChild(defineParent); + script.removeChild(defineParent); + callbackBlock.getParent().removeChild(callbackBlock); + Node before = script.getChildAtIndex(curIndex); + if (before != null) { + script.addChildBefore(callbackBlock, before); + } + script.addChildToBack(callbackBlock); + NodeUtil.tryMergeBlock(callbackBlock); + } + } + + /** + * Rewrites the return statement of the callback to be an assignment to + * module.exports. + */ + private class DefineCallbackReturnCallback extends + NodeTraversal.AbstractShallowStatementCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isReturn() && n.hasChildren()) { + Node retVal = n.getFirstChild(); + n.removeChild(retVal); + parent.replaceChild(n, IR.exprResult( + IR.assign( + IR.getprop(IR.name("module"), IR.string("exports")), retVal)) + .useSourceInfoFromForTree(n)); + } + } + } + + /** + * Renames names; + */ + private class RenameCallback extends AbstractPostOrderCallback { + + private final String from; + private final String to; + + public RenameCallback(String from, String to) { + this.from = from; + this.to = to; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isName() && from.equals(n.getString())) { + n.setString(to); + n.putProp(Node.ORIGINALNAME_PROP, from); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypeCheck.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypeCheck.java new file mode 100644 index 0000000..96f256d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypeCheck.java @@ -0,0 +1,2009 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import static com.google.javascript.rhino.jstype.JSTypeNative.ARRAY_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NULL_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.REGEXP_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.type.ReverseAbstractInterpreter; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.EnumType; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.TernaryValue; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Set; + +/** + *

      Checks the types of JS expressions against any declared type + * information.

      + * + */ +public class TypeCheck implements NodeTraversal.Callback, CompilerPass { + + // + // Internal errors + // + static final DiagnosticType UNEXPECTED_TOKEN = DiagnosticType.error( + "JSC_INTERNAL_ERROR_UNEXPECTED_TOKEN", + "Internal Error: Don't know how to handle {0}"); + + + // + // User errors + // + // TODO(nicksantos): delete this + static final DiagnosticType BAD_DELETE = + DiagnosticType.warning( + "JSC_BAD_DELETE_OPERAND", + "delete operator needs a reference operand"); + + + // + // User warnings + // + + protected static final String OVERRIDING_PROTOTYPE_WITH_NON_OBJECT = + "overriding prototype with non-object"; + + // TODO(user): make all the non private messages private once the + // TypedScopeCreator has been merged with the type checker. + static final DiagnosticType DETERMINISTIC_TEST = + DiagnosticType.warning( + "JSC_DETERMINISTIC_TEST", + "condition always evaluates to {2}\n" + + "left : {0}\n" + + "right: {1}"); + + static final DiagnosticType DETERMINISTIC_TEST_NO_RESULT = + DiagnosticType.warning( + "JSC_DETERMINISTIC_TEST_NO_RESULT", + "condition always evaluates to the same value\n" + + "left : {0}\n" + + "right: {1}"); + + static final DiagnosticType INEXISTENT_ENUM_ELEMENT = + DiagnosticType.warning( + "JSC_INEXISTENT_ENUM_ELEMENT", + "element {0} does not exist on this enum"); + + // disabled by default. This one only makes sense if you're using + // well-typed externs. + static final DiagnosticType INEXISTENT_PROPERTY = + DiagnosticType.disabled( + "JSC_INEXISTENT_PROPERTY", + "Property {0} never defined on {1}"); + + protected static final DiagnosticType NOT_A_CONSTRUCTOR = + DiagnosticType.warning( + "JSC_NOT_A_CONSTRUCTOR", + "cannot instantiate non-constructor"); + + static final DiagnosticType BIT_OPERATION = + DiagnosticType.warning( + "JSC_BAD_TYPE_FOR_BIT_OPERATION", + "operator {0} cannot be applied to {1}"); + + static final DiagnosticType NOT_CALLABLE = + DiagnosticType.warning( + "JSC_NOT_FUNCTION_TYPE", + "{0} expressions are not callable"); + + static final DiagnosticType CONSTRUCTOR_NOT_CALLABLE = + DiagnosticType.warning( + "JSC_CONSTRUCTOR_NOT_CALLABLE", + "Constructor {0} should be called with the \"new\" keyword"); + + static final DiagnosticType FUNCTION_MASKS_VARIABLE = + DiagnosticType.warning( + "JSC_FUNCTION_MASKS_VARIABLE", + "function {0} masks variable (IE bug)"); + + static final DiagnosticType MULTIPLE_VAR_DEF = DiagnosticType.warning( + "JSC_MULTIPLE_VAR_DEF", + "declaration of multiple variables with shared type information"); + + static final DiagnosticType ENUM_DUP = DiagnosticType.error("JSC_ENUM_DUP", + "enum element {0} already defined"); + + static final DiagnosticType ENUM_NOT_CONSTANT = + DiagnosticType.warning("JSC_ENUM_NOT_CONSTANT", + "enum key {0} must be a syntactic constant"); + + static final DiagnosticType INVALID_INTERFACE_MEMBER_DECLARATION = + DiagnosticType.warning( + "JSC_INVALID_INTERFACE_MEMBER_DECLARATION", + "interface members can only be empty property declarations," + + " empty functions{0}"); + + static final DiagnosticType INTERFACE_FUNCTION_NOT_EMPTY = + DiagnosticType.warning( + "JSC_INTERFACE_FUNCTION_NOT_EMPTY", + "interface member functions must have an empty body"); + + static final DiagnosticType CONFLICTING_EXTENDED_TYPE = + DiagnosticType.warning( + "JSC_CONFLICTING_EXTENDED_TYPE", + "{1} cannot extend this type; {0}s can only extend {0}s"); + + static final DiagnosticType CONFLICTING_IMPLEMENTED_TYPE = + DiagnosticType.warning( + "JSC_CONFLICTING_IMPLEMENTED_TYPE", + "{0} cannot implement this type; " + + "an interface can only extend, but not implement interfaces"); + + static final DiagnosticType BAD_IMPLEMENTED_TYPE = + DiagnosticType.warning( + "JSC_IMPLEMENTS_NON_INTERFACE", + "can only implement interfaces"); + + static final DiagnosticType HIDDEN_SUPERCLASS_PROPERTY = + DiagnosticType.warning( + "JSC_HIDDEN_SUPERCLASS_PROPERTY", + "property {0} already defined on superclass {1}; " + + "use @override to override it"); + + static final DiagnosticType HIDDEN_INTERFACE_PROPERTY = + DiagnosticType.warning( + "JSC_HIDDEN_INTERFACE_PROPERTY", + "property {0} already defined on interface {1}; " + + "use @override to override it"); + + static final DiagnosticType HIDDEN_SUPERCLASS_PROPERTY_MISMATCH = + DiagnosticType.warning("JSC_HIDDEN_SUPERCLASS_PROPERTY_MISMATCH", + "mismatch of the {0} property type and the type " + + "of the property it overrides from superclass {1}\n" + + "original: {2}\n" + + "override: {3}"); + + static final DiagnosticType UNKNOWN_OVERRIDE = + DiagnosticType.warning( + "JSC_UNKNOWN_OVERRIDE", + "property {0} not defined on any superclass of {1}"); + + static final DiagnosticType INTERFACE_METHOD_OVERRIDE = + DiagnosticType.warning( + "JSC_INTERFACE_METHOD_OVERRIDE", + "property {0} is already defined by the {1} extended interface"); + + static final DiagnosticType UNKNOWN_EXPR_TYPE = + DiagnosticType.warning("JSC_UNKNOWN_EXPR_TYPE", + "could not determine the type of this expression"); + + static final DiagnosticType UNRESOLVED_TYPE = + DiagnosticType.warning("JSC_UNRESOLVED_TYPE", + "could not resolve the name {0} to a type"); + + static final DiagnosticType WRONG_ARGUMENT_COUNT = + DiagnosticType.warning( + "JSC_WRONG_ARGUMENT_COUNT", + "Function {0}: called with {1} argument(s). " + + "Function requires at least {2} argument(s){3}."); + + static final DiagnosticType ILLEGAL_IMPLICIT_CAST = + DiagnosticType.warning( + "JSC_ILLEGAL_IMPLICIT_CAST", + "Illegal annotation on {0}. @implicitCast may only be used in " + + "externs."); + + static final DiagnosticType INCOMPATIBLE_EXTENDED_PROPERTY_TYPE = + DiagnosticType.warning( + "JSC_INCOMPATIBLE_EXTENDED_PROPERTY_TYPE", + "Interface {0} has a property {1} with incompatible types in " + + "its super interfaces {2} and {3}"); + + static final DiagnosticType EXPECTED_THIS_TYPE = + DiagnosticType.warning( + "JSC_EXPECTED_THIS_TYPE", + "\"{0}\" must be called with a \"this\" type"); + + static final DiagnosticType IN_USED_WITH_STRUCT = + DiagnosticType.warning("JSC_IN_USED_WITH_STRUCT", + "Cannot use the IN operator with structs"); + + static final DiagnosticType ILLEGAL_PROPERTY_CREATION = + DiagnosticType.warning("JSC_ILLEGAL_PROPERTY_CREATION", + "Cannot add a property to a struct instance " + + "after it is constructed."); + + static final DiagnosticType ILLEGAL_OBJLIT_KEY = + DiagnosticType.warning( + "ILLEGAL_OBJLIT_KEY", + "Illegal key, the object literal is a {0}"); + + static final DiagnosticGroup ALL_DIAGNOSTICS = new DiagnosticGroup( + DETERMINISTIC_TEST, + DETERMINISTIC_TEST_NO_RESULT, + INEXISTENT_ENUM_ELEMENT, + INEXISTENT_PROPERTY, + NOT_A_CONSTRUCTOR, + BIT_OPERATION, + NOT_CALLABLE, + CONSTRUCTOR_NOT_CALLABLE, + FUNCTION_MASKS_VARIABLE, + MULTIPLE_VAR_DEF, + ENUM_DUP, + ENUM_NOT_CONSTANT, + INVALID_INTERFACE_MEMBER_DECLARATION, + INTERFACE_FUNCTION_NOT_EMPTY, + CONFLICTING_EXTENDED_TYPE, + CONFLICTING_IMPLEMENTED_TYPE, + BAD_IMPLEMENTED_TYPE, + HIDDEN_SUPERCLASS_PROPERTY, + HIDDEN_INTERFACE_PROPERTY, + HIDDEN_SUPERCLASS_PROPERTY_MISMATCH, + UNKNOWN_OVERRIDE, + INTERFACE_METHOD_OVERRIDE, + UNKNOWN_EXPR_TYPE, + UNRESOLVED_TYPE, + WRONG_ARGUMENT_COUNT, + ILLEGAL_IMPLICIT_CAST, + INCOMPATIBLE_EXTENDED_PROPERTY_TYPE, + EXPECTED_THIS_TYPE, + IN_USED_WITH_STRUCT, + ILLEGAL_PROPERTY_CREATION, + ILLEGAL_OBJLIT_KEY, + RhinoErrorReporter.TYPE_PARSE_ERROR, + TypedScopeCreator.UNKNOWN_LENDS, + TypedScopeCreator.LENDS_ON_NON_OBJECT, + TypedScopeCreator.CTOR_INITIALIZER, + TypedScopeCreator.IFACE_INITIALIZER, + FunctionTypeBuilder.THIS_TYPE_NON_OBJECT); + + private final AbstractCompiler compiler; + private final TypeValidator validator; + + private final ReverseAbstractInterpreter reverseInterpreter; + + private final JSTypeRegistry typeRegistry; + private Scope topScope; + + private MemoizedScopeCreator scopeCreator; + + private final CheckLevel reportMissingOverride; + private final CheckLevel reportUnknownTypes; + + // This may be expensive, so don't emit these warnings if they're + // explicitly turned off. + private boolean reportMissingProperties = true; + + private InferJSDocInfo inferJSDocInfo = null; + + // These fields are used to calculate the percentage of expressions typed. + private int typedCount = 0; + private int nullCount = 0; + private int unknownCount = 0; + private boolean inExterns; + + // A state boolean to see we are currently in @notypecheck section of the + // code. + private int noTypeCheckSection = 0; + + public TypeCheck(AbstractCompiler compiler, + ReverseAbstractInterpreter reverseInterpreter, + JSTypeRegistry typeRegistry, + Scope topScope, + MemoizedScopeCreator scopeCreator, + CheckLevel reportMissingOverride, + CheckLevel reportUnknownTypes) { + this.compiler = compiler; + this.validator = compiler.getTypeValidator(); + this.reverseInterpreter = reverseInterpreter; + this.typeRegistry = typeRegistry; + this.topScope = topScope; + this.scopeCreator = scopeCreator; + this.reportMissingOverride = reportMissingOverride; + this.reportUnknownTypes = reportUnknownTypes; + this.inferJSDocInfo = new InferJSDocInfo(compiler); + } + + public TypeCheck(AbstractCompiler compiler, + ReverseAbstractInterpreter reverseInterpreter, + JSTypeRegistry typeRegistry, + CheckLevel reportMissingOverride, + CheckLevel reportUnknownTypes) { + this(compiler, reverseInterpreter, typeRegistry, null, null, + reportMissingOverride, reportUnknownTypes); + } + + TypeCheck(AbstractCompiler compiler, + ReverseAbstractInterpreter reverseInterpreter, + JSTypeRegistry typeRegistry) { + this(compiler, reverseInterpreter, typeRegistry, null, null, + CheckLevel.WARNING, CheckLevel.OFF); + } + + /** Turn on the missing property check. Returns this for easy chaining. */ + TypeCheck reportMissingProperties(boolean report) { + reportMissingProperties = report; + return this; + } + + /** + * Main entry point for this phase of processing. This follows the pattern for + * JSCompiler phases. + * + * @param externsRoot The root of the externs parse tree. + * @param jsRoot The root of the input parse tree to be checked. + */ + @Override + public void process(Node externsRoot, Node jsRoot) { + Preconditions.checkNotNull(scopeCreator); + Preconditions.checkNotNull(topScope); + + Node externsAndJs = jsRoot.getParent(); + Preconditions.checkState(externsAndJs != null); + Preconditions.checkState( + externsRoot == null || externsAndJs.hasChild(externsRoot)); + + if (externsRoot != null) { + check(externsRoot, true); + } + check(jsRoot, false); + } + + /** Main entry point of this phase for testing code. */ + public Scope processForTesting(Node externsRoot, Node jsRoot) { + Preconditions.checkState(scopeCreator == null); + Preconditions.checkState(topScope == null); + + Preconditions.checkState(jsRoot.getParent() != null); + Node externsAndJsRoot = jsRoot.getParent(); + + scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); + topScope = scopeCreator.createScope(externsAndJsRoot, null); + + TypeInferencePass inference = new TypeInferencePass(compiler, + reverseInterpreter, topScope, scopeCreator); + + inference.process(externsRoot, jsRoot); + process(externsRoot, jsRoot); + + return topScope; + } + + + public void check(Node node, boolean externs) { + Preconditions.checkNotNull(node); + + NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator); + inExterns = externs; + t.traverseWithScope(node, topScope); + if (externs) { + inferJSDocInfo.process(node, null); + } else { + inferJSDocInfo.process(null, node); + } + } + + + private void checkNoTypeCheckSection(Node n, boolean enterSection) { + switch (n.getType()) { + case Token.SCRIPT: + case Token.BLOCK: + case Token.VAR: + case Token.FUNCTION: + case Token.ASSIGN: + JSDocInfo info = n.getJSDocInfo(); + if (info != null && info.isNoTypeCheck()) { + if (enterSection) { + noTypeCheckSection++; + } else { + noTypeCheckSection--; + } + } + validator.setShouldReport(noTypeCheckSection == 0); + break; + } + } + + private void report(NodeTraversal t, Node n, DiagnosticType diagnosticType, + String... arguments) { + if (noTypeCheckSection == 0) { + t.report(n, diagnosticType, arguments); + } + } + + @Override + public boolean shouldTraverse( + NodeTraversal t, Node n, Node parent) { + checkNoTypeCheckSection(n, true); + switch (n.getType()) { + case Token.FUNCTION: + // normal type checking + final Scope outerScope = t.getScope(); + final String functionPrivateName = n.getFirstChild().getString(); + if (functionPrivateName != null && functionPrivateName.length() > 0 && + outerScope.isDeclared(functionPrivateName, false) && + // Ideally, we would want to check whether the type in the scope + // differs from the type being defined, but then the extern + // redeclarations of built-in types generates spurious warnings. + !(outerScope.getVar( + functionPrivateName).getType() instanceof FunctionType)) { + report(t, n, FUNCTION_MASKS_VARIABLE, functionPrivateName); + } + + // TODO(user): Only traverse the function's body. The function's + // name and arguments are traversed by the scope creator, and ideally + // should not be traversed by the type checker. + break; + } + return true; + } + + /** + * This is the meat of the type checking. It is basically one big switch, + * with each case representing one type of parse tree node. The individual + * cases are usually pretty straightforward. + * + * @param t The node traversal object that supplies context, such as the + * scope chain to use in name lookups as well as error reporting. + * @param n The node being visited. + * @param parent The parent of the node n. + */ + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + JSType childType; + JSType leftType, rightType; + Node left, right; + // To be explicitly set to false if the node is not typeable. + boolean typeable = true; + + switch (n.getType()) { + case Token.CAST: + Node expr = n.getFirstChild(); + ensureTyped(t, n, getJSType(expr)); + + // If the cast, tightens the type apply it, so it is available post + // normalization. + JSType castType = getJSType(n); + JSType exprType = getJSType(expr); + if (castType.isSubtype(exprType)) { + expr.setJSType(castType); + } + break; + + case Token.NAME: + typeable = visitName(t, n, parent); + break; + + case Token.PARAM_LIST: + typeable = false; + break; + + case Token.COMMA: + ensureTyped(t, n, getJSType(n.getLastChild())); + break; + + case Token.TRUE: + case Token.FALSE: + ensureTyped(t, n, BOOLEAN_TYPE); + break; + + case Token.THIS: + ensureTyped(t, n, t.getScope().getTypeOfThis()); + break; + + case Token.NULL: + ensureTyped(t, n, NULL_TYPE); + break; + + case Token.NUMBER: + ensureTyped(t, n, NUMBER_TYPE); + break; + + case Token.STRING: + ensureTyped(t, n, STRING_TYPE); + break; + + case Token.STRING_KEY: + typeable = false; + break; + + case Token.GETTER_DEF: + case Token.SETTER_DEF: + // Object literal keys are handled with OBJECTLIT + break; + + case Token.ARRAYLIT: + ensureTyped(t, n, ARRAY_TYPE); + break; + + case Token.REGEXP: + ensureTyped(t, n, REGEXP_TYPE); + break; + + case Token.GETPROP: + visitGetProp(t, n, parent); + typeable = !(parent.isAssign() && + parent.getFirstChild() == n); + break; + + case Token.GETELEM: + visitGetElem(t, n); + // The type of GETELEM is always unknown, so no point counting that. + // If that unknown leaks elsewhere (say by an assignment to another + // variable), then it will be counted. + typeable = false; + break; + + case Token.VAR: + visitVar(t, n); + typeable = false; + break; + + case Token.NEW: + visitNew(t, n); + break; + + case Token.CALL: + visitCall(t, n); + typeable = !parent.isExprResult(); + break; + + case Token.RETURN: + visitReturn(t, n); + typeable = false; + break; + + case Token.DEC: + case Token.INC: + left = n.getFirstChild(); + checkPropCreation(t, left); + validator.expectNumber(t, left, getJSType(left), "increment/decrement"); + ensureTyped(t, n, NUMBER_TYPE); + break; + + case Token.NOT: + ensureTyped(t, n, BOOLEAN_TYPE); + break; + + case Token.VOID: + ensureTyped(t, n, VOID_TYPE); + break; + + case Token.TYPEOF: + ensureTyped(t, n, STRING_TYPE); + break; + + case Token.BITNOT: + childType = getJSType(n.getFirstChild()); + if (!childType.matchesInt32Context()) { + report(t, n, BIT_OPERATION, NodeUtil.opToStr(n.getType()), + childType.toString()); + } + ensureTyped(t, n, NUMBER_TYPE); + break; + + case Token.POS: + case Token.NEG: + left = n.getFirstChild(); + validator.expectNumber(t, left, getJSType(left), "sign operator"); + ensureTyped(t, n, NUMBER_TYPE); + break; + + case Token.EQ: + case Token.NE: + case Token.SHEQ: + case Token.SHNE: { + left = n.getFirstChild(); + right = n.getLastChild(); + + if (left.isTypeOf()) { + if (right.isString()) { + checkTypeofString(t, right, right.getString()); + } + } else if (right.isTypeOf() && left.isString()) { + checkTypeofString(t, left, left.getString()); + } + + leftType = getJSType(left); + rightType = getJSType(right); + + // We do not want to warn about explicit comparisons to VOID. People + // often do this if they think their type annotations screwed up. + // + // We do want to warn about cases where people compare things like + // (Array|null) == (Function|null) + // because it probably means they screwed up. + // + // This heuristic here is not perfect, but should catch cases we + // care about without too many false negatives. + JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); + JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); + + TernaryValue result = TernaryValue.UNKNOWN; + if (n.getType() == Token.EQ || n.getType() == Token.NE) { + result = leftTypeRestricted.testForEquality(rightTypeRestricted); + if (n.isNE()) { + result = result.not(); + } + } else { + // SHEQ or SHNE + if (!leftTypeRestricted.canTestForShallowEqualityWith( + rightTypeRestricted)) { + result = n.getType() == Token.SHEQ ? + TernaryValue.FALSE : TernaryValue.TRUE; + } + } + + if (result != TernaryValue.UNKNOWN) { + report(t, n, DETERMINISTIC_TEST, leftType.toString(), + rightType.toString(), result.toString()); + } + ensureTyped(t, n, BOOLEAN_TYPE); + break; + } + + case Token.LT: + case Token.LE: + case Token.GT: + case Token.GE: + leftType = getJSType(n.getFirstChild()); + rightType = getJSType(n.getLastChild()); + if (rightType.isNumber()) { + validator.expectNumber( + t, n, leftType, "left side of numeric comparison"); + } else if (leftType.isNumber()) { + validator.expectNumber( + t, n, rightType, "right side of numeric comparison"); + } else if (leftType.matchesNumberContext() && + rightType.matchesNumberContext()) { + // OK. + } else { + // Whether the comparison is numeric will be determined at runtime + // each time the expression is evaluated. Regardless, both operands + // should match a string context. + String message = "left side of comparison"; + validator.expectString(t, n, leftType, message); + validator.expectNotNullOrUndefined( + t, n, leftType, message, getNativeType(STRING_TYPE)); + message = "right side of comparison"; + validator.expectString(t, n, rightType, message); + validator.expectNotNullOrUndefined( + t, n, rightType, message, getNativeType(STRING_TYPE)); + } + ensureTyped(t, n, BOOLEAN_TYPE); + break; + + case Token.IN: + left = n.getFirstChild(); + right = n.getLastChild(); + rightType = getJSType(right); + validator.expectString(t, left, getJSType(left), "left side of 'in'"); + validator.expectObject(t, n, rightType, "'in' requires an object"); + if (rightType.isStruct()) { + report(t, right, IN_USED_WITH_STRUCT); + } + ensureTyped(t, n, BOOLEAN_TYPE); + break; + + case Token.INSTANCEOF: + left = n.getFirstChild(); + right = n.getLastChild(); + rightType = getJSType(right).restrictByNotNullOrUndefined(); + validator.expectAnyObject( + t, left, getJSType(left), "deterministic instanceof yields false"); + validator.expectActualObject( + t, right, rightType, "instanceof requires an object"); + ensureTyped(t, n, BOOLEAN_TYPE); + break; + + case Token.ASSIGN: + visitAssign(t, n); + typeable = false; + break; + + case Token.ASSIGN_LSH: + case Token.ASSIGN_RSH: + case Token.ASSIGN_URSH: + case Token.ASSIGN_DIV: + case Token.ASSIGN_MOD: + case Token.ASSIGN_BITOR: + case Token.ASSIGN_BITXOR: + case Token.ASSIGN_BITAND: + case Token.ASSIGN_SUB: + case Token.ASSIGN_ADD: + case Token.ASSIGN_MUL: + checkPropCreation(t, n.getFirstChild()); + // fall through + + case Token.LSH: + case Token.RSH: + case Token.URSH: + case Token.DIV: + case Token.MOD: + case Token.BITOR: + case Token.BITXOR: + case Token.BITAND: + case Token.SUB: + case Token.ADD: + case Token.MUL: + visitBinaryOperator(n.getType(), t, n); + break; + + case Token.DELPROP: + ensureTyped(t, n, BOOLEAN_TYPE); + break; + + case Token.CASE: + JSType switchType = getJSType(parent.getFirstChild()); + JSType caseType = getJSType(n.getFirstChild()); + validator.expectSwitchMatchesCase(t, n, switchType, caseType); + typeable = false; + break; + + case Token.WITH: { + Node child = n.getFirstChild(); + childType = getJSType(child); + validator.expectObject(t, child, childType, "with requires an object"); + typeable = false; + break; + } + + case Token.FUNCTION: + visitFunction(t, n); + break; + + // These nodes have no interesting type behavior. + case Token.LABEL: + case Token.LABEL_NAME: + case Token.SWITCH: + case Token.BREAK: + case Token.CATCH: + case Token.TRY: + case Token.SCRIPT: + case Token.EXPR_RESULT: + case Token.BLOCK: + case Token.EMPTY: + case Token.DEFAULT_CASE: + case Token.CONTINUE: + case Token.DEBUGGER: + case Token.THROW: + typeable = false; + break; + + // These nodes require data flow analysis. + case Token.DO: + case Token.IF: + case Token.WHILE: + typeable = false; + break; + + case Token.FOR: + if (NodeUtil.isForIn(n)) { + Node obj = n.getChildAtIndex(1); + if (getJSType(obj).isStruct()) { + report(t, obj, IN_USED_WITH_STRUCT); + } + } + typeable = false; + break; + + // These nodes are typed during the type inference. + case Token.AND: + case Token.HOOK: + case Token.OBJECTLIT: + case Token.OR: + if (n.getJSType() != null) { // If we didn't run type inference. + ensureTyped(t, n); + } else { + // If this is an enum, then give that type to the objectlit as well. + if ((n.isObjectLit()) + && (parent.getJSType() instanceof EnumType)) { + ensureTyped(t, n, parent.getJSType()); + } else { + ensureTyped(t, n); + } + } + if (n.isObjectLit()) { + JSType typ = getJSType(n); + for (Node key : n.children()) { + visitObjLitKey(t, key, n, typ); + } + } + break; + + default: + report(t, n, UNEXPECTED_TOKEN, Token.name(n.getType())); + ensureTyped(t, n); + break; + } + + // Don't count externs since the user's code may not even use that part. + typeable = typeable && !inExterns; + + if (typeable) { + doPercentTypedAccounting(t, n); + } + + checkNoTypeCheckSection(n, false); + } + + private void checkTypeofString(NodeTraversal t, Node n, String s) { + if (!(s.equals("number") || s.equals("string") || s.equals("boolean") || + s.equals("undefined") || s.equals("function") || + s.equals("object") || s.equals("unknown"))) { + validator.expectValidTypeofName(t, n, s); + } + } + + /** + * Counts the given node in the typed statistics. + * @param n a node that should be typed + */ + private void doPercentTypedAccounting(NodeTraversal t, Node n) { + JSType type = n.getJSType(); + if (type == null) { + nullCount++; + } else if (type.isUnknownType()) { + if (reportUnknownTypes.isOn()) { + compiler.report( + t.makeError(n, reportUnknownTypes, UNKNOWN_EXPR_TYPE)); + } + unknownCount++; + } else { + typedCount++; + } + } + + /** + * Visits an assignment lvalue = rvalue. If the + * lvalue is a prototype modification, we change the schema + * of the object type it is referring to. + * @param t the traversal + * @param assign the assign node + * (assign.isAssign() is an implicit invariant) + */ + private void visitAssign(NodeTraversal t, Node assign) { + JSDocInfo info = assign.getJSDocInfo(); + Node lvalue = assign.getFirstChild(); + Node rvalue = assign.getLastChild(); + + // Check property sets to 'object.property' when 'object' is known. + if (lvalue.isGetProp()) { + Node object = lvalue.getFirstChild(); + JSType objectJsType = getJSType(object); + Node property = lvalue.getLastChild(); + String pname = property.getString(); + + // the first name in this getprop refers to an interface + // we perform checks in addition to the ones below + if (object.isGetProp()) { + JSType jsType = getJSType(object.getFirstChild()); + if (jsType.isInterface() && + object.getLastChild().getString().equals("prototype")) { + visitInterfaceGetprop(t, assign, object, pname, lvalue, rvalue); + } + } + + checkEnumAlias(t, info, rvalue); + checkPropCreation(t, lvalue); + + // Prototype assignments are special, because they actually affect + // the definition of a class. These are mostly validated + // during TypedScopeCreator, and we only look for the "dumb" cases here. + // object.prototype = ...; + if (pname.equals("prototype")) { + if (objectJsType != null && objectJsType.isFunctionType()) { + FunctionType functionType = objectJsType.toMaybeFunctionType(); + if (functionType.isConstructor()) { + JSType rvalueType = rvalue.getJSType(); + validator.expectObject(t, rvalue, rvalueType, + OVERRIDING_PROTOTYPE_WITH_NON_OBJECT); + // Only assign structs to the prototype of a @struct constructor + if (functionType.makesStructs() && !rvalueType.isStruct()) { + String funName = functionType.getTypeOfThis().toString(); + compiler.report(t.makeError(assign, CONFLICTING_EXTENDED_TYPE, + "struct", funName)); + } + return; + } + } + } + + // The generic checks for 'object.property' when 'object' is known, + // and 'property' is declared on it. + // object.property = ...; + ObjectType type = ObjectType.cast( + objectJsType.restrictByNotNullOrUndefined()); + if (type != null) { + if (type.hasProperty(pname) && + !type.isPropertyTypeInferred(pname) && + !propertyIsImplicitCast(type, pname)) { + JSType expectedType = type.getPropertyType(pname); + if (!expectedType.isUnknownType()) { + validator.expectCanAssignToPropertyOf( + t, assign, getJSType(rvalue), + expectedType, object, pname); + checkPropertyInheritanceOnGetpropAssign( + t, assign, object, pname, info, expectedType); + return; + } + } + } + + // If we couldn't get the property type with normal object property + // lookups, then check inheritance anyway with the unknown type. + checkPropertyInheritanceOnGetpropAssign( + t, assign, object, pname, info, getNativeType(UNKNOWN_TYPE)); + } + + // Check qualified name sets to 'object' and 'object.property'. + // This can sometimes handle cases when the type of 'object' is not known. + // e.g., + // var obj = createUnknownType(); + // /** @type {number} */ obj.foo = true; + JSType leftType = getJSType(lvalue); + if (lvalue.isQualifiedName()) { + // variable with inferred type case + JSType rvalueType = getJSType(assign.getLastChild()); + Var var = t.getScope().getVar(lvalue.getQualifiedName()); + if (var != null) { + if (var.isTypeInferred()) { + return; + } + + if (NodeUtil.getRootOfQualifiedName(lvalue).isThis() && + t.getScope() != var.getScope()) { + // Don't look at "this.foo" variables from other scopes. + return; + } + + if (var.getType() != null) { + leftType = var.getType(); + } + } + } + + // Fall through case for arbitrary LHS and arbitrary RHS. + Node rightChild = assign.getLastChild(); + JSType rightType = getJSType(rightChild); + if (validator.expectCanAssignTo( + t, assign, rightType, leftType, "assignment")) { + ensureTyped(t, assign, rightType); + } else { + ensureTyped(t, assign); + } + } + + /** Check that we don't create new properties on structs. */ + private void checkPropCreation(NodeTraversal t, Node lvalue) { + if (lvalue.isGetProp()) { + Node obj = lvalue.getFirstChild(); + Node prop = lvalue.getLastChild(); + JSType objType = getJSType(obj); + String pname = prop.getString(); + if (objType.isStruct() && !objType.hasProperty(pname)) { + if (!(obj.isThis() && + getJSType(t.getScope().getRootNode()).isConstructor())) { + report(t, prop, ILLEGAL_PROPERTY_CREATION); + } + } + } + } + + private void checkPropertyInheritanceOnGetpropAssign( + NodeTraversal t, Node assign, Node object, String property, + JSDocInfo info, JSType propertyType) { + // Inheritance checks for prototype properties. + // + // TODO(nicksantos): This isn't the right place to do this check. We + // really want to do this when we're looking at the constructor. + // We'd find all its properties and make sure they followed inheritance + // rules, like we currently do for @implements to make sure + // all the methods are implemented. + // + // As-is, this misses many other ways to override a property. + // + // object.prototype.property = ...; + if (object.isGetProp()) { + Node object2 = object.getFirstChild(); + String property2 = NodeUtil.getStringValue(object.getLastChild()); + + if ("prototype".equals(property2)) { + JSType jsType = getJSType(object2); + if (jsType.isFunctionType()) { + FunctionType functionType = jsType.toMaybeFunctionType(); + if (functionType.isConstructor() || functionType.isInterface()) { + checkDeclaredPropertyInheritance( + t, assign, functionType, property, info, propertyType); + } + } + } + } + } + + /** + * Visits an object literal field definition key : value. + * + * If the lvalue is a prototype modification, we change the + * schema of the object type it is referring to. + * + * @param t the traversal + * @param key the assign node + */ + private void visitObjLitKey( + NodeTraversal t, Node key, Node objlit, JSType litType) { + // Do not validate object lit value types in externs. We don't really care, + // and it makes it easier to generate externs. + if (objlit.isFromExterns()) { + ensureTyped(t, key); + return; + } + + // Structs must have unquoted keys and dicts must have quoted keys + if (litType.isStruct() && key.isQuotedString()) { + report(t, key, ILLEGAL_OBJLIT_KEY, "struct"); + } else if (litType.isDict() && !key.isQuotedString()) { + report(t, key, ILLEGAL_OBJLIT_KEY, "dict"); + } + + // TODO(johnlenz): Validate get and set function declarations are valid + // as is the functions can have "extraneous" bits. + + // For getter and setter property definitions the + // r-value type != the property type. + Node rvalue = key.getFirstChild(); + JSType rightType = NodeUtil.getObjectLitKeyTypeFromValueType( + key, getJSType(rvalue)); + if (rightType == null) { + rightType = getNativeType(UNKNOWN_TYPE); + } + + Node owner = objlit; + + // Validate value is assignable to the key type. + + JSType keyType = getJSType(key); + + JSType allowedValueType = keyType; + if (allowedValueType.isEnumElementType()) { + allowedValueType = + allowedValueType.toMaybeEnumElementType().getPrimitiveType(); + } + + boolean valid = validator.expectCanAssignToPropertyOf(t, key, + rightType, allowedValueType, + owner, NodeUtil.getObjectLitKeyName(key)); + if (valid) { + ensureTyped(t, key, rightType); + } else { + ensureTyped(t, key); + } + + // Validate that the key type is assignable to the object property type. + // This is necessary as the objlit may have been cast to a non-literal + // object type. + // TODO(johnlenz): consider introducing a CAST node to the AST (or + // perhaps a parentheses node). + + JSType objlitType = getJSType(objlit); + ObjectType type = ObjectType.cast( + objlitType.restrictByNotNullOrUndefined()); + if (type != null) { + String property = NodeUtil.getObjectLitKeyName(key); + if (type.hasProperty(property) && + !type.isPropertyTypeInferred(property) && + !propertyIsImplicitCast(type, property)) { + validator.expectCanAssignToPropertyOf( + t, key, keyType, + type.getPropertyType(property), owner, property); + } + return; + } + } + + /** + * Returns true if any type in the chain has an implicitCast annotation for + * the given property. + */ + private boolean propertyIsImplicitCast(ObjectType type, String prop) { + for (; type != null; type = type.getImplicitPrototype()) { + JSDocInfo docInfo = type.getOwnPropertyJSDocInfo(prop); + if (docInfo != null && docInfo.isImplicitCast()) { + return true; + } + } + return false; + } + + /** + * Given a constructor type and a property name, check that the property has + * the JSDoc annotation @override iff the property is declared on a + * superclass. Several checks regarding inheritance correctness are also + * performed. + */ + private void checkDeclaredPropertyInheritance( + NodeTraversal t, Node n, FunctionType ctorType, String propertyName, + JSDocInfo info, JSType propertyType) { + // If the supertype doesn't resolve correctly, we've warned about this + // already. + if (hasUnknownOrEmptySupertype(ctorType)) { + return; + } + + FunctionType superClass = ctorType.getSuperClassConstructor(); + boolean superClassHasProperty = superClass != null && + superClass.getInstanceType().hasProperty(propertyName); + boolean superClassHasDeclaredProperty = superClass != null && + superClass.getInstanceType().isPropertyTypeDeclared(propertyName); + + // For interface + boolean superInterfaceHasProperty = false; + boolean superInterfaceHasDeclaredProperty = false; + if (ctorType.isInterface()) { + for (ObjectType interfaceType : ctorType.getExtendedInterfaces()) { + superInterfaceHasProperty = + superInterfaceHasProperty || + interfaceType.hasProperty(propertyName); + superInterfaceHasDeclaredProperty = + superInterfaceHasDeclaredProperty || + interfaceType.isPropertyTypeDeclared(propertyName); + } + } + boolean declaredOverride = info != null && info.isOverride(); + + boolean foundInterfaceProperty = false; + if (ctorType.isConstructor()) { + for (JSType implementedInterface : + ctorType.getAllImplementedInterfaces()) { + if (implementedInterface.isUnknownType() || + implementedInterface.isEmptyType()) { + continue; + } + FunctionType interfaceType = + implementedInterface.toObjectType().getConstructor(); + Preconditions.checkNotNull(interfaceType); + + boolean interfaceHasProperty = + interfaceType.getPrototype().hasProperty(propertyName); + foundInterfaceProperty = foundInterfaceProperty || + interfaceHasProperty; + if (reportMissingOverride.isOn() + && !declaredOverride + && interfaceHasProperty) { + // @override not present, but the property does override an interface + // property + compiler.report(t.makeError(n, reportMissingOverride, + HIDDEN_INTERFACE_PROPERTY, propertyName, + interfaceType.getTopMostDefiningType(propertyName).toString())); + } + } + } + + if (!declaredOverride + && !superClassHasProperty + && !superInterfaceHasProperty) { + // nothing to do here, it's just a plain new property + return; + } + + ObjectType topInstanceType = superClassHasDeclaredProperty ? + superClass.getTopMostDefiningType(propertyName) : null; + boolean declaredLocally = + ctorType.isConstructor() && + (ctorType.getPrototype().hasOwnProperty(propertyName) || + ctorType.getInstanceType().hasOwnProperty(propertyName)); + if (reportMissingOverride.isOn() + && !declaredOverride + && superClassHasDeclaredProperty + && declaredLocally) { + // @override not present, but the property does override a superclass + // property + compiler.report(t.makeError(n, reportMissingOverride, + HIDDEN_SUPERCLASS_PROPERTY, propertyName, + topInstanceType.toString())); + } + + // @override is present and we have to check that it is ok + if (superClassHasDeclaredProperty) { + // there is a superclass implementation + JSType superClassPropType = + superClass.getInstanceType().getPropertyType(propertyName); + if (!propertyType.isSubtype(superClassPropType)) { + compiler.report( + t.makeError(n, HIDDEN_SUPERCLASS_PROPERTY_MISMATCH, + propertyName, topInstanceType.toString(), + superClassPropType.toString(), propertyType.toString())); + } + } else if (superInterfaceHasDeclaredProperty) { + // there is an super interface property + for (ObjectType interfaceType : ctorType.getExtendedInterfaces()) { + if (interfaceType.hasProperty(propertyName)) { + JSType superPropertyType = + interfaceType.getPropertyType(propertyName); + if (!propertyType.isSubtype(superPropertyType)) { + topInstanceType = interfaceType.getConstructor(). + getTopMostDefiningType(propertyName); + compiler.report( + t.makeError(n, HIDDEN_SUPERCLASS_PROPERTY_MISMATCH, + propertyName, topInstanceType.toString(), + superPropertyType.toString(), + propertyType.toString())); + } + } + } + } else if (!foundInterfaceProperty + && !superClassHasProperty + && !superInterfaceHasProperty) { + // there is no superclass nor interface implementation + compiler.report( + t.makeError(n, UNKNOWN_OVERRIDE, + propertyName, ctorType.getInstanceType().toString())); + } + } + + /** + * Given a constructor or an interface type, find out whether the unknown + * type is a supertype of the current type. + */ + private static boolean hasUnknownOrEmptySupertype(FunctionType ctor) { + Preconditions.checkArgument(ctor.isConstructor() || ctor.isInterface()); + Preconditions.checkArgument(!ctor.isUnknownType()); + + // The type system should notice inheritance cycles on its own + // and break the cycle. + while (true) { + ObjectType maybeSuperInstanceType = + ctor.getPrototype().getImplicitPrototype(); + if (maybeSuperInstanceType == null) { + return false; + } + if (maybeSuperInstanceType.isUnknownType() || + maybeSuperInstanceType.isEmptyType()) { + return true; + } + ctor = maybeSuperInstanceType.getConstructor(); + if (ctor == null) { + return false; + } + Preconditions.checkState(ctor.isConstructor() || ctor.isInterface()); + } + } + + /** + * Visits an ASSIGN node for cases such as + *
      +   * interface.property2.property = ...;
      +   * 
      + */ + private void visitInterfaceGetprop(NodeTraversal t, Node assign, Node object, + String property, Node lvalue, Node rvalue) { + + JSType rvalueType = getJSType(rvalue); + + // Only 2 values are allowed for methods: + // goog.abstractMethod + // function () {}; + // or for properties, no assignment such as: + // InterfaceFoo.prototype.foobar; + + String abstractMethodName = + compiler.getCodingConvention().getAbstractMethodName(); + if (!rvalueType.isFunctionType()) { + // This is bad i18n style but we don't localize our compiler errors. + String abstractMethodMessage = (abstractMethodName != null) + ? ", or " + abstractMethodName + : ""; + compiler.report( + t.makeError(object, INVALID_INTERFACE_MEMBER_DECLARATION, + abstractMethodMessage)); + } + + if (assign.getLastChild().isFunction() + && !NodeUtil.isEmptyBlock(assign.getLastChild().getLastChild())) { + compiler.report( + t.makeError(object, INTERFACE_FUNCTION_NOT_EMPTY, + abstractMethodName)); + } + } + + /** + * Visits a NAME node. + * + * @param t The node traversal object that supplies context, such as the + * scope chain to use in name lookups as well as error reporting. + * @param n The node being visited. + * @param parent The parent of the node n. + * @return whether the node is typeable or not + */ + boolean visitName(NodeTraversal t, Node n, Node parent) { + // At this stage, we need to determine whether this is a leaf + // node in an expression (which therefore needs to have a type + // assigned for it) versus some other decorative node that we + // can safely ignore. Function names, arguments (children of LP nodes) and + // variable declarations are ignored. + // TODO(user): remove this short-circuiting in favor of a + // pre order traversal of the FUNCTION, CATCH, LP and VAR nodes. + int parentNodeType = parent.getType(); + if (parentNodeType == Token.FUNCTION || + parentNodeType == Token.CATCH || + parentNodeType == Token.PARAM_LIST || + parentNodeType == Token.VAR) { + return false; + } + + JSType type = n.getJSType(); + if (type == null) { + type = getNativeType(UNKNOWN_TYPE); + Var var = t.getScope().getVar(n.getString()); + if (var != null) { + JSType varType = var.getType(); + if (varType != null) { + type = varType; + } + } + } + ensureTyped(t, n, type); + return true; + } + + /** + * Visits a GETPROP node. + * + * @param t The node traversal object that supplies context, such as the + * scope chain to use in name lookups as well as error reporting. + * @param n The node being visited. + * @param parent The parent of n + */ + private void visitGetProp(NodeTraversal t, Node n, Node parent) { + // obj.prop or obj.method() + // Lots of types can appear on the left, a call to a void function can + // never be on the left. getPropertyType will decide what is acceptable + // and what isn't. + Node property = n.getLastChild(); + Node objNode = n.getFirstChild(); + JSType childType = getJSType(objNode); + + if (childType.isDict()) { + report(t, property, TypeValidator.ILLEGAL_PROPERTY_ACCESS, "'.'", "dict"); + } else if (validator.expectNotNullOrUndefined(t, n, childType, + "No properties on this expression", getNativeType(OBJECT_TYPE))) { + checkPropertyAccess(childType, property.getString(), t, n); + } + ensureTyped(t, n); + } + + /** + * Emit a warning if we can prove that a property cannot possibly be + * defined on an object. Note the difference between JS and a strictly + * statically typed language: we're checking if the property + * *cannot be defined*, whereas a java compiler would check if the + * property *can be undefined*. + */ + private void checkPropertyAccess(JSType childType, String propName, + NodeTraversal t, Node n) { + // If the property type is unknown, check the object type to see if it + // can ever be defined. We explicitly exclude CHECKED_UNKNOWN (for + // properties where we've checked that it exists, or for properties on + // objects that aren't in this binary). + JSType propType = getJSType(n); + if (propType.isEquivalentTo(typeRegistry.getNativeType(UNKNOWN_TYPE))) { + childType = childType.autobox(); + ObjectType objectType = ObjectType.cast(childType); + if (objectType != null) { + // We special-case object types so that checks on enums can be + // much stricter, and so that we can use hasProperty (which is much + // faster in most cases). + if (!objectType.hasProperty(propName) || + objectType.isEquivalentTo( + typeRegistry.getNativeType(UNKNOWN_TYPE))) { + if (objectType instanceof EnumType) { + report(t, n, INEXISTENT_ENUM_ELEMENT, propName); + } else { + checkPropertyAccessHelper(objectType, propName, t, n); + } + } + + } else { + checkPropertyAccessHelper(childType, propName, t, n); + } + } + } + + private void checkPropertyAccessHelper(JSType objectType, String propName, + NodeTraversal t, Node n) { + if (!objectType.isEmptyType() && + reportMissingProperties && !isPropertyTest(n)) { + if (!typeRegistry.canPropertyBeDefined(objectType, propName)) { + report(t, n, INEXISTENT_PROPERTY, propName, + validator.getReadableJSTypeName(n.getFirstChild(), true)); + } + } + } + + /** + * Determines whether this node is testing for the existence of a property. + * If true, we will not emit warnings about a missing property. + * + * @param getProp The GETPROP being tested. + */ + private boolean isPropertyTest(Node getProp) { + Node parent = getProp.getParent(); + switch (parent.getType()) { + case Token.CALL: + return parent.getFirstChild() != getProp && + compiler.getCodingConvention().isPropertyTestFunction(parent); + + case Token.IF: + case Token.WHILE: + case Token.DO: + case Token.FOR: + return NodeUtil.getConditionExpression(parent) == getProp; + + case Token.INSTANCEOF: + case Token.TYPEOF: + return true; + + case Token.AND: + case Token.HOOK: + return parent.getFirstChild() == getProp; + + case Token.NOT: + return parent.getParent().isOr() && + parent.getParent().getFirstChild() == parent; + } + return false; + } + + /** + * Visits a GETELEM node. + * + * @param t The node traversal object that supplies context, such as the + * scope chain to use in name lookups as well as error reporting. + * @param n The node being visited. + */ + private void visitGetElem(NodeTraversal t, Node n) { + validator.expectIndexMatch( + t, n, getJSType(n.getFirstChild()), getJSType(n.getLastChild())); + ensureTyped(t, n); + } + + /** + * Visits a VAR node. + * + * @param t The node traversal object that supplies context, such as the + * scope chain to use in name lookups as well as error reporting. + * @param n The node being visited. + */ + private void visitVar(NodeTraversal t, Node n) { + // TODO(nicksantos): Fix this so that the doc info always shows up + // on the NAME node. We probably want to wait for the parser + // merge to fix this. + JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null; + for (Node name : n.children()) { + Node value = name.getFirstChild(); + // A null var would indicate a bug in the scope creation logic. + Var var = t.getScope().getVar(name.getString()); + + if (value != null) { + JSType valueType = getJSType(value); + JSType nameType = var.getType(); + nameType = (nameType == null) ? getNativeType(UNKNOWN_TYPE) : nameType; + + JSDocInfo info = name.getJSDocInfo(); + if (info == null) { + info = varInfo; + } + + checkEnumAlias(t, info, value); + if (var.isTypeInferred()) { + ensureTyped(t, name, valueType); + } else { + validator.expectCanAssignTo( + t, value, valueType, nameType, "initializing variable"); + } + } + } + } + + /** + * Visits a NEW node. + */ + private void visitNew(NodeTraversal t, Node n) { + Node constructor = n.getFirstChild(); + JSType type = getJSType(constructor).restrictByNotNullOrUndefined(); + if (type.isConstructor() || type.isEmptyType() || type.isUnknownType()) { + FunctionType fnType = type.toMaybeFunctionType(); + if (fnType != null) { + visitParameterList(t, n, fnType); + ensureTyped(t, n, fnType.getInstanceType()); + } else { + ensureTyped(t, n); + } + } else { + report(t, n, NOT_A_CONSTRUCTOR); + ensureTyped(t, n); + } + } + + /** + * Check whether there's any property conflict for for a particular super + * interface + * @param t The node traversal object that supplies context + * @param n The node being visited + * @param functionName The function name being checked + * @param properties The property names in the super interfaces that have + * been visited + * @param currentProperties The property names in the super interface + * that have been visited + * @param interfaceType The super interface that is being visited + */ + private void checkInterfaceConflictProperties(NodeTraversal t, Node n, + String functionName, HashMap properties, + HashMap currentProperties, + ObjectType interfaceType) { + ObjectType implicitProto = interfaceType.getImplicitPrototype(); + Set currentPropertyNames; + if (implicitProto == null) { + // This can be the case if interfaceType is proxy to a non-existent + // object (which is a bad type annotation, but shouldn't crash). + currentPropertyNames = ImmutableSet.of(); + } else { + currentPropertyNames = implicitProto.getOwnPropertyNames(); + } + for (String name : currentPropertyNames) { + ObjectType oType = properties.get(name); + if (oType != null) { + if (!interfaceType.getPropertyType(name).isEquivalentTo( + oType.getPropertyType(name))) { + compiler.report( + t.makeError(n, INCOMPATIBLE_EXTENDED_PROPERTY_TYPE, + functionName, name, oType.toString(), + interfaceType.toString())); + } + } + currentProperties.put(name, interfaceType); + } + for (ObjectType iType : interfaceType.getCtorExtendedInterfaces()) { + checkInterfaceConflictProperties(t, n, functionName, properties, + currentProperties, iType); + } + } + + /** + * Visits a {@link Token#FUNCTION} node. + * + * @param t The node traversal object that supplies context, such as the + * scope chain to use in name lookups as well as error reporting. + * @param n The node being visited. + */ + private void visitFunction(NodeTraversal t, Node n) { + FunctionType functionType = JSType.toMaybeFunctionType(n.getJSType()); + String functionPrivateName = n.getFirstChild().getString(); + if (functionType.isConstructor()) { + FunctionType baseConstructor = functionType.getSuperClassConstructor(); + if (baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE) && + baseConstructor != null && + baseConstructor.isInterface()) { + compiler.report( + t.makeError(n, CONFLICTING_EXTENDED_TYPE, + "constructor", functionPrivateName)); + } else { + if (baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE)) { + ObjectType proto = functionType.getPrototype(); + if (functionType.makesStructs() && !proto.isStruct()) { + compiler.report(t.makeError(n, CONFLICTING_EXTENDED_TYPE, + "struct", functionPrivateName)); + } else if (functionType.makesDicts() && !proto.isDict()) { + compiler.report(t.makeError(n, CONFLICTING_EXTENDED_TYPE, + "dict", functionPrivateName)); + } + } + // All interfaces are properly implemented by a class + for (JSType baseInterface : functionType.getImplementedInterfaces()) { + boolean badImplementedType = false; + ObjectType baseInterfaceObj = ObjectType.cast(baseInterface); + if (baseInterfaceObj != null) { + FunctionType interfaceConstructor = + baseInterfaceObj.getConstructor(); + if (interfaceConstructor != null && + !interfaceConstructor.isInterface()) { + badImplementedType = true; + } + } else { + badImplementedType = true; + } + if (badImplementedType) { + report(t, n, BAD_IMPLEMENTED_TYPE, functionPrivateName); + } + } + // check properties + validator.expectAllInterfaceProperties(t, n, functionType); + } + } else if (functionType.isInterface()) { + // Interface must extend only interfaces + for (ObjectType extInterface : functionType.getExtendedInterfaces()) { + if (extInterface.getConstructor() != null + && !extInterface.getConstructor().isInterface()) { + compiler.report( + t.makeError(n, CONFLICTING_EXTENDED_TYPE, + "interface", functionPrivateName)); + } + } + + // Check whether the extended interfaces have any conflicts + if (functionType.getExtendedInterfacesCount() > 1) { + // Only check when extending more than one interfaces + HashMap properties + = new HashMap(); + HashMap currentProperties + = new HashMap(); + for (ObjectType interfaceType : functionType.getExtendedInterfaces()) { + currentProperties.clear(); + checkInterfaceConflictProperties(t, n, functionPrivateName, + properties, currentProperties, interfaceType); + properties.putAll(currentProperties); + } + } + } + } + + /** + * Visits a CALL node. + * + * @param t The node traversal object that supplies context, such as the + * scope chain to use in name lookups as well as error reporting. + * @param n The node being visited. + */ + private void visitCall(NodeTraversal t, Node n) { + Node child = n.getFirstChild(); + JSType childType = getJSType(child).restrictByNotNullOrUndefined(); + + if (!childType.canBeCalled()) { + report(t, n, NOT_CALLABLE, childType.toString()); + ensureTyped(t, n); + return; + } + + // A couple of types can be called as if they were functions. + // If it is a function type, then validate parameters. + if (childType.isFunctionType()) { + FunctionType functionType = childType.toMaybeFunctionType(); + + boolean isExtern = false; + JSDocInfo functionJSDocInfo = functionType.getJSDocInfo(); + if( functionJSDocInfo != null && + functionJSDocInfo.getAssociatedNode() != null) { + isExtern = functionJSDocInfo.getAssociatedNode().isFromExterns(); + } + + // Non-native constructors should not be called directly + // unless they specify a return type and are defined + // in an extern. + if (functionType.isConstructor() && + !functionType.isNativeObjectType() && + (functionType.getReturnType().isUnknownType() || + functionType.getReturnType().isVoidType() || + !isExtern)) { + report(t, n, CONSTRUCTOR_NOT_CALLABLE, childType.toString()); + } + + // Functions with explicit 'this' types must be called in a GETPROP + // or GETELEM. + if (functionType.isOrdinaryFunction() && + !functionType.getTypeOfThis().isUnknownType() && + !(functionType.getTypeOfThis().toObjectType() != null && + functionType.getTypeOfThis().toObjectType().isNativeObjectType()) && + !(child.isGetElem() || + child.isGetProp())) { + report(t, n, EXPECTED_THIS_TYPE, functionType.toString()); + } + + visitParameterList(t, n, functionType); + ensureTyped(t, n, functionType.getReturnType()); + } else { + ensureTyped(t, n); + } + + // TODO: Add something to check for calls of RegExp objects, which is not + // supported by IE. Either say something about the return type or warn + // about the non-portability of the call or both. + } + + /** + * Visits the parameters of a CALL or a NEW node. + */ + private void visitParameterList(NodeTraversal t, Node call, + FunctionType functionType) { + Iterator arguments = call.children().iterator(); + arguments.next(); // skip the function name + + Iterator parameters = functionType.getParameters().iterator(); + int ordinal = 0; + Node parameter = null; + Node argument = null; + while (arguments.hasNext() && + (parameters.hasNext() || + parameter != null && parameter.isVarArgs())) { + // If there are no parameters left in the list, then the while loop + // above implies that this must be a var_args function. + if (parameters.hasNext()) { + parameter = parameters.next(); + } + argument = arguments.next(); + ordinal++; + + validator.expectArgumentMatchesParameter(t, argument, + getJSType(argument), getJSType(parameter), call, ordinal); + } + + int numArgs = call.getChildCount() - 1; + int minArgs = functionType.getMinArguments(); + int maxArgs = functionType.getMaxArguments(); + if (minArgs > numArgs || maxArgs < numArgs) { + report(t, call, WRONG_ARGUMENT_COUNT, + validator.getReadableJSTypeName(call.getFirstChild(), false), + String.valueOf(numArgs), String.valueOf(minArgs), + maxArgs != Integer.MAX_VALUE ? + " and no more than " + maxArgs + " argument(s)" : ""); + } + } + + /** + * Visits a RETURN node. + * + * @param t The node traversal object that supplies context, such as the + * scope chain to use in name lookups as well as error reporting. + * @param n The node being visited. + */ + private void visitReturn(NodeTraversal t, Node n) { + JSType jsType = getJSType(t.getEnclosingFunction()); + + if (jsType.isFunctionType()) { + FunctionType functionType = jsType.toMaybeFunctionType(); + + JSType returnType = functionType.getReturnType(); + + // if no return type is specified, undefined must be returned + // (it's a void function) + if (returnType == null) { + returnType = getNativeType(VOID_TYPE); + } + + // fetching the returned value's type + Node valueNode = n.getFirstChild(); + JSType actualReturnType; + if (valueNode == null) { + actualReturnType = getNativeType(VOID_TYPE); + valueNode = n; + } else { + actualReturnType = getJSType(valueNode); + } + + // verifying + validator.expectCanAssignTo(t, valueNode, actualReturnType, returnType, + "inconsistent return type"); + } + } + + /** + * This function unifies the type checking involved in the core binary + * operators and the corresponding assignment operators. The representation + * used internally is such that common code can handle both kinds of + * operators easily. + * + * @param op The operator. + * @param t The traversal object, needed to report errors. + * @param n The node being checked. + */ + private void visitBinaryOperator(int op, NodeTraversal t, Node n) { + Node left = n.getFirstChild(); + JSType leftType = getJSType(left); + Node right = n.getLastChild(); + JSType rightType = getJSType(right); + switch (op) { + case Token.ASSIGN_LSH: + case Token.ASSIGN_RSH: + case Token.LSH: + case Token.RSH: + case Token.ASSIGN_URSH: + case Token.URSH: + if (!leftType.matchesInt32Context()) { + report(t, left, BIT_OPERATION, + NodeUtil.opToStr(n.getType()), leftType.toString()); + } + if (!rightType.matchesUint32Context()) { + report(t, right, BIT_OPERATION, + NodeUtil.opToStr(n.getType()), rightType.toString()); + } + break; + + case Token.ASSIGN_DIV: + case Token.ASSIGN_MOD: + case Token.ASSIGN_MUL: + case Token.ASSIGN_SUB: + case Token.DIV: + case Token.MOD: + case Token.MUL: + case Token.SUB: + validator.expectNumber(t, left, leftType, "left operand"); + validator.expectNumber(t, right, rightType, "right operand"); + break; + + case Token.ASSIGN_BITAND: + case Token.ASSIGN_BITXOR: + case Token.ASSIGN_BITOR: + case Token.BITAND: + case Token.BITXOR: + case Token.BITOR: + validator.expectBitwiseable(t, left, leftType, + "bad left operand to bitwise operator"); + validator.expectBitwiseable(t, right, rightType, + "bad right operand to bitwise operator"); + break; + + case Token.ASSIGN_ADD: + case Token.ADD: + break; + + default: + report(t, n, UNEXPECTED_TOKEN, Token.name(op)); + } + ensureTyped(t, n); + } + + + /** + *

      Checks enum aliases. + * + *

      We verify that the enum element type of the enum used + * for initialization is a subtype of the enum element type of + * the enum the value is being copied in.

      + * + *

      Example:

      + *
      var myEnum = myOtherEnum;
      + * + *

      Enum aliases are irregular, so we need special code for this :(

      + * + * @param value the value used for initialization of the enum + */ + private void checkEnumAlias( + NodeTraversal t, JSDocInfo declInfo, Node value) { + if (declInfo == null || !declInfo.hasEnumParameterType()) { + return; + } + + JSType valueType = getJSType(value); + if (!valueType.isEnumType()) { + return; + } + + EnumType valueEnumType = valueType.toMaybeEnumType(); + JSType valueEnumPrimitiveType = + valueEnumType.getElementsType().getPrimitiveType(); + validator.expectCanAssignTo(t, value, valueEnumPrimitiveType, + declInfo.getEnumParameterType().evaluate(t.getScope(), typeRegistry), + "incompatible enum element types"); + } + + /** + * This method gets the JSType from the Node argument and verifies that it is + * present. + */ + private JSType getJSType(Node n) { + JSType jsType = n.getJSType(); + if (jsType == null) { + // TODO(nicksantos): This branch indicates a compiler bug, not worthy of + // halting the compilation but we should log this and analyze to track + // down why it happens. This is not critical and will be resolved over + // time as the type checker is extended. + return getNativeType(UNKNOWN_TYPE); + } else { + return jsType; + } + } + + // TODO(nicksantos): TypeCheck should never be attaching types to nodes. + // All types should be attached by TypeInference. This is not true today + // for legacy reasons. There are a number of places where TypeInference + // doesn't attach a type, as a signal to TypeCheck that it needs to check + // that node's type. + + /** + * Ensure that the given node has a type. If it does not have one, + * attach the UNKNOWN_TYPE. + */ + private void ensureTyped(NodeTraversal t, Node n) { + ensureTyped(t, n, getNativeType(UNKNOWN_TYPE)); + } + + private void ensureTyped(NodeTraversal t, Node n, JSTypeNative type) { + ensureTyped(t, n, getNativeType(type)); + } + + /** + * Enforces type casts, and ensures the node is typed. + * + * A cast in the way that we use it in JSDoc annotations never + * alters the generated code and therefore never can induce any runtime + * operation. What this means is that a 'cast' is really just a compile + * time constraint on the underlying value. In the future, we may add + * support for run-time casts for compiled tests. + * + * To ensure some shred of sanity, we enforce the notion that the + * type you are casting to may only meaningfully be a narrower type + * than the underlying declared type. We also invalidate optimizations + * on bad type casts. + * + * @param t The traversal object needed to report errors. + * @param n The node getting a type assigned to it. + * @param type The type to be assigned. + */ + private void ensureTyped(NodeTraversal t, Node n, JSType type) { + // Make sure FUNCTION nodes always get function type. + Preconditions.checkState(!n.isFunction() || + type.isFunctionType() || + type.isUnknownType()); + JSDocInfo info = n.getJSDocInfo(); + if (info != null) { + if (info.hasType()) { + // TODO(johnlenz): Change this so that we only look for casts on CAST + // nodes one the misplaced type annotation warning is on by default and + // people have been given a chance to fix them. As is, this is here + // simply for legacy casts. + JSType infoType = info.getType().evaluate(t.getScope(), typeRegistry); + validator.expectCanCast(t, n, infoType, type); + type = infoType; + } + + if (info.isImplicitCast() && !inExterns) { + String propName = n.isGetProp() ? + n.getLastChild().getString() : "(missing)"; + compiler.report( + t.makeError(n, ILLEGAL_IMPLICIT_CAST, propName)); + } + } + + if (n.getJSType() == null) { + n.setJSType(type); + } + } + + /** + * Returns the percentage of nodes typed by the type checker. + * @return a number between 0.0 and 100.0 + */ + double getTypedPercent() { + int total = nullCount + unknownCount + typedCount; + return (total == 0) ? 0.0 : (100.0 * typedCount) / total; + } + + private JSType getNativeType(JSTypeNative typeId) { + return typeRegistry.getNativeType(typeId); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypeInference.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypeInference.java new file mode 100644 index 0000000..3dc8ec6 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypeInference.java @@ -0,0 +1,1580 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import static com.google.javascript.rhino.jstype.JSTypeNative.ARRAY_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_OBJECT_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.CHECKED_UNKNOWN_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NULL_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_VALUE_OR_OBJECT_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec; +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; +import com.google.javascript.jscomp.type.FlowScope; +import com.google.javascript.jscomp.type.ReverseAbstractInterpreter; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.BooleanLiteralSet; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ModificationVisitor; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.ParameterizedType; +import com.google.javascript.rhino.jstype.StaticSlot; +import com.google.javascript.rhino.jstype.TemplateType; +import com.google.javascript.rhino.jstype.UnionType; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Type inference within a script node or a function body, using the data-flow + * analysis framework. + * + */ +class TypeInference + extends DataFlowAnalysis.BranchedForwardDataFlowAnalysis { + + // TODO(johnlenz): We no longer make this check, but we should. + static final DiagnosticType FUNCTION_LITERAL_UNDEFINED_THIS = + DiagnosticType.warning( + "JSC_FUNCTION_LITERAL_UNDEFINED_THIS", + "Function literal argument refers to undefined this argument"); + + private final AbstractCompiler compiler; + private final JSTypeRegistry registry; + private final ReverseAbstractInterpreter reverseInterpreter; + private final Scope syntacticScope; + private final FlowScope functionScope; + private final FlowScope bottomScope; + private final Map assertionFunctionsMap; + + // For convenience + private final ObjectType unknownType; + + TypeInference(AbstractCompiler compiler, ControlFlowGraph cfg, + ReverseAbstractInterpreter reverseInterpreter, + Scope functionScope, + Map assertionFunctionsMap) { + super(cfg, new LinkedFlowScope.FlowScopeJoinOp()); + this.compiler = compiler; + this.registry = compiler.getTypeRegistry(); + this.reverseInterpreter = reverseInterpreter; + this.unknownType = registry.getNativeObjectType(UNKNOWN_TYPE); + + this.syntacticScope = functionScope; + inferArguments(functionScope); + + this.functionScope = LinkedFlowScope.createEntryLattice(functionScope); + this.assertionFunctionsMap = assertionFunctionsMap; + + // For each local variable declared with the VAR keyword, the entry + // type is VOID. + Iterator varIt = + functionScope.getDeclarativelyUnboundVarsWithoutTypes(); + while (varIt.hasNext()) { + Var var = varIt.next(); + if (isUnflowable(var)) { + continue; + } + + this.functionScope.inferSlotType( + var.getName(), getNativeType(VOID_TYPE)); + } + + this.bottomScope = LinkedFlowScope.createEntryLattice( + Scope.createLatticeBottom(functionScope.getRootNode())); + } + + /** + * Infers all of a function's arguments if their types aren't declared. + */ + private void inferArguments(Scope functionScope) { + Node functionNode = functionScope.getRootNode(); + Node astParameters = functionNode.getFirstChild().getNext(); + Node iifeArgumentNode = null; + + if (NodeUtil.isCallOrNewTarget(functionNode)) { + iifeArgumentNode = functionNode.getNext(); + } + + FunctionType functionType = + JSType.toMaybeFunctionType(functionNode.getJSType()); + if (functionType != null) { + Node parameterTypes = functionType.getParametersNode(); + if (parameterTypes != null) { + Node parameterTypeNode = parameterTypes.getFirstChild(); + for (Node astParameter : astParameters.children()) { + Var var = functionScope.getVar(astParameter.getString()); + Preconditions.checkNotNull(var); + if (var.isTypeInferred() && + var.getType() == unknownType) { + JSType newType = null; + + if (iifeArgumentNode != null) { + newType = iifeArgumentNode.getJSType(); + } else if (parameterTypeNode != null) { + newType = parameterTypeNode.getJSType(); + } + + if (newType != null) { + var.setType(newType); + astParameter.setJSType(newType); + } + } + + if (parameterTypeNode != null) { + parameterTypeNode = parameterTypeNode.getNext(); + } + if (iifeArgumentNode != null) { + iifeArgumentNode = iifeArgumentNode.getNext(); + } + } + } + } + } + + @Override + FlowScope createInitialEstimateLattice() { + return bottomScope; + } + + @Override + FlowScope createEntryLattice() { + return functionScope; + } + + @Override + FlowScope flowThrough(Node n, FlowScope input) { + // If we have not walked a path from to , then we don't + // want to infer anything about this scope. + if (input == bottomScope) { + return input; + } + + FlowScope output = input.createChildFlowScope(); + output = traverse(n, output); + return output; + } + + @Override + @SuppressWarnings({"fallthrough", "incomplete-switch"}) + List branchedFlowThrough(Node source, FlowScope input) { + // NOTE(nicksantos): Right now, we just treat ON_EX edges like UNCOND + // edges. If we wanted to be perfect, we'd actually JOIN all the out + // lattices of this flow with the in lattice, and then make that the out + // lattice for the ON_EX edge. But it's probably to expensive to be + // worthwhile. + FlowScope output = flowThrough(source, input); + Node condition = null; + FlowScope conditionFlowScope = null; + BooleanOutcomePair conditionOutcomes = null; + + List> branchEdges = getCfg().getOutEdges(source); + List result = Lists.newArrayListWithCapacity(branchEdges.size()); + for (DiGraphEdge branchEdge : branchEdges) { + Branch branch = branchEdge.getValue(); + FlowScope newScope = output; + + switch (branch) { + case ON_TRUE: + if (NodeUtil.isForIn(source)) { + // item is assigned a property name, so its type should be string. + Node item = source.getFirstChild(); + Node obj = item.getNext(); + + FlowScope informed = traverse(obj, output.createChildFlowScope()); + + if (item.isVar()) { + item = item.getFirstChild(); + } + if (item.isName()) { + JSType iterKeyType = getNativeType(STRING_TYPE); + ObjectType objType = getJSType(obj).dereference(); + JSType objIndexType = objType == null ? + null : objType.getIndexType(); + if (objIndexType != null && !objIndexType.isUnknownType()) { + JSType narrowedKeyType = + iterKeyType.getGreatestSubtype(objIndexType); + if (!narrowedKeyType.isEmptyType()) { + iterKeyType = narrowedKeyType; + } + } + redeclareSimpleVar(informed, item, iterKeyType); + } + newScope = informed; + break; + } + + // FALL THROUGH + + case ON_FALSE: + if (condition == null) { + condition = NodeUtil.getConditionExpression(source); + if (condition == null && source.isCase()) { + condition = source; + + // conditionFlowScope is cached from previous iterations + // of the loop. + if (conditionFlowScope == null) { + conditionFlowScope = traverse( + condition.getFirstChild(), output.createChildFlowScope()); + } + } + } + + if (condition != null) { + if (condition.isAnd() || + condition.isOr()) { + // When handling the short-circuiting binary operators, + // the outcome scope on true can be different than the outcome + // scope on false. + // + // TODO(nicksantos): The "right" way to do this is to + // carry the known outcome all the way through the + // recursive traversal, so that we can construct a + // different flow scope based on the outcome. However, + // this would require a bunch of code and a bunch of + // extra computation for an edge case. This seems to be + // a "good enough" approximation. + + // conditionOutcomes is cached from previous iterations + // of the loop. + if (conditionOutcomes == null) { + conditionOutcomes = condition.isAnd() ? + traverseAnd(condition, output.createChildFlowScope()) : + traverseOr(condition, output.createChildFlowScope()); + } + newScope = + reverseInterpreter.getPreciserScopeKnowingConditionOutcome( + condition, + conditionOutcomes.getOutcomeFlowScope( + condition.getType(), branch == Branch.ON_TRUE), + branch == Branch.ON_TRUE); + } else { + // conditionFlowScope is cached from previous iterations + // of the loop. + if (conditionFlowScope == null) { + conditionFlowScope = + traverse(condition, output.createChildFlowScope()); + } + newScope = + reverseInterpreter.getPreciserScopeKnowingConditionOutcome( + condition, conditionFlowScope, branch == Branch.ON_TRUE); + } + } + break; + } + + result.add(newScope.optimize()); + } + return result; + } + + private FlowScope traverse(Node n, FlowScope scope) { + switch (n.getType()) { + case Token.ASSIGN: + scope = traverseAssign(n, scope); + break; + + case Token.NAME: + scope = traverseName(n, scope); + break; + + case Token.GETPROP: + scope = traverseGetProp(n, scope); + break; + + case Token.AND: + scope = traverseAnd(n, scope).getJoinedFlowScope() + .createChildFlowScope(); + break; + + case Token.OR: + scope = traverseOr(n, scope).getJoinedFlowScope() + .createChildFlowScope(); + break; + + case Token.HOOK: + scope = traverseHook(n, scope); + break; + + case Token.OBJECTLIT: + scope = traverseObjectLiteral(n, scope); + break; + + case Token.CALL: + scope = traverseCall(n, scope); + break; + + case Token.NEW: + scope = traverseNew(n, scope); + break; + + case Token.ASSIGN_ADD: + case Token.ADD: + scope = traverseAdd(n, scope); + break; + + case Token.POS: + case Token.NEG: + scope = traverse(n.getFirstChild(), scope); // Find types. + n.setJSType(getNativeType(NUMBER_TYPE)); + break; + + case Token.ARRAYLIT: + scope = traverseArrayLiteral(n, scope); + break; + + case Token.THIS: + n.setJSType(scope.getTypeOfThis()); + break; + + case Token.ASSIGN_LSH: + case Token.ASSIGN_RSH: + case Token.LSH: + case Token.RSH: + case Token.ASSIGN_URSH: + case Token.URSH: + case Token.ASSIGN_DIV: + case Token.ASSIGN_MOD: + case Token.ASSIGN_BITAND: + case Token.ASSIGN_BITXOR: + case Token.ASSIGN_BITOR: + case Token.ASSIGN_MUL: + case Token.ASSIGN_SUB: + case Token.DIV: + case Token.MOD: + case Token.BITAND: + case Token.BITXOR: + case Token.BITOR: + case Token.MUL: + case Token.SUB: + case Token.DEC: + case Token.INC: + case Token.BITNOT: + scope = traverseChildren(n, scope); + n.setJSType(getNativeType(NUMBER_TYPE)); + break; + + case Token.PARAM_LIST: + scope = traverse(n.getFirstChild(), scope); + n.setJSType(getJSType(n.getFirstChild())); + break; + + case Token.COMMA: + scope = traverseChildren(n, scope); + n.setJSType(getJSType(n.getLastChild())); + break; + + case Token.TYPEOF: + scope = traverseChildren(n, scope); + n.setJSType(getNativeType(STRING_TYPE)); + break; + + case Token.DELPROP: + case Token.LT: + case Token.LE: + case Token.GT: + case Token.GE: + case Token.NOT: + case Token.EQ: + case Token.NE: + case Token.SHEQ: + case Token.SHNE: + case Token.INSTANCEOF: + case Token.IN: + scope = traverseChildren(n, scope); + n.setJSType(getNativeType(BOOLEAN_TYPE)); + break; + + case Token.GETELEM: + scope = traverseGetElem(n, scope); + break; + + case Token.EXPR_RESULT: + scope = traverseChildren(n, scope); + if (n.getFirstChild().isGetProp()) { + ensurePropertyDeclared(n.getFirstChild()); + } + break; + + case Token.SWITCH: + scope = traverse(n.getFirstChild(), scope); + break; + + case Token.RETURN: + scope = traverseReturn(n, scope); + break; + + case Token.VAR: + case Token.THROW: + scope = traverseChildren(n, scope); + break; + + case Token.CATCH: + scope = traverseCatch(n, scope); + break; + + case Token.CAST: + scope = traverseChildren(n, scope); + break; + } + + // TODO(johnlenz): remove this after the CAST node change has shaken out. + if (!n.isFunction()) { + JSDocInfo info = n.getJSDocInfo(); + if (info != null && info.hasType()) { + JSType castType = info.getType().evaluate(syntacticScope, registry); + + // A stubbed type declaration on a qualified name should take + // effect for all subsequent accesses of that name, + // so treat it the same as an assign to that name. + if (n.isQualifiedName() && + n.getParent().isExprResult()) { + updateScopeForTypeChange(scope, n, n.getJSType(), castType); + } + + n.setJSType(castType); + } + } + + return scope; + } + + /** + * Traverse a return value. + */ + private FlowScope traverseReturn(Node n, FlowScope scope) { + scope = traverseChildren(n, scope); + + Node retValue = n.getFirstChild(); + if (retValue != null) { + JSType type = functionScope.getRootNode().getJSType(); + if (type != null) { + FunctionType fnType = type.toMaybeFunctionType(); + if (fnType != null) { + inferPropertyTypesToMatchConstraint( + retValue.getJSType(), fnType.getReturnType()); + } + } + } + return scope; + } + + /** + * Any value can be thrown, so it's really impossible to determine the type + * of a CATCH param. Treat it as the UNKNOWN type. + */ + private FlowScope traverseCatch(Node catchNode, FlowScope scope) { + Node name = catchNode.getFirstChild(); + String varName = name.getString(); + JSType type; + // If the catch expression name was declared in the catch use that type, + // otherwise use "unknown". + JSDocInfo info = name.getJSDocInfo(); + if (info != null && info.hasType()) { + type = info.getType().evaluate(syntacticScope, registry); + } else { + type = getNativeType(JSTypeNative.UNKNOWN_TYPE); + } + redeclareSimpleVar(scope, name, type); + name.setJSType(type); + return scope; + } + + private FlowScope traverseAssign(Node n, FlowScope scope) { + Node left = n.getFirstChild(); + Node right = n.getLastChild(); + scope = traverseChildren(n, scope); + + JSType leftType = left.getJSType(); + JSType rightType = getJSType(right); + n.setJSType(rightType); + + updateScopeForTypeChange(scope, left, leftType, rightType); + return scope; + } + + /** + * Updates the scope according to the result of a type change, like + * an assignment or a type cast. + */ + private void updateScopeForTypeChange( + FlowScope scope, Node left, JSType leftType, JSType resultType) { + Preconditions.checkNotNull(resultType); + switch (left.getType()) { + case Token.NAME: + String varName = left.getString(); + Var var = syntacticScope.getVar(varName); + + // When looking at VAR initializers for declared VARs, we trust + // the declared type over the type it's being initialized to. + // This has two purposes: + // 1) We avoid re-declaring declared variables so that built-in + // types defined in externs are not redeclared. + // 2) When there's a lexical closure like + // /** @type {?string} */ var x = null; + // function f() { x = 'xyz'; } + // the inference will ignore the lexical closure, + // which is just wrong. This bug needs to be fixed eventually. + boolean isVarDeclaration = left.hasChildren(); + if (!isVarDeclaration || var == null || var.isTypeInferred()) { + redeclareSimpleVar(scope, left, resultType); + } + left.setJSType(isVarDeclaration || leftType == null ? + resultType : null); + + if (var != null && var.isTypeInferred()) { + JSType oldType = var.getType(); + var.setType(oldType == null ? + resultType : oldType.getLeastSupertype(resultType)); + } + break; + case Token.GETPROP: + String qualifiedName = left.getQualifiedName(); + if (qualifiedName != null) { + scope.inferQualifiedSlot(left, qualifiedName, + leftType == null ? unknownType : leftType, + resultType); + } + + left.setJSType(resultType); + ensurePropertyDefined(left, resultType); + break; + } + } + + /** + * Defines a property if the property has not been defined yet. + */ + private void ensurePropertyDefined(Node getprop, JSType rightType) { + String propName = getprop.getLastChild().getString(); + Node obj = getprop.getFirstChild(); + JSType nodeType = getJSType(obj); + ObjectType objectType = ObjectType.cast( + nodeType.restrictByNotNullOrUndefined()); + if (objectType == null) { + registry.registerPropertyOnType(propName, nodeType); + } else { + // Don't add the property to @struct objects outside a constructor + if (nodeType.isStruct() && !objectType.hasProperty(propName)) { + if (!(obj.isThis() && + getJSType(syntacticScope.getRootNode()).isConstructor())) { + return; + } + } + + if (ensurePropertyDeclaredHelper(getprop, objectType)) { + return; + } + + if (!objectType.isPropertyTypeDeclared(propName)) { + // We do not want a "stray" assign to define an inferred property + // for every object of this type in the program. So we use a heuristic + // approach to determine whether to infer the property. + // + // 1) If the property is already defined, join it with the previously + // inferred type. + // 2) If this isn't an instance object, define it. + // 3) If the property of an object is being assigned in the constructor, + // define it. + // 4) If this is a stub, define it. + // 5) Otherwise, do not define the type, but declare it in the registry + // so that we can use it for missing property checks. + if (objectType.hasProperty(propName) || !objectType.isInstanceType()) { + if ("prototype".equals(propName)) { + objectType.defineDeclaredProperty(propName, rightType, getprop); + } else { + objectType.defineInferredProperty(propName, rightType, getprop); + } + } else if (obj.isThis() && + getJSType(syntacticScope.getRootNode()).isConstructor()) { + objectType.defineInferredProperty(propName, rightType, getprop); + } else { + registry.registerPropertyOnType(propName, objectType); + } + } + } + } + + /** + * Defines a declared property if it has not been defined yet. + * + * This handles the case where a property is declared on an object where + * the object type is inferred, and so the object type will not + * be known in {@code TypedScopeCreator}. + */ + private void ensurePropertyDeclared(Node getprop) { + ObjectType ownerType = ObjectType.cast( + getJSType(getprop.getFirstChild()).restrictByNotNullOrUndefined()); + if (ownerType != null) { + ensurePropertyDeclaredHelper(getprop, ownerType); + } + } + + /** + * Declares a property on its owner, if necessary. + * @return True if a property was declared. + */ + private boolean ensurePropertyDeclaredHelper( + Node getprop, ObjectType objectType) { + String propName = getprop.getLastChild().getString(); + String qName = getprop.getQualifiedName(); + if (qName != null) { + Var var = syntacticScope.getVar(qName); + if (var != null && !var.isTypeInferred()) { + // Handle normal declarations that could not be addressed earlier. + if (propName.equals("prototype") || + // Handle prototype declarations that could not be addressed earlier. + (!objectType.hasOwnProperty(propName) && + (!objectType.isInstanceType() || + (var.isExtern() && !objectType.isNativeObjectType())))) { + return objectType.defineDeclaredProperty( + propName, var.getType(), getprop); + } + } + } + return false; + } + + private FlowScope traverseName(Node n, FlowScope scope) { + String varName = n.getString(); + Node value = n.getFirstChild(); + JSType type = n.getJSType(); + if (value != null) { + scope = traverse(value, scope); + updateScopeForTypeChange(scope, n, n.getJSType() /* could be null */, + getJSType(value)); + return scope; + } else { + StaticSlot var = scope.getSlot(varName); + if (var != null) { + // There are two situations where we don't want to use type information + // from the scope, even if we have it. + + // 1) The var is escaped and assigned in an inner scope, e.g., + // function f() { var x = 3; function g() { x = null } (x); } + boolean isInferred = var.isTypeInferred(); + boolean unflowable = isInferred && + isUnflowable(syntacticScope.getVar(varName)); + + // 2) We're reading type information from another scope for an + // inferred variable. That variable is assigned more than once, + // and we can't know which type we're getting. + // + // var t = null; function f() { (t); } doStuff(); t = {}; + // + // Notice that this heuristic isn't perfect. For example, you might + // have: + // + // function f() { (t); } f(); var t = 3; + // + // In this case, we would infer the first reference to t as + // type {number}, even though it's undefined. + boolean nonLocalInferredSlot = false; + if (isInferred && syntacticScope.isLocal()) { + Var maybeOuterVar = syntacticScope.getParent().getVar(varName); + if (var == maybeOuterVar && + !maybeOuterVar.isMarkedAssignedExactlyOnce()) { + nonLocalInferredSlot = true; + } + } + + if (!unflowable && !nonLocalInferredSlot) { + type = var.getType(); + if (type == null) { + type = unknownType; + } + } + } + } + n.setJSType(type); + return scope; + } + + /** Traverse each element of the array. */ + private FlowScope traverseArrayLiteral(Node n, FlowScope scope) { + scope = traverseChildren(n, scope); + n.setJSType(getNativeType(ARRAY_TYPE)); + return scope; + } + + private FlowScope traverseObjectLiteral(Node n, FlowScope scope) { + JSType type = n.getJSType(); + Preconditions.checkNotNull(type); + + for (Node name = n.getFirstChild(); name != null; name = name.getNext()) { + scope = traverse(name.getFirstChild(), scope); + } + + // Object literals can be reflected on other types, or changed with + // type casts. + // See CodingConvention#getObjectLiteralCase and goog.object.reflect. + // Ignore these types of literals. + // TODO(nicksantos): There should be an "anonymous object" type that + // we can check for here. + ObjectType objectType = ObjectType.cast(type); + if (objectType == null) { + return scope; + } + + boolean hasLendsName = n.getJSDocInfo() != null && + n.getJSDocInfo().getLendsName() != null; + if (objectType.hasReferenceName() && !hasLendsName) { + return scope; + } + + String qObjName = NodeUtil.getBestLValueName( + NodeUtil.getBestLValue(n)); + for (Node name = n.getFirstChild(); name != null; + name = name.getNext()) { + Node value = name.getFirstChild(); + String memberName = NodeUtil.getObjectLitKeyName(name); + if (memberName != null) { + JSType rawValueType = name.getFirstChild().getJSType(); + JSType valueType = NodeUtil.getObjectLitKeyTypeFromValueType( + name, rawValueType); + if (valueType == null) { + valueType = unknownType; + } + objectType.defineInferredProperty(memberName, valueType, name); + + // Do normal flow inference if this is a direct property assignment. + if (qObjName != null && name.isStringKey()) { + String qKeyName = qObjName + "." + memberName; + Var var = syntacticScope.getVar(qKeyName); + JSType oldType = var == null ? null : var.getType(); + if (var != null && var.isTypeInferred()) { + var.setType(oldType == null ? + valueType : oldType.getLeastSupertype(oldType)); + } + + scope.inferQualifiedSlot(name, qKeyName, + oldType == null ? unknownType : oldType, + valueType); + } + } else { + n.setJSType(unknownType); + } + } + return scope; + } + + private FlowScope traverseAdd(Node n, FlowScope scope) { + Node left = n.getFirstChild(); + Node right = left.getNext(); + scope = traverseChildren(n, scope); + + JSType leftType = left.getJSType(); + JSType rightType = right.getJSType(); + + JSType type = unknownType; + if (leftType != null && rightType != null) { + boolean leftIsUnknown = leftType.isUnknownType(); + boolean rightIsUnknown = rightType.isUnknownType(); + if (leftIsUnknown && rightIsUnknown) { + type = unknownType; + } else if ((!leftIsUnknown && leftType.isString()) || + (!rightIsUnknown && rightType.isString())) { + type = getNativeType(STRING_TYPE); + } else if (leftIsUnknown || rightIsUnknown) { + type = unknownType; + } else if (isAddedAsNumber(leftType) && isAddedAsNumber(rightType)) { + type = getNativeType(NUMBER_TYPE); + } else { + type = registry.createUnionType(STRING_TYPE, NUMBER_TYPE); + } + } + n.setJSType(type); + + if (n.isAssignAdd()) { + updateScopeForTypeChange(scope, left, leftType, type); + } + + return scope; + } + + private boolean isAddedAsNumber(JSType type) { + return type.isSubtype(registry.createUnionType(VOID_TYPE, NULL_TYPE, + NUMBER_VALUE_OR_OBJECT_TYPE, BOOLEAN_TYPE, BOOLEAN_OBJECT_TYPE)); + } + + private FlowScope traverseHook(Node n, FlowScope scope) { + Node condition = n.getFirstChild(); + Node trueNode = condition.getNext(); + Node falseNode = n.getLastChild(); + + // verify the condition + scope = traverse(condition, scope); + + // reverse abstract interpret the condition to produce two new scopes + FlowScope trueScope = reverseInterpreter. + getPreciserScopeKnowingConditionOutcome( + condition, scope, true); + FlowScope falseScope = reverseInterpreter. + getPreciserScopeKnowingConditionOutcome( + condition, scope, false); + + // traverse the true node with the trueScope + traverse(trueNode, trueScope.createChildFlowScope()); + + // traverse the false node with the falseScope + traverse(falseNode, falseScope.createChildFlowScope()); + + // meet true and false nodes' types and assign + JSType trueType = trueNode.getJSType(); + JSType falseType = falseNode.getJSType(); + if (trueType != null && falseType != null) { + n.setJSType(trueType.getLeastSupertype(falseType)); + } else { + n.setJSType(null); + } + + return scope.createChildFlowScope(); + } + + private FlowScope traverseCall(Node n, FlowScope scope) { + scope = traverseChildren(n, scope); + + Node left = n.getFirstChild(); + JSType functionType = getJSType(left).restrictByNotNullOrUndefined(); + if (functionType.isFunctionType()) { + FunctionType fnType = functionType.toMaybeFunctionType(); + n.setJSType(fnType.getReturnType()); + backwardsInferenceFromCallSite(n, fnType); + } else if (functionType.isEquivalentTo( + getNativeType(CHECKED_UNKNOWN_TYPE))) { + n.setJSType(getNativeType(CHECKED_UNKNOWN_TYPE)); + } + + scope = tightenTypesAfterAssertions(scope, n); + return scope; + } + + private FlowScope tightenTypesAfterAssertions(FlowScope scope, + Node callNode) { + Node left = callNode.getFirstChild(); + Node firstParam = left.getNext(); + AssertionFunctionSpec assertionFunctionSpec = + assertionFunctionsMap.get(left.getQualifiedName()); + if (assertionFunctionSpec == null || firstParam == null) { + return scope; + } + Node assertedNode = assertionFunctionSpec.getAssertedParam(firstParam); + if (assertedNode == null) { + return scope; + } + JSType assertedType = assertionFunctionSpec.getAssertedType( + callNode, registry); + String assertedNodeName = assertedNode.getQualifiedName(); + + JSType narrowed; + // Handle assertions that enforce expressions evaluate to true. + if (assertedType == null) { + // Handle arbitrary expressions within the assert. + scope = reverseInterpreter.getPreciserScopeKnowingConditionOutcome( + assertedNode, scope, true); + // Build the result of the assertExpression + narrowed = getJSType(assertedNode).restrictByNotNullOrUndefined(); + } else { + // Handle assertions that enforce expressions are of a certain type. + JSType type = getJSType(assertedNode); + narrowed = type.getGreatestSubtype(assertedType); + if (assertedNodeName != null && type.differsFrom(narrowed)) { + scope = narrowScope(scope, assertedNode, narrowed); + } + } + + callNode.setJSType(narrowed); + return scope; + } + + private FlowScope narrowScope(FlowScope scope, Node node, JSType narrowed) { + if (node.isThis()) { + // "this" references don't need to be modeled in the control flow graph. + return scope; + } + + scope = scope.createChildFlowScope(); + if (node.isGetProp()) { + scope.inferQualifiedSlot( + node, node.getQualifiedName(), getJSType(node), narrowed); + } else { + redeclareSimpleVar(scope, node, narrowed); + } + return scope; + } + + /** + * We only do forward type inference. We do not do full backwards + * type inference. + * + * In other words, if we have, + * + * var x = f(); + * g(x); + * + * a forward type-inference engine would try to figure out the type + * of "x" from the return type of "f". A backwards type-inference engine + * would try to figure out the type of "x" from the parameter type of "g". + * + * However, there are a few special syntactic forms where we do some + * some half-assed backwards type-inference, because programmers + * expect it in this day and age. To take an example from Java, + * + * List x = Lists.newArrayList(); + * + * The Java compiler will be able to infer the generic type of the List + * returned by newArrayList(). + * + * In much the same way, we do some special-case backwards inference for + * JS. Those cases are enumerated here. + */ + private void backwardsInferenceFromCallSite(Node n, FunctionType fnType) { + boolean updatedFnType = inferTemplatedTypesForCall(n, fnType); + if (updatedFnType) { + fnType = n.getFirstChild().getJSType().toMaybeFunctionType(); + } + updateTypeOfParameters(n, fnType); + updateBind(n, fnType); + } + + /** + * When "bind" is called on a function, we infer the type of the returned + * "bound" function by looking at the number of parameters in the call site. + */ + private void updateBind(Node n, FunctionType fnType) { + CodingConvention.Bind bind = + compiler.getCodingConvention().describeFunctionBind(n, true); + if (bind == null) { + return; + } + + FunctionType callTargetFn = getJSType(bind.target) + .restrictByNotNullOrUndefined().toMaybeFunctionType(); + if (callTargetFn == null) { + return; + } + + n.setJSType( + callTargetFn.getBindReturnType( + // getBindReturnType expects the 'this' argument to be included. + bind.getBoundParameterCount() + 1)); + } + + /** + * For functions with function parameters, type inference will set the type of + * a function literal argument from the function parameter type. + */ + private void updateTypeOfParameters(Node n, FunctionType fnType) { + int i = 0; + int childCount = n.getChildCount(); + for (Node iParameter : fnType.getParameters()) { + if (i + 1 >= childCount) { + // TypeCheck#visitParametersList will warn so we bail. + return; + } + + JSType iParameterType = getJSType(iParameter); + Node iArgument = n.getChildAtIndex(i + 1); + JSType iArgumentType = getJSType(iArgument); + inferPropertyTypesToMatchConstraint(iArgumentType, iParameterType); + + // TODO(johnlenz): Filter out non-function types + // (such as null and undefined) as + // we only care about FUNCTION subtypes here. + JSType restrictedParameter = iParameterType + .restrictByNotNullOrUndefined() + .toMaybeFunctionType(); + if (restrictedParameter != null) { + if (iArgument.isFunction() && + iArgumentType.isFunctionType() && + iArgument.getJSDocInfo() == null) { + iArgument.setJSType(restrictedParameter); + } + } + i++; + } + } + + private Map inferTemplateTypesFromParameters( + FunctionType fnType, Node call) { + if (fnType.getTemplateKeys().isEmpty()) { + return Collections.emptyMap(); + } + + Map resolvedTypes = Maps.newIdentityHashMap(); + + Node callTarget = call.getFirstChild(); + if (NodeUtil.isGet(callTarget)) { + Node obj = callTarget.getFirstChild(); + maybeResolveTemplatedType( + fnType.getTypeOfThis(), + getJSType(obj), + resolvedTypes); + } + + if (call.hasMoreThanOneChild()) { + maybeResolveTemplateTypeFromNodes( + fnType.getParameters(), + call.getChildAtIndex(1).siblings(), + resolvedTypes); + } + return resolvedTypes; + } + + private void maybeResolveTemplatedType( + JSType paramType, + JSType argType, + Map resolvedTypes) { + if (paramType.isTemplateType()) { + // @param {T} + resolvedTemplateType( + resolvedTypes, paramType.toMaybeTemplateType(), argType); + } else if (paramType.isUnionType()) { + // @param {Array.|NodeList|Arguments|{length:number}} + UnionType unionType = paramType.toMaybeUnionType(); + for (JSType alernative : unionType.getAlternates()) { + maybeResolveTemplatedType(alernative, argType, resolvedTypes); + } + } else if (paramType.isFunctionType()) { + FunctionType paramFunctionType = paramType.toMaybeFunctionType(); + FunctionType argFunctionType = argType + .restrictByNotNullOrUndefined() + .collapseUnion() + .toMaybeFunctionType(); + if (argFunctionType != null && argFunctionType.isSubtype(paramType)) { + // infer from return type of the function type + maybeResolveTemplatedType( + paramFunctionType.getTypeOfThis(), + argFunctionType.getTypeOfThis(), resolvedTypes); + // infer from return type of the function type + maybeResolveTemplatedType( + paramFunctionType.getReturnType(), + argFunctionType.getReturnType(), resolvedTypes); + // infer from parameter types of the function type + maybeResolveTemplateTypeFromNodes( + paramFunctionType.getParameters(), + argFunctionType.getParameters(), resolvedTypes); + } + } else if (paramType.isParameterizedType()) { + ParameterizedType paramObjectType = paramType.toMaybeParameterizedType(); + JSType typeParameter = paramObjectType.getParameterType(); + Preconditions.checkNotNull(typeParameter); + if (typeParameter != null) { + // @param {Array.} + ObjectType argObjectType = argType + .restrictByNotNullOrUndefined() + .collapseUnion() + .toMaybeParameterizedType(); + if (argObjectType != null && argObjectType.isSubtype(paramType)) { + JSType argTypeParameter = argObjectType.getParameterType(); + Preconditions.checkNotNull(argTypeParameter); + maybeResolveTemplatedType( + typeParameter, argTypeParameter, resolvedTypes); + } + } + } + } + + private void maybeResolveTemplateTypeFromNodes( + Iterable declParams, + Iterable callParams, + Map resolvedTypes) { + maybeResolveTemplateTypeFromNodes( + declParams.iterator(), callParams.iterator(), resolvedTypes); + } + + private void maybeResolveTemplateTypeFromNodes( + Iterator declParams, + Iterator callParams, + Map resolvedTypes) { + while (declParams.hasNext() && callParams.hasNext()) { + Node declParam = declParams.next(); + maybeResolveTemplatedType( + getJSType(declParam), + getJSType(callParams.next()), + resolvedTypes); + if (declParam.isVarArgs()) { + while (callParams.hasNext()) { + maybeResolveTemplatedType( + getJSType(declParam), + getJSType(callParams.next()), + resolvedTypes); + } + } + } + } + + private void resolvedTemplateType( + Map map, TemplateType template, JSType resolved) { + JSType previous = map.get(template); + if (!resolved.isUnknownType()) { + if (previous == null) { + map.put(template, resolved); + } else { + JSType join = previous.getLeastSupertype(resolved); + map.put(template, join); + } + } + } + + private static class TemplateTypeReplacer extends ModificationVisitor { + private final Map replacements; + private final JSTypeRegistry registry; + boolean madeChanges = false; + + TemplateTypeReplacer( + JSTypeRegistry registry, Map replacements) { + super(registry); + this.registry = registry; + this.replacements = replacements; + } + + @Override + public JSType caseTemplateType(TemplateType type) { + madeChanges = true; + JSType replacement = replacements.get(type); + return replacement != null ? + replacement : registry.getNativeType(UNKNOWN_TYPE); + } + } + + /** + * For functions with function(this: T, ...) and T as parameters, type + * inference will set the type of this on a function literal argument to the + * the actual type of T. + */ + private boolean inferTemplatedTypesForCall( + Node n, FunctionType fnType) { + if (fnType.getTemplateKeys().isEmpty()) { + return false; + } + + // Try to infer the template types + Map inferred = inferTemplateTypesFromParameters( + fnType, n); + + // Replace all template types. If we couldn't find a replacement, we + // replace it with UNKNOWN. + TemplateTypeReplacer replacer = new TemplateTypeReplacer( + registry, inferred); + Node callTarget = n.getFirstChild(); + + FunctionType replacementFnType = fnType.visit(replacer) + .toMaybeFunctionType(); + Preconditions.checkNotNull(replacementFnType); + + callTarget.setJSType(replacementFnType); + n.setJSType(replacementFnType.getReturnType()); + + return replacer.madeChanges; + } + + private FlowScope traverseNew(Node n, FlowScope scope) { + scope = traverseChildren(n, scope); + + Node constructor = n.getFirstChild(); + JSType constructorType = constructor.getJSType(); + JSType type = null; + if (constructorType != null) { + constructorType = constructorType.restrictByNotNullOrUndefined(); + if (constructorType.isUnknownType()) { + type = unknownType; + } else { + FunctionType ct = constructorType.toMaybeFunctionType(); + if (ct == null && constructorType instanceof FunctionType) { + // If constructorType is a NoObjectType, then toMaybeFunctionType will + // return null. But NoObjectType implements the FunctionType + // interface, precisely because it can validly construct objects. + ct = (FunctionType) constructorType; + } + if (ct != null && ct.isConstructor()) { + type = ct.getInstanceType(); + backwardsInferenceFromCallSite(n, ct); + } + } + } + n.setJSType(type); + return scope; + } + + private BooleanOutcomePair traverseAnd(Node n, FlowScope scope) { + return traverseShortCircuitingBinOp(n, scope, true); + } + + private FlowScope traverseChildren(Node n, FlowScope scope) { + for (Node el = n.getFirstChild(); el != null; el = el.getNext()) { + scope = traverse(el, scope); + } + return scope; + } + + private FlowScope traverseGetElem(Node n, FlowScope scope) { + scope = traverseChildren(n, scope); + ObjectType objType = ObjectType.cast( + getJSType(n.getFirstChild()).restrictByNotNullOrUndefined()); + if (objType != null) { + JSType type = objType.getParameterType(); + if (type != null) { + n.setJSType(type); + } + } + return dereferencePointer(n.getFirstChild(), scope); + } + + private FlowScope traverseGetProp(Node n, FlowScope scope) { + Node objNode = n.getFirstChild(); + Node property = n.getLastChild(); + scope = traverseChildren(n, scope); + + n.setJSType( + getPropertyType( + objNode.getJSType(), property.getString(), n, scope)); + return dereferencePointer(n.getFirstChild(), scope); + } + + /** + * Suppose X is an object with inferred properties. + * Suppose also that X is used in a way where it would only type-check + * correctly if some of those properties are widened. + * Then we should be polite and automatically widen X's properties for him. + * + * For a concrete example, consider: + * param x {{prop: (number|undefined)}} + * function f(x) {} + * f({}); + * + * If we give the anonymous object an inferred property of (number|undefined), + * then this code will type-check appropriately. + */ + private void inferPropertyTypesToMatchConstraint( + JSType type, JSType constraint) { + if (type == null || constraint == null) { + return; + } + + type.matchConstraint(constraint); + } + + /** + * If we access a property of a symbol, then that symbol is not + * null or undefined. + */ + private FlowScope dereferencePointer(Node n, FlowScope scope) { + if (n.isQualifiedName()) { + JSType type = getJSType(n); + JSType narrowed = type.restrictByNotNullOrUndefined(); + if (type != narrowed) { + scope = narrowScope(scope, n, narrowed); + } + } + return scope; + } + + private JSType getPropertyType(JSType objType, String propName, + Node n, FlowScope scope) { + // We often have a couple of different types to choose from for the + // property. Ordered by accuracy, we have + // 1) A locally inferred qualified name (which is in the FlowScope) + // 2) A globally declared qualified name (which is in the FlowScope) + // 3) A property on the owner type (which is on objType) + // 4) A name in the type registry (as a last resort) + JSType propertyType = null; + boolean isLocallyInferred = false; + + // Scopes sometimes contain inferred type info about qualified names. + String qualifiedName = n.getQualifiedName(); + StaticSlot var = scope.getSlot(qualifiedName); + if (var != null) { + JSType varType = var.getType(); + if (varType != null) { + boolean isDeclared = !var.isTypeInferred(); + isLocallyInferred = (var != syntacticScope.getSlot(qualifiedName)); + if (isDeclared || isLocallyInferred) { + propertyType = varType; + } + } + } + + if (propertyType == null && objType != null) { + JSType foundType = objType.findPropertyType(propName); + if (foundType != null) { + propertyType = foundType; + } + } + + if ((propertyType == null || propertyType.isUnknownType()) + && qualifiedName != null) { + // If we find this node in the registry, then we can infer its type. + ObjectType regType = ObjectType.cast(registry.getType(qualifiedName)); + if (regType != null) { + propertyType = regType.getConstructor(); + } + } + + if (propertyType == null) { + return unknownType; + } else if (propertyType.isEquivalentTo(unknownType) && isLocallyInferred) { + // If the type has been checked in this scope, + // then use CHECKED_UNKNOWN_TYPE instead to indicate that. + return getNativeType(CHECKED_UNKNOWN_TYPE); + } else { + return propertyType; + } + } + + private BooleanOutcomePair traverseOr(Node n, FlowScope scope) { + return traverseShortCircuitingBinOp(n, scope, false); + } + + private BooleanOutcomePair traverseShortCircuitingBinOp( + Node n, FlowScope scope, boolean condition) { + Node left = n.getFirstChild(); + Node right = n.getLastChild(); + + // type the left node + BooleanOutcomePair leftLiterals = + traverseWithinShortCircuitingBinOp(left, + scope.createChildFlowScope()); + JSType leftType = left.getJSType(); + + // reverse abstract interpret the left node to produce the correct + // scope in which to verify the right node + FlowScope rightScope = reverseInterpreter. + getPreciserScopeKnowingConditionOutcome( + left, leftLiterals.getOutcomeFlowScope(left.getType(), condition), + condition); + + // type the right node + BooleanOutcomePair rightLiterals = + traverseWithinShortCircuitingBinOp( + right, rightScope.createChildFlowScope()); + JSType rightType = right.getJSType(); + + JSType type; + BooleanOutcomePair literals; + if (leftType != null && rightType != null) { + leftType = leftType.getRestrictedTypeGivenToBooleanOutcome(!condition); + if (leftLiterals.toBooleanOutcomes == + BooleanLiteralSet.get(!condition)) { + // Use the restricted left type, since the right side never gets + // evaluated. + type = leftType; + literals = leftLiterals; + } else { + // Use the join of the restricted left type knowing the outcome of the + // ToBoolean predicate and of the right type. + type = leftType.getLeastSupertype(rightType); + literals = + getBooleanOutcomePair(leftLiterals, rightLiterals, condition); + } + + // Exclude the boolean type if the literal set is empty because a boolean + // can never actually be returned. + if (literals.booleanValues == BooleanLiteralSet.EMPTY && + getNativeType(BOOLEAN_TYPE).isSubtype(type)) { + // Exclusion only make sense for a union type. + if (type.isUnionType()) { + type = type.toMaybeUnionType().getRestrictedUnion( + getNativeType(BOOLEAN_TYPE)); + } + } + } else { + type = null; + literals = new BooleanOutcomePair( + BooleanLiteralSet.BOTH, BooleanLiteralSet.BOTH, + leftLiterals.getJoinedFlowScope(), + rightLiterals.getJoinedFlowScope()); + } + n.setJSType(type); + + return literals; + } + + private BooleanOutcomePair traverseWithinShortCircuitingBinOp(Node n, + FlowScope scope) { + switch (n.getType()) { + case Token.AND: + return traverseAnd(n, scope); + + case Token.OR: + return traverseOr(n, scope); + + default: + scope = traverse(n, scope); + return newBooleanOutcomePair(n.getJSType(), scope); + } + } + + /** + * Infers the boolean outcome pair that can be taken by a + * short-circuiting binary operation ({@code &&} or {@code ||}). + * @see #getBooleanOutcomes(BooleanLiteralSet, BooleanLiteralSet, boolean) + */ + BooleanOutcomePair getBooleanOutcomePair(BooleanOutcomePair left, + BooleanOutcomePair right, boolean condition) { + return new BooleanOutcomePair( + getBooleanOutcomes(left.toBooleanOutcomes, right.toBooleanOutcomes, + condition), + getBooleanOutcomes(left.booleanValues, right.booleanValues, condition), + left.getJoinedFlowScope(), right.getJoinedFlowScope()); + } + + /** + * Infers the boolean literal set that can be taken by a + * short-circuiting binary operation ({@code &&} or {@code ||}). + * @param left the set of possible {@code ToBoolean} predicate results for + * the expression on the left side of the operator + * @param right the set of possible {@code ToBoolean} predicate results for + * the expression on the right side of the operator + * @param condition the left side {@code ToBoolean} predicate result that + * causes the right side to get evaluated (i.e. not short-circuited) + * @return a set of possible {@code ToBoolean} predicate results for the + * entire expression + */ + static BooleanLiteralSet getBooleanOutcomes(BooleanLiteralSet left, + BooleanLiteralSet right, boolean condition) { + return right.union(left.intersection(BooleanLiteralSet.get(!condition))); + } + + /** + * When traversing short-circuiting binary operations, we need to keep track + * of two sets of boolean literals: + * 1. {@code toBooleanOutcomes}: boolean literals as converted from any types, + * 2. {@code booleanValues}: boolean literals from just boolean types. + */ + private final class BooleanOutcomePair { + final BooleanLiteralSet toBooleanOutcomes; + final BooleanLiteralSet booleanValues; + + // The scope if only half of the expression executed, when applicable. + final FlowScope leftScope; + + // The scope when the whole expression executed. + final FlowScope rightScope; + + // The scope when we don't know how much of the expression is executed. + FlowScope joinedScope = null; + + BooleanOutcomePair( + BooleanLiteralSet toBooleanOutcomes, BooleanLiteralSet booleanValues, + FlowScope leftScope, FlowScope rightScope) { + this.toBooleanOutcomes = toBooleanOutcomes; + this.booleanValues = booleanValues; + this.leftScope = leftScope; + this.rightScope = rightScope; + } + + /** + * Gets the safe estimated scope without knowing if all of the + * subexpressions will be evaluated. + */ + FlowScope getJoinedFlowScope() { + if (joinedScope == null) { + if (leftScope == rightScope) { + joinedScope = rightScope; + } else { + joinedScope = join(leftScope, rightScope); + } + } + return joinedScope; + } + + /** + * Gets the outcome scope if we do know the outcome of the entire + * expression. + */ + FlowScope getOutcomeFlowScope(int nodeType, boolean outcome) { + if (nodeType == Token.AND && outcome || + nodeType == Token.OR && !outcome) { + // We know that the whole expression must have executed. + return rightScope; + } else { + return getJoinedFlowScope(); + } + } + } + + private BooleanOutcomePair newBooleanOutcomePair( + JSType jsType, FlowScope flowScope) { + if (jsType == null) { + return new BooleanOutcomePair( + BooleanLiteralSet.BOTH, BooleanLiteralSet.BOTH, flowScope, flowScope); + } + return new BooleanOutcomePair(jsType.getPossibleToBooleanOutcomes(), + registry.getNativeType(BOOLEAN_TYPE).isSubtype(jsType) ? + BooleanLiteralSet.BOTH : BooleanLiteralSet.EMPTY, + flowScope, flowScope); + } + + private void redeclareSimpleVar( + FlowScope scope, Node nameNode, JSType varType) { + Preconditions.checkState(nameNode.isName()); + String varName = nameNode.getString(); + if (varType == null) { + varType = getNativeType(JSTypeNative.UNKNOWN_TYPE); + } + if (isUnflowable(syntacticScope.getVar(varName))) { + return; + } + scope.inferSlotType(varName, varType); + } + + private boolean isUnflowable(Var v) { + return v != null && v.isLocal() && v.isMarkedEscaped() && + // It's OK to flow a variable in the scope where it's escaped. + v.getScope() == syntacticScope; + } + + /** + * This method gets the JSType from the Node argument and verifies that it is + * present. + */ + private JSType getJSType(Node n) { + JSType jsType = n.getJSType(); + if (jsType == null) { + // TODO(nicksantos): This branch indicates a compiler bug, not worthy of + // halting the compilation but we should log this and analyze to track + // down why it happens. This is not critical and will be resolved over + // time as the type checker is extended. + return unknownType; + } else { + return jsType; + } + } + + private JSType getNativeType(JSTypeNative typeId) { + return registry.getNativeType(typeId); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypeInferencePass.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypeInferencePass.java new file mode 100644 index 0000000..520717f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypeInferencePass.java @@ -0,0 +1,157 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec; +import com.google.javascript.jscomp.NodeTraversal.AbstractScopedCallback; +import com.google.javascript.jscomp.type.ReverseAbstractInterpreter; +import com.google.javascript.rhino.Node; + +import java.util.Map; + +/** + * A compiler pass to run the type inference analysis. + * + */ +class TypeInferencePass implements CompilerPass { + + static final DiagnosticType DATAFLOW_ERROR = DiagnosticType.warning( + "JSC_INTERNAL_ERROR_DATAFLOW", + "non-monotonic data-flow analysis"); + + private final AbstractCompiler compiler; + private final ReverseAbstractInterpreter reverseInterpreter; + private Scope topScope; + private MemoizedScopeCreator scopeCreator; + private final Map assertionFunctionsMap; + + TypeInferencePass(AbstractCompiler compiler, + ReverseAbstractInterpreter reverseInterpreter, + Scope topScope, MemoizedScopeCreator scopeCreator) { + this.compiler = compiler; + this.reverseInterpreter = reverseInterpreter; + this.topScope = topScope; + this.scopeCreator = scopeCreator; + + assertionFunctionsMap = Maps.newHashMap(); + for (AssertionFunctionSpec assertionFucntion : + compiler.getCodingConvention().getAssertionFunctions()) { + assertionFunctionsMap.put(assertionFucntion.getFunctionName(), + assertionFucntion); + } + } + + /** + * Main entry point for type inference when running over the whole tree. + * + * @param externsRoot The root of the externs parse tree. + * @param jsRoot The root of the input parse tree to be checked. + */ + @Override + public void process(Node externsRoot, Node jsRoot) { + Node externsAndJs = jsRoot.getParent(); + Preconditions.checkState(externsAndJs != null); + Preconditions.checkState( + externsRoot == null || externsAndJs.hasChild(externsRoot)); + + inferAllScopes(externsAndJs); + } + + /** Entry point for type inference when running over part of the tree. */ + void inferAllScopes(Node node) { + // Type analysis happens in two major phases. + // 1) Finding all the symbols. + // 2) Propagating all the inferred types. + // + // The order of this analysis is non-obvious. In a complete inference + // system, we may need to backtrack arbitrarily far. But the compile-time + // costs would be unacceptable. + // + // We do one pass where we do typed scope creation for all scopes + // in pre-order. + // + // Then we do a second pass where we do all type inference + // (type propagation) in pre-order. + // + // We use a memoized scope creator so that we never create a scope + // more than once. + // + // This will allow us to handle cases like: + // var ns = {}; + // (function() { /** JSDoc */ ns.method = function() {}; })(); + // ns.method(); + // In this code, we need to build the symbol table for the inner scope in + // order to propagate the type of ns.method in the outer scope. + (new NodeTraversal( + compiler, new FirstScopeBuildingCallback(), scopeCreator)) + .traverseWithScope(node, topScope); + (new NodeTraversal( + compiler, new SecondScopeBuildingCallback(), scopeCreator)) + .traverseWithScope(node, topScope); + } + + void inferScope(Node n, Scope scope) { + TypeInference typeInference = + new TypeInference( + compiler, computeCfg(n), reverseInterpreter, scope, + assertionFunctionsMap); + try { + typeInference.analyze(); + + // Resolve any new type names found during the inference. + compiler.getTypeRegistry().resolveTypesInScope(scope); + + } catch (DataFlowAnalysis.MaxIterationsExceededException e) { + compiler.report(JSError.make(n.getSourceFileName(), n, DATAFLOW_ERROR)); + } + } + + private class FirstScopeBuildingCallback extends AbstractScopedCallback { + @Override + public void enterScope(NodeTraversal t) { + t.getScope(); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + // Do nothing + } + } + + private class SecondScopeBuildingCallback extends AbstractScopedCallback { + @Override + public void enterScope(NodeTraversal t) { + // Only infer the entry root, rather than the scope root. + // This ensures that incremental compilation only touches the root + // that's been swapped out. + inferScope(t.getCurrentNode(), t.getScope()); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + // Do nothing + } + } + + private ControlFlowGraph computeCfg(Node n) { + ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, false); + cfa.process(null, n); + return cfa.getCfg(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypeValidator.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypeValidator.java new file mode 100644 index 0000000..5feecd0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypeValidator.java @@ -0,0 +1,830 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import static com.google.javascript.rhino.jstype.JSTypeNative.ARRAY_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NO_OBJECT_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NULL_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_STRING; +import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; + +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.StaticSlot; +import com.google.javascript.rhino.jstype.UnknownType; + +import java.text.MessageFormat; +import java.util.Iterator; +import java.util.List; + +/** + * A central reporter for all type violations: places where the programmer + * has annotated a variable (or property) with one type, but has assigned + * another type to it. + * + * Also doubles as a central repository for all type violations, so that + * type-based optimizations (like AmbiguateProperties) can be fault-tolerant. + * + * @author nicksantos@google.com (Nick Santos) + */ +class TypeValidator { + + private final AbstractCompiler compiler; + private final JSTypeRegistry typeRegistry; + private final JSType allValueTypes; + private boolean shouldReport = true; + private final JSType nullOrUndefined; + + // TODO(nicksantos): Provide accessors to better filter the list of type + // mismatches. For example, if we pass (Cake|null) where only Cake is + // allowed, that doesn't mean we should invalidate all Cakes. + private final List mismatches = Lists.newArrayList(); + + // User warnings + private static final String FOUND_REQUIRED = + "{0}\n" + + "found : {1}\n" + + "required: {2}"; + + // TODO(johnlenz): reenable this after after the next release. + static final DiagnosticType INVALID_CAST = + DiagnosticType.disabled("JSC_INVALID_CAST", + "invalid cast - must be a subtype or supertype\n" + + "from: {0}\n" + + "to : {1}"); + + static final DiagnosticType TYPE_MISMATCH_WARNING = + DiagnosticType.warning( + "JSC_TYPE_MISMATCH", + "{0}"); + + static final DiagnosticType MISSING_EXTENDS_TAG_WARNING = + DiagnosticType.warning( + "JSC_MISSING_EXTENDS_TAG", + "Missing @extends tag on type {0}"); + + static final DiagnosticType DUP_VAR_DECLARATION = + DiagnosticType.warning("JSC_DUP_VAR_DECLARATION", + "variable {0} redefined with type {1}, " + + "original definition at {2}:{3} with type {4}"); + + static final DiagnosticType HIDDEN_PROPERTY_MISMATCH = + DiagnosticType.warning("JSC_HIDDEN_PROPERTY_MISMATCH", + "mismatch of the {0} property type and the type " + + "of the property it overrides from superclass {1}\n" + + "original: {2}\n" + + "override: {3}"); + + static final DiagnosticType INTERFACE_METHOD_NOT_IMPLEMENTED = + DiagnosticType.warning( + "JSC_INTERFACE_METHOD_NOT_IMPLEMENTED", + "property {0} on interface {1} is not implemented by type {2}"); + + static final DiagnosticType HIDDEN_INTERFACE_PROPERTY_MISMATCH = + DiagnosticType.warning( + "JSC_HIDDEN_INTERFACE_PROPERTY_MISMATCH", + "mismatch of the {0} property type and the type " + + "of the property it overrides from interface {1}\n" + + "original: {2}\n" + + "override: {3}"); + + static final DiagnosticType UNKNOWN_TYPEOF_VALUE = + DiagnosticType.warning("JSC_UNKNOWN_TYPEOF_VALUE", "unknown type: {0}"); + + static final DiagnosticType ILLEGAL_PROPERTY_ACCESS = + DiagnosticType.warning("JSC_ILLEGAL_PROPERTY_ACCESS", + "Cannot do {0} access on a {1}"); + + static final DiagnosticGroup ALL_DIAGNOSTICS = new DiagnosticGroup( + INVALID_CAST, + TYPE_MISMATCH_WARNING, + MISSING_EXTENDS_TAG_WARNING, + DUP_VAR_DECLARATION, + HIDDEN_PROPERTY_MISMATCH, + INTERFACE_METHOD_NOT_IMPLEMENTED, + HIDDEN_INTERFACE_PROPERTY_MISMATCH, + UNKNOWN_TYPEOF_VALUE, + ILLEGAL_PROPERTY_ACCESS); + + TypeValidator(AbstractCompiler compiler) { + this.compiler = compiler; + this.typeRegistry = compiler.getTypeRegistry(); + this.allValueTypes = typeRegistry.createUnionType( + STRING_TYPE, NUMBER_TYPE, BOOLEAN_TYPE, NULL_TYPE, VOID_TYPE); + this.nullOrUndefined = typeRegistry.createUnionType( + NULL_TYPE, VOID_TYPE); + } + + /** + * Gets a list of type violations. + * + * For each violation, one element is the expected type and the other is + * the type that is actually found. Order is not significant. + */ + Iterable getMismatches() { + return mismatches; + } + + void setShouldReport(boolean report) { + this.shouldReport = report; + } + + // All non-private methods should have the form: + // expectCondition(NodeTraversal t, Node n, ...); + // If there is a mismatch, the {@code expect} method should issue + // a warning and attempt to correct the mismatch, when possible. + + void expectValidTypeofName(NodeTraversal t, Node n, String found) { + report(JSError.make(t.getSourceName(), n, UNKNOWN_TYPEOF_VALUE, found)); + } + + /** + * Expect the type to be an object, or a type convertible to object. If the + * expectation is not met, issue a warning at the provided node's source code + * position. + * @return True if there was no warning, false if there was a mismatch. + */ + boolean expectObject(NodeTraversal t, Node n, JSType type, String msg) { + if (!type.matchesObjectContext()) { + mismatch(t, n, msg, type, OBJECT_TYPE); + return false; + } + return true; + } + + /** + * Expect the type to be an object. Unlike expectObject, a type convertible + * to object is not acceptable. + */ + void expectActualObject(NodeTraversal t, Node n, JSType type, String msg) { + if (!type.isObject()) { + mismatch(t, n, msg, type, OBJECT_TYPE); + } + } + + /** + * Expect the type to contain an object sometimes. If the expectation is + * not met, issue a warning at the provided node's source code position. + */ + void expectAnyObject(NodeTraversal t, Node n, JSType type, String msg) { + JSType anyObjectType = getNativeType(NO_OBJECT_TYPE); + if (!anyObjectType.isSubtype(type) && !type.isEmptyType()) { + mismatch(t, n, msg, type, anyObjectType); + } + } + + /** + * Expect the type to be a string, or a type convertible to string. If the + * expectation is not met, issue a warning at the provided node's source code + * position. + */ + void expectString(NodeTraversal t, Node n, JSType type, String msg) { + if (!type.matchesStringContext()) { + mismatch(t, n, msg, type, STRING_TYPE); + } + } + + /** + * Expect the type to be a number, or a type convertible to number. If the + * expectation is not met, issue a warning at the provided node's source code + * position. + */ + void expectNumber(NodeTraversal t, Node n, JSType type, String msg) { + if (!type.matchesNumberContext()) { + mismatch(t, n, msg, type, NUMBER_TYPE); + } + } + + /** + * Expect the type to be a valid operand to a bitwise operator. This includes + * numbers, any type convertible to a number, or any other primitive type + * (undefined|null|boolean|string). + */ + void expectBitwiseable(NodeTraversal t, Node n, JSType type, String msg) { + if (!type.matchesNumberContext() && !type.isSubtype(allValueTypes)) { + mismatch(t, n, msg, type, allValueTypes); + } + } + + /** + * Expect the type to be a number or string, or a type convertible to a number + * or string. If the expectation is not met, issue a warning at the provided + * node's source code position. + */ + void expectStringOrNumber( + NodeTraversal t, Node n, JSType type, String msg) { + if (!type.matchesNumberContext() && !type.matchesStringContext()) { + mismatch(t, n, msg, type, NUMBER_STRING); + } + } + + /** + * Expect the type to be anything but the null or void type. If the + * expectation is not met, issue a warning at the provided node's + * source code position. Note that a union type that includes the + * void type and at least one other type meets the expectation. + * @return Whether the expectation was met. + */ + boolean expectNotNullOrUndefined( + NodeTraversal t, Node n, JSType type, String msg, JSType expectedType) { + if (!type.isNoType() && !type.isUnknownType() && + type.isSubtype(nullOrUndefined) && + !containsForwardDeclaredUnresolvedName(type)) { + + // There's one edge case right now that we don't handle well, and + // that we don't want to warn about. + // if (this.x == null) { + // this.initializeX(); + // this.x.foo(); + // } + // In this case, we incorrectly type x because of how we + // infer properties locally. See issue 109. + // http://code.google.com/p/closure-compiler/issues/detail?id=109 + // + // We do not do this inference globally. + if (n.isGetProp() && + !t.inGlobalScope() && type.isNullType()) { + return true; + } + + mismatch(t, n, msg, type, expectedType); + return false; + } + return true; + } + + private boolean containsForwardDeclaredUnresolvedName(JSType type) { + if (type.isUnionType()) { + for (JSType alt : type.toMaybeUnionType().getAlternates()) { + if (containsForwardDeclaredUnresolvedName(alt)) { + return true; + } + } + } + return type.isNoResolvedType(); + } + + /** + * Expect that the type of a switch condition matches the type of its + * case condition. + */ + void expectSwitchMatchesCase(NodeTraversal t, Node n, JSType switchType, + JSType caseType) { + // ECMA-262, page 68, step 3 of evaluation of CaseBlock, + // but allowing extra autoboxing. + // TODO(user): remove extra conditions when type annotations + // in the code base have adapted to the change in the compiler. + if (!switchType.canTestForShallowEqualityWith(caseType) && + (caseType.autoboxesTo() == null || + !caseType.autoboxesTo().isSubtype(switchType))) { + mismatch(t, n.getFirstChild(), + "case expression doesn't match switch", + caseType, switchType); + } + } + + /** + * Expect that the first type can be addressed with GETELEM syntax, + * and that the second type is the right type for an index into the + * first type. + * + * @param t The node traversal. + * @param n The GETELEM node to issue warnings on. + * @param objType The type of the left side of the GETELEM. + * @param indexType The type inside the brackets of the GETELEM. + */ + void expectIndexMatch(NodeTraversal t, Node n, JSType objType, + JSType indexType) { + Preconditions.checkState(n.isGetElem()); + Node indexNode = n.getLastChild(); + if (objType.isStruct()) { + report(JSError.make(t.getSourceName(), indexNode, + ILLEGAL_PROPERTY_ACCESS, "'[]'", "struct")); + } + if (objType.isUnknownType()) { + expectStringOrNumber(t, indexNode, indexType, "property access"); + } else { + ObjectType dereferenced = objType.dereference(); + if (dereferenced != null && dereferenced.getIndexType() != null) { + expectCanAssignTo(t, indexNode, indexType, dereferenced.getIndexType(), + "restricted index type"); + } else if (dereferenced != null && dereferenced.isArrayType()) { + expectNumber(t, indexNode, indexType, "array access"); + } else if (objType.matchesObjectContext()) { + expectString(t, indexNode, indexType, "property access"); + } else { + mismatch(t, n, "only arrays or objects can be accessed", + objType, + typeRegistry.createUnionType(ARRAY_TYPE, OBJECT_TYPE)); + } + } + } + + /** + * Expect that the first type can be assigned to a symbol of the second + * type. + * + * @param t The node traversal. + * @param n The node to issue warnings on. + * @param rightType The type on the RHS of the assign. + * @param leftType The type of the symbol on the LHS of the assign. + * @param owner The owner of the property being assigned to. + * @param propName The name of the property being assigned to. + * @return True if the types matched, false otherwise. + */ + boolean expectCanAssignToPropertyOf(NodeTraversal t, Node n, JSType rightType, + JSType leftType, Node owner, String propName) { + // The NoType check is a hack to make typedefs work OK. + if (!leftType.isNoType() && !rightType.isSubtype(leftType)) { + // Do not type-check interface methods, because we expect that + // they will have dummy implementations that do not match the type + // annotations. + JSType ownerType = getJSType(owner); + if (ownerType.isFunctionPrototypeType()) { + FunctionType ownerFn = ownerType.toObjectType().getOwnerFunction(); + if (ownerFn.isInterface() && + rightType.isFunctionType() && leftType.isFunctionType()) { + return true; + } + } + + mismatch(t, n, + "assignment to property " + propName + " of " + + getReadableJSTypeName(owner, true), + rightType, leftType); + return false; + } + return true; + } + + /** + * Expect that the first type can be assigned to a symbol of the second + * type. + * + * @param t The node traversal. + * @param n The node to issue warnings on. + * @param rightType The type on the RHS of the assign. + * @param leftType The type of the symbol on the LHS of the assign. + * @param msg An extra message for the mismatch warning, if necessary. + * @return True if the types matched, false otherwise. + */ + boolean expectCanAssignTo(NodeTraversal t, Node n, JSType rightType, + JSType leftType, String msg) { + if (!rightType.isSubtype(leftType)) { + mismatch(t, n, msg, rightType, leftType); + return false; + } + return true; + } + + /** + * Expect that the type of an argument matches the type of the parameter + * that it's fulfilling. + * + * @param t The node traversal. + * @param n The node to issue warnings on. + * @param argType The type of the argument. + * @param paramType The type of the parameter. + * @param callNode The call node, to help with the warning message. + * @param ordinal The argument ordinal, to help with the warning message. + */ + void expectArgumentMatchesParameter(NodeTraversal t, Node n, JSType argType, + JSType paramType, Node callNode, int ordinal) { + if (!argType.isSubtype(paramType)) { + mismatch(t, n, + String.format("actual parameter %d of %s does not match " + + "formal parameter", ordinal, + getReadableJSTypeName(callNode.getFirstChild(), false)), + argType, paramType); + } + } + + /** + * Expect that the first type can override a property of the second + * type. + * + * @param t The node traversal. + * @param n The node to issue warnings on. + * @param overridingType The overriding type. + * @param hiddenType The type of the property being overridden. + * @param propertyName The name of the property, for use in the + * warning message. + * @param ownerType The type of the owner of the property, for use + * in the warning message. + */ + void expectCanOverride(NodeTraversal t, Node n, JSType overridingType, + JSType hiddenType, String propertyName, JSType ownerType) { + if (!overridingType.isSubtype(hiddenType)) { + registerMismatch(overridingType, hiddenType, + report(t.makeError(n, HIDDEN_PROPERTY_MISMATCH, propertyName, + ownerType.toString(), hiddenType.toString(), + overridingType.toString()))); + } + } + + /** + * Expect that the first type is the direct superclass of the second type. + * + * @param t The node traversal. + * @param n The node where warnings should point to. + * @param superObject The expected super instance type. + * @param subObject The sub instance type. + */ + void expectSuperType(NodeTraversal t, Node n, ObjectType superObject, + ObjectType subObject) { + FunctionType subCtor = subObject.getConstructor(); + ObjectType implicitProto = subObject.getImplicitPrototype(); + ObjectType declaredSuper = + implicitProto == null ? null : implicitProto.getImplicitPrototype(); + if (declaredSuper != null && + !(superObject instanceof UnknownType) && + !declaredSuper.isEquivalentTo(superObject)) { + if (declaredSuper.isEquivalentTo(getNativeType(OBJECT_TYPE))) { + registerMismatch(superObject, declaredSuper, report( + t.makeError(n, MISSING_EXTENDS_TAG_WARNING, subObject.toString()))); + } else { + mismatch(t.getSourceName(), n, + "mismatch in declaration of superclass type", + superObject, declaredSuper); + } + + // Correct the super type. + if (!subCtor.hasCachedValues()) { + subCtor.setPrototypeBasedOn(superObject); + } + } + } + + /** + * Expect that the first type can be cast to the second type. The first type + * must have some relationship with the second. + * + * @param t The node traversal. + * @param n The node where warnings should point. + * @param type The type being cast from. + * @param castType The type being cast to. + */ + void expectCanCast(NodeTraversal t, Node n, JSType castType, JSType type) { + if (!type.canCastTo(castType)) { + registerMismatch(type, castType, report(t.makeError(n, INVALID_CAST, + type.toString(), castType.toString()))); + } + } + + /** + * Expect that the given variable has not been declared with a type. + * + * @param sourceName The name of the source file we're in. + * @param n The node where warnings should point to. + * @param parent The parent of {@code n}. + * @param var The variable that we're checking. + * @param variableName The name of the variable. + * @param newType The type being applied to the variable. Mostly just here + * for the benefit of the warning. + * @return The variable we end up with. Most of the time, this will just + * be {@code var}, but in some rare cases we will need to declare + * a new var with new source info. + */ + Var expectUndeclaredVariable(String sourceName, CompilerInput input, + Node n, Node parent, Var var, String variableName, JSType newType) { + Var newVar = var; + boolean allowDupe = false; + if (n.isGetProp() || + NodeUtil.isObjectLitKey(n, parent)) { + JSDocInfo info = n.getJSDocInfo(); + if (info == null) { + info = parent.getJSDocInfo(); + } + allowDupe = + info != null && info.getSuppressions().contains("duplicate"); + } + + JSType varType = var.getType(); + + // Only report duplicate declarations that have types. Other duplicates + // will be reported by the syntactic scope creator later in the + // compilation process. + if (varType != null && + varType != typeRegistry.getNativeType(UNKNOWN_TYPE) && + newType != null && + newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) { + // If there are two typed declarations of the same variable, that + // is an error and the second declaration is ignored, except in the + // case of native types. A null input type means that the declaration + // was made in TypedScopeCreator#createInitialScope and is a + // native type. We should redeclare it at the new input site. + if (var.input == null) { + Scope s = var.getScope(); + s.undeclare(var); + newVar = s.declare(variableName, n, varType, input, false); + + n.setJSType(varType); + if (parent.isVar()) { + if (n.getFirstChild() != null) { + n.getFirstChild().setJSType(varType); + } + } else { + Preconditions.checkState(parent.isFunction()); + parent.setJSType(varType); + } + } else { + // Always warn about duplicates if the overridden type does not + // match the original type. + // + // If the types match, suppress the warning iff there was a @suppress + // tag, or if the original declaration was a stub. + if (!(allowDupe || + var.getParentNode().isExprResult()) || + !newType.isEquivalentTo(varType)) { + report(JSError.make(sourceName, n, DUP_VAR_DECLARATION, + variableName, newType.toString(), var.getInputName(), + String.valueOf(var.nameNode.getLineno()), + varType.toString())); + } + } + } + + return newVar; + } + + /** + * Expect that all properties on interfaces that this type implements are + * implemented and correctly typed. + */ + void expectAllInterfaceProperties(NodeTraversal t, Node n, + FunctionType type) { + ObjectType instance = type.getInstanceType(); + for (ObjectType implemented : type.getAllImplementedInterfaces()) { + if (implemented.getImplicitPrototype() != null) { + for (String prop : + implemented.getImplicitPrototype().getOwnPropertyNames()) { + expectInterfaceProperty(t, n, instance, implemented, prop); + } + } + } + } + + /** + * Expect that the property in an interface that this type implements is + * implemented and correctly typed. + */ + private void expectInterfaceProperty(NodeTraversal t, Node n, + ObjectType instance, ObjectType implementedInterface, String prop) { + StaticSlot propSlot = instance.getSlot(prop); + if (propSlot == null) { + // Not implemented + String sourceName = n.getSourceFileName(); + sourceName = sourceName == null ? "" : sourceName; + registerMismatch(instance, implementedInterface, + report(JSError.make(sourceName, n, + INTERFACE_METHOD_NOT_IMPLEMENTED, + prop, implementedInterface.toString(), instance.toString()))); + } else { + Node propNode = propSlot.getDeclaration() == null ? + null : propSlot.getDeclaration().getNode(); + + // Fall back on the constructor node if we can't find a node for the + // property. + propNode = propNode == null ? n : propNode; + + JSType found = propSlot.getType(); + JSType required + = implementedInterface.getImplicitPrototype().getPropertyType(prop); + found = found.restrictByNotNullOrUndefined(); + required = required.restrictByNotNullOrUndefined(); + if (!found.isSubtype(required)) { + // Implemented, but not correctly typed + FunctionType constructor = + implementedInterface.toObjectType().getConstructor(); + registerMismatch(found, required, report(t.makeError(propNode, + HIDDEN_INTERFACE_PROPERTY_MISMATCH, prop, + constructor.getTopMostDefiningType(prop).toString(), + required.toString(), found.toString()))); + } + } + } + + /** + * Report a type mismatch + */ + private void mismatch(NodeTraversal t, Node n, + String msg, JSType found, JSType required) { + mismatch(t.getSourceName(), n, msg, found, required); + } + + private void mismatch(NodeTraversal t, Node n, + String msg, JSType found, JSTypeNative required) { + mismatch(t, n, msg, found, getNativeType(required)); + } + + private void mismatch(String sourceName, Node n, + String msg, JSType found, JSType required) { + registerMismatch(found, required, report( + JSError.make(sourceName, n, TYPE_MISMATCH_WARNING, + formatFoundRequired(msg, found, required)))); + } + + private void registerMismatch(JSType found, JSType required, JSError error) { + // Don't register a mismatch for differences in null or undefined or if the + // code didn't downcast. + found = found.restrictByNotNullOrUndefined(); + required = required.restrictByNotNullOrUndefined(); + if (found.isSubtype(required) || required.isSubtype(found)) { + return; + } + + mismatches.add(new TypeMismatch(found, required, error)); + if (found.isFunctionType() && + required.isFunctionType()) { + FunctionType fnTypeA = found.toMaybeFunctionType(); + FunctionType fnTypeB = required.toMaybeFunctionType(); + Iterator paramItA = fnTypeA.getParameters().iterator(); + Iterator paramItB = fnTypeB.getParameters().iterator(); + while (paramItA.hasNext() && paramItB.hasNext()) { + registerIfMismatch(paramItA.next().getJSType(), + paramItB.next().getJSType(), error); + } + + registerIfMismatch( + fnTypeA.getReturnType(), fnTypeB.getReturnType(), error); + } + } + + private void registerIfMismatch( + JSType found, JSType required, JSError error) { + if (found != null && required != null && + !found.isSubtype(required)) { + registerMismatch(found, required, error); + } + } + + /** + * Formats a found/required error message. + */ + private String formatFoundRequired(String description, JSType found, + JSType required) { + return MessageFormat.format(FOUND_REQUIRED, description, found, required); + } + + /** + * Given a node, get a human-readable name for the type of that node so + * that will be easy for the programmer to find the original declaration. + * + * For example, if SubFoo's property "bar" might have the human-readable + * name "Foo.prototype.bar". + * + * @param n The node. + * @param dereference If true, the type of the node will be dereferenced + * to an Object type, if possible. + */ + String getReadableJSTypeName(Node n, boolean dereference) { + // If we're analyzing a GETPROP, the property may be inherited by the + // prototype chain. So climb the prototype chain and find out where + // the property was originally defined. + if (n.isGetProp()) { + ObjectType objectType = getJSType(n.getFirstChild()).dereference(); + if (objectType != null) { + String propName = n.getLastChild().getString(); + if (objectType.getConstructor() != null && + objectType.getConstructor().isInterface()) { + objectType = FunctionType.getTopDefiningInterface( + objectType, propName); + } else { + // classes + while (objectType != null && !objectType.hasOwnProperty(propName)) { + objectType = objectType.getImplicitPrototype(); + } + } + + // Don't show complex function names or anonymous types. + // Instead, try to get a human-readable type name. + if (objectType != null && + (objectType.getConstructor() != null || + objectType.isFunctionPrototypeType())) { + return objectType.toString() + "." + propName; + } + } + } + + JSType type = getJSType(n); + if (dereference) { + ObjectType dereferenced = type.dereference(); + if (dereferenced != null) { + type = dereferenced; + } + } + + String qualifiedName = n.getQualifiedName(); + if (type.isFunctionPrototypeType() || + (type.toObjectType() != null && + type.toObjectType().getConstructor() != null)) { + return type.toString(); + } else if (qualifiedName != null) { + return qualifiedName; + } else if (type.isFunctionType()) { + // Don't show complex function names. + return "function"; + } else { + return type.toString(); + } + } + + /** + * This method gets the JSType from the Node argument and verifies that it is + * present. + */ + private JSType getJSType(Node n) { + JSType jsType = n.getJSType(); + if (jsType == null) { + // TODO(user): This branch indicates a compiler bug, not worthy of + // halting the compilation but we should log this and analyze to track + // down why it happens. This is not critical and will be resolved over + // time as the type checker is extended. + return getNativeType(UNKNOWN_TYPE); + } else { + return jsType; + } + } + + private JSType getNativeType(JSTypeNative typeId) { + return typeRegistry.getNativeType(typeId); + } + + private JSError report(JSError error) { + if (shouldReport) { + compiler.report(error); + } + return error; + } + + /** + * Signals that the first type and the second type have been + * used interchangeably. + * + * Type-based optimizations should take this into account + * so that they don't wreck code with type warnings. + */ + static class TypeMismatch { + final JSType typeA; + final JSType typeB; + final JSError src; + + /** + * It's the responsibility of the class that creates the + * {@code TypeMismatch} to ensure that {@code a} and {@code b} are + * non-matching types. + */ + TypeMismatch(JSType a, JSType b, JSError src) { + this.typeA = a; + this.typeB = b; + this.src = src; + } + + @Override public boolean equals(Object object) { + if (object instanceof TypeMismatch) { + TypeMismatch that = (TypeMismatch) object; + return (that.typeA.isEquivalentTo(this.typeA) + && that.typeB.isEquivalentTo(this.typeB)) + || (that.typeB.isEquivalentTo(this.typeA) + && that.typeA.isEquivalentTo(this.typeB)); + } + return false; + } + + @Override public int hashCode() { + return Objects.hashCode(typeA, typeB); + } + + @Override public String toString() { + return "(" + typeA + ", " + typeB + ")"; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypedCodeGenerator.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypedCodeGenerator.java new file mode 100644 index 0000000..64c3d95 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypedCodeGenerator.java @@ -0,0 +1,215 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Sets; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.ObjectType; + +import java.util.Set; + +/** + * A code generator that outputs type annotations for functions and + * constructors. + */ +class TypedCodeGenerator extends CodeGenerator { + TypedCodeGenerator(CodeConsumer consumer, CompilerOptions options) { + super(consumer, options); + } + + @Override + void add(Node n, Context context) { + Node parent = n.getParent(); + if (parent != null + && (parent.isBlock() + || parent.isScript())) { + if (n.isFunction()) { + add(getFunctionAnnotation(n)); + } else if (n.isExprResult() + && n.getFirstChild().isAssign()) { + Node rhs = n.getFirstChild().getLastChild(); + add(getTypeAnnotation(rhs)); + } else if (n.isVar() + && n.getFirstChild().getFirstChild() != null) { + add(getTypeAnnotation(n.getFirstChild().getFirstChild())); + } + } + + super.add(n, context); + } + + private String getTypeAnnotation(Node node) { + // Only add annotations for things with JSDoc, or function literals. + JSDocInfo jsdoc = NodeUtil.getBestJSDocInfo(node); + if (jsdoc == null && !node.isFunction()) { + return ""; + } + + JSType type = node.getJSType(); + if (type == null) { + return ""; + } else if (type.isFunctionType()) { + return getFunctionAnnotation(node); + } else if (type.isEnumType()) { + return "/** @enum {" + + type.toMaybeEnumType().getElementsType().toAnnotationString() + + "} */\n"; + } else if (!type.isUnknownType() + && !type.isEmptyType() + && !type.isVoidType() + && !type.isFunctionPrototypeType()) { + return "/** @type {" + node.getJSType().toAnnotationString() + "} */\n"; + } else { + return ""; + } + } + + /** + * @param fnNode A node for a function for which to generate a type annotation + */ + private String getFunctionAnnotation(Node fnNode) { + Preconditions.checkState(fnNode.isFunction()); + StringBuilder sb = new StringBuilder("/**\n"); + + JSType type = fnNode.getJSType(); + + if (type == null || type.isUnknownType()) { + return ""; + } + + FunctionType funType = type.toMaybeFunctionType(); + + // We need to use the child nodes of the function as the nodes for the + // parameters of the function type do not have the real parameter names. + // FUNCTION + // NAME + // LP + // NAME param1 + // NAME param2 + if (fnNode != null) { + Node paramNode = NodeUtil.getFunctionParameters(fnNode).getFirstChild(); + + // Param types + for (Node n : funType.getParameters()) { + // Bail out if the paramNode is not there. + if (paramNode == null) { + break; + } + sb.append(" * "); + appendAnnotation(sb, "param", getParameterNodeJSDocType(n)); + sb.append(" ") + .append(paramNode.getString()) + .append("\n"); + paramNode = paramNode.getNext(); + } + } + + // Return type + JSType retType = funType.getReturnType(); + if (retType != null && !retType.isUnknownType() && !retType.isEmptyType()) { + sb.append(" * "); + appendAnnotation(sb, "return", retType.toAnnotationString()); + sb.append("\n"); + } + + // Constructor/interface + if (funType.isConstructor() || funType.isInterface()) { + + FunctionType superConstructor = funType.getSuperClassConstructor(); + + if (superConstructor != null) { + ObjectType superInstance = + funType.getSuperClassConstructor().getInstanceType(); + if (!superInstance.toString().equals("Object")) { + sb.append(" * "); + appendAnnotation(sb, "extends", superInstance.toAnnotationString()); + sb.append("\n"); + } + } + + if (funType.isInterface()) { + for (ObjectType interfaceType : funType.getExtendedInterfaces()) { + sb.append(" * "); + appendAnnotation(sb, "extends", interfaceType.toAnnotationString()); + sb.append("\n"); + } + } + + // Avoid duplicates, add implemented type to a set first + Set interfaces = Sets.newTreeSet(); + for (ObjectType interfaze : funType.getImplementedInterfaces()) { + interfaces.add(interfaze.toAnnotationString()); + } + for (String interfaze : interfaces) { + sb.append(" * "); + appendAnnotation(sb, "implements", interfaze); + sb.append("\n"); + } + + if (funType.isConstructor()) { + sb.append(" * @constructor\n"); + } else if (funType.isInterface()) { + sb.append(" * @interface\n"); + } + } + + if (fnNode != null && fnNode.getBooleanProp(Node.IS_DISPATCHER)) { + sb.append(" * @javadispatch\n"); + } + + sb.append(" */\n"); + return sb.toString(); + } + + private void appendAnnotation(StringBuilder sb, String name, String type) { + sb.append("@").append(name).append(" {").append(type).append("}"); + } + + /** + * Creates a JSDoc-suitable String representation the type of a parameter. + * + * @param parameterNode The parameter node. + */ + private String getParameterNodeJSDocType(Node parameterNode) { + JSType parameterType = parameterNode.getJSType(); + String typeString; + + // Emit unknown types as '*' (AllType) since '?' (UnknownType) is not + // a valid JSDoc type. + if (parameterType.isUnknownType()) { + typeString = "*"; + } else { + // Fix-up optional and vararg parameters to match JSDoc type language + if (parameterNode.isOptionalArg()) { + typeString = + parameterType.restrictByNotNullOrUndefined().toAnnotationString() + + "="; + } else if (parameterNode.isVarArgs()) { + typeString = "..." + + parameterType.restrictByNotNullOrUndefined().toAnnotationString(); + } else { + typeString = parameterType.toAnnotationString(); + } + } + + return typeString; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypedScopeCreator.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypedScopeCreator.java new file mode 100644 index 0000000..461cddc --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/TypedScopeCreator.java @@ -0,0 +1,2087 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import static com.google.javascript.jscomp.TypeCheck.ENUM_NOT_CONSTANT; +import static com.google.javascript.jscomp.TypeCheck.MULTIPLE_VAR_DEF; +import static com.google.javascript.rhino.jstype.JSTypeNative.ARRAY_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.DATE_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.ERROR_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.EVAL_ERROR_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.FUNCTION_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.FUNCTION_INSTANCE_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.GLOBAL_THIS; +import static com.google.javascript.rhino.jstype.JSTypeNative.NO_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NULL_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_OBJECT_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.RANGE_ERROR_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.REFERENCE_ERROR_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.REGEXP_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.REGEXP_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_OBJECT_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.SYNTAX_ERROR_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.TYPE_ERROR_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.U2U_CONSTRUCTOR_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.URI_ERROR_FUNCTION_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multiset; +import com.google.javascript.jscomp.CodingConvention.DelegateRelationship; +import com.google.javascript.jscomp.CodingConvention.ObjectLiteralCast; +import com.google.javascript.jscomp.CodingConvention.SubclassRelationship; +import com.google.javascript.jscomp.CodingConvention.SubclassType; +import com.google.javascript.jscomp.FunctionTypeBuilder.AstFunctionContents; +import com.google.javascript.jscomp.NodeTraversal.AbstractScopedCallback; +import com.google.javascript.jscomp.NodeTraversal.AbstractShallowStatementCallback; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.ErrorReporter; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.EnumType; +import com.google.javascript.rhino.jstype.FunctionParamBuilder; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.Property; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nullable; + +/** + * Creates the symbol table of variables available in the current scope and + * their types. + * + * Scopes created by this class are very different from scopes created + * by the syntactic scope creator. These scopes have type information, and + * include some qualified names in addition to variables + * (like Class.staticMethod). + * + * When building scope information, also declares relevant information + * about types in the type registry. + * + * @author nicksantos@google.com (Nick Santos) + */ +final class TypedScopeCreator implements ScopeCreator { + /** + * A suffix for naming delegate proxies differently from their base. + */ + static final String DELEGATE_PROXY_SUFFIX = + ObjectType.createDelegateSuffix("Proxy"); + + static final DiagnosticType MALFORMED_TYPEDEF = + DiagnosticType.warning( + "JSC_MALFORMED_TYPEDEF", + "Typedef for {0} does not have any type information"); + + static final DiagnosticType ENUM_INITIALIZER = + DiagnosticType.warning( + "JSC_ENUM_INITIALIZER_NOT_ENUM", + "enum initializer must be an object literal or an enum"); + + static final DiagnosticType CTOR_INITIALIZER = + DiagnosticType.warning( + "JSC_CTOR_INITIALIZER_NOT_CTOR", + "Constructor {0} must be initialized at declaration"); + + static final DiagnosticType IFACE_INITIALIZER = + DiagnosticType.warning( + "JSC_IFACE_INITIALIZER_NOT_IFACE", + "Interface {0} must be initialized at declaration"); + + static final DiagnosticType CONSTRUCTOR_EXPECTED = + DiagnosticType.warning( + "JSC_REFLECT_CONSTRUCTOR_EXPECTED", + "Constructor expected as first argument"); + + static final DiagnosticType UNKNOWN_LENDS = + DiagnosticType.warning( + "JSC_UNKNOWN_LENDS", + "Variable {0} not declared before @lends annotation."); + + static final DiagnosticType LENDS_ON_NON_OBJECT = + DiagnosticType.warning( + "JSC_LENDS_ON_NON_OBJECT", + "May only lend properties to object types. {0} has type {1}."); + + private final AbstractCompiler compiler; + private final ErrorReporter typeParsingErrorReporter; + private final TypeValidator validator; + private final CodingConvention codingConvention; + private final JSTypeRegistry typeRegistry; + private final List delegateProxyPrototypes = Lists.newArrayList(); + private final Map delegateCallingConventions = + Maps.newHashMap(); + + // Simple properties inferred about functions. + private final Map functionAnalysisResults = + Maps.newHashMap(); + + // For convenience + private final ObjectType unknownType; + + /** + * Defer attachment of types to nodes until all type names + * have been resolved. Then, we can resolve the type and attach it. + */ + private class DeferredSetType { + final Node node; + final JSType type; + + DeferredSetType(Node node, JSType type) { + Preconditions.checkNotNull(node); + Preconditions.checkNotNull(type); + this.node = node; + this.type = type; + + // Other parts of this pass may read off the node. + // (like when we set the LHS of an assign with a typed RHS function.) + node.setJSType(type); + } + + void resolve(Scope scope) { + node.setJSType(type.resolve(typeParsingErrorReporter, scope)); + } + } + + TypedScopeCreator(AbstractCompiler compiler) { + this(compiler, compiler.getCodingConvention()); + } + + TypedScopeCreator(AbstractCompiler compiler, + CodingConvention codingConvention) { + this.compiler = compiler; + this.validator = compiler.getTypeValidator(); + this.codingConvention = codingConvention; + this.typeRegistry = compiler.getTypeRegistry(); + this.typeParsingErrorReporter = typeRegistry.getErrorReporter(); + this.unknownType = typeRegistry.getNativeObjectType(UNKNOWN_TYPE); + } + + /** + * Creates a scope with all types declared. Declares newly discovered types + * and type properties in the type registry. + */ + @Override + public Scope createScope(Node root, Scope parent) { + // Constructing the global scope is very different than constructing + // inner scopes, because only global scopes can contain named classes that + // show up in the type registry. + Scope newScope = null; + AbstractScopeBuilder scopeBuilder = null; + if (parent == null) { + JSType globalThis = + typeRegistry.getNativeObjectType(JSTypeNative.GLOBAL_THIS); + + // Mark the main root, the externs root, and the src root + // with the global this type. + root.setJSType(globalThis); + root.getFirstChild().setJSType(globalThis); + root.getLastChild().setJSType(globalThis); + + // Run a first-order analysis over the syntax tree. + (new FirstOrderFunctionAnalyzer(compiler, functionAnalysisResults)) + .process(root.getFirstChild(), root.getLastChild()); + + // Find all the classes in the global scope. + newScope = createInitialScope(root); + + GlobalScopeBuilder globalScopeBuilder = new GlobalScopeBuilder(newScope); + scopeBuilder = globalScopeBuilder; + NodeTraversal.traverse(compiler, root, scopeBuilder); + } else { + newScope = new Scope(parent, root); + LocalScopeBuilder localScopeBuilder = new LocalScopeBuilder(newScope); + scopeBuilder = localScopeBuilder; + localScopeBuilder.build(); + } + + scopeBuilder.resolveStubDeclarations(); + scopeBuilder.resolveTypes(); + + // Gather the properties in each function that we found in the + // global scope, if that function has a @this type that we can + // build properties on. + for (Node functionNode : scopeBuilder.nonExternFunctions) { + JSType type = functionNode.getJSType(); + if (type != null && type.isFunctionType()) { + FunctionType fnType = type.toMaybeFunctionType(); + JSType fnThisType = fnType.getTypeOfThis(); + if (!fnThisType.isUnknownType()) { + NodeTraversal.traverse(compiler, functionNode.getLastChild(), + scopeBuilder.new CollectProperties(fnThisType)); + } + } + } + + if (parent == null) { + codingConvention.defineDelegateProxyPrototypeProperties( + typeRegistry, newScope, delegateProxyPrototypes, + delegateCallingConventions); + } + return newScope; + } + + /** + * Patches a given global scope by removing variables previously declared in + * a script and re-traversing a new version of that script. + * + * @param globalScope The global scope generated by {@code createScope}. + * @param scriptRoot The script that is modified. + */ + void patchGlobalScope(Scope globalScope, Node scriptRoot) { + // Preconditions: This is supposed to be called only on (named) SCRIPT nodes + // and a global typed scope should have been generated already. + Preconditions.checkState(scriptRoot.isScript()); + Preconditions.checkNotNull(globalScope); + Preconditions.checkState(globalScope.isGlobal()); + + String scriptName = NodeUtil.getSourceName(scriptRoot); + Preconditions.checkNotNull(scriptName); + for (Node node : ImmutableList.copyOf(functionAnalysisResults.keySet())) { + if (scriptName.equals(NodeUtil.getSourceName(node))) { + functionAnalysisResults.remove(node); + } + } + + (new FirstOrderFunctionAnalyzer( + compiler, functionAnalysisResults)).process(null, scriptRoot); + + // TODO(bashir): Variable declaration is not the only side effect of last + // global scope generation but here we only wipe that part off! + + // Remove all variables that were previously declared in this scripts. + // First find all vars to remove then remove them because of iterator! + Iterator varIter = globalScope.getVars(); + List varsToRemove = Lists.newArrayList(); + while (varIter.hasNext()) { + Var oldVar = varIter.next(); + if (scriptName.equals(oldVar.getInputName())) { + varsToRemove.add(oldVar); + } + } + for (Var var : varsToRemove) { + globalScope.undeclare(var); + globalScope.getTypeOfThis().toObjectType().removeProperty(var.getName()); + } + + // Now re-traverse the given script. + GlobalScopeBuilder scopeBuilder = new GlobalScopeBuilder(globalScope); + NodeTraversal.traverse(compiler, scriptRoot, scopeBuilder); + } + + /** + * Create the outermost scope. This scope contains native binding such as + * {@code Object}, {@code Date}, etc. + */ + @VisibleForTesting + Scope createInitialScope(Node root) { + + NodeTraversal.traverse( + compiler, root, new DiscoverEnumsAndTypedefs(typeRegistry)); + + Scope s = Scope.createGlobalScope(root); + declareNativeFunctionType(s, ARRAY_FUNCTION_TYPE); + declareNativeFunctionType(s, BOOLEAN_OBJECT_FUNCTION_TYPE); + declareNativeFunctionType(s, DATE_FUNCTION_TYPE); + declareNativeFunctionType(s, ERROR_FUNCTION_TYPE); + declareNativeFunctionType(s, EVAL_ERROR_FUNCTION_TYPE); + declareNativeFunctionType(s, FUNCTION_FUNCTION_TYPE); + declareNativeFunctionType(s, NUMBER_OBJECT_FUNCTION_TYPE); + declareNativeFunctionType(s, OBJECT_FUNCTION_TYPE); + declareNativeFunctionType(s, RANGE_ERROR_FUNCTION_TYPE); + declareNativeFunctionType(s, REFERENCE_ERROR_FUNCTION_TYPE); + declareNativeFunctionType(s, REGEXP_FUNCTION_TYPE); + declareNativeFunctionType(s, STRING_OBJECT_FUNCTION_TYPE); + declareNativeFunctionType(s, SYNTAX_ERROR_FUNCTION_TYPE); + declareNativeFunctionType(s, TYPE_ERROR_FUNCTION_TYPE); + declareNativeFunctionType(s, URI_ERROR_FUNCTION_TYPE); + declareNativeValueType(s, "undefined", VOID_TYPE); + + // There is no longer a need to special case ActiveXObject + // but this remains here until we can get the extern forks + // cleaned up. + declareNativeValueType(s, "ActiveXObject", FUNCTION_INSTANCE_TYPE); + + return s; + } + + private void declareNativeFunctionType(Scope scope, JSTypeNative tId) { + FunctionType t = typeRegistry.getNativeFunctionType(tId); + declareNativeType(scope, t.getInstanceType().getReferenceName(), t); + declareNativeType( + scope, t.getPrototype().getReferenceName(), t.getPrototype()); + } + + private void declareNativeValueType(Scope scope, String name, + JSTypeNative tId) { + declareNativeType(scope, name, typeRegistry.getNativeType(tId)); + } + + private void declareNativeType(Scope scope, String name, JSType t) { + scope.declare(name, null, t, null, false); + } + + private static class DiscoverEnumsAndTypedefs + extends AbstractShallowStatementCallback { + private final JSTypeRegistry registry; + + DiscoverEnumsAndTypedefs(JSTypeRegistry registry) { + this.registry = registry; + } + + @Override + public void visit(NodeTraversal t, Node node, Node parent) { + Node nameNode = null; + switch (node.getType()) { + case Token.VAR: + for (Node child = node.getFirstChild(); + child != null; child = child.getNext()) { + identifyNameNode( + child, child.getFirstChild(), + NodeUtil.getBestJSDocInfo(child)); + } + break; + case Token.EXPR_RESULT: + Node firstChild = node.getFirstChild(); + if (firstChild.isAssign()) { + identifyNameNode( + firstChild.getFirstChild(), firstChild.getLastChild(), + firstChild.getJSDocInfo()); + } else { + identifyNameNode( + firstChild, null, firstChild.getJSDocInfo()); + } + break; + } + } + + private void identifyNameNode( + Node nameNode, Node valueNode, JSDocInfo info) { + if (nameNode.isQualifiedName()) { + if (info != null) { + if (info.hasEnumParameterType()) { + registry.identifyNonNullableName(nameNode.getQualifiedName()); + } else if (info.hasTypedefType()) { + registry.identifyNonNullableName(nameNode.getQualifiedName()); + } + } + } + } + } + + private JSType getNativeType(JSTypeNative nativeType) { + return typeRegistry.getNativeType(nativeType); + } + + private abstract class AbstractScopeBuilder + implements NodeTraversal.Callback { + + /** + * The scope that we're building. + */ + final Scope scope; + + private final List deferredSetTypes = + Lists.newArrayList(); + + /** + * Functions that we found in the global scope and not in externs. + */ + private final List nonExternFunctions = Lists.newArrayList(); + + /** + * Object literals with a @lends annotation aren't analyzed until we + * reach the root of the statement they're defined in. + * + * This ensures that if there are any @lends annotations on the object + * literals, the type on the @lends annotation resolves correctly. + * + * For more information, see + * http://code.google.com/p/closure-compiler/issues/detail?id=314 + */ + private List lentObjectLiterals = null; + + /** + * Type-less stubs. + * + * If at the end of traversal, we still don't have types for these + * stubs, then we should declare UNKNOWN types. + */ + private final List stubDeclarations = + Lists.newArrayList(); + + /** + * The current source file that we're in. + */ + private String sourceName = null; + + /** + * The InputId of the current node. + */ + private InputId inputId; + + private AbstractScopeBuilder(Scope scope) { + this.scope = scope; + } + + void setDeferredType(Node node, JSType type) { + deferredSetTypes.add(new DeferredSetType(node, type)); + } + + void resolveTypes() { + // Resolve types and attach them to nodes. + for (DeferredSetType deferred : deferredSetTypes) { + deferred.resolve(scope); + } + + // Resolve types and attach them to scope slots. + Iterator vars = scope.getVars(); + while (vars.hasNext()) { + vars.next().resolveType(typeParsingErrorReporter); + } + + // Tell the type registry that any remaining types + // are unknown. + typeRegistry.resolveTypesInScope(scope); + } + + @Override + public final boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + inputId = t.getInputId(); + if (n.isFunction() || + n.isScript()) { + Preconditions.checkNotNull(inputId); + sourceName = NodeUtil.getSourceName(n); + } + + // We do want to traverse the name of a named function, but we don't + // want to traverse the arguments or body. + boolean descend = parent == null || !parent.isFunction() || + n == parent.getFirstChild() || parent == scope.getRootNode(); + + if (descend) { + // Handle hoisted functions on pre-order traversal, so that they + // get hit before other things in the scope. + if (NodeUtil.isStatementParent(n)) { + for (Node child = n.getFirstChild(); + child != null; + child = child.getNext()) { + if (NodeUtil.isHoistedFunctionDeclaration(child)) { + defineFunctionLiteral(child); + } + } + } + } + + return descend; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + inputId = t.getInputId(); + attachLiteralTypes(t, n); + + switch (n.getType()) { + case Token.CALL: + checkForClassDefiningCalls(t, n, parent); + checkForCallingConventionDefiningCalls(n, delegateCallingConventions); + break; + + case Token.FUNCTION: + if (t.getInput() == null || !t.getInput().isExtern()) { + nonExternFunctions.add(n); + } + + // Hoisted functions are handled during pre-traversal. + if (!NodeUtil.isHoistedFunctionDeclaration(n)) { + defineFunctionLiteral(n); + } + break; + + case Token.ASSIGN: + // Handle initialization of properties. + Node firstChild = n.getFirstChild(); + if (firstChild.isGetProp() && + firstChild.isQualifiedName()) { + maybeDeclareQualifiedName(t, n.getJSDocInfo(), + firstChild, n, firstChild.getNext()); + } + break; + + case Token.CATCH: + defineCatch(n); + break; + + case Token.VAR: + defineVar(n); + break; + + case Token.GETPROP: + // Handle stubbed properties. + if (parent.isExprResult() && + n.isQualifiedName()) { + maybeDeclareQualifiedName(t, n.getJSDocInfo(), n, parent, null); + } + break; + } + + // Analyze any @lends object literals in this statement. + if (n.getParent() != null && NodeUtil.isStatement(n) && + lentObjectLiterals != null) { + for (Node objLit : lentObjectLiterals) { + defineObjectLiteral(objLit); + } + lentObjectLiterals.clear(); + } + } + + private void attachLiteralTypes(NodeTraversal t, Node n) { + switch (n.getType()) { + case Token.NULL: + n.setJSType(getNativeType(NULL_TYPE)); + break; + + case Token.VOID: + n.setJSType(getNativeType(VOID_TYPE)); + break; + + case Token.STRING: + n.setJSType(getNativeType(STRING_TYPE)); + break; + + case Token.NUMBER: + n.setJSType(getNativeType(NUMBER_TYPE)); + break; + + case Token.TRUE: + case Token.FALSE: + n.setJSType(getNativeType(BOOLEAN_TYPE)); + break; + + case Token.REGEXP: + n.setJSType(getNativeType(REGEXP_TYPE)); + break; + + case Token.OBJECTLIT: + JSDocInfo info = n.getJSDocInfo(); + if (info != null && + info.getLendsName() != null) { + if (lentObjectLiterals == null) { + lentObjectLiterals = Lists.newArrayList(); + } + lentObjectLiterals.add(n); + } else { + defineObjectLiteral(n); + } + break; + + // NOTE(nicksantos): If we ever support Array tuples, + // we will need to put ARRAYLIT here as well. + } + } + + private void defineObjectLiteral(Node objectLit) { + // Handle the @lends annotation. + JSType type = null; + JSDocInfo info = objectLit.getJSDocInfo(); + if (info != null && info.getLendsName() != null) { + String lendsName = info.getLendsName(); + Var lendsVar = scope.getVar(lendsName); + if (lendsVar == null) { + compiler.report( + JSError.make(sourceName, objectLit, UNKNOWN_LENDS, lendsName)); + } else { + type = lendsVar.getType(); + if (type == null) { + type = unknownType; + } + if (!type.isSubtype(typeRegistry.getNativeType(OBJECT_TYPE))) { + compiler.report( + JSError.make(sourceName, objectLit, LENDS_ON_NON_OBJECT, + lendsName, type.toString())); + type = null; + } else { + objectLit.setJSType(type); + } + } + } + + info = NodeUtil.getBestJSDocInfo(objectLit); + Node lValue = NodeUtil.getBestLValue(objectLit); + String lValueName = NodeUtil.getBestLValueName(lValue); + boolean createdEnumType = false; + if (info != null && info.hasEnumParameterType()) { + type = createEnumTypeFromNodes(objectLit, lValueName, info, lValue); + createdEnumType = true; + } + + if (type == null) { + type = typeRegistry.createAnonymousObjectType(info); + } + + setDeferredType(objectLit, type); + + // If this is an enum, the properties were already taken care of above. + processObjectLitProperties( + objectLit, ObjectType.cast(objectLit.getJSType()), !createdEnumType); + } + + /** + * Process an object literal and all the types on it. + * @param objLit The OBJECTLIT node. + * @param objLitType The type of the OBJECTLIT node. This might be a named + * type, because of the lends annotation. + * @param declareOnOwner If true, declare properties on the objLitType as + * well. If false, the caller should take care of this. + */ + void processObjectLitProperties( + Node objLit, ObjectType objLitType, + boolean declareOnOwner) { + for (Node keyNode = objLit.getFirstChild(); keyNode != null; + keyNode = keyNode.getNext()) { + Node value = keyNode.getFirstChild(); + String memberName = NodeUtil.getObjectLitKeyName(keyNode); + JSDocInfo info = keyNode.getJSDocInfo(); + JSType valueType = + getDeclaredType(keyNode.getSourceFileName(), info, keyNode, value); + JSType keyType = objLitType.isEnumType() ? + objLitType.toMaybeEnumType().getElementsType() : + NodeUtil.getObjectLitKeyTypeFromValueType(keyNode, valueType); + + // Try to declare this property in the current scope if it + // has an authoritative name. + String qualifiedName = NodeUtil.getBestLValueName(keyNode); + if (qualifiedName != null) { + boolean inferred = keyType == null; + defineSlot(keyNode, objLit, qualifiedName, keyType, inferred); + } else if (keyType != null) { + setDeferredType(keyNode, keyType); + } + + if (keyType != null && objLitType != null && declareOnOwner) { + // Declare this property on its object literal. + boolean isExtern = keyNode.isFromExterns(); + objLitType.defineDeclaredProperty(memberName, keyType, keyNode); + } + } + } + + /** + * Returns the type specified in a JSDoc annotation near a GETPROP or NAME. + * + * Extracts type information from either the {@code @type} tag or from + * the {@code @return} and {@code @param} tags. + */ + private JSType getDeclaredTypeInAnnotation(String sourceName, + Node node, JSDocInfo info) { + JSType jsType = null; + Node objNode = + node.isGetProp() ? node.getFirstChild() : + NodeUtil.isObjectLitKey(node, node.getParent()) ? node.getParent() : + null; + if (info != null) { + if (info.hasType()) { + jsType = info.getType().evaluate(scope, typeRegistry); + } else if (FunctionTypeBuilder.isFunctionTypeDeclaration(info)) { + String fnName = node.getQualifiedName(); + jsType = createFunctionTypeFromNodes( + null, fnName, info, node); + } + } + return jsType; + } + + /** + * Asserts that it's OK to define this node's name. + * The node should have a source name and be of the specified type. + */ + void assertDefinitionNode(Node n, int type) { + Preconditions.checkState(sourceName != null); + Preconditions.checkState(n.getType() == type); + } + + /** + * Defines a catch parameter. + */ + void defineCatch(Node n) { + assertDefinitionNode(n, Token.CATCH); + Node catchName = n.getFirstChild(); + defineSlot(catchName, n, + getDeclaredType( + sourceName, catchName.getJSDocInfo(), catchName, null)); + } + + /** + * Defines a VAR initialization. + */ + void defineVar(Node n) { + assertDefinitionNode(n, Token.VAR); + JSDocInfo info = n.getJSDocInfo(); + if (n.hasMoreThanOneChild()) { + if (info != null) { + // multiple children + compiler.report(JSError.make(sourceName, n, MULTIPLE_VAR_DEF)); + } + for (Node name : n.children()) { + defineName(name, n, name.getJSDocInfo()); + } + } else { + Node name = n.getFirstChild(); + defineName(name, n, (info != null) ? info : name.getJSDocInfo()); + } + } + + /** + * Defines a function literal. + */ + void defineFunctionLiteral(Node n) { + assertDefinitionNode(n, Token.FUNCTION); + + // Determine the name and JSDocInfo and l-value for the function. + // Any of these may be null. + Node lValue = NodeUtil.getBestLValue(n); + JSDocInfo info = NodeUtil.getBestJSDocInfo(n); + String functionName = NodeUtil.getBestLValueName(lValue); + FunctionType functionType = + createFunctionTypeFromNodes(n, functionName, info, lValue); + + // Assigning the function type to the function node + setDeferredType(n, functionType); + + // Declare this symbol in the current scope iff it's a function + // declaration. Otherwise, the declaration will happen in other + // code paths. + if (NodeUtil.isFunctionDeclaration(n)) { + defineSlot(n.getFirstChild(), n, functionType); + } + } + + /** + * Defines a variable based on the {@link Token#NAME} node passed. + * @param name The {@link Token#NAME} node. + * @param var The parent of the {@code name} node, which must be a + * {@link Token#VAR} node. + * @param info the {@link JSDocInfo} information relating to this + * {@code name} node. + */ + private void defineName(Node name, Node var, JSDocInfo info) { + Node value = name.getFirstChild(); + + // variable's type + JSType type = getDeclaredType(sourceName, info, name, value); + if (type == null) { + // The variable's type will be inferred. + type = name.isFromExterns() ? unknownType : null; + } + defineSlot(name, var, type); + } + + /** + * If a variable is assigned a function literal in the global scope, + * make that a declared type (even if there's no doc info). + * There's only one exception to this rule: + * if the return type is inferred, and we're in a local + * scope, we should assume the whole function is inferred. + */ + private boolean shouldUseFunctionLiteralType( + FunctionType type, JSDocInfo info, Node lValue) { + if (info != null) { + return true; + } + if (lValue != null && + NodeUtil.isObjectLitKey(lValue, lValue.getParent())) { + return false; + } + return scope.isGlobal() || !type.isReturnTypeInferred(); + } + + /** + * Creates a new function type, based on the given nodes. + * + * This handles two cases that are semantically very different, but + * are not mutually exclusive: + * - A function literal that needs a type attached to it. + * - An assignment expression with function-type info in the JsDoc. + * + * All parameters are optional, and we will do the best we can to create + * a function type. + * + * This function will always create a function type, so only call it if + * you're sure that's what you want. + * + * @param rValue The function node. + * @param name the function's name + * @param info the {@link JSDocInfo} attached to the function definition + * @param lvalueNode The node where this function is being + * assigned. For example, {@code A.prototype.foo = ...} would be used to + * determine that this function is a method of A.prototype. May be + * null to indicate that this is not being assigned to a qualified name. + */ + private FunctionType createFunctionTypeFromNodes( + @Nullable Node rValue, + @Nullable String name, + @Nullable JSDocInfo info, + @Nullable Node lvalueNode) { + + FunctionType functionType = null; + + // Global ctor aliases should be registered with the type registry. + if (rValue != null && rValue.isQualifiedName() && scope.isGlobal()) { + Var var = scope.getVar(rValue.getQualifiedName()); + if (var != null && var.getType() != null && + var.getType().isFunctionType()) { + FunctionType aliasedType = var.getType().toMaybeFunctionType(); + if ((aliasedType.isConstructor() || aliasedType.isInterface()) && + !aliasedType.isNativeObjectType()) { + functionType = aliasedType; + + if (name != null && scope.isGlobal()) { + typeRegistry.declareType(name, functionType.getInstanceType()); + } + } + } + } + + if (functionType == null) { + Node errorRoot = rValue == null ? lvalueNode : rValue; + boolean isFnLiteral = + rValue != null && rValue.isFunction(); + Node fnRoot = isFnLiteral ? rValue : null; + Node parametersNode = isFnLiteral ? + rValue.getFirstChild().getNext() : null; + Node fnBlock = isFnLiteral ? parametersNode.getNext() : null; + + if (info != null && info.hasType()) { + JSType type = info.getType().evaluate(scope, typeRegistry); + + // Known to be not null since we have the FUNCTION token there. + type = type.restrictByNotNullOrUndefined(); + if (type.isFunctionType()) { + functionType = type.toMaybeFunctionType(); + functionType.setJSDocInfo(info); + } + } + + if (functionType == null) { + // Find the type of any overridden function. + Node ownerNode = NodeUtil.getBestLValueOwner(lvalueNode); + String ownerName = NodeUtil.getBestLValueName(ownerNode); + Var ownerVar = null; + String propName = null; + ObjectType ownerType = null; + if (ownerName != null) { + ownerVar = scope.getVar(ownerName); + if (ownerVar != null) { + ownerType = ObjectType.cast(ownerVar.getType()); + } + if (name != null) { + propName = name.substring(ownerName.length() + 1); + } + } + + FunctionType overriddenType = null; + if (ownerType != null && propName != null) { + overriddenType = findOverriddenFunction(ownerType, propName); + } + + FunctionTypeBuilder builder = + new FunctionTypeBuilder(name, compiler, errorRoot, sourceName, + scope) + .setContents(getFunctionAnalysisResults(fnRoot)) + .inferFromOverriddenFunction(overriddenType, parametersNode) + .inferTemplateTypeName(info) + .inferReturnType(info) + .inferInheritance(info); + + // Infer the context type. + boolean searchedForThisType = false; + if (ownerType != null && ownerType.isFunctionPrototypeType() && + ownerType.getOwnerFunction().hasInstanceType()) { + builder.inferThisType( + info, ownerType.getOwnerFunction().getInstanceType()); + searchedForThisType = true; + } else if (ownerNode != null && ownerNode.isThis()) { + // If 'this' has a type, use that instead. + // This is a hack, necessary because CollectProperties (below) + // doesn't run with the scope that it's building, + // so scope.getTypeOfThis() will be wrong. + JSType injectedThisType = ownerNode.getJSType(); + builder.inferThisType( + info, + injectedThisType == null ? + scope.getTypeOfThis() : injectedThisType); + searchedForThisType = true; + } + + if (!searchedForThisType) { + builder.inferThisType(info); + } + + functionType = builder + .inferParameterTypes(parametersNode, info) + .buildAndRegister(); + } + } + + // all done + return functionType; + } + + /** + * Find the function that's being overridden on this type, if any. + */ + private FunctionType findOverriddenFunction( + ObjectType ownerType, String propName) { + // First, check to see if the property is implemented + // on a superclass. + JSType propType = ownerType.getPropertyType(propName); + if (propType != null && propType.isFunctionType()) { + return propType.toMaybeFunctionType(); + } else { + // If it's not, then check to see if it's implemented + // on an implemented interface. + for (ObjectType iface : + ownerType.getCtorImplementedInterfaces()) { + propType = iface.getPropertyType(propName); + if (propType != null && propType.isFunctionType()) { + return propType.toMaybeFunctionType(); + } + } + } + + return null; + } + + /** + * Creates a new enum type, based on the given nodes. + * + * This handles two cases that are semantically very different, but + * are not mutually exclusive: + * - An object literal that needs an enum type attached to it. + * - An assignment expression with an enum tag in the JsDoc. + * + * This function will always create an enum type, so only call it if + * you're sure that's what you want. + * + * @param rValue The node of the enum. + * @param name The enum's name + * @param info The {@link JSDocInfo} attached to the enum definition. + * @param lValueNode The node where this function is being + * assigned. + */ + private EnumType createEnumTypeFromNodes(Node rValue, String name, + JSDocInfo info, Node lValueNode) { + Preconditions.checkNotNull(info); + Preconditions.checkState(info.hasEnumParameterType()); + + EnumType enumType = null; + if (rValue != null && rValue.isQualifiedName()) { + // Handle an aliased enum. + Var var = scope.getVar(rValue.getQualifiedName()); + if (var != null && var.getType() instanceof EnumType) { + enumType = (EnumType) var.getType(); + } + } + + if (enumType == null) { + JSType elementsType = + info.getEnumParameterType().evaluate(scope, typeRegistry); + enumType = typeRegistry.createEnumType(name, rValue, elementsType); + + if (rValue != null && rValue.isObjectLit()) { + // collect enum elements + Node key = rValue.getFirstChild(); + while (key != null) { + String keyName = NodeUtil.getStringValue(key); + if (keyName == null) { + // GET and SET don't have a String value; + compiler.report( + JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName)); + } else if (!codingConvention.isValidEnumKey(keyName)) { + compiler.report( + JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName)); + } else { + enumType.defineElement(keyName, key); + } + key = key.getNext(); + } + } + } + + if (name != null && scope.isGlobal()) { + typeRegistry.declareType(name, enumType.getElementsType()); + } + + return enumType; + } + + /** + * Defines a typed variable. The defining node will be annotated with the + * variable's type or {@code null} if its type is inferred. + * @param name the defining node. It must be a {@link Token#NAME}. + * @param parent the {@code name}'s parent. + * @param type the variable's type. It may be {@code null}, in which case + * the variable's type will be inferred. + */ + private void defineSlot(Node name, Node parent, JSType type) { + defineSlot(name, parent, type, type == null); + } + + /** + * Defines a typed variable. The defining node will be annotated with the + * variable's type of {@link JSTypeNative#UNKNOWN_TYPE} if its type is + * inferred. + * + * Slots may be any variable or any qualified name in the global scope. + * + * @param n the defining NAME or GETPROP node. + * @param parent the {@code n}'s parent. + * @param type the variable's type. It may be {@code null} if + * {@code inferred} is {@code true}. + */ + void defineSlot(Node n, Node parent, JSType type, boolean inferred) { + Preconditions.checkArgument(inferred || type != null); + + // Only allow declarations of NAMEs and qualified names. + // Object literal keys will have to compute their names themselves. + if (n.isName()) { + Preconditions.checkArgument( + parent.isFunction() || + parent.isVar() || + parent.isParamList() || + parent.isCatch()); + } else { + Preconditions.checkArgument( + n.isGetProp() && + (parent.isAssign() || + parent.isExprResult())); + } + defineSlot(n, parent, n.getQualifiedName(), type, inferred); + } + + + /** + * Defines a symbol in the current scope. + * + * @param n the defining NAME or GETPROP or object literal key node. + * @param parent the {@code n}'s parent. + * @param variableName The name that this should be known by. + * @param type the variable's type. It may be {@code null} if + * {@code inferred} is {@code true}. + * @param inferred Whether the type is inferred or declared. + */ + void defineSlot(Node n, Node parent, String variableName, + JSType type, boolean inferred) { + Preconditions.checkArgument(!variableName.isEmpty()); + + boolean isGlobalVar = n.isName() && scope.isGlobal(); + boolean shouldDeclareOnGlobalThis = + isGlobalVar && + (parent.isVar() || + parent.isFunction()); + + // If n is a property, then we should really declare it in the + // scope where the root object appears. This helps out people + // who declare "global" names in an anonymous namespace. + Scope scopeToDeclareIn = scope; + if (n.isGetProp() && !scope.isGlobal() && + isQnameRootedInGlobalScope(n)) { + Scope globalScope = scope.getGlobalScope(); + + // don't try to declare in the global scope if there's + // already a symbol there with this name. + if (!globalScope.isDeclared(variableName, false)) { + scopeToDeclareIn = scope.getGlobalScope(); + } + } + + // The input may be null if we are working with a AST snippet. So read + // the extern info from the node. + boolean isExtern = n.isFromExterns(); + Var newVar = null; + + // declared in closest scope? + CompilerInput input = compiler.getInput(inputId); + if (scopeToDeclareIn.isDeclared(variableName, false)) { + Var oldVar = scopeToDeclareIn.getVar(variableName); + newVar = validator.expectUndeclaredVariable( + sourceName, input, n, parent, oldVar, variableName, type); + } else { + if (type != null) { + setDeferredType(n, type); + } + + newVar = + scopeToDeclareIn.declare(variableName, n, type, input, inferred); + + if (type instanceof EnumType) { + Node initialValue = newVar.getInitialValue(); + boolean isValidValue = initialValue != null && + (initialValue.isObjectLit() || + initialValue.isQualifiedName()); + if (!isValidValue) { + compiler.report(JSError.make(sourceName, n, ENUM_INITIALIZER)); + } + } + } + + // We need to do some additional work for constructors and interfaces. + FunctionType fnType = JSType.toMaybeFunctionType(type); + if (fnType != null && + // We don't want to look at empty function types. + !type.isEmptyType()) { + + // We want to make sure that when we declare a new instance type + // (with @constructor) that there's actually a ctor for it. + // This doesn't apply to structural constructors (like + // function(new:Array). Checking the constructed type against + // the variable name is a sufficient check for this. + if ((fnType.isConstructor() || fnType.isInterface()) && + variableName.equals(fnType.getReferenceName())) { + finishConstructorDefinition(n, variableName, fnType, scopeToDeclareIn, + input, newVar); + } + } + + if (shouldDeclareOnGlobalThis) { + ObjectType globalThis = + typeRegistry.getNativeObjectType(GLOBAL_THIS); + if (inferred) { + globalThis.defineInferredProperty(variableName, + type == null ? + getNativeType(JSTypeNative.NO_TYPE) : + type, + n); + } else { + globalThis.defineDeclaredProperty(variableName, type, n); + } + } + + if (isGlobalVar && "Window".equals(variableName) + && type != null + && type.isFunctionType() + && type.isConstructor()) { + FunctionType globalThisCtor = + typeRegistry.getNativeObjectType(GLOBAL_THIS).getConstructor(); + globalThisCtor.getInstanceType().clearCachedValues(); + globalThisCtor.getPrototype().clearCachedValues(); + globalThisCtor + .setPrototypeBasedOn((type.toMaybeFunctionType()).getInstanceType()); + } + } + + private void finishConstructorDefinition( + Node n, String variableName, FunctionType fnType, + Scope scopeToDeclareIn, CompilerInput input, Var newVar) { + // Declare var.prototype in the scope chain. + FunctionType superClassCtor = fnType.getSuperClassConstructor(); + Property prototypeSlot = fnType.getSlot("prototype"); + + // When we declare the function prototype implicitly, we + // want to make sure that the function and its prototype + // are declared at the same node. We also want to make sure + // that the if a symbol has both a Var and a JSType, they have + // the same node. + // + // This consistency is helpful to users of SymbolTable, + // because everything gets declared at the same place. + prototypeSlot.setNode(n); + + String prototypeName = variableName + ".prototype"; + + // There are some rare cases where the prototype will already + // be declared. See TypedScopeCreatorTest#testBogusPrototypeInit. + // Fortunately, other warnings will complain if this happens. + Var prototypeVar = scopeToDeclareIn.getVar(prototypeName); + if (prototypeVar != null && prototypeVar.scope == scopeToDeclareIn) { + scopeToDeclareIn.undeclare(prototypeVar); + } + + scopeToDeclareIn.declare(prototypeName, + n, prototypeSlot.getType(), input, + /* declared iff there's an explicit supertype */ + superClassCtor == null || + superClassCtor.getInstanceType().isEquivalentTo( + getNativeType(OBJECT_TYPE))); + + // Make sure the variable is initialized to something if + // it constructs itself. + if (newVar.getInitialValue() == null && + !n.isFromExterns()) { + compiler.report( + JSError.make(sourceName, n, + fnType.isConstructor() ? + CTOR_INITIALIZER : IFACE_INITIALIZER, + variableName)); + } + } + + /** + * Check if the given node is a property of a name in the global scope. + */ + private boolean isQnameRootedInGlobalScope(Node n) { + Scope scope = getQnameRootScope(n); + return scope != null && scope.isGlobal(); + } + + /** + * Return the scope for the name of the given node. + */ + private Scope getQnameRootScope(Node n) { + Node root = NodeUtil.getRootOfQualifiedName(n); + if (root.isName()) { + Var var = scope.getVar(root.getString()); + if (var != null) { + return var.getScope(); + } + } + return null; + } + + /** + * Look for a type declaration on a property assignment + * (in an ASSIGN or an object literal key). + * + * @param info The doc info for this property. + * @param lValue The l-value node. + * @param rValue The node that {@code n} is being initialized to, + * or {@code null} if this is a stub declaration. + */ + private JSType getDeclaredType(String sourceName, JSDocInfo info, + Node lValue, @Nullable Node rValue) { + if (info != null && info.hasType()) { + return getDeclaredTypeInAnnotation(sourceName, lValue, info); + } else if (rValue != null && rValue.isFunction() && + shouldUseFunctionLiteralType( + JSType.toMaybeFunctionType(rValue.getJSType()), info, lValue)) { + return rValue.getJSType(); + } else if (info != null) { + if (info.hasEnumParameterType()) { + if (rValue != null && rValue.isObjectLit()) { + return rValue.getJSType(); + } else { + return createEnumTypeFromNodes( + rValue, lValue.getQualifiedName(), info, lValue); + } + } else if (info.isConstructor() || info.isInterface()) { + return createFunctionTypeFromNodes( + rValue, lValue.getQualifiedName(), info, lValue); + } else { + // Check if this is constant, and if it has a known type. + if (info.isConstant()) { + JSType knownType = null; + if (rValue != null) { + JSDocInfo rValueInfo = rValue.getJSDocInfo(); + if (rValueInfo != null && rValueInfo.hasType()) { + // If rValue has a type-cast, we use the type in the type-cast. + return rValueInfo.getType().evaluate(scope, typeRegistry); + } else if (rValue.getJSType() != null + && !rValue.getJSType().isUnknownType()) { + // If rValue's type was already computed during scope creation, + // then we can safely use that. + return rValue.getJSType(); + } else if (rValue.isOr()) { + // Check for a very specific JS idiom: + // var x = x || TYPE; + // This is used by Closure's base namespace for esoteric + // reasons. + Node firstClause = rValue.getFirstChild(); + Node secondClause = firstClause.getNext(); + boolean namesMatch = firstClause.isName() + && lValue.isName() + && firstClause.getString().equals(lValue.getString()); + if (namesMatch && secondClause.getJSType() != null + && !secondClause.getJSType().isUnknownType()) { + return secondClause.getJSType(); + } + } + } + } + } + } + + return getDeclaredTypeInAnnotation(sourceName, lValue, info); + } + + private FunctionType getFunctionType(@Nullable Var v) { + JSType t = v == null ? null : v.getType(); + ObjectType o = t == null ? null : t.dereference(); + return JSType.toMaybeFunctionType(o); + } + + /** + * Look for calls that set a delegate method's calling convention. + */ + private void checkForCallingConventionDefiningCalls( + Node n, Map delegateCallingConventions) { + codingConvention.checkForCallingConventionDefiningCalls(n, + delegateCallingConventions); + } + + /** + * Look for class-defining calls. + * Because JS has no 'native' syntax for defining classes, + * this is often very coding-convention dependent and business-logic heavy. + */ + private void checkForClassDefiningCalls( + NodeTraversal t, Node n, Node parent) { + SubclassRelationship relationship = + codingConvention.getClassesDefinedByCall(n); + if (relationship != null) { + FunctionType superCtor = getFunctionType( + scope.getVar(relationship.superclassName)); + FunctionType subCtor = getFunctionType( + scope.getVar(relationship.subclassName)); + if (superCtor != null && superCtor.isConstructor() && + subCtor != null && subCtor.isConstructor()) { + ObjectType superClass = superCtor.getInstanceType(); + ObjectType subClass = subCtor.getInstanceType(); + + // superCtor and subCtor might be structural constructors + // (like {function(new:Object)}) so we need to resolve them back + // to the original ctor objects. + superCtor = superClass.getConstructor(); + subCtor = subClass.getConstructor(); + + if (relationship.type == SubclassType.INHERITS && + !superClass.isEmptyType() && !subClass.isEmptyType()) { + validator.expectSuperType(t, n, superClass, subClass); + } + + if (superCtor != null && subCtor != null) { + codingConvention.applySubclassRelationship( + superCtor, subCtor, relationship.type); + } + } + } + + String singletonGetterClassName = + codingConvention.getSingletonGetterClassName(n); + if (singletonGetterClassName != null) { + ObjectType objectType = ObjectType.cast( + typeRegistry.getType(singletonGetterClassName)); + if (objectType != null) { + FunctionType functionType = objectType.getConstructor(); + + if (functionType != null) { + FunctionType getterType = + typeRegistry.createFunctionType(objectType); + codingConvention.applySingletonGetter(functionType, getterType, + objectType); + } + } + } + + DelegateRelationship delegateRelationship = + codingConvention.getDelegateRelationship(n); + if (delegateRelationship != null) { + applyDelegateRelationship(delegateRelationship); + } + + ObjectLiteralCast objectLiteralCast = + codingConvention.getObjectLiteralCast(n); + if (objectLiteralCast != null) { + if (objectLiteralCast.diagnosticType == null) { + ObjectType type = ObjectType.cast( + typeRegistry.getType(objectLiteralCast.typeName)); + if (type != null && type.getConstructor() != null) { + setDeferredType(objectLiteralCast.objectNode, type); + } else { + compiler.report(JSError.make(t.getSourceName(), n, + CONSTRUCTOR_EXPECTED)); + } + } else { + compiler.report(JSError.make(t.getSourceName(), n, + objectLiteralCast.diagnosticType)); + } + } + } + + /** + * Apply special properties that only apply to delegates. + */ + private void applyDelegateRelationship( + DelegateRelationship delegateRelationship) { + ObjectType delegatorObject = ObjectType.cast( + typeRegistry.getType(delegateRelationship.delegator)); + ObjectType delegateBaseObject = ObjectType.cast( + typeRegistry.getType(delegateRelationship.delegateBase)); + ObjectType delegateSuperObject = ObjectType.cast( + typeRegistry.getType(codingConvention.getDelegateSuperclassName())); + if (delegatorObject != null && + delegateBaseObject != null && + delegateSuperObject != null) { + FunctionType delegatorCtor = delegatorObject.getConstructor(); + FunctionType delegateBaseCtor = delegateBaseObject.getConstructor(); + FunctionType delegateSuperCtor = delegateSuperObject.getConstructor(); + + if (delegatorCtor != null && delegateBaseCtor != null && + delegateSuperCtor != null) { + FunctionParamBuilder functionParamBuilder = + new FunctionParamBuilder(typeRegistry); + functionParamBuilder.addRequiredParams( + getNativeType(U2U_CONSTRUCTOR_TYPE)); + FunctionType findDelegate = typeRegistry.createFunctionType( + typeRegistry.createDefaultObjectUnion(delegateBaseObject), + functionParamBuilder.build()); + + FunctionType delegateProxy = typeRegistry.createConstructorType( + delegateBaseObject.getReferenceName() + DELEGATE_PROXY_SUFFIX, + null, null, null, null); + delegateProxy.setPrototypeBasedOn(delegateBaseObject); + + codingConvention.applyDelegateRelationship( + delegateSuperObject, delegateBaseObject, delegatorObject, + delegateProxy, findDelegate); + delegateProxyPrototypes.add(delegateProxy.getPrototype()); + } + } + } + + /** + * Declare the symbol for a qualified name in the global scope. + * + * @param info The doc info for this property. + * @param n A top-level GETPROP node (it should not be contained inside + * another GETPROP). + * @param parent The parent of {@code n}. + * @param rhsValue The node that {@code n} is being initialized to, + * or {@code null} if this is a stub declaration. + */ + void maybeDeclareQualifiedName(NodeTraversal t, JSDocInfo info, + Node n, Node parent, Node rhsValue) { + Node ownerNode = n.getFirstChild(); + String ownerName = ownerNode.getQualifiedName(); + String qName = n.getQualifiedName(); + String propName = n.getLastChild().getString(); + Preconditions.checkArgument(qName != null && ownerName != null); + + // Precedence of type information on GETPROPs: + // 1) @type annotation / @enum annotation + // 2) ASSIGN to FUNCTION literal + // 3) @param/@return annotation (with no function literal) + // 4) ASSIGN to something marked @const + // 5) ASSIGN to anything else + // + // 1, 3, and 4 are declarations, 5 is inferred, and 2 is a declaration iff + // the function has JsDoc or has not been declared before. + // + // FUNCTION literals are special because TypedScopeCreator is very smart + // about getting as much type information as possible for them. + + // Determining type for #1 + #2 + #3 + #4 + JSType valueType = getDeclaredType(t.getSourceName(), info, n, rhsValue); + if (valueType == null && rhsValue != null) { + // Determining type for #5 + valueType = rhsValue.getJSType(); + } + + // Function prototypes are special. + // It's a common JS idiom to do: + // F.prototype = { ... }; + // So if F does not have an explicitly declared super type, + // allow F.prototype to be redefined arbitrarily. + if ("prototype".equals(propName)) { + Var qVar = scope.getVar(qName); + if (qVar != null) { + // If the programmer has declared that F inherits from Super, + // and they assign F.prototype to an object literal, + // then they are responsible for making sure that the object literal's + // implicit prototype is set up appropriately. We just obey + // the @extends tag. + ObjectType qVarType = ObjectType.cast(qVar.getType()); + if (qVarType != null && + rhsValue != null && + rhsValue.isObjectLit()) { + typeRegistry.resetImplicitPrototype( + rhsValue.getJSType(), qVarType.getImplicitPrototype()); + } else if (!qVar.isTypeInferred()) { + // If the programmer has declared that F inherits from Super, + // and they assign F.prototype to some arbitrary expression, + // there's not much we can do. We just ignore the expression, + // and hope they've annotated their code in a way to tell us + // what props are going to be on that prototype. + return; + } + if (qVar.getScope() == scope) { + scope.undeclare(qVar); + } + } + } + + if (valueType == null) { + if (parent.isExprResult()) { + stubDeclarations.add(new StubDeclaration( + n, + t.getInput() != null && t.getInput().isExtern(), + ownerName)); + } + + return; + } + + boolean inferred = isQualifiedNameInferred( + qName, n, info, rhsValue, valueType); + if (!inferred) { + ObjectType ownerType = getObjectSlot(ownerName); + if (ownerType != null) { + // Only declare this as an official property if it has not been + // declared yet. + boolean isExtern = t.getInput() != null && t.getInput().isExtern(); + if ((!ownerType.hasOwnProperty(propName) || + ownerType.isPropertyTypeInferred(propName)) && + ((isExtern && !ownerType.isNativeObjectType()) || + !ownerType.isInstanceType())) { + // If the property is undeclared or inferred, declare it now. + ownerType.defineDeclaredProperty(propName, valueType, n); + } + } + + // If the property is already declared, the error will be + // caught when we try to declare it in the current scope. + defineSlot(n, parent, valueType, inferred); + } else if (rhsValue != null && rhsValue.isTrue()) { + // We declare these for delegate proxy method properties. + FunctionType ownerType = + JSType.toMaybeFunctionType(getObjectSlot(ownerName)); + if (ownerType != null) { + JSType ownerTypeOfThis = ownerType.getTypeOfThis(); + String delegateName = codingConvention.getDelegateSuperclassName(); + JSType delegateType = delegateName == null ? + null : typeRegistry.getType(delegateName); + if (delegateType != null && + ownerTypeOfThis.isSubtype(delegateType)) { + defineSlot(n, parent, getNativeType(BOOLEAN_TYPE), true); + } + } + } + } + + /** + * Determines whether a qualified name is inferred. + * NOTE(nicksantos): Determining whether a property is declared or not + * is really really obnoxious. + * + * The problem is that there are two (equally valid) coding styles: + * + * (function() { + * /* The authoritative definition of goog.bar. / + * goog.bar = function() {}; + * })(); + * + * function f() { + * goog.bar(); + * /* Reset goog.bar to a no-op. / + * goog.bar = function() {}; + * } + * + * In a dynamic language with first-class functions, it's very difficult + * to know which one the user intended without looking at lots of + * contextual information (the second example demonstrates a small case + * of this, but there are some really pathological cases as well). + * + * The current algorithm checks if either the declaration has + * JsDoc type information, or @const with a known type, + * or a function literal with a name we haven't seen before. + */ + private boolean isQualifiedNameInferred( + String qName, Node n, JSDocInfo info, + Node rhsValue, JSType valueType) { + if (valueType == null) { + return true; + } + + boolean inferred = true; + if (info != null) { + inferred = !(info.hasType() + || info.hasEnumParameterType() + || (info.isConstant() && valueType != null + && !valueType.isUnknownType()) + || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); + } + + if (inferred && rhsValue != null && rhsValue.isFunction()) { + if (info != null) { + return false; + } else if (!scope.isDeclared(qName, false) && + n.isUnscopedQualifiedName()) { + + // Check if this is in a conditional block. + // Functions assigned in conditional blocks are inferred. + for (Node current = n.getParent(); + !(current.isScript() || current.isFunction()); + current = current.getParent()) { + if (NodeUtil.isControlStructure(current)) { + return true; + } + } + + // Check if this is assigned in an inner scope. + // Functions assigned in inner scopes are inferred. + AstFunctionContents contents = + getFunctionAnalysisResults(scope.getRootNode()); + if (contents == null || + !contents.getEscapedQualifiedNames().contains(qName)) { + return false; + } + } + } + return inferred; + } + + /** + * Find the ObjectType associated with the given slot. + * @param slotName The name of the slot to find the type in. + * @return An object type, or null if this slot does not contain an object. + */ + private ObjectType getObjectSlot(String slotName) { + Var ownerVar = scope.getVar(slotName); + if (ownerVar != null) { + JSType ownerVarType = ownerVar.getType(); + return ObjectType.cast(ownerVarType == null ? + null : ownerVarType.restrictByNotNullOrUndefined()); + } + return null; + } + + /** + * Resolve any stub declarations to unknown types if we could not + * find types for them during traversal. + */ + void resolveStubDeclarations() { + for (StubDeclaration stub : stubDeclarations) { + Node n = stub.node; + Node parent = n.getParent(); + String qName = n.getQualifiedName(); + String propName = n.getLastChild().getString(); + String ownerName = stub.ownerName; + boolean isExtern = stub.isExtern; + + if (scope.isDeclared(qName, false)) { + continue; + } + + // If we see a stub property, make sure to register this property + // in the type registry. + ObjectType ownerType = getObjectSlot(ownerName); + defineSlot(n, parent, unknownType, true); + + if (ownerType != null && + (isExtern || ownerType.isFunctionPrototypeType())) { + // If this is a stub for a prototype, just declare it + // as an unknown type. These are seen often in externs. + ownerType.defineInferredProperty( + propName, unknownType, n); + } else { + typeRegistry.registerPropertyOnType( + propName, ownerType == null ? unknownType : ownerType); + } + } + } + + /** + * Collects all declared properties in a function, and + * resolves them relative to the global scope. + */ + private final class CollectProperties + extends AbstractShallowStatementCallback { + private final JSType thisType; + + CollectProperties(JSType thisType) { + this.thisType = thisType; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isExprResult()) { + Node child = n.getFirstChild(); + switch (child.getType()) { + case Token.ASSIGN: + maybeCollectMember(t, child.getFirstChild(), child, + child.getLastChild()); + break; + case Token.GETPROP: + maybeCollectMember(t, child, child, null); + break; + } + } + } + + private void maybeCollectMember(NodeTraversal t, + Node member, Node nodeWithJsDocInfo, @Nullable Node value) { + JSDocInfo info = nodeWithJsDocInfo.getJSDocInfo(); + + // Do nothing if there is no JSDoc type info, or + // if the node is not a member expression, or + // if the member expression is not of the form: this.someProperty. + if (info == null || + !member.isGetProp() || + !member.getFirstChild().isThis()) { + return; + } + + member.getFirstChild().setJSType(thisType); + JSType jsType = getDeclaredType(t.getSourceName(), info, member, value); + Node name = member.getLastChild(); + if (jsType != null && + (name.isName() || name.isString()) && + thisType.toObjectType() != null) { + thisType.toObjectType().defineDeclaredProperty( + name.getString(), + jsType, + member); + } + } + } // end CollectProperties + } + + /** + * A stub declaration without any type information. + */ + private static final class StubDeclaration { + private final Node node; + private final boolean isExtern; + private final String ownerName; + + private StubDeclaration(Node node, boolean isExtern, String ownerName) { + this.node = node; + this.isExtern = isExtern; + this.ownerName = ownerName; + } + } + + /** + * A shallow traversal of the global scope to build up all classes, + * functions, and methods. + */ + private final class GlobalScopeBuilder extends AbstractScopeBuilder { + + private GlobalScopeBuilder(Scope scope) { + super(scope); + } + + /** + * Visit a node in the global scope, and add anything it declares to the + * global symbol table. + * + * @param t The current traversal. + * @param n The node being visited. + * @param parent The parent of n + */ + @Override public void visit(NodeTraversal t, Node n, Node parent) { + super.visit(t, n, parent); + + switch (n.getType()) { + + case Token.VAR: + // Handle typedefs. + if (n.hasOneChild()) { + checkForTypedef(t, n.getFirstChild(), n.getJSDocInfo()); + } + break; + } + } + + @Override + void maybeDeclareQualifiedName( + NodeTraversal t, JSDocInfo info, + Node n, Node parent, Node rhsValue) { + checkForTypedef(t, n, info); + super.maybeDeclareQualifiedName(t, info, n, parent, rhsValue); + } + + /** + * Handle typedefs. + * @param t The current traversal. + * @param candidate A qualified name node. + * @param info JSDoc comments. + */ + private void checkForTypedef( + NodeTraversal t, Node candidate, JSDocInfo info) { + if (info == null || !info.hasTypedefType()) { + return; + } + + String typedef = candidate.getQualifiedName(); + if (typedef == null) { + return; + } + + // TODO(nicksantos|user): This is a terrible, terrible hack + // to bail out on recursive typedefs. We'll eventually need + // to handle these properly. + typeRegistry.declareType(typedef, unknownType); + + JSType realType = info.getTypedefType().evaluate(scope, typeRegistry); + if (realType == null) { + compiler.report( + JSError.make( + t.getSourceName(), candidate, MALFORMED_TYPEDEF, typedef)); + } + + typeRegistry.overwriteDeclaredType(typedef, realType); + if (candidate.isGetProp()) { + defineSlot(candidate, candidate.getParent(), + getNativeType(NO_TYPE), false); + } + } + } // end GlobalScopeBuilder + + /** + * A shallow traversal of a local scope to find all arguments and + * local variables. + */ + private final class LocalScopeBuilder extends AbstractScopeBuilder { + /** + * @param scope The scope that we're building. + */ + private LocalScopeBuilder(Scope scope) { + super(scope); + } + + /** + * Traverse the scope root and build it. + */ + void build() { + NodeTraversal.traverse(compiler, scope.getRootNode(), this); + + AstFunctionContents contents = + getFunctionAnalysisResults(scope.getRootNode()); + if (contents != null) { + for (String varName : contents.getEscapedVarNames()) { + Var v = scope.getVar(varName); + Preconditions.checkState(v.getScope() == scope); + v.markEscaped(); + } + + for (Multiset.Entry entry : + contents.getAssignedNameCounts().entrySet()) { + Var v = scope.getVar(entry.getElement()); + Preconditions.checkState(v.getScope() == scope); + if (entry.getCount() == 1) { + v.markAssignedExactlyOnce(); + } + } + } + } + + /** + * Visit a node in a local scope, and add any local variables or catch + * parameters into the local symbol table. + * + * @param t The node traversal. + * @param n The node being visited. + * @param parent The parent of n + */ + @Override public void visit(NodeTraversal t, Node n, Node parent) { + if (n == scope.getRootNode()) return; + + if (n.isParamList() && parent == scope.getRootNode()) { + handleFunctionInputs(parent); + return; + } + + super.visit(t, n, parent); + } + + /** Handle bleeding functions and function parameters. */ + private void handleFunctionInputs(Node fnNode) { + // Handle bleeding functions. + Node fnNameNode = fnNode.getFirstChild(); + String fnName = fnNameNode.getString(); + if (!fnName.isEmpty()) { + Scope.Var fnVar = scope.getVar(fnName); + if (fnVar == null || + // Make sure we're not touching a native function. Native + // functions aren't bleeding, but may not have a declaration + // node. + (fnVar.getNameNode() != null && + // Make sure that the function is actually bleeding by checking + // if has already been declared. + fnVar.getInitialValue() != fnNode)) { + defineSlot(fnNameNode, fnNode, fnNode.getJSType(), false); + } + } + + declareArguments(fnNode); + } + + /** + * Declares all of a function's arguments. + */ + private void declareArguments(Node functionNode) { + Node astParameters = functionNode.getFirstChild().getNext(); + Node iifeArgumentNode = null; + + if (NodeUtil.isCallOrNewTarget(functionNode)) { + iifeArgumentNode = functionNode.getNext(); + } + + Node body = astParameters.getNext(); + FunctionType functionType = + JSType.toMaybeFunctionType(functionNode.getJSType()); + if (functionType != null) { + Node jsDocParameters = functionType.getParametersNode(); + if (jsDocParameters != null) { + Node jsDocParameter = jsDocParameters.getFirstChild(); + for (Node astParameter : astParameters.children()) { + JSType paramType = jsDocParameter == null ? + unknownType : jsDocParameter.getJSType(); + boolean inferred = paramType == null || paramType == unknownType; + + if (iifeArgumentNode != null && inferred) { + String argumentName = iifeArgumentNode.getQualifiedName(); + Var argumentVar = + argumentName == null || scope.getParent() == null + ? null : scope.getParent().getVar(argumentName); + if (argumentVar != null && !argumentVar.isTypeInferred()) { + paramType = argumentVar.getType(); + } + } + + if (paramType == null) { + paramType = unknownType; + } + + defineSlot(astParameter, functionNode, paramType, inferred); + + if (jsDocParameter != null) { + jsDocParameter = jsDocParameter.getNext(); + } + if (iifeArgumentNode != null) { + iifeArgumentNode = iifeArgumentNode.getNext(); + } + } + } + } + } // end declareArguments + } // end LocalScopeBuilder + + /** + * Does a first-order function analysis that just looks at simple things + * like what variables are escaped, and whether 'this' is used. + */ + private static class FirstOrderFunctionAnalyzer + extends AbstractScopedCallback implements CompilerPass { + private final AbstractCompiler compiler; + private final Map data; + + FirstOrderFunctionAnalyzer( + AbstractCompiler compiler, Map outParam) { + this.compiler = compiler; + this.data = outParam; + } + + @Override public void process(Node externs, Node root) { + if (externs == null) { + NodeTraversal.traverse(compiler, root, this); + } else { + NodeTraversal.traverseRoots( + compiler, ImmutableList.of(externs, root), this); + } + } + + @Override public void enterScope(NodeTraversal t) { + if (!t.inGlobalScope()) { + Node n = t.getScopeRoot(); + data.put(n, new AstFunctionContents(n)); + } + } + + @Override public void visit(NodeTraversal t, Node n, Node parent) { + if (t.inGlobalScope()) { + return; + } + + if (n.isReturn() && n.getFirstChild() != null) { + data.get(t.getScopeRoot()).recordNonEmptyReturn(); + } + + if (t.getScopeDepth() <= 1) { + // The first-order function analyzer looks at two types of variables: + // + // 1) Local variables that are assigned in inner scopes ("escaped vars") + // + // 2) Local variables that are assigned more than once. + // + // We treat all global variables as escaped by default, so there's + // no reason to do this extra computation for them. + return; + } + + if (n.isName() && NodeUtil.isLValue(n) && + // Be careful of bleeding functions, which create variables + // in the inner scope, not the scope where the name appears. + !NodeUtil.isBleedingFunctionName(n)) { + String name = n.getString(); + Scope scope = t.getScope(); + Var var = scope.getVar(name); + if (var != null) { + Scope ownerScope = var.getScope(); + if (ownerScope.isLocal()) { + data.get(ownerScope.getRootNode()).recordAssignedName(name); + } + + if (scope != ownerScope && ownerScope.isLocal()) { + data.get(ownerScope.getRootNode()).recordEscapedVarName(name); + } + } + } else if (n.isGetProp() && n.isUnscopedQualifiedName() && + NodeUtil.isLValue(n)) { + String name = NodeUtil.getRootOfQualifiedName(n).getString(); + Scope scope = t.getScope(); + Var var = scope.getVar(name); + if (var != null) { + Scope ownerScope = var.getScope(); + if (scope != ownerScope && ownerScope.isLocal()) { + data.get(ownerScope.getRootNode()) + .recordEscapedQualifiedName(n.getQualifiedName()); + } + } + } + } + } + + private AstFunctionContents getFunctionAnalysisResults(@Nullable Node n) { + if (n == null) { + return null; + } + + // Sometimes this will return null in things like + // NameReferenceGraphConstruction that build partial scopes. + return functionAnalysisResults.get(n); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/UnreachableCodeElimination.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/UnreachableCodeElimination.java new file mode 100644 index 0000000..b5ffbd1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/UnreachableCodeElimination.java @@ -0,0 +1,260 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.jscomp.graph.GraphReachability; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Removes dead code from a parse tree. The kinds of dead code that this pass + * removes are: + * - Any code following a return statement, such as the alert + * call in: if (x) { return; alert('unreachable'); }. + * - Statements that have no side effects, such as: + * a.b.MyClass.prototype.propertyName; or true;. + * That first kind of statement sometimes appears intentionally, so that + * prototype properties can be annotated using JSDoc without actually + * being initialized. + * + */ +class UnreachableCodeElimination extends AbstractPostOrderCallback + implements CompilerPass, ScopedCallback { + private static final Logger logger = + Logger.getLogger(UnreachableCodeElimination.class.getName()); + + private final AbstractCompiler compiler; + private final boolean removeNoOpStatements; + + UnreachableCodeElimination(AbstractCompiler compiler, + boolean removeNoOpStatements) { + this.compiler = compiler; + this.removeNoOpStatements = removeNoOpStatements; + } + + @Override + public void exitScope(NodeTraversal t) { + Scope scope = t.getScope(); + + // Computes the control flow graph. + ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, false); + cfa.process(null, scope.getRootNode()); + ControlFlowGraph cfg = cfa.getCfg(); + + new GraphReachability(cfg) + .compute(cfg.getEntry().getValue()); + + Node root = scope.getRootNode(); + if (scope.isLocal()) { + root = root.getLastChild(); + } + NodeTraversal.traverse( + compiler, root, new EliminationPass(cfg)); + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, this); + } + + private class EliminationPass extends AbstractShallowCallback { + private final ControlFlowGraph cfg; + private EliminationPass(ControlFlowGraph cfg) { + this.cfg = cfg; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (parent == null) { + return; + } + if (n.isFunction() || n.isScript()) { + return; + } + + DiGraphNode gNode = cfg.getDirectedGraphNode(n); + if (gNode == null) { // Not in CFG. + return; + } + if (gNode.getAnnotation() != GraphReachability.REACHABLE || + (removeNoOpStatements && !NodeUtil.mayHaveSideEffects(n, compiler))) { + removeDeadExprStatementSafely(n); + return; + } + + tryRemoveUnconditionalBranching(n); + } + + /** + * Tries to remove n if it is an unconditional branch node (break, continue, + * or return) and the target of n is the same as the the follow of n. + * That is, if removing n preserves the control flow. Also if n targets + * another unconditional branch, this function will recursively try to remove + * the target branch as well. The reason why we want to cascade this removal + * is because we only run this pass once. If we have code such as + * + * break -> break -> break + * + * where all 3 breaks are useless, then the order of removal matters. When we + * first look at the first break, we see that it branches to the 2nd break. + * However, if we remove the last break, the 2nd break becomes useless and + * finally the first break becomes useless as well. + * + * @return The target of this jump. If the target is also useless jump, + * the target of that useless jump recursively. + */ + @SuppressWarnings("fallthrough") + private Node tryRemoveUnconditionalBranching(Node n) { + /* + * For each unconditional branching control flow node, check to see + * if the ControlFlowAnalysis.computeFollowNode of that node is same as + * the branching target. If it is, the branch node is safe to be removed. + * + * This is not as clever as MinimizeExitPoints because it doesn't do any + * if-else conversion but it handles more complicated switch statements + * much more nicely. + */ + + // If n is null the target is the end of the function, nothing to do. + if (n == null) { + return n; + } + + DiGraphNode gNode = cfg.getDirectedGraphNode(n); + + if (gNode == null) { + return n; + } + + switch (n.getType()) { + case Token.RETURN: + if (n.hasChildren()) { + break; + } + case Token.BREAK: + case Token.CONTINUE: + + // We are looking for a control flow changing statement that always + // branches to the same node. If after removing it control still + // branches to the same node, it is safe to remove. + List> outEdges = gNode.getOutEdges(); + if (outEdges.size() == 1 && + // If there is a next node, there is no chance this jump is useless. + (n.getNext() == null || n.getNext().isFunction())) { + + Preconditions.checkState(outEdges.get(0).getValue() == Branch.UNCOND); + Node fallThrough = computeFollowing(n); + Node nextCfgNode = outEdges.get(0).getDestination().getValue(); + if (nextCfgNode == fallThrough) { + removeDeadExprStatementSafely(n); + return fallThrough; + } + } + } + return n; + } + + private Node computeFollowing(Node n) { + Node next = ControlFlowAnalysis.computeFollowNode(n); + while (next != null && next.isBlock()) { + if (next.hasChildren()) { + next = next.getFirstChild(); + } else { + next = computeFollowing(next); + } + } + return next; + } + + private void removeDeadExprStatementSafely(Node n) { + Node parent = n.getParent(); + if (n.isEmpty() || + (n.isBlock() && !n.hasChildren())) { + // Not always trivial to remove, let FoldConstants work its magic later. + return; + } + + // TODO(user): This is a problem with removeNoOpStatements. + // Every expression in a FOR-IN header looks side effect free on its own. + if (NodeUtil.isForIn(parent)) { + return; + } + + switch (n.getType()) { + // Removing an unreachable DO node is messy because it means we still have + // to execute one iteration. If the DO's body has breaks in the middle, it + // can get even more tricky and code size might actually increase. + case Token.DO: + return; + + case Token.BLOCK: + // BLOCKs are used in several ways including wrapping CATCH + // blocks in TRYs + if (parent.isTry()) { + if (NodeUtil.isTryCatchNodeContainer(n)) { + return; + } + } + break; + + case Token.CATCH: + Node tryNode = parent.getParent(); + NodeUtil.maybeAddFinally(tryNode); + break; + } + + + if (n.isVar() && !n.getFirstChild().hasChildren()) { + // Very Edge case, Consider this: + // File 1: {throw 1} + // File 2: {var x} + // The node var x is unreachable in the global scope. + // Before we remove the node, redeclareVarsInsideBranch + // would basically move var x to the beginning of File 2, + // which resulted in zero changes to the AST but triggered + // reportCodeChange(). + // Instead, we should just ignore dead variable declarations. + return; + } + + NodeUtil.redeclareVarsInsideBranch(n); + compiler.reportCodeChange(); + if (logger.isLoggable(Level.FINE)) { + logger.fine("Removing " + n.toString()); + } + + NodeUtil.removeChild(n.getParent(), n); + } + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) {} + + @Override + public void enterScope(NodeTraversal t) {} +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/UseSite.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/UseSite.java new file mode 100644 index 0000000..58cd25a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/UseSite.java @@ -0,0 +1,50 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + * Information about the context in which a Definition is used. + * Includes the referring node, and context in which the reference + * occurs - including the module in which the reference appears. + * + */ + +class UseSite { + final Node node; + final Scope scope; + final JSModule module; + + UseSite(Node node, Scope scope, JSModule module) { + this.node = node; + this.scope = scope; + this.module = module; + } + + // Use the node as the identifying feature to make the UseSite recreatable. + + @Override + public int hashCode() { + return this.node.hashCode(); + } + + @Override + public boolean equals(Object o) { + return (o instanceof UseSite && ((UseSite)(o)).node.equals(this.node)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VarCheck.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VarCheck.java new file mode 100644 index 0000000..a497232 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VarCheck.java @@ -0,0 +1,279 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Set; + +/** + * Checks that all variables are declared, that file-private variables are + * accessed only in the file that declares them, and that any var references + * that cross module boundaries respect declared module dependencies. + * + */ +class VarCheck extends AbstractPostOrderCallback implements + HotSwapCompilerPass { + + static final DiagnosticType UNDEFINED_VAR_ERROR = DiagnosticType.error( + "JSC_UNDEFINED_VARIABLE", + "variable {0} is undeclared"); + + static final DiagnosticType VIOLATED_MODULE_DEP_ERROR = DiagnosticType.error( + "JSC_VIOLATED_MODULE_DEPENDENCY", + "module {0} cannot reference {2}, defined in " + + "module {1}, since {1} loads after {0}"); + + static final DiagnosticType MISSING_MODULE_DEP_ERROR = DiagnosticType.warning( + "JSC_MISSING_MODULE_DEPENDENCY", + "missing module dependency; module {0} should depend " + + "on module {1} because it references {2}"); + + static final DiagnosticType STRICT_MODULE_DEP_ERROR = DiagnosticType.disabled( + "JSC_STRICT_MODULE_DEPENDENCY", + "module {0} cannot reference {2}, defined in " + + "module {1}"); + + static final DiagnosticType NAME_REFERENCE_IN_EXTERNS_ERROR = + DiagnosticType.warning( + "JSC_NAME_REFERENCE_IN_EXTERNS", + "accessing name {0} in externs has no effect"); + + static final DiagnosticType UNDEFINED_EXTERN_VAR_ERROR = + DiagnosticType.warning( + "JSC_UNDEFINED_EXTERN_VAR_ERROR", + "name {0} is not undefined in the externs."); + + private Node synthesizedExternsRoot = null; + + // Vars that still need to be declared in externs. These will be declared + // at the end of the pass, or when we see the equivalent var declared + // in the normal code. + private final Set varsToDeclareInExterns = Sets.newHashSet(); + + private final AbstractCompiler compiler; + + // Whether this is the post-processing sanity check. + private final boolean sanityCheck; + + // Whether extern checks emit error. + private final boolean strictExternCheck; + + VarCheck(AbstractCompiler compiler) { + this(compiler, false); + } + + VarCheck(AbstractCompiler compiler, boolean sanityCheck) { + this.compiler = compiler; + this.strictExternCheck = compiler.getErrorLevel( + JSError.make("", 0, 0, UNDEFINED_EXTERN_VAR_ERROR)) == CheckLevel.ERROR; + this.sanityCheck = sanityCheck; + } + + @Override + public void process(Node externs, Node root) { + // Don't run externs-checking in sanity check mode. Normalization will + // remove duplicate VAR declarations, which will make + // externs look like they have assigns. + if (!sanityCheck) { + NodeTraversal.traverse(compiler, externs, new NameRefInExternsCheck()); + } + + NodeTraversal.traverseRoots( + compiler, Lists.newArrayList(externs, root), this); + for (String varName : varsToDeclareInExterns) { + createSynthesizedExternVar(varName); + } + } + + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + Preconditions.checkState(scriptRoot.isScript()); + NodeTraversal t = new NodeTraversal(compiler, this); + // Note we use the global scope to prevent wrong "undefined-var errors" on + // variables that are defined in other JS files. + t.traverseWithScope(scriptRoot, + SyntacticScopeCreator.generateUntypedTopScope(compiler)); + // TODO(bashir) Check if we need to createSynthesizedExternVar like process. + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (!n.isName()) { + return; + } + + String varName = n.getString(); + + // Only a function can have an empty name. + if (varName.isEmpty()) { + Preconditions.checkState(parent.isFunction()); + Preconditions.checkState(NodeUtil.isFunctionExpression(parent)); + return; + } + + // Check if this is a declaration for a var that has been declared + // elsewhere. If so, mark it as a duplicate. + if ((parent.isVar() || + NodeUtil.isFunctionDeclaration(parent)) && + varsToDeclareInExterns.contains(varName)) { + createSynthesizedExternVar(varName); + + n.addSuppression("duplicate"); + } + + // Check that the var has been declared. + Scope scope = t.getScope(); + Scope.Var var = scope.getVar(varName); + if (var == null) { + if (NodeUtil.isFunctionExpression(parent)) { + // e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the + // current scope. + } else { + // The extern checks are stricter, don't report a second error. + if (!strictExternCheck || !t.getInput().isExtern()) { + t.report(n, UNDEFINED_VAR_ERROR, varName); + } + + if (sanityCheck) { + throw new IllegalStateException("Unexpected variable " + varName); + } else { + createSynthesizedExternVar(varName); + scope.getGlobalScope().declare(varName, n, + null, getSynthesizedExternsInput()); + } + } + return; + } + + CompilerInput currInput = t.getInput(); + CompilerInput varInput = var.input; + if (currInput == varInput || currInput == null || varInput == null) { + // The variable was defined in the same file. This is fine. + return; + } + + // Check module dependencies. + JSModule currModule = currInput.getModule(); + JSModule varModule = varInput.getModule(); + JSModuleGraph moduleGraph = compiler.getModuleGraph(); + if (!sanityCheck && + varModule != currModule && varModule != null && currModule != null) { + if (moduleGraph.dependsOn(currModule, varModule)) { + // The module dependency was properly declared. + } else { + if (scope.isGlobal()) { + if (moduleGraph.dependsOn(varModule, currModule)) { + // The variable reference violates a declared module dependency. + t.report(n, VIOLATED_MODULE_DEP_ERROR, + currModule.getName(), varModule.getName(), varName); + } else { + // The variable reference is between two modules that have no + // dependency relationship. This should probably be considered an + // error, but just issue a warning for now. + t.report(n, MISSING_MODULE_DEP_ERROR, + currModule.getName(), varModule.getName(), varName); + } + } else { + t.report(n, STRICT_MODULE_DEP_ERROR, + currModule.getName(), varModule.getName(), varName); + } + } + } + } + + /** + * Create a new variable in a synthetic script. This will prevent + * subsequent compiler passes from crashing. + */ + private void createSynthesizedExternVar(String varName) { + Node nameNode = IR.name(varName); + + // Mark the variable as constant if it matches the coding convention + // for constant vars. + // NOTE(nicksantos): honestly, I'm not sure how much this matters. + // AFAIK, all people who use the CONST coding convention also + // compile with undeclaredVars as errors. We have some test + // cases for this configuration though, and it makes them happier. + if (compiler.getCodingConvention().isConstant(varName)) { + nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); + } + + getSynthesizedExternsRoot().addChildToBack( + IR.var(nameNode)); + varsToDeclareInExterns.remove(varName); + compiler.reportCodeChange(); + } + + /** + * A check for name references in the externs inputs. These used to prevent + * a variable from getting renamed, but no longer have any effect. + */ + private class NameRefInExternsCheck extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isName()) { + switch (parent.getType()) { + case Token.VAR: + case Token.FUNCTION: + case Token.PARAM_LIST: + // These are okay. + break; + case Token.GETPROP: + if (n == parent.getFirstChild()) { + Scope scope = t.getScope(); + Scope.Var var = scope.getVar(n.getString()); + if (var == null) { + t.report(n, UNDEFINED_EXTERN_VAR_ERROR, n.getString()); + varsToDeclareInExterns.add(n.getString()); + } + } + break; + default: + t.report(n, NAME_REFERENCE_IN_EXTERNS_ERROR, n.getString()); + + Scope scope = t.getScope(); + Scope.Var var = scope.getVar(n.getString()); + if (var == null) { + varsToDeclareInExterns.add(n.getString()); + } + break; + } + } + } + } + + /** Lazily create a "new" externs input for undeclared variables. */ + private CompilerInput getSynthesizedExternsInput() { + return compiler.getSynthesizedExternsInput(); + } + + /** Lazily create a "new" externs root for undeclared variables. */ + private Node getSynthesizedExternsRoot() { + if (synthesizedExternsRoot == null) { + CompilerInput synthesizedExterns = getSynthesizedExternsInput(); + synthesizedExternsRoot = synthesizedExterns.getAstRoot(compiler); + } + return synthesizedExternsRoot; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VariableMap.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VariableMap.java new file mode 100644 index 0000000..57349d0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VariableMap.java @@ -0,0 +1,209 @@ +/* + * Copyright 2005 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableMap; +import com.google.common.io.ByteStreams; +import com.google.common.io.CharStreams; +import com.google.common.io.Files; + +import java.io.*; +import java.text.*; +import java.util.*; + +/** + * Stores the mapping from original variable name to new variable names. + * @see RenameVars + */ +public class VariableMap { + + /** Maps original source name to new name */ + private final ImmutableMap map; + + /** Maps new name to source name, lazily initialized */ + private ImmutableMap reverseMap = null; + + private static final char SEPARATOR = ':'; + + VariableMap(Map map) { + this.map = ImmutableMap.copyOf(map); + } + + /** + * Given an original variable name, look up new name, may return null + * if it's not found. + */ + public String lookupNewName(String sourceName) { + return map.get(sourceName); + } + + /** + * Given a new variable name, lookup the source name, may return null + * if it's not found. + */ + public String lookupSourceName(String newName) { + initReverseMap(); + return reverseMap.get(newName); + } + + /** + * Initializes the reverse map. + */ + private synchronized void initReverseMap() { + if (reverseMap == null) { + ImmutableMap.Builder rm = ImmutableMap.builder(); + for (Map.Entry entry : map.entrySet()) { + rm.put(entry.getValue(), entry.getKey()); + } + reverseMap = rm.build(); + } + } + + /** + * Returns an unmodifiable mapping from original names to new names. + */ + public Map getOriginalNameToNewNameMap() { + return map; + } + + /** + * Returns an unmodifiable mapping from new names to original names. + */ + public Map getNewNameToOriginalNameMap() { + initReverseMap(); + return reverseMap; + } + + /** + * Saves the variable map to a file. + */ + public void save(String filename) throws IOException { + Files.write(toBytes(), new File(filename)); + } + + /** + * Reads the variable map from a file written via {@link #save(String)}. + */ + public static VariableMap load(String filename) throws IOException { + try { + return fromBytes(Files.toByteArray(new File(filename))); + } catch (ParseException e) { + // Wrap parse exception for backwards compatibility. + throw new IOException(e); + } + } + + /** + * Serializes the variable map to a byte array. + */ + public byte[] toBytes() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Writer writer = new OutputStreamWriter(baos, Charsets.UTF_8); + try { + for (Map.Entry entry : map.entrySet()) { + writer.write(escape(entry.getKey())); + writer.write(SEPARATOR); + writer.write(escape(entry.getValue())); + writer.write('\n'); + } + writer.close(); + } catch (IOException e) { + // Note: A ByteArrayOutputStream never throws IOException. This try/catch + // is just here to appease the Java compiler. + throw new RuntimeException(e); + } + return baos.toByteArray(); + } + + /** + * Deserializes the variable map from a byte array returned by + * {@link #toBytes()}. + */ + public static VariableMap fromBytes(byte[] bytes) throws ParseException { + Iterable lines; + try { + lines = CharStreams.readLines(CharStreams.newReaderSupplier( + ByteStreams.newInputStreamSupplier(bytes), Charsets.UTF_8)); + } catch (IOException e) { + // Note: An IOException is never thrown while reading from a byte array. + // This try/catch is just here to appease the Java compiler. + throw new RuntimeException(e); + } + + ImmutableMap.Builder map = ImmutableMap.builder(); + + for (String line : lines) { + int pos = findIndexOfChar(line, SEPARATOR); + if (pos <= 0 || pos == line.length() - 1) { + throw new ParseException("Bad line: " + line, 0); + } + map.put( + unescape(line.substring(0, pos)), + unescape(line.substring(pos + 1))); + } + return new VariableMap(map.build()); + } + + private static String escape(String value) { + return value.replace("\\", "\\\\") + .replace(":", "\\:") + .replace("\n", "\\n"); + } + + private static int findIndexOfChar(String value, char stopChar) { + int len = value.length(); + for (int i=0; i map) { + return new VariableMap(map); + } + + @VisibleForTesting + Map toMap() { + return map; + } +} \ No newline at end of file diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VariableNameGenerator.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VariableNameGenerator.java new file mode 100644 index 0000000..13b1167 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VariableNameGenerator.java @@ -0,0 +1,43 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Sets; + +import java.util.Set; + +/** + * Generates new variables names that would not collide with existing names in + * a scope. + * + * + */ +class VariableNameGenerator { + private final NameGenerator names; + private final Scope scope; + VariableNameGenerator(Scope scope) { + this.scope = scope; + Set usedNames = Sets.newHashSet(); + names = new NameGenerator(usedNames, "", null); + } + + String getNextNewName() { + String name = null; + while (scope.isDeclared(name = names.generateNextName(), true)) {} + return name; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VariableReferenceCheck.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VariableReferenceCheck.java new file mode 100644 index 0000000..c934263 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VariableReferenceCheck.java @@ -0,0 +1,199 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.ReferenceCollectingCallback.BasicBlock; +import com.google.javascript.jscomp.ReferenceCollectingCallback.Behavior; +import com.google.javascript.jscomp.ReferenceCollectingCallback.Reference; +import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceMap; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.Node; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * Checks variables to see if they are referenced before their declaration, or + * if they are redeclared in a way that is suspicious (i.e. not dictated by + * control structures). This is a more aggressive version of {@link VarCheck}, + * but it lacks the cross-module checks. + * + * @author kushal@google.com (Kushal Dave) + */ +class VariableReferenceCheck implements HotSwapCompilerPass { + + static final DiagnosticType UNDECLARED_REFERENCE = DiagnosticType.warning( + "JSC_REFERENCE_BEFORE_DECLARE", + "Variable referenced before declaration: {0}"); + + static final DiagnosticType REDECLARED_VARIABLE = DiagnosticType.warning( + "JSC_REDECLARED_VARIABLE", + "Redeclared variable: {0}"); + + static final DiagnosticType AMBIGUOUS_FUNCTION_DECL = + DiagnosticType.disabled("AMBIGUOUS_FUNCTION_DECL", + "Ambiguous use of a named function: {0}."); + + private final AbstractCompiler compiler; + private final CheckLevel checkLevel; + + // NOTE(nicksantos): It's a lot faster to use a shared Set that + // we clear after each method call, because the Set never gets too big. + private final Set blocksWithDeclarations = Sets.newHashSet(); + + public VariableReferenceCheck(AbstractCompiler compiler, + CheckLevel checkLevel) { + this.compiler = compiler; + this.checkLevel = checkLevel; + } + + @Override + public void process(Node externs, Node root) { + ReferenceCollectingCallback callback = new ReferenceCollectingCallback( + compiler, new ReferenceCheckingBehavior()); + callback.process(externs, root); + } + + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + ReferenceCollectingCallback callback = new ReferenceCollectingCallback( + compiler, new ReferenceCheckingBehavior()); + callback.hotSwapScript(scriptRoot, originalRoot); + } + + /** + * Behavior that checks variables for redeclaration or early references + * just after they go out of scope. + */ + private class ReferenceCheckingBehavior implements Behavior { + + @Override + public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) { + // TODO(bashir) In hot-swap version this means that for global scope we + // only go through all global variables accessed in the modified file not + // all global variables. This should be fixed. + + // Check all vars after finishing a scope + for (Iterator it = t.getScope().getVars(); it.hasNext();) { + Var v = it.next(); + checkVar(t, v, referenceMap.getReferences(v).references); + } + } + + /** + * If the variable is declared more than once in a basic block, generate a + * warning. Also check if a variable is used in a given scope before it is + * declared, which suggest a likely error. Relies on the fact that + * references is in parse-tree order. + */ + private void checkVar(NodeTraversal t, Var v, List references) { + blocksWithDeclarations.clear(); + boolean isDeclaredInScope = false; + boolean isUnhoistedNamedFunction = false; + Reference hoistedFn = null; + + // Look for hoisted functions. + for (Reference reference : references) { + if (reference.isHoistedFunction()) { + blocksWithDeclarations.add(reference.getBasicBlock()); + isDeclaredInScope = true; + hoistedFn = reference; + break; + } else if (NodeUtil.isFunctionDeclaration( + reference.getNode().getParent())) { + isUnhoistedNamedFunction = true; + } + } + + for (Reference reference : references) { + if (reference == hoistedFn) { + continue; + } + + BasicBlock basicBlock = reference.getBasicBlock(); + boolean isDeclaration = reference.isDeclaration(); + + boolean allowDupe = + SyntacticScopeCreator.hasDuplicateDeclarationSuppression( + reference.getNode(), v); + if (isDeclaration && !allowDupe) { + // Look through all the declarations we've found so far, and + // check if any of them are before this block. + for (BasicBlock declaredBlock : blocksWithDeclarations) { + if (declaredBlock.provablyExecutesBefore(basicBlock)) { + // TODO(johnlenz): Fix AST generating clients that so they would + // have property StaticSourceFile attached at each node. Or + // better yet, make sure the generated code never violates + // the requirement to pass aggressive var check! + String filename = NodeUtil.getSourceName(reference.getNode()); + compiler.report( + JSError.make(filename, + reference.getNode(), + checkLevel, + REDECLARED_VARIABLE, v.name)); + break; + } + } + } + + if (isUnhoistedNamedFunction && !isDeclaration && isDeclaredInScope) { + // Only allow an unhoisted named function to be used within the + // block it is declared. + for (BasicBlock declaredBlock : blocksWithDeclarations) { + if (!declaredBlock.provablyExecutesBefore(basicBlock)) { + String filename = NodeUtil.getSourceName(reference.getNode()); + compiler.report( + JSError.make(filename, + reference.getNode(), + AMBIGUOUS_FUNCTION_DECL, v.name)); + break; + } + } + } + + if (!isDeclaration && !isDeclaredInScope) { + // Don't check the order of refer in externs files. + if (!reference.getNode().isFromExterns()) { + // Special case to deal with var goog = goog || {} + Node grandparent = reference.getGrandparent(); + if (grandparent.isName() + && grandparent.getString() == v.name) { + continue; + } + + // Only generate warnings if the scopes do not match in order + // to deal with possible forward declarations and recursion + if (reference.getScope() == v.scope) { + String filename = NodeUtil.getSourceName(reference.getNode()); + compiler.report( + JSError.make(filename, + reference.getNode(), + checkLevel, + UNDECLARED_REFERENCE, v.name)); + } + } + } + + if (isDeclaration) { + blocksWithDeclarations.add(basicBlock); + isDeclaredInScope = true; + } + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VariableRenamingPolicy.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VariableRenamingPolicy.java new file mode 100644 index 0000000..a247329 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VariableRenamingPolicy.java @@ -0,0 +1,40 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Policies to determine which variables should be renamed. + */ +public enum VariableRenamingPolicy { + /** + * Rename no variables. + */ + OFF, + + /** + * Rename local variables only. + */ + LOCAL, + + /** + * Rename all variables and functions unless they are exported or externed. + */ + ALL, + + // for transitioning off old flags. not for public consumption. + UNSPECIFIED +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VariableVisibilityAnalysis.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VariableVisibilityAnalysis.java new file mode 100644 index 0000000..0880bc3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VariableVisibilityAnalysis.java @@ -0,0 +1,150 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.Node; +import java.util.Map; + +/** + * An analysis pass that determines the visibility of variables -- that is, + * whether a variable is truly local, a local captured by an inner scope, a + * parameter, or a global variable. + * + * SideEffectsAnalysis uses this class to partition a potentially infinite + * number of concrete storage locations into a (small) finite number of + * abstract storage locations based on a variable's storage visibility. + * + * @author dcc@google.com (Devin Coughlin) + */ +class VariableVisibilityAnalysis implements CompilerPass { + + enum VariableVisibility { + + /** Local variable, not captured by closure */ + LOCAL, + + /** Local variable captured by a closure */ + CAPTURED_LOCAL, + + /** + * Formal parameter declaration variable + * + * Parameters are different than local variables because they can be + * aliased by elements of the arguments object. + */ + PARAMETER, + + /** A global variable */ + GLOBAL + } + + private AbstractCompiler compiler; + + /** + * Maps the declaring name node for a variable to that variable's + * visibility. + */ + private Map visibilityByDeclaringNameNode; + + public VariableVisibilityAnalysis(AbstractCompiler compiler) { + this.compiler = compiler; + + visibilityByDeclaringNameNode = Maps.newHashMap(); + } + + /** + * Returns the visibility of of a variable, given that variable's declaring + * name node. + * + * The name node's parent must be one of: + *
      +   *    Token.VAR (for a variable declaration)
      +   *    Token.FUNCTION (for a function declaration)
      +   *    Token.PARAM_LIST (for a function formal parameter)
      +   * 
      + * + * The returned visibility will be one of: + *
      +   *    LOCAL_VARIABLE : the variable is a local variable used only in its
      +   *        declared scope
      +   *    CAPTURED_LOCAL_VARIABLE : A local variable that is used in a capturing
      +   *        closure
      +   *     PARAMETER_VARIABLE : the variable is a formal parameter
      +   *     GLOBAL_VARIABLE : the variable is declared in the global scope
      +   *  
      + * + * @param declaringNameNode The name node for a declaration. + */ + public VariableVisibility getVariableVisibility(Node declaringNameNode) { + Node parent = declaringNameNode.getParent(); + + Preconditions.checkArgument(parent.isVar() + || parent.isFunction() + || parent.isParamList()); + + return visibilityByDeclaringNameNode.get(declaringNameNode); + } + + /** + * Determines the visibility class for each variable in root. + */ + @Override + public void process(Node externs, Node root) { + ReferenceCollectingCallback callback = + new ReferenceCollectingCallback(compiler, + ReferenceCollectingCallback.DO_NOTHING_BEHAVIOR); + + NodeTraversal.traverse(compiler, root, callback); + + for (Var variable : callback.getAllSymbols()) { + ReferenceCollection referenceCollection = + callback.getReferences(variable); + + VariableVisibility visibility; + + if (variableIsParameter(variable)) { + visibility = VariableVisibility.PARAMETER; + } else if (variable.isLocal()) { + if (referenceCollection.isEscaped()) { + visibility = VariableVisibility.CAPTURED_LOCAL; + } else { + visibility = VariableVisibility.LOCAL; + } + } else if (variable.isGlobal()) { + visibility = VariableVisibility.GLOBAL; + } else { + throw new IllegalStateException("Un-handled variable visibility for " + + variable); + } + + visibilityByDeclaringNameNode.put(variable.getNameNode(), visibility); + } + } + + /** + * Returns true if the variable is a formal parameter. + */ + private static boolean variableIsParameter(Var variable) { + Node variableParent = variable.getParentNode(); + + return variableParent != null && variableParent.isParamList(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VerboseMessageFormatter.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VerboseMessageFormatter.java new file mode 100644 index 0000000..0d6e622 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/VerboseMessageFormatter.java @@ -0,0 +1,56 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.base.Strings; + +import com.google.javascript.jscomp.CheckLevel; + +/** + * Verbose message formatter. This formatter generates very loud and long + * messages with multi-line source excerpts. + * + */ +class VerboseMessageFormatter extends AbstractMessageFormatter { + VerboseMessageFormatter(SourceExcerptProvider source) { + super(source); + } + + @Override + public String formatError(JSError error) { + return getLevelName(CheckLevel.ERROR) + ": " + format(error); + } + + @Override + public String formatWarning(JSError warning) { + return getLevelName(CheckLevel.WARNING) + ": " + format(warning); + } + + private String format(JSError message) { + String description = message.description; + String sourceName = message.sourceName; + int lineNumber = message.lineNumber; + Region sourceRegion = getSource().getSourceRegion(sourceName, lineNumber); + String lineSource = null; + if (sourceRegion != null) { + lineSource = sourceRegion.getSourceExcerpt(); + } + return String.format("%s at %s line %s %s", description, + (Strings.isNullOrEmpty(sourceName) ? "(unknown source)" : sourceName), + ((lineNumber < 0) ? String.valueOf(lineNumber) : "(unknown line)"), + ((lineSource != null) ? ":\n\n" + lineSource : ".")); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/WarningLevel.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/WarningLevel.java new file mode 100644 index 0000000..79244df --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/WarningLevel.java @@ -0,0 +1,118 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.CompilerOptions; + +/** + * Convert the warnings level to an Options object. + * + */ +public enum WarningLevel { + QUIET, + + DEFAULT, + + VERBOSE; + + public void setOptionsForWarningLevel(CompilerOptions options) { + switch (this) { + case QUIET: + silenceAllWarnings(options); + break; + case DEFAULT: + addDefaultWarnings(options); + break; + case VERBOSE: + addVerboseWarnings(options); + break; + default: + throw new RuntimeException("Unknown warning level."); + } + } + + /** + * Silence all non-essential warnings. + */ + private static void silenceAllWarnings(CompilerOptions options) { + // Just use a ShowByPath warnings guard, so that we don't have + // to maintain a separate class of warnings guards for silencing warnings. + options.addWarningsGuard( + new ShowByPathWarningsGuard( + "the_longest_path_that_cannot_be_expressed_as_a_string")); + + // Allow passes that aren't going to report anything to be skipped. + + options.checkRequires = CheckLevel.OFF; + options.checkProvides = CheckLevel.OFF; + options.checkMissingGetCssNameLevel = CheckLevel.OFF; + options.aggressiveVarCheck = CheckLevel.OFF; + options.checkTypes = false; + options.setWarningLevel(DiagnosticGroups.CHECK_TYPES, CheckLevel.OFF); + options.checkUnreachableCode = CheckLevel.OFF; + options.checkMissingReturn = CheckLevel.OFF; + options.setWarningLevel(DiagnosticGroups.ACCESS_CONTROLS, CheckLevel.OFF); + options.setWarningLevel(DiagnosticGroups.CONST, CheckLevel.OFF); + options.setWarningLevel(DiagnosticGroups.CONSTANT_PROPERTY, CheckLevel.OFF); + options.checkGlobalNamesLevel = CheckLevel.OFF; + options.checkSuspiciousCode = false; + options.checkGlobalThisLevel = CheckLevel.OFF; + options.setWarningLevel(DiagnosticGroups.GLOBAL_THIS, CheckLevel.OFF); + options.setWarningLevel(DiagnosticGroups.ES5_STRICT, CheckLevel.OFF); + options.checkCaja = false; + } + + /** + * Add the default checking pass to the compilation options. + * @param options The CompilerOptions object to set the options on. + */ + private static void addDefaultWarnings(CompilerOptions options) { + options.checkSuspiciousCode = true; + options.checkUnreachableCode = CheckLevel.WARNING; + options.checkControlStructures = true; + } + + /** + * Add all the check pass that are possibly relevant to a non-googler. + * @param options The CompilerOptions object to set the options on. + */ + private static void addVerboseWarnings(CompilerOptions options) { + addDefaultWarnings(options); + + // checkSuspiciousCode needs to be enabled for CheckGlobalThis to get run. + options.checkSuspiciousCode = true; + options.checkGlobalThisLevel = CheckLevel.WARNING; + options.checkSymbols = true; + options.checkMissingReturn = CheckLevel.WARNING; + + // checkTypes has the side-effect of asserting that the + // correct number of arguments are passed to a function. + // Because the CodingConvention used with the web service does not provide a + // way for optional arguments to be specified, these warnings may result in + // false positives. + options.checkTypes = true; + options.checkGlobalNamesLevel = CheckLevel.WARNING; + options.aggressiveVarCheck = CheckLevel.WARNING; + options.setWarningLevel( + DiagnosticGroups.MISSING_PROPERTIES, CheckLevel.WARNING); + options.setWarningLevel( + DiagnosticGroups.DEPRECATED, CheckLevel.WARNING); + options.setWarningLevel( + DiagnosticGroups.ES5_STRICT, CheckLevel.WARNING); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/WarningsGuard.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/WarningsGuard.java new file mode 100644 index 0000000..2cd4f6c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/WarningsGuard.java @@ -0,0 +1,108 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.CheckLevel; + +import java.io.Serializable; + +/** + * Class that allows to flexibly manage what to do with a reported + * warning/error. + * + * Guard has several choices: + * - return OFF - suppress the warning/error + * - return WARNING + * - return ERROR report it with high severity + * - return null. Does not know what to do with it. Lets the other guard + * decide what to do with it. + * + * Although the interface is very simple, it allows you easily customize what + * warnings you are interested in. + * + * For example there are could be several implementations: + * StrictGuard - {return ERROR}. All warnings should be treat as errors. + * SilentGuard - {if (WARNING) return OFF}. Suppress all warnings but still + * fail if JS has errors. + * WhitelistGuard (if !whitelistErrors.contains(error) return ERROR) return + * error if it does not present in the whitelist. + * + * @author anatol@google.com (Anatol Pomazau) + */ +public abstract class WarningsGuard implements Serializable { + + public static enum Priority { + MAX(1), + MIN(100), + STRICT(100), + DEFAULT(50), + SUPPRESS_BY_WHITELIST(40), + SUPPRESS_DOC(20), + FILTER_BY_PATH(1); + + final int value; + + Priority(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + /** + * Returns a new check level for a given error. OFF - suppress it, ERROR - + * report as error. null means that this guard does not know what to do + * with the error. Null is extremely helpful when you have a chain of + * guards. If current guard returns null, then the next in the chain should + * process it. + * + * @param error a reported error. + * @return what level given error should have. + */ + public abstract CheckLevel level(JSError error); + + /** + * The priority in which warnings guards are applied. Lower means the + * guard will be applied sooner. Expressed on a scale of 1 to 100. + */ + protected int getPriority() { + return Priority.DEFAULT.value; + } + + /** + * Returns whether all warnings in the given diagnostic group will be + * filtered out. Used to determine which passes to skip. + * + * @param group A group of DiagnosticTypes. + * @return Whether all warnings of these types are disabled by this guard. + */ + protected boolean disables(DiagnosticGroup group) { + return false; + } + + /** + * Returns whether any of the warnings in the given diagnostic group will be + * upgraded to a warning or error. + * + * @param group A group of DiagnosticTypes. + * @return Whether any warnings of these types are enabled by this guard. + */ + protected boolean enables(DiagnosticGroup group) { + return false; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/WhitelistWarningsGuard.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/WhitelistWarningsGuard.java new file mode 100644 index 0000000..19ddc9e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/WhitelistWarningsGuard.java @@ -0,0 +1,294 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Charsets; +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.common.collect.TreeMultimap; +import com.google.common.io.CharStreams; +import com.google.common.io.Files; +import com.google.common.io.InputSupplier; +import com.google.javascript.jscomp.CheckLevel; + +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.io.Reader; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * An extension of {@code WarningsGuard} that provides functionality to maintain + * a list of warnings (white-list). It is subclasses' responsibility to decide + * what to do with the white-list by implementing the {@code level} function. + * Warnings are defined by the name of the JS file and the first line of + * warnings description. + * + * @author anatol@google.com (Anatol Pomazau) + * @author bashir@google.com (Bashir Sadjad) + */ +public class WhitelistWarningsGuard extends WarningsGuard { + private static final Splitter LINE_SPLITTER = Splitter.on("\n"); + + /** The set of white-listed warnings, same format as {@code formatWarning}. */ + private final Set whitelist; + + /** Pattern to match line number in error descriptions. */ + private static final Pattern LINE_NUMBER = Pattern.compile(":-?\\d+"); + + /** + * This class depends on an input set that contains the white-list. The format + * of each white-list string is: + * :? + * # + * + * @param whitelist The set of JS-warnings that are white-listed. This is + * expected to have similar format as {@code formatWarning(JSError)}. + */ + public WhitelistWarningsGuard(Set whitelist) { + Preconditions.checkNotNull(whitelist); + this.whitelist = normalizeWhitelist(whitelist); + } + + /** + * Loads legacy warnings list from the set of strings. During development line + * numbers are changed very often - we just cut them and compare without ones. + * + * @return known legacy warnings without line numbers. + */ + private static Set normalizeWhitelist(Set whitelist) { + Set result = Sets.newHashSet(); + for (String line : whitelist) { + String trimmed = line.trim(); + if (trimmed.isEmpty() || trimmed.charAt(0) == '#') { + // strip out empty lines and comments. + continue; + } + + // Strip line number for matching. + result.add(LINE_NUMBER.matcher(trimmed).replaceFirst(":")); + } + return ImmutableSet.copyOf(result); + } + + @Override + public CheckLevel level(JSError error) { + if (containWarning(formatWarning(error))) { + // If the message matches the guard we use WARNING, so that it + // - Shows up on stderr, and + // - Gets caught by the WhitelistBuilder downstream in the pipeline + return CheckLevel.WARNING; + } + + return null; + } + + /** + * Determines whether a given warning is included in the white-list. + * + * @param formattedWarning the warning formatted by {@code formatWarning} + * @return whether the given warning is white-listed or not. + */ + protected boolean containWarning(String formattedWarning) { + return whitelist.contains(formattedWarning); + } + + @Override + public int getPriority() { + return WarningsGuard.Priority.SUPPRESS_BY_WHITELIST.getValue(); + } + + /** Creates a warnings guard from a file. */ + public static WhitelistWarningsGuard fromFile(File file) { + return new WhitelistWarningsGuard(loadWhitelistedJsWarnings(file)); + } + + /** + * Loads legacy warnings list from the file. + * @return The lines of the file. + */ + public static Set loadWhitelistedJsWarnings(File file) { + return loadWhitelistedJsWarnings( + Files.newReaderSupplier(file, Charsets.UTF_8)); + } + + /** + * Loads legacy warnings list from the file. + * @return The lines of the file. + */ + protected static Set loadWhitelistedJsWarnings( + InputSupplier supplier) { + try { + return loadWhitelistedJsWarnings(supplier.getInput()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Loads legacy warnings list from the file. + * @return The lines of the file. + */ + // TODO(nicksantos): This is a weird API. + static Set loadWhitelistedJsWarnings(Reader reader) + throws IOException { + Preconditions.checkNotNull(reader); + Set result = Sets.newHashSet(); + + for (String line : CharStreams.readLines(reader)) { + result.add(line); + } + + return result; + } + + public static String formatWarning(JSError error) { + return formatWarning(error, false); + } + + /** + * @param withMetaData If true, include metadata that's useful to humans + * This metadata won't be used for matching the warning. + */ + public static String formatWarning(JSError error, boolean withMetaData) { + StringBuilder sb = new StringBuilder(); + sb.append(error.sourceName).append(":"); + if (withMetaData) { + sb.append(error.lineNumber); + } + List lines = ImmutableList.copyOf( + LINE_SPLITTER.split(error.description)); + sb.append(" ").append(lines.get(0)); + + // Add the rest of the message as a comment. + if (withMetaData) { + for (int i = 1; i < lines.size(); i++) { + sb.append("\n# ").append(lines.get(i)); + } + sb.append("\n"); + } + + return sb.toString(); + } + + public static String getFirstLine(String warning) { + int lineLength = warning.indexOf('\n'); + if (lineLength > 0) { + warning = warning.substring(0, lineLength); + } + return warning; + } + + public static class WhitelistBuilder implements ErrorHandler { + private final Set warnings = Sets.newLinkedHashSet(); + private String productName = null; + private String generatorTarget = null; + private String headerNote = null; + + /** Fill in your product name to get a fun message! */ + public WhitelistBuilder setProductName(String name) { + this.productName = name; + return this; + } + + /** Fill in instructions on how to generate this whitelist. */ + public WhitelistBuilder setGeneratorTarget(String name) { + this.generatorTarget = name; + return this; + } + + /** A note to include at the top of the whitelist file. */ + public WhitelistBuilder setNote(String note) { + this.headerNote = note; + return this; + } + + /** We now always record the line number. */ + @Deprecated + public WhitelistBuilder setWithLineNumber(boolean line) { + return this; + } + + @Override + public void report(CheckLevel level, JSError error) { + warnings.add(error); + } + + /** + * Writes the warnings collected in a format that the WhitelistWarningsGuard + * can read back later. + */ + public void writeWhitelist(File out) throws IOException { + PrintStream stream = new PrintStream(out); + appendWhitelist(stream); + stream.close(); + } + + /** + * Writes the warnings collected in a format that the WhitelistWarningsGuard + * can read back later. + */ + public void appendWhitelist(PrintStream out) { + out.append( + "# This is a list of legacy warnings that have yet to be fixed.\n"); + + if (productName != null) { + out.append("# Please find some time and fix at least one of them " + + "and it will be the happiest day for " + productName + ".\n"); + } + + if (generatorTarget != null) { + out.append("# When you fix any of these warnings, run " + + generatorTarget + " task.\n"); + } + + if (headerNote != null) { + out.append("#" + + Joiner.on("\n# ").join(Splitter.on("\n").split(headerNote)) + + "\n"); + } + + Multimap warningsByType = TreeMultimap.create(); + for (JSError warning : warnings) { + warningsByType.put( + warning.getType(), + formatWarning(warning, true /* withLineNumber */)); + } + + for (DiagnosticType type : warningsByType.keySet()) { + out.append("\n# Warning ") + .append(type.key) + .append(": ") + .println(Iterables.get( + LINE_SPLITTER.split(type.format.toPattern()), 0)); + + for (String warning : warningsByType.get(type)) { + out.println(warning); + } + } + out.flush(); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/XtbMessageBundle.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/XtbMessageBundle.java new file mode 100644 index 0000000..f975abd --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/XtbMessageBundle.java @@ -0,0 +1,221 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; + +import java.io.*; +import java.util.*; + +import javax.annotation.Nullable; +import javax.xml.XMLConstants; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +/** + * A MessageBundle that parses messages from an XML Translation Bundle (XTB) + * file. + * + */ +@SuppressWarnings("sunapi") +public class XtbMessageBundle implements MessageBundle { + private static final SecureEntityResolver NOOP_RESOLVER + = new SecureEntityResolver(); + + private final Map messages; + private final JsMessage.IdGenerator idGenerator; + + public XtbMessageBundle( + InputStream xtb, @Nullable String projectId, boolean unused) { + this(xtb, projectId); + } + + /** + * Creates an instance and initializes it with the messages in an XTB file. + * + * @param xtb the XTB file as a byte stream + * @param projectId the translation console project id (i.e. name) + */ + public XtbMessageBundle(InputStream xtb, @Nullable String projectId) { + Preconditions.checkState(!"".equals(projectId)); + this.messages = Maps.newHashMap(); + this.idGenerator = new GoogleJsMessageIdGenerator(projectId); + + try { + // Use a SAX parser for speed and less memory usage. + SAXParser parser = createSAXParser(); + XMLReader reader = parser.getXMLReader(); + Handler contentHandler = new Handler(); + reader.setContentHandler(contentHandler); + reader.parse(new InputSource(xtb)); + } catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } catch (SAXException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + // Inlined from guava-internal. + private SAXParser createSAXParser() + throws ParserConfigurationException, SAXException { + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setValidating(false); + factory.setXIncludeAware(false); + factory.setFeature( + "http://xml.org/sax/features/external-general-entities", false); + factory.setFeature( + "http://xml.org/sax/features/external-parameter-entities",false); + factory.setFeature( + "http://apache.org/xml/features/nonvalidating/load-external-dtd", + false); + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + + SAXParser parser = factory.newSAXParser(); + XMLReader xmlReader = parser.getXMLReader(); + xmlReader.setEntityResolver(NOOP_RESOLVER); + return parser; + } + + @Override + public JsMessage getMessage(String id) { + return messages.get(id); + } + + @Override + public JsMessage.IdGenerator idGenerator() { + return idGenerator; + } + + @Override + public Iterable getAllMessages() { + return Iterables.unmodifiableIterable(messages.values()); + } + + /** + * A {@link ContentHandler} that creates a {@link JsMessage} for each message + * parsed from an XML Translation Bundle (XTB) file. + */ + private class Handler implements ContentHandler { + private static final String BUNDLE_ELEM_NAME = "translationbundle"; + private static final String LANG_ATT_NAME = "lang"; + + private static final String TRANSLATION_ELEM_NAME = "translation"; + private static final String MESSAGE_ID_ATT_NAME = "id"; + + private static final String PLACEHOLDER_ELEM_NAME = "ph"; + private static final String PLACEHOLDER_NAME_ATT_NAME = "name"; + + String lang; + JsMessage.Builder msgBuilder; + + @Override + public void setDocumentLocator(Locator locator) {} + + @Override + public void startDocument() {} + + @Override + public void endDocument() {} + + @Override + public void startPrefixMapping(String prefix, String uri) {} + + @Override + public void endPrefixMapping(String prefix) {} + + @Override + public void startElement(String uri, String localName, String qName, + Attributes atts) { + if (BUNDLE_ELEM_NAME.equals(qName)) { + Preconditions.checkState(lang == null); + lang = atts.getValue(LANG_ATT_NAME); + Preconditions.checkState(lang != null && !lang.isEmpty()); + } else if (TRANSLATION_ELEM_NAME.equals(qName)) { + Preconditions.checkState(msgBuilder == null); + String id = atts.getValue(MESSAGE_ID_ATT_NAME); + Preconditions.checkState(id != null && !id.isEmpty()); + msgBuilder = new JsMessage.Builder(id); + } else if (PLACEHOLDER_ELEM_NAME.equals(qName)) { + Preconditions.checkState(msgBuilder != null); + String phRef = atts.getValue(PLACEHOLDER_NAME_ATT_NAME); + phRef = JsMessageVisitor.toLowerCamelCaseWithNumericSuffixes(phRef); + msgBuilder.appendPlaceholderReference(phRef); + } + } + + @Override + public void endElement(String uri, String localName, String qName) { + if (TRANSLATION_ELEM_NAME.equals(qName)) { + Preconditions.checkState(msgBuilder != null); + if (!msgBuilder.hasParts()) { + msgBuilder.appendStringPart(""); + } + String key = msgBuilder.getKey(); + messages.put(key, msgBuilder.build()); + msgBuilder = null; + } + } + + @Override + public void characters(char ch[], int start, int length) { + if (msgBuilder != null) { + // Append a string literal to the message. + msgBuilder.appendStringPart(String.valueOf(ch, start, length)); + } + } + + @Override + public void ignorableWhitespace(char ch[], int start, int length) { + if (msgBuilder != null) { + // Preserve whitespace in messages. + msgBuilder.appendStringPart(String.valueOf(ch, start, length)); + } + } + + @Override + public void processingInstruction(String target, String data) {} + + @Override + public void skippedEntity(String name) {} + } + + /** + * A secure EntityResolver that returns an empty string in response to + * any attempt to resolve an external entity. The class is used by our + * secure version of the internal saxon SAX parser. + */ + private static final class SecureEntityResolver implements EntityResolver { + + @Override + public InputSource resolveEntity(String publicId, String systemId) { + return new InputSource(new StringReader("")); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ant/AntErrorManager.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ant/AntErrorManager.java new file mode 100644 index 0000000..d17cdb5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ant/AntErrorManager.java @@ -0,0 +1,68 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.ant; + +import com.google.javascript.jscomp.BasicErrorManager; +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.JSError; +import com.google.javascript.jscomp.MessageFormatter; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; + +/** + * An error manager that pipes warnings and errors properly into the Ant + * task infrastructure. + */ +public final class AntErrorManager + extends BasicErrorManager { + private final MessageFormatter formatter; + private final Task task; + + public AntErrorManager(MessageFormatter formatter, Task task) { + this.formatter = formatter; + this.task = task; + } + + @Override + public void println(CheckLevel level, JSError error) { + switch (level) { + case ERROR: + this.task.log(error.format(level, this.formatter), Project.MSG_ERR); + break; + case WARNING: + this.task.log(error.format(level, this.formatter), Project.MSG_WARN); + break; + case OFF: + break; + } + } + + @Override + protected void printSummary() { + String message = + getErrorCount() + " error(s), " + getWarningCount() + " warning(s)"; + + if (getTypedPercent() > 0.0) { + message += ", " + getTypedPercent() + " typed"; + } + + int level = (getErrorCount() + getWarningCount() == 0) ? + Project.MSG_INFO : Project.MSG_WARN; + this.task.log(message, level); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ant/CompileTask.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ant/CompileTask.java new file mode 100644 index 0000000..1adfb25 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ant/CompileTask.java @@ -0,0 +1,586 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.ant; + +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.CommandLineRunner; +import com.google.javascript.jscomp.CompilationLevel; +import com.google.javascript.jscomp.Compiler; +import com.google.javascript.jscomp.CompilerOptions; +import com.google.javascript.jscomp.DiagnosticGroup; +import com.google.javascript.jscomp.DiagnosticGroups; +import com.google.javascript.jscomp.SourceFile; +import com.google.javascript.jscomp.MessageFormatter; +import com.google.javascript.jscomp.Result; +import com.google.javascript.jscomp.WarningLevel; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.FileList; +import org.apache.tools.ant.types.Parameter; +import org.apache.tools.ant.types.Path; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.Charset; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; + +/** + * This class implements a simple Ant task to do almost the same as + * CommandLineRunner. + * + * Most of the public methods of this class are entry points for the + * Ant code to hook into. + * + */ +public final class CompileTask + extends Task { + private CompilerOptions.LanguageMode languageIn; + private WarningLevel warningLevel; + private boolean debugOptions; + private String encoding = "UTF-8"; + private String outputEncoding = "UTF-8"; + private CompilationLevel compilationLevel; + private boolean customExternsOnly; + private boolean manageDependencies; + private boolean prettyPrint; + private boolean printInputDelimiter; + private boolean generateExports; + private boolean replaceProperties; + private boolean forceRecompile; + private String replacePropertiesPrefix; + private File outputFile; + private final List defineParams; + private final List externFileLists; + private final List sourceFileLists; + private final List sourcePaths; + private final List warnings; + + public CompileTask() { + this.languageIn = CompilerOptions.LanguageMode.ECMASCRIPT3; + this.warningLevel = WarningLevel.DEFAULT; + this.debugOptions = false; + this.compilationLevel = CompilationLevel.SIMPLE_OPTIMIZATIONS; + this.customExternsOnly = false; + this.manageDependencies = false; + this.prettyPrint = false; + this.printInputDelimiter = false; + this.generateExports = false; + this.replaceProperties = false; + this.forceRecompile = false; + this.replacePropertiesPrefix = "closure.define."; + this.defineParams = Lists.newLinkedList(); + this.externFileLists = Lists.newLinkedList(); + this.sourceFileLists = Lists.newLinkedList(); + this.sourcePaths = Lists.newLinkedList(); + this.warnings = Lists.newLinkedList(); + } + + /** + * Set the language to which input sources conform. + * @param value The name of the language. + * (ECMASCRIPT3, ECMASCRIPT5, ECMASCRIPT5_STRICT). + */ + public void setLanguageIn(String value) { + if (value.equals("ECMASCRIPT5_STRICT") || value.equals("ES5_STRICT")) { + this.languageIn = CompilerOptions.LanguageMode.ECMASCRIPT5_STRICT; + } else if (value.equals("ECMASCRIPT5") || value.equals("ES5")) { + this.languageIn = CompilerOptions.LanguageMode.ECMASCRIPT5; + } else if (value.equals("ECMASCRIPT3") || value.equals("ES3")) { + this.languageIn = CompilerOptions.LanguageMode.ECMASCRIPT3; + } else { + throw new BuildException( + "Unrecognized 'languageIn' option value (" + value + ")"); + } + } + + /** + * Set the warning level. + * @param value The warning level by string name. (default, quiet, verbose). + */ + public void setWarning(String value) { + if ("default".equalsIgnoreCase(value)) { + this.warningLevel = WarningLevel.DEFAULT; + } else if ("quiet".equalsIgnoreCase(value)) { + this.warningLevel = WarningLevel.QUIET; + } else if ("verbose".equalsIgnoreCase(value)) { + this.warningLevel = WarningLevel.VERBOSE; + } else { + throw new BuildException( + "Unrecognized 'warning' option value (" + value + ")"); + } + } + + /** + * Enable debugging options. + * @param value True if debug mode is enabled. + */ + public void setDebug(boolean value) { + this.debugOptions = value; + } + + /** + * Set the compilation level. + * @param value The optimization level by string name. + * (whitespace, simple, advanced). + */ + public void setCompilationLevel(String value) { + if ("simple".equalsIgnoreCase(value)) { + this.compilationLevel = CompilationLevel.SIMPLE_OPTIMIZATIONS; + } else if ("advanced".equalsIgnoreCase(value)) { + this.compilationLevel = CompilationLevel.ADVANCED_OPTIMIZATIONS; + } else if ("whitespace".equalsIgnoreCase(value)) { + this.compilationLevel = CompilationLevel.WHITESPACE_ONLY; + } else { + throw new BuildException( + "Unrecognized 'compilation' option value (" + value + ")"); + } + } + + public void setManageDependencies(boolean value) { + this.manageDependencies = value; + } + + /** + * Use only custom externs. + */ + public void setCustomExternsOnly(boolean value) { + this.customExternsOnly = value; + } + + /** + * Set output file. + */ + public void setOutput(File value) { + this.outputFile = value; + } + + /** + * Set the replacement property prefix. + */ + public void setReplacePropertiesPrefix(String value) { + this.replacePropertiesPrefix = value; + } + + /** + * Whether to replace {@code @define} lines with properties + */ + public void setReplaceProperties(boolean value) { + this.replaceProperties = value; + } + + /** + * Set input file encoding + */ + public void setEncoding(String encoding) { + this.encoding = encoding; + } + + /** + * Set output file encoding + */ + public void setOutputEncoding(String outputEncoding) { + this.outputEncoding = outputEncoding; + } + + /** + * Set pretty print formatting option + */ + public void setPrettyPrint(boolean pretty) { + this.prettyPrint = pretty; + } + + /** + * Set print input delimiter formatting option + */ + public void setPrintInputDelimiter(boolean print) { + this.printInputDelimiter = print; + } + + /** + * Set force recompile option + */ + public void setForceRecompile(boolean forceRecompile) { + this.forceRecompile = forceRecompile; + } + + /** + * Set generateExports option + */ + public void setGenerateExports(boolean generateExports) { + this.generateExports = generateExports; + } + + /** + * Sets the externs file. + */ + public void addExterns(FileList list) { + this.externFileLists.add(list); + } + + /** + * Adds a entry + * + * Each warning entry must have two attributes, group and level. Group must + * contain one of the constants from DiagnosticGroups (e.g., + * "ACCESS_CONTROLS"), while level must contain one of the CheckLevel + * constants ("ERROR", "WARNING" or "OFF"). + */ + public void addWarning(Warning warning) { + this.warnings.add(warning); + } + + /** + * Sets the source files. + */ + public void addSources(FileList list) { + this.sourceFileLists.add(list); + } + + /** + * Adds a entry. + */ + public void addPath(Path list) { + this.sourcePaths.add(list); + } + + @Override + public void execute() { + if (this.outputFile == null) { + throw new BuildException("outputFile attribute must be set"); + } + + Compiler.setLoggingLevel(Level.OFF); + + CompilerOptions options = createCompilerOptions(); + Compiler compiler = createCompiler(options); + + List externs = findExternFiles(); + List sources = findSourceFiles(); + + if (isStale() || forceRecompile) { + log("Compiling " + sources.size() + " file(s) with " + + externs.size() + " extern(s)"); + + Result result = compiler.compile(externs, sources, options); + if (result.success) { + writeResult(compiler.toSource()); + } else { + throw new BuildException("Compilation failed."); + } + } else { + log("None of the files changed. Compilation skipped."); + } + } + + private CompilerOptions createCompilerOptions() { + CompilerOptions options = new CompilerOptions(); + + this.compilationLevel.setOptionsForCompilationLevel(options); + if (this.debugOptions) { + this.compilationLevel.setDebugOptionsForCompilationLevel(options); + } + + options.prettyPrint = this.prettyPrint; + options.printInputDelimiter = this.printInputDelimiter; + options.generateExports = this.generateExports; + + options.setLanguageIn(this.languageIn); + + this.warningLevel.setOptionsForWarningLevel(options); + options.setManageClosureDependencies(manageDependencies); + + if (replaceProperties) { + convertPropertiesMap(options); + } + + convertDefineParameters(options); + + for (Warning warning : warnings) { + CheckLevel level = warning.getLevel(); + String groupName = warning.getGroup(); + DiagnosticGroup group = new DiagnosticGroups().forName(groupName); + if (group == null) { + throw new BuildException( + "Unrecognized 'warning' option value (" + groupName + ")"); + } + options.setWarningLevel(group, level); + } + + return options; + } + + /** + * Creates a new {@code } nested element. Supports name and value + * attributes. + */ + public Parameter createDefine() { + Parameter param = new Parameter(); + defineParams.add(param); + return param; + } + + /** + * Converts {@code } nested elements into Compiler {@code @define} + * replacements. Note: unlike project properties, {@code } elements + * do not need to be named starting with the replacement prefix. + */ + private void convertDefineParameters(CompilerOptions options) { + for (Parameter p : defineParams) { + String key = p.getName(); + Object value = p.getValue(); + + if (!setDefine(options, key, value)) { + log("Unexpected @define value for name=" + key + "; value=" + value); + } + } + } + + /** + * Converts project properties beginning with the replacement prefix + * into Compiler {@code @define} replacements. + * + * @param options + */ + private void convertPropertiesMap(CompilerOptions options) { + @SuppressWarnings("unchecked") + Map props = getProject().getProperties(); + for (Map.Entry entry : props.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + + if (key.startsWith(replacePropertiesPrefix)) { + key = key.substring(replacePropertiesPrefix.length()); + + if (!setDefine(options, key, value)) { + log("Unexpected property value for key=" + key + "; value=" + value); + } + } + } + } + + /** + * Maps Ant-style values (e.g., from Properties) into expected + * Closure {@code @define} literals + * + * @return True if the {@code @define} replacement succeeded, false if + * the variable's value could not be mapped properly. + */ + private boolean setDefine(CompilerOptions options, + String key, Object value) { + boolean success = false; + + if (value instanceof String) { + final boolean isTrue = "true".equals(value); + final boolean isFalse = "false".equals(value); + + if (isTrue || isFalse) { + options.setDefineToBooleanLiteral(key, isTrue); + } else { + try { + double dblTemp = Double.parseDouble((String) value); + options.setDefineToDoubleLiteral(key, dblTemp); + } catch (NumberFormatException nfe) { + // Not a number, assume string + options.setDefineToStringLiteral(key, (String) value); + } + } + + success = true; + } else if (value instanceof Boolean) { + options.setDefineToBooleanLiteral(key, (Boolean) value); + success = true; + } else if (value instanceof Integer) { + options.setDefineToNumberLiteral(key, (Integer) value); + success = true; + } else if (value instanceof Double) { + options.setDefineToDoubleLiteral(key, (Double) value); + success = true; + } + + return success; + } + + private Compiler createCompiler(CompilerOptions options) { + Compiler compiler = new Compiler(); + MessageFormatter formatter = + options.errorFormat.toFormatter(compiler, false); + AntErrorManager errorManager = new AntErrorManager(formatter, this); + compiler.setErrorManager(errorManager); + return compiler; + } + + private List findExternFiles() { + List files = Lists.newLinkedList(); + if (!this.customExternsOnly) { + files.addAll(getDefaultExterns()); + } + + for (FileList list : this.externFileLists) { + files.addAll(findJavaScriptFiles(list)); + } + + return files; + } + + private List findSourceFiles() { + List files = Lists.newLinkedList(); + + for (FileList list : this.sourceFileLists) { + files.addAll(findJavaScriptFiles(list)); + } + + for (Path list : this.sourcePaths) { + files.addAll(findJavaScriptFiles(list)); + } + + return files; + } + + /** + * Translates an Ant file list into the file format that the compiler + * expects. + */ + private List findJavaScriptFiles(FileList fileList) { + List files = Lists.newLinkedList(); + File baseDir = fileList.getDir(getProject()); + + for (String included : fileList.getFiles(getProject())) { + files.add(SourceFile.fromFile(new File(baseDir, included), + Charset.forName(encoding))); + } + + return files; + } + + /** + * Translates an Ant Path into the file list format that the compiler + * expects. + */ + private List findJavaScriptFiles(Path path) { + List files = Lists.newArrayList(); + + for (String included : path.list()) { + files.add(SourceFile.fromFile(new File(included), + Charset.forName(encoding))); + } + + return files; + } + + /** + * Gets the default externs set. + * + * Adapted from {@link CommandLineRunner}. + */ + private List getDefaultExterns() { + try { + return CommandLineRunner.getDefaultExterns(); + } catch (IOException e) { + throw new BuildException(e); + } + } + + private void writeResult(String source) { + if (this.outputFile.getParentFile().mkdirs()) { + log("Created missing parent directory " + + this.outputFile.getParentFile(), Project.MSG_DEBUG); + } + + try { + OutputStreamWriter out = new OutputStreamWriter( + new FileOutputStream(this.outputFile), outputEncoding); + out.append(source); + out.flush(); + out.close(); + } catch (IOException e) { + throw new BuildException(e); + } + + log("Compiled JavaScript written to " + this.outputFile.getAbsolutePath(), + Project.MSG_DEBUG); + } + + /** + * Determine if compilation must actually happen, i.e. if any input file + * (extern or source) has changed after the outputFile was last modified. + * + * @return true if compilation should happen + */ + private boolean isStale() { + long lastRun = outputFile.lastModified(); + long sourcesLastModified = Math.max( + getLastModifiedTime(this.sourceFileLists), + getLastModifiedTime(this.sourcePaths)); + long externsLastModified = getLastModifiedTime(this.externFileLists); + + return lastRun <= sourcesLastModified || lastRun <= externsLastModified; + } + + /** + * Returns the most recent modified timestamp of the file collection. + * + * Note: this must be combined into one method to account for both + * Path and FileList erasure types. + * + * @param fileLists Collection of FileList or Path + * @return Most recent modified timestamp + */ + private long getLastModifiedTime(List fileLists) { + long lastModified = 0; + + for (Object entry : fileLists) { + if (entry instanceof FileList) { + FileList list = (FileList) entry; + + for (String fileName : list.getFiles(this.getProject())) { + File path = list.getDir(this.getProject()); + File file = new File(path, fileName); + lastModified = Math.max(getLastModifiedTime(file), lastModified); + } + } else if (entry instanceof Path) { + Path path = (Path) entry; + for (String src : path.list()) { + File file = new File(src); + lastModified = Math.max(getLastModifiedTime(file), lastModified); + } + } + } + + return lastModified; + } + + /** + * Returns the last modified timestamp of the given File. + */ + private long getLastModifiedTime(File file) { + long fileLastModified = file.lastModified(); + // If the file is absent, we don't know if it changed (maybe was deleted), + // so assume it has just changed. + if (fileLastModified == 0) { + fileLastModified = new Date().getTime(); + } + return fileLastModified; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ant/Warning.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ant/Warning.java new file mode 100644 index 0000000..74c9c5e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/ant/Warning.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.ant; + +import com.google.javascript.jscomp.CheckLevel; + +/** Simple representation of a warning flag in Ant */ +public class Warning { + private String group; + private CheckLevel level; + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public CheckLevel getLevel() { + return level; + } + + public void setLevel(CheckLevel level) { + this.level = level; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/DependencyInfo.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/DependencyInfo.java new file mode 100644 index 0000000..804e80f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/DependencyInfo.java @@ -0,0 +1,39 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.deps; + +import java.util.Collection; + +/** + * A data structure for JS dependency information for a single .js file. + * + * @author agrieve@google.com (Andrew Grieve) + */ +public interface DependencyInfo { + + /** Gets the unique name / path of this file. */ + public String getName(); + + /** Gets the path of this file relative to Closure's base.js file. */ + public String getPathRelativeToClosureBase(); + + /** Gets the symbols provided by this file. */ + public Collection getProvides(); + + /** Gets the symbols required by this file. */ + public Collection getRequires(); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/DepsFileParser.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/DepsFileParser.java new file mode 100644 index 0000000..e6deb73 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/DepsFileParser.java @@ -0,0 +1,174 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.deps; + +import com.google.common.base.CharMatcher; +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.ErrorManager; + +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A parser that can extract dependency information from existing deps.js files. + * + *

      See //javascript/closure/deps.js for an example file.

      + * + * @author agrieve@google.com (Andrew Grieve) + */ +public class DepsFileParser extends JsFileLineParser { + + private static Logger logger = Logger.getLogger(DepsFileParser.class.getName()); + + /** + * Pattern for matching JavaScript string literals. The group is: + * goog.addDependency({1}); + */ + private final Matcher depMatcher = + Pattern.compile("\\s*goog.addDependency\\((.*)\\);?\\s*").matcher(""); + + /** + * Pattern for matching the args of a goog.addDependency(). The group is: + * goog.addDependency({1}, {2}, {3}); + */ + private final Matcher depArgsMatch = + Pattern.compile("\\s*([^,]*), (\\[[^\\]]*\\]), (\\[[^\\]]*\\])\\s*").matcher(""); + + /** + * The dependency information extracted from the current file. + */ + private List depInfos; + + /** Translates paths in different build systems. */ + private final Function pathTranslator; + + /** + * Constructor + * + * @param errorManager Handles parse errors. + */ + public DepsFileParser(ErrorManager errorManager) { + this(Functions.identity(), errorManager); + } + + /** + * @param pathTranslator Translates paths in different build systems. + * @param errorManager Handles parse errors. + */ + public DepsFileParser(Function pathTranslator, + ErrorManager errorManager) { + super(errorManager); + this.pathTranslator = pathTranslator; + } + + /** + * Parses the given file and returns a list of dependency information that it + * contained. + * + * @param filePath Path to the file to parse. + * @return A list of DependencyInfo objects. + * @throws IOException Thrown if the file could not be read. + */ + public List parseFile(String filePath) throws IOException { + return parseFileReader(filePath, new FileReader(filePath)); + } + + /** + * Parses the given file and returns a list of dependency information that it + * contained. + * It uses the passed in fileContents instead of reading the file. + * + * @param filePath Path to the file to parse. + * @param fileContents The contents to parse. + * @return A list of DependencyInfo objects. + */ + public List parseFile(String filePath, String fileContents) { + return parseFileReader(filePath, new StringReader(fileContents)); + } + + + /** + * Parses the file from the given reader and returns a list of + * dependency information that it contained. + * + * @param filePath Path to the file to parse. + * @param reader A reader for the file. + * @return A list of DependencyInfo objects. + */ + public List parseFileReader(String filePath, Reader reader) { + depInfos = Lists.newArrayList(); + logger.fine("Parsing Dep: " + filePath); + doParse(filePath, reader); + return depInfos; + } + + /** + * Extracts dependency information from lines that look like + * goog.addDependency('pathRelativeToClosure', ['provides'], ['requires']); + * Adds the dependencies to depInfos. + * + * @throws ParseException Thrown if the given line has a malformed + * goog.addDependency(). + */ + @Override + protected boolean parseLine(String line) throws ParseException { + boolean hasDependencies = false; + + // Quick sanity check that will catch most cases. This is a performance + // win for people with a lot of JS. + if (line.indexOf("addDependency") != -1) { + depMatcher.reset(line); + // See if the line looks like: goog.addDependency(...) + if (depMatcher.matches()) { + hasDependencies = true; + String addDependencyParams = depMatcher.group(1); + depArgsMatch.reset(addDependencyParams); + // Extract the three parameters. + if (!depArgsMatch.matches()) { + // Although we could recover, we mark this as fatal since there should + // not be problems with generated deps.js files. + throw new ParseException("Invalid arguments to goog.addDependency(). Found: " + + addDependencyParams, true); + } + // Parse the file path. + String path = pathTranslator.apply(parseJsString(depArgsMatch.group(1))); + DependencyInfo depInfo = new SimpleDependencyInfo(path, filePath, + // Parse the provides. + parseJsStringArray(depArgsMatch.group(2)), + // Parse the requires. + parseJsStringArray(depArgsMatch.group(3))); + + if (logger.isLoggable(Level.FINE)) { + logger.fine("Found dep: " + depInfo); + } + depInfos.add(depInfo); + } + } + + return !shortcutMode || hasDependencies || + CharMatcher.WHITESPACE.matchesAllOf(line); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/DepsGenerator.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/DepsGenerator.java new file mode 100644 index 0000000..135c87c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/DepsGenerator.java @@ -0,0 +1,435 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.deps; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.DiagnosticType; +import com.google.javascript.jscomp.ErrorManager; +import com.google.javascript.jscomp.JSError; +import com.google.javascript.jscomp.SourceFile; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; + +/** + * Generates deps.js files by scanning JavaScript files for + * calls to goog.provide(), goog.require() and goog.addDependency(). + * + * @author agrieve@google.com (Andrew Grieve) + */ +public class DepsGenerator { + + public static enum InclusionStrategy { + ALWAYS, + WHEN_IN_SRCS, + DO_NOT_DUPLICATE + } + + private static Logger logger = + Logger.getLogger(DepsGenerator.class.getName()); + + // See the Flags in MakeJsDeps for descriptions of these. + private final Collection srcs; + private final Collection deps; + private final String closurePathAbs; + private final InclusionStrategy mergeStrategy; + final ErrorManager errorManager; + + static final DiagnosticType SAME_FILE_WARNING = DiagnosticType.warning( + "DEPS_SAME_FILE", + "Namespace \"{0}\" is both required and provided in the same file."); + + static final DiagnosticType NEVER_PROVIDED_ERROR = DiagnosticType.error( + "DEPS_NEVER_PROVIDED", + "Namespace \"{0}\" is required but never provided."); + + static final DiagnosticType DUPE_PROVIDES_WARNING = DiagnosticType.warning( + "DEPS_DUPE_PROVIDES", + "Multiple calls to goog.provide(\"{0}\")"); + + static final DiagnosticType MULTIPLE_PROVIDES_ERROR = DiagnosticType.error( + "DEPS_DUPE_PROVIDES", + "Namespace \"{0}\" is already provided in other file {1}"); + + static final DiagnosticType DUPE_REQUIRE_WARNING = DiagnosticType.warning( + "DEPS_DUPE_REQUIRES", + "Namespace \"{0}\" is required multiple times"); + + static final DiagnosticType NO_DEPS_WARNING = DiagnosticType.warning( + "DEPS_NO_DEPS", + "No dependencies found in file"); + + /** + * Creates a new DepsGenerator. + */ + public DepsGenerator( + Collection deps, + Collection srcs, + InclusionStrategy mergeStrategy, + String closurePathAbs, + ErrorManager errorManager) { + this.deps = deps; + this.srcs = srcs; + this.mergeStrategy = mergeStrategy; + this.closurePathAbs = closurePathAbs; + this.errorManager = errorManager; + } + + /** + * Performs the parsing inputs and writing of outputs. + * @throws IOException Occurs upon an IO error. + * @return Returns a String of goog.addDependency calls that will build + * the dependency graph. Returns null if there was an error. + */ + public String computeDependencyCalls() throws IOException { + // Build a map of closure-relative path -> DepInfo. + Map depsFiles = parseDepsFiles(); + logger.fine("preparsedFiles: " + depsFiles); + + // Find all goog.provides & goog.requires in src files + Map jsFiles = parseSources(depsFiles.keySet()); + + // Check if there were any parse errors. + if (errorManager.getErrorCount() > 0) { + return null; + } + + cleanUpDuplicatedFiles(depsFiles, jsFiles); + + // Check for missing provides or other semantic inconsistencies. + validateDependencies(depsFiles.values(), jsFiles.values()); + + if (errorManager.getErrorCount() > 0) { + return null; + } + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + writeDepsContent(depsFiles, jsFiles, new PrintStream(output)); + return new String(output.toByteArray()); + } + + /** + * Removes duplicated depsInfo from jsFiles if this info already present in + * some of the parsed deps.js + * + * @param depsFiles DepsInfo from deps.js dependencies + * @param jsFiles DepsInfo from some of jsSources + */ + protected void cleanUpDuplicatedFiles(Map depsFiles, + Map jsFiles) { + Set depsPathsCopy = Sets.newHashSet(depsFiles.keySet()); + for (String path : depsPathsCopy) { + if (mergeStrategy != InclusionStrategy.WHEN_IN_SRCS) { + jsFiles.remove(path); + } + } + + for (String path : jsFiles.keySet()) { + // If a generated file appears in both the jsFiles and in depsFiles, then + // remove it from depsFiles in order to get the full path the generated + // file. + depsFiles.remove(path); + } + } + + /** + * Reports if there are any dependency problems with the given dependency + * information. Reported problems include: + * - A namespace being provided more than once + * - A namespace being required multiple times from within one file + * - A namespace being provided and required in the same file + * - A namespace being required that is never provided + * @param preparsedFileDepedencies Dependency information from existing + * deps.js files. + * @param parsedFileDependencies Dependency information from parsed .js files. + */ + private void validateDependencies(Iterable preparsedFileDepedencies, + Iterable parsedFileDependencies) { + // Create a map of namespace -> file providing it. + // Also report any duplicate provides. + Map providesMap = Maps.newHashMap(); + addToProvideMap(preparsedFileDepedencies, providesMap); + addToProvideMap(parsedFileDependencies, providesMap); + // For each require in the parsed sources: + for (DependencyInfo depInfo : parsedFileDependencies) { + List requires = Lists.newArrayList(depInfo.getRequires()); + for (int i = 0, l = requires.size(); i < l; ++i) { + String namespace = requires.get(i); + // Check for multiple requires. + if (requires.subList(i + 1, l).contains(namespace)) { + reportDuplicateRequire(namespace, depInfo); + } + // Check for missing provides. + DependencyInfo provider = providesMap.get(namespace); + if (provider == null) { + reportUndefinedNamespace(namespace, depInfo); + } else if (provider == depInfo) { + reportSameFile(namespace, depInfo); + } + } + } + } + + private void reportSameFile(String namespace, DependencyInfo depInfo) { + errorManager.report(CheckLevel.WARNING, + JSError.make(depInfo.getName(), -1, -1, + SAME_FILE_WARNING, namespace)); + } + + private void reportUndefinedNamespace( + String namespace, DependencyInfo depInfo) { + errorManager.report(CheckLevel.ERROR, + JSError.make(depInfo.getName(), -1, -1, + NEVER_PROVIDED_ERROR, namespace)); + } + + private void reportDuplicateProvide(String namespace, DependencyInfo firstDep, + DependencyInfo secondDep) { + if (firstDep == secondDep) { + errorManager.report(CheckLevel.WARNING, + JSError.make(firstDep.getName(), -1, -1, + DUPE_PROVIDES_WARNING, namespace)); + } else { + errorManager.report(CheckLevel.ERROR, + JSError.make(secondDep.getName(), -1, -1, + MULTIPLE_PROVIDES_ERROR, namespace, firstDep.getName())); + } + } + + private void reportDuplicateRequire( + String namespace, DependencyInfo depInfo) { + errorManager.report(CheckLevel.WARNING, + JSError.make(depInfo.getName(), -1, -1, + DUPE_REQUIRE_WARNING, namespace)); + } + + private void reportNoDepsInDepsFile(String filePath) { + errorManager.report(CheckLevel.WARNING, + JSError.make(filePath, -1, -1, NO_DEPS_WARNING)); + } + + /** + * Adds the given DependencyInfos to the given providesMap. Also checks for + * and reports duplicate provides. + */ + private void addToProvideMap(Iterable depInfos, + Map providesMap) { + for (DependencyInfo depInfo : depInfos) { + for (String provide : depInfo.getProvides()) { + DependencyInfo prevValue = providesMap.put(provide, depInfo); + // Check for duplicate provides. + if (prevValue != null) { + reportDuplicateProvide(provide, prevValue, depInfo); + } + } + } + } + + protected DepsFileParser createDepsFileParser() { + DepsFileParser depsParser = new DepsFileParser(errorManager); + depsParser.setShortcutMode(true); + return depsParser; + } + + /** + * Returns whether we should ignore dependency info in the given deps file. + */ + protected boolean shouldSkipDepsFile(SourceFile file) { + return false; + } + + /** + * Parses all deps.js files in the deps list and creates a map of + * closure-relative path -> DependencyInfo. + */ + private Map parseDepsFiles() throws IOException { + DepsFileParser depsParser = createDepsFileParser(); + Map depsFiles = Maps.newHashMap(); + for (SourceFile file : deps) { + if (!shouldSkipDepsFile(file)) { + List + depInfos = depsParser.parseFileReader( + file.getName(), file.getCodeReader()); + if (depInfos.isEmpty()) { + reportNoDepsInDepsFile(file.getName()); + } else { + for (DependencyInfo info : depInfos) { + depsFiles.put(info.getPathRelativeToClosureBase(), info); + } + } + } + } + + // If a deps file also appears in srcs, our build tools will move it + // into srcs. So we need to scan all the src files for addDependency + // calls as well. + for (SourceFile src : srcs) { + if ((new File(src.getName())).exists() && + !shouldSkipDepsFile(src)) { + List srcInfos = + depsParser.parseFileReader(src.getName(), src.getCodeReader()); + for (DependencyInfo info : srcInfos) { + depsFiles.put(info.getPathRelativeToClosureBase(), info); + } + } + } + + return depsFiles; + } + + /** + * Parses all source files for dependency information. + * @param preparsedFiles A set of closure-relative paths. + * Files in this set are not parsed if they are encountered in srcs. + * @return Returns a map of closure-relative paths -> DependencyInfo for the + * newly parsed files. + * @throws IOException Occurs upon an IO error. + */ + private Map parseSources( + Set preparsedFiles) throws IOException { + Map parsedFiles = Maps.newHashMap(); + JsFileParser jsParser = new JsFileParser(errorManager); + + for (SourceFile file : srcs) { + String closureRelativePath = + PathUtil.makeRelative( + closurePathAbs, PathUtil.makeAbsolute(file.getName())); + logger.fine("Closure-relative path: " + closureRelativePath); + + if (InclusionStrategy.WHEN_IN_SRCS == mergeStrategy || + !preparsedFiles.contains(closureRelativePath)) { + DependencyInfo depInfo = + jsParser.parseFile( + file.getName(), closureRelativePath, + file.getCode()); + + // Kick the source out of memory. + file.clearCachedSource(); + parsedFiles.put(closureRelativePath, depInfo); + } + } + + return parsedFiles; + } + + /** + * Creates the content to put into the output deps.js file. If mergeDeps is + * true, then all of the dependency information in the providedDeps will be + * included in the output. + * @throws IOException Occurs upon an IO error. + */ + private void writeDepsContent(Map depsFiles, + Map jsFiles, PrintStream out) + throws IOException { + // Print all dependencies extracted from srcs. + writeDepInfos(out, jsFiles.values()); + + // Print all dependencies extracted from deps. + if (mergeStrategy == InclusionStrategy.ALWAYS) { + // This multimap is just for splitting DepsInfo objects by + // it's definition deps.js file + Multimap infosIndex = Multimaps.index( + depsFiles.values(), + new Function() { + @Override + public String apply(DependencyInfo from) { + return from.getName(); + } + }); + + for (String depsPath : infosIndex.keySet()) { + String path = formatPathToDepsFile(depsPath); + out.println("\n// Included from: " + path); + writeDepInfos(out, infosIndex.get(depsPath)); + } + } + } + + /** + * Format the deps file path so that it can be included in the output file. + */ + protected String formatPathToDepsFile(String path) { + return path; + } + + /** + * Writes goog.addDependency() lines for each DependencyInfo in depInfos. + * @throws IOException Occurs upon an IO error. + */ + private void writeDepInfos(PrintStream out, + Collection depInfos + ) throws IOException { + // Print dependencies. + // Lines look like this: + // goog.addDependency('../../path/to/file.js', ['goog.Delay'], + // ['goog.Disposable', 'goog.Timer']); + for (DependencyInfo depInfo : depInfos) { + Collection provides = depInfo.getProvides(); + Collection requires = depInfo.getRequires(); + + out.print("goog.addDependency('" + + depInfo.getPathRelativeToClosureBase() + "', "); + writeJsArray(out, provides); + out.print(", "); + writeJsArray(out, requires); + out.println(");"); + } + } + + /** + * Prints a list of strings formatted as a JavaScript array of string + * literals. + */ + private static void writeJsArray(PrintStream out, Collection values) { + if (values.isEmpty()) { + out.print("[]"); + } else { + out.print("['"); + out.print(Joiner.on("', '").join(values)); + out.print("']"); + } + } + + static List createSourceFilesFromPaths( + Collection paths) { + List files = Lists.newArrayList(); + for (String path : paths) { + files.add(SourceFile.fromFile(path)); + } + return files; + } + + static List createSourceFilesFromPaths( + String ... paths) { + return createSourceFilesFromPaths(Arrays.asList(paths)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/JsFileLineParser.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/JsFileLineParser.java new file mode 100644 index 0000000..fcdcce0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/JsFileLineParser.java @@ -0,0 +1,261 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.deps; + +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.DiagnosticType; +import com.google.javascript.jscomp.ErrorManager; +import com.google.javascript.jscomp.JSError; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Base class for classes that parse JavaScript sources on a line-by-line basis. Strips comments + * from files and records all parsing errors. + * + * @author agrieve@google.com (Andrew Grieve) + */ +public abstract class JsFileLineParser { + + static final DiagnosticType PARSE_WARNING = DiagnosticType.warning( + "DEPS_PARSE_WARNING", "{0}\n{1}"); + static final DiagnosticType PARSE_ERROR = DiagnosticType.error( + "DEPS_PARSE_ERROR", "{0}\n{1}"); + + boolean shortcutMode = false; + + /** + * Thrown by base classes to signify a problem parsing a line. + */ + static class ParseException extends Exception { + public static final long serialVersionUID = 1L; + private boolean fatal; + + /** + * Constructor. + * + * @param message A description of what caused the exception. + * @param fatal Whether the exception is recoverable. + */ + public ParseException(String message, boolean fatal) { + super(message); + this.fatal = fatal; + } + + public boolean isFatal() { + return fatal; + } + } + + /** Pattern for matching JavaScript string literals. */ + private static final Pattern STRING_LITERAL_PATTERN = Pattern.compile( + "\\s*(?:'((?:\\\\'|[^'])*?)'|\"((?:\\\\\"|[^\"])*?)\")\\s*"); + + /** Matcher used in the parsing string literals. */ + private Matcher valueMatcher = STRING_LITERAL_PATTERN.matcher(""); + + /** Path of the file currently being parsed. */ + String filePath; + /** The line number of the line currently being parsed. */ + int lineNum; + /** Handles error messages. */ + ErrorManager errorManager; + /** Did our parse succeed. */ + boolean parseSucceeded; + + /** + * Constructor. + * + * @param errorManager Parse error handler. + */ + public JsFileLineParser(ErrorManager errorManager) { + this.errorManager = errorManager; + } + + /** + * In shortcut mode, the file line parser can stop reading early if + * it thinks it found enough information. + * + * For example, many parsers assume that dependency information never + * shows up after "real" code. + */ + public void setShortcutMode(boolean mode) { + this.shortcutMode = mode; + } + + public boolean didParseSucceed() { + return parseSucceeded; + } + + /** + * Performs the line-by-line parsing of the given fileContents. This method + * strips out JavaScript comments and then uses the abstract parseLine() + * method to do the line parsing. + * + * @param filePath The path to the file being parsed. Used for reporting parse + * exceptions. + * @param fileContents A reader for the contents of the file. + */ + void doParse(String filePath, Reader fileContents) { + this.filePath = filePath; + parseSucceeded = true; + + BufferedReader lineBuffer = new BufferedReader(fileContents); + + // Parse all lines. + String line = null; + lineNum = 0; + boolean inMultilineComment = false; + + try { + while (null != (line = lineBuffer.readLine())) { + ++lineNum; + try { + String revisedLine = line; + if (inMultilineComment) { + int endOfComment = revisedLine.indexOf("*/"); + if (endOfComment != -1) { + revisedLine = revisedLine.substring(endOfComment + 2); + inMultilineComment = false; + } else { + revisedLine = ""; + } + } + + if (!inMultilineComment) { + while (true) { + int startOfLineComment = revisedLine.indexOf("//"); + int startOfMultilineComment = revisedLine.indexOf("/*"); + if (startOfLineComment != -1 && + (startOfMultilineComment == -1 || + startOfLineComment < startOfMultilineComment)) { + revisedLine = revisedLine.substring(0, startOfLineComment); + break; + } else if (startOfMultilineComment != -1) { + int endOfMultilineComment = revisedLine.indexOf("*/", + startOfMultilineComment + 2); + if (endOfMultilineComment == -1) { + revisedLine = revisedLine.substring( + 0, startOfMultilineComment); + inMultilineComment = true; + break; + } else { + revisedLine = + revisedLine.substring(0, startOfMultilineComment) + + revisedLine.substring(endOfMultilineComment + 2); + } + } else { + break; + } + } + } + + if (!revisedLine.isEmpty()) { + // This check for shortcut mode should be redundant, but + // it's done for safety reasons. + if (!parseLine(revisedLine) && shortcutMode) { + break; + } + } + } catch (ParseException e) { + // Inform the error handler of the exception. + errorManager.report( + e.isFatal() ? CheckLevel.ERROR : CheckLevel.WARNING, + JSError.make(filePath, lineNum, 0 /* char offset */, + e.isFatal() ? PARSE_ERROR : PARSE_WARNING, + e.getMessage(), line)); + parseSucceeded = parseSucceeded && !e.isFatal(); + } + } + } catch (IOException e) { + errorManager.report(CheckLevel.ERROR, + JSError.make(filePath, 0, 0 /* char offset */, + PARSE_ERROR, "Error reading file: " + filePath)); + parseSucceeded = false; + } + } + + /** + * Called for each line of the file being parsed. + * + * @param line The line to parse. + * @return true to keep going, false otherwise. + * @throws ParseException Should be thrown to signify a problem with the line. + */ + abstract boolean parseLine(String line) throws ParseException; + + /** + * Parses a JS string literal. + * + * @param jsStringLiteral The literal. Must look like "asdf" or 'asdf' + * @throws ParseException Thrown if there is a string literal that cannot be + * parsed. + */ + String parseJsString(String jsStringLiteral) throws ParseException { + valueMatcher.reset(jsStringLiteral); + if (!valueMatcher.matches()) { + throw new ParseException("Syntax error in JS String literal", true /* fatal */); + } + return valueMatcher.group(1) != null ? valueMatcher.group(1) : valueMatcher.group(2); + } + + /** + * Parses a JavaScript array of string literals. (eg: ['a', 'b', "c"]). + * @param input A string containing a JavaScript array of string literals. + * @return A list of parsed string literals. + * @throws ParseException Thrown if there is a syntax error with the input. + */ + List parseJsStringArray(String input) + throws ParseException { + List results = Lists.newArrayList(); + int indexStart = input.indexOf('['); + int indexEnd = input.lastIndexOf(']'); + if ((indexStart == -1) || (indexEnd == -1)) { + throw new ParseException("Syntax error when parsing JS array", true /* fatal */); + } + String innerValues = input.substring(indexStart + 1, indexEnd); + + if (!innerValues.trim().isEmpty()) { + valueMatcher.reset(innerValues); + for (;;) { + // Parse the current string literal. + if (!valueMatcher.lookingAt()) { + throw new ParseException("Syntax error in JS String literal", true /* fatal */); + } + // Add it to the results. + results.add(valueMatcher.group(1) != null ? + valueMatcher.group(1) : valueMatcher.group(2)); + if (valueMatcher.hitEnd()) { + break; + } + // Ensure there is a comma after the value. + if (innerValues.charAt(valueMatcher.end()) != ',') { + throw new ParseException("Missing comma in string array", true /* fatal */); + } + // Move to the next value. + valueMatcher.region(valueMatcher.end() + 1, valueMatcher.regionEnd()); + } + } + return results; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/JsFileParser.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/JsFileParser.java new file mode 100644 index 0000000..b09b6f7 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/JsFileParser.java @@ -0,0 +1,189 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.deps; + +import com.google.common.base.CharMatcher; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.ErrorManager; + +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.List; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A parser that can extract goog.require() and goog.provide() dependency + * information from a .js file. + * + * @author agrieve@google.com (Andrew Grieve) + */ +public class JsFileParser extends JsFileLineParser { + + private static Logger logger = Logger.getLogger(JsFileParser.class.getName()); + + /** Pattern for matching goog.provide(*) and goog.require(*). */ + private static final Pattern GOOG_PROVIDE_REQUIRE_PATTERN = Pattern.compile( + "(?:^|;)\\s*goog\\.(provide|require|addDependency)\\s*\\((.*?)\\)"); + + /** The first non-comment line of base.js */ + private static final String BASE_JS_START = "var COMPILED = false;"; + + /** Matchers used in the parsing. */ + private Matcher googMatcher = GOOG_PROVIDE_REQUIRE_PATTERN.matcher(""); + + /** The info for the file we are currently parsing. */ + private List provides; + private List requires; + private boolean fileHasProvidesOrRequires; + + /** Whether to provide/require the root namespace. */ + private boolean includeGoogBase = false; + + /** + * Constructor + * + * @param errorManager Handles parse errors. + */ + public JsFileParser(ErrorManager errorManager) { + super(errorManager); + } + + /** + * Sets whether we should create implicit provides and requires of the + * root namespace. + * + * When generating deps files, you do not want this behavior. Deps files + * need base.js to run anyway, so they don't need information about it. + * + * When generating abstract build graphs, you probably do want this behavior. + * It will create an implicit dependency of all files with provides/requires + * on base.js. + * + * @return this for easy chaining. + */ + public JsFileParser setIncludeGoogBase(boolean include) { + includeGoogBase = include; + return this; + } + + /** + * Parses the given file and returns the dependency information that it + * contained. + * + * @param filePath Path to the file to parse. + * @param closureRelativePath Path of the file relative to closure. + * @return A DependencyInfo containing all provides/requires found in the + * file. + * @throws IOException Thrown if there was an problem reading the given file. + */ + public DependencyInfo parseFile(String filePath, String closureRelativePath) + throws IOException { + return parseReader(filePath, closureRelativePath, new FileReader(filePath)); + } + + /** + * Parses the given file and returns the dependency information that it + * contained. + * + * @param filePath Path to the file to parse. + * @param closureRelativePath Path of the file relative to closure. + * @param fileContents The contents to parse. + * @return A DependencyInfo containing all provides/requires found in the + * file. + */ + public DependencyInfo parseFile(String filePath, String closureRelativePath, + String fileContents) { + return parseReader(filePath, closureRelativePath, + new StringReader(fileContents)); + } + + private DependencyInfo parseReader(String filePath, + String closureRelativePath, Reader fileContents) { + provides = Lists.newArrayList(); + requires = Lists.newArrayList(); + fileHasProvidesOrRequires = false; + + logger.fine("Parsing Source: " + filePath); + doParse(filePath, fileContents); + + DependencyInfo dependencyInfo = new SimpleDependencyInfo( + closureRelativePath, filePath, provides, requires); + logger.fine("DepInfo: " + dependencyInfo); + return dependencyInfo; + } + + /** + * Parses a line of JavaScript, extracting goog.provide and goog.require + * information. + */ + @Override + protected boolean parseLine(String line) throws ParseException { + boolean lineHasProvidesOrRequires = false; + + // Quick sanity check that will catch most cases. This is a performance + // win for people with a lot of JS. + if (line.indexOf("provide") != -1 || + line.indexOf("require") != -1 || + line.indexOf("addDependency") != -1) { + // Iterate over the provides/requires. + googMatcher.reset(line); + while (googMatcher.find()) { + lineHasProvidesOrRequires = true; + + if (includeGoogBase && !fileHasProvidesOrRequires) { + fileHasProvidesOrRequires = true; + requires.add("goog"); + } + + // See if it's a require or provide. + char firstChar = googMatcher.group(1).charAt(0); + boolean isProvide = firstChar == 'p'; + boolean isRequire = firstChar == 'r'; + + if (isProvide || isRequire) { + // Parse the param. + String arg = parseJsString(googMatcher.group(2)); + + // Add the dependency. + if (isRequire) { + // goog is always implicit. + // TODO(nicksantos): I'm pretty sure we don't need this anymore. + // Remove this later. + if (!"goog".equals(arg)) { + requires.add(arg); + } + } else { + provides.add(arg); + } + } + } + } else if (includeGoogBase && line.startsWith(BASE_JS_START) && + provides.isEmpty() && requires.isEmpty()) { + provides.add("goog"); + + // base.js can't provide or require anything else. + return false; + } + + return !shortcutMode || lineHasProvidesOrRequires || + CharMatcher.WHITESPACE.matchesAllOf(line); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/JsFunctionParser.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/JsFunctionParser.java new file mode 100644 index 0000000..3e2f0d2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/JsFunctionParser.java @@ -0,0 +1,151 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.deps; + +import com.google.common.base.CharMatcher; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.ErrorManager; + +import java.io.Reader; +import java.io.StringReader; +import java.util.Collection; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A parser that can extract dependency information from a .js file. + * + * @author agrieve@google.com (Andrew Grieve) + * @author ielashi@google.com (Islam El-Ashi) + */ +public class JsFunctionParser extends JsFileLineParser { + + public static class SymbolInfo { + public final String functionName; + public final String symbol; + + private SymbolInfo(String functionName, String symbol) { + this.functionName = functionName; + this.symbol = symbol; + } + } + + private static Logger logger = + Logger.getLogger(JsFunctionParser.class.getName()); + + /** Pattern for matching functions. */ + private Pattern pattern; + + /** Matcher used in the parsing. */ + private Matcher matcher; + + /** Symbols parsed. */ + private Collection symbols; + + /** Functions to parse */ + private Collection functionsToParse; + + /** + * Constructor + * + * @param functions Functions to parse. + * @param errorManager Handles parse errors. + */ + public JsFunctionParser( + Collection functions, ErrorManager errorManager) { + super(errorManager); + functionsToParse = functions; + pattern = getPattern(functions); + matcher = pattern.matcher(""); + } + + /** + * Constructs a pattern to extract the arguments of the given functions. + * + * @param functions Functions to parse. + * @return A pattern to extract {@code functions}' arguments. + */ + private Pattern getPattern(Collection functions) { + StringBuilder sb = new StringBuilder("(?:^|;)\\s*("); + + for (String function : functions) { + sb.append(Pattern.quote(function) + "|"); + } + + // remove last '|' + sb.deleteCharAt(sb.length() - 1); + sb.append(")\\s*\\((.*?)\\)"); + + return Pattern.compile(sb.toString()); + } + + /** + * Parses the given file and returns the dependency information that it + * contained. + * + * @param filePath Path to the file to parse. + * @param fileContents The contents to parse. + * @return A collection containing all symbols found in the + * file. + */ + public Collection parseFile( + String filePath, String fileContents) { + return parseReader(filePath, new StringReader(fileContents)); + } + + private Collection parseReader( + String filePath, Reader fileContents) { + symbols = Lists.newArrayList(); + + logger.fine("Parsing Source: " + filePath); + doParse(filePath, fileContents); + + return symbols; + } + + /** + * Parses a line of JavaScript, extracting dependency information. + */ + @Override + protected boolean parseLine(String line) throws ParseException { + boolean hasFunctions = false; + boolean parseLine = false; + + // Quick sanity check that will catch most cases. This is a performance + // win for people with a lot of JS. + for (String function : functionsToParse) { + if (line.indexOf(function) != -1) { + parseLine = true; + break; + } + } + + if (parseLine) { + matcher.reset(line); + while (matcher.find()) { + hasFunctions = true; + String functionName = matcher.group(1); + String arg = parseJsString(matcher.group(2)); // Parse the param. + symbols.add(new SymbolInfo(functionName, arg)); + } + } + + return !shortcutMode || hasFunctions || + CharMatcher.WHITESPACE.matchesAllOf(line); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/PathUtil.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/PathUtil.java new file mode 100644 index 0000000..68efd2a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/PathUtil.java @@ -0,0 +1,208 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.deps; + +import com.google.common.base.CharMatcher; +import com.google.common.base.Joiner; +import com.google.common.base.Strings; +import com.google.common.collect.Lists; + +import java.util.Arrays; +import java.util.List; + +/** + * Utility methods for manipulation of UNIX-like paths. + * NOTE: According to kevinb, equivalent methods will be in the standard library once + * jsr203 is ready. + * + */ +public final class PathUtil { + + private static final CharMatcher SLASH_MATCHER = CharMatcher.is('/'); + private static final CharMatcher NON_SLASH_MATCHER = CharMatcher.isNot('/'); + + private PathUtil() { + } + + /** + * Removes all ../ and ./ entries from within the given path. If there are extra ..s that move + * beyond the first directory given, they are removed. + * + * Examples: + * "a/b/../c" results in "a/c" + * "./foo/./../bar" results in "bar" + * "a/.." results in "" + * "a/../../foo" results in "foo" + * + * @param path The path to remove dots from. + * @return The path with all dots collapsed. + */ + public static String collapseDots(String path) { + path = removeExtraneousSlashes(path); + // Optimization: Most paths don't contain dots. + if (!path.contains(".")) { + return path; + } + + String[] srcFragments = path.split("/"); + List dstFragments = Lists.newArrayList(); + for (String fragment : srcFragments) { + if (fragment.equals("..")) { + if (!dstFragments.isEmpty()) { + dstFragments.remove(dstFragments.size() - 1); + } + } else if (!fragment.equals(".")) { + dstFragments.add(fragment); + } + } + + // Special case for Join.join([""]); -> "/" + if (dstFragments.size() == 1 && dstFragments.get(0).isEmpty()) { + return "/"; + } + return Joiner.on("/").join(dstFragments); + } + + /** + * Determines if a path is absolute or not by testing for the presence of "/" + * at the front of the string. + * + * @param path The path to test + * @return true if the path starts with DELIMITER, false otherwise. + */ + static boolean isAbsolute(String path) { + return path.startsWith("/"); + } + + /** + * Removes extra slashes from a path. Leading slash is preserved, trailing + * slash is stripped, and any runs of more than one slash in the middle is + * replaced by a single slash. + */ + static String removeExtraneousSlashes(String s) { + int lastNonSlash = NON_SLASH_MATCHER.lastIndexIn(s); + if (lastNonSlash != -1) { + s = s.substring(0, lastNonSlash + 1); + } + + return SLASH_MATCHER.collapseFrom(s, '/'); + } + + + /** + * Converts the given path into an absolute one. This prepends the current + * working directory and removes all .'s from the path. If an absolute path + * is given, it will not be prefixed. + * + *

      Unlike File.getAbsolutePath(), this function does remove .'s from the + * path and unlike File.getCanonicalPath(), this function does not resolve + * symlinks and does not use filesystem calls.

      + * + * @param path The path to make absolute. + * @return The path made absolute. + */ + public static String makeAbsolute(String path) { + return makeAbsolute(path, System.getProperty("user.dir")); + } + + /** + * Converts the given path into an absolute one. This prepends the given + * rootPath and removes all .'s from the path. If an absolute path is given, + * it will not be prefixed. + * + *

      Unlike File.getAbsolutePath(), this function does remove .'s from the + * path and unlike File.getCanonicalPath(), this function does not resolve + * symlinks and does not use filesystem calls.

      + * + * @param rootPath The path to prefix to path if path is not already absolute. + * @param path The path to make absolute. + * @return The path made absolute. + */ + public static String makeAbsolute(String path, String rootPath) { + if (!isAbsolute(path)) { + path = rootPath + "/" + path; + } + return collapseDots(path); + } + + /** + * Returns targetPath relative to basePath. + * + *

      basePath and targetPath must either both be relative, or both be + * absolute paths.

      + * + *

      This function is different from makeRelative + * in that it is able to add in ../ components and collapse existing ones as well.

      + * + * Examples: + * base="some/relative/path" target="some/relative/path/foo" return="foo" + * base="some/relative/path" target="some/relative" return=".." + * base="some/relative/path" target="foo/bar" return="../../../foo/bar" + * base="/some/abs/path" target="/foo/bar" return="../../../foo/bar" + * + * @param basePath The path to make targetPath relative to. + * @param targetPath The path to make relative. + * @return A path relative to targetPath. The returned value will never start + * with a slash. + */ + public static String makeRelative(String basePath, String targetPath) { + // Ensure the paths are both absolute or both relative. + if (isAbsolute(basePath) != + isAbsolute(targetPath)) { + throw new IllegalArgumentException( + "Paths must both be relative or both absolute.\n" + + " basePath: " + basePath + "\n" + + " targetPath: " + targetPath); + } + + basePath = collapseDots(basePath); + targetPath = collapseDots(targetPath); + String[] baseFragments = basePath.split("/"); + String[] targetFragments = targetPath.split("/"); + + int i = -1; + do { + i += 1; + if (i == baseFragments.length && i == targetFragments.length) { + // Eg) base: /java/com/google + // target: /java/com/google + // result: . <-- . is better than "" since "" + "/path" = "/path" + return "."; + } else if (i == baseFragments.length) { + // Eg) base: /java/com/google + // target: /java/com/google/c/ui + // result: c/ui + return Joiner.on("/").join( + Lists.newArrayList( + Arrays.asList(targetFragments).listIterator(i))); + } else if (i == targetFragments.length) { + // Eg) base: /java/com/google/c/ui + // target: /java/com/google + // result: ../.. + return Strings.repeat("../", baseFragments.length - i - 1) + ".."; + } + + } while (baseFragments[i].equals(targetFragments[i])); + + // Eg) base: /java/com/google/c + // target: /java/com/google/common/base + // result: ../common/base + return Strings.repeat("../", baseFragments.length - i) + + Joiner.on("/").join( + Lists.newArrayList(Arrays.asList(targetFragments).listIterator(i))); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/SimpleDependencyInfo.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/SimpleDependencyInfo.java new file mode 100644 index 0000000..69e1516 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/SimpleDependencyInfo.java @@ -0,0 +1,103 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.deps; + +import com.google.common.base.Objects; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * A class to hold JS dependency information for a single .js file. + * + * @author agrieve@google.com (Andrew Grieve) + */ +public class SimpleDependencyInfo implements DependencyInfo { + + /** A list of provided symbols. */ + private final List provides; + + /** A list of required symbols. */ + private final List requires; + + /** The path of the file relative to closure. */ + private final String srcPathRelativeToClosure; + + /** The path to the file from which we extracted the dependency information.*/ + private final String pathOfDefiningFile; + + /** + * Constructs a DependencyInfo object with the given list of provides & + * requires. This does *not* copy the given lists, but uses them directly. + * + * @param srcPathRelativeToClosure The closure-relative path of the file + * associated with this DependencyInfo. + * @param pathOfDefiningFile The path to the file from which this dependency + * information was extracted. + * @param provides List of provided symbols. + * @param requires List of required symbols. + */ + public SimpleDependencyInfo( + String srcPathRelativeToClosure, String pathOfDefiningFile, + List provides, List requires) { + this.srcPathRelativeToClosure = srcPathRelativeToClosure; + this.pathOfDefiningFile = pathOfDefiningFile; + this.provides = provides; + this.requires = requires; + } + + @Override + public String getName() { + return pathOfDefiningFile; + } + + @Override + public String getPathRelativeToClosureBase() { + return srcPathRelativeToClosure; + } + + @Override + public Collection getProvides() { + return Collections.unmodifiableList(provides); + } + + @Override + public Collection getRequires() { + return Collections.unmodifiableList(requires); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SimpleDependencyInfo)) { + return false; + } + SimpleDependencyInfo other = (SimpleDependencyInfo)obj; + return Objects.equal(other.srcPathRelativeToClosure, + srcPathRelativeToClosure) && + Objects.equal(other.pathOfDefiningFile, pathOfDefiningFile) && + Objects.equal(other.requires, this.requires) && + Objects.equal(other.provides, this.provides); + } + + @Override + public String toString() { + return String.format("DependencyInfo(relativePath='%1$s', path='%2$s', " + + "provides=%3$s, requires=%4$s)", srcPathRelativeToClosure, + pathOfDefiningFile, provides, requires); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/SortedDependencies.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/SortedDependencies.java new file mode 100644 index 0000000..95e2071 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/SortedDependencies.java @@ -0,0 +1,301 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.deps; + +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import com.google.common.collect.Multiset; +import com.google.common.collect.Sets; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Deque; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.Set; + +/** + * A sorted list of inputs with dependency information. Uses a stable + * topological sort to make sure that an input always comes after its + * dependencies. + * + * Also exposes other information about the inputs, like which inputs + * do not provide symbols. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class SortedDependencies { + + private final List inputs; + + // A topologically sorted list of the inputs. + private final List sortedList; + + // A list of all the inputs that do not have provides. + private final List noProvides; + + private final Map provideMap = Maps.newHashMap(); + + public SortedDependencies(List inputs) + throws CircularDependencyException { + this.inputs = Lists.newArrayList(inputs); + noProvides = Lists.newArrayList(); + + // Collect all symbols provided in these files. + for (INPUT input : inputs) { + Collection currentProvides = input.getProvides(); + if (currentProvides.isEmpty()) { + noProvides.add(input); + } + + for (String provide : currentProvides) { + provideMap.put(provide, input); + } + } + + // Get the direct dependencies. + final Multimap deps = HashMultimap.create(); + for (INPUT input : inputs) { + for (String req : input.getRequires()) { + INPUT dep = provideMap.get(req); + if (dep != null && dep != input) { + deps.put(input, dep); + } + } + } + + // Sort the inputs by sucking in 0-in-degree nodes until we're done. + sortedList = topologicalStableSort(inputs, deps); + + // The dependency graph of inputs has a cycle iff sortedList is a proper + // subset of inputs. Also, it has a cycle iff the subgraph + // (inputs - sortedList) has a cycle. It's fairly easy to prove this + // by the lemma that a graph has a cycle iff it has a subgraph where + // no nodes have out-degree 0. I'll leave the proof of this as an exercise + // to the reader. + if (sortedList.size() < inputs.size()) { + List subGraph = Lists.newArrayList(inputs); + subGraph.removeAll(sortedList); + + throw new CircularDependencyException( + cycleToString(findCycle(subGraph, deps))); + } + } + + /** + * Return the input that gives us the given symbol. + * @throws MissingProvideException An exception if there is no + * input for this symbol. + */ + public INPUT getInputProviding(String symbol) + throws MissingProvideException { + if (provideMap.containsKey(symbol)) { + return provideMap.get(symbol); + } + throw new MissingProvideException(symbol); + } + + /** + * Return the input that gives us the given symbol, or null. + */ + public INPUT maybeGetInputProviding(String symbol) { + return provideMap.get(symbol); + } + + /** + * Returns the first circular dependency found. Expressed as a list of + * items in reverse dependency order (the second element depends on the + * first, etc.). + */ + private List findCycle( + List subGraph, Multimap deps) { + return findCycle(subGraph.get(0), Sets.newHashSet(subGraph), + deps, Sets.newHashSet()); + } + + private List findCycle( + INPUT current, Set subGraph, Multimap deps, + Set covered) { + if (covered.add(current)) { + List cycle = findCycle( + findRequireInSubGraphOrFail(current, subGraph), + subGraph, deps, covered); + + // Don't add the input to the list if the cycle has closed already. + if (cycle.get(0) != cycle.get(cycle.size() - 1)) { + cycle.add(current); + } + + return cycle; + } else { + // Explicitly use the add() method, to prevent a generics constructor + // warning that is dumb. The condition it's protecting is + // obscure, and I think people have proposed that it be removed. + List cycle = Lists.newArrayList(); + cycle.add(current); + return cycle; + } + } + + private INPUT findRequireInSubGraphOrFail(INPUT input, Set subGraph) { + for (String symbol : input.getRequires()) { + INPUT candidate = provideMap.get(symbol); + if (subGraph.contains(candidate)) { + return candidate; + } + } + throw new IllegalStateException("no require found in subgraph"); + } + + /** + * @param cycle A cycle in reverse-dependency order. + */ + private String cycleToString(List cycle) { + List symbols = Lists.newArrayList(); + for (int i = cycle.size() - 1; i >= 0; i--) { + symbols.add(cycle.get(i).getProvides().iterator().next()); + } + symbols.add(symbols.get(0)); + return Joiner.on(" -> ").join(symbols); + } + + public List getSortedList() { + return Collections.unmodifiableList(sortedList); + } + + /** + * Gets all the dependencies of the given roots. The inputs must be returned + * in a stable order. In other words, if A comes before B, and A does not + * transitively depend on B, then A must also come before B in the returned + * list. + */ + public List getSortedDependenciesOf(List roots) { + return getDependenciesOf(roots, true); + } + + /** + * Gets all the dependencies of the given roots. The inputs must be returned + * in a stable order. In other words, if A comes before B, and A does not + * transitively depend on B, then A must also come before B in the returned + * list. + * + * @param sorted If true, get them in topologically sorted order. If false, + * get them in the original order they were passed to the compiler. + */ + public List getDependenciesOf(List roots, boolean sorted) { + Preconditions.checkArgument(inputs.containsAll(roots)); + Set included = Sets.newHashSet(); + Deque worklist = new ArrayDeque(roots); + while (!worklist.isEmpty()) { + INPUT current = worklist.pop(); + if (included.add(current)) { + for (String req : current.getRequires()) { + INPUT dep = provideMap.get(req); + if (dep != null) { + worklist.add(dep); + } + } + } + } + + ImmutableList.Builder builder = ImmutableList.builder(); + for (INPUT current : (sorted ? sortedList : inputs)) { + if (included.contains(current)) { + builder.add(current); + } + } + return builder.build(); + } + + public List getInputsWithoutProvides() { + return Collections.unmodifiableList(noProvides); + } + + private static List topologicalStableSort( + List items, Multimap deps) { + if (items.size() == 0) { + // Priority queue blows up if we give it a size of 0. Since we need + // to special case this either way, just bail out. + return Lists.newArrayList(); + } + + final Map originalIndex = Maps.newHashMap(); + for (int i = 0; i < items.size(); i++) { + originalIndex.put(items.get(i), i); + } + + PriorityQueue inDegreeZero = new PriorityQueue(items.size(), + new Comparator() { + @Override + public int compare(T a, T b) { + return originalIndex.get(a).intValue() - + originalIndex.get(b).intValue(); + } + }); + List result = Lists.newArrayList(); + + Multiset inDegree = HashMultiset.create(); + Multimap reverseDeps = ArrayListMultimap.create(); + Multimaps.invertFrom(deps, reverseDeps); + + // First, add all the inputs with in-degree 0. + for (T item : items) { + Collection itemDeps = deps.get(item); + inDegree.add(item, itemDeps.size()); + if (itemDeps.isEmpty()) { + inDegreeZero.add(item); + } + } + + // Then, iterate to a fixed point over the reverse dependency graph. + while (!inDegreeZero.isEmpty()) { + T item = inDegreeZero.remove(); + result.add(item); + for (T inWaiting : reverseDeps.get(item)) { + inDegree.remove(inWaiting, 1); + if (inDegree.count(inWaiting) == 0) { + inDegreeZero.add(inWaiting); + } + } + } + + return result; + } + + public static class CircularDependencyException extends Exception { + CircularDependencyException(String message) { + super(message); + } + } + + public static class MissingProvideException extends Exception { + MissingProvideException(String provide) { + super(provide); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/package.html b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/package.html new file mode 100644 index 0000000..eb172ad --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/deps/package.html @@ -0,0 +1,10 @@ + + + + + + +Analyzes information about dependencies between files. + + + diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/function_info.proto b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/function_info.proto new file mode 100644 index 0000000..d1d01f3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/function_info.proto @@ -0,0 +1,26 @@ +// Copyright 2008 Google Inc. +// Author: Mark Goodman + +syntax = "proto2"; + +package jscomp; + +option java_package = "com.google.javascript.jscomp"; +option java_multiple_files = true; + +message FunctionInformationMap { + repeated group Entry = 1 { + required int32 id = 2; + required string source_name = 3; + required int32 line_number = 4; + required string module_name = 5; + required int32 size = 6; + required string name = 7; + required string compiled_source = 8; + } + + repeated group Module = 101 { + required string name = 102; + required string compiled_source = 103; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/AdjacencyGraph.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/AdjacencyGraph.java new file mode 100644 index 0000000..41943dc --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/AdjacencyGraph.java @@ -0,0 +1,57 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + +import java.util.Collection; + +/** + * A minimal graph interface. Provided is add nodes to the graph, adjacency + * calculation between a SubGraph and a GraphNode, and adding node annotations. + * + *

      For a more extensive interface, see {@link Graph}. + * + * + * @param Value type that the graph node stores. + * @param Value type that the graph edge stores. + * @see Graph + */ +public interface AdjacencyGraph { + /** Gets an immutable list of all nodes. */ + Collection> getNodes(); + + /** + * Gets a node from the graph given a value. Values equality are compared + * using Object.equals. + * + * @param value The node's value. + * @return The corresponding node in the graph, null if there value has no + * corresponding node. + */ + GraphNode getNode(N value); + + /** Returns an empty SubGraph for this Graph. */ + SubGraph newSubGraph(); + + /** Makes each node's annotation null. */ + void clearNodeAnnotations(); + + /** + * Returns a weight for the given value to be used in ordering nodes, e.g. + * in {@link GraphColoring}. + */ + int getWeight(N value); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/Annotatable.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/Annotatable.java new file mode 100644 index 0000000..60d7e66 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/Annotatable.java @@ -0,0 +1,38 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + + +/** + * Object that has an annotation. + */ +public interface Annotatable { + /** + * Annotates a piece of information to the object. + * + * @param data Information to be annotated. + */ + void setAnnotation(Annotation data); + + /** + * Retrieves a piece of information that has been annotated. + * + * @return The annotation or null if the object has not + * been annotated. + */ + A getAnnotation(); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/Annotation.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/Annotation.java new file mode 100644 index 0000000..0625e13 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/Annotation.java @@ -0,0 +1,25 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + + +/** + * Information that can be annotated to a {@link GraphNode} or + * {@link Graph.GraphEdge}. + */ +public interface Annotation { +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/DiGraph.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/DiGraph.java new file mode 100644 index 0000000..e7dd133 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/DiGraph.java @@ -0,0 +1,132 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + +import java.util.List; + +/** + * A generic directed graph. + * + * + * @param Value type that the graph node stores. + * @param Value type that the graph edge stores. + */ +public abstract class DiGraph extends Graph { + + /** + * Gets an immutable iterable over all the nodes in the graph. + */ + public abstract Iterable> getDirectedGraphNodes(); + + /** + * Gets an immutable list of out edges of the given node. + */ + public abstract List> getOutEdges(N nodeValue); + + /** + * Gets an immutable list of in edges of the given node. + */ + public abstract List> getInEdges(N nodeValue); + + public abstract List> getDirectedPredNodes( + DiGraphNode n); + + public abstract List> getDirectedSuccNodes( + DiGraphNode n); + + public abstract List> + getDirectedPredNodes(N nodeValue); + + public abstract List> + getDirectedSuccNodes(N nodeValue); + + public abstract DiGraphNode createDirectedGraphNode(N nodeValue); + + public abstract DiGraphNode getDirectedGraphNode(N nodeValue); + + public abstract List> + getDirectedGraphEdges(N n1, N n2); + + /** + * Disconnects all edges from n1 to n2. + * + * @param n1 Source node. + * @param n2 Destination node. + */ + public abstract void disconnectInDirection(N n1, N n2); + + /** + * Checks whether two nodes in the graph are connected via a directed edge. + * + * @param n1 Node 1. + * @param n2 Node 2. + * @return true if the graph contains edge from n1 to n2. + */ + public abstract boolean isConnectedInDirection(N n1, N n2); + + /** + * Checks whether two nodes in the graph are connected via a directed edge + * with the given value. + * + * @param n1 Node 1. + * @param edgeValue edge value tag + * @param n2 Node 2. + * @return true if the edge exists. + */ + public abstract boolean isConnectedInDirection(N n1, E edgeValue, N n2); + + @Override + public boolean isConnected(N n1, N n2) { + return isConnectedInDirection(n1, n2) || isConnectedInDirection(n2, n1); + } + + @Override + public boolean isConnected(N n1, E e, N n2) { + return isConnectedInDirection(n1, e, n2) || + isConnectedInDirection(n2, e, n1); + } + + /** + * A generic directed graph node. + * + * @param Value type that the graph node stores. + * @param Value type that the graph edge stores. + */ + public static interface DiGraphNode extends GraphNode { + + public List> getOutEdges(); + + public List> getInEdges(); + } + + /** + * A generic directed graph edge. + * + * @param Value type that the graph node stores. + * @param Value type that the graph edge stores. + */ + public static interface DiGraphEdge extends GraphEdge { + + public DiGraphNode getSource(); + + public DiGraphNode getDestination(); + + public void setSource(DiGraphNode node); + + public void setDestination(DiGraphNode node); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/FixedPointGraphTraversal.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/FixedPointGraphTraversal.java new file mode 100644 index 0000000..d30ecf8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/FixedPointGraphTraversal.java @@ -0,0 +1,144 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.graph.DiGraph; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * A utility class for doing fixed-point computations. We traverse + * the edges over the given directed graph until the graph reaches + * a steady state. + * + * @author nicksantos@google.com (Nick Santos) + * + * @param Value type that the graph node stores. + * @param Value type that the graph edge stores. + */ +public final class FixedPointGraphTraversal { + // TODO(nicksantos): Generalize the algorithm for undirected graphs, if we + // need it. + + private final EdgeCallback callback; + + public static final String NON_HALTING_ERROR_MSG = + "Fixed point computation not halting"; + + /** + * Create a new traversal. + * @param callback A callback for updating the state of the graph each + * time an edge is traversed. + */ + public FixedPointGraphTraversal(EdgeCallback callback) { + this.callback = callback; + } + + /** + * Helper method for creating new traversals. + */ + public static FixedPointGraphTraversal newTraversal( + EdgeCallback callback) { + return new FixedPointGraphTraversal(callback); + } + + /** + * Compute a fixed point for the given graph. + * @param graph The graph to traverse. + */ + public void computeFixedPoint(DiGraph graph) { + Set nodes = Sets.newHashSet(); + for (DiGraphNode node : graph.getDirectedGraphNodes()) { + nodes.add(node.getValue()); + } + computeFixedPoint(graph, nodes); + } + + /** + * Compute a fixed point for the given graph, entering from the given node. + * @param graph The graph to traverse. + * @param entry The node to begin traversing from. + */ + public void computeFixedPoint(DiGraph graph, N entry) { + Set entrySet = Sets.newHashSet(); + entrySet.add(entry); + computeFixedPoint(graph, entrySet); + } + + /** + * Compute a fixed point for the given graph, entering from the given nodes. + * @param graph The graph to traverse. + * @param entrySet The nodes to begin traversing from. + */ + public void computeFixedPoint(DiGraph graph, Set entrySet) { + int cycleCount = 0; + long nodeCount = graph.getNodes().size(); + + // Choose a bail-out heuristically in case the computation + // doesn't converge. + long maxIterations = Math.max(nodeCount * nodeCount * nodeCount, 100); + + // Use a LinkedHashSet, so that the traversal is deterministic. + LinkedHashSet> workSet = + Sets.newLinkedHashSet(); + for (N n : entrySet) { + workSet.add(graph.getDirectedGraphNode(n)); + } + for (; !workSet.isEmpty() && cycleCount < maxIterations; cycleCount++) { + // For every out edge in the workSet, traverse that edge. If that + // edge updates the state of the graph, then add the destination + // node to the resultSet, so that we can update all of its out edges + // on the next iteration. + DiGraphNode source = workSet.iterator().next(); + N sourceValue = source.getValue(); + + workSet.remove(source); + + List> outEdges = source.getOutEdges(); + for (DiGraphEdge edge : outEdges) { + N destNode = edge.getDestination().getValue(); + if (callback.traverseEdge(sourceValue, edge.getValue(), destNode)) { + workSet.add(edge.getDestination()); + } + } + } + + Preconditions.checkState(cycleCount != maxIterations, + NON_HALTING_ERROR_MSG); + } + + public static interface EdgeCallback { + /** + * Update the state of the destination node when the given edge + * is traversed. For the fixed-point computation to work, only the + * destination node may be modified. The source node and the edge must + * not be modified. + * + * @param source The start node. + * @param e The edge. + * @param destination The end node. + * @return Whether the state of the destination node changed. + */ + boolean traverseEdge(Node source, Edge e, Node destination); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/Graph.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/Graph.java new file mode 100644 index 0000000..b7c47c0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/Graph.java @@ -0,0 +1,353 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; + +/** + * The base generic class for graph-like data structure and algorithms in + * the compiler. + *

      + * Nodes and edges in the graph can store a piece of data that this graph is + * used to represent. For example, a variable interference graph might store a + * variable in the node. This piece of data can be accessed with + * {@link GraphNode#getValue} and {@link GraphEdge#getValue}. + *

      + * Algorithms and analysis can annotate information on the nodes and edges + * using {@link GraphNode#getValue} and {@link GraphEdge#getValue}. For example, + * a graph coloring algorithm can store the color as an annotation. If multiple + * analyses are required, it is up to the user of the analysis to save the + * annotated solution between passes. + *

      + * We implemented our own graph data structure (as opposed to using + * com.google.common.graph) for two reasons. First, aside from + * the node's label value, we would like to annotate information on the nodes + * and edges. Using a map to annotate would introduce too much overhead during + * fix point analysis. Also, com.google.common.graph does not + * support labeling of edges. Secondly, not using another external package would + * limit our dependencies. + *

      + * TODO(user): All functionality for removing nodes and edges. + * + * + * @param Value type that the graph node stores. + * @param Value type that the graph edge stores. + */ +public abstract class Graph implements AdjacencyGraph { + /** + * Pseudo typedef for a pair of annotations. Record of an object's + * annotation at some state. + */ + private static final class AnnotationState { + private final Annotatable first; + private final Annotation second; + + public AnnotationState(Annotatable annotatable, Annotation annotation) { + this.first = annotatable; + this.second = annotation; + } + } + + /** + * Pseudo typedef for ArrayList. Record of a collection of + * objects' annotations at some state. + */ + private static class GraphAnnotationState extends ArrayList { + private static final long serialVersionUID = 1L; + + public GraphAnnotationState(int size) { + super(size); + } + } + + /** + * Used by {@link #pushNodeAnnotations()} and {@link #popNodeAnnotations()}. + */ + private Deque nodeAnnotationStack; + + /** + * Used by {@link #pushEdgeAnnotations()} and {@link #popEdgeAnnotations()}. + */ + private Deque edgeAnnotationStack; + + /** + * Connects two nodes in the graph with an edge. + * + * @param n1 First node. + * @param edge The edge. + * @param n2 Second node. + */ + public abstract void connect(N n1, E edge, N n2); + + /** + * Disconnects two nodes in the graph by removing all edges between them. + * + * @param n1 First node. + * @param n2 Second node. + */ + public abstract void disconnect(N n1, N n2); + + /** + * Connects two nodes in the graph with an edge if such edge does not already + * exists between the nodes. + * + * @param n1 First node. + * @param edge The edge. + * @param n2 Second node. + */ + public final void connectIfNotFound(N n1, E edge, N n2) { + if (!isConnected(n1, edge, n2)) { + connect(n1, edge, n2); + } + } + + /** + * Gets a node from the graph given a value. New nodes are created if that + * value has not been assigned a graph node. Values equality are compared + * using Object.equals. + * + * @param value The node's value. + * @return The corresponding node in the graph. + */ + public abstract GraphNode createNode(N value); + + /** Gets an immutable list of all nodes. */ + @Override + public abstract Collection> getNodes(); + + /** Gets an immutable list of all edges. */ + public abstract List> getEdges(); + + /** + * Gets the degree of a node. + * + * @param value The node's value. + * @return The degree of the node. + */ + public abstract int getNodeDegree(N value); + + @Override + public int getWeight(N value) { + return getNodeDegree(value); + } + + /** + * Gets the neighboring nodes. + * + * @param value The node's value. + * @return A list of neighboring nodes. + */ + public abstract List> getNeighborNodes(N value); + + public abstract Iterator> getNeighborNodesIterator(N value); + + /** + * Retrieves an edge from the graph. + * + * @param n1 Node one. + * @param n2 Node two. + * @return The list of edges between those two values in the graph. + */ + public abstract List> getEdges(N n1, N n2); + + /** + * Retrieves any edge from the graph. + * + * @param n1 Node one. + * @param n2 Node two. + * @return The first edges between those two values in the graph. null if + * there are none. + */ + public abstract GraphEdge getFirstEdge(N n1, N n2); + + /** + * Checks whether the node exists in the graph ({@link #createNode(Object)} + * has been called with that value). + * + * @param n Node. + * @return true if it exist. + */ + public final boolean hasNode(N n) { + return getNode(n) != null; + } + + /** + * Checks whether two nodes in the graph are connected. + * + * @param n1 Node 1. + * @param n2 Node 2. + * @return true if the two nodes are connected. + */ + public abstract boolean isConnected(N n1, N n2); + + /** + * Checks whether two nodes in the graph are connected by the given + * edge type. + * + * @param n1 Node 1. + * @param e The edge type. + * @param n2 Node 2. + */ + public abstract boolean isConnected(N n1, E e, N n2); + + /** + * Gets the node of the specified type, or throws an + * IllegalArgumentException. + */ + @SuppressWarnings("unchecked") + > T getNodeOrFail(N val) { + T node = (T) getNode(val); + if (node == null) { + throw new IllegalArgumentException(val + " does not exist in graph"); + } + return node; + } + + @Override + public final void clearNodeAnnotations() { + for (GraphNode n : getNodes()) { + n.setAnnotation(null); + } + } + + /** Makes each edge's annotation null. */ + public final void clearEdgeAnnotations() { + for (GraphEdge e : getEdges()) { + e.setAnnotation(null); + } + } + + /** + * Pushes nodes' annotation values. Restored with + * {@link #popNodeAnnotations()}. Nodes' annotation values are cleared. + */ + public final void pushNodeAnnotations() { + if (nodeAnnotationStack == null) { + nodeAnnotationStack = Lists.newLinkedList(); + } + pushAnnotations(nodeAnnotationStack, getNodes()); + } + + /** + * Restores nodes' annotation values to state before last + * {@link #pushNodeAnnotations()}. + */ + public final void popNodeAnnotations() { + Preconditions.checkNotNull(nodeAnnotationStack, + "Popping node annotations without pushing."); + popAnnotations(nodeAnnotationStack); + } + + /** + * Pushes edges' annotation values. Restored with + * {@link #popEdgeAnnotations()}. Edges' annotation values are cleared. + */ + public final void pushEdgeAnnotations() { + if (edgeAnnotationStack == null) { + edgeAnnotationStack = Lists.newLinkedList(); + } + pushAnnotations(edgeAnnotationStack, getEdges()); + } + + /** + * Restores edges' annotation values to state before last + * {@link #pushEdgeAnnotations()}. + */ + public final void popEdgeAnnotations() { + Preconditions.checkNotNull(edgeAnnotationStack, + "Popping edge annotations without pushing."); + popAnnotations(edgeAnnotationStack); + } + + /** + * A generic edge. + * + * @param Value type that the graph node stores. + * @param Value type that the graph edge stores. + */ + public interface GraphEdge extends Annotatable { + /** + * Retrieves the edge's value. + * + * @return The value. + */ + E getValue(); + + GraphNode getNodeA(); + + GraphNode getNodeB(); + } + + /** + * A simple implementation of SubGraph that calculates adjacency by iterating + * over a node's neighbors. + */ + class SimpleSubGraph implements SubGraph { + private Graph graph; + private List> nodes = Lists.newArrayList(); + + SimpleSubGraph(Graph graph) { + this.graph = graph; + } + + @Override + public boolean isIndependentOf(N value) { + GraphNode node = graph.getNode(value); + for (GraphNode n : nodes) { + if (graph.getNeighborNodes(n.getValue()).contains(node)) { + return false; + } + } + return true; + } + + @Override + public void addNode(N value) { + nodes.add(graph.getNodeOrFail(value)); + } + } + + /** + * Pushes a new list on stack and stores nodes annotations in the new list. + * Clears objects' annotations as well. + */ + private static void pushAnnotations( + Deque stack, + Collection haveAnnotations) { + stack.push(new GraphAnnotationState(haveAnnotations.size())); + for (Annotatable h : haveAnnotations) { + stack.peek().add(new AnnotationState(h, h.getAnnotation())); + h.setAnnotation(null); + } + } + + /** + * Restores the node annotations on the top of stack and pops stack. + */ + private static void popAnnotations(Deque stack) { + for (AnnotationState as : stack.pop()) { + as.first.setAnnotation(as.second); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/GraphColoring.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/GraphColoring.java new file mode 100644 index 0000000..14900a4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/GraphColoring.java @@ -0,0 +1,168 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.graph.Annotation; +import com.google.javascript.jscomp.graph.GraphNode; +import com.google.javascript.jscomp.graph.SubGraph; + +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +/** + * Annotates the graph with a color in a way that no connected node will have + * the same color. Nodes of the same color cab then be partitioned together and + * be represented by a super node. This class will merely annotate the nodes + * with a color using {@link GraphNode#setAnnotation(Annotation)} and provide + * a node to super node mapping with {@link #getPartitionSuperNode(Object)}. The + * give graph itself will not be modified. + * + *

      This algorithm is NOT deterministic by default. Passes that + * requires deterministic output should provide a {@code Comparator} in the + * constructor as a tie-breaker. This tie-break will be used when deciding + * which node should be colored first when multiple nodes have the same degree. + * + * @param Value type that the graph node stores. + * @param Value type that the graph edge stores. + * + */ +public abstract class GraphColoring { + // Maps a color (represented by an integer) to a variable. If, for example, + // the color 5 is mapped to "foo". Then any other variables colored with the + // color 5 will now use the name "foo". + protected N[] colorToNodeMap; + protected final AdjacencyGraph graph; + + public GraphColoring(AdjacencyGraph graph) { + this.graph = graph; + } + + /** + * Annotates the graph with {@link Color} objects using + * {@link GraphNode#setAnnotation(Annotation)}. + * + * @return The number of unique colors need. + */ + public abstract int color(); + + /** + * Using the coloring as partitions, finds the node that represents that + * partition as the super node. The first to retrieve its partition will + * become the super node. + */ + public N getPartitionSuperNode(N node) { + Preconditions.checkNotNull(colorToNodeMap, + "No coloring founded. color() should be called first."); + Color color = graph.getNode(node).getAnnotation(); + N headNode = colorToNodeMap[color.value]; + if (headNode == null) { + colorToNodeMap[color.value] = node; + return node; + } else { + return headNode; + } + } + + public AdjacencyGraph getGraph() { + return graph; + } + + public static class Color implements Annotation { + int value = 0; + + Color(int value) { + this.value = value; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof Color)) { + return false; + } else { + return value == ((Color) other).value; + } + } + + @Override + public int hashCode() { + return value; + } + } + + /** + * Greedily assign nodes with high degree unique colors. + */ + public static class GreedyGraphColoring extends GraphColoring { + + private final Comparator tieBreaker; + public GreedyGraphColoring(AdjacencyGraph graph) { + this(graph, null); + } + + /** + * @param tieBreaker In case of a tie between two nodes of the same degree, + * this comparator will determine which node should be colored first. + */ + public GreedyGraphColoring( + AdjacencyGraph graph, Comparator tieBreaker) { + super(graph); + this.tieBreaker = tieBreaker; + } + + @Override + public int color() { + graph.clearNodeAnnotations(); + List> worklist = Lists.newArrayList(graph.getNodes()); + + // Sort nodes by degree. + Collections.sort(worklist, new Comparator>() { + @Override + public int compare(GraphNode o1, GraphNode o2) { + int result = graph.getWeight(o2.getValue()) + - graph.getWeight(o1.getValue()); + return result == 0 && tieBreaker != null ? + tieBreaker.compare(o1.getValue(), o2.getValue()) : result; + } + }); + + // Idea: From the highest to lowest degree, assign any uncolored node with + // a unique color if none of its neighbor has been assigned that color. + int count = 0; + do { + Color color = new Color(count); + SubGraph subgraph = graph.newSubGraph(); + for (Iterator> i = worklist.iterator(); i.hasNext();) { + GraphNode node = i.next(); + if (subgraph.isIndependentOf(node.getValue())) { + subgraph.addNode(node.getValue()); + node.setAnnotation(color); + i.remove(); + } + } + count++; + } while (!worklist.isEmpty()); + @SuppressWarnings("unchecked") + N[] map = (N[]) new Object[count]; + colorToNodeMap = map; + return count; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/GraphNode.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/GraphNode.java new file mode 100644 index 0000000..aa76510 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/GraphNode.java @@ -0,0 +1,33 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + + +/** + * A generic node. + * + * @param Value type that the graph node stores. + * @param Value type that the graph edge stores. + */ +public interface GraphNode extends Annotatable { + /** + * Retrieves the node's value. + * + * @return The value. + */ + N getValue(); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/GraphPruner.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/GraphPruner.java new file mode 100644 index 0000000..9e88230 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/GraphPruner.java @@ -0,0 +1,100 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + +import com.google.common.base.Predicate; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; + +/** + * Prunes a graph, creating a new graph with nodes removed. + * + * If a node is removed from the graph, any paths through that node + * will be replaced with edges. In other words, if A and B are nodes + * in the original graph and the pruned graph, then there exists a path + * from A -> B in the original graph iff there's a path from A -> B + * in the pruned graph. + * + * We do not make any guarantees about what edges are in the pruned graph. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class GraphPruner { + private final DiGraph graph; + + public GraphPruner(DiGraph graph) { + this.graph = graph; + } + + public LinkedDirectedGraph prune(Predicate keep) { + LinkedDirectedGraph workGraph = cloneGraph(graph); + + // Create a work graph where all nodes with a path between them have + // an edge. + for (DiGraphNode node : workGraph.getDirectedGraphNodes()) { + for (DiGraphEdge inEdge : node.getInEdges()) { + for (DiGraphEdge outEdge : node.getOutEdges()) { + N source = inEdge.getSource().getValue(); + N dest = outEdge.getDestination().getValue(); + if (!workGraph.isConnectedInDirection(source, dest)) { + workGraph.connect(source, outEdge.getValue(), dest); + } + } + } + } + + + // Build a complete subgraph of workGraph. + LinkedDirectedGraph resultGraph = + LinkedDirectedGraph.create(); + for (DiGraphNode node : workGraph.getDirectedGraphNodes()) { + if (keep.apply(node.getValue())) { + resultGraph.createNode(node.getValue()); + + for (DiGraphEdge outEdge : node.getOutEdges()) { + N source = node.getValue(); + N dest = outEdge.getDestination().getValue(); + if (keep.apply(dest)) { + resultGraph.createNode(dest); + if (source != dest && + !resultGraph.isConnectedInDirection(source, dest)) { + resultGraph.connect(source, outEdge.getValue(), dest); + } + } + } + } + } + + return resultGraph; + } + + private static LinkedDirectedGraph cloneGraph( + DiGraph graph) { + LinkedDirectedGraph newGraph = LinkedDirectedGraph.create(); + for (DiGraphNode node : graph.getDirectedGraphNodes()) { + newGraph.createNode(node.getValue()); + + for (DiGraphEdge outEdge : node.getOutEdges()) { + N dest = outEdge.getDestination().getValue(); + newGraph.createNode(dest); + newGraph.connect(node.getValue(), outEdge.getValue(), dest); + } + } + + return newGraph; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/GraphReachability.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/GraphReachability.java new file mode 100644 index 0000000..1f72d96 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/GraphReachability.java @@ -0,0 +1,98 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.javascript.jscomp.graph.FixedPointGraphTraversal.EdgeCallback; + +/** + * Computes all the reachable nodes. Upon execution of {@link #compute(Object)}, + * the graph nodes will be annotated with {@link #REACHABLE} if it is reachable + * from the specified entry node. + * + * @see GraphNode#getAnnotation() + */ +public class GraphReachability implements EdgeCallback { + + // TODO(user): This should work for undirected graphs when + // FixedPointGraphTraversal accepts them. + private final DiGraph graph; + + private final Predicate> edgePredicate; + + public GraphReachability(DiGraph graph) { + this(graph, null); + } + + /** + * @param graph The graph. + * @param edgePredicate Given the predecessor P of the a node S and the edge + * coming from P to S, this predicate should return true if S is + * reachable from P using the edge. + */ + public GraphReachability(DiGraph graph, + Predicate> edgePredicate) { + this.graph = graph; + this.edgePredicate = edgePredicate; + } + + public void compute(N entry) { + graph.clearNodeAnnotations(); + graph.getNode(entry).setAnnotation(REACHABLE); + FixedPointGraphTraversal.newTraversal(this) + .computeFixedPoint(graph, entry); + } + + public void recompute(N reachableNode) { + GraphNode newReachable = graph.getNode(reachableNode); + Preconditions.checkState(newReachable.getAnnotation() != REACHABLE); + newReachable.setAnnotation(REACHABLE); + FixedPointGraphTraversal.newTraversal(this) + .computeFixedPoint(graph, reachableNode); + } + + @Override + public boolean traverseEdge(N source, E e, N destination) { + if (graph.getNode(source).getAnnotation() == REACHABLE && + (edgePredicate == null || + edgePredicate.apply(new EdgeTuple(source, e, destination)))) { + GraphNode destNode = graph.getNode(destination); + if (destNode.getAnnotation() != REACHABLE) { + destNode.setAnnotation(REACHABLE); + return true; + } + } + return false; + } + + public static final Annotation REACHABLE = new Annotation() {}; + + /** + * Represents Source Node, Edge and Destination Node. + */ + public static final class EdgeTuple { + public final N sourceNode; + public final E edge; + public final N destNode; + public EdgeTuple(N sourceNode, E edge, N destNode) { + this.sourceNode = sourceNode; + this.edge = edge; + this.destNode = destNode; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/GraphvizGraph.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/GraphvizGraph.java new file mode 100644 index 0000000..dffc1ac --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/GraphvizGraph.java @@ -0,0 +1,123 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + +import java.util.List; + +/** + * A graph that can be dumped to a Graphviz DOT file. + *

      + * An object which can be visualized as a graph should implement this interface. + * The DotFormatter.toDot function can be used to get a + * visualization of the object for debugging purpose. + * + */ +public interface GraphvizGraph { + + /** + * Name of the graph. + * + * @return Name of the graph. + */ + String getName(); + + /** + * Graph type. + * + * @return True if the graph is a directed graph. + */ + boolean isDirected(); + + /** + * Retrieve a list of nodes in the graph. + * + * @return A list of nodes in the graph. + */ + List getGraphvizNodes(); + + /** + * Retrieve a list of edges in the graph. + * + * @return A list of edges in the graph. + */ + List getGraphvizEdges(); + + + /** + * A Graphviz node. + */ + interface GraphvizNode { + + /** + * Retrieves the unique ID. + * + * @return A the unique ID of the node. + */ + String getId(); + + /** + * Retrieves color of the node. + * + * @return The color of the node. + */ + String getColor(); + + /** + * Retrieves the label of the node. + * + * @return Label of the node. + */ + String getLabel(); + } + + + /** + * A Graphviz edge. + */ + interface GraphvizEdge { + + /** + * Get the first node in the edge. In a directed node, this will be the + * source node. + * + * @return First node in the edge. + */ + String getNode1Id(); + + /** + * Get the second node in the edge. In a directed node, this will be the + * destination node. + * + * @return First node in the edge. + */ + String getNode2Id(); + + /** + * Retrieves color of the edge. + * + * @return The color of the edge. + */ + String getColor(); + + /** + * Retrieves the label of the edge. + * + * @return Label of the edge. + */ + String getLabel(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/LatticeElement.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/LatticeElement.java new file mode 100644 index 0000000..8d8ab06 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/LatticeElement.java @@ -0,0 +1,25 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + +/** + * A lattice element. + * + */ +public interface LatticeElement { + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/LinkedDirectedGraph.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/LinkedDirectedGraph.java new file mode 100644 index 0000000..1b60dc5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/LinkedDirectedGraph.java @@ -0,0 +1,581 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * A directed graph using linked list within nodes to store edge information. + *

      + * This implementation favors directed graph operations inherited from + * DirectedGraph. + * Operations from Graph would tends to be slower. + * + * + * @param Value type that the graph node stores. + * @param Value type that the graph edge stores. + */ +public class LinkedDirectedGraph + extends DiGraph implements GraphvizGraph { + protected final Map> nodes = + Maps.newHashMap(); + + @Override + public SubGraph newSubGraph() { + return new SimpleSubGraph(this); + } + + public static LinkedDirectedGraph createWithoutAnnotations() { + return new LinkedDirectedGraph(false, false); + } + + public static LinkedDirectedGraph createWithNodeAnnotations() { + return new LinkedDirectedGraph(true, false); + } + + public static LinkedDirectedGraph createWithEdgeAnnotations() { + return new LinkedDirectedGraph(false, true); + } + + public static LinkedDirectedGraph create() { + return new LinkedDirectedGraph(true, true); + } + + private final boolean useNodeAnnotations; + private final boolean useEdgeAnnotations; + + protected LinkedDirectedGraph( + boolean useNodeAnnotations, boolean useEdgeAnnotations) { + this.useNodeAnnotations = useNodeAnnotations; + this.useEdgeAnnotations = useEdgeAnnotations; + } + + @Override + public void connect(N srcValue, E edgeValue, N destValue) { + LinkedDirectedGraphNode src = getNodeOrFail(srcValue); + LinkedDirectedGraphNode dest = getNodeOrFail(destValue); + LinkedDirectedGraphEdge edge = + useEdgeAnnotations ? + new AnnotatedLinkedDirectedGraphEdge(src, edgeValue, dest) : + new LinkedDirectedGraphEdge(src, edgeValue, dest); + src.getOutEdges().add(edge); + dest.getInEdges().add(edge); + } + + @Override + public void disconnect(N n1, N n2) { + disconnectInDirection(n1, n2); + disconnectInDirection(n2, n1); + } + + @Override + public void disconnectInDirection(N srcValue, N destValue) { + LinkedDirectedGraphNode src = getNodeOrFail(srcValue); + LinkedDirectedGraphNode dest = getNodeOrFail(destValue); + for (DiGraphEdge edge : getDirectedGraphEdges(srcValue, destValue)) { + src.getOutEdges().remove(edge); + dest.getInEdges().remove(edge); + } + } + + @Override + public Iterable> getDirectedGraphNodes() { + return Collections.>unmodifiableCollection( + nodes.values()); + } + + @Override + public DiGraphNode getDirectedGraphNode(N nodeValue) { + return nodes.get(nodeValue); + } + + @Override + public GraphNode getNode(N nodeValue) { + return getDirectedGraphNode(nodeValue); + } + + @Override + public List> getInEdges(N nodeValue) { + LinkedDirectedGraphNode node = getNodeOrFail(nodeValue); + return Collections.>unmodifiableList(node.getInEdges()); + } + + @Override + public List> getOutEdges(N nodeValue) { + LinkedDirectedGraphNode node = getNodeOrFail(nodeValue); + return Collections.>unmodifiableList(node.getOutEdges()); + } + + @Override + public DiGraphNode createDirectedGraphNode(N nodeValue) { + LinkedDirectedGraphNode node = nodes.get(nodeValue); + if (node == null) { + node = useNodeAnnotations ? + new AnnotatedLinkedDirectedGraphNode(nodeValue) : + new LinkedDirectedGraphNode(nodeValue); + nodes.put(nodeValue, node); + } + return node; + } + + @Override + public List> getEdges(N n1, N n2) { + // Since this is a method from a generic graph, edges from both + // directions must be added to the returning list. + List> forwardEdges = getDirectedGraphEdges(n1, n2); + List> backwardEdges = getDirectedGraphEdges(n2, n1); + int totalSize = forwardEdges.size() + backwardEdges.size(); + List> edges = Lists.newArrayListWithCapacity(totalSize); + edges.addAll(forwardEdges); + edges.addAll(backwardEdges); + return edges; + } + + @Override + public GraphEdge getFirstEdge(N n1, N n2) { + DiGraphNode dNode1 = getNodeOrFail(n1); + DiGraphNode dNode2 = getNodeOrFail(n2); + for (DiGraphEdge outEdge : dNode1.getOutEdges()) { + if (outEdge.getDestination() == dNode2) { + return outEdge; + } + } + for (DiGraphEdge outEdge : dNode2.getOutEdges()) { + if (outEdge.getDestination() == dNode1) { + return outEdge; + } + } + return null; + } + + @Override + public GraphNode createNode(N value) { + return createDirectedGraphNode(value); + } + + @Override + public List> getDirectedGraphEdges(N n1, N n2) { + DiGraphNode dNode1 = getNodeOrFail(n1); + DiGraphNode dNode2 = getNodeOrFail(n2); + List> edges = Lists.newArrayList(); + for (DiGraphEdge outEdge : dNode1.getOutEdges()) { + if (outEdge.getDestination() == dNode2) { + edges.add(outEdge); + } + } + return edges; + } + + @Override + public boolean isConnectedInDirection(N n1, N n2) { + return isConnectedInDirection(n1, Predicates.alwaysTrue(), n2); + } + + @Override + public boolean isConnectedInDirection(N n1, E edgeValue, N n2) { + return isConnectedInDirection(n1, Predicates.equalTo(edgeValue), n2); + } + + private boolean isConnectedInDirection(N n1, Predicate edgeMatcher, N n2) { + // Verify the nodes. + DiGraphNode dNode1 = getNodeOrFail(n1); + DiGraphNode dNode2 = getNodeOrFail(n2); + for (DiGraphEdge outEdge : dNode1.getOutEdges()) { + if (outEdge.getDestination() == dNode2 && + edgeMatcher.apply(outEdge.getValue())) { + return true; + } + } + + return false; + } + + @Override + public List> getDirectedPredNodes(N nodeValue) { + return getDirectedPredNodes(nodes.get(nodeValue)); + } + + @Override + public List> getDirectedSuccNodes(N nodeValue) { + return getDirectedSuccNodes(nodes.get(nodeValue)); + } + + @Override + public List> getDirectedPredNodes( + DiGraphNode dNode) { + if (dNode == null) { + throw new IllegalArgumentException(dNode + " is null"); + } + List> nodeList = Lists.newArrayList(); + for (DiGraphEdge edge : dNode.getInEdges()) { + nodeList.add(edge.getSource()); + } + return nodeList; + } + + @Override + public List> getDirectedSuccNodes( + DiGraphNode dNode) { + if (dNode == null) { + throw new IllegalArgumentException(dNode + " is null"); + } + List> nodeList = Lists.newArrayList(); + for (DiGraphEdge edge : dNode.getOutEdges()) { + nodeList.add(edge.getDestination()); + } + return nodeList; + } + + @Override + public List getGraphvizEdges() { + List edgeList = Lists.newArrayList(); + for (LinkedDirectedGraphNode node : nodes.values()) { + for (DiGraphEdge edge : node.getOutEdges()) { + edgeList.add((LinkedDirectedGraphEdge) edge); + } + } + return edgeList; + } + + @Override + public List getGraphvizNodes() { + List nodeList = + Lists.newArrayListWithCapacity(nodes.size()); + for (LinkedDirectedGraphNode node : nodes.values()) { + nodeList.add(node); + } + return nodeList; + } + + @Override + public String getName() { + return "LinkedGraph"; + } + + @Override + public boolean isDirected() { + return true; + } + + @Override + public Collection> getNodes() { + return Collections.>unmodifiableCollection(nodes.values()); + } + + @Override + public List> getNeighborNodes(N value) { + DiGraphNode node = getDirectedGraphNode(value); + return getNeighborNodes(node); + } + + public List> getNeighborNodes(DiGraphNode node) { + List> result = Lists.newArrayList(); + for (Iterator> i = + ((LinkedDirectedGraphNode) node).neighborIterator();i.hasNext();) { + result.add(i.next()); + } + return result; + } + + @Override + public Iterator> getNeighborNodesIterator(N value) { + LinkedDirectedGraphNode node = nodes.get(value); + Preconditions.checkNotNull(node); + return node.neighborIterator(); + } + + @Override + public List> getEdges() { + List> result = Lists.newArrayList(); + for (DiGraphNode node : nodes.values()) { + for (DiGraphEdge edge : node.getOutEdges()) { + result.add(edge); + } + } + return Collections.unmodifiableList(result); + } + + @Override + public int getNodeDegree(N value) { + DiGraphNode node = getNodeOrFail(value); + return node.getInEdges().size() + node.getOutEdges().size(); + } + + /** + * A directed graph node that stores outgoing edges and incoming edges as an + * list within the node itself. + */ + static class LinkedDirectedGraphNode implements DiGraphNode, + GraphvizNode { + + List> inEdgeList = Lists.newArrayList(); + List> outEdgeList = + Lists.newArrayList(); + + protected final N value; + + /** + * Constructor + * + * @param nodeValue Node's value. + */ + LinkedDirectedGraphNode(N nodeValue) { + this.value = nodeValue; + } + + @Override + public N getValue() { + return value; + } + + @Override + public A getAnnotation() { + throw new UnsupportedOperationException( + "Graph initialized with node annotations turned off"); + } + + @Override + public void setAnnotation(Annotation data) { + throw new UnsupportedOperationException( + "Graph initialized with node annotations turned off"); + } + + @Override + public String getColor() { + return "white"; + } + + @Override + public String getId() { + return "LDN" + hashCode(); + } + + @Override + public String getLabel() { + return value != null ? value.toString() : "null"; + } + + @Override + public String toString() { + return getLabel(); + } + + @Override + public List> getInEdges() { + return inEdgeList; + } + + @Override + public List> getOutEdges() { + return outEdgeList; + } + + private Iterator> neighborIterator() { + return new NeighborIterator(); + } + + private class NeighborIterator implements Iterator> { + + private final Iterator> in = inEdgeList.iterator(); + private final Iterator> out = outEdgeList.iterator(); + + @Override + public boolean hasNext() { + return in.hasNext() || out.hasNext(); + } + + @Override + public GraphNode next() { + boolean isOut = !in.hasNext(); + Iterator> curIterator = isOut ? out : in; + DiGraphEdge s = curIterator.next(); + return isOut ? s.getDestination() : s.getSource(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Remove not supported."); + } + } + } + + /** + * A directed graph node with annotations. + */ + static class AnnotatedLinkedDirectedGraphNode + extends LinkedDirectedGraphNode { + + protected Annotation annotation; + + /** + * @param nodeValue Node's value. + */ + AnnotatedLinkedDirectedGraphNode(N nodeValue) { + super(nodeValue); + } + + @SuppressWarnings("unchecked") + @Override + public A getAnnotation() { + return (A) annotation; + } + + @Override + public void setAnnotation(Annotation data) { + annotation = data; + } + } + + /** + * A directed graph edge that stores the source and destination nodes at each + * edge. + */ + static class LinkedDirectedGraphEdge implements DiGraphEdge, + GraphvizEdge { + + private DiGraphNode sourceNode; + + private DiGraphNode destNode; + + protected final E value; + + /** + * Constructor. + * + * @param edgeValue Edge Value. + */ + LinkedDirectedGraphEdge(DiGraphNode sourceNode, + E edgeValue, DiGraphNode destNode) { + this.value = edgeValue; + this.sourceNode = sourceNode; + this.destNode = destNode; + } + + @Override + public DiGraphNode getSource() { + return sourceNode; + } + + @Override + public DiGraphNode getDestination() { + return destNode; + } + + @Override + public void setDestination(DiGraphNode node) { + destNode = node; + } + + @Override + public void setSource(DiGraphNode node) { + sourceNode = node; + } + + @Override + public E getValue() { + return value; + } + + @Override + public A getAnnotation() { + throw new UnsupportedOperationException( + "Graph initialized with edge annotations turned off"); + } + + @Override + public void setAnnotation(Annotation data) { + throw new UnsupportedOperationException( + "Graph initialized with edge annotations turned off"); + } + + @Override + public String getColor() { + return "black"; + } + + @Override + public String getLabel() { + return value != null ? value.toString() : "null"; + } + + @Override + public String getNode1Id() { + return ((LinkedDirectedGraphNode) sourceNode).getId(); + } + + @Override + public String getNode2Id() { + return ((LinkedDirectedGraphNode) destNode).getId(); + } + + @Override + public String toString() { + return sourceNode.toString() + " -> " + destNode.toString(); + } + + @Override + public GraphNode getNodeA() { + return sourceNode; + } + + @Override + public GraphNode getNodeB() { + return destNode; + } + } + + /** + * A directed graph edge that stores the source and destination nodes at each + * edge. + */ + static class AnnotatedLinkedDirectedGraphEdge + extends LinkedDirectedGraphEdge { + + protected Annotation annotation; + + /** + * Constructor. + * + * @param edgeValue Edge Value. + */ + AnnotatedLinkedDirectedGraphEdge(DiGraphNode sourceNode, + E edgeValue, DiGraphNode destNode) { + super(sourceNode, edgeValue, destNode); + } + + @SuppressWarnings("unchecked") + @Override + public A getAnnotation() { + return (A) annotation; + } + + @Override + public void setAnnotation(Annotation data) { + annotation = data; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/LinkedUndirectedGraph.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/LinkedUndirectedGraph.java new file mode 100644 index 0000000..45e6922 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/LinkedUndirectedGraph.java @@ -0,0 +1,487 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * An undirected graph using linked list within nodes to store edge + * information. + * + * + * @param Value type that the graph node stores. + * @param Value type that the graph edge stores. + */ +public class LinkedUndirectedGraph + extends UndiGraph implements GraphvizGraph { + protected final Map> nodes = + Maps.newHashMap(); + + @Override + public SubGraph newSubGraph() { + return new SimpleSubGraph(this); + } + + public static LinkedUndirectedGraph createWithoutAnnotations() { + return new LinkedUndirectedGraph(false, false); + } + + public static LinkedUndirectedGraph createWithNodeAnnotations() { + return new LinkedUndirectedGraph(true, false); + } + + public static LinkedUndirectedGraph createWithEdgeAnnotations() { + return new LinkedUndirectedGraph(false, true); + } + + public static LinkedUndirectedGraph create() { + return new LinkedUndirectedGraph(true, true); + } + + private final boolean useNodeAnnotations; + private final boolean useEdgeAnnotations; + + protected LinkedUndirectedGraph( + boolean useNodeAnnotations, boolean useEdgeAnnotations) { + this.useNodeAnnotations = useNodeAnnotations; + this.useEdgeAnnotations = useEdgeAnnotations; + } + + @Override + public void connect(N srcValue, E edgeValue, N destValue) { + LinkedUndirectedGraphNode src = getNodeOrFail(srcValue); + LinkedUndirectedGraphNode dest = getNodeOrFail(destValue); + LinkedUndirectedGraphEdge edge = + useEdgeAnnotations ? + new AnnotatedLinkedUndirectedGraphEdge(src, edgeValue, dest) : + new LinkedUndirectedGraphEdge(src, edgeValue, dest); + src.getNeighborEdges().add(edge); + dest.getNeighborEdges().add(edge); + } + + @Override + public void disconnect(N srcValue, N destValue) { + LinkedUndirectedGraphNode src = getNodeOrFail(srcValue); + LinkedUndirectedGraphNode dest = getNodeOrFail(destValue); + for (UndiGraphEdge edge : + getUndirectedGraphEdges(srcValue, destValue)) { + src.getNeighborEdges().remove(edge); + dest.getNeighborEdges().remove(edge); + } + } + + @Override + public UndiGraphNode createUndirectedGraphNode( + N nodeValue) { + LinkedUndirectedGraphNode node = nodes.get(nodeValue); + if (node == null) { + node = useNodeAnnotations ? + new AnnotatedLinkedUndirectedGraphNode(nodeValue) : + new LinkedUndirectedGraphNode(nodeValue); + nodes.put(nodeValue, node); + } + return node; + } + + @Override + public List> getNeighborNodes(N value) { + UndiGraphNode uNode = getUndirectedGraphNode(value); + List> nodeList = Lists.newArrayList(); + for (Iterator> i = getNeighborNodesIterator(value); + i.hasNext();) { + nodeList.add(i.next()); + } + return nodeList; + } + + @Override + public Iterator> getNeighborNodesIterator(N value) { + UndiGraphNode uNode = getUndirectedGraphNode(value); + Preconditions.checkNotNull(uNode, "%s should be in the graph.", value); + return ((LinkedUndirectedGraphNode) uNode).neighborIterator(); + } + + @SuppressWarnings("unchecked") + @Override + public List> getUndirectedGraphEdges(N n1, N n2) { + UndiGraphNode dNode1 = nodes.get(n1); + if (dNode1 == null) { + return null; + } + UndiGraphNode dNode2 = nodes.get(n2); + if (dNode2 == null) { + return null; + } + List> edges = Lists.newArrayList(); + for (UndiGraphEdge outEdge : dNode1.getNeighborEdges()) { + if (outEdge.getNodeA() == dNode2 || outEdge.getNodeB() == dNode2) { + edges.add(outEdge); + } + } + return edges; + } + + @Override + public UndiGraphNode getUndirectedGraphNode(N nodeValue) { + return nodes.get(nodeValue); + } + + @Override + public Collection> getUndirectedGraphNodes() { + return Collections.>unmodifiableCollection( + nodes.values()); + } + + @Override + public GraphNode createNode(N value) { + return createUndirectedGraphNode(value); + } + + @Override + public List> getEdges(N n1, N n2) { + return Collections.>unmodifiableList( + getUndirectedGraphEdges(n1, n2)); + } + + @Override + public GraphEdge getFirstEdge(N n1, N n2) { + UndiGraphNode dNode1 = getNodeOrFail(n1); + UndiGraphNode dNode2 = getNodeOrFail(n2); + for (UndiGraphEdge outEdge : dNode1.getNeighborEdges()) { + if (outEdge.getNodeA() == dNode2 || outEdge.getNodeB() == dNode2) { + return outEdge; + } + } + return null; + } + + @Override + public GraphNode getNode(N value) { + return getUndirectedGraphNode(value); + } + + @Override + public boolean isConnected(N n1, N n2) { + return isConnected(n1, Predicates.alwaysTrue(), n2); + } + + @Override + public boolean isConnected(N n1, E e, N n2) { + return isConnected(n1, Predicates.equalTo(e), n2); + } + + private boolean isConnected(N n1, Predicate edgePredicate, N n2) { + UndiGraphNode dNode1 = nodes.get(n1); + if (dNode1 == null) { + return false; + } + UndiGraphNode dNode2 = nodes.get(n2); + if (dNode2 == null) { + return false; + } + for (UndiGraphEdge outEdge : dNode1.getNeighborEdges()) { + if ((outEdge.getNodeA() == dNode1 && outEdge.getNodeB() == dNode2) || + (outEdge.getNodeA() == dNode2 && outEdge.getNodeB() == dNode1)) { + if (edgePredicate.apply(outEdge.getValue())) { + return true; + } + } + } + return false; + } + + @Override + public List getGraphvizEdges() { + List edgeList = Lists.newArrayList(); + for (LinkedUndirectedGraphNode node : nodes.values()) { + for (UndiGraphEdge edge : node.getNeighborEdges()) { + if (edge.getNodeA() == node) { + edgeList.add((GraphvizEdge) edge); + } + } + } + return edgeList; + } + + @Override + public String getName() { + return "LinkedUndirectedGraph"; + } + + @Override + public List getGraphvizNodes() { + List nodeList = + Lists.newArrayListWithCapacity(nodes.size()); + for (LinkedUndirectedGraphNode node : nodes.values()) { + nodeList.add(node); + } + return nodeList; + } + + @Override + public boolean isDirected() { + return false; + } + + @Override + public Collection> getNodes() { + return Collections.> unmodifiableCollection(nodes.values()); + } + + @SuppressWarnings("unchecked") + @Override + public List> getEdges() { + List> result = Lists.newArrayList(); + for (LinkedUndirectedGraphNode node : nodes.values()) { + for (UndiGraphEdge edge : node.getNeighborEdges()) { + if (edge.getNodeA() == node) { + result.add(edge); + } + } + } + return result; + } + + @Override + public int getNodeDegree(N value) { + UndiGraphNode uNode = getUndirectedGraphNode(value); + if (uNode == null) { + throw new IllegalArgumentException(value + " not found in graph"); + } + return uNode.getNeighborEdges().size(); + } + + /** + * An undirected graph node that stores outgoing edges and incoming edges as + * an list within the node itself. + */ + static class LinkedUndirectedGraphNode implements UndiGraphNode, + GraphvizNode { + + private List> neighborList = + Lists.newArrayList(); + private final N value; + + LinkedUndirectedGraphNode(N nodeValue) { + this.value = nodeValue; + } + + @Override + public List> getNeighborEdges() { + return neighborList; + } + + @Override + public Iterator> getNeighborEdgesIterator() { + return neighborList.iterator(); + } + + @Override + public A getAnnotation() { + throw new UnsupportedOperationException( + "Graph initialized with node annotations turned off"); + } + + @Override + public void setAnnotation(Annotation data) { + throw new UnsupportedOperationException( + "Graph initialized with node annotations turned off"); + } + + @Override + public N getValue() { + return value; + } + + @Override + public String getColor() { + return "white"; + } + + @Override + public String getId() { + return "LDN" + hashCode(); + } + + @Override + public String getLabel() { + return value != null ? value.toString() : "null"; + } + + public Iterator> neighborIterator() { + return new NeighborIterator(); + } + + private class NeighborIterator implements Iterator> { + + private final Iterator> edgeIterator = + neighborList.iterator(); + + @Override + public boolean hasNext() { + return edgeIterator.hasNext(); + } + + @Override + public GraphNode next() { + UndiGraphEdge edge = edgeIterator.next(); + if (edge.getNodeA() == LinkedUndirectedGraphNode.this) { + return edge.getNodeB(); + } else { + return edge.getNodeA(); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Remove not supported."); + } + } + } + + /** + * An undirected graph node with annotations. + */ + static class AnnotatedLinkedUndirectedGraphNode + extends LinkedUndirectedGraphNode { + + protected Annotation annotation; + + AnnotatedLinkedUndirectedGraphNode(N nodeValue) { + super(nodeValue); + } + + @SuppressWarnings("unchecked") + @Override + public A getAnnotation() { + return (A) annotation; + } + + @Override + public void setAnnotation(Annotation data) { + annotation = data; + } + } + + /** + * An undirected graph edge that stores two nodes at each edge. + */ + static class LinkedUndirectedGraphEdge implements UndiGraphEdge, + GraphvizEdge { + + private UndiGraphNode nodeA; + private UndiGraphNode nodeB; + protected final E value; + + LinkedUndirectedGraphEdge(UndiGraphNode nodeA, E edgeValue, + UndiGraphNode nodeB) { + this.value = edgeValue; + this.nodeA = nodeA; + this.nodeB = nodeB; + } + + @Override + public E getValue() { + return value; + } + + @Override + public GraphNode getNodeA() { + return nodeA; + } + + @Override + public GraphNode getNodeB() { + return nodeB; + } + + @Override + public A getAnnotation() { + throw new UnsupportedOperationException( + "Graph initialized with edge annotations turned off"); + } + + @Override + public void setAnnotation(Annotation data) { + throw new UnsupportedOperationException( + "Graph initialized with edge annotations turned off"); + } + + @Override + public String getColor() { + return "black"; + } + + @Override + public String getLabel() { + return value != null ? value.toString() : "null"; + } + + @SuppressWarnings("unchecked") + @Override + public String getNode1Id() { + return ((LinkedUndirectedGraphNode) nodeA).getId(); + } + + @SuppressWarnings("unchecked") + @Override + public String getNode2Id() { + return ((LinkedUndirectedGraphNode) nodeB).getId(); + } + + @Override + public String toString() { + return nodeA.toString() + " -- " + nodeB.toString(); + } + } + + /** + * An annotated undirected graph edge.. + */ + static class AnnotatedLinkedUndirectedGraphEdge + extends LinkedUndirectedGraphEdge { + + protected Annotation annotation; + + AnnotatedLinkedUndirectedGraphEdge( + UndiGraphNode nodeA, E edgeValue, + UndiGraphNode nodeB) { + super(nodeA, edgeValue, nodeB); + } + + @SuppressWarnings("unchecked") + @Override + public A getAnnotation() { + return (A) annotation; + } + + @Override + public void setAnnotation(Annotation data) { + annotation = data; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/StandardUnionFind.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/StandardUnionFind.java new file mode 100644 index 0000000..1362ba2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/StandardUnionFind.java @@ -0,0 +1,227 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp.graph; + +import javax.annotation.Nullable; +import com.google.common.base.Objects; +import static com.google.common.base.Preconditions.checkArgument; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + +import static com.google.common.collect.Iterators.filter; +import com.google.common.collect.Maps; + +import java.io.Serializable; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * A Union-Find implementation. + * + *

      This class implements Union-Find algorithm with rank and path + * compression. + * + *

      See + * algorithmist for more detail. + * + * @param element type + */ +public class StandardUnionFind implements Serializable, UnionFind { + + private static final long serialVersionUID = -1L; + + /** All values with the same root node are in the same equivalence set. */ + private final Map> elmap = Maps.newLinkedHashMap(); + + /** Creates an empty UnionFind structure. */ + public StandardUnionFind() { + } + + /** + * Creates an UnionFind structure being a copy of other structure. + * The created structure is optimal in a sense that the paths to + * the root from any element will have a length of at most 1. + * + * @param other structure to be copied + */ + public StandardUnionFind(UnionFind other) { + for (E elem : other.elements()) { + union(elem, other.find(elem)); + } + } + + @Override + public void add(E e) { + union(e, e); + } + + @Override + public E union(E a, E b) { + Node nodeA = findRootOrCreateNode(a); + Node nodeB = findRootOrCreateNode(b); + + if (nodeA == nodeB) { + return nodeA.element; + } + if (nodeA.rank > nodeB.rank) { + nodeB.parent = nodeA; + nodeA.size += nodeB.size; + return nodeA.element; + } + nodeA.parent = nodeB; + if (nodeA.rank == nodeB.rank) { + nodeB.rank++; + } + nodeB.size += nodeA.size; + return nodeB.element; + } + + @Override + public E find(E e) { + checkArgument(elmap.containsKey(e), "Element does not exist: %s", e); + return findRoot(elmap.get(e)).element; + } + + @Override + public boolean areEquivalent(E a, E b) { + E aRep = find(a); + E bRep = find(b); + return aRep == bRep; + } + + @Override + public Set elements() { + return Collections.unmodifiableSet(elmap.keySet()); + } + + @Override + public Collection> allEquivalenceClasses() { + Map, ImmutableSet.Builder> groupsTmp = Maps.newHashMap(); + for (Node elem : elmap.values()) { + Node root = findRoot(elem); + ImmutableSet.Builder builder = groupsTmp.get(root); + if (builder == null) { + builder = ImmutableSet.builder(); + groupsTmp.put(root, builder); + } + builder.add(elem.element); + } + ImmutableList.Builder> result = ImmutableList.builder(); + for (ImmutableSet.Builder group : groupsTmp.values()) { + result.add(group.build()); + } + return result.build(); + } + + /** + * If e is already in a non-trivial equivalence class, that is, a class with + * more than two elements, then return the {@link Node} corresponding to the + * representative element. Otherwise, if e sits in an equivalence class by + * itself, then create a {@link Node}, put it into elmap and return it. + */ + private Node findRootOrCreateNode(E e) { + Node node = elmap.get(e); + if (node != null) { + return findRoot(node); + } + node = new Node(e); + elmap.put(e, node); + return node; + } + + /** + * Given a {@link Node}, walk the parent field as far as possible, until + * reaching the root, which is the {@link Node} for the current + * representative of this equivalence class. To achieve low runtime + * complexity, also compress the path, by making each node a direct child of + * the root. + */ + private Node findRoot(Node node) { + if (node.parent != node) { + node.parent = findRoot(node.parent); + } + return node.parent; + } + + @Override + public Set findAll(final E value) { + checkArgument(elmap.containsKey(value), "Element does not exist: " + value); + + final Predicate isSameRoot = new Predicate() { + + /** some node that's close to the root, or null */ + Node nodeForValue = elmap.get(value); + + @Override + public boolean apply(@Nullable Object b) { + if (Objects.equal(value, b)) { + return true; + } + Node nodeForB = elmap.get(b); + if (nodeForB == null) { + return false; + } + nodeForValue = findRoot(nodeForValue); + return findRoot(nodeForB) == nodeForValue; + } + }; + + return new AbstractSet() { + + @Override public boolean contains(Object o) { + return isSameRoot.apply(o); + } + + @Override public Iterator iterator() { + return filter(elmap.keySet().iterator(), + isSameRoot); + } + + @Override public int size() { + return findRoot(elmap.get(value)).size; + } + }; + } + + /** The internal node representation. */ + private static class Node { + /** The parent node of this element. */ + Node parent; + + /** The element represented by this node. */ + final E element; + + /** A bound on the depth of the subtree rooted to this node. */ + int rank = 0; + + /** + * If this node is the root of a tree, this is the number of elements in the + * tree. Otherwise, it's undefined. + */ + int size = 1; + + Node(E element) { + this.parent = this; + this.element = element; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/SubGraph.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/SubGraph.java new file mode 100644 index 0000000..9f0b503 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/SubGraph.java @@ -0,0 +1,33 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + + +/** + * An interface representing a subgraph that provides adjacency calculation to + * a node. + * + * @param Value type that the graph node stores. + * @param Value type that the graph edge stores. + */ +public interface SubGraph { + /** Returns true if the node is a neighbor of any node in this SubGraph. */ + boolean isIndependentOf(N node); + + /** Adds the node into this subgraph. */ + void addNode(N value); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/UndiGraph.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/UndiGraph.java new file mode 100644 index 0000000..06b60ff --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/UndiGraph.java @@ -0,0 +1,61 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** + * A generic undirected graph. + * + * @param Value type that the graph node stores. + * @param Value type that the graph edge stores. + */ +public abstract class UndiGraph extends Graph { + + /** + * Gets an immutable collection of all the nodes in this graph. + */ + abstract Collection> getUndirectedGraphNodes(); + + abstract UndiGraphNode createUndirectedGraphNode(N nodeValue); + + public abstract UndiGraphNode getUndirectedGraphNode(N nodeValue); + + abstract List> getUndirectedGraphEdges(N n1, N n2); + + /** + * A generic undirected graph node. + * + * @param Value type that the graph node stores. + * @param Value type that the graph edge stores. + */ + public static interface UndiGraphNode extends GraphNode { + public List> getNeighborEdges(); + public Iterator> getNeighborEdgesIterator(); + } + + /** + * A generic undirected graph edge. + * + * @param Value type that the graph node stores. + * @param Value type that the graph edge stores. + */ + public static interface UndiGraphEdge extends GraphEdge { + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/UnionFind.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/UnionFind.java new file mode 100644 index 0000000..aa26bf2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/UnionFind.java @@ -0,0 +1,88 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp.graph; + +import java.util.Collection; +import java.util.Set; + + +/** + * Union-Find is a classical algorithm used to find connected components in + * graph theory. + * + *

      Each equivalence class has a representative element that is chosen + * arbitrarily and is used to determine if two elements are members of the same + * class. + * + *

      See + * algorithmist for more detail. + * + * @param element type + */ +public interface UnionFind { + + /** + * Adds the given element to a new set if it is not already in a set. + * + * @throws UnsupportedOperationException if the add operation is not + * supported by this union-find. + */ + public void add(E e); + + /** + * Unions the equivalence classes of {@code a} and {@code b} and returns the + * representative of the resulting equivalence class. The elements will be + * added if they are not already present. + * + * @throws UnsupportedOperationException if the add operation is not + * supported by this union-find. + */ + public E union(E a, E b); + + /** Returns the representative of the equivalence class of {@code e}. */ + public E find(E e); + + /** + * Returns true if {@code a} and {@code b} belong to the same equivalence + * class. + * + * @throws IllegalArgumentException if any argument is not an element of this + * structure. + */ + public boolean areEquivalent(E a, E b); + + /** Returns an unmodifiable set of all elements added to the UnionFind. */ + public Set elements(); + + /** + * Returns an immutable collection containing all equivalence classes. The + * returned collection represents a snapshot of the current state and will not + * reflect changes made to the data structure. + */ + public Collection> allEquivalenceClasses(); + + /** + * Returns the elements in the same equivalence class as {@code value}. + * + * @return an unmodifiable view. As equivalence classes are merged, this set + * will reflect those changes. + * @throws IllegalArgumentException if a requested element does not belong + * to the structure. + */ + public Set findAll(final E value); + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/package.html b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/package.html new file mode 100644 index 0000000..36f9fd1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/graph/package.html @@ -0,0 +1,12 @@ + + + + + + + +Provides graph data structures and algorithms for coloring and fixed-point +computations. + + + diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/instrumentation_template.proto b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/instrumentation_template.proto new file mode 100644 index 0000000..f96b454 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/instrumentation_template.proto @@ -0,0 +1,45 @@ +// Copyright 2008 Google Inc. All Rights Reserved. +// Author: avd@google.com (Antonio Vicente) +// +// Provides JS Compiler with parameters for the instrumentation pass + +syntax = "proto2"; + +package jscomp; + +option java_package = "com.google.javascript.jscomp"; +option java_multiple_files = true; + +message Instrumentation { + // name of function(ID = ); + // used to inform the harness about the contents of a module + optional string report_defined = 1; + + // name of function(ID = ); + // used to inform the harness about a function call + optional string report_call = 2; + + // name of function(ID = , VAL = ); + // used to inform the harness about a function exit. Must return + // its second argument. + // + // @returns VAL + optional string report_exit = 6; + + // List of variable declarations in the application's source code + // that should be replaced by variables with the same name that are + // part of the instrumentation harness. The presence of these + // declarations in the original code allows debug UIs that access + // these variables to compile when the instrumentation pass is + // disabled. + repeated string declaration_to_remove = 3; + + // Definition of functions used to report module contents and + // function calls. Will be added to the start of the app's main + // module. + repeated string init = 4; + + // name of function(); + // used to inform the harness about the app name + optional string app_name_setter = 5; +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/js/base.js b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/js/base.js new file mode 100644 index 0000000..e9dd7ee --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/js/base.js @@ -0,0 +1,28 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * @fileoverview The base namespace for code injected by the compiler + * at compile-time. + * + * @author nicksantos@google.com (Nick Santos) + */ + +// Because this is injected at compile-time, we don't annotate +// it as a constant, because that would just be extra work for +// the compiler. +var $jscomp = {}; diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/js/runtime_type_check.js b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/js/runtime_type_check.js new file mode 100644 index 0000000..433266b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/js/runtime_type_check.js @@ -0,0 +1,436 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * @fileoverview Provides the boilerplate code for run-time type checking. + * + */ + +/** @const */ +$jscomp.typecheck = {}; + +/** + * A state variable to suspend checking, to avoid infinite calls + * caused by calling checked code from the checking functions. + * + * @type {boolean} + */ +$jscomp.typecheck.suspendChecking = false; + + +/** + * Log and possibly format the run-time type check warning. This + * function is customized at compile-time. + * + * @param {string} warning the warning to log. + * @param {*} expr the faulty expression. + */ +$jscomp.typecheck.log = function(warning, expr) {}; + +/** + * Checks that the given expression matches one of the given checkers, + * logging if not, and returning the expression regardless. + * + * @param {*} expr the expression to check. + * @param {!Array.} checkers the checkers to + * use in checking, one of these has to match for checking to succeed. + * #return {*} the given expression back. + */ +$jscomp.typecheck.checkType = function(expr, checkers) { + if ($jscomp.typecheck.suspendChecking) { + return expr; + } + $jscomp.typecheck.suspendChecking = true; + + for (var i = 0; i < checkers.length; i++) { + var checker = checkers[i]; + var ok = checker.check(expr); + if (ok) { + $jscomp.typecheck.suspendChecking = false; + return expr; + } + } + + var warning = $jscomp.typecheck.prettify_(expr) + ' not in ' + + checkers.join(' '); + + $jscomp.typecheck.log(warning, expr); + + $jscomp.typecheck.suspendChecking = false; + return expr; +}; + + +/** + * Prettify the given expression for printing. + * + * @param {*} expr the expression. + * @return {string} a string representation of the given expression. + * @private + */ +$jscomp.typecheck.prettify_ = function(expr) { + return $jscomp.typecheck.getClassName_(expr) || String(expr); +}; + +/** + * Gets the class name if the given expression is an object. + * + * @param {*} expr the expression. + * @return {string|undefined} the class name or undefined if the + * expression is not an object. + * @private + */ +$jscomp.typecheck.getClassName_ = function(expr) { + var className = void 0; + if (typeof expr == 'object' && expr && expr.constructor) { + className = expr.constructor.name; + if (!className) { + var funNameRe = /function (.{1,})\(/; + var m = (funNameRe).exec(expr.constructor.toString()); + className = m && m.length > 1 ? m[1] : void 0; + } + } + return className; +}; + +/** + * Interface for all checkers. + * + * @interface + */ +$jscomp.typecheck.Checker = function() {}; + + +/** + * Checks the given expression. + * + * @param {*} expr the expression to check. + * @return {boolean} whether the given expression matches this checker. + */ +$jscomp.typecheck.Checker.prototype.check = function(expr) {}; + + + +/** + * A class for all value checkers, except the null checker. + * + * @param {string} type the value type (e.g. 'number') of this checker. + * @constructor + * @implements {$jscomp.typecheck.Checker} + * @private + */ +$jscomp.typecheck.ValueChecker_ = function(type) { + /** + * The value type of this checker. + * @type {string} + * @private + */ + this.type_ = type; +}; + + +/** @inheritDoc */ +$jscomp.typecheck.ValueChecker_.prototype.check = function(expr) { + return typeof(expr) == this.type_; +}; + + +/** @inheritDoc */ +$jscomp.typecheck.ValueChecker_.prototype.toString = function() { + return 'value(' + this.type_ + ')'; +}; + + + +/** + * A checker class for null values. + * + * @constructor + * @implements {$jscomp.typecheck.Checker} + * @private + */ +$jscomp.typecheck.NullChecker_ = function() {}; + + +/** @inheritDoc */ +$jscomp.typecheck.NullChecker_.prototype.check = function(expr) { + return expr === null; +}; + + +/** @inheritDoc */ +$jscomp.typecheck.NullChecker_.prototype.toString = function() { + return 'value(null)'; +}; + + +/** + * A checker class for a class defined in externs, including built-in + * JS types. + * + *

      If the class type is undefined, then checking is suspended to + * avoid spurious warnings. This is necessary because some externs + * types are not defined in all browsers. For example, Window is not + * defined Chrome, as window has the type DOMWindow. + * + *

      Another subtlety is that a built-in type may be referenced in a + * different frame than the one in which it was created. This causes + * instanceOf to return false even though the object is of the correct + * type. We work around this by checking as many windows as possible, + * redefining open on top and window to keep track of them. + * + * @param {string} className the name of the extern class to check. + * @constructor + * @implements {$jscomp.typecheck.Checker} + * @private + */ +$jscomp.typecheck.ExternClassChecker_ = function(className) { + /** + * The name of the extern class to check. + * @type {string} + * @private + */ + this.className_ = className; +}; + + +/** + * A list of (hopefully all) open windows. + * + * @type {!Array.} + */ +$jscomp.typecheck.ExternClassChecker_.windows = []; + + +/** + * A list of the original open methods that have been redefined. + * + * @type {!Array.} + */ +$jscomp.typecheck.ExternClassChecker_.oldOpenFuns = []; + + +/** + * Redefines the open method on the given window, adding tracking. + * + * @param {!Object} win the window to track. + */ +$jscomp.typecheck.ExternClassChecker_.trackOpenOnWindow = function(win) { + if (win.tracked) { + return; + } + win.tracked = true; + + var key = $jscomp.typecheck.ExternClassChecker_.oldOpenFuns.length; + + $jscomp.typecheck.ExternClassChecker_.oldOpenFuns.push(win.open); + $jscomp.typecheck.ExternClassChecker_.windows.push(win); + + win.open = function() { + var w = $jscomp.typecheck.ExternClassChecker_.oldOpenFuns[key].apply( + this, arguments); + $jscomp.typecheck.ExternClassChecker_.trackOpenOnWindow(w); + return w; + }; +}; + + +/** + * Returns the global 'this' object. This will normally be the same as 'window' + * but when running in a worker thread, the DOM is not available. + * @return {!Object} + * @private + */ +$jscomp.typecheck.ExternClassChecker_.getGlobalThis_ = function() { + return (function() { return this; }).call(null); +}; + + +// Install listeners on the global 'this' object. +(function() { + var globalThis = $jscomp.typecheck.ExternClassChecker_.getGlobalThis_(); + $jscomp.typecheck.ExternClassChecker_.trackOpenOnWindow(globalThis); + + var theTop = globalThis['top']; + if (theTop) { + $jscomp.typecheck.ExternClassChecker_.trackOpenOnWindow(theTop); + } +})(); + + +/** @inheritDoc */ +$jscomp.typecheck.ExternClassChecker_.prototype.check = function(expr) { + var classTypeDefined = [ false ]; + for (var i = 0; i < $jscomp.typecheck.ExternClassChecker_.windows.length; + i++) { + var w = $jscomp.typecheck.ExternClassChecker_.windows[i]; + if (this.checkWindow_(w, expr, classTypeDefined)) { + return true; + } + } + return !classTypeDefined[0]; +}; + + +/** @inheritDoc */ +$jscomp.typecheck.ExternClassChecker_.prototype.toString = function() { + return 'ext_class(' + this.className_ + ')'; +}; + + +/** + * Checks whether the given expression is an instance of this extern + * class in this window or any of its frames and subframes. + * + * @param {!Window} w the window to start checking from. + * @param {*} expr the expression to check. + * @param {!Array.} classTypeDefined a wrapped boolean + * updated to indicate whether the class type was seen in any frame. + * @return true if the given expression is an instance of this class. + * @private + */ +$jscomp.typecheck.ExternClassChecker_.prototype.checkWindow_ = + function(w, expr, classTypeDefined) { + var classType = w[this.className_]; + classTypeDefined[0] |= !!classType; + if (classType && expr instanceof classType) { + return true; + } + for (var i = 0; i < w.length; i++) { + if (this.checkWindow_(w.frames[i], expr, classTypeDefined)) { + return true; + } + } + return false; +}; + + + +/** + * A class for all checkers of user-defined classes. + * + * @param {string} className name of the class to check. + * @constructor + * @implements {$jscomp.typecheck.Checker} + * @private + */ +$jscomp.typecheck.ClassChecker_ = function(className) { + + /** + * The name of the class to check. + * #type {string} + * @private + */ + this.className_ = className; +}; + + +/** @inheritDoc */ +$jscomp.typecheck.ClassChecker_.prototype.check = function(expr) { + return !!(expr && expr['instance_of__' + this.className_]); +}; + + +/** @inheritDoc */ +$jscomp.typecheck.ClassChecker_.prototype.toString = function() { + return 'class(' + this.className_ + ')'; +}; + + + +/** + * A class for all checkers of user-defined interfaces. + * + * @param {string} interfaceName name of the interface to check. + * @constructor + * @implements {$jscomp.typecheck.Checker} + * @private + */ +$jscomp.typecheck.InterfaceChecker_ = function(interfaceName) { + + /** + * The name of the interface to check. + * #type {string} + * @private + */ + this.interfaceName_ = interfaceName; +}; + + +/** @inheritDoc */ +$jscomp.typecheck.InterfaceChecker_.prototype.check = function(expr) { + return !!(expr && expr['implements__' + this.interfaceName_]); +}; + + +/** @inheritDoc */ +$jscomp.typecheck.InterfaceChecker_.prototype.toString = function() { + return 'interface(' + this.interfaceName_ + ')'; +}; + + + +/** + * A checker for null values. + * + * #type {!$jscomp.typecheck.Checker} a checker. + */ +$jscomp.typecheck.nullChecker = new $jscomp.typecheck.NullChecker_(); + + +/** + * Creates a checker for the given value type (excluding the null type). + * + * @param {string} type the value type. + * @return {!$jscomp.typecheck.Checker} a checker. + */ +$jscomp.typecheck.valueChecker = function(type) { + return new $jscomp.typecheck.ValueChecker_(type); +}; + + +/** + * Creates a checker for the given extern class name. + * + * @param {string} className the class name. + * @return {!$jscomp.typecheck.Checker} a checker. + */ +$jscomp.typecheck.externClassChecker = function(className) { + return new $jscomp.typecheck.ExternClassChecker_(className); +}; + + +/** + * Creates a checker for the given user-defined class. + * + * @param {string} className the class name. + * @return {!$jscomp.typecheck.Checker} a checker. + */ +$jscomp.typecheck.classChecker = function(className) { + return new $jscomp.typecheck.ClassChecker_(className); +}; + + +/** + * Creates a checker for the given user-defined interface. + * + * @param {string} interfaceName the interface name. + * @return {!$jscomp.typecheck.Checker} a checker. + */ +$jscomp.typecheck.interfaceChecker = function(interfaceName) { + return new $jscomp.typecheck.InterfaceChecker_(interfaceName); +}; diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/ErrorLevel.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/ErrorLevel.java new file mode 100644 index 0000000..fac6a06 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/ErrorLevel.java @@ -0,0 +1,28 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.jsonml; + +/** + * Represents possible error levels for JsonML errors. + * + * @author dhans@google.com (Daniel Hans) + */ +public enum ErrorLevel { + COMPILATION_ERROR, + COMPILATION_WARNING, + SYNTAX_ERROR, +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/JsonML.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/JsonML.java new file mode 100644 index 0000000..76b78d5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/JsonML.java @@ -0,0 +1,312 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.jsonml; + +import java.io.IOException; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Class which represents JsonML element according to the specification at + * "http://code.google.com/p/es-lab/wiki/JsonMLASTFormat" + * + * @author dhans@google.com (Daniel Hans) + */ +public class JsonML { + private final TagType type; + private Map attributes = + new EnumMap(TagAttr.class); + private List children = new ArrayList(); + + /** + * Creates a new element with a given type. + * @param type + */ + public JsonML(TagType type) { + this.type = type; + } + + /** + * Creates a new element. + * @param type type of the element + * @param children children to append to the element + */ + public JsonML(TagType type, JsonML... children) { + this(type, Arrays.asList(children)); + } + + public JsonML(TagType type, List children) { + this(type, Collections.emptyMap(), children); + } + + public JsonML(TagType type, Map attributes) { + this(type, attributes, Collections.emptyList()); + } + + public JsonML(TagType type, Map attributes, + List children) { + this.type = type; + this.attributes.putAll(attributes); + appendChildren(children); + } + + /** + * Inserts the given JsonML element at the given position in the + * list of children. + * @param index index at which the given element is to be inserted + * @param element JsonML element to be inserted + */ + public void addChild(int index, JsonML element) { + children.add(index, element); + } + + /** + * Appends a given child element to the list of children. + * @param element JsonML element to append + */ + public void appendChild(JsonML element) { + children.add(element); + } + + /** + * Appends a collection of children to the back of the list of children. + * @param elements collection of JsonML elements to append + */ + public void appendChildren(Collection elements) { + children.addAll(elements); + } + + /** + * Returns number of the children. + */ + public int childrenSize() { + return children.size(); + } + + /** + * Removes all elements from the list of children. + */ + public void clearChildren() { + setChildren(); + } + + /** + * Returns value associated with a given attribute. + * @param name name of the attribute + * @return associated value or null if the attribute is not present + */ + public Object getAttribute(TagAttr name) { + return attributes.get(name); + } + + /** + * Returns a map with attributes and respective values. + */ + public Map getAttributes() { + return attributes; + } + + /** + * Returns child at a given position. + */ + public JsonML getChild(int index) { + return children.get(index); + } + + /** + * Returns a list of all children. + */ + public List getChildren() { + return children; + } + + /** + * Returns the portion of children list between the specified + * fromIndex, inclusive, and toIndex, exclusive. + * @param fromIndex low endpoint (inclusive) + * @param toIndex high endpoint (exclusive) + */ + public List getChildren(int fromIndex, int toIndex) { + return children.subList(fromIndex, toIndex); + } + + /** + * Returns type of the JsonML element. + */ + public TagType getType() { + return type; + } + + /** + * Returns true if the JsonML element has at least one child. + */ + public boolean hasChildren() { + return !children.isEmpty(); + } + + /** + * Sets value for a given attribute. + * @param name name of the attribute + * @param value value to associate with the attribute + */ + public void setAttribute(TagAttr name, Object value) { + attributes.put(name, value); + } + + /** + * Sets attributes of the JsonML element. + * @param attributes map with attributes and their values + */ + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + + /** + * Replaces the element at the given position in the list of children wit + * the given JsonML element. + * @param index index of element to replace + * @param element JsonML element to append + */ + public void setChild(int index, JsonML element) { + children.set(index, element); + } + + /** + * Replaces all elements in the list of children with the given + * JsonML elements. + * @param children a comma separated list of JsonML elements + */ + public void setChildren(JsonML... children) { + this.children.clear(); + this.children.addAll(Arrays.asList(children)); + } + + /** + * Replaces all elements in the list of children with the given + * list of JsonML elements.. + * @param children a list of JsonML elements. + */ + public void setChildren(List children) { + this.children = children; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + toString(sb, true, true); + return sb.toString(); + } + + private void toString(StringBuilder sb, boolean printAttributes, + boolean printChildren) { + sb.append("[\""); + escapeStringOnto(type.name(), sb); + sb.append('"'); + + if (printAttributes) { + sb.append(", {"); + boolean first = true; + for (Entry entry : attributes.entrySet()) { + if (first) { + first = false; + } else { + sb.append(", "); + } + sb.append('"'); + escapeStringOnto(entry.getKey().toString(), sb); + sb.append("\": "); + Object value = entry.getValue(); + if (value == null) { + sb.append("null"); + } else if (value instanceof String) { + sb.append('"'); + escapeStringOnto((String) value, sb); + sb.append('"'); + } else { + sb.append(value); + } + } + sb.append("}"); + } + + if (printChildren) { + for (JsonML child : children) { + sb.append(", "); + sb.append(child.toString()); + } + } + sb.append(']'); + } + + + /** + * Encodes the specified string and appends it to the given StringBuilder. + */ + private static void escapeStringOnto(String s, StringBuilder sb) { + int pos = 0, n = s.length(); + for (int i = 0; i < n; ++i) { + char ch = s.charAt(i); + switch (ch) { + case '\r': case '\n': case '"': case '\\': + // these two characters are the exceptions to the general rule + // that JSON is a syntactic subset of JavaScript + // From JSON's perspective they are considered to be whitespaces, + // while ES5 specifies them as line terminators. + case '\u2028': case '\u2029': + String hex = Integer.toString(ch, 16); + sb.append(s, pos, i) + .append("\\u").append("0000", hex.length(), 4).append(hex); + pos = i + 1; + break; + } + } + sb.append(s, pos, n); + } + + /** + * Prints a JsonML tree in a human readable format. + */ + public String toStringTree() { + try { + StringBuilder s = new StringBuilder(); + toStringTreeHelper(this, 0, s); + return s.toString(); + } catch (IOException e) { + throw new RuntimeException("Should not happen\n" + e); + } + } + + private static void toStringTreeHelper(JsonML element, int level, + StringBuilder sb) throws IOException { + for (int i = 0; i < level; ++i) { + sb.append(" "); + } + element.toString(sb, true, false); + sb.append("\n"); + for (JsonML child : element.getChildren()) { + toStringTreeHelper(child, level + 1, sb); + } + } +} + diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/JsonMLAst.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/JsonMLAst.java new file mode 100644 index 0000000..b62a893 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/JsonMLAst.java @@ -0,0 +1,182 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.jsonml; + +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.AbstractCompiler; +import com.google.javascript.jscomp.AstValidator; +import com.google.javascript.jscomp.SourceAst; +import com.google.javascript.jscomp.SourceFile; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.Node; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * Generates an AST from a JsonML source file. + * + * JsonML format for representation of JavaScript is specified + * here. + * + * @author dhans@google.com (Daniel Hans) + * + */ +public class JsonMLAst implements SourceAst { + private static final long serialVersionUID = 1L; + private static final String DEFAULT_SOURCE_NAME = "[[jsonmlsource]]"; + + /* + * Root element of JavaScript source which is represented by a JsonML tree. + * See JsonML class for more details. + */ + private JsonML jsonml; + + /* + * Root node of internal JS Compiler AST which represents the same source. + * In order to get the tree, getAstRoot() has to be called. + */ + private Node root; + + private final SourceFile sourceFile; + private final InputId inputId; + + public JsonMLAst(JsonML jsonml) { + this.jsonml = jsonml; + this.inputId = new InputId(getSourceName()); + this.sourceFile = new SourceFile(getSourceName()); + } + + @Override + public void clearAst() { + root = null; + } + + /** + * Generates AST based on AST representation + * @see com.google.javascript.jscomp.SourceAst#getAstRoot(AbstractCompiler) + */ + @Override + public Node getAstRoot(AbstractCompiler compiler) { + if (root == null) { + createAst(compiler); + } + return root; + } + + @Override + public SourceFile getSourceFile() { + return null; + } + + @Override + public void setSourceFile(SourceFile file) { + throw new UnsupportedOperationException( + "JsonMLAst cannot be associated with a SourceFile instance."); + } + + public String getSourceName() { + Object obj = jsonml.getAttribute(TagAttr.SOURCE); + if (obj instanceof String) { + return (String) obj; + } else { + return DEFAULT_SOURCE_NAME; + } + } + + private void createAst(AbstractCompiler compiler) { + Reader translator = new Reader(); + translator.setRootElement(jsonml); + try { + root = translator.parse(compiler); + root.setInputId(inputId); + root.setStaticSourceFile(sourceFile); + new AstValidator().validateScript(root); + } catch (JsonMLException e) { + // compiler should already have JSErrors + } + } + + public JsonML convertToJsonML () { + if (root != null) { + Writer converter = new Writer(); + return converter.processAst(root); + } + return null; + } + + /** + * Returns a JsonML element with the specified number from the tree in + * pre-order walk. + * + * @return nth node or null if the node does not exists + */ + public JsonML getElementPreOrder(int n) { + Preconditions.checkState(jsonml != null); + + if (n == 0) { + return jsonml; + } + + Deque stack = + new ArrayDeque(); + stack.push(new WalkHelper(jsonml, 0)); + int i = 0; + while (i <= n && !stack.isEmpty()) { + WalkHelper current = stack.pop(); + JsonML element = current.element; + Integer childno = current.childno; + + // not all the children of this node have been visited + if (childno < element.childrenSize()) { + stack.push(new WalkHelper(element, childno + 1)); + // we visit the next child + i++; + element = element.getChild(childno); + + if (i == n) { + return element; + } + + // put the next child on the stack to preserve pre-order + stack.push(new WalkHelper(element, 0)); + } + } + return null; + } + + /* + * Represents a walk step while the JsonML tree is traversed. + */ + private static class WalkHelper { + // JsonML element that corresponds to this step + final JsonML element; + + // number of children of the element which has already been visited + final int childno; + + WalkHelper(JsonML element, int childno) { + this.element = element; + this.childno = childno; + } + } + + @Override + public InputId getInputId() { + return inputId; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/JsonMLError.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/JsonMLError.java new file mode 100644 index 0000000..2514f21 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/JsonMLError.java @@ -0,0 +1,82 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.jsonml; + +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.DiagnosticType; +import com.google.javascript.jscomp.JSError; + +/** + * Class used to represent errors which correspond to JsonML elements. + * + * @author dhans@google.com (Daniel Hans) + */ +public class JsonMLError { + + /** Description of the error */ + public final String description; + + /** Name of the source */ + public final String sourceName; + + /** Node where the warning occurred. */ + public final JsonML element; + + /** Line number of the source */ + public final int lineNumber; + + /** Level */ + public final ErrorLevel level; + + private JsonMLError(DiagnosticType type, String sourceName, JsonML element, + int lineNumber, ErrorLevel level, String... arguments) { + this.description = type.format.format(arguments); + this.sourceName = sourceName; + this.element = element; + this.lineNumber = lineNumber; + this.level = level; + } + + private JsonMLError(String description, DiagnosticType type, + String sourceName, JsonML element, int lineNumber, ErrorLevel level) { + this.description = description; + this.sourceName = sourceName; + this.element = element; + this.lineNumber = lineNumber; + this.level = level; + } + + public static JsonMLError make(DiagnosticType type, String sourceName, + JsonML element, int lineNumber, ErrorLevel level, String... arguments) { + return new JsonMLError(type, sourceName, element, lineNumber, level, + arguments); + } + + public static JsonMLError make(JSError error, JsonMLAst ast) { + // try to find the corresponding JsonML element + // it is stored as line number of the JSError + int n = error.lineNumber; + JsonML element = ast.getElementPreOrder(n); + + ErrorLevel level = error.getDefaultLevel() == CheckLevel.ERROR + ? ErrorLevel.COMPILATION_ERROR + : ErrorLevel.COMPILATION_WARNING; + + return new JsonMLError(error.getType(), error.sourceName, element, 0, + level, error.description); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/JsonMLException.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/JsonMLException.java new file mode 100644 index 0000000..4abc6b3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/JsonMLException.java @@ -0,0 +1,33 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.jsonml; + +/** + * Class used to report internal exceptions which concern JsonML. + * + * @author dhans@google.com (Daniel Hans) + * + */ +class JsonMLException extends Exception { + private static final long serialVersionUID = 1L; + + JsonMLException() {} + + JsonMLException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/JsonMLUtil.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/JsonMLUtil.java new file mode 100644 index 0000000..519d06a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/JsonMLUtil.java @@ -0,0 +1,320 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.jsonml; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.Iterator; + +/** + * JsonMLUtil contains utilities for the JsonML object. + * + * @author dhans@google.com (Daniel Hans) + */ +public class JsonMLUtil { + + /** + * Checks if the specified JsonML element represents an expression. + */ + public static boolean isExpression(JsonML element) { + switch (element.getType()) { + case ArrayExpr: + case AssignExpr: + case BinaryExpr: + case CallExpr: + case ConditionalExpr: + case CountExpr: + case DeleteExpr: + case EvalExpr: + case FunctionExpr: + case IdExpr: + case InvokeExpr: + case LiteralExpr: + case LogicalAndExpr: + case LogicalOrExpr: + case MemberExpr: + case NewExpr: + case ObjectExpr: + case RegExpExpr: + case ThisExpr: + case TypeofExpr: + case UnaryExpr: + return true; + default: + return false; + } + } + + /** + * Parses JSON string which contains serialized JsonML content. + * @param jsonml string representation of JsonML + * @return root element of a JsonML tree + */ + public static JsonML parseString(String jsonml) throws Exception { + return parseElement(new JSONArray(jsonml)); + } + + private static JsonML parseElement(JSONArray element) + throws Exception { + JsonML jsonMLElement = new JsonML(TagType.valueOf(element.getString(0))); + + // set attributes for the JsonML element + JSONObject attrs = element.getJSONObject(1); + Iterator it = attrs.keys(); + while (it.hasNext()) { + String key = (String) it.next(); + Object value = attrs.get(key); + TagAttr tag = TagAttr.get(key); + + // an unsupported attribute + if (tag == null) { + continue; + } + + if (value instanceof Number) { + value = ((Number) value).doubleValue(); + } + + switch (tag) { + case NAME: + case BODY: + case FLAGS: + case OP: + case TYPE: + case IS_PREFIX: + case LABEL: + jsonMLElement.setAttribute(tag, value); + break; + case VALUE: + // we do not want to deal with JSONObject.NULL + if (value != null && value.equals(null)) { + value = null; + } + + // we want all numbers to be stored as double values + if (value instanceof Number) { + jsonMLElement.setAttribute(tag, ((Number) value).doubleValue()); + } else { + jsonMLElement.setAttribute(tag, value); + } + break; + default: + } + } + + // recursively set children for the JsonML element + for (int i = 2; i < element.length(); ++i) { + jsonMLElement.appendChild(parseElement(element.getJSONArray(i))); + } + + return jsonMLElement; + } + + /** + * Compares two specified JsonML trees. + * + * Two JsonML nodes are considered to be equal when the following conditions + * are met: + * + * - have the same type + * - have the same attributes from the list of attributes to compare + * - have the same number of children + * - nodes in each pair of corresponding children are equal + * + * Two JsonML trees are equal, if their roots are equal. + * + * When two nodes are compared, only the following attributes are taken + * into account: + * TagAttr.BODY, TagAttr.FLAGS, TagAttr.IS_PREFIX, TagAttr.LABEL, + * TagAttr.NAME, TagAttr.OP, TagAttr.TYPE, TagAttr.VALUE + * Generally, the comparator does not care about debugging attributes. + * + * @return + * Returns string describing the inequality in the following format: + * + * The trees are not equal: + * + * Tree1: + * -- string representation of Tree1 + * + * Tree2: + * -- string representation of Tree2 + * + * Subtree1: + * -- string representation of the subtree of the Tree1 which is not + * -- equal to the corresponding subtree of the Tree2 + * + * Subtree2: + * -- see Subtree1 + * + * If the trees are equal, null is returned. + */ + public static String compare(JsonML tree1, JsonML tree2) { + return (new JsonMLComparator(tree1, tree2)).compare(); + } + + /** + * Returns true if the trees are equal, false otherwise. + */ + static boolean compareSilent(JsonML tree1, JsonML tree2) { + return (new JsonMLComparator(tree1, tree2)).compareSilent(); + } + + /** + * Helper class which actually compares two given JsonML trees. + * + */ + private static class JsonMLComparator { + private static final TagAttr[] ATTRS_TO_COMPARE = { + TagAttr.BODY, TagAttr.FLAGS, TagAttr.IS_PREFIX, TagAttr.LABEL, + TagAttr.NAME, TagAttr.OP, TagAttr.TYPE, TagAttr.VALUE + }; + private JsonML treeA; + private JsonML treeB; + private JsonML mismatchA; + private JsonML mismatchB; + + JsonMLComparator(JsonML treeA, JsonML treeB) { + this.treeA = treeA; + this.treeB = treeB; + if (compareElements(treeA, treeB)) { + mismatchA = null; + mismatchB = null; + } + } + + private boolean setMismatch(JsonML a, JsonML b) { + mismatchA = a; + mismatchB = b; + return false; + } + + /** + * Check if two elements are equal (including comparing their children). + */ + private boolean compareElements(JsonML a, JsonML b) { + // the elements are considered to be equal if they are both null + if (a == null || b == null) { + if (a == null && b == null) { + return true; + } else { + return setMismatch(a, b); + } + } + + // the elements themselves have to be equivalent + if (!areEquivalent(a, b)) { + return setMismatch(a, b); + } + + // they both have to have the same number of children + if (a.childrenSize() != b.childrenSize()) { + return setMismatch(a, b); + } + + // all the children has to be the same + Iterator itA = a.getChildren().listIterator(); + Iterator itB = b.getChildren().listIterator(); + while (itA.hasNext()) { + if (!compareElements(itA.next(), itB.next())) { + return false; + } + } + + return true; + } + + /** + * Checks if two elements are semantically the same. + */ + private boolean areEquivalent(JsonML a, JsonML b) { + // both elements must have the same type + if (a.getType() != b.getType()) { + return false; + } + + for (TagAttr attr : ATTRS_TO_COMPARE) { + if (!compareAttribute(attr, a, b)) { + return false; + } + } + return true; + } + + private boolean compareAttribute(TagAttr attr, JsonML a, JsonML b) { + Object valueA = a.getAttributes().get(attr); + Object valueB = b.getAttributes().get(attr); + + // none of the elements have the attribute + if (valueA == null && valueB == null) { + return true; + } + + // only one of the elements has the attribute + if (valueA == null || valueB == null) { + return false; + } + + // check if corresponding values are equal + if (!(valueA.equals(valueB))) { + // there is still a chance that both attributes are numbers, but are + // represented by different classes + + Double doubleA = null, doubleB = null; + + if (valueA instanceof Number) { + doubleA = ((Number) valueA).doubleValue(); + } else if (valueA instanceof String) { + doubleA = Double.valueOf((String) valueA); + } else { + return false; + } + + if (valueB instanceof Number) { + doubleB = ((Number) valueB).doubleValue(); + } else if (valueB instanceof String) { + doubleB = Double.valueOf((String) valueB); + } else { + return false; + } + + if (!doubleA.equals(doubleB)) { + return false; + } + } + + return true; + } + + private boolean compareSilent() { + return mismatchA == null && mismatchB == null; + } + + private String compare() { + if (compareSilent()) { + return null; + } + return "The trees are not equal: " + + "\n\nTree1:\n " + treeA.toStringTree() + + "\n\nTree2:\n " + treeB.toStringTree() + + "\n\nSubtree1:\n " + mismatchA.toStringTree() + + "\n\nSubtree2:\n " + mismatchB.toStringTree(); + + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/NodeUtil.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/NodeUtil.java new file mode 100644 index 0000000..01d87fd --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/NodeUtil.java @@ -0,0 +1,76 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.jsonml; + +import com.google.common.base.Preconditions; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * Non public methods copied from com.google.javascript.jscomp.NodeUtil class. + * + * @author dhans@google.com (Daniel Hans) + */ +class NodeUtil { + + /** + * @return Whether the node represents a FOR-IN loop. + */ + static boolean isForIn(Node n) { + return n.getType() == Token.FOR + && n.getChildCount() == 3; + } + + /** + * @return Whether the node is used as a statement. + */ + static boolean isStatement(Node n) { + Node parent = n.getParent(); + // It is not possible to determine definitely if a node is a statement + // or not if it is not part of the AST. A FUNCTION node can be + // either part of an expression or a statement. + Preconditions.checkState(parent != null); + switch (parent.getType()) { + case Token.SCRIPT: + case Token.BLOCK: + case Token.LABEL: + return true; + default: + return false; + } + } + + /** + * Is this node a function declaration? A function declaration is a function + * that has a name that is added to the current scope (i.e. a function that + * is not part of a expression). + */ + static boolean isFunctionDeclaration(Node n) { + return n.getType() == Token.FUNCTION && isStatement(n); + } + + /** + * Is this node a hoisted function declaration? A function declaration in the + * scope root is hoisted to the top of the scope. + * See {@link #isFunctionDeclaration}). + */ + static boolean isHoistedFunctionDeclaration(Node n) { + return isFunctionDeclaration(n) + && (n.getParent().getType() == Token.SCRIPT + || n.getParent().getParent().getType() == Token.FUNCTION); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/Reader.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/Reader.java new file mode 100644 index 0000000..9b7e760 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/Reader.java @@ -0,0 +1,1565 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.jsonml; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.AbstractCompiler; +import com.google.javascript.jscomp.DiagnosticType; +import com.google.javascript.jscomp.JSError; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Traverse JsonML source tree and generates AST. + * + * @author dhans@google.com (Daniel Hans) + */ +public class Reader { + + static final DiagnosticType JSONML_SYNTAX = DiagnosticType.error( + "JSONML_SYNTAX", "Syntax error: {0}"); + + /** Root element of JsonML tree which contains JavaScript source. */ + private JsonML rootElement; + + /** Name of JavaScript source file */ + private String sourceName; + + /** Error reporter */ + private ErrorReporter errorReporter; + + /** List of ES5 directives supported by JsonML */ + private final Set ALLOWED_DIRECTIVES = Sets.newHashSet("use strict"); + + /** Number of node in JsonML order which is currently processed */ + private int nodeIndex; + + /** + * Inner class which is responsible for passing reader errors + * to the JS compiler. + */ + private class ErrorReporter { + private AbstractCompiler compiler; + + ErrorReporter(AbstractCompiler compiler) { + this.compiler = compiler; + } + + private void report(JsonML element, String...arguments) + throws JsonMLException { + report(JSONML_SYNTAX, element, arguments); + } + + private void report(DiagnosticType type, JsonML element, + String... arguments) throws JsonMLException { + // nodeIndex is the number of the node in which the error occurred + // we will store it in line number + int lineno = nodeIndex; + int charno = -1; + + report(JSError.make(sourceName, lineno, charno, type, arguments)); + } + + /** + * Reports a new parser error to the compiler and terminates the job. + * @param error JSError instance to be passed to the compiler + */ + private void report(JSError error) throws JsonMLException { + report(error, true); + } + + /** + * Reports a new parser error to the compiler and terminates the job + * if the error is fatal. + * @param error JSError instance to be passed to the compiler + * @param terminal if true, parsing is terminated by throwing exception + */ + private void report(JSError error, boolean terminal) + throws JsonMLException { + compiler.report(error); + if (terminal) { + throw new JsonMLException(); + } + } + } + + // TODO(dhans): Maybe this state can be replaced with a simpler check + /** + * Stores state if EXPR_RESULT node should be inserted. The reason why + * we have to keep track on that is JsonML representation does not have this + * information. + */ + private boolean insertExprResultState = true; + + public void setRootElement(JsonML rootElement) { + this.rootElement = rootElement; + } + + /** + * Generates AST for a specified JsonML source file. + * @return root node of the generated AST + * @throws JsonMLException if an error occurs + */ + public Node parse(AbstractCompiler compiler) throws JsonMLException { + if (compiler == null) { + // TODO(dhans): Review error handling + // maybe throw an exception that compiler is required for errors. + return null; + } + + errorReporter = this.new ErrorReporter(compiler); + Node root = IR.block(); + nodeIndex = -1; + + Preconditions.checkState(rootElement.getType() == TagType.Program); + transformElement(rootElement, root); + return root.removeFirstChild(); + } + + /** + * Retrieves value of an attribute, but does not throw an exception if + * the attribute is not present for a specified JsonML element. + * @param type desired type of the attribute + * @return value of the attribute or null if it is not specified + * @throws JsonMLException i.e. when the value has a wrong type + */ + private T getOptionalAttribute(JsonML element, TagAttr attr, + Class type) throws JsonMLException { + return getAttribute(element, attr, type, true); + } + + /** + * Retrieves value of an attribute and throws an exception if + * the attribute is not present for a specified JsonML element. + * @param type desired type of the attribute + * @return value of the attribute + * @throws JsonMLException i.e. when the attribute does not exist + */ + private T getAttribute(JsonML element, TagAttr attr, Class type) + throws JsonMLException { + return getAttribute(element, attr, type, false); + } + + private T getAttribute(JsonML element, TagAttr attr, Class type, + boolean optional) throws JsonMLException { + Object value = element.getAttribute(attr); + + if (value == null) { + if (type == null || optional) { + return null; + } + + throw new JsonMLException( + "Missing " + attr.name() + " attribute for " + + element.getType().name() + " element."); + } + + // Double type is a special case, as it might be represented by all + // Number types or even by certain strings which contain only digit chars + if (type.equals(Double.class)) { + if (value instanceof Number) { + return type.cast(((Number) value).doubleValue()); + } + if (value instanceof String) { + return type.cast(Double.valueOf((String) value)); + } + + throw new JsonMLException( + "Wrong type of " + attr.name() + " attribute. " + + "Received: " + value.getClass() + ". Expected: " + type.getName()); + } + + if (type.isInstance(value)) { + return type.cast(value); + } + + throw new JsonMLException( + "Wrong type of " + attr.name() + "attribute. " + + "Received: " + value.getClass() + ". Expected: " + type.getName()); + } + + /** + * Retrieves an attribute whose type should be Object. + */ + private Object getObjectAttribute(JsonML element, TagAttr attr) + throws JsonMLException { + return getAttribute(element, attr, Object.class); + } + + /** + * Retrieves an attribute whose type should be String. + */ + private String getStringAttribute(JsonML element, TagAttr attr) + throws JsonMLException { + return getAttribute(element, attr, String.class); + } + + private void validate(JsonML element) throws JsonMLException { + String errorMessage = Validator.validate(element); + if (errorMessage != null) { + errorReporter.report(element, errorMessage); + } + } + + /** + * Recursively transforms JsonML tree into AST. + * + * @param element JsonML element to transform + * @param parent current parent AST node, i.e. when the element is + * transformed + * to a new AST node, it should be added as a last child to the parent Node. + */ + private void transformElement(JsonML element, Node parent) + throws JsonMLException { + // next element is transformed + nodeIndex++; + + // the element has to be validated + validate(element); + + // determine if EXPR_RESULT should be inserted + if (insertExprResultState && JsonMLUtil.isExpression(element)) { + transformExpr(element, parent); + return; + } + + switch (element.getType()) { + case ArrayExpr: + transformArrayExpr(element, parent); + break; + case AssignExpr: + transformAssignExpr(element, parent); + break; + case BinaryExpr: + transformBinaryExpr(element, parent); + break; + case BlockStmt: + transformBlock(element, parent); + break; + case BreakStmt: + transformBreakStmt(element, parent); + break; + case CallExpr: + transformCallExpr(element, parent); + break; + case Case: + transformCase(element, parent); + break; + case CatchClause: + transformCatchClause(element, parent); + break; + case ConditionalExpr: + transformConditionalExpr(element, parent); + break; + case ContinueStmt: + transformContinueStmt(element, parent); + break; + case CountExpr: + transformCountExpr(element, parent); + break; + case DataProp: + transformDataProp(element, parent); + break; + case GetterProp: + transformGetterProp(element, parent); + break; + case SetterProp: + transformSetterProp(element, parent); + break; + case DefaultCase: + transformDefaultCase(element, parent); + break; + case DeleteExpr: + transformDeleteExpr(element, parent); + break; + case DoWhileStmt: + transformDoWhileStmt(element, parent); + break; + case Empty: + transformEmpty(element, parent); + break; + case EmptyStmt: + transformEmptyStmt(element, parent); + break; + case EvalExpr: + transformEvalExpr(element, parent); + break; + case ForInStmt: + transformForInStmt(element, parent); + break; + case ForStmt: + transformForStmt(element, parent); + break; + case FunctionDecl: + transformFunctionDecl(element, parent); + break; + case FunctionExpr: + transformFunctionExpr(element, parent); + break; + case IdExpr: + transformIdExpr(element, parent); + break; + case IdPatt: + transformIdPatt(element, parent); + break; + case IfStmt: + transformIfStmt(element, parent); + break; + case InitPatt: + transformInitPatt(element, parent); + break; + case InvokeExpr: + transformInvokeExpr(element, parent); + break; + case LabelledStmt: + transformLabelledStmt(element, parent); + break; + case LiteralExpr: + transformLiteralExpr(element, parent); + break; + case LogicalAndExpr: + transformLogicalAndExpr(element, parent); + break; + case LogicalOrExpr: + transformLogicalOrExpr(element, parent); + break; + case MemberExpr: + transformMemberExpr(element, parent); + break; + case NewExpr: + transformNewExpr(element, parent); + break; + case ObjectExpr: + transformObjectExpr(element, parent); + break; + case ParamDecl: + transformParamDecl(element, parent); + break; + case Program: + transformProgram(element, parent); + break; + case PrologueDecl: + transformPrologueDecl(element, parent); + break; + case RegExpExpr: + transformRegExpExpr(element, parent); + break; + case ReturnStmt: + transformReturnStmt(element, parent); + break; + case SwitchStmt: + transformSwitchStmt(element, parent); + break; + case ThisExpr: + transformThisExpr(element, parent); + break; + case ThrowStmt: + transformThrowStmt(element, parent); + break; + case TryStmt: + transformTryStmt(element, parent); + break; + case TypeofExpr: + transformTypeofExpr(element, parent); + break; + case UnaryExpr: + transformUnaryExpr(element, parent); + break; + case VarDecl: + transformVarDecl(element, parent); + break; + case WhileStmt: + transformWhileStmt(element, parent); + break; + case WithStmt: + transformWithStmt(element, parent); + break; + } + } + + /* + * Helper functions. + * Usually called by functions which process particular JsonML elements. + */ + + private void transformAllChildren(JsonML element, Node parent, + boolean newState) throws JsonMLException { + transformElements(element.getChildren(), parent, newState); + } + + private void transformAllChildren(JsonML element, Node parent) + throws JsonMLException { + transformElements(element.getChildren(), parent); + } + + private void transformAllChildrenFromIndex(JsonML element, Node parent, + int fromIndex, boolean newState) throws JsonMLException { + transformElements(element.getChildren().subList( + fromIndex, element.childrenSize()), parent, newState); + } + + private void transformElements(List elements, Node parent, + boolean newState) throws JsonMLException { + boolean oldState = insertExprResultState; + insertExprResultState = newState; + transformElements(elements, parent); + insertExprResultState = oldState; + } + + private void transformElements(List elements, Node parent) + throws JsonMLException { + for (JsonML element : elements) { + transformElement(element, parent); + } + } + + /** + * Responsible for inserting EXPR_RESULT nodes. + */ + private boolean transformExpr(JsonML element, Node parent) + throws JsonMLException { + boolean result = false; + if (insertExprResultState) { + Node node = new Node(Token.EXPR_RESULT); + parent.addChildToBack(node); + insertExprResultState = false; + nodeIndex--; // the same node will be transformed again + transformElement(element, node); + insertExprResultState = true; + result = true; + } + return result; + } + + /** + * Generic function responsible for dealing with JsonML elements describing + * for loop (ForStmt and ForInStmt). + */ + private void transformForLoop(JsonML element, Node parent, int childno) + throws JsonMLException { + Preconditions.checkState(insertExprResultState == true); + insertExprResultState = false; + + Node node = createNode(Token.FOR, element); + parent.addChildToBack(node); + + JsonML child; + for (int i = 0; i < childno; ++i) { + child = element.getChild(i); + if (child.getType() == TagType.EmptyStmt || + child.getType() == TagType.Empty) { + nodeIndex++; + node.addChildToBack(IR.empty()); + } else { + transformElement(child, node); + } + } + + transformPotentiallyUnwrappedBlock(element.getChild(childno), node); + insertExprResultState = true; + } + + /** + * Generic function responsible for dealing with the following JsonML + * elements: BreakStmt and ContinueStmt. + */ + private void transformJumpStmt(JsonML element, Node parent, int type) + throws JsonMLException { + Node node = createNode(type, element); + parent.addChildToBack(node); + + String label = getOptionalAttribute(element, TagAttr.LABEL, String.class); + if (label != null) { + node.addChildToBack(IR.labelName(label)); + } + } + + /** + * Generic function responsible for dealing with JsonML elements describing + * logical two arguments expressions: LogicalAndExpr and LogicalOrExpr. + */ + private void transformLogicalExpr(JsonML element, Node parent, int type) + throws JsonMLException { + transformTwoArgumentExpr(element, parent, type); + } + + /** + * Generic function responsible for dealing with all kind of expressions + * which are passed exactly two arguments. + */ + private void transformTwoArgumentExpr(JsonML element, Node parent, + int type) throws JsonMLException { + Node node = createNode(type, element); + parent.addChildToBack(node); + transformAllChildren(element, node); + } + + /** + * Transforms an element which should be transformed into a BLOCK node, but + * may not be represented by BlockStmt. In this case, additional BLOCK node + * is created. + */ + private void transformPotentiallyUnwrappedBlock(JsonML element, Node parent) + throws JsonMLException { + + // in theory it should be always EmptyStmt, but due to possible + // compatibility issues Empty element is allowed as well + if (element.getType() == TagType.EmptyStmt || + element.getType() == TagType.Empty) { + nodeIndex++; + // Empty elements are only replaced by BLOCK node + Node block = IR.block(); + parent.addChildToBack(block); + block.putBooleanProp(Node.EMPTY_BLOCK, true); + } else if (element.getType() != TagType.BlockStmt) { + Node block = IR.block(); + parent.addChildToBack(block); + boolean state = insertExprResultState; + insertExprResultState = true; + transformElement(element, block); + insertExprResultState = state; + } else { + nodeIndex++; + transformBlock(element, parent); + } + } + + /* + * Main functions. + * Functions responsible for handling particular JsonML elements. Depending + * on type, transformElement function dispatches actual work to + * the corresponding function below. + */ + + private void transformArrayExpr(JsonML element, Node parent) + throws JsonMLException { + + Node node = createNode(Token.ARRAYLIT, element); + parent.addChildToBack(node); + + // iterate through all the children and look for empty elements + for (JsonML child : element.getChildren()) { + transformElement(child, node); + } + } + + private void transformAssignExpr(JsonML element, Node parent) + throws JsonMLException { + String op = getStringAttribute(element, TagAttr.OP); + int type = Operator.getNodeTypeForAssignOp(op); + transformTwoArgumentExpr(element, parent, type); + } + + private void transformBinaryExpr(JsonML element, Node parent) + throws JsonMLException { + String op = getStringAttribute(element, TagAttr.OP); + int type = Operator.getNodeTypeForBinaryOp(op); + transformTwoArgumentExpr(element, parent, type); + } + + private void transformBlock(JsonML element, Node parent) + throws JsonMLException { + transformBlock(element, parent, 0, element.childrenSize()); + } + + private void transformBlock(JsonML element, Node parent, int start) + throws JsonMLException { + transformBlock(element, parent, start, element.childrenSize()); + } + + private void transformBlock(JsonML element, Node parent, int start, int end) + throws JsonMLException { + Node node = createNode(Token.BLOCK, element); + parent.addChildToBack(node); + transformElements(element.getChildren(start, end), node, true); + } + + private void transformBreakStmt(JsonML element, Node parent) + throws JsonMLException { + transformJumpStmt(element, parent, Token.BREAK); + } + + private void transformCallExpr(JsonML element, Node parent) + throws JsonMLException { + + Node node = createNode(Token.CALL, element); + parent.addChildToBack(node); + + transformAllChildren(element, node); + + // Keep track of of the "this" context of a call. A call without an + // explicit "this" is a free call. + Node first = node.getFirstChild(); + if (first.getType() != Token.GETPROP && first.getType() != Token.GETELEM) { + node.putBooleanProp(Node.FREE_CALL, true); + } + } + + private void transformCase(JsonML element, Node parent) + throws JsonMLException { + + Node node = createNode(Token.CASE, element); + parent.addChildToBack(node); + + // the first element represents case id + JsonML child = element.getChild(0); + transformElement(child, node); + + // always insert an extra BLOCK node + Node block = IR.block(); + block.setIsSyntheticBlock(true); + node.addChildToBack(block); + + transformAllChildrenFromIndex(element, block, 1, true); + } + + private void transformCatchClause(JsonML element, Node parent) + throws JsonMLException { + + Node node = createNode(Token.CATCH, element); + parent.addChildToBack(node); + + JsonML child = element.getChild(0); + transformElement(child, node); + + // the second child represents actual block + child = element.getChild(1); + transformElement(child, node); + } + + private void transformConditionalExpr(JsonML element, Node parent) + throws JsonMLException { + Node node = createNode(Token.HOOK, element); + parent.addChildToBack(node); + + transformAllChildren(element, node); + } + + private void transformContinueStmt(JsonML element, Node parent) + throws JsonMLException { + transformJumpStmt(element, parent, Token.CONTINUE); + } + + /* + * CountExpr are both incrementing and decrementing expressions (++x, --x) + */ + private void transformCountExpr(JsonML element, Node parent) + throws JsonMLException { + String op = getStringAttribute(element, TagAttr.OP); + + int type = Operator.getNodeTypeForCountOp(op); + + Boolean isPrefix = getAttribute(element, TagAttr.IS_PREFIX, Boolean.class); + Node node = createNode(type, element); + node.putIntProp(Node.INCRDECR_PROP, isPrefix ? 0 : 1); + parent.addChildToBack(node); + + transformElement(element.getChild(0), node); + } + + /* + * DataProp is the name for an object property which is initialized + * when the object is created by object literal. + * For example, in {x: 1, y: 2} each property is represented by its own + * DataProp. + */ + private void transformDataProp(JsonML element, Node parent) + throws JsonMLException { + Object name = getObjectAttribute(element, TagAttr.NAME); + + Node node = null; + if (name instanceof Number) { + node = IR.stringKey(getStringValue(((Number) name).doubleValue())); + } else if (name instanceof String) { + node = IR.stringKey((String) name); + } else { + throw new IllegalStateException( + "The name of the property has invalid type."); + } + + setPosition(node); + parent.addChildToBack(node); + + transformElement(element.getChild(0), node); + } + + private static String getStringValue(double value) { + long longValue = (long) value; + + // Return "1" instead of "1.0" + if (longValue == value) { + return Long.toString(longValue); + } else { + return Double.toString(value); + } + } + + /* + * GetterProp is a object literal entry for a getter. + * For example, {get x() {return 1}} + */ + private void transformGetterProp(JsonML element, Node parent) + throws JsonMLException { + transformProp(Token.GETTER_DEF, element, parent); + } + + /* + * GetterProp is a object literal entry for a getter. + * For example, {set x() {return 1}} + */ + private void transformSetterProp(JsonML element, Node parent) + throws JsonMLException { + transformProp(Token.SETTER_DEF, element, parent); + } + + private void transformProp(int tokenType, JsonML element, Node parent) + throws JsonMLException { + Object name = getObjectAttribute(element, TagAttr.NAME); + + Node node = null; + if (name instanceof Number) { + // TODO(johnlenz): convert the number to a quoted string. + throw new IllegalStateException( + "Not yet supported."); + } else if (name instanceof String) { + node = Node.newString(tokenType, (String) name); + } else { + throw new IllegalStateException( + "The name of the property has invalid type."); + } + + setPosition(node); + parent.addChildToBack(node); + + transformElement(element.getChild(0), node); + } + + + private void transformDefaultCase(JsonML element, Node parent) + throws JsonMLException { + Node node = createNode(Token.DEFAULT_CASE, element); + parent.addChildToBack(node); + + // the first child represent body + Node block = IR.block(); + block.setIsSyntheticBlock(true); + node.addChildToBack(block); + + transformAllChildren(element, block, true); + } + + private void transformDeleteExpr(JsonML element, Node parent) + throws JsonMLException { + + Node node = createNode(Token.DELPROP, element); + parent.addChildToBack(node); + + transformElement(element.getChild(0), node); + } + + private void transformDoWhileStmt(JsonML element, Node parent) + throws JsonMLException { + Preconditions.checkState(insertExprResultState == true); + insertExprResultState = false; + + Node node = createNode(Token.DO, element); + parent.addChildToBack(node); + + // the first child represents body + JsonML child = element.getChild(0); + transformPotentiallyUnwrappedBlock(child, node); + + // the second child represents condition + child = element.getChild(1); + transformElement(child, node); + + insertExprResultState = true; + } + + private void transformEmpty(JsonML element, Node parent) { + switch (parent.getType()) { + case Token.ARRAYLIT: + parent.addChildToBack(IR.empty()); + break; + case Token.FUNCTION: + parent.addChildToBack(IR.name("")); + break; + default: + throw new IllegalArgumentException("Unexpected Empty element."); + } + } + + private void transformEmptyStmt(JsonML element, Node parent) { + Preconditions.checkState( + parent.getType() == Token.BLOCK || parent.getType() == Token.SCRIPT); + parent.addChildToBack(IR.empty()); + } + + private void transformEvalExpr(JsonML element, Node parent) + throws JsonMLException { + + Node node = createNode(Token.CALL, element); + node.putBooleanProp(Node.FREE_CALL, true); + parent.addChildToBack(node); + + Node child = IR.name("eval"); + child.putBooleanProp(Node.DIRECT_EVAL, true); + node.addChildToBack(child); + + transformAllChildren(element, node); + } + + private void transformForInStmt(JsonML element, Node parent) + throws JsonMLException { + transformForLoop(element, parent, 2); + } + + private void transformForStmt(JsonML element, Node parent) + throws JsonMLException { + transformForLoop(element, parent, 3); + } + + private void transformFunction(JsonML element, Node parent, + boolean needsName) throws JsonMLException { + Node node = createNode(Token.FUNCTION, element); + parent.addChildToBack(node); + + JsonML child = element.getChild(0); + String name = ""; + + // it be already validated at this point that a non empty name exists + // if it is a function declaration + transformElement(element.getChild(0), node); + + transformElement(element.getChild(1), node); + + // other children represents function body which should be + // wrapped inside a block node + transformBlock(element, node, 2); + } + + private void transformFunctionDecl(JsonML element, Node parent) + throws JsonMLException { + transformFunction(element, parent, true); + } + + private void transformFunctionExpr(JsonML element, Node parent) + throws JsonMLException { + transformFunction(element, parent, false); + } + + private void transformIdExpr(JsonML element, Node parent) + throws JsonMLException { + String name = getStringAttribute(element, TagAttr.NAME); + Node node = IR.name(name); + setPosition(node); + parent.addChildToBack(node); + } + + /* + * InitPatt represents all variable declarations value initialization. + * It has two children: name of the variable and the initial value. + */ + private void transformInitPatt(JsonML element, Node parent) + throws JsonMLException { + JsonML child = element.getChild(0); + nodeIndex++; + Node node = IR.name( + getAttribute(child, TagAttr.NAME, String.class)); + setPosition(node); + parent.addChildToBack(node); + + child = element.getChild(1); + transformElement(child, node); + } + + private void transformIdPatt(JsonML element, Node parent) + throws JsonMLException { + Node node = IR.name( + getStringAttribute(element, TagAttr.NAME)); + setPosition(node); + parent.addChildToBack(node); + } + + private void transformIfStmt(JsonML element, Node parent) + throws JsonMLException { + Preconditions.checkState(insertExprResultState == true); + insertExprResultState = false; + + Node node = createNode(Token.IF, element); + parent.addChildToBack(node); + + // the first child represents condition + JsonML child = element.getChild(0); + transformElement(child, node); + + // the second child is required + child = element.getChild(1); + transformPotentiallyUnwrappedBlock(child, node); + + // the third child represents else part and is not required by AST + child = element.getChild(2); + if (child.getType() != TagType.EmptyStmt && + child.getType() != TagType.Empty) { + transformPotentiallyUnwrappedBlock(child, node); + } else { + nodeIndex++; + } + insertExprResultState = true; + } + + private void transformInvokeExpr(JsonML element, Node parent) + throws JsonMLException { + Node node = createNode(Token.CALL, element); + parent.addChildToBack(node); + + transformMemberExpr(element, node); + + transformElements(element.getChildren(2, element.childrenSize()), node); + } + + private void transformLabelledStmt(JsonML element, Node parent) + throws JsonMLException { + String label = getStringAttribute(element, TagAttr.LABEL); + Node node = createNode(Token.LABEL, element); + node.addChildToBack(IR.labelName(label)); + parent.addChildToBack(node); + + JsonML child = element.getChild(0); + if (child.getType() == TagType.EmptyStmt) { + nodeIndex++; + node.addChildToBack(IR.empty()); + } else { + transformElement(element.getChild(0), node); + } + } + + private void transformLiteralExpr(JsonML element, Node parent) + throws JsonMLException { + + Node node = null; + Type type = Type.get(getStringAttribute(element, TagAttr.TYPE)); + switch (type) { + case BOOLEAN: { + Boolean value = getAttribute(element, TagAttr.VALUE, Boolean.class); + if (value) { + node = IR.trueNode(); + } else { + node = IR.falseNode(); + } + break; + } + + case NULL: { + // needed to throw an exception if value is not null + getAttribute(element, TagAttr.VALUE, null); + node = IR.nullNode(); + break; + } + + case NUMBER: { + Double value = getAttribute(element, TagAttr.VALUE, Double.class); + node = IR.number(value); + break; + } + + case STRING: { + String value = getStringAttribute(element, TagAttr.VALUE); + node = IR.string(value); + break; + } + + default: + throw new JsonMLException("Unrecognized type attribute."); + } + + setPosition(node); + parent.addChildToBack(node); + } + + private void transformLogicalAndExpr(JsonML element, Node parent) + throws JsonMLException { + transformLogicalExpr(element, parent, Token.AND); + } + + private void transformLogicalOrExpr(JsonML element, Node parent) + throws JsonMLException { + transformLogicalExpr(element, parent, Token.OR); + } + + private void transformMemberExpr(JsonML element, Node parent) + throws JsonMLException { + + String op = getAttribute(element, TagAttr.OP, String.class); + int type; + if (op.equals(".")) { + type = Token.GETPROP; + } else if (op.equals("[]")) { + type = Token.GETELEM; + } else { + throw new JsonMLException("Invalid OP argument: " + op); + } + + Node node = createNode(type, element); + parent.addChildToBack(node); + + transformElement(element.getChild(0), node); + transformElement(element.getChild(1), node); + } + + private void transformNewExpr(JsonML element, Node parent) + throws JsonMLException { + Node node = createNode(Token.NEW, element); + parent.addChildToBack(node); + transformAllChildren(element, node); + } + + private void transformObjectExpr(JsonML element, Node parent) + throws JsonMLException { + Node node = createNode(Token.OBJECTLIT, element); + parent.addChildToBack(node); + + transformAllChildren(element, node); + } + + private void transformParamDecl(JsonML element, Node parent) + throws JsonMLException { + // formal arguments should be wrapped by LP node + Node node = createNode(Token.PARAM_LIST, element); + parent.addChildToBack(node); + + transformAllChildren(element, node); + } + + private void transformProgram(JsonML element, Node parent) + throws JsonMLException { + Preconditions.checkNotNull(parent); + insertExprResultState = true; + + Node script = IR.script(); + parent.addChildToBack(script); + + for (JsonML child : element.getChildren()) { + transformElement(child, script); + } + } + + private void transformPrologueDecl(JsonML element, Node parent) + throws JsonMLException { + String directive = getStringAttribute(element, TagAttr.DIRECTIVE); + + if (ALLOWED_DIRECTIVES.contains(directive)) { + Set directives = parent.getDirectives(); + if (directives == null) { + directives = Sets.newHashSet(); + } + directives.add(directive); + parent.setDirectives(directives); + } else { + // for a directive which is not supported, we create a regular node + Node node = IR.exprResult(IR.string(directive)); + parent.addChildToBack(node); + } + } + + private void transformRegExpExpr(JsonML element, Node parent) + throws JsonMLException { + Node node = createNode(Token.REGEXP, element); + parent.addChildToBack(node); + + String body = getStringAttribute(element, TagAttr.BODY); + node.addChildToBack(IR.string(body)); + + String flags = getStringAttribute(element, TagAttr.FLAGS); + if (!(flags.equals(""))) { + node.addChildToBack(IR.string(flags)); + } + } + + private void transformReturnStmt(JsonML element, Node parent) + throws JsonMLException { + Preconditions.checkState(insertExprResultState == true); + Node node = createNode(Token.RETURN, element); + parent.addChildToBack(node); + + if (element.hasChildren()) { + insertExprResultState = false; + transformElement(element.getChild(0), node); + insertExprResultState = true; + } + } + + private void transformSwitchStmt(JsonML element, Node parent) + throws JsonMLException { + Preconditions.checkState(insertExprResultState == true); + insertExprResultState = false; + + Node node = createNode(Token.SWITCH, element); + parent.addChildToBack(node); + + // make sure it has at least one child + // the first child represents switch param + JsonML child = element.getChild(0); + transformElement(child, node); + + // the rest of the children represent cases + for (int i = 1; i < element.childrenSize(); ++i) { + child = element.getChild(i); + // make sure it is case or default + transformElement(child, node); + } + + insertExprResultState = true; + } + + /** + * @throws JsonMLException + */ + private void transformThisExpr(JsonML element, Node parent) + throws JsonMLException { + parent.addChildToBack(createNode(Token.THIS, element)); + } + + private void transformThrowStmt(JsonML element, Node parent) + throws JsonMLException { + Preconditions.checkState(insertExprResultState == true); + Node node = createNode(Token.THROW, element); + parent.addChildToBack(node); + + insertExprResultState = false; + transformElement(element.getChild(0), node); + insertExprResultState = true; + } + + private void transformTryStmt(JsonML element, Node parent) + throws JsonMLException { + Preconditions.checkState(insertExprResultState == true); + Node node = createNode(Token.TRY, element); + parent.addChildToBack(node); + + // the first child represents try body + JsonML child = element.getChild(0); + transformElement(child, node); + + // the second child represents catch + Node block = IR.block(); + node.addChildToBack(block); + child = element.getChild(1); + + if (child.getType() == TagType.CatchClause) { + transformElement(child, block); + } else { + // catch clause is not present, but the element has to be counted + nodeIndex++; + } + + // if the third child is present, it represents finally + if (element.childrenSize() == 3) { + child = element.getChild(2); + transformElement(child, node); + } + } + + private void transformTypeofExpr(JsonML element, Node parent) + throws JsonMLException { + Node node = createNode(Token.TYPEOF, element); + parent.addChildToBack(node); + transformElement(element.getChild(0), node); + } + + private void transformUnaryExpr(JsonML element, Node parent) + throws JsonMLException { + String op = getStringAttribute(element, TagAttr.OP); + int type = Operator.getNodeTypeForUnaryOp(op); + + Node node = createNode(type, element); + parent.addChildToBack(node); + + transformAllChildren(element, node); + } + + private void transformVarDecl(JsonML element, Node parent) + throws JsonMLException { + Node node = createNode(Token.VAR, element); + parent.addChildToBack(node); + + transformAllChildren(element, node, false); + } + + private void transformWhileStmt(JsonML element, Node parent) + throws JsonMLException { + Preconditions.checkState(insertExprResultState == true); + insertExprResultState = false; + + Node node = createNode(Token.WHILE, element); + parent.addChildToBack(node); + + // the first child represents loop condition + JsonML child = element.getChild(0); + transformElement(child, node); + + // the second child represents loop body + child = element.getChild(1); + transformPotentiallyUnwrappedBlock(child, node); + + insertExprResultState = true; + } + + private void transformWithStmt(JsonML element, Node parent) + throws JsonMLException { + Preconditions.checkState(insertExprResultState == true); + insertExprResultState = false; + + Node node = createNode(Token.WITH, element); + parent.addChildToBack(node); + + // the first child represents object + JsonML child = element.getChild(0); + transformElement(child, node); + + // the second child represents body + child = element.getChild(1); + transformPotentiallyUnwrappedBlock(child, node); + + insertExprResultState = true; + } + + /** + * Creates a node which refers to a particular JsonML element. + */ + private Node createNode(int type, JsonML element) { + return new Node(type, nodeIndex, -1); + } + + /** + * Sets position for a node which refers to a particular JsonML element. + * The position says which number (in pre-order) has the corresponding + * JsonML element in the tree. + */ + private void setPosition(Node node) { + node.setLineno(nodeIndex); + } + + /** + * Internal representation for operators which are used by JsonML as + * attributes for various elements. + */ + private enum Operator { + // Assign Operators + ASSIGN("="), + ASSIGN_BITOR("|="), + ASSIGN_BITXOR("^="), + ASSIGN_BITAND("&="), + ASSIGN_LSH("<<="), + ASSIGN_RSH(">>="), + ASSIGN_URSH(">>>="), + ASSIGN_ADD("+="), + ASSIGN_SUB("-="), + ASSIGN_MUL("*="), + ASSIGN_DIV("/="), + ASSIGN_MOD("%="), + + // Binary Operators + BITOR("|"), + BITXOR("^"), + BITAND("&"), + EQ("=="), + NE("!="), + LT("<"), + LE("<="), + GT(">"), + GE(">="), + LSH("<<"), + RSH(">>"), + URSH(">>>"), + ADD("+"), + SUB("-"), + MUL("*"), + DIV("/"), + MOD("%"), + SHEQ("==="), + SHNE("!=="), + COMMA(","), + INSTANCEOF("instanceof"), + IN("in"), + + // Count Operators + DEC("--"), + INC("++"), + + // Unary Operators + NOT("!"), + BITNOT("~"), + POS("+_unary"), // "+" would be a duplicate with ADD + NEG("-_unary"), // "-" would be a duplicate with SUB + VOID("void"); + + private final String name; + private static Map lookup = Maps.newHashMap(); + + // Maps string representation of operators with corresponding enums + static { + for (Operator op : Operator.values()) { + lookup.put(op.getName(), op); + } + } + + private String getName() { + return this.name; + } + + private Operator(String name) { + this.name = name; + } + + private static Operator get(String name) { + return lookup.get(name); + } + + /** + * Returns assign operator associated with a specified name. + */ + private static int getNodeTypeForAssignOp(String name) { + Operator op = get(name); + if (op == null) { + return Token.ERROR; + } + + int type; + switch (op) { + case ASSIGN: + type = Token.ASSIGN; + break; + case ASSIGN_BITOR: + type = Token.ASSIGN_BITOR; + break; + case ASSIGN_BITXOR: + type = Token.ASSIGN_BITXOR; + break; + case ASSIGN_BITAND: + type = Token.ASSIGN_BITAND; + break; + case ASSIGN_LSH: + type = Token.ASSIGN_LSH; + break; + case ASSIGN_RSH: + type = Token.ASSIGN_RSH; + break; + case ASSIGN_URSH: + type = Token.ASSIGN_URSH; + break; + case ASSIGN_ADD: + type = Token.ASSIGN_ADD; + break; + case ASSIGN_SUB: + type = Token.ASSIGN_SUB; + break; + case ASSIGN_MUL: + type = Token.ASSIGN_MUL; + break; + case ASSIGN_DIV: + type = Token.ASSIGN_DIV; + break; + case ASSIGN_MOD: + type = Token.ASSIGN_MOD; + break; + default: + throw new IllegalArgumentException("" + + "Invalid type of assign expression."); + } + return type; + } + + /** + * Returns binary operator associated with a specified name. + */ + private static int getNodeTypeForBinaryOp(String name) { + Operator op = get(name); + + int type; + switch (op) { + case BITOR: + type = Token.BITOR; + break; + case BITXOR: + type = Token.BITXOR; + break; + case BITAND: + type = Token.BITAND; + break; + case EQ: + type = Token.EQ; + break; + case NE: + type = Token.NE; + break; + case LT: + type = Token.LT; + break; + case LE: + type = Token.LE; + break; + case GT: + type = Token.GT; + break; + case GE: + type = Token.GE; + break; + case LSH: + type = Token.LSH; + break; + case RSH: + type = Token.RSH; + break; + case URSH: + type = Token.URSH; + break; + case ADD: + type = Token.ADD; + break; + case SUB: + type = Token.SUB; + break; + case MUL: + type = Token.MUL; + break; + case DIV: + type = Token.DIV; + break; + case MOD: + type = Token.MOD; + break; + case SHEQ: + type = Token.SHEQ; + break; + case SHNE: + type = Token.SHNE; + break; + case COMMA: + type = Token.COMMA; + break; + case INSTANCEOF: + type = Token.INSTANCEOF; + break; + case IN: + type = Token.IN; + break; + default: + throw new IllegalArgumentException("" + + "Invalid type of binary expression."); + } + return type; + } + + /** + * Returns count operator(++, --) associated with a specified name. + */ + private static int getNodeTypeForCountOp(String name) { + Operator op = get(name); + if (op == null) { + return Token.ERROR; + } + + int type; + switch (op) { + case DEC: + type = Token.DEC; + break; + case INC: + type = Token.INC; + break; + default: + throw new IllegalArgumentException("" + + "Invalid type of count expression."); + } + return type; + } + + /** + * Returns assign operator associated with a specified name. + */ + private static int getNodeTypeForUnaryOp(String name) { + String realName = new String(name); + if (name.equals("+") || name.equals("-")) { + realName += "_unary"; + } + Operator op = get(realName); + + int type; + switch (op) { + case NOT: + type = Token.NOT; + break; + case BITNOT: + type = Token.BITNOT; + break; + case POS: + type = Token.POS; + break; + case NEG: + type = Token.NEG; + break; + case VOID: + type = Token.VOID; + break; + default: + throw new IllegalArgumentException("" + + "Invalid type of unary expression."); + } + return type; + } + } + + /** + * Internal representation of possible types of arguments of JsonML. + */ + private enum Type { + BOOLEAN("boolean"), + NULL("null"), + NUMBER("number"), + STRING("string"); + + private final String name; + private static Map lookup = new HashMap(); + + static { + for (Type type : Type.values()) { + lookup.put(type.getName(), type); + } + } + + private String getName() { + return this.name; + } + + private Type(String name) { + this.name = name; + } + + private static Type get(String name) { + return lookup.get(name); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/SecureCompiler.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/SecureCompiler.java new file mode 100644 index 0000000..26da822 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/SecureCompiler.java @@ -0,0 +1,198 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.jsonml; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.Compiler; +import com.google.javascript.jscomp.CompilerInput; +import com.google.javascript.jscomp.CompilerOptions; +import com.google.javascript.jscomp.CompilerOptions.Reach; +import com.google.javascript.jscomp.JSError; +import com.google.javascript.jscomp.JSModule; +import com.google.javascript.jscomp.Result; +import com.google.javascript.jscomp.SourceFile; +import com.google.javascript.jscomp.VariableRenamingPolicy; + +import java.util.ArrayList; + +/** + * Compilation of JavaScript code which guarantees that all security + * capabilities are preserved after the process. In particular, it can be + * safely applied to cajoled source. + * + * JS Compiler is used for code analysis and optimization. It runs a series + * of passes which try to improve the code. + * + * For safety reasons, only a subset of local passes, which are provided by + * JS Compiler, are processed. Currently it includes: + * - elimination of temporary variables + * + * Using SecureCompiler is quite straightforward. A user just needs to create + * a new instance and call compile() method. Currently the only input which + * is supported is JsonML. + * + * @author dhans@google.com (Daniel Hans) + */ +public class SecureCompiler { + + private static final String COMPILATION_UNCOMPLETED_MSG = + "No compilation has been completed yet."; + + private static final String COMPILATION_UNSUCCESSFUL_MSG = + "The last compilation was not successful."; + + private static final String COMPILATION_ALREADY_COMPLETED_MSG = + "This instance has already compiled one source."; + + private Compiler compiler; + private CompilerOptions options; + private JsonMLAst sourceAst; + + /** Report from the last compilation */ + private Report report; + + public SecureCompiler() { + compiler = new Compiler(); + options = getSecureCompilerOptions(); + } + + /** + * Returns compiled source in JsonML format. + */ + public JsonML getJsonML() { + Preconditions.checkState(report != null, COMPILATION_UNCOMPLETED_MSG); + Preconditions.checkState(report.success, COMPILATION_UNSUCCESSFUL_MSG); + return sourceAst.convertToJsonML(); + } + + /** + * Returns compiled source as a JavaScript. + */ + public String getString() { + Preconditions.checkState(report != null, COMPILATION_UNCOMPLETED_MSG); + Preconditions.checkState(report.success, COMPILATION_UNSUCCESSFUL_MSG); + return compiler.toSource(); + } + + /** + * Returns report from the last compilation. + */ + public Report getReport() { + Preconditions.checkState(report != null, COMPILATION_UNCOMPLETED_MSG); + return report; + } + + public void compile(JsonML source) { + if (report != null) { + throw new IllegalStateException(COMPILATION_ALREADY_COMPLETED_MSG); + } + + sourceAst = new JsonMLAst(source); + + CompilerInput input = new CompilerInput( + sourceAst, "[[jsonmlsource]]", false); + + JSModule module = new JSModule("[[jsonmlmodule]]"); + module.add(input); + + Result result = compiler.compileModules( + ImmutableList.of(), + ImmutableList.of(module), + options); + + report = generateReport(result); + } + + /** + * Returns compiler options which are safe for compilation of a cajoled + * module. The set of options is similar to the one which is used by + * CompilationLevel in simple mode. The main difference is that variable + * renaming and closurePass options are turned off. + */ + private CompilerOptions getSecureCompilerOptions() { + CompilerOptions options = new CompilerOptions(); + + options.variableRenaming = VariableRenamingPolicy.OFF; + options.setInlineVariables(Reach.LOCAL_ONLY); + options.inlineLocalFunctions = true; + options.checkGlobalThisLevel = CheckLevel.OFF; + options.coalesceVariableNames = true; + options.deadAssignmentElimination = true; + options.collapseVariableDeclarations = true; + options.convertToDottedProperties = true; + options.labelRenaming = true; + options.removeDeadCode = true; + options.optimizeArgumentsArray = true; + options.removeUnusedVars = false; + options.removeUnusedLocalVars = true; + + return options; + } + + public void enableFoldConstant() { + options.foldConstants = true; + } + + Report generateReport(Result result) { + // a report may be generated only after actual compilation is complete + if (result == null) { + return null; + } + + ArrayList errors = Lists.newArrayList(); + for (JSError error : result.errors) { + errors.add(JsonMLError.make(error, sourceAst)); + } + + ArrayList warnings = Lists.newArrayList(); + for (JSError warning : result.warnings) { + warnings.add(JsonMLError.make(warning, sourceAst)); + } + + return new Report( + errors.toArray(new JsonMLError[0]), + warnings.toArray(new JsonMLError[0])); + } + + public class Report { + private final boolean success; + private final JsonMLError[] errors; + private final JsonMLError[] warnings; + + private Report(JsonMLError[] errors, JsonMLError[] warnings) { + this.success = errors.length == 0; + this.errors = errors; + this.warnings = warnings; + } + + public boolean isSuccessful() { + return success; + } + + public JsonMLError[] getErrors() { + return errors; + } + + public JsonMLError[] getWarnings() { + return warnings; + } + } + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/TagAttr.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/TagAttr.java new file mode 100644 index 0000000..71d2812 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/TagAttr.java @@ -0,0 +1,70 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.jsonml; + +import java.util.HashMap; +import java.util.Map; + +/** + * List of attributes that a JsonML element may have. + * + * @author dhans@google.com (Daniel Hans) + */ +public enum TagAttr { + BODY("body"), + DIRECTIVE("directive"), + END_COLUMN("endColumn"), + END_LINE("endLine"), + FLAGS("flags"), + IS_PREFIX("isPrefix"), + LABEL("label"), + NAME("name"), + OP("op"), + OPAQUE_POSITION("opaque_position"), + SOURCE("source"), + START_COLUMN("startColumn"), + START_LINE("startLine"), + TYPE("type"), + VALUE("value"); + + private final String name; + private static final Map lookup = + new HashMap(); + + static { + for (TagAttr t : TagAttr.values()) { + lookup.put(t.getName(), t); + } + } + + private String getName() { + return name; + } + + private TagAttr(String name) { + this.name = name; + } + + public static TagAttr get(String name) { + return lookup.get(name); + } + + @Override + public String toString() { + return name; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/TagType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/TagType.java new file mode 100644 index 0000000..83d21a0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/TagType.java @@ -0,0 +1,94 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.jsonml; + +/** + * List of types allowed for JsonML elements. + * + * @author dhans@google.com (Daniel Hans) + */ +public enum TagType { + + // *Expr types + ArrayExpr, + AssignExpr, + BinaryExpr, + CallExpr, + ConditionalExpr, + CountExpr, + DeleteExpr, + EvalExpr, + FunctionExpr, + IdExpr, + InvokeExpr, + LiteralExpr, + LogicalAndExpr, + LogicalOrExpr, + MemberExpr, + NewExpr, + ObjectExpr, + RegExpExpr, + ThisExpr, + TypeofExpr, + UnaryExpr, + + // *Stmt types + BlockStmt, + BreakStmt, + ContinueStmt, + DebuggerStmt, + DoWhileStmt, + EmptyStmt, + ForInStmt, + ForStmt, + IfStmt, + LabelledStmt, + ReturnStmt, + SwitchStmt, + ThrowStmt, + TryStmt, + WhileStmt, + WithStmt, + + // *Decl types + FunctionDecl, + ParamDecl, + PrologueDecl, // TODO + VarDecl, + + // *Prop types + DataProp, + GetterProp, + SetterProp, + + // *Patt types + IdPatt, + InitPatt, + + // *Case types + Case, + DefaultCase, + + // CatchClause type + CatchClause, + + // Empty type + Empty, + + // Program type (root) + Program, +} \ No newline at end of file diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/Validator.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/Validator.java new file mode 100644 index 0000000..78aa19a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/Validator.java @@ -0,0 +1,538 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.jsonml; + +import java.util.Arrays; + +/** + * Statically validates JsonML elements. + * + * It is done in constant time: no subtree is traversed, but the element + * is validated based only on its properties. Sometimes, also its children + * are taken into account. + * + * Usually it checks if the specified element has a correct number of children, + * and if all require attributes exist. It does not enforce all restrictions + * which are implied by ES3 or ES5 specification. + * + * @author dhans@google.com (Daniel Hans) + */ +public class Validator { + public static final String MISSING_ARGUMENT = "" + + "No %s attribute specified for %s."; + public static final String NOT_ENOUGH_CHILDREN_FMT = "" + + "Not enough children for %s. Expected: %d. Found: %d."; + public static final String TOO_MANY_CHILDREN_FMT = "" + + "Too many children for %s. Expected: %d. Found: %d."; + public static final String WRONG_CHILD_TYPE_FMT = "" + + "Wrong type of child number %d for %s. Expected: %s. Found: %s."; + + // used to check if a JsonML element represents an expression + public static TagType[] exprTypes = { + TagType.ArrayExpr, TagType.AssignExpr, TagType.BinaryExpr, + TagType.CallExpr, TagType.ConditionalExpr, TagType.CountExpr, + TagType.DeleteExpr, TagType.EvalExpr, TagType.IdExpr, TagType.InvokeExpr, + TagType.LiteralExpr, TagType.LogicalAndExpr, TagType.LogicalOrExpr, + TagType.MemberExpr, TagType.NewExpr, TagType.ObjectExpr, + TagType.RegExpExpr, TagType.ThisExpr, TagType.TypeofExpr, + TagType.UnaryExpr, TagType.FunctionExpr + }; + + private final StringBuilder b; + private boolean error; + + private Validator() { + b = new StringBuilder(); + error = false; + } + + /** + * Validates the specified JsonML element. + * @param element JsonML element to validate + * @return error message if the element could not be + * validated, an empty string otherwise + */ + public static String validate(JsonML element) { + return (new Validator()).doValidate(element); + } + + /** + * Validates the specified JsonML element. + */ + private String doValidate(JsonML element) { + String message; + switch (element.getType()) { + case AssignExpr: + validateAssignExpr(element); + break; + case BinaryExpr: + validateBinaryExpr(element); + break; + case BreakStmt: + case ContinueStmt: + validateJmpStmt(element); + break; + case Case: + validateCase(element); + break; + case CatchClause: + validateCatchClause(element); + break; + case ConditionalExpr: + validateConditionalExpr(element); + break; + case CountExpr: + validateCountExpr(element); + break; + case DataProp: + validateProp(element); + break; + case GetterProp: + validateProp(element); + break; + case SetterProp: + validateProp(element); + break; + case DeleteExpr: + validateDeleteExpr(element); + break; + case DoWhileStmt: + validateDoWhileStmt(element); + break; + case EmptyStmt: + validateEmptyStmt(element); + break; + case ForInStmt: + validateForInStmt(element); + break; + case ForStmt: + validateForStmt(element); + break; + case FunctionDecl: + validateFunctionDecl(element); + break; + case FunctionExpr: + validateFunctionExpr(element); + break; + case IdExpr: + validateIdExpr(element); + break; + case IdPatt: + validateIdPatt(element); + break; + case IfStmt: + validateIfStmt(element); + break; + case InvokeExpr: + validateInvokeExpr(element); + break; + case LabelledStmt: + validateLabelledStmt(element); + break; + case LiteralExpr: + validateLiteralExpr(element); + break; + case LogicalAndExpr: + case LogicalOrExpr: + validateLogicalExpr(element); + break; + case MemberExpr: + validateMemberExpr(element); + break; + case NewExpr: + validateNewExpr(element); + break; + case ObjectExpr: + validateObjectExpr(element); + break; + case ParamDecl: + validateParamDecl(element); + break; + case RegExpExpr: + validateRegExpExpr(element); + break; + case ReturnStmt: + validateReturnStmt(element); + break; + case SwitchStmt: + validateSwitchStmt(element); + break; + case ThisExpr: + validateThisExpr(element); + break; + case ThrowStmt: + validateThrowStmt(element); + break; + case TryStmt: + validateTryStmt(element); + break; + case TypeofExpr: + validateTypeofExpr(element); + break; + case UnaryExpr: + validateUnaryExpr(element); + break; + case VarDecl: + validateVarDecl(element); + break; + case WhileStmt: + validateWhileStmt(element); + break; + case WithStmt: + validateWithStmt(element); + break; + } + return b.length() != 0 ? b.toString() : null; + } + + private void validateAssignExpr(JsonML element) { + validateChildrenSize(element, 2); + validateArgument(element, TagAttr.OP); + } + + private void validateBinaryExpr(JsonML element) { + validateChildrenSize(element, 2); + validateArgument(element, TagAttr.OP); + } + + private void validateCase(JsonML element) { + validateMinChildrenSize(element, 1); + if (!error) { + validateIsChildExpression(element, 0); + } + } + + private void validateCatchClause(JsonML element) { + validateChildrenSize(element, 2); + if (!error) { + validateChildType(element, TagType.IdPatt , 0); + validateChildType(element, TagType.BlockStmt, 1); + } + } + + private void validateConditionalExpr(JsonML element) { + validateChildrenSize(element, 3); + } + + private void validateCountExpr(JsonML element) { + validateChildrenSize(element, 1); + validateArgument(element, TagAttr.IS_PREFIX); + validateArgument(element, TagAttr.OP); + } + + private void validateProp(JsonML element) { + validateChildrenSize(element, 1); + if (!error) { + validateArgument(element, TagAttr.NAME); + } + } + + private void validateDeleteExpr(JsonML element) { + validateChildrenSize(element, 1); + // TODO(dhans): maybe add that it has to be expression + } + + private void validateDoWhileStmt(JsonML element) { + validateChildrenSize(element, 2); + // TODO(dhans): maybe add that the second child has to be an exception + } + + private void validateEmptyStmt(JsonML element) { + validateChildrenSize(element, 0); + } + + private void validateForInStmt(JsonML element) { + validateChildrenSize(element, 3); + } + + private void validateForStmt(JsonML element) { + validateChildrenSize(element, 4); + } + + private void validateFunctionDecl(JsonML element) { + validateFunction(element, true); + } + + private void validateFunctionExpr(JsonML element) { + validateFunction(element, false); + } + + private void validateIdExpr(JsonML element) { + validateChildrenSize(element, 0); + if (!error) { + validateArgument(element, TagAttr.NAME); + } + } + + private void validateIdPatt(JsonML element) { + validateChildrenSize(element, 0); + validateArgument(element, TagAttr.NAME); + } + + private void validateIfStmt(JsonML element) { + validateChildrenSize(element, 3); + if (!error) { + // TODO(dhans): check the first child is condition + } + } + + private void validateInvokeExpr(JsonML element) { + validateMinChildrenSize(element, 2); + validateArgument(element, TagAttr.OP); + } + + private void validateJmpStmt(JsonML element) { + // for both BreakStmt and ContinueStmt + validateChildrenSize(element, 0); + } + + private void validateLabelledStmt(JsonML element) { + validateChildrenSize(element, 1); + validateArgument(element, TagAttr.LABEL); + } + + private void validateLiteralExpr(JsonML element) { + validateChildrenSize(element, 0); + validateArgument(element, TagAttr.TYPE); + validateArgument(element, TagAttr.VALUE); + } + + private void validateLogicalExpr(JsonML element) { + validateChildrenSize(element, 2); + } + + private void validateMemberExpr(JsonML element) { + validateChildrenSize(element, 2); + validateArgument(element, TagAttr.OP); + } + + private void validateNewExpr(JsonML element) { + validateMinChildrenSize(element, 1); + } + + private void validateObjectExpr(JsonML element) { + TagType[] expected = + {TagType.DataProp, TagType.GetterProp, TagType.SetterProp}; + for (int i = 0; i < element.childrenSize(); ++i) { + validateChildType(element, expected, i); + } + } + + private void validateParamDecl(JsonML element) { + for (int i = 0; i < element.childrenSize(); ++i) { + validateChildType(element, TagType.IdPatt, i); + } + } + + private void validateRegExpExpr(JsonML element) { + validateChildrenSize(element, 0); + validateArgument(element, TagAttr.BODY); + validateArgument(element, TagAttr.FLAGS); + } + + private void validateReturnStmt(JsonML element) { + validateMaxChildrenSize(element, 1); + } + + private void validateSwitchStmt(JsonML element) { + validateMinChildrenSize(element, 1); + boolean defaultStmt = false; + for (int i = 1; i < element.childrenSize(); ++i) { + if (!defaultStmt) { + validateChildType(element, + new TagType[] {TagType.Case, TagType.DefaultCase}, i); + } else { + validateChildType(element, TagType.Case, i); + } + + if (error) { + break; + } + + if (element.getChild(i).getType() == TagType.DefaultCase) { + defaultStmt = true; + } + } + } + + private void validateThisExpr(JsonML element) { + validateChildrenSize(element, 0); + } + + private void validateThrowStmt(JsonML element) { + validateChildrenSize(element, 1); + } + + private void validateTryStmt(JsonML element) { + validateChildrenSize(element, 2, 3); + + if (error) { + return; + } + + validateChildType(element, TagType.BlockStmt, 0); + + TagType[] types = new TagType[] { TagType.CatchClause, TagType.Empty }; + validateChildType(element, types, 1); + + if (element.childrenSize() > 2) { + validateChildType(element, TagType.BlockStmt, 2); + } + } + + private void validateFunction(JsonML element, boolean needsName) { + validateMinChildrenSize(element, 2); + + if (error) { + return; + } + + if (needsName) { + validateChildType( + element, new TagType[] { TagType.IdPatt }, 0); + } else { + validateChildType( + element, new TagType[] { TagType.IdPatt, TagType.Empty }, 0); + } + + validateChildType(element, TagType.ParamDecl, 1); + } + + private void validateTypeofExpr(JsonML element) { + validateChildrenSize(element, 1); + } + + private void validateUnaryExpr(JsonML element) { + validateChildrenSize(element, 1); + if (!error) { + validateArgument(element, TagAttr.OP); + } + } + + private void validateVarDecl(JsonML element) { + validateMinChildrenSize(element, 1); + + TagType[] types = new TagType[] { TagType.InitPatt, TagType.IdPatt }; + for (int i = 0; i < element.childrenSize(); ++i) { + validateChildType(element, types, i); + } + } + + private void validateWhileStmt(JsonML element) { + validateChildrenSize(element, 2); + //TODO(dhans): check if the first child is expression + } + + private void validateWithStmt(JsonML element) { + validateChildrenSize(element, 2); + //TODO(dhans): check if the first child is expression + } + + private void validateArgument(JsonML element, TagAttr attr) { + Object value = element.getAttribute(attr); + if (value == null) { + + // there is an exceptional situation when the value can be null + // {'value': null, 'type': 'null'} + String type; + if ((type = (String) element.getAttribute(TagAttr.TYPE)) != null && + type.equals("null")) { + return; + } + + error = true; + appendLine(String.format( + MISSING_ARGUMENT, + attr, element.getType())); + } + } + + private void validateChildrenSize(JsonML element, int expected) { + validateChildrenSize(element, expected, expected); + } + + private void validateChildrenSize(JsonML element, int min, int max) { + validateMinChildrenSize(element, min); + if (!error) { + validateMaxChildrenSize(element, max); + } + } + + private void validateMinChildrenSize(JsonML element, int min) { + int size = element.childrenSize(); + if (size < min) { + appendLine(String.format( + NOT_ENOUGH_CHILDREN_FMT, + element.getType(), min, size)); + error = true; + } + } + + private void validateMaxChildrenSize(JsonML element, int max) { + int size = element.childrenSize(); + if (size > max) { + appendLine(String.format( + TOO_MANY_CHILDREN_FMT, + element.getType().toString(), max, size)); + error = true; + } + } + + private void validateIsChildExpression(JsonML element, int index) { + validateChildType(element, exprTypes, index); + } + + private void validateChildType(JsonML element, TagType expected, + int index) { + TagType[] types = { expected }; + validateChildType(element, types, index); + } + + private void validateChildType(JsonML element, TagType[] expected, + int index) { + TagType type = element.getChild(index).getType(); + if (!Arrays.asList(expected).contains(type)) { + appendLine(String.format( + WRONG_CHILD_TYPE_FMT, + index, element.getType(), printList(expected), type)); + error = true; + } + } + + private void appendLine(String line) { + b.append(String.format("%s", line)); + } + + // public for test purposes only + public static String printList(Object[] list) { + StringBuilder builder = new StringBuilder(""); + if (list.length == 1) { + builder.append(list[0].toString()); + } else if (list.length > 1) { + builder.append('['); + for (int i = 0; i < list.length; ++i) { + builder.append(list[i].toString()); + if (i < list.length - 1) { + builder.append(", "); + } + } + builder.append("]"); + } + return builder.toString(); + } + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/Writer.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/Writer.java new file mode 100644 index 0000000..b357eb9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/Writer.java @@ -0,0 +1,847 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.jsonml; + +import com.google.common.base.Preconditions; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Iterator; +import java.util.Set; + +/** + * Converts internal AST into JsonML tree. + * + * @author dhans@google.com (Daniel Hans) + */ +public class Writer { + + /** + * Creates JsonML tree based on a specified AST. + * @param root AST node + * @return root of a created JsonML tree + */ + public JsonML processAst(Node root) { + Preconditions.checkNotNull(root); + Preconditions.checkArgument( + root.getType() == Token.BLOCK || root.getType() == Token.SCRIPT); + + JsonML rootElement = new JsonML(TagType.BlockStmt); + if (root.getType() == Token.SCRIPT) { + processNode(root, rootElement); + return rootElement.getChild(0); + } else { + Node child = root.getFirstChild(); + while (child != null) { + processNode(child, rootElement); + child = child.getNext(); + } + // TODO(johnlenz): Add support for multiple scripts. + return rootElement.getChild(0); + } + } + + /** + * Dispatches an AST node to a function which converts it to JsonML. + * @param node AST node to convert into JsonML element. + * @param currentParent element to which newly created JsonML element will + * be attached as a child + */ + private void processNode(Node node, JsonML currentParent) { + switch (node.getType()) { + case Token.RETURN: + processReturn(node, currentParent); + break; + case Token.BITOR: + processBinaryExpr(node, currentParent, "|"); + break; + case Token.BITXOR: + processBinaryExpr(node, currentParent, "^"); + break; + case Token.BITAND: + processBinaryExpr(node, currentParent, "&"); + break; + case Token.EQ: + processBinaryExpr(node, currentParent, "=="); + break; + case Token.NE: + processBinaryExpr(node, currentParent, "!="); + break; + case Token.LT: + processBinaryExpr(node, currentParent, "<"); + break; + case Token.LE: + processBinaryExpr(node, currentParent, "<="); + break; + case Token.GT: + processBinaryExpr(node, currentParent, ">"); + break; + case Token.GE: + processBinaryExpr(node, currentParent, ">="); + break; + case Token.LSH: + processBinaryExpr(node, currentParent, "<<"); + break; + case Token.RSH: + processBinaryExpr(node, currentParent, ">>"); + break; + case Token.URSH: + processBinaryExpr(node, currentParent, ">>>"); + break; + case Token.ADD: + processBinaryExpr(node, currentParent, "+"); + break; + case Token.SUB: + processBinaryExpr(node, currentParent, "-"); + break; + case Token.MUL: + processBinaryExpr(node, currentParent, "*"); + break; + case Token.DIV: + processBinaryExpr(node, currentParent, "/"); + break; + case Token.MOD: + processBinaryExpr(node, currentParent, "%"); + break; + case Token.NOT: + processUnaryExpr(node, currentParent, "!"); + break; + case Token.BITNOT: + processUnaryExpr(node, currentParent, "~"); + break; + case Token.POS: + processUnaryExpr(node, currentParent, "+"); + break; + case Token.NEG: + processUnaryExpr(node, currentParent, "-"); + break; + case Token.NEW: + processNew(node, currentParent, TagType.NewExpr); + break; + case Token.DELPROP: + processOneArgExpr(node, currentParent, TagType.DeleteExpr); + break; + case Token.TYPEOF: + processOneArgExpr(node, currentParent, TagType.TypeofExpr); + break; + case Token.GETPROP: + processMemberExpr(node, currentParent, "."); + break; + case Token.GETELEM: + processMemberExpr(node, currentParent, "[]"); + break; + case Token.CALL: + processCall(node, currentParent); + break; + case Token.NAME: + processName(node, currentParent); + break; + case Token.NUMBER: + case Token.STRING: + case Token.NULL: + case Token.FALSE: + case Token.TRUE: + processLiteral(node, currentParent); + break; + case Token.THIS: + processThis(node, currentParent); + break; + case Token.SHEQ: + processBinaryExpr(node, currentParent, "==="); + break; + case Token.SHNE: + processBinaryExpr(node, currentParent, "!=="); + break; + case Token.REGEXP: + processRegExp(node, currentParent); + break; + case Token.THROW: + processThrow(node, currentParent); + break; + case Token.IN: + processBinaryExpr(node, currentParent, "in"); + break; + case Token.INSTANCEOF: + processBinaryExpr(node, currentParent, "instanceof"); + break; + case Token.ARRAYLIT: + processArrayLiteral(node, currentParent); + break; + case Token.OBJECTLIT: + processObjectLiteral(node, currentParent); + break; + case Token.TRY: + processTry(node, currentParent); + break; + case Token.COMMA: + processBinaryExpr(node, currentParent, ","); + break; + case Token.ASSIGN: + processAssignExpr(node, currentParent, "="); + break; + case Token.ASSIGN_BITOR: + processAssignExpr(node, currentParent, "|="); + break; + case Token.ASSIGN_BITXOR: + processAssignExpr(node, currentParent, "^="); + break; + case Token.ASSIGN_BITAND: + processAssignExpr(node, currentParent, "&="); + break; + case Token.ASSIGN_LSH: + processAssignExpr(node, currentParent, "<<="); + break; + case Token.ASSIGN_RSH: + processAssignExpr(node, currentParent, ">>="); + break; + case Token.ASSIGN_URSH: + processAssignExpr(node, currentParent, ">>>="); + break; + case Token.ASSIGN_ADD: + processAssignExpr(node, currentParent, "+="); + break; + case Token.ASSIGN_SUB: + processAssignExpr(node, currentParent, "-="); + break; + case Token.ASSIGN_MUL: + processAssignExpr(node, currentParent, "*="); + break; + case Token.ASSIGN_DIV: + processAssignExpr(node, currentParent, "/="); + break; + case Token.ASSIGN_MOD: + processAssignExpr(node, currentParent, "%="); + break; + case Token.HOOK: + processHook(node, currentParent); + break; + case Token.OR: + processLogicalExpr(node, currentParent, "||"); + break; + case Token.AND: + processLogicalExpr(node, currentParent, "&&"); + break; + case Token.INC: + processIncrDecrExpr(node, currentParent, "++"); + break; + case Token.DEC: + processIncrDecrExpr(node, currentParent, "--"); + break; + case Token.FUNCTION: + processFunction(node, currentParent); + break; + case Token.IF: + processIf(node, currentParent); + break; + case Token.SWITCH: + processSwitch(node, currentParent); + break; + case Token.CASE: + processCase(node, currentParent, TagType.Case); + break; + case Token.DEFAULT_CASE: + processCase(node, currentParent, TagType.DefaultCase); + break; + case Token.WHILE: + processLoop(node, currentParent, TagType.WhileStmt); + break; + case Token.DO: + processLoop(node, currentParent, TagType.DoWhileStmt); + break; + case Token.FOR: + processForLoop(node, currentParent); + break; + case Token.BREAK: + processJmp(node, currentParent, TagType.BreakStmt); + break; + case Token.CONTINUE: + processJmp(node, currentParent, TagType.ContinueStmt); + break; + case Token.VAR: + processVar(node, currentParent); + break; + case Token.WITH: + processWith(node, currentParent); + break; + case Token.CATCH: + processCatch(node, currentParent); + break; + case Token.VOID: + processUnaryExpr(node, currentParent, "void"); + break; + case Token.EMPTY: + processEmpty(node, currentParent); + break; + case Token.BLOCK: + processBlock(node, currentParent); + break; + case Token.LABEL: + processLabel(node, currentParent); + break; + case Token.EXPR_RESULT: + processExprResult(node, currentParent); + break; + case Token.SCRIPT: + processScript(node, currentParent); + break; + } + } + + private void processAssignExpr(Node node, JsonML currentParent, String op) { + processTwoArgExpr(node, currentParent, TagType.AssignExpr, op); + } + + private void processArrayLiteral(Node node, JsonML currentParent) { + JsonML element = new JsonML(TagType.ArrayExpr); + currentParent.appendChild(element); + Iterator it = node.children().iterator(); + while (it.hasNext()) { + processNode(it.next(), element); + } + } + + private void processBinaryExpr(Node node, JsonML currentParent, + String op) { + processTwoArgExpr(node, currentParent, TagType.BinaryExpr, op); + } + + private void processBlock(Node node, JsonML currentParent) { + JsonML element = new JsonML(TagType.BlockStmt); + if (currentParent != null) { + currentParent.appendChild(element); + } + + processDirectives(node, element); + + for (Node child : node.children()) { + processNode(child, element); + } + } + + private void processCall(Node node, JsonML currentParent) { + Iterator it = node.children().iterator(); + Node child = it.next(); + JsonML element; + // the first child may indicate that it is invoke expression + // or a standard function call + switch (child.getType()) { + case Token.GETPROP: // a.x() + case Token.GETELEM: // a[x]() + // we have to process this node here and cannot call processNode(child) + // other children of CALL represent arguments, so we need to have + // access to them while processing InvokeExpr + element = new JsonML(TagType.InvokeExpr); + element.setAttribute( + TagAttr.OP, + child.getType() == Token.GETPROP ? "." : "[]"); + currentParent.appendChild(element); + + // there should be exactly two children + Node grandchild = child.getFirstChild(); + processNode(grandchild, element); + processNode(grandchild.getNext(), element); + + + break; + case Token.NAME: + // Caja treats calls to eval in a special way + if (child.getString().equals("eval")) { + element = new JsonML(TagType.EvalExpr); + } else { + // element representing function name is created + element = new JsonML(TagType.IdExpr); + element.setAttribute(TagAttr.NAME, child.getString()); + // element representing function is created + element = new JsonML(TagType.CallExpr, element); + } + currentParent.appendChild(element); + break; + default: + // it addresses all cases where the first argument evaluates to + // another expression + element = new JsonML(TagType.CallExpr); + currentParent.appendChild(element); + processNode(child, element); + break; + } + + // there may be arguments applied + while (it.hasNext()) { + processNode(it.next(), element); + } + } + + private void processCase(Node node, JsonML currentParent, + TagType type) { + JsonML element = new JsonML(type); + currentParent.appendChild(element); + + Node child = node.getFirstChild(); + // for case, the first child represents its argument + if (type == TagType.Case) { + processNode(child, element); + child = child.getNext(); + } + + // it should be a BLOCK which is required by rhino for compatibility + // the writer skips the node and move on to its children + Preconditions.checkNotNull(child); + Preconditions.checkState(child.getType() == Token.BLOCK); + child = child.getFirstChild(); + while (child != null) { + processNode(child, element); + child = child.getNext(); + } + } + + private void processCatch(Node node, JsonML currentParent) { + JsonML element = new JsonML(TagType.CatchClause); + currentParent.appendChild(element); + + // the first child represents exception's name + Node child = node.getFirstChild(); + JsonML patt = new JsonML(TagType.IdPatt); + patt.setAttribute(TagAttr.NAME, child.getString()); + element.appendChild(patt); + + // the second child represents content + child = child.getNext(); + processNode(child, element); + } + + private void processEmpty(Node node, JsonML currentParent) { + if (currentParent.getType() == TagType.ArrayExpr) { + // Empty expression are only found in Array literals + currentParent.appendChild(new JsonML(TagType.Empty)); + } else { + currentParent.appendChild(new JsonML(TagType.EmptyStmt)); + } + } + + private void processExprResult(Node node, JsonML currentParent) { + // this not interesting to JsonML, so we just need to skip it + processNode(node.getFirstChild(), currentParent); + } + + private void processForLoop(Node node, JsonML currentParent) { + if (NodeUtil.isForIn(node)) { + processLoop(node, currentParent, TagType.ForInStmt); + } else { + processLoop(node, currentParent, TagType.ForStmt); + } + } + + private void processFunction(Node node, JsonML currentParent) { + JsonML element; + if (NodeUtil.isFunctionDeclaration(node)) { + element = new JsonML(TagType.FunctionDecl); + } else { // isFunctionExpresion == true + element = new JsonML(TagType.FunctionExpr); + } + currentParent.appendChild(element); + + // the first child represents function's name + Node child = node.getFirstChild(); + String name = child.getString(); + if (!name.equals("")) { + JsonML nameElement = new JsonML(TagType.IdPatt); + nameElement.setAttribute(TagAttr.NAME, name); + element.appendChild(nameElement); + } else { + element.appendChild(new JsonML(TagType.Empty)); + } + + // the second child is a wrapper for formal parameters + child = child.getNext(); + JsonML params = new JsonML(TagType.ParamDecl); + element.appendChild(params); + Iterator it = child.children().iterator(); + while (it.hasNext()) { + JsonML param = new JsonML(TagType.IdPatt); + Node nameNode = it.next(); + param.setAttribute(TagAttr.NAME, nameNode.getString()); + params.appendChild(param); + } + + // the third child represents function's body + child = child.getNext(); + + // it can contain some directives + processDirectives(child, element); + + it = child.children().iterator(); + while (it.hasNext()) { + processNode(it.next(), element); + } + } + + private void processHook(Node node, JsonML currentParent) { + JsonML element = new JsonML(TagType.ConditionalExpr); + currentParent.appendChild(element); + processChildren(node, element); + } + + private void processIf(Node node, JsonML currentParent) { + JsonML element = new JsonML(TagType.IfStmt); + currentParent.appendChild(element); + Iterator it = node.children().iterator(); + + // there should be at least one child + while (it.hasNext()) { + processNode(it.next(), element); + } + // append EmptyStmt for each missing part + int childCount = node.getChildCount(); + Preconditions.checkState(childCount >= 2); + if (childCount < 3) { // no "else" part for sure + element.appendChild(new JsonML(TagType.EmptyStmt)); + } + } + + private void processIncrDecrExpr(Node node, JsonML currentParent, + String op) { + JsonML element = new JsonML(TagType.CountExpr); + currentParent.appendChild(element); + if (op.equals("++")) { + element.setAttribute(TagAttr.OP, "++"); + } else { // op.equals("--") + element.setAttribute(TagAttr.OP, "--"); + } + + if (node.getIntProp(Node.INCRDECR_PROP) == 1) { + element.setAttribute(TagAttr.IS_PREFIX, false); + } else { // INCRDECR_PROP == 0 + element.setAttribute(TagAttr.IS_PREFIX, true); + } + + // there is exactly one child + processNode(node.getFirstChild(), element); + } + + private void processJmp(Node node, JsonML currentParent, + TagType type) { + JsonML element = new JsonML(type); + currentParent.appendChild(element); + + // optional child may point to a label + Node child = node.getFirstChild(); + if (child != null) { + element.setAttribute(TagAttr.LABEL, child.getString()); + } + } + + private void processLabel(Node node, JsonML currentParent) { + JsonML element = new JsonML(TagType.LabelledStmt); + currentParent.appendChild(element); + + // the first child represents label's name + Node child = node.getFirstChild(); + element.setAttribute(TagAttr.LABEL, child.getString()); + + // the second child represents labeled content + child = child.getNext(); + processNode(child, element); + } + + private void processLiteral(Node node, JsonML currentParent) { + JsonML element = new JsonML(TagType.LiteralExpr); + switch (node.getType()) { + case Token.NUMBER: + element.setAttribute(TagAttr.TYPE, "number"); + element.setAttribute(TagAttr.VALUE, node.getDouble()); + break; + case Token.STRING: + element.setAttribute(TagAttr.TYPE, "string"); + element.setAttribute(TagAttr.VALUE, node.getString()); + break; + case Token.NULL: + element.setAttribute(TagAttr.TYPE, "null"); + element.setAttribute(TagAttr.VALUE, null); + break; + case Token.TRUE: + element.setAttribute(TagAttr.TYPE, "boolean"); + element.setAttribute(TagAttr.VALUE, true); + break; + case Token.FALSE: + element.setAttribute(TagAttr.TYPE, "boolean"); + element.setAttribute(TagAttr.VALUE, false); + break; + default: + throw new IllegalArgumentException("Illegal type of node."); + } + currentParent.appendChild(element); + } + + private void processLogicalExpr(Node node, JsonML currentParent, + String op) { + if (op.equals("||")) { + processTwoArgExpr(node, currentParent, TagType.LogicalOrExpr); + } else if (op.endsWith("&&")) { + processTwoArgExpr(node, currentParent, TagType.LogicalAndExpr); + } else { + throw new IllegalArgumentException("Unsupported value of op argument."); + } + } + + private void processLoop(Node node, JsonML currentParent, + TagType type) { + JsonML element = new JsonML(type); + currentParent.appendChild(element); + processChildren(node, element); + } + + private void processMemberExpr(Node node, JsonML currentParent, + String op) { + JsonML element = new JsonML(TagType.MemberExpr); + element.setAttribute(TagAttr.OP, op); + currentParent.appendChild(element); + + // there should be exactly two children + Node child = node.getFirstChild(); + processNode(child, element); + processNode(child.getNext(), element); + } + + private void processName(Node node, JsonML currentParent) { + Preconditions.checkState(!node.hasChildren()); + + JsonML element = new JsonML(TagType.IdExpr); + element.setAttribute(TagAttr.NAME, node.getString()); + currentParent.appendChild(element); + } + + private void processNew(Node node, JsonML currentParent, TagType type) { + JsonML element = new JsonML(type); + currentParent.appendChild(element); + + processChildren(node, element); + } + + private void processObjectLiteral(Node node, JsonML currentParent) { + JsonML element = new JsonML(TagType.ObjectExpr); + currentParent.appendChild(element); + for (Node key : node.children()) { + Node value = key.getFirstChild(); + JsonML item; + Object name; + switch (key.getType()) { + case Token.STRING_KEY: + item = new JsonML(TagType.DataProp); + name = key.getString(); + break; + case Token.GETTER_DEF: + item = new JsonML(TagType.GetterProp); + name = key.getString(); + break; + case Token.SETTER_DEF: + item = new JsonML(TagType.SetterProp); + name = key.getString(); + break; + default: + throw new IllegalArgumentException("Illegal type of node."); + } + item.setAttribute(TagAttr.NAME, name); + processNode(value, item); + element.appendChild(item); + } + } + + private void processRegExp(Node node, JsonML currentParent) { + JsonML element = new JsonML(TagType.RegExpExpr); + currentParent.appendChild(element); + + // first child represents expression's body + Node child = node.getFirstChild(); + element.setAttribute(TagAttr.BODY, child.getString()); + + // optional second child represents flags + String flags = ""; + child = child.getNext(); + if (child != null) { + flags = child.getString(); + } + element.setAttribute(TagAttr.FLAGS, flags); + } + + private void processSwitch(Node node, JsonML currentParent) { + JsonML element = new JsonML(TagType.SwitchStmt); + currentParent.appendChild(element); + + // the first child represents expression + Node child = node.getFirstChild(); + processNode(child, element); + + // next children represent particular cases + for (Node c = child.getNext(); c != null; c = c.getNext()) { + processNode(c, element); + } + } + + private void processThis(Node node, JsonML currentParent) { + currentParent.appendChild(new JsonML(TagType.ThisExpr)); + } + + private void processThrow(Node node, JsonML currentParent) { + JsonML element = new JsonML(TagType.ThrowStmt); + currentParent.appendChild(element); + + // there is exactly one child + processNode(node.getFirstChild(), element); + } + + private void processTry(Node node, JsonML currentParent) { + JsonML element = new JsonML(TagType.TryStmt); + currentParent.appendChild(element); + + // first child represents actual try block + Node child = node.getFirstChild(); + processNode(child, element); + + // second child (precisely: child of that child) represents catch block + child = child.getNext(); + if (child.hasChildren()) { + processNode(child.getFirstChild(), element); + } else { // catch block is not present + element.appendChild(new JsonML(TagType.Empty)); + } + + //optional third child represents finally block + child = child.getNext(); + if (child != null) { + processNode(child, element); + } + } + + private void processTwoArgExpr(Node node, JsonML currentParent, + TagType type) { + processTwoArgExpr(node, currentParent, type, null); + } + + private void processTwoArgExpr(Node node, JsonML currentParent, + TagType type, String op) { + JsonML element = new JsonML(type); + if (op != null) { + element.setAttribute(TagAttr.OP, op); + } + currentParent.appendChild(element); + + Preconditions.checkState(node.getChildCount() == 2); + Node child = node.getFirstChild(); + processNode(child, element); + processNode(child.getNext(), element); + } + + /** + * Process nodes which JsonML represents by UnaryExpr. + * @param node node to process + * @param op operation for this unary expression - depends on node type + */ + private void processUnaryExpr(Node node, JsonML currentParent, + String op) { + JsonML element = new JsonML(TagType.UnaryExpr); + element.setAttribute(TagAttr.OP, op); + currentParent.appendChild(element); + + processNode(node.getFirstChild(), element); + } + + private void processVar(Node node, JsonML currentParent) { + JsonML element = new JsonML(TagType.VarDecl); + currentParent.appendChild(element); + + //there may be many actual declarations + Iterator it = node.children().iterator(); + while (it.hasNext()) { + Node child = it.next(); // this node represents var's id + // its own child represents initial value + + JsonML id = new JsonML(TagType.IdPatt); + id.setAttribute(TagAttr.NAME, child.getString()); + + if (child.hasChildren()) { + JsonML patt = new JsonML(TagType.InitPatt); + element.appendChild(patt); + patt.appendChild(id); + processNode(child.getFirstChild(), patt); + } else { + element.appendChild(id); + } + } + } + + private void processReturn(Node currentNode, JsonML currentParent) { + JsonML element = new JsonML(TagType.ReturnStmt); + currentParent.appendChild(element); + + // there is exactly one child if return statement is not empty + if (currentNode.hasChildren()) { + processNode(currentNode.getFirstChild(), element); + } + } + + private void processScript(Node node, JsonML currentParent) { + JsonML element = new JsonML(TagType.Program); + currentParent.appendChild(element); + + processDirectives(node, element); + + processChildren(node, element); + } + + private void processWith(Node node, JsonML currentParent) { + JsonML element = new JsonML(TagType.WithStmt); + currentParent.appendChild(element); + + // the first child represent object + Node child = node.getFirstChild(); + processNode(child, element); + + // the second one represents content + child = child.getNext(); + processNode(child, element); + } + + private void processChildren(Node node, JsonML currentParent) { + for (Node child : node.children()) { + processNode(child, currentParent); + } + } + + private void processDirectives(Node node, JsonML currentParent) { + Set directives = node.getDirectives(); + + if (directives == null) { + return; + } + + for (String directive : directives) { + JsonML element = new JsonML(TagType.PrologueDecl); + element.setAttribute(TagAttr.DIRECTIVE, directive); + element.setAttribute(TagAttr.VALUE, directive); + currentParent.appendChild(element); + } + } + + private void processOneArgExpr(Node node, JsonML currentParent, + TagType type) { + JsonML element = new JsonML(type); + currentParent.appendChild(element); + + // there is only one child node + processNode(node.getFirstChild(), element); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/package.html b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/package.html new file mode 100644 index 0000000..bfdb6d2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/jsonml/package.html @@ -0,0 +1,11 @@ + + + + + + + +Provides the classes to support JsonML format and secure compiler. + + + diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/package.html b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/package.html new file mode 100644 index 0000000..07f0245 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/package.html @@ -0,0 +1,11 @@ + + + + + + + +Provides the core compiler and its public API. + + + diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/Annotation.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/Annotation.java new file mode 100644 index 0000000..2ebea02 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/Annotation.java @@ -0,0 +1,134 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.parsing; + +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +/** + * All natively recognized JSDoc annotations. + * @author nicksantos@google.com (Nick Santos) + */ +enum Annotation { + AUTHOR, + CONSISTENTIDGENERATOR, + CONSTANT, + CONSTRUCTOR, + DEFINE, + DEPRECATED, + DESC, + DICT, + ENUM, + EXTENDS, + EXTERNS, + EXPORT, + EXPOSE, + FILE_OVERVIEW, + HIDDEN, + IDGENERATOR, + IMPLEMENTS, + IMPLICIT_CAST, + INHERIT_DOC, + INTERFACE, + JAVA_DISPATCH, + LENDS, + LICENSE, // same as preserve + MEANING, + MODIFIES, + NO_ALIAS, + NO_COMPILE, + NO_SHADOW, + NO_SIDE_EFFECTS, + NO_TYPE_CHECK, + NOT_IMPLEMENTED, + OVERRIDE, + PARAM, + PRESERVE, // same as license + PRESERVE_TRY, + PRIVATE, + PROTECTED, + PUBLIC, + RETURN, + SEE, + STABLEIDGENERATOR, + STRUCT, + SUPPRESS, + TEMPLATE, + THIS, + THROWS, + TYPE, + TYPEDEF, + VERSION; + + static final Map recognizedAnnotations = + new ImmutableMap.Builder(). + put("argument", Annotation.PARAM). + put("author", Annotation.AUTHOR). + put("consistentIdGenerator", Annotation.CONSISTENTIDGENERATOR). + put("const", Annotation.CONSTANT). + put("constant", Annotation.CONSTANT). + put("constructor", Annotation.CONSTRUCTOR). + put("define", Annotation.DEFINE). + put("deprecated", Annotation.DEPRECATED). + put("desc", Annotation.DESC). + put("dict", Annotation.DICT). + put("enum", Annotation.ENUM). + put("export", Annotation.EXPORT). + put("expose", Annotation.EXPOSE). + put("extends", Annotation.EXTENDS). + put("externs", Annotation.EXTERNS). + put("fileoverview", Annotation.FILE_OVERVIEW). + put("final", Annotation.CONSTANT). + put("hidden", Annotation.HIDDEN). + put("idGenerator", Annotation.IDGENERATOR). + put("implements", Annotation.IMPLEMENTS). + put("implicitCast", Annotation.IMPLICIT_CAST). + put("inheritDoc", Annotation.INHERIT_DOC). + put("interface", Annotation.INTERFACE). + put("javadispatch", Annotation.JAVA_DISPATCH). + put("lends", Annotation.LENDS). + put("license", Annotation.LICENSE). + put("meaning", Annotation.MEANING). + put("modifies", Annotation.MODIFIES). + put("noalias", Annotation.NO_ALIAS). + put("nocompile", Annotation.NO_COMPILE). + put("noshadow", Annotation.NO_SHADOW). + put("nosideeffects", Annotation.NO_SIDE_EFFECTS). + put("notypecheck", Annotation.NO_TYPE_CHECK). + put("override", Annotation.OVERRIDE). + put("owner", Annotation.AUTHOR). + put("param", Annotation.PARAM). + put("preserve", Annotation.PRESERVE). + put("preserveTry", Annotation.PRESERVE_TRY). + put("private", Annotation.PRIVATE). + put("protected", Annotation.PROTECTED). + put("public", Annotation.PUBLIC). + put("return", Annotation.RETURN). + put("returns", Annotation.RETURN). + put("see", Annotation.SEE). + put("stableIdGenerator", Annotation.STABLEIDGENERATOR). + put("struct", Annotation.STRUCT). + put("suppress", Annotation.SUPPRESS). + put("template", Annotation.TEMPLATE). + put("this", Annotation.THIS). + put("throws", Annotation.THROWS). + put("type", Annotation.TYPE). + put("typedef", Annotation.TYPEDEF). + put("version", Annotation.VERSION). + build(); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/Config.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/Config.java new file mode 100644 index 0000000..6e85129 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/Config.java @@ -0,0 +1,102 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.parsing; + +import com.google.common.collect.ImmutableMap; + +import java.util.Map; +import java.util.Set; + +/** + * Configuration for the AST factory. Should be shared across AST creation + * for all files of a compilation process. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class Config { + + public enum LanguageMode { + ECMASCRIPT3, + ECMASCRIPT5, + ECMASCRIPT5_STRICT, + } + + /** + * Whether to parse the descriptions of JsDoc comments. + */ + final boolean parseJsDocDocumentation; + + /** + * Whether we're in IDE mode. + */ + final boolean isIdeMode; + + /** + * Recognized JSDoc annotations, mapped from their name to their internal + * representation. + */ + final Map annotationNames; + + /** + * Recognized names in a {@code @suppress} tag. + */ + final Set suppressionNames; + + /** + * Accept ECMAScript5 syntax, such as getter/setter. + */ + final LanguageMode languageMode; + + /** + * Accept `const' keyword. + */ + final boolean acceptConstKeyword; + + /** + * Annotation names. + */ + + Config(Set annotationWhitelist, Set suppressionNames, + boolean isIdeMode, LanguageMode languageMode, + boolean acceptConstKeyword) { + this.annotationNames = buildAnnotationNames(annotationWhitelist); + this.parseJsDocDocumentation = isIdeMode; + this.suppressionNames = suppressionNames; + this.isIdeMode = isIdeMode; + this.languageMode = languageMode; + this.acceptConstKeyword = acceptConstKeyword; + } + + /** + * Create the annotation names from the user-specified + * annotation whitelist. + */ + private static Map buildAnnotationNames( + Set annotationWhitelist) { + ImmutableMap.Builder annotationBuilder = + ImmutableMap.builder(); + annotationBuilder.putAll(Annotation.recognizedAnnotations); + for (String unrecognizedAnnotation : annotationWhitelist) { + if (!Annotation.recognizedAnnotations.containsKey( + unrecognizedAnnotation)) { + annotationBuilder.put( + unrecognizedAnnotation, Annotation.NOT_IMPLEMENTED); + } + } + return annotationBuilder.build(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/IRFactory.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/IRFactory.java new file mode 100644 index 0000000..ff3284d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/IRFactory.java @@ -0,0 +1,1449 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.parsing; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.parsing.Config.LanguageMode; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.head.ErrorReporter; +import com.google.javascript.rhino.head.Token.CommentType; +import com.google.javascript.rhino.head.ast.ArrayLiteral; +import com.google.javascript.rhino.head.ast.Assignment; +import com.google.javascript.rhino.head.ast.AstNode; +import com.google.javascript.rhino.head.ast.AstRoot; +import com.google.javascript.rhino.head.ast.Block; +import com.google.javascript.rhino.head.ast.BreakStatement; +import com.google.javascript.rhino.head.ast.CatchClause; +import com.google.javascript.rhino.head.ast.Comment; +import com.google.javascript.rhino.head.ast.ConditionalExpression; +import com.google.javascript.rhino.head.ast.ContinueStatement; +import com.google.javascript.rhino.head.ast.DoLoop; +import com.google.javascript.rhino.head.ast.ElementGet; +import com.google.javascript.rhino.head.ast.EmptyExpression; +import com.google.javascript.rhino.head.ast.EmptyStatement; +import com.google.javascript.rhino.head.ast.ExpressionStatement; +import com.google.javascript.rhino.head.ast.ForInLoop; +import com.google.javascript.rhino.head.ast.ForLoop; +import com.google.javascript.rhino.head.ast.FunctionCall; +import com.google.javascript.rhino.head.ast.FunctionNode; +import com.google.javascript.rhino.head.ast.IfStatement; +import com.google.javascript.rhino.head.ast.InfixExpression; +import com.google.javascript.rhino.head.ast.KeywordLiteral; +import com.google.javascript.rhino.head.ast.Label; +import com.google.javascript.rhino.head.ast.LabeledStatement; +import com.google.javascript.rhino.head.ast.Name; +import com.google.javascript.rhino.head.ast.NewExpression; +import com.google.javascript.rhino.head.ast.NumberLiteral; +import com.google.javascript.rhino.head.ast.ObjectLiteral; +import com.google.javascript.rhino.head.ast.ObjectProperty; +import com.google.javascript.rhino.head.ast.ParenthesizedExpression; +import com.google.javascript.rhino.head.ast.PropertyGet; +import com.google.javascript.rhino.head.ast.RegExpLiteral; +import com.google.javascript.rhino.head.ast.ReturnStatement; +import com.google.javascript.rhino.head.ast.Scope; +import com.google.javascript.rhino.head.ast.StringLiteral; +import com.google.javascript.rhino.head.ast.SwitchCase; +import com.google.javascript.rhino.head.ast.SwitchStatement; +import com.google.javascript.rhino.head.ast.ThrowStatement; +import com.google.javascript.rhino.head.ast.TryStatement; +import com.google.javascript.rhino.head.ast.UnaryExpression; +import com.google.javascript.rhino.head.ast.VariableDeclaration; +import com.google.javascript.rhino.head.ast.VariableInitializer; +import com.google.javascript.rhino.head.ast.WhileLoop; +import com.google.javascript.rhino.head.ast.WithStatement; +import com.google.javascript.rhino.jstype.StaticSourceFile; + +import java.util.Set; + +/** + * IRFactory transforms the new AST to the old AST. + * + */ +class IRFactory { + + static final String GETTER_ERROR_MESSAGE = + "getters are not supported in older versions of JS. " + + "If you are targeting newer versions of JS, " + + "set the appropriate language_in option."; + + static final String SETTER_ERROR_MESSAGE = + "setters are not supported in older versions of JS. " + + "If you are targeting newer versions of JS, " + + "set the appropriate language_in option."; + + static final String SUSPICIOUS_COMMENT_WARNING = + "Non-JSDoc comment has annotations. " + + "Did you mean to start it with '/**'?"; + + static final String MISPLACED_TYPE_ANNOTATION = + "Type annotations are not allowed here. Are you missing parentheses?"; + + private final String sourceString; + private final StaticSourceFile sourceFile; + private final String sourceName; + private final Config config; + private final ErrorReporter errorReporter; + private final TransformDispatcher transformDispatcher; + + private static final ImmutableSet ALLOWED_DIRECTIVES = + ImmutableSet.of("use strict"); + + private static final ImmutableSet ES5_RESERVED_KEYWORDS = + ImmutableSet.of( + // From Section 7.6.1.2 + "class", "const", "enum", "export", "extends", "import", "super"); + private static final ImmutableSet ES5_STRICT_RESERVED_KEYWORDS = + ImmutableSet.of( + // From Section 7.6.1.2 + "class", "const", "enum", "export", "extends", "import", "super", + "implements", "interface", "let", "package", "private", "protected", + "public", "static", "yield"); + + private final Set reservedKeywords; + private final Set parsedComments = Sets.newHashSet(); + + // @license text gets appended onto the fileLevelJsDocBuilder as found, + // and stored in JSDocInfo for placeholder node. + Node rootNodeJsDocHolder = new Node(Token.SCRIPT); + Node.FileLevelJsDocBuilder fileLevelJsDocBuilder = + rootNodeJsDocHolder.getJsDocBuilderForNode(); + JSDocInfo fileOverviewInfo = null; + + // Use a template node for properties set on all nodes to minimize the + // memory footprint associated with these. + private Node templateNode; + + // TODO(johnlenz): Consider creating a template pool for ORIGINALNAME_PROP. + + private IRFactory(String sourceString, + StaticSourceFile sourceFile, + Config config, + ErrorReporter errorReporter) { + this.sourceString = sourceString; + this.sourceFile = sourceFile; + + // Sometimes this will be null in tests. + this.sourceName = sourceFile == null ? null : sourceFile.getName(); + + this.config = config; + this.errorReporter = errorReporter; + this.transformDispatcher = new TransformDispatcher(); + // The template node properties are applied to all nodes in this transform. + this.templateNode = createTemplateNode(); + + switch (config.languageMode) { + case ECMASCRIPT3: + // Reserved words are handled by the Rhino parser. + reservedKeywords = null; + break; + case ECMASCRIPT5: + reservedKeywords = ES5_RESERVED_KEYWORDS; + break; + case ECMASCRIPT5_STRICT: + reservedKeywords = ES5_STRICT_RESERVED_KEYWORDS; + break; + default: + throw new IllegalStateException("unknown language mode"); + } + } + + // Create a template node to use as a source of common attributes, this allows + // the prop structure to be shared among all the node from this source file. + // This reduces the cost of these properties to O(nodes) to O(files). + private Node createTemplateNode() { + // The Node type choice is arbitrary. + Node templateNode = new Node(Token.SCRIPT); + templateNode.setStaticSourceFile(sourceFile); + return templateNode; + } + + public static Node transformTree(AstRoot node, + StaticSourceFile sourceFile, + String sourceString, + Config config, + ErrorReporter errorReporter) { + IRFactory irFactory = new IRFactory(sourceString, sourceFile, + config, errorReporter); + Node irNode = irFactory.transform(node); + + if (node.getComments() != null) { + for (Comment comment : node.getComments()) { + if (comment.getCommentType() == CommentType.JSDOC && + !irFactory.parsedComments.contains(comment)) { + irFactory.handlePossibleFileOverviewJsDoc(comment, irNode); + } else if (comment.getCommentType() == CommentType.BLOCK_COMMENT) { + irFactory.handleBlockComment(comment); + } + } + } + + irFactory.setFileOverviewJsDoc(irNode); + + return irNode; + } + + private void setFileOverviewJsDoc(Node irNode) { + // Only after we've seen all @fileoverview entries, attach the + // last one to the root node, and copy the found license strings + // to that node. + JSDocInfo rootNodeJsDoc = rootNodeJsDocHolder.getJSDocInfo(); + if (rootNodeJsDoc != null) { + irNode.setJSDocInfo(rootNodeJsDoc); + rootNodeJsDoc.setAssociatedNode(irNode); + } + + if (fileOverviewInfo != null) { + if ((irNode.getJSDocInfo() != null) && + (irNode.getJSDocInfo().getLicense() != null)) { + fileOverviewInfo.setLicense(irNode.getJSDocInfo().getLicense()); + } + irNode.setJSDocInfo(fileOverviewInfo); + fileOverviewInfo.setAssociatedNode(irNode); + } + } + + private Node transformBlock(AstNode node) { + Node irNode = transform(node); + if (!irNode.isBlock()) { + if (irNode.isEmpty()) { + irNode.setType(Token.BLOCK); + irNode.setWasEmptyNode(true); + } else { + Node newBlock = newNode(Token.BLOCK, irNode); + newBlock.setLineno(irNode.getLineno()); + newBlock.setCharno(irNode.getCharno()); + maybeSetLengthFrom(newBlock, node); + irNode = newBlock; + } + } + return irNode; + } + + /** + * Check to see if the given block comment looks like it should be JSDoc. + */ + private void handleBlockComment(Comment comment) { + String value = comment.getValue(); + if (value.indexOf("/* @") != -1 || + value.indexOf("\n * @") != -1) { + errorReporter.warning( + SUSPICIOUS_COMMENT_WARNING, + sourceName, + comment.getLineno(), "", 0); + } + } + + /** + * @return true if the jsDocParser represents a fileoverview. + */ + private boolean handlePossibleFileOverviewJsDoc( + JsDocInfoParser jsDocParser) { + if (jsDocParser.getFileOverviewJSDocInfo() != fileOverviewInfo) { + fileOverviewInfo = jsDocParser.getFileOverviewJSDocInfo(); + return true; + } + return false; + } + + private void handlePossibleFileOverviewJsDoc(Comment comment, Node irNode) { + JsDocInfoParser jsDocParser = createJsDocInfoParser(comment, irNode); + parsedComments.add(comment); + handlePossibleFileOverviewJsDoc(jsDocParser); + } + + private JSDocInfo handleJsDoc(AstNode node, Node irNode) { + Comment comment = node.getJsDocNode(); + if (comment != null) { + JsDocInfoParser jsDocParser = createJsDocInfoParser(comment, irNode); + parsedComments.add(comment); + if (!handlePossibleFileOverviewJsDoc(jsDocParser)) { + JSDocInfo info = jsDocParser.retrieveAndResetParsedJSDocInfo(); + if (info != null) { + validateTypeAnnotations(info, node, irNode); + } + return info; + } + } + return null; + } + + private void validateTypeAnnotations( + JSDocInfo info, AstNode node, Node irNode) { + if (info.hasType()) { + boolean valid = false; + switch (node.getType()) { + // Casts are valid + case com.google.javascript.rhino.head.Token.LP: + valid = node instanceof ParenthesizedExpression; + break; + // Variable declarations are valid + case com.google.javascript.rhino.head.Token.VAR: + valid = true; + break; + // Function declarations are valid + case com.google.javascript.rhino.head.Token.FUNCTION: + FunctionNode fnNode = (FunctionNode)node; + valid = fnNode.getFunctionType() == FunctionNode.FUNCTION_STATEMENT; + break; + // Object literal properties and catch declarations are valid. + case com.google.javascript.rhino.head.Token.NAME: + valid = node.getParent() instanceof ObjectProperty + || node.getParent() instanceof CatchClause + || node.getParent() instanceof FunctionNode; + break; + // Object literal properties are valid + case com.google.javascript.rhino.head.Token.GET: + case com.google.javascript.rhino.head.Token.SET: + case com.google.javascript.rhino.head.Token.NUMBER: + case com.google.javascript.rhino.head.Token.STRING: + valid = node.getParent() instanceof ObjectProperty; + break; + + // Property assignments are valid, if at the root of an expression. + case com.google.javascript.rhino.head.Token.ASSIGN: + if (node instanceof Assignment) { + valid = isExprStmt(node.getParent()) + && isPropAccess(((Assignment)node).getLeft()); + } + break; + + // Property definitions are valid, if at the root of an expression. + case com.google.javascript.rhino.head.Token.GETPROP: + case com.google.javascript.rhino.head.Token.GETELEM: + valid = isExprStmt(node.getParent()); + break; + } + if (!valid) { + errorReporter.warning(MISPLACED_TYPE_ANNOTATION, + sourceName, + node.getLineno(), "", 0); + } + } + } + + private boolean isPropAccess(AstNode node) { + return node.getType() == com.google.javascript.rhino.head.Token.GETPROP + || node.getType() == com.google.javascript.rhino.head.Token.GETELEM; + } + + private boolean isExprStmt(AstNode node) { + return node.getType() == com.google.javascript.rhino.head.Token.EXPR_RESULT + || node.getType() == com.google.javascript.rhino.head.Token.EXPR_VOID; + } + + private Node transform(AstNode node) { + Node irNode = justTransform(node); + JSDocInfo jsDocInfo = handleJsDoc(node, irNode); + if (jsDocInfo != null) { + irNode = maybeInjectCastNode(node, jsDocInfo, irNode); + irNode.setJSDocInfo(jsDocInfo); + } + setSourceInfo(irNode, node); + return irNode; + } + + private Node maybeInjectCastNode(AstNode node, JSDocInfo info, Node irNode) { + if (node.getType() == com.google.javascript.rhino.head.Token.LP + && node instanceof ParenthesizedExpression + && info.hasType() + // TODO(johnlenz): for now, attach object literal type directly. + && !irNode.isObjectLit()) { + irNode = newNode(Token.CAST, irNode); + } + return irNode; + } + + private Node transformNameAsString(Name node) { + Node irNode = transformDispatcher.processName(node, true); + JSDocInfo jsDocInfo = handleJsDoc(node, irNode); + if (jsDocInfo != null) { + irNode.setJSDocInfo(jsDocInfo); + } + setSourceInfo(irNode, node); + return irNode; + } + + private Node transformNumberAsString(NumberLiteral literalNode) { + Node irNode = newStringNode(getStringValue(literalNode.getNumber())); + JSDocInfo jsDocInfo = handleJsDoc(literalNode, irNode); + if (jsDocInfo != null) { + irNode.setJSDocInfo(jsDocInfo); + } + setSourceInfo(irNode, literalNode); + return irNode; + } + + private static String getStringValue(double value) { + long longValue = (long) value; + + // Return "1" instead of "1.0" + if (longValue == value) { + return Long.toString(longValue); + } else { + return Double.toString(value); + } + } + + private void setSourceInfo(Node irNode, AstNode node) { + if (irNode.getLineno() == -1) { + // If we didn't already set the line, then set it now. This avoids + // cases like ParenthesizedExpression where we just return a previous + // node, but don't want the new node to get its parent's line number. + int lineno = node.getLineno(); + irNode.setLineno(lineno); + int charno = position2charno(node.getAbsolutePosition()); + irNode.setCharno(charno); + maybeSetLengthFrom(irNode, node); + } + } + + /** + * Creates a JsDocInfoParser and parses the JsDoc string. + * + * Used both for handling individual JSDoc comments and for handling + * file-level JSDoc comments (@fileoverview and @license). + * + * @param node The JsDoc Comment node to parse. + * @param irNode + * @return A JsDocInfoParser. Will contain either fileoverview JsDoc, or + * normal JsDoc, or no JsDoc (if the method parses to the wrong level). + */ + private JsDocInfoParser createJsDocInfoParser(Comment node, Node irNode) { + String comment = node.getValue(); + int lineno = node.getLineno(); + int position = node.getAbsolutePosition(); + + // The JsDocInfoParser expects the comment without the initial '/**'. + int numOpeningChars = 3; + JsDocInfoParser jsdocParser = + new JsDocInfoParser( + new JsDocTokenStream(comment.substring(numOpeningChars), + lineno, + position2charno(position) + numOpeningChars), + node, + irNode, + config, + errorReporter); + jsdocParser.setFileLevelJsDocBuilder(fileLevelJsDocBuilder); + jsdocParser.setFileOverviewJSDocInfo(fileOverviewInfo); + jsdocParser.parse(); + return jsdocParser; + } + + // Set the length on the node if we're in IDE mode. + private void maybeSetLengthFrom(Node node, AstNode source) { + if (config.isIdeMode) { + node.setLength(source.getLength()); + } + } + + private int position2charno(int position) { + int lineIndex = sourceString.lastIndexOf('\n', position); + if (lineIndex == -1) { + return position; + } else { + // Subtract one for initial position being 0. + return position - lineIndex - 1; + } + } + + private Node justTransform(AstNode node) { + return transformDispatcher.process(node); + } + + private class TransformDispatcher extends TypeSafeDispatcher { + private Node processGeneric( + com.google.javascript.rhino.head.Node n) { + Node node = newNode(transformTokenType(n.getType())); + for (com.google.javascript.rhino.head.Node child : n) { + node.addChildToBack(transform((AstNode)child)); + } + return node; + } + + /** + * Transforms the given node and then sets its type to Token.STRING if it + * was Token.NAME. If its type was already Token.STRING, then quotes it. + * Used for properties, as the old AST uses String tokens, while the new one + * uses Name tokens for unquoted strings. For example, in + * var o = {'a' : 1, b: 2}; + * the string 'a' is quoted, while the name b is turned into a string, but + * unquoted. + */ + private Node transformAsString(AstNode n) { + Node ret; + if (n instanceof Name) { + ret = transformNameAsString((Name)n); + } else if (n instanceof NumberLiteral) { + ret = transformNumberAsString((NumberLiteral)n); + ret.putBooleanProp(Node.QUOTED_PROP, true); + } else { + ret = transform(n); + ret.putBooleanProp(Node.QUOTED_PROP, true); + } + Preconditions.checkState(ret.isString()); + return ret; + } + + @Override + Node processArrayLiteral(ArrayLiteral literalNode) { + if (literalNode.isDestructuring()) { + reportDestructuringAssign(literalNode); + } + + Node node = newNode(Token.ARRAYLIT); + for (AstNode child : literalNode.getElements()) { + Node c = transform(child); + node.addChildToBack(c); + } + return node; + } + + @Override + Node processAssignment(Assignment assignmentNode) { + Node assign = processInfixExpression(assignmentNode); + Node target = assign.getFirstChild(); + if (!validAssignmentTarget(target)) { + errorReporter.error( + "invalid assignment target", + sourceName, + target.getLineno(), "", 0); + } + return assign; + } + + @Override + Node processAstRoot(AstRoot rootNode) { + Node node = newNode(Token.SCRIPT); + for (com.google.javascript.rhino.head.Node child : rootNode) { + node.addChildToBack(transform((AstNode)child)); + } + parseDirectives(node); + return node; + } + + /** + * Parse the directives, encode them in the AST, and remove their nodes. + * + * For information on ES5 directives, see section 14.1 of + * ECMA-262, Edition 5. + * + * It would be nice if Rhino would eventually take care of this for + * us, but right now their directive-processing is a one-off. + */ + private void parseDirectives(Node node) { + // Remove all the directives, and encode them in the AST. + Set directives = null; + while (isDirective(node.getFirstChild())) { + String directive = node.removeFirstChild().getFirstChild().getString(); + if (directives == null) { + directives = Sets.newHashSet(directive); + } else { + directives.add(directive); + } + } + + if (directives != null) { + node.setDirectives(directives); + } + } + + private boolean isDirective(Node n) { + if (n == null) return false; + + int nType = n.getType(); + return nType == Token.EXPR_RESULT && + n.getFirstChild().isString() && + ALLOWED_DIRECTIVES.contains(n.getFirstChild().getString()); + } + + @Override + Node processBlock(Block blockNode) { + return processGeneric(blockNode); + } + + @Override + Node processBreakStatement(BreakStatement statementNode) { + Node node = newNode(Token.BREAK); + if (statementNode.getBreakLabel() != null) { + Node labelName = transform(statementNode.getBreakLabel()); + // Change the NAME to LABEL_NAME + labelName.setType(Token.LABEL_NAME); + node.addChildToBack(labelName); + } + return node; + } + + @Override + Node processCatchClause(CatchClause clauseNode) { + AstNode catchVar = clauseNode.getVarName(); + Node node = newNode(Token.CATCH, transform(catchVar)); + if (clauseNode.getCatchCondition() != null) { + errorReporter.error( + "Catch clauses are not supported", + sourceName, + clauseNode.getCatchCondition().getLineno(), "", 0); + } + node.addChildToBack(transformBlock(clauseNode.getBody())); + return node; + } + + @Override + Node processConditionalExpression(ConditionalExpression exprNode) { + return newNode( + Token.HOOK, + transform(exprNode.getTestExpression()), + transform(exprNode.getTrueExpression()), + transform(exprNode.getFalseExpression())); + } + + @Override + Node processContinueStatement(ContinueStatement statementNode) { + Node node = newNode(Token.CONTINUE); + if (statementNode.getLabel() != null) { + Node labelName = transform(statementNode.getLabel()); + // Change the NAME to LABEL_NAME + labelName.setType(Token.LABEL_NAME); + node.addChildToBack(labelName); + } + return node; + } + + @Override + Node processDoLoop(DoLoop loopNode) { + return newNode( + Token.DO, + transformBlock(loopNode.getBody()), + transform(loopNode.getCondition())); + } + + @Override + Node processElementGet(ElementGet getNode) { + return newNode( + Token.GETELEM, + transform(getNode.getTarget()), + transform(getNode.getElement())); + } + + @Override + Node processEmptyExpression(EmptyExpression exprNode) { + Node node = newNode(Token.EMPTY); + return node; + } + + @Override + Node processEmptyStatement(EmptyStatement exprNode) { + Node node = newNode(Token.EMPTY); + return node; + } + + @Override + Node processExpressionStatement(ExpressionStatement statementNode) { + Node node = newNode(transformTokenType(statementNode.getType())); + node.addChildToBack(transform(statementNode.getExpression())); + return node; + } + + @Override + Node processForInLoop(ForInLoop loopNode) { + if (loopNode.isForEach()) { + errorReporter.error( + "unsupported language extension: for each", + sourceName, + loopNode.getLineno(), "", 0); + + // Return the bare minimum to put the AST in a valid state. + return newNode(Token.EXPR_RESULT, Node.newNumber(0)); + } + return newNode( + Token.FOR, + transform(loopNode.getIterator()), + transform(loopNode.getIteratedObject()), + transformBlock(loopNode.getBody())); + } + + @Override + Node processForLoop(ForLoop loopNode) { + Node node = newNode( + Token.FOR, + transform(loopNode.getInitializer()), + transform(loopNode.getCondition()), + transform(loopNode.getIncrement())); + node.addChildToBack(transformBlock(loopNode.getBody())); + return node; + } + + @Override + Node processFunctionCall(FunctionCall callNode) { + Node node = newNode(transformTokenType(callNode.getType()), + transform(callNode.getTarget())); + for (AstNode child : callNode.getArguments()) { + node.addChildToBack(transform(child)); + } + + node.setLineno(node.getFirstChild().getLineno()); + node.setCharno(node.getFirstChild().getCharno()); + maybeSetLengthFrom(node, callNode); + return node; + } + + @Override + Node processFunctionNode(FunctionNode functionNode) { + Name name = functionNode.getFunctionName(); + Boolean isUnnamedFunction = false; + if (name == null) { + int functionType = functionNode.getFunctionType(); + if (functionType != FunctionNode.FUNCTION_EXPRESSION) { + errorReporter.error( + "unnamed function statement", + sourceName, + functionNode.getLineno(), "", 0); + + // Return the bare minimum to put the AST in a valid state. + return newNode(Token.EXPR_RESULT, Node.newNumber(0)); + } + name = new Name(); + name.setIdentifier(""); + isUnnamedFunction = true; + } + Node node = newNode(Token.FUNCTION); + Node newName = transform(name); + if (isUnnamedFunction) { + // Old Rhino tagged the empty name node with the line number of the + // declaration. + newName.setLineno(functionNode.getLineno()); + // TODO(bowdidge) Mark line number of paren correctly. + // Same problem as below - the left paren might not be on the + // same line as the function keyword. + int lpColumn = functionNode.getAbsolutePosition() + + functionNode.getLp(); + newName.setCharno(position2charno(lpColumn)); + maybeSetLengthFrom(newName, name); + } + + node.addChildToBack(newName); + Node lp = newNode(Token.PARAM_LIST); + // The left paren's complicated because it's not represented by an + // AstNode, so there's nothing that has the actual line number that it + // appeared on. We know the paren has to appear on the same line as the + // function name (or else a semicolon will be inserted.) If there's no + // function name, assume the paren was on the same line as the function. + // TODO(bowdidge): Mark line number of paren correctly. + Name fnName = functionNode.getFunctionName(); + if (fnName != null) { + lp.setLineno(fnName.getLineno()); + } else { + lp.setLineno(functionNode.getLineno()); + } + int lparenCharno = functionNode.getLp() + + functionNode.getAbsolutePosition(); + + lp.setCharno(position2charno(lparenCharno)); + for (AstNode param : functionNode.getParams()) { + Node paramNode = transform(param); + // When in ideMode Rhino can generate a param list with only a single + // ErrorNode. This is transformed into an EMPTY node. Drop this node in + // ideMode to keep the AST in a valid state. + if (paramNode.isName()) { + lp.addChildToBack(paramNode); + } else { + // We expect this in ideMode or when there is an error handling + // destructuring parameter assignments which aren't supported + // (an error has already been reported). + Preconditions.checkState( + config.isIdeMode + || paramNode.isObjectLit() + || paramNode.isArrayLit()); + } + } + node.addChildToBack(lp); + + Node bodyNode = transform(functionNode.getBody()); + if (!bodyNode.isBlock()) { + // When in ideMode Rhino tries to parse some constructs the compiler + // doesn't support, repair it here. see Rhino's + // Parser#parseFunctionBodyExpr. + Preconditions.checkState(config.isIdeMode); + bodyNode = IR.block(); + } + parseDirectives(bodyNode); + node.addChildToBack(bodyNode); + return node; + } + + @Override + Node processIfStatement(IfStatement statementNode) { + Node node = newNode(Token.IF); + node.addChildToBack(transform(statementNode.getCondition())); + node.addChildToBack(transformBlock(statementNode.getThenPart())); + if (statementNode.getElsePart() != null) { + node.addChildToBack(transformBlock(statementNode.getElsePart())); + } + return node; + } + + @Override + Node processInfixExpression(InfixExpression exprNode) { + Node n = newNode( + transformTokenType(exprNode.getType()), + transform(exprNode.getLeft()), + transform(exprNode.getRight())); + n.setLineno(exprNode.getLineno()); + n.setCharno(position2charno(exprNode.getAbsolutePosition())); + maybeSetLengthFrom(n, exprNode); + return n; + } + + @Override + Node processKeywordLiteral(KeywordLiteral literalNode) { + return newNode(transformTokenType(literalNode.getType())); + } + + @Override + Node processLabel(Label labelNode) { + return newStringNode(Token.LABEL_NAME, labelNode.getName()); + } + + @Override + Node processLabeledStatement(LabeledStatement statementNode) { + Node node = newNode(Token.LABEL); + Node prev = null; + Node cur = node; + for (Label label : statementNode.getLabels()) { + if (prev != null) { + prev.addChildToBack(cur); + } + cur.addChildToBack(transform(label)); + + cur.setLineno(label.getLineno()); + maybeSetLengthFrom(cur, label); + + int clauseAbsolutePosition = + position2charno(label.getAbsolutePosition()); + cur.setCharno(clauseAbsolutePosition); + + prev = cur; + cur = newNode(Token.LABEL); + } + prev.addChildToBack(transform(statementNode.getStatement())); + return node; + } + + @Override + Node processName(Name nameNode) { + return processName(nameNode, false); + } + + Node processName(Name nameNode, boolean asString) { + if (asString) { + return newStringNode(Token.STRING, nameNode.getIdentifier()); + } else { + if (isReservedKeyword(nameNode.getIdentifier())) { + errorReporter.error( + "identifier is a reserved word", + sourceName, + nameNode.getLineno(), "", 0); + } + return newStringNode(Token.NAME, nameNode.getIdentifier()); + } + } + + /** + * @return Whether the + */ + private boolean isReservedKeyword(String identifier) { + return reservedKeywords != null && reservedKeywords.contains(identifier); + } + + @Override + Node processNewExpression(NewExpression exprNode) { + Node node = newNode( + transformTokenType(exprNode.getType()), + transform(exprNode.getTarget())); + for (AstNode child : exprNode.getArguments()) { + node.addChildToBack(transform(child)); + } + node.setLineno(exprNode.getLineno()); + node.setCharno(position2charno(exprNode.getAbsolutePosition())); + maybeSetLengthFrom(node, exprNode); + return node; + } + + @Override + Node processNumberLiteral(NumberLiteral literalNode) { + return newNumberNode(literalNode.getNumber()); + } + + @Override + Node processObjectLiteral(ObjectLiteral literalNode) { + if (literalNode.isDestructuring()) { + reportDestructuringAssign(literalNode); + } + + Node node = newNode(Token.OBJECTLIT); + for (ObjectProperty el : literalNode.getElements()) { + if (config.languageMode == LanguageMode.ECMASCRIPT3) { + if (el.isGetter()) { + reportGetter(el); + continue; + } else if (el.isSetter()) { + reportSetter(el); + continue; + } + } + + Node key = transformAsString(el.getLeft()); + key.setType(Token.STRING_KEY); + + Node value = transform(el.getRight()); + if (el.isGetter()) { + key.setType(Token.GETTER_DEF); + Preconditions.checkState(value.isFunction()); + if (getFnParamNode(value).hasChildren()) { + reportGetterParam(el.getLeft()); + } + } else if (el.isSetter()) { + key.setType(Token.SETTER_DEF); + Preconditions.checkState(value.isFunction()); + if (!getFnParamNode(value).hasOneChild()) { + reportSetterParam(el.getLeft()); + } + } + key.addChildToFront(value); + node.addChildToBack(key); + } + return node; + } + + /** + * @param fnNode The function. + * @return The Node containing the Function parameters. + */ + Node getFnParamNode(Node fnNode) { + // Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ] + Preconditions.checkArgument(fnNode.isFunction()); + return fnNode.getFirstChild().getNext(); + } + + @Override + Node processObjectProperty(ObjectProperty propertyNode) { + return processInfixExpression(propertyNode); + } + + @Override + Node processParenthesizedExpression(ParenthesizedExpression exprNode) { + Node node = transform(exprNode.getExpression()); + return node; + } + + @Override + Node processPropertyGet(PropertyGet getNode) { + Node leftChild = transform(getNode.getTarget()); + Node newNode = newNode( + Token.GETPROP, leftChild, transformAsString(getNode.getProperty())); + newNode.setLineno(leftChild.getLineno()); + newNode.setCharno(leftChild.getCharno()); + maybeSetLengthFrom(newNode, getNode); + return newNode; + } + + @Override + Node processRegExpLiteral(RegExpLiteral literalNode) { + Node literalStringNode = newStringNode(literalNode.getValue()); + // assume it's on the same line. + literalStringNode.setLineno(literalNode.getLineno()); + maybeSetLengthFrom(literalStringNode, literalNode); + Node node = newNode(Token.REGEXP, literalStringNode); + String flags = literalNode.getFlags(); + if (flags != null && !flags.isEmpty()) { + Node flagsNode = newStringNode(flags); + // Assume the flags are on the same line as the literal node. + flagsNode.setLineno(literalNode.getLineno()); + maybeSetLengthFrom(flagsNode, literalNode); + node.addChildToBack(flagsNode); + } + return node; + } + + @Override + Node processReturnStatement(ReturnStatement statementNode) { + Node node = newNode(Token.RETURN); + if (statementNode.getReturnValue() != null) { + node.addChildToBack(transform(statementNode.getReturnValue())); + } + return node; + } + + @Override + Node processScope(Scope scopeNode) { + return processGeneric(scopeNode); + } + + @Override + Node processStringLiteral(StringLiteral literalNode) { + String value = literalNode.getValue(); + Node n = newStringNode(value); + if (value.indexOf('\u000B') != -1) { + // NOTE(nicksantos): In JavaScript, there are 3 ways to + // represent a vertical tab: \v, \x0B, \u000B. + // The \v notation was added later, and is not understood + // on IE. So we need to preserve it as-is. This is really + // obnoxious, because we do not have a good way to represent + // how the original string was encoded without making the + // representation of strings much more complicated. + // + // To handle this, we look at the original source test, and + // mark the string as \v-encoded or not. If a string is + // \v encoded, then all the vertical tabs in that string + // will be encoded with a \v. + int start = literalNode.getAbsolutePosition(); + int end = start + literalNode.getLength(); + if (start < sourceString.length() && + (sourceString.substring( + start, Math.min(sourceString.length(), end)) + .indexOf("\\v") != -1)) { + n.putBooleanProp(Node.SLASH_V, true); + } + } + return n; + } + + @Override + Node processSwitchCase(SwitchCase caseNode) { + Node node; + if (caseNode.isDefault()) { + node = newNode(Token.DEFAULT_CASE); + } else { + AstNode expr = caseNode.getExpression(); + node = newNode(Token.CASE, transform(expr)); + } + Node block = newNode(Token.BLOCK); + block.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); + block.setLineno(caseNode.getLineno()); + block.setCharno(position2charno(caseNode.getAbsolutePosition())); + maybeSetLengthFrom(block, caseNode); + if (caseNode.getStatements() != null) { + for (AstNode child : caseNode.getStatements()) { + block.addChildToBack(transform(child)); + } + } + node.addChildToBack(block); + return node; + } + + @Override + Node processSwitchStatement(SwitchStatement statementNode) { + Node node = newNode(Token.SWITCH, + transform(statementNode.getExpression())); + for (AstNode child : statementNode.getCases()) { + node.addChildToBack(transform(child)); + } + return node; + } + + @Override + Node processThrowStatement(ThrowStatement statementNode) { + return newNode(Token.THROW, + transform(statementNode.getExpression())); + } + + @Override + Node processTryStatement(TryStatement statementNode) { + Node node = newNode(Token.TRY, + transformBlock(statementNode.getTryBlock())); + Node block = newNode(Token.BLOCK); + boolean lineSet = false; + + for (CatchClause cc : statementNode.getCatchClauses()) { + // Mark the enclosing block at the same line as the first catch + // clause. + if (lineSet == false) { + block.setLineno(cc.getLineno()); + maybeSetLengthFrom(block, cc); + lineSet = true; + } + block.addChildToBack(transform(cc)); + } + node.addChildToBack(block); + + AstNode finallyBlock = statementNode.getFinallyBlock(); + if (finallyBlock != null) { + node.addChildToBack(transformBlock(finallyBlock)); + } + + // If we didn't set the line on the catch clause, then + // we've got an empty catch clause. Set its line to be the same + // as the finally block (to match Old Rhino's behavior.) + if ((lineSet == false) && (finallyBlock != null)) { + block.setLineno(finallyBlock.getLineno()); + maybeSetLengthFrom(block, finallyBlock); + } + + return node; + } + + @Override + Node processUnaryExpression(UnaryExpression exprNode) { + int type = transformTokenType(exprNode.getType()); + Node operand = transform(exprNode.getOperand()); + if (type == Token.NEG && operand.isNumber()) { + operand.setDouble(-operand.getDouble()); + return operand; + } else { + if (type == Token.DELPROP && + !(operand.isGetProp() || + operand.isGetElem() || + operand.isName())) { + String msg = + "Invalid delete operand. Only properties can be deleted."; + errorReporter.error( + msg, + sourceName, + operand.getLineno(), "", 0); + } else if (type == Token.INC || type == Token.DEC) { + if (!validAssignmentTarget(operand)) { + String msg = (type == Token.INC) + ? "invalid increment target" + : "invalid decrement target"; + errorReporter.error( + msg, + sourceName, + operand.getLineno(), "", 0); + } + } + + Node node = newNode(type, operand); + if (exprNode.isPostfix()) { + node.putBooleanProp(Node.INCRDECR_PROP, true); + } + return node; + } + } + + private boolean validAssignmentTarget(Node target) { + switch (target.getType()) { + case Token.NAME: + case Token.GETPROP: + case Token.GETELEM: + return true; + } + return false; + } + + @Override + Node processVariableDeclaration(VariableDeclaration declarationNode) { + if (!config.acceptConstKeyword && declarationNode.getType() == + com.google.javascript.rhino.head.Token.CONST) { + processIllegalToken(declarationNode); + } + + Node node = newNode(Token.VAR); + for (VariableInitializer child : declarationNode.getVariables()) { + node.addChildToBack(transform(child)); + } + return node; + } + + @Override + Node processVariableInitializer(VariableInitializer initializerNode) { + Node node = transform(initializerNode.getTarget()); + if (initializerNode.getInitializer() != null) { + Node initalizer = transform(initializerNode.getInitializer()); + node.addChildToBack(initalizer); + } + return node; + } + + @Override + Node processWhileLoop(WhileLoop loopNode) { + return newNode( + Token.WHILE, + transform(loopNode.getCondition()), + transformBlock(loopNode.getBody())); + } + + @Override + Node processWithStatement(WithStatement statementNode) { + return newNode( + Token.WITH, + transform(statementNode.getExpression()), + transformBlock(statementNode.getStatement())); + } + + @Override + Node processIllegalToken(AstNode node) { + errorReporter.error( + "Unsupported syntax: " + + com.google.javascript.rhino.head.Token.typeToName( + node.getType()), + sourceName, + node.getLineno(), "", 0); + return newNode(Token.EMPTY); + } + + void reportDestructuringAssign(AstNode node) { + errorReporter.error( + "destructuring assignment forbidden", + sourceName, + node.getLineno(), "", 0); + } + + void reportGetter(AstNode node) { + errorReporter.error( + GETTER_ERROR_MESSAGE, + sourceName, + node.getLineno(), "", 0); + } + + void reportSetter(AstNode node) { + errorReporter.error( + SETTER_ERROR_MESSAGE, + sourceName, + node.getLineno(), "", 0); + } + + void reportGetterParam(AstNode node) { + errorReporter.error( + "getters may not have parameters", + sourceName, + node.getLineno(), "", 0); + } + + void reportSetterParam(AstNode node) { + errorReporter.error( + "setters must have exactly one parameter", + sourceName, + node.getLineno(), "", 0); + } + } + + private static int transformTokenType(int token) { + switch (token) { + case com.google.javascript.rhino.head.Token.RETURN: + return Token.RETURN; + case com.google.javascript.rhino.head.Token.BITOR: + return Token.BITOR; + case com.google.javascript.rhino.head.Token.BITXOR: + return Token.BITXOR; + case com.google.javascript.rhino.head.Token.BITAND: + return Token.BITAND; + case com.google.javascript.rhino.head.Token.EQ: + return Token.EQ; + case com.google.javascript.rhino.head.Token.NE: + return Token.NE; + case com.google.javascript.rhino.head.Token.LT: + return Token.LT; + case com.google.javascript.rhino.head.Token.LE: + return Token.LE; + case com.google.javascript.rhino.head.Token.GT: + return Token.GT; + case com.google.javascript.rhino.head.Token.GE: + return Token.GE; + case com.google.javascript.rhino.head.Token.LSH: + return Token.LSH; + case com.google.javascript.rhino.head.Token.RSH: + return Token.RSH; + case com.google.javascript.rhino.head.Token.URSH: + return Token.URSH; + case com.google.javascript.rhino.head.Token.ADD: + return Token.ADD; + case com.google.javascript.rhino.head.Token.SUB: + return Token.SUB; + case com.google.javascript.rhino.head.Token.MUL: + return Token.MUL; + case com.google.javascript.rhino.head.Token.DIV: + return Token.DIV; + case com.google.javascript.rhino.head.Token.MOD: + return Token.MOD; + case com.google.javascript.rhino.head.Token.NOT: + return Token.NOT; + case com.google.javascript.rhino.head.Token.BITNOT: + return Token.BITNOT; + case com.google.javascript.rhino.head.Token.POS: + return Token.POS; + case com.google.javascript.rhino.head.Token.NEG: + return Token.NEG; + case com.google.javascript.rhino.head.Token.NEW: + return Token.NEW; + case com.google.javascript.rhino.head.Token.DELPROP: + return Token.DELPROP; + case com.google.javascript.rhino.head.Token.TYPEOF: + return Token.TYPEOF; + case com.google.javascript.rhino.head.Token.GETPROP: + return Token.GETPROP; + case com.google.javascript.rhino.head.Token.GETELEM: + return Token.GETELEM; + case com.google.javascript.rhino.head.Token.CALL: + return Token.CALL; + case com.google.javascript.rhino.head.Token.NAME: + return Token.NAME; + case com.google.javascript.rhino.head.Token.NUMBER: + return Token.NUMBER; + case com.google.javascript.rhino.head.Token.STRING: + return Token.STRING; + case com.google.javascript.rhino.head.Token.NULL: + return Token.NULL; + case com.google.javascript.rhino.head.Token.THIS: + return Token.THIS; + case com.google.javascript.rhino.head.Token.FALSE: + return Token.FALSE; + case com.google.javascript.rhino.head.Token.TRUE: + return Token.TRUE; + case com.google.javascript.rhino.head.Token.SHEQ: + return Token.SHEQ; + case com.google.javascript.rhino.head.Token.SHNE: + return Token.SHNE; + case com.google.javascript.rhino.head.Token.REGEXP: + return Token.REGEXP; + case com.google.javascript.rhino.head.Token.THROW: + return Token.THROW; + case com.google.javascript.rhino.head.Token.IN: + return Token.IN; + case com.google.javascript.rhino.head.Token.INSTANCEOF: + return Token.INSTANCEOF; + case com.google.javascript.rhino.head.Token.ARRAYLIT: + return Token.ARRAYLIT; + case com.google.javascript.rhino.head.Token.OBJECTLIT: + return Token.OBJECTLIT; + case com.google.javascript.rhino.head.Token.TRY: + return Token.TRY; + // The LP represents a parameter list + case com.google.javascript.rhino.head.Token.LP: + return Token.PARAM_LIST; + case com.google.javascript.rhino.head.Token.COMMA: + return Token.COMMA; + case com.google.javascript.rhino.head.Token.ASSIGN: + return Token.ASSIGN; + case com.google.javascript.rhino.head.Token.ASSIGN_BITOR: + return Token.ASSIGN_BITOR; + case com.google.javascript.rhino.head.Token.ASSIGN_BITXOR: + return Token.ASSIGN_BITXOR; + case com.google.javascript.rhino.head.Token.ASSIGN_BITAND: + return Token.ASSIGN_BITAND; + case com.google.javascript.rhino.head.Token.ASSIGN_LSH: + return Token.ASSIGN_LSH; + case com.google.javascript.rhino.head.Token.ASSIGN_RSH: + return Token.ASSIGN_RSH; + case com.google.javascript.rhino.head.Token.ASSIGN_URSH: + return Token.ASSIGN_URSH; + case com.google.javascript.rhino.head.Token.ASSIGN_ADD: + return Token.ASSIGN_ADD; + case com.google.javascript.rhino.head.Token.ASSIGN_SUB: + return Token.ASSIGN_SUB; + case com.google.javascript.rhino.head.Token.ASSIGN_MUL: + return Token.ASSIGN_MUL; + case com.google.javascript.rhino.head.Token.ASSIGN_DIV: + return Token.ASSIGN_DIV; + case com.google.javascript.rhino.head.Token.ASSIGN_MOD: + return Token.ASSIGN_MOD; + case com.google.javascript.rhino.head.Token.HOOK: + return Token.HOOK; + case com.google.javascript.rhino.head.Token.OR: + return Token.OR; + case com.google.javascript.rhino.head.Token.AND: + return Token.AND; + case com.google.javascript.rhino.head.Token.INC: + return Token.INC; + case com.google.javascript.rhino.head.Token.DEC: + return Token.DEC; + case com.google.javascript.rhino.head.Token.FUNCTION: + return Token.FUNCTION; + case com.google.javascript.rhino.head.Token.IF: + return Token.IF; + case com.google.javascript.rhino.head.Token.SWITCH: + return Token.SWITCH; + case com.google.javascript.rhino.head.Token.CASE: + return Token.CASE; + case com.google.javascript.rhino.head.Token.DEFAULT: + return Token.DEFAULT_CASE; + case com.google.javascript.rhino.head.Token.WHILE: + return Token.WHILE; + case com.google.javascript.rhino.head.Token.DO: + return Token.DO; + case com.google.javascript.rhino.head.Token.FOR: + return Token.FOR; + case com.google.javascript.rhino.head.Token.BREAK: + return Token.BREAK; + case com.google.javascript.rhino.head.Token.CONTINUE: + return Token.CONTINUE; + case com.google.javascript.rhino.head.Token.VAR: + return Token.VAR; + case com.google.javascript.rhino.head.Token.WITH: + return Token.WITH; + case com.google.javascript.rhino.head.Token.CATCH: + return Token.CATCH; + case com.google.javascript.rhino.head.Token.VOID: + return Token.VOID; + case com.google.javascript.rhino.head.Token.EMPTY: + return Token.EMPTY; + case com.google.javascript.rhino.head.Token.BLOCK: + return Token.BLOCK; + case com.google.javascript.rhino.head.Token.LABEL: + return Token.LABEL; + case com.google.javascript.rhino.head.Token.EXPR_VOID: + case com.google.javascript.rhino.head.Token.EXPR_RESULT: + return Token.EXPR_RESULT; + case com.google.javascript.rhino.head.Token.SCRIPT: + return Token.SCRIPT; + case com.google.javascript.rhino.head.Token.GET: + return Token.GETTER_DEF; + case com.google.javascript.rhino.head.Token.SET: + return Token.SETTER_DEF; + case com.google.javascript.rhino.head.Token.CONST: + return Token.CONST; + case com.google.javascript.rhino.head.Token.DEBUGGER: + return Token.DEBUGGER; + } + + // Token without name + throw new IllegalStateException(String.valueOf(token)); + } + + // Simple helper to create nodes and set the initial node properties. + private Node newNode(int type) { + return new Node(type).clonePropsFrom(templateNode); + } + + private Node newNode(int type, Node child1) { + return new Node(type, child1).clonePropsFrom(templateNode); + } + + private Node newNode(int type, Node child1, Node child2) { + return new Node(type, child1, child2).clonePropsFrom(templateNode); + } + + private Node newNode(int type, Node child1, Node child2, Node child3) { + return new Node(type, child1, child2, child3).clonePropsFrom(templateNode); + } + + private Node newStringNode(String value) { + return IR.string(value).clonePropsFrom(templateNode); + } + + private Node newStringNode(int type, String value) { + return Node.newString(type, value).clonePropsFrom(templateNode); + } + + private Node newNumberNode(Double value) { + return IR.number(value).clonePropsFrom(templateNode); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/JsDocInfoParser.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/JsDocInfoParser.java new file mode 100644 index 0000000..5ef8850 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/JsDocInfoParser.java @@ -0,0 +1,2402 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.parsing; + +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.parsing.Config.LanguageMode; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.JSDocInfo.Visibility; +import com.google.javascript.rhino.JSDocInfoBuilder; +import com.google.javascript.rhino.JSTypeExpression; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.ScriptRuntime; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.head.ErrorReporter; +import com.google.javascript.rhino.head.ast.Comment; +import com.google.javascript.rhino.jstype.StaticSourceFile; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * A parser for JSDoc comments. + * + */ +// TODO(nicksantos): Unify all the JSDocInfo stuff into one package, instead of +// spreading it across multiple packages. +public final class JsDocInfoParser { + + private final JsDocTokenStream stream; + private final JSDocInfoBuilder jsdocBuilder; + private final StaticSourceFile sourceFile; + private final Node associatedNode; + private final ErrorReporter errorReporter; + private final ErrorReporterParser parser = new ErrorReporterParser(); + + // Use a template node for properties set on all nodes to minimize the + // memory footprint associated with these (similar to IRFactory). + private final Node templateNode; + + private class ErrorReporterParser { + void addParserWarning(String messageId, String messageArg, int lineno, + int charno) { + errorReporter.warning(ScriptRuntime.getMessage1(messageId, messageArg), + getSourceName(), lineno, null, charno); + } + + void addParserWarning(String messageId, int lineno, int charno) { + errorReporter.warning(ScriptRuntime.getMessage0(messageId), + getSourceName(), lineno, null, charno); + } + + void addTypeWarning(String messageId, String messageArg, int lineno, + int charno) { + errorReporter.warning( + "Bad type annotation. " + + ScriptRuntime.getMessage1(messageId, messageArg), + getSourceName(), lineno, null, charno); + } + + void addTypeWarning(String messageId, int lineno, int charno) { + errorReporter.warning( + "Bad type annotation. " + + ScriptRuntime.getMessage0(messageId), + getSourceName(), lineno, null, charno); + } + } + + // The DocInfo with the fileoverview tag for the whole file. + private JSDocInfo fileOverviewJSDocInfo = null; + private State state; + + private final Map annotationNames; + private final Set suppressionNames; + static private final Set modifiesAnnotationKeywords = + ImmutableSet.of("this", "arguments"); + + private Node.FileLevelJsDocBuilder fileLevelJsDocBuilder; + + /** + * Sets the JsDocBuilder for the file-level (root) node of this parse. The + * parser uses the builder to append any preserve annotations it encounters + * in JsDoc comments. + * + * @param fileLevelJsDocBuilder + */ + void setFileLevelJsDocBuilder( + Node.FileLevelJsDocBuilder fileLevelJsDocBuilder) { + this.fileLevelJsDocBuilder = fileLevelJsDocBuilder; + } + + /** + * Sets the file overview JSDocInfo, in order to warn about multiple uses of + * the @fileoverview tag in a file. + */ + void setFileOverviewJSDocInfo(JSDocInfo fileOverviewJSDocInfo) { + this.fileOverviewJSDocInfo = fileOverviewJSDocInfo; + } + + private enum State { + SEARCHING_ANNOTATION, + SEARCHING_NEWLINE, + NEXT_IS_ANNOTATION + } + + JsDocInfoParser(JsDocTokenStream stream, + Comment commentNode, + Node associatedNode, + Config config, + ErrorReporter errorReporter) { + this.stream = stream; + this.associatedNode = associatedNode; + + // Sometimes this will be null in tests. + this.sourceFile = associatedNode == null + ? null : associatedNode.getStaticSourceFile(); + + this.jsdocBuilder = new JSDocInfoBuilder(config.parseJsDocDocumentation); + if (commentNode != null) { + this.jsdocBuilder.recordOriginalCommentString(commentNode.getValue()); + } + this.annotationNames = config.annotationNames; + this.suppressionNames = config.suppressionNames; + + this.errorReporter = errorReporter; + this.templateNode = this.createTemplateNode(); + } + + private String getSourceName() { + return sourceFile == null ? null : sourceFile.getName(); + } + + /** + * Parses a string containing a JsDoc type declaration, returning the + * type if the parsing succeeded or {@code null} if it failed. + */ + public static Node parseTypeString(String typeString) { + Config config = new Config( + Sets.newHashSet(), + Sets.newHashSet(), + false, + LanguageMode.ECMASCRIPT3, + false); + JsDocInfoParser parser = new JsDocInfoParser( + new JsDocTokenStream(typeString), + null, + null, + config, + NullErrorReporter.forNewRhino()); + + return parser.parseTopLevelTypeExpression(parser.next()); + } + + /** + * Parses a {@link JSDocInfo} object. This parsing method reads all tokens + * returned by the {@link JsDocTokenStream#getJsDocToken()} method until the + * {@link JsDocToken#EOC} is returned. + * + * @return {@code true} if JSDoc information was correctly parsed, + * {@code false} otherwise + */ + @SuppressWarnings("incomplete-switch") + boolean parse() { + int lineno; + int charno; + + // JSTypes are represented as Rhino AST nodes, and then resolved later. + JSTypeExpression type; + + state = State.SEARCHING_ANNOTATION; + skipEOLs(); + + JsDocToken token = next(); + + List extendedTypes = Lists.newArrayList(); + + // Always record that we have a comment. + if (jsdocBuilder.shouldParseDocumentation()) { + ExtractionInfo blockInfo = extractBlockComment(token); + token = blockInfo.token; + if (!blockInfo.string.isEmpty()) { + jsdocBuilder.recordBlockDescription(blockInfo.string); + } + } else { + if (token != JsDocToken.ANNOTATION && + token != JsDocToken.EOC) { + // Mark that there was a description, but don't bother marking + // what it was. + jsdocBuilder.recordBlockDescription(""); + } + } + + // Parse the actual JsDoc. + retry: for (;;) { + switch (token) { + case ANNOTATION: + if (state == State.SEARCHING_ANNOTATION) { + state = State.SEARCHING_NEWLINE; + lineno = stream.getLineno(); + charno = stream.getCharno(); + + String annotationName = stream.getString(); + Annotation annotation = annotationNames.get(annotationName); + if (annotation == null) { + parser.addParserWarning("msg.bad.jsdoc.tag", annotationName, + stream.getLineno(), stream.getCharno()); + } else { + // Mark the beginning of the annotation. + jsdocBuilder.markAnnotation(annotationName, lineno, charno); + + switch (annotation) { + case AUTHOR: + if (jsdocBuilder.shouldParseDocumentation()) { + ExtractionInfo authorInfo = extractSingleLineBlock(); + String author = authorInfo.string; + + if (author.length() == 0) { + parser.addParserWarning("msg.jsdoc.authormissing", + stream.getLineno(), stream.getCharno()); + } else { + jsdocBuilder.addAuthor(author); + } + token = authorInfo.token; + } else { + token = eatTokensUntilEOL(token); + } + continue retry; + + case CONSISTENTIDGENERATOR: + if (!jsdocBuilder.recordConsistentIdGenerator()) { + parser.addParserWarning("msg.jsdoc.consistidgen", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case STRUCT: + if (!jsdocBuilder.recordStruct()) { + parser.addTypeWarning("msg.jsdoc.incompat.type", + stream.getLineno(), + stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case DICT: + if (!jsdocBuilder.recordDict()) { + parser.addTypeWarning("msg.jsdoc.incompat.type", + stream.getLineno(), + stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case CONSTRUCTOR: + if (!jsdocBuilder.recordConstructor()) { + if (jsdocBuilder.isInterfaceRecorded()) { + parser.addTypeWarning("msg.jsdoc.interface.constructor", + stream.getLineno(), stream.getCharno()); + } else { + parser.addTypeWarning("msg.jsdoc.incompat.type", + stream.getLineno(), stream.getCharno()); + } + } + token = eatTokensUntilEOL(); + continue retry; + + case DEPRECATED: + if (!jsdocBuilder.recordDeprecated()) { + parser.addParserWarning("msg.jsdoc.deprecated", + stream.getLineno(), stream.getCharno()); + } + + // Find the reason/description, if any. + ExtractionInfo reasonInfo = + extractMultilineTextualBlock(token); + + String reason = reasonInfo.string; + + if (reason.length() > 0) { + jsdocBuilder.recordDeprecationReason(reason); + } + + token = reasonInfo.token; + continue retry; + + case INTERFACE: + if (!jsdocBuilder.recordInterface()) { + if (jsdocBuilder.isConstructorRecorded()) { + parser.addTypeWarning("msg.jsdoc.interface.constructor", + stream.getLineno(), stream.getCharno()); + } else { + parser.addTypeWarning("msg.jsdoc.incompat.type", + stream.getLineno(), stream.getCharno()); + } + } + token = eatTokensUntilEOL(); + continue retry; + + case DESC: + if (jsdocBuilder.isDescriptionRecorded()) { + parser.addParserWarning("msg.jsdoc.desc.extra", + stream.getLineno(), stream.getCharno()); + token = eatTokensUntilEOL(); + continue retry; + } else { + ExtractionInfo descriptionInfo = + extractMultilineTextualBlock(token); + + String description = descriptionInfo.string; + + jsdocBuilder.recordDescription(description); + token = descriptionInfo.token; + continue retry; + } + + case FILE_OVERVIEW: + String fileOverview = ""; + if (jsdocBuilder.shouldParseDocumentation()) { + ExtractionInfo fileOverviewInfo = + extractMultilineTextualBlock(token, + WhitespaceOption.TRIM); + + fileOverview = fileOverviewInfo.string; + + token = fileOverviewInfo.token; + } else { + token = eatTokensUntilEOL(token); + } + + if (!jsdocBuilder.recordFileOverview(fileOverview)) { + parser.addParserWarning("msg.jsdoc.fileoverview.extra", + stream.getLineno(), stream.getCharno()); + } + continue retry; + + case LICENSE: + case PRESERVE: + ExtractionInfo preserveInfo = + extractMultilineTextualBlock(token, + WhitespaceOption.PRESERVE); + + String preserve = preserveInfo.string; + + if (preserve.length() > 0) { + if (fileLevelJsDocBuilder != null) { + fileLevelJsDocBuilder.append(preserve); + } + } + + token = preserveInfo.token; + continue retry; + + case ENUM: + token = next(); + lineno = stream.getLineno(); + charno = stream.getCharno(); + + type = null; + if (token != JsDocToken.EOL && token != JsDocToken.EOC) { + type = createJSTypeExpression( + parseAndRecordTypeNode(token)); + } + + if (type == null) { + type = createJSTypeExpression(newStringNode("number")); + } + if (!jsdocBuilder.recordEnumParameterType(type)) { + parser.addTypeWarning( + "msg.jsdoc.incompat.type", lineno, charno); + } + token = eatTokensUntilEOL(token); + continue retry; + + case EXPORT: + if (!jsdocBuilder.recordExport()) { + parser.addParserWarning("msg.jsdoc.export", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case EXPOSE: + if (!jsdocBuilder.recordExpose()) { + parser.addParserWarning("msg.jsdoc.expose", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case EXTERNS: + if (!jsdocBuilder.recordExterns()) { + parser.addParserWarning("msg.jsdoc.externs", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case JAVA_DISPATCH: + if (!jsdocBuilder.recordJavaDispatch()) { + parser.addParserWarning("msg.jsdoc.javadispatch", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case EXTENDS: + case IMPLEMENTS: + skipEOLs(); + token = next(); + lineno = stream.getLineno(); + charno = stream.getCharno(); + boolean matchingRc = false; + + if (token == JsDocToken.LC) { + token = next(); + matchingRc = true; + } + + if (token == JsDocToken.STRING) { + Node typeNode = parseAndRecordTypeNameNode( + token, lineno, charno, matchingRc); + + lineno = stream.getLineno(); + charno = stream.getCharno(); + + typeNode = wrapNode(Token.BANG, typeNode); + type = createJSTypeExpression(typeNode); + + if (annotation == Annotation.EXTENDS) { + // record the extended type, check later + extendedTypes.add(new ExtendedTypeInfo( + type, stream.getLineno(), stream.getCharno())); + } else { + Preconditions.checkState( + annotation == Annotation.IMPLEMENTS); + if (!jsdocBuilder.recordImplementedInterface(type)) { + parser.addTypeWarning("msg.jsdoc.implements.duplicate", + lineno, charno); + } + } + token = next(); + if (matchingRc) { + if (token != JsDocToken.RC) { + parser.addTypeWarning("msg.jsdoc.missing.rc", + stream.getLineno(), stream.getCharno()); + } + } else if (token != JsDocToken.EOL && + token != JsDocToken.EOF && token != JsDocToken.EOC) { + parser.addTypeWarning("msg.end.annotation.expected", + stream.getLineno(), stream.getCharno()); + } + } else { + parser.addTypeWarning("msg.no.type.name", lineno, charno); + } + token = eatTokensUntilEOL(token); + continue retry; + + case HIDDEN: + if (!jsdocBuilder.recordHiddenness()) { + parser.addParserWarning("msg.jsdoc.hidden", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case LENDS: + skipEOLs(); + + matchingRc = false; + if (match(JsDocToken.LC)) { + token = next(); + matchingRc = true; + } + + if (match(JsDocToken.STRING)) { + token = next(); + if (!jsdocBuilder.recordLends(stream.getString())) { + parser.addTypeWarning("msg.jsdoc.lends.incompatible", + stream.getLineno(), stream.getCharno()); + } + } else { + parser.addTypeWarning("msg.jsdoc.lends.missing", + stream.getLineno(), stream.getCharno()); + } + + if (matchingRc && !match(JsDocToken.RC)) { + parser.addTypeWarning("msg.jsdoc.missing.rc", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case MEANING: + ExtractionInfo meaningInfo = + extractMultilineTextualBlock(token); + String meaning = meaningInfo.string; + token = meaningInfo.token; + if (!jsdocBuilder.recordMeaning(meaning)) { + parser.addParserWarning("msg.jsdoc.meaning.extra", + stream.getLineno(), stream.getCharno()); + } + continue retry; + + case NO_ALIAS: + if (!jsdocBuilder.recordNoAlias()) { + parser.addParserWarning("msg.jsdoc.noalias", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case NO_COMPILE: + if (!jsdocBuilder.recordNoCompile()) { + parser.addParserWarning("msg.jsdoc.nocompile", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case NO_TYPE_CHECK: + if (!jsdocBuilder.recordNoTypeCheck()) { + parser.addParserWarning("msg.jsdoc.nocheck", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case NOT_IMPLEMENTED: + token = eatTokensUntilEOL(); + continue retry; + + case INHERIT_DOC: + case OVERRIDE: + if (!jsdocBuilder.recordOverride()) { + parser.addTypeWarning("msg.jsdoc.override", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case THROWS: + skipEOLs(); + token = next(); + lineno = stream.getLineno(); + charno = stream.getCharno(); + type = null; + + if (token == JsDocToken.LC) { + type = createJSTypeExpression( + parseAndRecordTypeNode(token)); + + if (type == null) { + // parsing error reported during recursive descent + // recovering parsing + token = eatTokensUntilEOL(); + continue retry; + } + } + + // *Update* the token to that after the type annotation. + token = current(); + + // Save the throw type. + jsdocBuilder.recordThrowType(type); + + // Find the throw's description (if applicable). + if (jsdocBuilder.shouldParseDocumentation()) { + ExtractionInfo descriptionInfo = + extractMultilineTextualBlock(token); + + String description = descriptionInfo.string; + + if (description.length() > 0) { + jsdocBuilder.recordThrowDescription(type, description); + } + + token = descriptionInfo.token; + } else { + token = eatTokensUntilEOL(token); + } + continue retry; + + case PARAM: + skipEOLs(); + token = next(); + lineno = stream.getLineno(); + charno = stream.getCharno(); + type = null; + + if (token == JsDocToken.LC) { + type = createJSTypeExpression( + parseAndRecordParamTypeNode(token)); + + if (type == null) { + // parsing error reported during recursive descent + // recovering parsing + token = eatTokensUntilEOL(); + continue retry; + } + skipEOLs(); + token = next(); + lineno = stream.getLineno(); + charno = stream.getCharno(); + } + + String name = null; + boolean isBracketedParam = JsDocToken.LB == token; + if (isBracketedParam) { + token = next(); + } + + if (JsDocToken.STRING != token) { + parser.addTypeWarning("msg.missing.variable.name", + lineno, charno); + } else { + name = stream.getString(); + + if (isBracketedParam) { + token = next(); + + // Throw out JsDocToolkit's "default" parameter + // annotation. It makes no sense under our type + // system. + if (JsDocToken.EQUALS == token) { + token = next(); + if (JsDocToken.STRING == token) { + token = next(); + } + } + + if (JsDocToken.RB != token) { + reportTypeSyntaxWarning("msg.jsdoc.missing.rb"); + } else if (type != null) { + // Make the type expression optional, if it isn't + // already. + type = JSTypeExpression.makeOptionalArg(type); + } + } + + // If the param name has a DOT in it, just throw it out + // quietly. We do not handle the JsDocToolkit method + // for handling properties of params. + if (name.indexOf('.') > -1) { + name = null; + } else if (!jsdocBuilder.recordParameter(name, type)) { + if (jsdocBuilder.hasParameter(name)) { + parser.addTypeWarning("msg.dup.variable.name", name, + lineno, charno); + } else { + parser.addTypeWarning("msg.jsdoc.incompat.type", name, + lineno, charno); + } + } + } + + if (name == null) { + token = eatTokensUntilEOL(token); + continue retry; + } + + jsdocBuilder.markName(name, sourceFile, lineno, charno); + + // Find the parameter's description (if applicable). + if (jsdocBuilder.shouldParseDocumentation()) { + ExtractionInfo paramDescriptionInfo = + extractMultilineTextualBlock(token); + + String paramDescription = paramDescriptionInfo.string; + + if (paramDescription.length() > 0) { + jsdocBuilder.recordParameterDescription(name, + paramDescription); + } + + token = paramDescriptionInfo.token; + } else { + token = eatTokensUntilEOL(token); + } + continue retry; + + case PRESERVE_TRY: + if (!jsdocBuilder.recordPreserveTry()) { + parser.addParserWarning("msg.jsdoc.preservertry", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case NO_SHADOW: + if (!jsdocBuilder.recordNoShadow()) { + parser.addParserWarning("msg.jsdoc.noshadow", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case NO_SIDE_EFFECTS: + if (!jsdocBuilder.recordNoSideEffects()) { + parser.addParserWarning("msg.jsdoc.nosideeffects", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case MODIFIES: + token = parseModifiesTag(next()); + continue retry; + + case IMPLICIT_CAST: + if (!jsdocBuilder.recordImplicitCast()) { + parser.addTypeWarning("msg.jsdoc.implicitcast", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case SEE: + if (jsdocBuilder.shouldParseDocumentation()) { + ExtractionInfo referenceInfo = extractSingleLineBlock(); + String reference = referenceInfo.string; + + if (reference.length() == 0) { + parser.addParserWarning("msg.jsdoc.seemissing", + stream.getLineno(), stream.getCharno()); + } else { + jsdocBuilder.addReference(reference); + } + + token = referenceInfo.token; + } else { + token = eatTokensUntilEOL(token); + } + continue retry; + + case STABLEIDGENERATOR: + if (!jsdocBuilder.recordStableIdGenerator()) { + parser.addParserWarning("msg.jsdoc.stableidgen", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case SUPPRESS: + token = parseSuppressTag(next()); + continue retry; + + case TEMPLATE: + ExtractionInfo templateInfo = extractSingleLineBlock(); + List names = Lists.newArrayList( + Splitter.on(',') + .trimResults() + .split(templateInfo.string)); + + if (names.size() == 0 || names.get(0).length() == 0) { + parser.addTypeWarning("msg.jsdoc.templatemissing", + stream.getLineno(), stream.getCharno()); + } else if (!jsdocBuilder.recordTemplateTypeNames(names)) { + parser.addTypeWarning("msg.jsdoc.template.at.most.once", + stream.getLineno(), stream.getCharno()); + } + + token = templateInfo.token; + continue retry; + + case IDGENERATOR: + if (!jsdocBuilder.recordIdGenerator()) { + parser.addParserWarning("msg.jsdoc.idgen", + stream.getLineno(), stream.getCharno()); + } + token = eatTokensUntilEOL(); + continue retry; + + case VERSION: + ExtractionInfo versionInfo = extractSingleLineBlock(); + String version = versionInfo.string; + + if (version.length() == 0) { + parser.addParserWarning("msg.jsdoc.versionmissing", + stream.getLineno(), stream.getCharno()); + } else { + if (!jsdocBuilder.recordVersion(version)) { + parser.addParserWarning("msg.jsdoc.extraversion", + stream.getLineno(), stream.getCharno()); + } + } + + token = versionInfo.token; + continue retry; + + case CONSTANT: + case DEFINE: + case RETURN: + case PRIVATE: + case PROTECTED: + case PUBLIC: + case THIS: + case TYPE: + case TYPEDEF: + lineno = stream.getLineno(); + charno = stream.getCharno(); + + Node typeNode = null; + boolean hasType = lookAheadForTypeAnnotation(); + boolean isAlternateTypeAnnotation = + (annotation == Annotation.PRIVATE || + annotation == Annotation.PROTECTED || + annotation == Annotation.PUBLIC || + annotation == Annotation.CONSTANT); + boolean canSkipTypeAnnotation = + (isAlternateTypeAnnotation || + annotation == Annotation.RETURN); + type = null; + if (hasType || !canSkipTypeAnnotation) { + skipEOLs(); + token = next(); + typeNode = parseAndRecordTypeNode(token); + + if (annotation == Annotation.THIS) { + typeNode = wrapNode(Token.BANG, typeNode); + } + type = createJSTypeExpression(typeNode); + } + + // The error was reported during recursive descent + // recovering parsing + boolean hasError = type == null && !canSkipTypeAnnotation; + if (!hasError) { + // Record types for @type. + // If the @private, @protected, or @public annotations + // have a type attached, pretend that they actually wrote: + // @type {type}\n@private + // This will have some weird behavior in some cases + // (for example, @private can now be used as a type-cast), + // but should be mostly OK. + if ((type != null && isAlternateTypeAnnotation) + || annotation == Annotation.TYPE) { + if (!jsdocBuilder.recordType(type)) { + parser.addTypeWarning( + "msg.jsdoc.incompat.type", lineno, charno); + } + } + + switch (annotation) { + case CONSTANT: + if (!jsdocBuilder.recordConstancy()) { + parser.addParserWarning("msg.jsdoc.const", + stream.getLineno(), stream.getCharno()); + } + break; + + case DEFINE: + if (!jsdocBuilder.recordDefineType(type)) { + parser.addParserWarning("msg.jsdoc.define", + lineno, charno); + } + break; + + case PRIVATE: + if (!jsdocBuilder.recordVisibility(Visibility.PRIVATE)) { + parser.addParserWarning( + "msg.jsdoc.visibility.private", + lineno, charno); + } + break; + + case PROTECTED: + if (!jsdocBuilder.recordVisibility(Visibility.PROTECTED)) { + parser.addParserWarning( + "msg.jsdoc.visibility.protected", + lineno, charno); + } + break; + + case PUBLIC: + if (!jsdocBuilder.recordVisibility(Visibility.PUBLIC)) { + parser.addParserWarning( + "msg.jsdoc.visibility.public", + lineno, charno); + } + break; + + case RETURN: + if (type == null) { + type = createJSTypeExpression(newNode(Token.QMARK)); + } + + if (!jsdocBuilder.recordReturnType(type)) { + parser.addTypeWarning( + "msg.jsdoc.incompat.type", lineno, charno); + break; + } + + // Find the return's description (if applicable). + if (jsdocBuilder.shouldParseDocumentation()) { + ExtractionInfo returnDescriptionInfo = + extractMultilineTextualBlock(token); + + String returnDescription = + returnDescriptionInfo.string; + + if (returnDescription.length() > 0) { + jsdocBuilder.recordReturnDescription( + returnDescription); + } + + token = returnDescriptionInfo.token; + } else { + token = eatTokensUntilEOL(token); + } + continue retry; + + case THIS: + if (!jsdocBuilder.recordThisType(type)) { + parser.addTypeWarning( + "msg.jsdoc.incompat.type", lineno, charno); + } + break; + + case TYPEDEF: + if (!jsdocBuilder.recordTypedef(type)) { + parser.addTypeWarning( + "msg.jsdoc.incompat.type", lineno, charno); + } + break; + } + } + + token = eatTokensUntilEOL(); + continue retry; + } + } + } + break; + + case EOC: + if (hasParsedFileOverviewDocInfo()) { + fileOverviewJSDocInfo = retrieveAndResetParsedJSDocInfo(); + } + checkExtendedTypes(extendedTypes); + return true; + + case EOF: + // discard any accumulated information + jsdocBuilder.build(null); + parser.addParserWarning("msg.unexpected.eof", + stream.getLineno(), stream.getCharno()); + checkExtendedTypes(extendedTypes); + return false; + + case EOL: + if (state == State.SEARCHING_NEWLINE) { + state = State.SEARCHING_ANNOTATION; + } + token = next(); + continue retry; + + default: + if (token == JsDocToken.STAR && state == State.SEARCHING_ANNOTATION) { + token = next(); + continue retry; + } else { + state = State.SEARCHING_NEWLINE; + token = eatTokensUntilEOL(); + continue retry; + } + } + + // next token + token = next(); + } + } + + private void checkExtendedTypes(List extendedTypes) { + for (ExtendedTypeInfo typeInfo : extendedTypes) { + // If interface, record the multiple extended interfaces + if (jsdocBuilder.isInterfaceRecorded()) { + if (!jsdocBuilder.recordExtendedInterface(typeInfo.type)) { + parser.addParserWarning("msg.jsdoc.extends.duplicate", + typeInfo.lineno, typeInfo.charno); + } + } else { + if (!jsdocBuilder.recordBaseType(typeInfo.type)) { + parser.addTypeWarning("msg.jsdoc.incompat.type", + typeInfo.lineno, typeInfo.charno); + } + } + } + } + + /** + * Parse a {@code @suppress} tag of the form + * {@code @suppress{warning1|warning2}}. + * + * @param token The current token. + */ + private JsDocToken parseSuppressTag(JsDocToken token) { + if (token == JsDocToken.LC) { + Set suppressions = new HashSet(); + while (true) { + if (match(JsDocToken.STRING)) { + String name = stream.getString(); + if (!suppressionNames.contains(name)) { + parser.addParserWarning("msg.jsdoc.suppress.unknown", name, + stream.getLineno(), stream.getCharno()); + } + + suppressions.add(stream.getString()); + token = next(); + } else { + parser.addParserWarning("msg.jsdoc.suppress", + stream.getLineno(), stream.getCharno()); + return token; + } + + if (match(JsDocToken.PIPE)) { + token = next(); + } else { + break; + } + } + + if (!match(JsDocToken.RC)) { + parser.addParserWarning("msg.jsdoc.suppress", + stream.getLineno(), stream.getCharno()); + } else { + token = next(); + if (!jsdocBuilder.recordSuppressions(suppressions)) { + parser.addParserWarning("msg.jsdoc.suppress.duplicate", + stream.getLineno(), stream.getCharno()); + } + } + } + return token; + } + + /** + * Parse a {@code @modifies} tag of the form + * {@code @modifies{this|arguments|param}}. + * + * @param token The current token. + */ + private JsDocToken parseModifiesTag(JsDocToken token) { + if (token == JsDocToken.LC) { + Set modifies = new HashSet(); + while (true) { + if (match(JsDocToken.STRING)) { + String name = stream.getString(); + if (!modifiesAnnotationKeywords.contains(name) + && !jsdocBuilder.hasParameter(name)) { + parser.addParserWarning("msg.jsdoc.modifies.unknown", name, + stream.getLineno(), stream.getCharno()); + } + + modifies.add(stream.getString()); + token = next(); + } else { + parser.addParserWarning("msg.jsdoc.modifies", + stream.getLineno(), stream.getCharno()); + return token; + } + + if (match(JsDocToken.PIPE)) { + token = next(); + } else { + break; + } + } + + if (!match(JsDocToken.RC)) { + parser.addParserWarning("msg.jsdoc.modifies", + stream.getLineno(), stream.getCharno()); + } else { + token = next(); + if (!jsdocBuilder.recordModifies(modifies)) { + parser.addParserWarning("msg.jsdoc.modifies.duplicate", + stream.getLineno(), stream.getCharno()); + } + } + } + return token; + } + + /** + * Looks for a type expression at the current token and if found, + * returns it. Note that this method consumes input. + * + * @param token The current token. + * @return The type expression found or null if none. + */ + private Node parseAndRecordTypeNode(JsDocToken token) { + return parseAndRecordTypeNode(token, token == JsDocToken.LC); + } + + /** + * Looks for a type expression at the current token and if found, + * returns it. Note that this method consumes input. + * + * @param token The current token. + * @param matchingLC Whether the type expression starts with a "{". + * @return The type expression found or null if none. + */ + private Node parseAndRecordTypeNode(JsDocToken token, boolean matchingLC) { + return parseAndRecordTypeNode(token, stream.getLineno(), stream.getCharno(), + matchingLC, false); + } + + /** + * Looks for a type expression at the current token and if found, + * returns it. Note that this method consumes input. + * + * @param token The current token. + * @param lineno The line of the type expression. + * @param startCharno The starting character position of the type expression. + * @param matchingLC Whether the type expression starts with a "{". + * @return The type expression found or null if none. + */ + private Node parseAndRecordTypeNameNode(JsDocToken token, int lineno, + int startCharno, boolean matchingLC) { + return parseAndRecordTypeNode(token, lineno, startCharno, matchingLC, true); + } + + /** + * Looks for a type expression at the current token and if found, + * returns it. Note that this method consumes input. + * + * Parameter type expressions are special for two reasons: + *

        + *
      1. They must begin with '{', to distinguish type names from param names. + *
      2. They may end in '=', to denote optionality. + *
      + * + * @param token The current token. + * @return The type expression found or null if none. + */ + private Node parseAndRecordParamTypeNode(JsDocToken token) { + Preconditions.checkArgument(token == JsDocToken.LC); + int lineno = stream.getLineno(); + int startCharno = stream.getCharno(); + + Node typeNode = parseParamTypeExpressionAnnotation(token); + if (typeNode != null) { + int endLineno = stream.getLineno(); + int endCharno = stream.getCharno(); + + jsdocBuilder.markTypeNode(typeNode, lineno, startCharno, + endLineno, endCharno, true); + } + return typeNode; + } + + /** + * Looks for a parameter type expression at the current token and if found, + * returns it. Note that this method consumes input. + * + * @param token The current token. + * @param lineno The line of the type expression. + * @param startCharno The starting character position of the type expression. + * @param matchingLC Whether the type expression starts with a "{". + * @param onlyParseSimpleNames If true, only simple type names are parsed + * (via a call to parseTypeNameAnnotation instead of + * parseTypeExpressionAnnotation). + * @return The type expression found or null if none. + */ + private Node parseAndRecordTypeNode(JsDocToken token, int lineno, + int startCharno, + boolean matchingLC, + boolean onlyParseSimpleNames) { + Node typeNode = null; + + if (onlyParseSimpleNames) { + typeNode = parseTypeNameAnnotation(token); + } else { + typeNode = parseTypeExpressionAnnotation(token); + } + + if (typeNode != null) { + int endLineno = stream.getLineno(); + int endCharno = stream.getCharno(); + + jsdocBuilder.markTypeNode( + typeNode, lineno, startCharno, endLineno, endCharno, matchingLC); + } + + return typeNode; + } + + /** + * Converts a JSDoc token to its string representation. + */ + private String toString(JsDocToken token) { + switch (token) { + case ANNOTATION: + return "@" + stream.getString(); + + case BANG: + return "!"; + + case COMMA: + return ","; + + case COLON: + return ":"; + + case GT: + return ">"; + + case LB: + return "["; + + case LC: + return "{"; + + case LP: + return "("; + + case LT: + return ".<"; + + case QMARK: + return "?"; + + case PIPE: + return "|"; + + case RB: + return "]"; + + case RC: + return "}"; + + case RP: + return ")"; + + case STAR: + return "*"; + + case ELLIPSIS: + return "..."; + + case EQUALS: + return "="; + + case STRING: + return stream.getString(); + + default: + throw new IllegalStateException(token.toString()); + } + } + + /** + * Constructs a new {@code JSTypeExpression}. + * @param n A node. May be null. + */ + private JSTypeExpression createJSTypeExpression(Node n) { + return n == null ? null : + new JSTypeExpression(n, getSourceName()); + } + + /** + * Tuple for returning both the string extracted and the + * new token following a call to any of the extract*Block + * methods. + */ + private static class ExtractionInfo { + private final String string; + private final JsDocToken token; + + public ExtractionInfo(String string, JsDocToken token) { + this.string = string; + this.token = token; + } + } + + /** + * Tuple for recording extended types + */ + private static class ExtendedTypeInfo { + final JSTypeExpression type; + final int lineno; + final int charno; + + public ExtendedTypeInfo(JSTypeExpression type, int lineno, int charno) { + this.type = type; + this.lineno = lineno; + this.charno = charno; + } + } + + /** + * Extracts the text found on the current line starting at token. Note that + * token = token.info; should be called after this method is used to update + * the token properly in the parser. + * + * @return The extraction information. + */ + private ExtractionInfo extractSingleLineBlock() { + + // Get the current starting point. + stream.update(); + int lineno = stream.getLineno(); + int charno = stream.getCharno() + 1; + + String line = stream.getRemainingJSDocLine().trim(); + + // Record the textual description. + if (line.length() > 0) { + jsdocBuilder.markText(line, lineno, charno, lineno, + charno + line.length()); + } + + return new ExtractionInfo(line, next()); + } + + private ExtractionInfo extractMultilineTextualBlock(JsDocToken token) { + return extractMultilineTextualBlock(token, WhitespaceOption.SINGLE_LINE); + } + + private enum WhitespaceOption { + /** + * Preserves all whitespace and formatting. Needed for licenses and + * purposely formatted text. + */ + PRESERVE, + + /** Preserves newlines but trims the output. */ + TRIM, + + /** Removes newlines and turns the output into a single line string. */ + SINGLE_LINE + } + + /** + * Extracts the text found on the current line and all subsequent + * until either an annotation, end of comment or end of file is reached. + * Note that if this method detects an end of line as the first token, it + * will quit immediately (indicating that there is no text where it was + * expected). Note that token = info.token; should be called after this + * method is used to update the token properly in the parser. + * + * @param token The start token. + * @param option How to handle whitespace. + * + * @return The extraction information. + */ + @SuppressWarnings("fallthrough") + private ExtractionInfo extractMultilineTextualBlock(JsDocToken token, + WhitespaceOption option) { + + if (token == JsDocToken.EOC || token == JsDocToken.EOL || + token == JsDocToken.EOF) { + return new ExtractionInfo("", token); + } + + stream.update(); + int startLineno = stream.getLineno(); + int startCharno = stream.getCharno() + 1; + + // Read the content from the first line. + String line = stream.getRemainingJSDocLine(); + if (option != WhitespaceOption.PRESERVE) { + line = line.trim(); + } + + StringBuilder builder = new StringBuilder(); + builder.append(line); + + state = State.SEARCHING_ANNOTATION; + token = next(); + + boolean ignoreStar = false; + + // Track the start of the line to count whitespace that + // the tokenizer skipped. Because this case is rare, it's easier + // to do this here than in the tokenizer. + int lineStartChar = -1; + + do { + switch (token) { + case STAR: + if (ignoreStar) { + // Mark the position after the star as the new start of the line. + lineStartChar = stream.getCharno() + 1; + } else { + // The star is part of the comment. + if (builder.length() > 0) { + builder.append(' '); + } + + builder.append('*'); + } + + token = next(); + continue; + + case EOL: + if (option != WhitespaceOption.SINGLE_LINE) { + builder.append("\n"); + } + + ignoreStar = true; + lineStartChar = 0; + token = next(); + continue; + + default: + ignoreStar = false; + state = State.SEARCHING_ANNOTATION; + + boolean isEOC = token == JsDocToken.EOC; + if (!isEOC) { + if (lineStartChar != -1 && option == WhitespaceOption.PRESERVE) { + int numSpaces = stream.getCharno() - lineStartChar; + for (int i = 0; i < numSpaces; i++) { + builder.append(' '); + } + lineStartChar = -1; + } else if (builder.length() > 0) { + // All tokens must be separated by a space. + builder.append(' '); + } + } + + if (token == JsDocToken.EOC || + token == JsDocToken.EOF || + // When we're capturing a license block, annotations + // in the block are OK. + (token == JsDocToken.ANNOTATION && + option != WhitespaceOption.PRESERVE)) { + String multilineText = builder.toString(); + + if (option != WhitespaceOption.PRESERVE) { + multilineText = multilineText.trim(); + } + + int endLineno = stream.getLineno(); + int endCharno = stream.getCharno(); + + if (multilineText.length() > 0) { + jsdocBuilder.markText(multilineText, startLineno, startCharno, + endLineno, endCharno); + } + + return new ExtractionInfo(multilineText, token); + } + + builder.append(toString(token)); + + line = stream.getRemainingJSDocLine(); + + if (option != WhitespaceOption.PRESERVE) { + line = trimEnd(line); + } + + builder.append(line); + token = next(); + } + } while (true); + } + + + /** + * Extracts the top-level block comment from the JsDoc comment, if any. + * This method differs from the extractMultilineTextualBlock in that it + * terminates under different conditions (it doesn't have the same + * prechecks), it does not first read in the remaining of the current + * line and its conditions for ignoring the "*" (STAR) are different. + * + * @param token The starting token. + * + * @return The extraction information. + */ + private ExtractionInfo extractBlockComment(JsDocToken token) { + StringBuilder builder = new StringBuilder(); + + boolean ignoreStar = true; + + do { + switch (token) { + case ANNOTATION: + case EOC: + case EOF: + return new ExtractionInfo(builder.toString().trim(), token); + + case STAR: + if (!ignoreStar) { + if (builder.length() > 0) { + builder.append(' '); + } + + builder.append('*'); + } + + token = next(); + continue; + + case EOL: + ignoreStar = true; + builder.append('\n'); + token = next(); + continue; + + default: + if (!ignoreStar && builder.length() > 0) { + builder.append(' '); + } + + ignoreStar = false; + + builder.append(toString(token)); + + String line = stream.getRemainingJSDocLine(); + line = trimEnd(line); + builder.append(line); + token = next(); + } + } while (true); + } + + /** + * Trim characters from only the end of a string. + * This method will remove all whitespace characters + * (defined by Character.isWhitespace(char), in addition to the characters + * provided, from the end of the provided string. + * + * @param s String to be trimmed + * @return String with whitespace and characters in extraChars removed + * from the end. + */ + private static String trimEnd(String s) { + int trimCount = 0; + while (trimCount < s.length()) { + char ch = s.charAt(s.length() - trimCount - 1); + if (Character.isWhitespace(ch)) { + trimCount++; + } else { + break; + } + } + + if (trimCount == 0) { + return s; + } + return s.substring(0, s.length() - trimCount); + } + + // Based on ES4 grammar proposed on July 10, 2008. + // http://wiki.ecmascript.org/doku.php?id=spec:spec + // Deliberately written to line up with the actual grammar rules, + // for maximum flexibility. + + // TODO(nicksantos): The current implementation tries to maintain backwards + // compatibility with previous versions of the spec whenever we can. + // We should try to gradually withdraw support for these. + + /** + * TypeExpressionAnnotation := TypeExpression | + * '{' TopLevelTypeExpression '}' + */ + private Node parseTypeExpressionAnnotation(JsDocToken token) { + if (token == JsDocToken.LC) { + skipEOLs(); + Node typeNode = parseTopLevelTypeExpression(next()); + if (typeNode != null) { + skipEOLs(); + if (!match(JsDocToken.RC)) { + reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); + } else { + next(); + } + } + + return typeNode; + } else { + return parseTypeExpression(token); + } + } + + /** + * ParamTypeExpressionAnnotation := + * '{' OptionalParameterType '}' | + * '{' TopLevelTypeExpression '}' | + * '{' '...' TopLevelTypeExpression '}' + * + * OptionalParameterType := + * TopLevelTypeExpression '=' + */ + private Node parseParamTypeExpressionAnnotation(JsDocToken token) { + Preconditions.checkArgument(token == JsDocToken.LC); + + skipEOLs(); + + boolean restArg = false; + token = next(); + if (token == JsDocToken.ELLIPSIS) { + token = next(); + if (token == JsDocToken.RC) { + // EMPTY represents the UNKNOWN type in the Type AST. + return wrapNode(Token.ELLIPSIS, IR.empty()); + } + restArg = true; + } + + Node typeNode = parseTopLevelTypeExpression(token); + if (typeNode != null) { + skipEOLs(); + if (restArg) { + typeNode = wrapNode(Token.ELLIPSIS, typeNode); + } else if (match(JsDocToken.EQUALS)) { + next(); + skipEOLs(); + typeNode = wrapNode(Token.EQUALS, typeNode); + } + + if (!match(JsDocToken.RC)) { + reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); + } else { + next(); + } + } + + return typeNode; + } + + /** + * TypeNameAnnotation := TypeName | '{' TypeName '}' + */ + private Node parseTypeNameAnnotation(JsDocToken token) { + if (token == JsDocToken.LC) { + skipEOLs(); + Node typeNode = parseTypeName(next()); + if (typeNode != null) { + skipEOLs(); + if (!match(JsDocToken.RC)) { + reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); + } else { + next(); + } + } + + return typeNode; + } else { + return parseTypeName(token); + } + } + + /** + * TopLevelTypeExpression := TypeExpression + * | TypeUnionList + * + * We made this rule up, for the sake of backwards compatibility. + */ + private Node parseTopLevelTypeExpression(JsDocToken token) { + Node typeExpr = parseTypeExpression(token); + if (typeExpr != null) { + // top-level unions are allowed + if (match(JsDocToken.PIPE)) { + next(); + if (match(JsDocToken.PIPE)) { + // We support double pipes for backwards-compatibility. + next(); + } + skipEOLs(); + token = next(); + return parseUnionTypeWithAlternate(token, typeExpr); + } + } + return typeExpr; + } + + /** + * TypeExpressionList := TopLevelTypeExpression + * | TopLevelTypeExpression ',' TypeExpressionList + */ + private Node parseTypeExpressionList(JsDocToken token) { + Node typeExpr = parseTopLevelTypeExpression(token); + if (typeExpr == null) { + return null; + } + Node typeList = IR.block(); + typeList.addChildToBack(typeExpr); + while (match(JsDocToken.COMMA)) { + next(); + skipEOLs(); + typeExpr = parseTopLevelTypeExpression(next()); + if (typeExpr == null) { + return null; + } + typeList.addChildToBack(typeExpr); + } + return typeList; + } + + /** + * TypeExpression := BasicTypeExpression + * | '?' BasicTypeExpression + * | '!' BasicTypeExpression + * | BasicTypeExpression '?' + * | BasicTypeExpression '!' + * | '?' + */ + private Node parseTypeExpression(JsDocToken token) { + if (token == JsDocToken.QMARK) { + // A QMARK could mean that a type is nullable, or that it's unknown. + // We use look-ahead 1 to determine whether it's unknown. Otherwise, + // we assume it means nullable. There are 5 cases: + // {?} - right curly + // {?=} - equals + // {function(?, number)} - comma + // {function(number, ?)} - right paren + // {function(number, ...[?])} - right bracket + // {function(): ?|number} - pipe + // {Array.} - greater than + // I'm not a big fan of using look-ahead for this, but it makes + // the type language a lot nicer. + token = next(); + if (token == JsDocToken.COMMA || + token == JsDocToken.EQUALS || + token == JsDocToken.RB || + token == JsDocToken.RC || + token == JsDocToken.RP || + token == JsDocToken.PIPE || + token == JsDocToken.GT) { + restoreLookAhead(token); + return newNode(Token.QMARK); + } + + return wrapNode(Token.QMARK, parseBasicTypeExpression(token)); + } else if (token == JsDocToken.BANG) { + return wrapNode(Token.BANG, parseBasicTypeExpression(next())); + } else { + Node basicTypeExpr = parseBasicTypeExpression(token); + if (basicTypeExpr != null) { + if (match(JsDocToken.QMARK)) { + next(); + return wrapNode(Token.QMARK, basicTypeExpr); + } else if (match(JsDocToken.BANG)) { + next(); + return wrapNode(Token.BANG, basicTypeExpr); + } + } + + return basicTypeExpr; + } + } + + /** + * BasicTypeExpression := '*' | 'null' | 'undefined' | TypeName + * | FunctionType | UnionType | RecordType | ArrayType + */ + private Node parseBasicTypeExpression(JsDocToken token) { + if (token == JsDocToken.STAR) { + return newNode(Token.STAR); + } else if (token == JsDocToken.LB) { + skipEOLs(); + return parseArrayType(next()); + } else if (token == JsDocToken.LC) { + skipEOLs(); + return parseRecordType(next()); + } else if (token == JsDocToken.LP) { + skipEOLs(); + return parseUnionType(next()); + } else if (token == JsDocToken.STRING) { + String string = stream.getString(); + if ("function".equals(string)) { + skipEOLs(); + return parseFunctionType(next()); + } else if ("null".equals(string) || "undefined".equals(string)) { + return newStringNode(string); + } else { + return parseTypeName(token); + } + } + + restoreLookAhead(token); + return reportGenericTypeSyntaxWarning(); + } + + /** + * TypeName := NameExpression | NameExpression TypeApplication + * TypeApplication := '.<' TypeExpressionList '>' + */ + private Node parseTypeName(JsDocToken token) { + if (token != JsDocToken.STRING) { + return reportGenericTypeSyntaxWarning(); + } + + String typeName = stream.getString(); + int lineno = stream.getLineno(); + int charno = stream.getCharno(); + while (match(JsDocToken.EOL) && + typeName.charAt(typeName.length() - 1) == '.') { + skipEOLs(); + if (match(JsDocToken.STRING)) { + next(); + typeName += stream.getString(); + } + } + + Node typeNameNode = newStringNode(typeName, lineno, charno); + + if (match(JsDocToken.LT)) { + next(); + skipEOLs(); + Node memberType = parseTypeExpressionList(next()); + if (memberType != null) { + typeNameNode.addChildToFront(memberType); + + skipEOLs(); + if (!match(JsDocToken.GT)) { + return reportTypeSyntaxWarning("msg.jsdoc.missing.gt"); + } + + next(); + } + } + return typeNameNode; + } + + /** + * FunctionType := 'function' FunctionSignatureType + * FunctionSignatureType := + * TypeParameters '(' 'this' ':' TypeName, ParametersType ')' ResultType + */ + private Node parseFunctionType(JsDocToken token) { + // NOTE(nicksantos): We're not implementing generics at the moment, so + // just throw out TypeParameters. + if (token != JsDocToken.LP) { + restoreLookAhead(token); + return reportTypeSyntaxWarning("msg.jsdoc.missing.lp"); + } + + Node functionType = newNode(Token.FUNCTION); + Node parameters = null; + skipEOLs(); + if (!match(JsDocToken.RP)) { + token = next(); + + boolean hasParams = true; + if (token == JsDocToken.STRING) { + String tokenStr = stream.getString(); + boolean isThis = "this".equals(tokenStr); + boolean isNew = "new".equals(tokenStr); + if (isThis || isNew) { + if (match(JsDocToken.COLON)) { + next(); + skipEOLs(); + Node contextType = wrapNode( + isThis ? Token.THIS : Token.NEW, + parseTypeName(next())); + if (contextType == null) { + return null; + } + + functionType.addChildToFront(contextType); + } else { + return reportTypeSyntaxWarning("msg.jsdoc.missing.colon"); + } + + if (match(JsDocToken.COMMA)) { + next(); + skipEOLs(); + token = next(); + } else { + hasParams = false; + } + } + } + + if (hasParams) { + parameters = parseParametersType(token); + if (parameters == null) { + return null; + } + } + } + + if (parameters != null) { + functionType.addChildToBack(parameters); + } + + skipEOLs(); + if (!match(JsDocToken.RP)) { + return reportTypeSyntaxWarning("msg.jsdoc.missing.rp"); + } + + skipEOLs(); + Node resultType = parseResultType(next()); + if (resultType == null) { + return null; + } else { + functionType.addChildToBack(resultType); + } + return functionType; + } + + /** + * ParametersType := RestParameterType | NonRestParametersType + * | NonRestParametersType ',' RestParameterType + * RestParameterType := '...' Identifier + * NonRestParametersType := ParameterType ',' NonRestParametersType + * | ParameterType + * | OptionalParametersType + * OptionalParametersType := OptionalParameterType + * | OptionalParameterType, OptionalParametersType + * OptionalParameterType := ParameterType= + * ParameterType := TypeExpression | Identifier ':' TypeExpression + */ + // NOTE(nicksantos): The official ES4 grammar forces optional and rest + // arguments to come after the required arguments. Our parser does not + // enforce this. Instead we allow them anywhere in the function at parse-time, + // and then warn about them during type resolution. + // + // In theory, it might be mathematically nicer to do the order-checking here. + // But in practice, the order-checking for structural functions is exactly + // the same as the order-checking for @param annotations. And the latter + // has to happen during type resolution. Rather than duplicate the + // order-checking in two places, we just do all of it in type resolution. + private Node parseParametersType(JsDocToken token) { + Node paramsType = newNode(Token.PARAM_LIST); + boolean isVarArgs = false; + Node paramType = null; + if (token != JsDocToken.RP) { + do { + if (paramType != null) { + // skip past the comma + next(); + skipEOLs(); + token = next(); + } + + if (token == JsDocToken.ELLIPSIS) { + // In the latest ES4 proposal, there are no type constraints allowed + // on variable arguments. We support the old syntax for backwards + // compatibility, but we should gradually tear it out. + skipEOLs(); + if (match(JsDocToken.RP)) { + paramType = newNode(Token.ELLIPSIS); + } else { + skipEOLs(); + if (!match(JsDocToken.LB)) { + return reportTypeSyntaxWarning("msg.jsdoc.missing.lb"); + } + + next(); + skipEOLs(); + paramType = wrapNode(Token.ELLIPSIS, parseTypeExpression(next())); + skipEOLs(); + if (!match(JsDocToken.RB)) { + return reportTypeSyntaxWarning("msg.jsdoc.missing.rb"); + } + skipEOLs(); + next(); + } + + isVarArgs = true; + } else { + paramType = parseTypeExpression(token); + if (match(JsDocToken.EQUALS)) { + skipEOLs(); + next(); + paramType = wrapNode(Token.EQUALS, paramType); + } + } + + if (paramType == null) { + return null; + } + paramsType.addChildToBack(paramType); + if (isVarArgs) { + break; + } + } while (match(JsDocToken.COMMA)); + } + + if (isVarArgs && match(JsDocToken.COMMA)) { + return reportTypeSyntaxWarning("msg.jsdoc.function.varargs"); + } + + // The right paren will be checked by parseFunctionType + + return paramsType; + } + + /** + * ResultType := | ':' void | ':' TypeExpression + */ + private Node parseResultType(JsDocToken token) { + skipEOLs(); + if (!match(JsDocToken.COLON)) { + return newNode(Token.EMPTY); + } + + token = next(); + skipEOLs(); + if (match(JsDocToken.STRING) && "void".equals(stream.getString())) { + next(); + return newNode(Token.VOID); + } else { + return parseTypeExpression(next()); + } + } + + /** + * UnionType := '(' TypeUnionList ')' + * TypeUnionList := TypeExpression | TypeExpression '|' TypeUnionList + * + * We've removed the empty union type. + */ + private Node parseUnionType(JsDocToken token) { + return parseUnionTypeWithAlternate(token, null); + } + + /** + * Create a new union type, with an alternate that has already been + * parsed. The alternate may be null. + */ + private Node parseUnionTypeWithAlternate(JsDocToken token, Node alternate) { + Node union = newNode(Token.PIPE); + if (alternate != null) { + union.addChildToBack(alternate); + } + + Node expr = null; + do { + if (expr != null) { + skipEOLs(); + token = next(); + Preconditions.checkState( + token == JsDocToken.PIPE || token == JsDocToken.COMMA); + + boolean isPipe = token == JsDocToken.PIPE; + if (isPipe && match(JsDocToken.PIPE)) { + // We support double pipes for backwards compatibility. + next(); + } + skipEOLs(); + token = next(); + } + expr = parseTypeExpression(token); + if (expr == null) { + return null; + } + + union.addChildToBack(expr); + // We support commas for backwards compatibility. + } while (match(JsDocToken.PIPE, JsDocToken.COMMA)); + + if (alternate == null) { + skipEOLs(); + if (!match(JsDocToken.RP)) { + return reportTypeSyntaxWarning("msg.jsdoc.missing.rp"); + } + next(); + } + return union; + } + + /** + * ArrayType := '[' ElementTypeList ']' + * ElementTypeList := | TypeExpression | '...' TypeExpression + * | TypeExpression ',' ElementTypeList + */ + private Node parseArrayType(JsDocToken token) { + Node array = newNode(Token.LB); + Node arg = null; + boolean hasVarArgs = false; + + do { + if (arg != null) { + next(); + skipEOLs(); + token = next(); + } + if (token == JsDocToken.ELLIPSIS) { + arg = wrapNode(Token.ELLIPSIS, parseTypeExpression(next())); + hasVarArgs = true; + } else { + arg = parseTypeExpression(token); + } + + if (arg == null) { + return null; + } + + array.addChildToBack(arg); + if (hasVarArgs) { + break; + } + skipEOLs(); + } while (match(JsDocToken.COMMA)); + + if (!match(JsDocToken.RB)) { + return reportTypeSyntaxWarning("msg.jsdoc.missing.rb"); + } + next(); + return array; + } + + /** + * RecordType := '{' FieldTypeList '}' + */ + private Node parseRecordType(JsDocToken token) { + Node recordType = newNode(Token.LC); + Node fieldTypeList = parseFieldTypeList(token); + + if (fieldTypeList == null) { + return reportGenericTypeSyntaxWarning(); + } + + skipEOLs(); + if (!match(JsDocToken.RC)) { + return reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); + } + + next(); + + recordType.addChildToBack(fieldTypeList); + return recordType; + } + + /** + * FieldTypeList := FieldType | FieldType ',' FieldTypeList + */ + private Node parseFieldTypeList(JsDocToken token) { + Node fieldTypeList = newNode(Token.LB); + + do { + Node fieldType = parseFieldType(token); + + if (fieldType == null) { + return null; + } + + fieldTypeList.addChildToBack(fieldType); + + skipEOLs(); + if (!match(JsDocToken.COMMA)) { + break; + } + + // Move to the comma token. + next(); + + // Move to the token passed the comma. + skipEOLs(); + token = next(); + } while (true); + + return fieldTypeList; + } + + /** + * FieldType := FieldName | FieldName ':' TypeExpression + */ + private Node parseFieldType(JsDocToken token) { + Node fieldName = parseFieldName(token); + + if (fieldName == null) { + return null; + } + + skipEOLs(); + if (!match(JsDocToken.COLON)) { + return fieldName; + } + + // Move to the colon. + next(); + + // Move to the token after the colon and parse + // the type expression. + skipEOLs(); + Node typeExpression = parseTypeExpression(next()); + + if (typeExpression == null) { + return null; + } + + Node fieldType = newNode(Token.COLON); + fieldType.addChildToBack(fieldName); + fieldType.addChildToBack(typeExpression); + return fieldType; + } + + /** + * FieldName := NameExpression | StringLiteral | NumberLiteral | + * ReservedIdentifier + */ + private Node parseFieldName(JsDocToken token) { + switch (token) { + case STRING: + String string = stream.getString(); + return newStringNode(string); + + default: + return null; + } + } + + private Node wrapNode(int type, Node n) { + return n == null ? null : + new Node(type, n, stream.getLineno(), + stream.getCharno()).clonePropsFrom(templateNode); + } + + private Node newNode(int type) { + return new Node(type, stream.getLineno(), + stream.getCharno()).clonePropsFrom(templateNode); + } + + private Node newStringNode(String s) { + return newStringNode(s, stream.getLineno(), stream.getCharno()); + } + + private Node newStringNode(String s, int lineno, int charno) { + Node n = Node.newString(s, lineno, charno).clonePropsFrom(templateNode); + n.setLength(s.length()); + return n; + } + + // This is similar to IRFactory.createTemplateNode to share common props + // e.g., source-name, between all nodes. + private Node createTemplateNode() { + // The Node type choice is arbitrary. + Node templateNode = IR.script(); + templateNode.setStaticSourceFile( + this.associatedNode != null ? + this.associatedNode.getStaticSourceFile() : + null); + return templateNode; + } + + private Node reportTypeSyntaxWarning(String warning) { + parser.addTypeWarning(warning, stream.getLineno(), stream.getCharno()); + return null; + } + + private Node reportGenericTypeSyntaxWarning() { + return reportTypeSyntaxWarning("msg.jsdoc.type.syntax"); + } + + /** + * Eats tokens until {@link JsDocToken#EOL} included, and switches back the + * state to {@link State#SEARCHING_ANNOTATION}. + */ + private JsDocToken eatTokensUntilEOL() { + return eatTokensUntilEOL(next()); + } + + /** + * Eats tokens until {@link JsDocToken#EOL} included, and switches back the + * state to {@link State#SEARCHING_ANNOTATION}. + */ + private JsDocToken eatTokensUntilEOL(JsDocToken token) { + do { + if (token == JsDocToken.EOL || token == JsDocToken.EOC || + token == JsDocToken.EOF) { + state = State.SEARCHING_ANNOTATION; + return token; + } + token = next(); + } while (true); + } + + /** + * Specific value indicating that the {@link #unreadToken} contains no token. + */ + private static final JsDocToken NO_UNREAD_TOKEN = null; + + /** + * One token buffer. + */ + private JsDocToken unreadToken = NO_UNREAD_TOKEN; + + /** Restores the lookahead token to the token stream */ + private void restoreLookAhead(JsDocToken token) { + unreadToken = token; + } + + /** + * Tests whether the next symbol of the token stream matches the specific + * token. + */ + private boolean match(JsDocToken token) { + unreadToken = next(); + return unreadToken == token; + } + + /** + * Tests that the next symbol of the token stream matches one of the specified + * tokens. + */ + private boolean match(JsDocToken token1, JsDocToken token2) { + unreadToken = next(); + return unreadToken == token1 || unreadToken == token2; + } + + /** + * Gets the next token of the token stream or the buffered token if a matching + * was previously made. + */ + private JsDocToken next() { + if (unreadToken == NO_UNREAD_TOKEN) { + return stream.getJsDocToken(); + } else { + return current(); + } + } + + /** + * Gets the current token, invalidating it in the process. + */ + private JsDocToken current() { + JsDocToken t = unreadToken; + unreadToken = NO_UNREAD_TOKEN; + return t; + } + + /** + * Skips all EOLs and all empty lines in the JSDoc. Call this method if you + * want the JSDoc entry to span multiple lines. + */ + private void skipEOLs() { + while (match(JsDocToken.EOL)) { + next(); + if (match(JsDocToken.STAR)) { + next(); + } + } + } + + /** + * Determines whether the parser has been populated with docinfo with a + * fileoverview tag. + */ + private boolean hasParsedFileOverviewDocInfo() { + return jsdocBuilder.isPopulatedWithFileOverview(); + } + + boolean hasParsedJSDocInfo() { + return jsdocBuilder.isPopulated(); + } + + JSDocInfo retrieveAndResetParsedJSDocInfo() { + return jsdocBuilder.build(associatedNode); + } + + /** + * Gets the fileoverview JSDocInfo, if any. + */ + JSDocInfo getFileOverviewJSDocInfo() { + return fileOverviewJSDocInfo; + } + + /** + * Look ahead for a type annotation by advancing the character stream. + * Does not modify the token stream. + * This is kind of a hack, and is only necessary because we use the token + * stream to parse types, but need the underlying character stream to get + * JsDoc descriptions. + * @return Whether we found a type annotation. + */ + private boolean lookAheadForTypeAnnotation() { + boolean matchedLc = false; + int c; + while (true) { + c = stream.getChar(); + if (c == ' ') { + continue; + } else if (c == '{') { + matchedLc = true; + break; + } else { + break; + } + } + stream.ungetChar(c); + return matchedLc; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/JsDocToken.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/JsDocToken.java new file mode 100644 index 0000000..0e700fe --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/JsDocToken.java @@ -0,0 +1,49 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.parsing; + +/** + * JSDoc-specific tokens. + * + * This class is based on Rhino's Token. + * + */ +enum JsDocToken { + // Tokens recycled from Rhino + EOF, // end of file token - (not EOF_CHAR) + EOL, // end of line + LT, + GT, + STRING, + LB, // left and right brackets + RB, + LC, // left and right curlies (braces) + RC, + LP, // left and right parentheses + RP, + COMMA, // comma operator + COLON, + // JsDoc-only tokens + ANNOTATION, + PIPE, + STAR, + EOC, + QMARK, + ELLIPSIS, + BANG, + EQUALS; +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/JsDocTokenStream.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/JsDocTokenStream.java new file mode 100644 index 0000000..0fc2b3b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/parsing/JsDocTokenStream.java @@ -0,0 +1,487 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.parsing; + +import com.google.common.base.Preconditions; +import com.google.javascript.rhino.head.ScriptRuntime; + +/** + * This class implements the scanner for JsDoc strings. + * + * It is heavily based on Rhino's TokenStream. + * + */ +class JsDocTokenStream { + /* + * For chars - because we need something out-of-range + * to check. (And checking EOF by exception is annoying.) + * Note distinction from EOF token type! + */ + private final static int + EOF_CHAR = -1; + + JsDocTokenStream(String sourceString) { + this(sourceString, 0); + } + + JsDocTokenStream(String sourceString, int lineno) { + this(sourceString, lineno, 0); + } + + JsDocTokenStream(String sourceString, int lineno, int initCharno) { + Preconditions.checkNotNull(sourceString); + this.lineno = lineno; + this.sourceString = sourceString; + this.sourceEnd = sourceString.length(); + this.sourceCursor = this.cursor = 0; + this.initLineno = lineno; + this.initCharno = initCharno; + } + + /** + * Tokenizes JSDoc comments. + */ + @SuppressWarnings("fallthrough") + final JsDocToken getJsDocToken() { + int c; + stringBufferTop = 0; + for (;;) { + // eat white spaces + for (;;) { + charno = -1; + c = getChar(); + if (c == EOF_CHAR) { + return JsDocToken.EOF; + } else if (c == '\n') { + return JsDocToken.EOL; + } else if (!isJSSpace(c)) { + break; + } + } + + switch (c) { + // annotation, e.g. @type or @constructor + case '@': + do { + c = getChar(); + if (isAlpha(c)) { + addToString(c); + } else { + ungetChar(c); + this.string = getStringFromBuffer(); + stringBufferTop = 0; + return JsDocToken.ANNOTATION; + } + } while (true); + + case '*': + if (matchChar('/')) { + return JsDocToken.EOC; + } else { + return JsDocToken.STAR; + } + + case ',': + return JsDocToken.COMMA; + + case '>': + return JsDocToken.GT; + + case '(': + return JsDocToken.LP; + + case ')': + return JsDocToken.RP; + + case '{': + return JsDocToken.LC; + + case '}': + return JsDocToken.RC; + + case '[': + return JsDocToken.LB; + + case ']': + return JsDocToken.RB; + + case '?': + return JsDocToken.QMARK; + + case '!': + return JsDocToken.BANG; + + case ':': + return JsDocToken.COLON; + + case '=': + return JsDocToken.EQUALS; + + case '|': + matchChar('|'); + return JsDocToken.PIPE; + + case '.': + c = getChar(); + if (c == '<') { + return JsDocToken.LT; + } else { + if (c == '.') { + c = getChar(); + if (c == '.') { + return JsDocToken.ELLIPSIS; + } else { + addToString('.'); + } + } + // we may backtrack across line boundary + ungetBuffer[ungetCursor++] = c; + c = '.'; + } + // fall through + + default: { + // recognize a JsDoc string but discard last . if it is followed by + // a non-JsDoc comment char, e.g. Array.< + int c1 = c; + addToString(c); + int c2 = getChar(); + if (!isJSDocString(c2)) { + ungetChar(c2); + this.string = getStringFromBuffer(); + stringBufferTop = 0; + return JsDocToken.STRING; + } else { + do { + c1 = c2; + c2 = getChar(); + if (c1 == '.' && c2 == '<') { + ungetChar(c2); + ungetChar(c1); + this.string = getStringFromBuffer(); + stringBufferTop = 0; + return JsDocToken.STRING; + } else { + if (isJSDocString(c2)) { + addToString(c1); + } else { + ungetChar(c2); + addToString(c1); + this.string = getStringFromBuffer(); + stringBufferTop = 0; + return JsDocToken.STRING; + } + } + } while (true); + } + } + } + } + } + + /** + * Gets the remaining JSDoc line without the {@link JsDocToken#EOL}, + * {@link JsDocToken#EOF} or {@link JsDocToken#EOC}. + */ + @SuppressWarnings("fallthrough") + String getRemainingJSDocLine() { + int c; + for (;;) { + c = getChar(); + switch (c) { + case '*': + if (peekChar() != '/') { + addToString(c); + break; + } + // fall through + case EOF_CHAR: + case '\n': + ungetChar(c); + this.string = getStringFromBuffer(); + stringBufferTop = 0; + return this.string; + + default: + addToString(c); + break; + } + } + } + + final int getLineno() { return lineno; } + + final int getCharno() { + return lineno == initLineno? initCharno + charno : charno; + } + + final String getString() { return string; } + + final boolean eof() { return hitEOF; } + + private String getStringFromBuffer() { + tokenEnd = cursor; + return new String(stringBuffer, 0, stringBufferTop); + } + + private void addToString(int c) { + int N = stringBufferTop; + if (N == stringBuffer.length) { + char[] tmp = new char[stringBuffer.length * 2]; + System.arraycopy(stringBuffer, 0, tmp, 0, N); + stringBuffer = tmp; + } + stringBuffer[N] = (char)c; + stringBufferTop = N + 1; + } + + void ungetChar(int c) { + // can not unread past across line boundary + assert(!(ungetCursor != 0 && ungetBuffer[ungetCursor - 1] == '\n')); + ungetBuffer[ungetCursor++] = c; + cursor--; + } + + private boolean matchChar(int test) { + int c = getCharIgnoreLineEnd(); + if (c == test) { + tokenEnd = cursor; + return true; + } else { + ungetCharIgnoreLineEnd(c); + return false; + } + } + + private static boolean isAlpha(int c) { + // Use 'Z' < 'a' + if (c <= 'Z') { + return 'A' <= c; + } else { + return 'a' <= c && c <= 'z'; + } + } + + private boolean isJSDocString(int c) { + switch (c) { + case '@': + case '*': + case ',': + case '>': + case ':': + case '(': + case ')': + case '{': + case '}': + case '[': + case ']': + case '?': + case '!': + case '|': + case '=': + case EOF_CHAR: + case '\n': + return false; + + default: + return !isJSSpace(c); + } + } + + /* As defined in ECMA. jsscan.c uses C isspace() (which allows + * \v, I think.) note that code in getChar() implicitly accepts + * '\r' == \u000D as well. + */ + static boolean isJSSpace(int c) { + if (c <= 127) { + return c == 0x20 || c == 0x9 || c == 0xC || c == 0xB; + } else { + return c == 0xA0 + || Character.getType((char)c) == Character.SPACE_SEPARATOR; + } + } + + private static boolean isJSFormatChar(int c) { + return c > 127 && Character.getType((char)c) == Character.FORMAT; + } + + /** + * Allows the JSDocParser to update the character offset + * so that getCharno() returns a valid character position. + */ + void update() { + charno = getOffset(); + } + + private int peekChar() { + int c = getChar(); + ungetChar(c); + return c; + } + + protected int getChar() { + if (ungetCursor != 0) { + cursor++; + --ungetCursor; + if (charno == -1) { + charno = getOffset(); + } + return ungetBuffer[ungetCursor]; + } + + for(;;) { + int c; + if (sourceCursor == sourceEnd) { + hitEOF = true; + if (charno == -1) { + charno = getOffset(); + } + return EOF_CHAR; + } + cursor++; + c = sourceString.charAt(sourceCursor++); + + + if (lineEndChar >= 0) { + if (lineEndChar == '\r' && c == '\n') { + lineEndChar = '\n'; + continue; + } + lineEndChar = -1; + lineStart = sourceCursor - 1; + lineno++; + } + + if (c <= 127) { + if (c == '\n' || c == '\r') { + lineEndChar = c; + c = '\n'; + } + } else { + if (isJSFormatChar(c)) { + continue; + } + if (ScriptRuntime.isJSLineTerminator(c)) { + lineEndChar = c; + c = '\n'; + } + } + + if (charno == -1) { + charno = getOffset(); + } + + return c; + } + } + + private int getCharIgnoreLineEnd() { + if (ungetCursor != 0) { + cursor++; + --ungetCursor; + if (charno == -1) { + charno = getOffset(); + } + return ungetBuffer[ungetCursor]; + } + + for(;;) { + int c; + if (sourceCursor == sourceEnd) { + hitEOF = true; + if (charno == -1) { + charno = getOffset(); + } + return EOF_CHAR; + } + cursor++; + c = sourceString.charAt(sourceCursor++); + + + if (c <= 127) { + if (c == '\n' || c == '\r') { + lineEndChar = c; + c = '\n'; + } + } else { + if (isJSFormatChar(c)) { + continue; + } + if (ScriptRuntime.isJSLineTerminator(c)) { + lineEndChar = c; + c = '\n'; + } + } + + if (charno == -1) { + charno = getOffset(); + } + + return c; + } + } + + private void ungetCharIgnoreLineEnd(int c) { + ungetBuffer[ungetCursor++] = c; + cursor--; + } + + /** + * Returns the offset into the current line. + */ + final int getOffset() { + return sourceCursor - lineStart - ungetCursor - 1; + } + + // Set this to an initial non-null value so that the Parser has + // something to retrieve even if an error has occurred and no + // string is found. Fosters one class of error, but saves lots of + // code. + private String string = ""; + + private char[] stringBuffer = new char[128]; + private int stringBufferTop; + + // Room to backtrace from to < on failed match of the last - in + + + + + +Provides utilities to help with parsing JSDoc annotations and performing AST +transformations. + + + diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/regex/CaseCanonicalize.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/regex/CaseCanonicalize.java new file mode 100644 index 0000000..26a4644 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/regex/CaseCanonicalize.java @@ -0,0 +1,631 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.regex; + +import com.google.common.collect.ImmutableList; + +/** + * Implements the ECMAScript 5 + * Canonicalize operation + * used to specify how case-insensitive regular expressions match. + * + *

      + * From section 15.10.2.9, + *

      + * The abstract operation Canonicalize takes a character parameter ch and + * performs the following steps: + *
        + *
      • If IgnoreCase is false, return ch. + *
      • Let u be ch converted to upper case as if by calling the standard + * built-in method {@code String.prototype.toUpperCase} on the one-character + * String ch. + *
      • If u does not consist of a single character, return ch. + *
      • Let cu be u's character. + *
      • If ch's code unit value is greater than or equal to decimal 128 and + * cu's code unit value is less than decimal 128, then return ch. + *
      • Return cu. + *
      + * + * @author Mike Samuel + */ +public final class CaseCanonicalize { + + private CaseCanonicalize() { + // Uninstantiable. + } + + // Below are tables that implement the Canonicalize operation. + // We cannot use java.lang.Character.toUpperCase since that is based on + // a more modern version of Unicode than that required by the ECMAScript spec. + + /** + * Set of code units that are case-insensitively equivalent to some other + * code unit according to the EcmaScript + * Canonicalize operation + * described in section 15.10.2.8. + * The case sensitive characters are the ones that canonicalize to a character + * other than themselves or have a character that canonicalizes to them. + * Canonicalize is based on the definition of + * {@code String.prototype.toUpperCase} which is itself based on Unicode 3.0.0 + * as specified at + * + * UnicodeData-3.0.0 + * + * and + * SpecialCasings-2.txt + * . + * + *

      + * This table was generated by running the below on Chrome: + *

      + *
      +   * for (var cc = 0; cc < 0x10000; ++cc) {
      +   *   var ch = String.fromCharCode(cc);
      +   *   var u = ch.toUpperCase();
      +   *   if (ch != u && u.length === 1) {
      +   *     var cu = u.charCodeAt(0);
      +   *     if (cc <= 128 || u.charCodeAt(0) > 128) {
      +   *       print('0x' + cc.toString(16) + ', 0x' + cu.toString(16) + ',');
      +   *     }
      +   *   }
      +   * }
      +   * 
      + */ + public static final CharRanges CASE_SENSITIVE = CharRanges.withRanges( + 0x41, 0x5b, + 0x61, 0x7b, + 0xb5, 0xb6, + 0xc0, 0xd7, + 0xd8, 0xdf, + 0xe0, 0xf7, + 0xf8, 0x130, + 0x132, 0x138, + 0x139, 0x149, + 0x14a, 0x17f, + 0x180, 0x18d, + 0x18e, 0x19b, + 0x19c, 0x1aa, + 0x1ac, 0x1ba, + 0x1bc, 0x1be, + 0x1bf, 0x1c0, + 0x1c4, 0x1f0, + 0x1f1, 0x221, + 0x222, 0x234, + 0x23a, 0x23f, + 0x241, 0x250, + 0x253, 0x255, + 0x256, 0x258, + 0x259, 0x25a, + 0x25b, 0x25c, + 0x260, 0x261, + 0x263, 0x264, + 0x268, 0x26a, + 0x26b, 0x26c, + 0x26f, 0x270, + 0x272, 0x273, + 0x275, 0x276, + 0x27d, 0x27e, + 0x280, 0x281, + 0x283, 0x284, + 0x288, 0x28d, + 0x292, 0x293, + 0x345, 0x346, + 0x37b, 0x37e, + 0x386, 0x387, + 0x388, 0x38b, + 0x38c, 0x38d, + 0x38e, 0x390, + 0x391, 0x3a2, + 0x3a3, 0x3b0, + 0x3b1, 0x3cf, + 0x3d0, 0x3d2, + 0x3d5, 0x3d7, + 0x3d8, 0x3f3, + 0x3f5, 0x3f6, + 0x3f7, 0x3fc, + 0x3fd, 0x482, + 0x48a, 0x514, + 0x531, 0x557, + 0x561, 0x587, + 0x10a0, 0x10c6, + 0x1d7d, 0x1d7e, + 0x1e00, 0x1e96, + 0x1e9b, 0x1e9c, + 0x1ea0, 0x1efa, + 0x1f00, 0x1f16, + 0x1f18, 0x1f1e, + 0x1f20, 0x1f46, + 0x1f48, 0x1f4e, + 0x1f51, 0x1f52, + 0x1f53, 0x1f54, + 0x1f55, 0x1f56, + 0x1f57, 0x1f58, + 0x1f59, 0x1f5a, + 0x1f5b, 0x1f5c, + 0x1f5d, 0x1f5e, + 0x1f5f, 0x1f7e, + 0x1fb0, 0x1fb2, + 0x1fb8, 0x1fbc, + 0x1fbe, 0x1fbf, + 0x1fc8, 0x1fcc, + 0x1fd0, 0x1fd2, + 0x1fd8, 0x1fdc, + 0x1fe0, 0x1fe2, + 0x1fe5, 0x1fe6, + 0x1fe8, 0x1fed, + 0x1ff8, 0x1ffc, + 0x2132, 0x2133, + 0x214e, 0x214f, + 0x2160, 0x2180, + 0x2183, 0x2185, + 0x24b6, 0x24ea, + 0x2c00, 0x2c2f, + 0x2c30, 0x2c5f, + 0x2c60, 0x2c6d, + 0x2c75, 0x2c77, + 0x2c80, 0x2ce4, + 0x2d00, 0x2d26, + 0xff21, 0xff3b, + 0xff41, 0xff5b + ); + + + /** + * Returns the case canonical version of the given string. + */ + public static String caseCanonicalize(String s) { + for (int i = 0, n = s.length(); i < n; ++i) { + char ch = s.charAt(i); + char cu = caseCanonicalize(ch); + if (cu != ch) { + StringBuilder sb = new StringBuilder(s); + sb.setCharAt(i, cu); + while (++i < n) { + sb.setCharAt(i, caseCanonicalize(s.charAt(i))); + } + return sb.toString(); + } + } + return s; + } + + /** + * Returns the case canonical version of the given code-unit. ECMAScript 5 + * explicitly says that code-units are to be treated as their code-point + * equivalent, even surrogates. + */ + public static char caseCanonicalize(char ch) { + if (ch < 0x80) { // Normal case. + return ('A' <= ch && ch <= 'Z') ? (char) (ch | 32) : ch; + } + // Non-ASCII case. + if (CASE_SENSITIVE.contains(ch)) { + for (DeltaSet ds : CANON_DELTA_SETS) { + if (ds.codeUnits.contains(ch)) { + return (char) (ch - ds.delta); + } + } + } + return ch; + } + + /** + * Given a character range that may include case sensitive code-units, + * such as {@code [0-9B-M]}, returns the character range that includes all + * the code-units in the input and those that are case-insensitively + * equivalent to a code-unit in the input. + */ + public static CharRanges expandToAllMatched(CharRanges ranges) { + CharRanges caseSensitive = ranges.intersection(CASE_SENSITIVE); + if (caseSensitive.isEmpty()) { return ranges; } + CharRanges expanded = CharRanges.EMPTY; + for (DeltaSet ds : DELTA_SETS) { + expanded = expanded.union( + caseSensitive.intersection(ds.codeUnits).shift(-ds.delta)); + } + return ranges.union(expanded); + } + + + private static final CharRanges UCASE_ASCII_LETTERS + = CharRanges.inclusive('A', 'Z'); + + /** + * Given a character range that may include case sensitive code-units, + * such as {@code [0-9B-M]}, returns the character range that includes + * the minimal set of code units such that for every code unit in the + * input there is a case-sensitively equivalent canonical code unit in the + * output. + */ + public static CharRanges reduceToMinimum(CharRanges ranges) { + CharRanges caseSensitive = ranges.intersection(CASE_SENSITIVE); + if (caseSensitive.isEmpty()) { return ranges; } + CharRanges expanded = CharRanges.EMPTY; + for (DeltaSet ds : CANON_DELTA_SETS) { + expanded = expanded.union( + caseSensitive.intersection(ds.codeUnits).shift(-ds.delta)); + } + // Letters a-z gzip better than uppercase A-Z since JavaScript keywords + // are lower-case, so, even though the definition of Canonicalize is + // based on String.prototype.toUpperCase, we use lowercase ASCII characters + // in the minimal form. + expanded = expanded.difference(UCASE_ASCII_LETTERS).union( + expanded.intersection(UCASE_ASCII_LETTERS).shift(32)); + return ranges.difference(caseSensitive).union(expanded); + } + + /** + * Sets of code units broken down by delta that are case-insensitively + * equivalent to another code unit that differs from the first by that delta. + */ + private static final ImmutableList DELTA_SETS = ImmutableList.of( + new DeltaSet(-10795, CharRanges.withMembers(0x23a)), + new DeltaSet(-10792, CharRanges.withMembers(0x23e)), + new DeltaSet(-10743, CharRanges.withMembers(0x26b)), + new DeltaSet(-10727, CharRanges.withMembers(0x27d)), + new DeltaSet(-7264, CharRanges.withRanges(0x10a0, 0x10c6)), + new DeltaSet(-7205, CharRanges.withMembers(0x399)), + new DeltaSet(-3814, CharRanges.withMembers(0x1d7d)), + new DeltaSet(-743, CharRanges.withMembers(0xb5)), + new DeltaSet(-219, CharRanges.withMembers(0x1b7)), + new DeltaSet(-218, CharRanges.withMembers(0x1a6, 0x1a9, 0x1ae)), + new DeltaSet(-217, CharRanges.withRanges(0x1b1, 0x1b3)), + new DeltaSet(-214, CharRanges.withMembers(0x19f)), + new DeltaSet(-213, CharRanges.withMembers(0x19d)), + new DeltaSet(-211, CharRanges.withMembers(0x196, 0x19c)), + new DeltaSet(-210, CharRanges.withMembers(0x181)), + new DeltaSet(-209, CharRanges.withMembers(0x197)), + new DeltaSet(-207, CharRanges.withMembers(0x194)), + new DeltaSet(-206, CharRanges.withMembers(0x186)), + new DeltaSet(-205, CharRanges.withRanges(0x189, 0x18b, 0x193, 0x194)), + new DeltaSet(-203, CharRanges.withMembers(0x190)), + new DeltaSet(-202, CharRanges.withMembers(0x18f)), + new DeltaSet(-195, CharRanges.withMembers(0x180)), + new DeltaSet(-163, CharRanges.withMembers(0x19a)), + new DeltaSet(-130, CharRanges.withRanges(0x19e, 0x19f, 0x37b, 0x37e)), + new DeltaSet(-128, CharRanges.withRanges(0x1f78, 0x1f7a)), + new DeltaSet(-126, CharRanges.withRanges(0x1f7c, 0x1f7e)), + new DeltaSet(-121, CharRanges.withMembers(0xff)), + new DeltaSet(-112, CharRanges.withRanges(0x1f7a, 0x1f7c)), + new DeltaSet(-100, CharRanges.withRanges(0x1f76, 0x1f78)), + new DeltaSet(-97, CharRanges.withMembers(0x195)), + new DeltaSet(-96, CharRanges.withMembers(0x395)), + new DeltaSet(-86, CharRanges.withRanges(0x39a, 0x39b, 0x1f72, 0x1f76)), + new DeltaSet(-84, CharRanges.withMembers(0x345)), + new DeltaSet(-80, CharRanges.withRanges(0x3a1, 0x3a2, 0x400, 0x410)), + new DeltaSet(-79, CharRanges.withMembers(0x18e)), + new DeltaSet(-74, CharRanges.withRanges(0x1f70, 0x1f72)), + new DeltaSet(-71, CharRanges.withMembers(0x245)), + new DeltaSet(-69, CharRanges.withMembers(0x244)), + new DeltaSet(-64, CharRanges.withMembers(0x38c)), + new DeltaSet(-63, CharRanges.withRanges(0x38e, 0x390)), + new DeltaSet(-62, CharRanges.withMembers(0x392)), + new DeltaSet(-59, CharRanges.withMembers(0x1e60)), + new DeltaSet(-57, CharRanges.withMembers(0x398)), + new DeltaSet(-56, CharRanges.withMembers(0x1bf)), + new DeltaSet(-54, CharRanges.withMembers(0x3a0)), + new DeltaSet(-48, CharRanges.withRanges(0x531, 0x557, 0x2c00, 0x2c2f)), + new DeltaSet(-47, CharRanges.withMembers(0x3a6)), + new DeltaSet(-38, CharRanges.withMembers(0x386)), + new DeltaSet(-37, CharRanges.withRanges(0x388, 0x38b)), + new DeltaSet(-32, CharRanges.withRanges( + 0x41, 0x5b, 0xc0, 0xd7, 0xd8, 0xdf, 0x391, 0x3a2, 0x3a3, 0x3ac, + 0x410, 0x430, 0xff21, 0xff3b)), + new DeltaSet(-31, CharRanges.withMembers(0x3a3)), + new DeltaSet(-28, CharRanges.withMembers(0x2132)), + new DeltaSet(-26, CharRanges.withRanges(0x24b6, 0x24d0)), + new DeltaSet(-16, CharRanges.withRanges(0x2160, 0x2170)), + new DeltaSet(-15, CharRanges.withMembers(0x4c0)), + new DeltaSet(-8, CharRanges.withRanges( + 0x1f00, 0x1f08, 0x1f10, 0x1f16, 0x1f20, 0x1f28, 0x1f30, 0x1f38, + 0x1f40, 0x1f46, 0x1f51, 0x1f52, 0x1f53, 0x1f54, 0x1f55, 0x1f56, + 0x1f57, 0x1f58, 0x1f60, 0x1f68, 0x1fb0, 0x1fb2, 0x1fd0, 0x1fd2, + 0x1fe0, 0x1fe2)), + new DeltaSet(-7, CharRanges.withMembers(0x3f2, 0x1fe5)), + new DeltaSet(-2, CharRanges.withMembers(0x1c4, 0x1c7, 0x1ca, 0x1f1)), + new DeltaSet(-1, CharRanges.withMembers( + 0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e, 0x110, 0x112, + 0x114, 0x116, 0x118, 0x11a, 0x11c, 0x11e, 0x120, 0x122, 0x124, 0x126, + 0x128, 0x12a, 0x12c, 0x12e, 0x132, 0x134, 0x136, 0x139, 0x13b, 0x13d, + 0x13f, 0x141, 0x143, 0x145, 0x147, 0x14a, 0x14c, 0x14e, 0x150, 0x152, + 0x154, 0x156, 0x158, 0x15a, 0x15c, 0x15e, 0x160, 0x162, 0x164, 0x166, + 0x168, 0x16a, 0x16c, 0x16e, 0x170, 0x172, 0x174, 0x176, 0x179, 0x17b, + 0x17d, 0x182, 0x184, 0x187, 0x18b, 0x191, 0x198, 0x1a0, 0x1a2, 0x1a4, + 0x1a7, 0x1ac, 0x1af, 0x1b3, 0x1b5, 0x1b8, 0x1bc, 0x1c4, 0x1c7, 0x1ca, + 0x1cd, 0x1cf, 0x1d1, 0x1d3, 0x1d5, 0x1d7, 0x1d9, 0x1db, 0x1de, 0x1e0, + 0x1e2, 0x1e4, 0x1e6, 0x1e8, 0x1ea, 0x1ec, 0x1ee, 0x1f1, 0x1f4, 0x1f8, + 0x1fa, 0x1fc, 0x1fe, 0x200, 0x202, 0x204, 0x206, 0x208, 0x20a, 0x20c, + 0x20e, 0x210, 0x212, 0x214, 0x216, 0x218, 0x21a, 0x21c, 0x21e, 0x222, + 0x224, 0x226, 0x228, 0x22a, 0x22c, 0x22e, 0x230, 0x232, 0x23b, 0x241, + 0x246, 0x248, 0x24a, 0x24c, 0x24e, 0x3d8, 0x3da, 0x3dc, 0x3de, 0x3e0, + 0x3e2, 0x3e4, 0x3e6, 0x3e8, 0x3ea, 0x3ec, 0x3ee, 0x3f7, 0x3fa, 0x460, + 0x462, 0x464, 0x466, 0x468, 0x46a, 0x46c, 0x46e, 0x470, 0x472, 0x474, + 0x476, 0x478, 0x47a, 0x47c, 0x47e, 0x480, 0x48a, 0x48c, 0x48e, 0x490, + 0x492, 0x494, 0x496, 0x498, 0x49a, 0x49c, 0x49e, 0x4a0, 0x4a2, 0x4a4, + 0x4a6, 0x4a8, 0x4aa, 0x4ac, 0x4ae, 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8, + 0x4ba, 0x4bc, 0x4be, 0x4c1, 0x4c3, 0x4c5, 0x4c7, 0x4c9, 0x4cb, 0x4cd, + 0x4d0, 0x4d2, 0x4d4, 0x4d6, 0x4d8, 0x4da, 0x4dc, 0x4de, 0x4e0, 0x4e2, + 0x4e4, 0x4e6, 0x4e8, 0x4ea, 0x4ec, 0x4ee, 0x4f0, 0x4f2, 0x4f4, 0x4f6, + 0x4f8, 0x4fa, 0x4fc, 0x4fe, 0x500, 0x502, 0x504, 0x506, 0x508, 0x50a, + 0x50c, 0x50e, 0x510, 0x512, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, + 0x1e0a, 0x1e0c, 0x1e0e, 0x1e10, 0x1e12, 0x1e14, 0x1e16, 0x1e18, + 0x1e1a, 0x1e1c, 0x1e1e, 0x1e20, 0x1e22, 0x1e24, 0x1e26, 0x1e28, + 0x1e2a, 0x1e2c, 0x1e2e, 0x1e30, 0x1e32, 0x1e34, 0x1e36, 0x1e38, + 0x1e3a, 0x1e3c, 0x1e3e, 0x1e40, 0x1e42, 0x1e44, 0x1e46, 0x1e48, + 0x1e4a, 0x1e4c, 0x1e4e, 0x1e50, 0x1e52, 0x1e54, 0x1e56, 0x1e58, + 0x1e5a, 0x1e5c, 0x1e5e, 0x1e60, 0x1e62, 0x1e64, 0x1e66, 0x1e68, + 0x1e6a, 0x1e6c, 0x1e6e, 0x1e70, 0x1e72, 0x1e74, 0x1e76, 0x1e78, + 0x1e7a, 0x1e7c, 0x1e7e, 0x1e80, 0x1e82, 0x1e84, 0x1e86, 0x1e88, + 0x1e8a, 0x1e8c, 0x1e8e, 0x1e90, 0x1e92, 0x1e94, 0x1ea0, 0x1ea2, + 0x1ea4, 0x1ea6, 0x1ea8, 0x1eaa, 0x1eac, 0x1eae, 0x1eb0, 0x1eb2, + 0x1eb4, 0x1eb6, 0x1eb8, 0x1eba, 0x1ebc, 0x1ebe, 0x1ec0, 0x1ec2, + 0x1ec4, 0x1ec6, 0x1ec8, 0x1eca, 0x1ecc, 0x1ece, 0x1ed0, 0x1ed2, + 0x1ed4, 0x1ed6, 0x1ed8, 0x1eda, 0x1edc, 0x1ede, 0x1ee0, 0x1ee2, + 0x1ee4, 0x1ee6, 0x1ee8, 0x1eea, 0x1eec, 0x1eee, 0x1ef0, 0x1ef2, + 0x1ef4, 0x1ef6, 0x1ef8, 0x2183, 0x2c60, 0x2c67, 0x2c69, 0x2c6b, + 0x2c75, 0x2c80, 0x2c82, 0x2c84, 0x2c86, 0x2c88, 0x2c8a, 0x2c8c, + 0x2c8e, 0x2c90, 0x2c92, 0x2c94, 0x2c96, 0x2c98, 0x2c9a, 0x2c9c, + 0x2c9e, 0x2ca0, 0x2ca2, 0x2ca4, 0x2ca6, 0x2ca8, 0x2caa, 0x2cac, + 0x2cae, 0x2cb0, 0x2cb2, 0x2cb4, 0x2cb6, 0x2cb8, 0x2cba, 0x2cbc, + 0x2cbe, 0x2cc0, 0x2cc2, 0x2cc4, 0x2cc6, 0x2cc8, 0x2cca, 0x2ccc, + 0x2cce, 0x2cd0, 0x2cd2, 0x2cd4, 0x2cd6, 0x2cd8, 0x2cda, 0x2cdc, + 0x2cde, 0x2ce0, 0x2ce2)), + new DeltaSet(1, CharRanges.withMembers( + 0x101, 0x103, 0x105, 0x107, 0x109, 0x10b, 0x10d, 0x10f, 0x111, 0x113, + 0x115, 0x117, 0x119, 0x11b, 0x11d, 0x11f, 0x121, 0x123, 0x125, 0x127, + 0x129, 0x12b, 0x12d, 0x12f, 0x133, 0x135, 0x137, 0x13a, 0x13c, 0x13e, + 0x140, 0x142, 0x144, 0x146, 0x148, 0x14b, 0x14d, 0x14f, 0x151, 0x153, + 0x155, 0x157, 0x159, 0x15b, 0x15d, 0x15f, 0x161, 0x163, 0x165, 0x167, + 0x169, 0x16b, 0x16d, 0x16f, 0x171, 0x173, 0x175, 0x177, 0x17a, 0x17c, + 0x17e, 0x183, 0x185, 0x188, 0x18c, 0x192, 0x199, 0x1a1, 0x1a3, 0x1a5, + 0x1a8, 0x1ad, 0x1b0, 0x1b4, 0x1b6, 0x1b9, 0x1bd, 0x1c5, 0x1c8, 0x1cb, + 0x1ce, 0x1d0, 0x1d2, 0x1d4, 0x1d6, 0x1d8, 0x1da, 0x1dc, 0x1df, 0x1e1, + 0x1e3, 0x1e5, 0x1e7, 0x1e9, 0x1eb, 0x1ed, 0x1ef, 0x1f2, 0x1f5, 0x1f9, + 0x1fb, 0x1fd, 0x1ff, 0x201, 0x203, 0x205, 0x207, 0x209, 0x20b, 0x20d, + 0x20f, 0x211, 0x213, 0x215, 0x217, 0x219, 0x21b, 0x21d, 0x21f, 0x223, + 0x225, 0x227, 0x229, 0x22b, 0x22d, 0x22f, 0x231, 0x233, 0x23c, 0x242, + 0x247, 0x249, 0x24b, 0x24d, 0x24f, 0x3d9, 0x3db, 0x3dd, 0x3df, 0x3e1, + 0x3e3, 0x3e5, 0x3e7, 0x3e9, 0x3eb, 0x3ed, 0x3ef, 0x3f8, 0x3fb, 0x461, + 0x463, 0x465, 0x467, 0x469, 0x46b, 0x46d, 0x46f, 0x471, 0x473, 0x475, + 0x477, 0x479, 0x47b, 0x47d, 0x47f, 0x481, 0x48b, 0x48d, 0x48f, 0x491, + 0x493, 0x495, 0x497, 0x499, 0x49b, 0x49d, 0x49f, 0x4a1, 0x4a3, 0x4a5, + 0x4a7, 0x4a9, 0x4ab, 0x4ad, 0x4af, 0x4b1, 0x4b3, 0x4b5, 0x4b7, 0x4b9, + 0x4bb, 0x4bd, 0x4bf, 0x4c2, 0x4c4, 0x4c6, 0x4c8, 0x4ca, 0x4cc, 0x4ce, + 0x4d1, 0x4d3, 0x4d5, 0x4d7, 0x4d9, 0x4db, 0x4dd, 0x4df, 0x4e1, 0x4e3, + 0x4e5, 0x4e7, 0x4e9, 0x4eb, 0x4ed, 0x4ef, 0x4f1, 0x4f3, 0x4f5, 0x4f7, + 0x4f9, 0x4fb, 0x4fd, 0x4ff, 0x501, 0x503, 0x505, 0x507, 0x509, 0x50b, + 0x50d, 0x50f, 0x511, 0x513, 0x1e01, 0x1e03, 0x1e05, 0x1e07, 0x1e09, + 0x1e0b, 0x1e0d, 0x1e0f, 0x1e11, 0x1e13, 0x1e15, 0x1e17, 0x1e19, + 0x1e1b, 0x1e1d, 0x1e1f, 0x1e21, 0x1e23, 0x1e25, 0x1e27, 0x1e29, + 0x1e2b, 0x1e2d, 0x1e2f, 0x1e31, 0x1e33, 0x1e35, 0x1e37, 0x1e39, + 0x1e3b, 0x1e3d, 0x1e3f, 0x1e41, 0x1e43, 0x1e45, 0x1e47, 0x1e49, + 0x1e4b, 0x1e4d, 0x1e4f, 0x1e51, 0x1e53, 0x1e55, 0x1e57, 0x1e59, + 0x1e5b, 0x1e5d, 0x1e5f, 0x1e61, 0x1e63, 0x1e65, 0x1e67, 0x1e69, + 0x1e6b, 0x1e6d, 0x1e6f, 0x1e71, 0x1e73, 0x1e75, 0x1e77, 0x1e79, + 0x1e7b, 0x1e7d, 0x1e7f, 0x1e81, 0x1e83, 0x1e85, 0x1e87, 0x1e89, + 0x1e8b, 0x1e8d, 0x1e8f, 0x1e91, 0x1e93, 0x1e95, 0x1ea1, 0x1ea3, + 0x1ea5, 0x1ea7, 0x1ea9, 0x1eab, 0x1ead, 0x1eaf, 0x1eb1, 0x1eb3, + 0x1eb5, 0x1eb7, 0x1eb9, 0x1ebb, 0x1ebd, 0x1ebf, 0x1ec1, 0x1ec3, + 0x1ec5, 0x1ec7, 0x1ec9, 0x1ecb, 0x1ecd, 0x1ecf, 0x1ed1, 0x1ed3, + 0x1ed5, 0x1ed7, 0x1ed9, 0x1edb, 0x1edd, 0x1edf, 0x1ee1, 0x1ee3, + 0x1ee5, 0x1ee7, 0x1ee9, 0x1eeb, 0x1eed, 0x1eef, 0x1ef1, 0x1ef3, + 0x1ef5, 0x1ef7, 0x1ef9, 0x2184, 0x2c61, 0x2c68, 0x2c6a, 0x2c6c, + 0x2c76, 0x2c81, 0x2c83, 0x2c85, 0x2c87, 0x2c89, 0x2c8b, 0x2c8d, + 0x2c8f, 0x2c91, 0x2c93, 0x2c95, 0x2c97, 0x2c99, 0x2c9b, 0x2c9d, + 0x2c9f, 0x2ca1, 0x2ca3, 0x2ca5, 0x2ca7, 0x2ca9, 0x2cab, 0x2cad, + 0x2caf, 0x2cb1, 0x2cb3, 0x2cb5, 0x2cb7, 0x2cb9, 0x2cbb, 0x2cbd, + 0x2cbf, 0x2cc1, 0x2cc3, 0x2cc5, 0x2cc7, 0x2cc9, 0x2ccb, 0x2ccd, + 0x2ccf, 0x2cd1, 0x2cd3, 0x2cd5, 0x2cd7, 0x2cd9, 0x2cdb, 0x2cdd, + 0x2cdf, 0x2ce1, 0x2ce3)), + new DeltaSet(2, CharRanges.withMembers(0x1c6, 0x1c9, 0x1cc, 0x1f3)), + new DeltaSet(7, CharRanges.withMembers(0x3f9, 0x1fec)), + new DeltaSet(8, CharRanges.withRanges( + 0x1f08, 0x1f10, 0x1f18, 0x1f1e, 0x1f28, 0x1f30, 0x1f38, 0x1f40, + 0x1f48, 0x1f4e, 0x1f59, 0x1f5a, 0x1f5b, 0x1f5c, 0x1f5d, 0x1f5e, + 0x1f5f, 0x1f60, 0x1f68, 0x1f70, 0x1fb8, 0x1fba, 0x1fd8, 0x1fda, + 0x1fe8, 0x1fea)), + new DeltaSet(15, CharRanges.withMembers(0x4cf)), + new DeltaSet(16, CharRanges.withRanges(0x2170, 0x2180)), + new DeltaSet(26, CharRanges.withRanges(0x24d0, 0x24ea)), + new DeltaSet(28, CharRanges.withMembers(0x214e)), + new DeltaSet(31, CharRanges.withMembers(0x3c2)), + new DeltaSet(32, CharRanges.withRanges( + 0x61, 0x7b, 0xe0, 0xf7, 0xf8, 0xff, 0x3b1, 0x3c2, 0x3c3, 0x3cc, + 0x430, 0x450, 0xff41, 0xff5b)), + new DeltaSet(37, CharRanges.withRanges(0x3ad, 0x3b0)), + new DeltaSet(38, CharRanges.withMembers(0x3ac)), + new DeltaSet(47, CharRanges.withMembers(0x3d5)), + new DeltaSet(48, CharRanges.withRanges(0x561, 0x587, 0x2c30, 0x2c5f)), + new DeltaSet(54, CharRanges.withMembers(0x3d6)), + new DeltaSet(56, CharRanges.withMembers(0x1f7)), + new DeltaSet(57, CharRanges.withMembers(0x3d1)), + new DeltaSet(59, CharRanges.withMembers(0x1e9b)), + new DeltaSet(62, CharRanges.withMembers(0x3d0)), + new DeltaSet(63, CharRanges.withRanges(0x3cd, 0x3cf)), + new DeltaSet(64, CharRanges.withMembers(0x3cc)), + new DeltaSet(69, CharRanges.withMembers(0x289)), + new DeltaSet(71, CharRanges.withMembers(0x28c)), + new DeltaSet(74, CharRanges.withRanges(0x1fba, 0x1fbc)), + new DeltaSet(79, CharRanges.withMembers(0x1dd)), + new DeltaSet(80, CharRanges.withRanges(0x3f1, 0x3f2, 0x450, 0x460)), + new DeltaSet(84, CharRanges.withMembers(0x399)), + new DeltaSet(86, CharRanges.withRanges(0x3f0, 0x3f1, 0x1fc8, 0x1fcc)), + new DeltaSet(96, CharRanges.withMembers(0x3f5)), + new DeltaSet(97, CharRanges.withMembers(0x1f6)), + new DeltaSet(100, CharRanges.withRanges(0x1fda, 0x1fdc)), + new DeltaSet(112, CharRanges.withRanges(0x1fea, 0x1fec)), + new DeltaSet(121, CharRanges.withMembers(0x178)), + new DeltaSet(126, CharRanges.withRanges(0x1ffa, 0x1ffc)), + new DeltaSet(128, CharRanges.withRanges(0x1ff8, 0x1ffa)), + new DeltaSet(130, CharRanges.withRanges(0x220, 0x221, 0x3fd, 0x400)), + new DeltaSet(163, CharRanges.withMembers(0x23d)), + new DeltaSet(195, CharRanges.withMembers(0x243)), + new DeltaSet(202, CharRanges.withMembers(0x259)), + new DeltaSet(203, CharRanges.withMembers(0x25b)), + new DeltaSet(205, CharRanges.withRanges(0x256, 0x258, 0x260, 0x261)), + new DeltaSet(206, CharRanges.withMembers(0x254)), + new DeltaSet(207, CharRanges.withMembers(0x263)), + new DeltaSet(209, CharRanges.withMembers(0x268)), + new DeltaSet(210, CharRanges.withMembers(0x253)), + new DeltaSet(211, CharRanges.withMembers(0x269, 0x26f)), + new DeltaSet(213, CharRanges.withMembers(0x272)), + new DeltaSet(214, CharRanges.withMembers(0x275)), + new DeltaSet(217, CharRanges.withRanges(0x28a, 0x28c)), + new DeltaSet(218, CharRanges.withMembers(0x280, 0x283, 0x288)), + new DeltaSet(219, CharRanges.withMembers(0x292)), + new DeltaSet(743, CharRanges.withMembers(0x39c)), + new DeltaSet(3814, CharRanges.withMembers(0x2c63)), + new DeltaSet(7205, CharRanges.withMembers(0x1fbe)), + new DeltaSet(7264, CharRanges.withRanges(0x2d00, 0x2d26)), + new DeltaSet(10727, CharRanges.withMembers(0x2c64)), + new DeltaSet(10743, CharRanges.withMembers(0x2c62)), + new DeltaSet(10792, CharRanges.withMembers(0x2c66)), + new DeltaSet(10795, CharRanges.withMembers(0x2c65)) + ); + + private static final ImmutableList CANON_DELTA_SETS + = ImmutableList.of( + new DeltaSet(-10743, CharRanges.withMembers(0x26b)), + new DeltaSet(-10727, CharRanges.withMembers(0x27d)), + new DeltaSet(-3814, CharRanges.withMembers(0x1d7d)), + new DeltaSet(-743, CharRanges.withMembers(0xb5)), + new DeltaSet(-195, CharRanges.withMembers(0x180)), + new DeltaSet(-163, CharRanges.withMembers(0x19a)), + new DeltaSet(-130, CharRanges.withRanges(0x19e, 0x19f, 0x37b, 0x37e)), + new DeltaSet(-128, CharRanges.withRanges(0x1f78, 0x1f7a)), + new DeltaSet(-126, CharRanges.withRanges(0x1f7c, 0x1f7e)), + new DeltaSet(-121, CharRanges.withMembers(0xff)), + new DeltaSet(-112, CharRanges.withRanges(0x1f7a, 0x1f7c)), + new DeltaSet(-100, CharRanges.withRanges(0x1f76, 0x1f78)), + new DeltaSet(-97, CharRanges.withMembers(0x195)), + new DeltaSet(-86, CharRanges.withRanges(0x1f72, 0x1f76)), + new DeltaSet(-84, CharRanges.withMembers(0x345)), + new DeltaSet(-74, CharRanges.withRanges(0x1f70, 0x1f72)), + new DeltaSet(-56, CharRanges.withMembers(0x1bf)), + new DeltaSet(-8, CharRanges.withRanges( + 0x1f00, 0x1f08, 0x1f10, 0x1f16, 0x1f20, 0x1f28, 0x1f30, 0x1f38, + 0x1f40, 0x1f46, 0x1f51, 0x1f52, 0x1f53, 0x1f54, 0x1f55, 0x1f56, + 0x1f57, 0x1f58, 0x1f60, 0x1f68, 0x1fb0, 0x1fb2, 0x1fd0, 0x1fd2, + 0x1fe0, 0x1fe2)), + new DeltaSet(-7, CharRanges.withMembers(0x3f2, 0x1fe5)), + new DeltaSet(1, CharRanges.withMembers( + 0x101, 0x103, 0x105, 0x107, 0x109, 0x10b, 0x10d, 0x10f, 0x111, 0x113, + 0x115, 0x117, 0x119, 0x11b, 0x11d, 0x11f, 0x121, 0x123, 0x125, 0x127, + 0x129, 0x12b, 0x12d, 0x12f, 0x133, 0x135, 0x137, 0x13a, 0x13c, 0x13e, + 0x140, 0x142, 0x144, 0x146, 0x148, 0x14b, 0x14d, 0x14f, 0x151, 0x153, + 0x155, 0x157, 0x159, 0x15b, 0x15d, 0x15f, 0x161, 0x163, 0x165, 0x167, + 0x169, 0x16b, 0x16d, 0x16f, 0x171, 0x173, 0x175, 0x177, 0x17a, 0x17c, + 0x17e, 0x183, 0x185, 0x188, 0x18c, 0x192, 0x199, 0x1a1, 0x1a3, 0x1a5, + 0x1a8, 0x1ad, 0x1b0, 0x1b4, 0x1b6, 0x1b9, 0x1bd, 0x1c5, 0x1c8, 0x1cb, + 0x1ce, 0x1d0, 0x1d2, 0x1d4, 0x1d6, 0x1d8, 0x1da, 0x1dc, 0x1df, 0x1e1, + 0x1e3, 0x1e5, 0x1e7, 0x1e9, 0x1eb, 0x1ed, 0x1ef, 0x1f2, 0x1f5, 0x1f9, + 0x1fb, 0x1fd, 0x1ff, 0x201, 0x203, 0x205, 0x207, 0x209, 0x20b, 0x20d, + 0x20f, 0x211, 0x213, 0x215, 0x217, 0x219, 0x21b, 0x21d, 0x21f, 0x223, + 0x225, 0x227, 0x229, 0x22b, 0x22d, 0x22f, 0x231, 0x233, 0x23c, 0x242, + 0x247, 0x249, 0x24b, 0x24d, 0x24f, 0x3d9, 0x3db, 0x3dd, 0x3df, 0x3e1, + 0x3e3, 0x3e5, 0x3e7, 0x3e9, 0x3eb, 0x3ed, 0x3ef, 0x3f8, 0x3fb, 0x461, + 0x463, 0x465, 0x467, 0x469, 0x46b, 0x46d, 0x46f, 0x471, 0x473, 0x475, + 0x477, 0x479, 0x47b, 0x47d, 0x47f, 0x481, 0x48b, 0x48d, 0x48f, 0x491, + 0x493, 0x495, 0x497, 0x499, 0x49b, 0x49d, 0x49f, 0x4a1, 0x4a3, 0x4a5, + 0x4a7, 0x4a9, 0x4ab, 0x4ad, 0x4af, 0x4b1, 0x4b3, 0x4b5, 0x4b7, 0x4b9, + 0x4bb, 0x4bd, 0x4bf, 0x4c2, 0x4c4, 0x4c6, 0x4c8, 0x4ca, 0x4cc, 0x4ce, + 0x4d1, 0x4d3, 0x4d5, 0x4d7, 0x4d9, 0x4db, 0x4dd, 0x4df, 0x4e1, 0x4e3, + 0x4e5, 0x4e7, 0x4e9, 0x4eb, 0x4ed, 0x4ef, 0x4f1, 0x4f3, 0x4f5, 0x4f7, + 0x4f9, 0x4fb, 0x4fd, 0x4ff, 0x501, 0x503, 0x505, 0x507, 0x509, 0x50b, + 0x50d, 0x50f, 0x511, 0x513, 0x1e01, 0x1e03, 0x1e05, 0x1e07, 0x1e09, + 0x1e0b, 0x1e0d, 0x1e0f, 0x1e11, 0x1e13, 0x1e15, 0x1e17, 0x1e19, + 0x1e1b, 0x1e1d, 0x1e1f, 0x1e21, 0x1e23, 0x1e25, 0x1e27, 0x1e29, + 0x1e2b, 0x1e2d, 0x1e2f, 0x1e31, 0x1e33, 0x1e35, 0x1e37, 0x1e39, + 0x1e3b, 0x1e3d, 0x1e3f, 0x1e41, 0x1e43, 0x1e45, 0x1e47, 0x1e49, + 0x1e4b, 0x1e4d, 0x1e4f, 0x1e51, 0x1e53, 0x1e55, 0x1e57, 0x1e59, + 0x1e5b, 0x1e5d, 0x1e5f, 0x1e61, 0x1e63, 0x1e65, 0x1e67, 0x1e69, + 0x1e6b, 0x1e6d, 0x1e6f, 0x1e71, 0x1e73, 0x1e75, 0x1e77, 0x1e79, + 0x1e7b, 0x1e7d, 0x1e7f, 0x1e81, 0x1e83, 0x1e85, 0x1e87, 0x1e89, + 0x1e8b, 0x1e8d, 0x1e8f, 0x1e91, 0x1e93, 0x1e95, 0x1ea1, 0x1ea3, + 0x1ea5, 0x1ea7, 0x1ea9, 0x1eab, 0x1ead, 0x1eaf, 0x1eb1, 0x1eb3, + 0x1eb5, 0x1eb7, 0x1eb9, 0x1ebb, 0x1ebd, 0x1ebf, 0x1ec1, 0x1ec3, + 0x1ec5, 0x1ec7, 0x1ec9, 0x1ecb, 0x1ecd, 0x1ecf, 0x1ed1, 0x1ed3, + 0x1ed5, 0x1ed7, 0x1ed9, 0x1edb, 0x1edd, 0x1edf, 0x1ee1, 0x1ee3, + 0x1ee5, 0x1ee7, 0x1ee9, 0x1eeb, 0x1eed, 0x1eef, 0x1ef1, 0x1ef3, + 0x1ef5, 0x1ef7, 0x1ef9, 0x2184, 0x2c61, 0x2c68, 0x2c6a, 0x2c6c, + 0x2c76, 0x2c81, 0x2c83, 0x2c85, 0x2c87, 0x2c89, 0x2c8b, 0x2c8d, + 0x2c8f, 0x2c91, 0x2c93, 0x2c95, 0x2c97, 0x2c99, 0x2c9b, 0x2c9d, + 0x2c9f, 0x2ca1, 0x2ca3, 0x2ca5, 0x2ca7, 0x2ca9, 0x2cab, 0x2cad, + 0x2caf, 0x2cb1, 0x2cb3, 0x2cb5, 0x2cb7, 0x2cb9, 0x2cbb, 0x2cbd, + 0x2cbf, 0x2cc1, 0x2cc3, 0x2cc5, 0x2cc7, 0x2cc9, 0x2ccb, 0x2ccd, + 0x2ccf, 0x2cd1, 0x2cd3, 0x2cd5, 0x2cd7, 0x2cd9, 0x2cdb, 0x2cdd, + 0x2cdf, 0x2ce1, 0x2ce3)), + new DeltaSet(2, CharRanges.withMembers(0x1c6, 0x1c9, 0x1cc, 0x1f3)), + new DeltaSet(15, CharRanges.withMembers(0x4cf)), + new DeltaSet(16, CharRanges.withRanges(0x2170, 0x2180)), + new DeltaSet(26, CharRanges.withRanges(0x24d0, 0x24ea)), + new DeltaSet(28, CharRanges.withMembers(0x214e)), + new DeltaSet(31, CharRanges.withMembers(0x3c2)), + new DeltaSet(32, CharRanges.withRanges( + 0x61, 0x7b, 0xe0, 0xf7, 0xf8, 0xff, 0x3b1, 0x3c2, 0x3c3, 0x3cc, 0x430, + 0x450, 0xff41, 0xff5b)), + new DeltaSet(37, CharRanges.withRanges(0x3ad, 0x3b0)), + new DeltaSet(38, CharRanges.withMembers(0x3ac)), + new DeltaSet(47, CharRanges.withMembers(0x3d5)), + new DeltaSet(48, CharRanges.withRanges(0x561, 0x587, 0x2c30, 0x2c5f)), + new DeltaSet(54, CharRanges.withMembers(0x3d6)), + new DeltaSet(57, CharRanges.withMembers(0x3d1)), + new DeltaSet(59, CharRanges.withMembers(0x1e9b)), + new DeltaSet(62, CharRanges.withMembers(0x3d0)), + new DeltaSet(63, CharRanges.withRanges(0x3cd, 0x3cf)), + new DeltaSet(64, CharRanges.withMembers(0x3cc)), + new DeltaSet(69, CharRanges.withMembers(0x289)), + new DeltaSet(71, CharRanges.withMembers(0x28c)), + new DeltaSet(79, CharRanges.withMembers(0x1dd)), + new DeltaSet(80, CharRanges.withRanges(0x3f1, 0x3f2, 0x450, 0x460)), + new DeltaSet(86, CharRanges.withMembers(0x3f0)), + new DeltaSet(96, CharRanges.withMembers(0x3f5)), + new DeltaSet(202, CharRanges.withMembers(0x259)), + new DeltaSet(203, CharRanges.withMembers(0x25b)), + new DeltaSet(205, CharRanges.withRanges(0x256, 0x258, 0x260, 0x261)), + new DeltaSet(206, CharRanges.withMembers(0x254)), + new DeltaSet(207, CharRanges.withMembers(0x263)), + new DeltaSet(209, CharRanges.withMembers(0x268)), + new DeltaSet(210, CharRanges.withMembers(0x253)), + new DeltaSet(211, CharRanges.withMembers(0x269, 0x26f)), + new DeltaSet(213, CharRanges.withMembers(0x272)), + new DeltaSet(214, CharRanges.withMembers(0x275)), + new DeltaSet(217, CharRanges.withRanges(0x28a, 0x28c)), + new DeltaSet(218, CharRanges.withMembers(0x280, 0x283, 0x288)), + new DeltaSet(219, CharRanges.withMembers(0x292)), + new DeltaSet(7205, CharRanges.withMembers(0x1fbe)), + new DeltaSet(7264, CharRanges.withRanges(0x2d00, 0x2d26)), + new DeltaSet(10792, CharRanges.withMembers(0x2c66)), + new DeltaSet(10795, CharRanges.withMembers(0x2c65)) + ); + + + /** + * A group of code units such that for all cu in codeUnits, cu is equivalent, + * case-insensitively, to cu + delta. + */ + private static final class DeltaSet { + final int delta; + final CharRanges codeUnits; + + DeltaSet(int delta, CharRanges codeUnits) { + this.delta = delta; + this.codeUnits = codeUnits; + } + } + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/regex/CharRanges.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/regex/CharRanges.java new file mode 100644 index 0000000..0736d68 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/regex/CharRanges.java @@ -0,0 +1,425 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.regex; + +import java.util.Arrays; + +/** + * An immutable sparse bitset that deals well where the data is chunky: + * where P(bit[x+1] == bit[x]). E.g. [101,102,103,104,105,1001,1002,1003,1004] + * is chunky. + * + * @author mikesamuel@gmail.com (Mike Samuel) + */ +final class CharRanges { + /** + * A strictly increasing set of bit indices where even members are the + * inclusive starts of ranges, and odd members are the exclusive ends. + *

      + * E.g., { 1, 5, 6, 10 } represents the set ( 1, 2, 3, 4, 6, 7, 8, 9 ). + */ + private final int[] ranges; + + public static final CharRanges EMPTY = new CharRanges(new int[0]); + + public static final CharRanges ALL_CODE_UNITS + = new CharRanges(new int[] { 0, 0x10000 }); + + public static CharRanges inclusive(int start, int end) { + if (start > end) { + throw new IndexOutOfBoundsException(start + " > " + end); + } + return new CharRanges(new int[] { start, end + 1 }); + } + + /** + * Returns an instance containing all and only the given members. + */ + public static CharRanges withMembers(int... members) { + return new CharRanges(intArrayToRanges(members.clone())); + } + + /** + * Returns an instance containing the given ranges. + * @param ranges An even-length ordered sequence of non-overlapping, + * non-contiguous, [inclusive start, exclusive end) ranges. + */ + public static CharRanges withRanges(int... ranges) { + ranges = ranges.clone(); + if ((ranges.length & 1) != 0) { throw new IllegalArgumentException(); } + for (int i = 1; i < ranges.length; ++i) { + if (ranges[i] <= ranges[i - 1]) { + throw new IllegalArgumentException(ranges[i] + " > " + ranges[i - 1]); + } + } + return new CharRanges(ranges); + } + + private CharRanges(int[] ranges) { + this.ranges = ranges; + } + + private static int[] intArrayToRanges(int[] members) { + int nMembers = members.length; + if (nMembers == 0) { + return new int[0]; + } + + Arrays.sort(members); + + // Count the number of runs. + int nRuns = 1; + for (int i = 1; i < nMembers; ++i) { + int current = members[i], last = members[i - 1]; + if (current == last) { continue; } + if (current != last + 1) { ++nRuns; } + } + + int[] ranges = new int[nRuns * 2]; + ranges[0] = members[0]; + int k = 0; + for (int i = 1; k + 2 < ranges.length; ++i) { + int current = members[i], last = members[i - 1]; + if (current == last) { continue; } + if (current != last + 1) { + ranges[++k] = last + 1; // add 1 to make end exclusive + ranges[++k] = current; + } + } + ranges[++k] = members[nMembers - 1] + 1; // add 1 to make end exclusive + return ranges; + } + + public boolean contains(int bit) { + return (Arrays.binarySearch(ranges, bit) & 1) == 0; + // By the contract of Arrays.binarySearch, its result is either the position + // of bit in ranges or it is the bitwise inverse of the position of the + // least element greater than bit. + + // Two cases + // case (idx >= 0) + // We ended up exactly on a range boundary. + // Starts are inclusive and ends are both exclusive, so this contains + // bit iff idx is even. + // + // case (idx < 0) + // If the least element greater than bit is an odd element, + // then bit must be greater than a start and less than an end, so + // contained. + // + // If bit is greater than all elements, then idx will be past the end of + // the array, and will be even since ranges.length is even. + // + // Otherwise, bit must be in the space between two runs, so not + // contained. + // + // In all cases, oddness is equivalent to containedness. + + // Those two cases lead to + // idx >= 0 ? ((idx & 1) == 0) : ((~idx & 1) == 1) + + // But ~n & bit == bit <=> n & bit == 0, so + // idx >= 0 ? ((idx & 1) == 0) : ((~idx & 1) == 1) + // => idx >= 0 ? ((idx & 1) == 0) : ((idx & 1) == 0) + // => (idx & 1) == 0 + } + + public int minSetBit() { + return ranges.length >= 0 ? ranges[0] : Integer.MIN_VALUE; + } + + public boolean isEmpty() { + return ranges.length == 0; + } + + public int getNumRanges() { return ranges.length >> 1; } + + public int start(int i) { return ranges[i << 1]; } + + public int end(int i) { return ranges[(i << 1) | 1]; } + + public CharRanges union(CharRanges other) { + // Index of the input ranges + int[] q = this.ranges, r = other.ranges; + // Lengths of the inputs + int m = q.length, n = r.length; + + if (m == 0) { return other; } + if (n == 0) { return this; } + + // The output array. The length is m+n in the worst case when all the + // ranges in a are disjoint from the ranges in b. + int[] out = new int[m + n]; + + // Indexes into the various arrays + int i = 0, j = 0, k = 0; + // Since there are three arrays, and indices into them the following + // should never occur in this function: + // (1) q[j] or q[k] -- q is indexed by i + // (2) r[i] or r[k] -- r is indexed by j + // (3) out[i] or out[j] -- out is indexed by k + // (4) i < n or j < m -- index compared to wrong limit + + // This loop exits because we always increment at least one of i,j. + while (i < m && j < n) { + // Range starts and ends. + int a0 = q[i], a1 = q[i + 1], + b0 = r[j], b1 = r[j + 1]; + if (a1 < b0) { // [a0, a1) ends before [b0, b1) starts + out[k++] = a0; + out[k++] = a1; + i += 2; + } else if (b1 < a0) { // [b0, b1) ends before [a0, a1) starts + out[k++] = b0; + out[k++] = b1; + j += 2; + } else { // ranges overlap + // We need to compute a new range based on the set of ranges that + // transitively overlap. + // AAAAAAAAA AAA + // BBB BBB* BBB + // In the range above, the start comes from one set, and the end from + // another. The range with the asterisk next to it is subsumed entirely + // by a range from the other, and so not all ranges on the input + // contribute a value to the output. + // The last BBB run serves only as a bridge -- it overlaps two + // disjoint ranges in the other one so establishes that they + // transitively overlap. + int start = Math.min(a0, b0); + // Guess at the end, and lookahead to come up with a more complete + // estimate. + int end = Math.max(a1, b1); + i += 2; + j += 2; + while (i < m || j < n) { + if (i < m && q[i] <= end) { + end = Math.max(end, q[i + 1]); + i += 2; + } else if (j < n && r[j] <= end) { + end = Math.max(end, r[j + 1]); + j += 2; + } else { + break; + } + } + out[k++] = start; + out[k++] = end; + } + } + // There may be unprocessed ranges at the end of one of the inputs. + if (i < m) { + System.arraycopy(q, i, out, k, m - i); + k += m - i; + } else if (j < n) { + System.arraycopy(r, j, out, k, n - j); + k += n - j; + } + // We guessed at the output length above. Cut off the tail. + if (k != out.length) { + int[] clipped = new int[k]; + System.arraycopy(out, 0, clipped, 0, k); + out = clipped; + } + return new CharRanges(out); + } + + public CharRanges intersection(CharRanges other) { + int[] aRanges = ranges, bRanges = other.ranges; + int aLen = aRanges.length, bLen = bRanges.length; + if (aLen == 0) { return this; } + if (bLen == 0) { return other; } + int aIdx = 0, bIdx = 0; + int[] intersection = new int[Math.min(aLen, bLen)]; + int intersectionIdx = 0; + int pos = Math.min(aRanges[0], bRanges[0]); + while (aIdx < aLen && bIdx < bLen) { + if (aRanges[aIdx + 1] <= pos) { + aIdx += 2; + } else if (bRanges[bIdx + 1] <= pos) { + bIdx += 2; + } else { + int start = Math.max(aRanges[aIdx], bRanges[bIdx]); + if (pos < start) { // Advance to start of common block. + pos = start; + } else { + // Now we know that pos is less than the ends of the two ranges and + // greater or equal to the starts of the two ranges. + int end = Math.min(aRanges[aIdx + 1], bRanges[bIdx + 1]); + if (intersectionIdx != 0 + && pos == intersection[intersectionIdx - 1]) { + intersection[intersectionIdx - 1] = end; + } else { + if (intersectionIdx == intersection.length) { + int[] newArr = new int[intersectionIdx * 2]; + System.arraycopy(intersection, 0, newArr, 0, intersectionIdx); + intersection = newArr; + } + intersection[intersectionIdx++] = pos; + intersection[intersectionIdx++] = end; + } + pos = end; + } + } + } + if (intersectionIdx != intersection.length) { + int[] newArr = new int[intersectionIdx]; + System.arraycopy(intersection, 0, newArr, 0, intersectionIdx); + intersection = newArr; + } + return new CharRanges(intersection); + } + + public CharRanges difference(CharRanges subtrahendRanges) { + // difference = minuend - subtrahend + int[] minuend = this.ranges; + int[] subtrahend = subtrahendRanges.ranges; + + int mn = minuend.length, sn = subtrahend.length; + if (mn == 0 || sn == 0) { return this; } + + int[] difference = new int[minuend.length]; + + // Indices into minuend.ranges, subtrahend.ranges, and difference. + int mIdx = 0, sIdx = 0, dIdx = 0; + + int pos = minuend[0]; + while (mIdx < mn) { + if (pos >= minuend[mIdx + 1]) { + mIdx += 2; + } else if (pos < minuend[mIdx]) { + // Skip gaps in the minuend. + pos = minuend[mIdx]; + } else if (sIdx < sn && pos >= subtrahend[sIdx]) { + // Skip over a removed part. + pos = subtrahend[sIdx + 1]; + sIdx += 2; + } else { + // Now we know that pos is between [minuend[i], minuend[i + 1]) + // and outside [subtrahend[j], subtrahend[j + 1]). + int end = sIdx < sn + ? Math.min(minuend[mIdx + 1], subtrahend[sIdx]) : minuend[mIdx + 1]; + if (dIdx != 0 && difference[dIdx - 1] == pos) { + difference[dIdx - 1] = pos; + } else { + if (dIdx == difference.length) { + int[] newArr = new int[dIdx * 2]; + System.arraycopy(difference, 0, newArr, 0, dIdx); + difference = newArr; + } + difference[dIdx++] = pos; + difference[dIdx++] = end; + } + pos = end; + } + } + + if (dIdx != difference.length) { + int[] newArr = new int[dIdx]; + System.arraycopy(difference, 0, newArr, 0, dIdx); + difference = newArr; + } + + return new CharRanges(difference); + } + + public boolean containsAll(CharRanges sub) { + int[] superRanges = this.ranges; + int[] subRanges = sub.ranges; + + int superIdx = 0, subIdx = 0; + int superLen = superRanges.length, subLen = subRanges.length; + while (subIdx < subLen) { + if (superIdx == superLen) { + return false; + } + if (superRanges[superIdx + 1] <= subRanges[subIdx]) { + // Super range ends before subRange starts. + superIdx += 2; + } else if (superRanges[superIdx] > subRanges[subIdx]) { + // Uncontained portion at start of sub range. + return false; + } else if (superRanges[superIdx + 1] >= subRanges[subIdx + 1]) { + // A sub range is completely contained in the super range. + // We know this because of the above condition and we have already + // ruled out that subRanges[subIdx] < superRanges[superIdx]. + subIdx += 2; + } else { + // Uncontained portion at end of sub range. + return false; + } + } + return subIdx == subLen; + } + + /** + * Shifts the bits matched by the given delta. + * So if this has the bits (a, b, c, ..., z) set then the result has the bits + * ((a - delta), (b - delta), (c - delta), ...., (z - delta)) set. + * + * @throws IndexOutOfBoundsException if shifting by delta would cause an + * overflow or underflow in a 32 bit {@code signed int} range boundary. + * Since the end boundaries of ranges are exclusive, even if there is no + * range containing {@link Integer#MAX_VALUE}, shifting by a delta of 1 + * can cause an overflow. + */ + public CharRanges shift(int delta) { + int n = ranges.length; + if (delta == 0 || n == 0) { return this; } + // Test overflow/underflow + if (delta < 0) { + long lmin = ranges[0] + delta; + if (lmin < Integer.MIN_VALUE) { throw new IndexOutOfBoundsException(); } + } else { + long lmax = ranges[n - 1] + delta; + if (lmax > Integer.MAX_VALUE) { throw new IndexOutOfBoundsException(); } + } + // Create a shifted range. + int[] shiftedRanges = new int[n]; + for (int i = n; --i >= 0;) { + shiftedRanges[i] = ranges[i] + delta; + } + return new CharRanges(shiftedRanges); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (int i = 0; i < ranges.length; ++i) { + if ((i & 1) != 0 && ranges[i] == ranges[i - 1] + 1) { continue; } + if (i != 0) { sb.append((i & 1) == 0 ? ' ' : '-'); } + sb.append("0x").append(Integer.toString(ranges[i] - (i & 1), 16)); + } + sb.append(']'); + return sb.toString(); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof CharRanges)) { return false; } + return Arrays.equals(this.ranges, ((CharRanges) o).ranges); + } + + @Override + public int hashCode() { + int hc = 0; + for (int i = 0, n = Math.min(16, ranges.length); i < n; ++i) { + hc = (hc << 2) + ranges[i]; + } + return hc; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/regex/RegExpTree.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/regex/RegExpTree.java new file mode 100644 index 0000000..c0c470a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/regex/RegExpTree.java @@ -0,0 +1,1859 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.regex; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * An AST for JavaScript regular expressions. + * + * @author Mike Samuel + */ +public abstract class RegExpTree { + + /** + * Returns a simpler regular expression that is semantically the same assuming + * the given flags. + * @param flags Regular expression flags, e.g. {@code "igm"}. + */ + public abstract RegExpTree simplify(String flags); + + /** + * True if the presence or absence of an {@code "i"} flag would change the + * meaning of this regular expression. + */ + public abstract boolean isCaseSensitive(); + + /** + * True if the regular expression contains an anchor : {@code ^} or {@code $}. + */ + public abstract boolean containsAnchor(); + + /** + * True if the regular expression contains capturing groups. + */ + public final boolean hasCapturingGroup() { + return numCapturingGroups() != 0; + } + + /** + * The number of capturing groups. + */ + public abstract int numCapturingGroups(); + + /** + * The children of this node. + */ + public abstract List children(); + + /** + * Appends this regular expression source to the given buffer. + */ + protected abstract void appendSourceCode(StringBuilder sb); + + protected abstract void appendDebugInfo(StringBuilder sb); + + @Override + public final String toString() { + StringBuilder sb = new StringBuilder(); + sb.append('/'); + appendSourceCode(sb); + // Don't emit a regular expression that looks like a line comment start. + if (sb.length() == 1) { + sb.append("(?:)"); + } + sb.append('/'); + return sb.toString(); + } + + public final String toDebugString() { + StringBuilder sb = new StringBuilder(); + appendDebugString(sb); + return sb.toString(); + } + + private void appendDebugString(StringBuilder sb) { + sb.append('(').append(getClass().getSimpleName()); + int len = sb.length(); + sb.append(' '); + appendDebugInfo(sb); + if (sb.length() == len + 1) { sb.setLength(len); } + for (RegExpTree child : children()) { + sb.append(' '); + child.appendDebugString(sb); + } + sb.append(')'); + } + + @Override + public abstract boolean equals(Object o); + + @Override + public abstract int hashCode(); + + + /** + * Parses a regular expression to an AST. + * + * @param pattern The {@code foo} From {@code /foo/i}. + * @param flags The {@code i} From {@code /foo/i}. + */ + public static RegExpTree parseRegExp( + final String pattern, final String flags) { + + /** A recursive descent parser that closes over pattern and flags above. */ + class Parser { + /** The number of characters in pattern consumed. */ + int pos; + /** The number of capturing groups seen so far. */ + int numCapturingGroups = 0; + /** The length of pattern. */ + final int limit = pattern.length(); + + RegExpTree parseTopLevel() { + this.pos = 0; + RegExpTree out = parse(); + if (pos < limit) { // Unmatched closed paren maybe. + throw new IllegalArgumentException(pattern.substring(pos)); + } + return out; + } + + RegExpTree parse() { + // Collects ["foo", "bar", "baz"] for /foo|bar|baz/. + ImmutableList.Builder alternatives = null; + // The last item parsed within an alternation. + RegExpTree preceder = null; + + topLoop: + while (pos < limit) { + char ch = pattern.charAt(pos); + RegExpTree atom; + switch (ch) { + case '[': + atom = parseCharset(); + break; + case '(': + atom = parseParenthetical(); + break; + case ')': + break topLoop; + case '\\': + atom = parseEscape(); + break; + case '^': + case '$': + atom = new Anchor(ch); + ++pos; + break; + case '.': + // We represent . as a character set to make it easy to simplify + // things like /.|[\r\n]/. + atom = DOT_CHARSET; + ++pos; + break; + case '|': + // An alternative may be empty as in /foo||bar/. + // The '|' is consumed below. + atom = Empty.INSTANCE; + break; + default: + // Find a run of concatenated characters to avoid building a + // tree node per literal character. + int start = pos; + int end = pos + 1; + charsLoop: + while (end < limit) { + switch (pattern.charAt(end)) { + case '[': + case '(': + case ')': + case '\\': + case '^': + case '$': + case '|': + case '.': + case '*': + case '+': + case '?': + case '{': + break charsLoop; + default: + // Repetition binds more tightly than concatenation. + // Only consume up to "foo" in /foob*/ so that the suffix + // operator parser below has the right precedence. + if (end + 1 >= limit + || !isRepetitionStart(pattern.charAt(end + 1))) { + ++end; + } else { + break charsLoop; + } + } + } + atom = new Text(pattern.substring(start, end)); + pos = end; + break; + } + if (pos < limit && isRepetitionStart(pattern.charAt(pos))) { + atom = parseRepetition(atom); + } + if (preceder == null) { + preceder = atom; + } else { + preceder = new Concatenation(preceder, atom); + } + // If this is an alternative in a alternation, then add it to the + // list of complete alternatives, and reset the parser state for the + // next alternative. + if (pos < limit && pattern.charAt(pos) == '|') { + if (alternatives == null) { + alternatives = ImmutableList.builder(); + } + alternatives.add(preceder); + preceder = null; + ++pos; + } + } + // An alternative may have no parsed content blank as in /foo|/. + if (preceder == null) { preceder = Empty.INSTANCE; } + if (alternatives != null) { + alternatives.add(preceder); + return new Alternation(alternatives.build()); + } else { + return preceder; + } + } + + /** + * Handles capturing groups {@code (...)}, + * non-capturing groups {@code (?:...)}, and lookahead assertions + * {@code (?=...)}. + */ + private RegExpTree parseParenthetical() { + Preconditions.checkState(pattern.charAt(pos) == '('); + int start = pos; + ++pos; + boolean capturing = true; + int type = 0; + if (pos < limit && pattern.charAt(pos) == '?') { + if (pos + 1 < limit) { + capturing = false; + char ch = pattern.charAt(pos + 1); + switch (ch) { + case ':': // A (?:...) style non-capturing group. + pos += 2; + break; + case '!': // A lookahead assertion + case '=': + pos += 2; + type = ch; + break; + default: + throw new IllegalArgumentException( + "Malformed parenthetical: " + pattern.substring(start)); + } + } + } + RegExpTree body = parse(); + if (pos < limit && pattern.charAt(pos) == ')') { + ++pos; + } else { + throw new IllegalArgumentException( + "Unclosed parenthetical group: " + pattern.substring(start)); + } + if (capturing) { + ++numCapturingGroups; + return new CapturingGroup(body); + } else if (type != 0) { + return new LookaheadAssertion(body, type == '='); + } else { + return body; + } + } + + /** + * Parses a square bracketed character set. + * Standalone character groups (@code /\d/} are handled by + * {@link #parseEscape}. + */ + private RegExpTree parseCharset() { + Preconditions.checkState(pattern.charAt(pos) == '['); + ++pos; + + boolean isCaseInsensitive = flags.indexOf('i') >= 0; + boolean inverse = pos < limit && pattern.charAt(pos) == '^'; + if (inverse) { ++pos; } + CharRanges ranges = CharRanges.EMPTY; + CharRanges ieExplicits = CharRanges.EMPTY; + while (pos < limit && pattern.charAt(pos) != ']') { + char ch = pattern.charAt(pos); + char start; + if (ch == '\\') { + ++pos; + char possibleGroupName = pattern.charAt(pos); + CharRanges group = NAMED_CHAR_GROUPS.get(possibleGroupName); + if (group != null) { + ++pos; + ranges = ranges.union(group); + continue; + } + start = parseEscapeChar(); + } else { + start = ch; + ++pos; + } + char end = start; + if (pos + 1 < limit && pattern.charAt(pos) == '-' + && pattern.charAt(pos + 1) != ']') { + ++pos; + ch = pattern.charAt(pos); + if (ch == '\\') { + ++pos; + end = parseEscapeChar(); + } else { + end = ch; + ++pos; + } + } + CharRanges range = CharRanges.inclusive(start, end); + ranges = ranges.union(range); + if (IE_SPEC_ERRORS.contains(start) && IE_SPEC_ERRORS.contains(end)) { + ieExplicits = ieExplicits.union(range.intersection(IE_SPEC_ERRORS)); + } + if (isCaseInsensitive) { + // If the flags contain the 'i' flag, then it is not correct to + // say that [^a-z] contains the letter 'A', or that [a-z] does not + // contain the letter 'A'. + // We expand out letter groups here so that parse returns something + // that is valid independent of flags. + // Calls to simplify(flags) may later reintroduce flag assumptions. + // but without this step, later steps might conflate + // /[a-z]/i + // and + // /[^\0-`{-\uffff]/i + // which matches nothing because the information about whether the + // ^ is present has been lost during optimizations and charset + // unionizing as in /[...]|[^...]/. + ranges = CaseCanonicalize.expandToAllMatched(ranges); + } + } + ++pos; // Consume ']' + + if (inverse) { + ranges = CharRanges.ALL_CODE_UNITS.difference(ranges); + } + + return new Charset(ranges, ieExplicits); + } + + /** + * Parses an escape to a code point. + * Some of the characters parsed here have special meanings in various + * contexts, so contexts must filter those instead. + * E.g. '\b' means a different thing inside a charset than without. + */ + private char parseEscapeChar() { + char ch = pattern.charAt(pos++); + switch (ch) { + case 'b': return '\b'; + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case 't': return '\t'; + case 'u': return parseHex(4); + case 'v': return '\u000b'; + case 'x': return parseHex(2); + default: + if ('0' <= ch && ch <= '7') { + char codeUnit = (char) (ch - '0'); + // Allow octal literals in the range \0-\377. + // \41 might be a group, but \041 is not a group. + // We read, but do not emit octal literals since they + // are deprecated in ES5. + int octLimit = Math.min( + limit, pos + (ch <= '3' ? 2 : 1) + (ch == '0' ? 1 : 0)); + while (pos < octLimit) { + ch = pattern.charAt(pos); + if ('0' <= ch && ch <= '7') { + codeUnit = (char) ((codeUnit << 3) + (ch - '0')); + ++pos; + } else { + break; + } + } + return codeUnit; + } + return ch; + } + } + + /** + * Parses an escape that appears outside a charset. + */ + private RegExpTree parseEscape() { + Preconditions.checkState(pattern.charAt(pos) == '\\'); + ++pos; + char ch = pattern.charAt(pos); + if (ch == 'b' || ch == 'B') { + ++pos; + return new WordBoundary(ch); + } else if ('1' <= ch && ch <= '9') { + ++pos; + int possibleGroupIndex = ch - '0'; + if (numCapturingGroups >= possibleGroupIndex) { + if (pos < limit) { + char next = pattern.charAt(pos); + if ('0' <= next && next <= '9') { + int twoDigitGroupIndex = possibleGroupIndex * 10 + (next - '0'); + if (numCapturingGroups >= twoDigitGroupIndex) { + ++pos; + possibleGroupIndex = twoDigitGroupIndex; + } + } + } + return new BackReference(possibleGroupIndex); + } else { + // \1 - \7 are octal escapes if there is no such group. + // \8 and \9 are the literal characters '8' and '9' if there + // is no such group. + return new Text(Character.toString( + possibleGroupIndex <= 7 ? (char) possibleGroupIndex : ch)); + } + } else { + CharRanges charGroup = NAMED_CHAR_GROUPS.get(ch); + if (charGroup != null) { // Handle \d, etc. + ++pos; + return new Charset(charGroup, CharRanges.EMPTY); + } + return new Text("" + parseEscapeChar()); + } + } + + /** + * Parses n hex digits to a code-unit. + */ + private char parseHex(int n) { + if (pos + n > limit) { + throw new IllegalArgumentException( + "Abbreviated hex escape " + pattern.substring(pos)); + } + int result = 0; + while (--n >= 0) { + char ch = pattern.charAt(pos); + int digit; + if ('0' <= ch && ch <= '9') { + digit = ch - '0'; + } else if ('a' <= ch && ch <= 'f') { + digit = ch + (10 - 'a'); + } else if ('A' <= ch && ch <= 'F') { + digit = ch + (10 - 'A'); + } else { + throw new IllegalArgumentException(pattern.substring(pos)); + } + ++pos; + result = (result << 4) | digit; + } + return (char) result; + } + + private boolean isRepetitionStart(char ch) { + switch (ch) { + case '?': + case '*': + case '+': + case '{': + return true; + default: + return false; + } + } + + /** + * Parse a repetition. {@code x?} is treated as a repetition -- + * an optional production can be matched 0 or 1 time. + */ + private RegExpTree parseRepetition(RegExpTree body) { + if (pos == limit) { return body; } + int min, max; + switch (pattern.charAt(pos)) { + case '+': + ++pos; + min = 1; + max = Integer.MAX_VALUE; + break; + case '*': + ++pos; + min = 0; + max = Integer.MAX_VALUE; + break; + case '?': + ++pos; + min = 0; + max = 1; + break; + case '{': + ++pos; + int start = pos; + int end = pattern.indexOf('}', start); + if (end < 0) { + pos = start - 1; + return body; + } + String counts = pattern.substring(start, end); + pos = end + 1; + int comma = counts.indexOf(','); + try { + min = Integer.parseInt( + comma >= 0 ? counts.substring(0, comma) : counts); + max = comma >= 0 + ? comma + 1 != counts.length() + ? Integer.parseInt(counts.substring(comma + 1)) + : Integer.MAX_VALUE + : min; + } catch (NumberFormatException ex) { + min = max = -1; + } + if (min < 0 || min > max) { + // Treat the open curly bracket literally. + pos = start - 1; + return body; + } + break; + default: + return body; + } + boolean greedy = true; + if (pos < limit && pattern.charAt(pos) == '?') { + greedy = false; + ++pos; + } + return new Repetition(body, min, max, greedy); + } + } + + return new Parser().parseTopLevel(); + } + + + /** + * True if, but not necessarily always when the, given regular expression + * must match the whole input or none of it. + */ + public static boolean matchesWholeInput(RegExpTree t, String flags) { + if (flags.indexOf('m') >= 0) { return false; } + + if (!(t instanceof Concatenation)) { + return false; + } + + Concatenation c = (Concatenation) t; + if (c.elements.isEmpty()) { return false; } + RegExpTree first = c.elements.get(0), + last = c.elements.get(c.elements.size() - 1); + if (!(first instanceof Anchor && last instanceof Anchor)) { return false; } + return ((Anchor) first).type == '^' && ((Anchor) last).type == '$'; + } + + + static abstract class RegExpTreeAtom extends RegExpTree { + @Override + public boolean isCaseSensitive() { + return false; + } + + @Override + public boolean containsAnchor() { + return false; + } + + @Override + public final int numCapturingGroups() { + return 0; + } + + @Override + public final List children() { + return ImmutableList.of(); + } + } + + static final class Empty extends RegExpTreeAtom { + static final Empty INSTANCE = new Empty(); + + @Override + public RegExpTree simplify(String flags) { + return this; + } + + @Override + protected void appendSourceCode(StringBuilder sb) { + // No output + } + + @Override + protected void appendDebugInfo(StringBuilder sb) { + // No output + } + + @Override + public boolean equals(Object o) { + return o instanceof Empty; + } + + @Override + public int hashCode() { + return 0x7ee06141; + } + } + + static final class Anchor extends RegExpTreeAtom { + final char type; + Anchor(char type) { this.type = type; } + + @Override + public RegExpTree simplify(String flags) { + return this; + } + + @Override + public boolean containsAnchor() { + return true; + } + + @Override + protected void appendSourceCode(StringBuilder sb) { + sb.append(type); + } + + @Override + protected void appendDebugInfo(StringBuilder sb) { + sb.append(type); + } + + @Override + public boolean equals(Object o) { + return o instanceof Anchor && type == ((Anchor) o).type; + } + + @Override + public int hashCode() { + return type ^ 0xe85317ff; + } + } + + static final class WordBoundary extends RegExpTreeAtom { + final char type; + + WordBoundary(char type) { + this.type = type; + } + + @Override + public RegExpTree simplify(String flags) { + return this; + } + + @Override + protected void appendSourceCode(StringBuilder sb) { + sb.append('\\').append(type); + } + + @Override + protected void appendDebugInfo(StringBuilder sb) { + sb.append(type); + } + + @Override + public boolean equals(Object o) { + return o instanceof WordBoundary && type == ((WordBoundary) o).type; + } + + @Override + public int hashCode() { + return 0x5673aa29 ^ type; + } + } + + static final class BackReference extends RegExpTreeAtom { + final int groupIndex; + + BackReference(int groupIndex) { + Preconditions.checkArgument(groupIndex >= 0 && groupIndex <= 99); + this.groupIndex = groupIndex; + } + + @Override + public RegExpTree simplify(String flags) { + return this; + } + + @Override + protected void appendSourceCode(StringBuilder sb) { + sb.append('\\').append(groupIndex); + } + + @Override + protected void appendDebugInfo(StringBuilder sb) { + sb.append(groupIndex); + } + + @Override + public boolean equals(Object o) { + return o instanceof BackReference + && groupIndex == ((BackReference) o).groupIndex; + } + + @Override + public int hashCode() { + return 0xff072663 ^ groupIndex; + } + } + + static final class Text extends RegExpTreeAtom { + final String text; + + Text(String text) { + this.text = text; + } + + /** + * @param ch The code-unit to escape. + * @param next The next code-unit or -1 if indeterminable. + */ + private static void escapeRegularCharOnto( + char ch, int next, StringBuilder sb) { + switch (ch) { + case '$': + case '^': + case '*': + case '(': + case ')': + case '+': + case '[': + case '|': + case '.': + case '/': + case '?': + sb.append('\\').append(ch); + break; + case '{': + // If possibly part of a repetition, then escape. + // Concatenation is handled by the digitsMightBleed check. + if ('0' <= next && next <= '9') { + sb.append('\\'); + } + sb.append(ch); + break; + default: + escapeCharOnto(ch, sb); + } + } + + @Override + public RegExpTree simplify(String flags) { + int n = text.length(); + if (n == 0) { + return Empty.INSTANCE; + } + if (flags.indexOf('i') >= 0) { + String canonicalized = CaseCanonicalize.caseCanonicalize(text); + if (text != canonicalized) { + return new Text(canonicalized); + } + } + return this; + } + + @Override + public boolean isCaseSensitive() { + for (int i = 0, n = text.length(); i < n; ++i) { + if (CaseCanonicalize.CASE_SENSITIVE.contains(text.charAt(i))) { + return true; + } + } + return false; + } + + @Override + protected void appendSourceCode(StringBuilder sb) { + for (int i = 0, n = text.length(); i < n; ++i) { + escapeRegularCharOnto(text.charAt(i), i + 1 < n ? text.charAt(i + 1) : -1, sb); + } + } + + @Override + protected void appendDebugInfo(StringBuilder sb) { + sb.append('`').append(text).append('`'); + } + + @Override + public boolean equals(Object o) { + return o instanceof Text && text.equals(((Text) o).text); + } + + @Override + public int hashCode() { + return text.hashCode() ^ 0x617e310; + } + } + + static final class Repetition extends RegExpTree { + final RegExpTree body; + final int min, max; + final boolean greedy; + + Repetition(RegExpTree body, int min, int max, boolean greedy) { + this.body = body; + this.min = min; + this.max = max; + this.greedy = greedy; + } + + @Override + public RegExpTree simplify(String flags) { + RegExpTree body = this.body.simplify(flags); + if (max == 0 && !body.hasCapturingGroup()) { return Empty.INSTANCE; } + if (body instanceof Empty || NEVER_MATCHES.equals(body)) { return body; } + int min = this.min; + int max = this.max; + if (body instanceof Repetition) { + Repetition rbody = (Repetition) body; + if (rbody.greedy == greedy) { + long lmin = ((long) min) * rbody.min; + long lmax = ((long) max) * rbody.max; + if (lmin < Integer.MAX_VALUE) { + body = rbody.body; + min = (int) lmin; + max = lmax >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) lmax; + } + } + } + if (min == 1 && max == 1) { return body; } + boolean greedy = this.greedy || min == max; + return body.equals(this.body) && min == this.min && max == this.max + && greedy == this.greedy + ? this + : new Repetition(body, min, max, greedy).simplify(flags); + } + + @Override + public boolean isCaseSensitive() { + return body.isCaseSensitive(); + } + + @Override + public boolean containsAnchor() { + return body.containsAnchor(); + } + + @Override + public int numCapturingGroups() { + return body.numCapturingGroups(); + } + + @Override + public List children() { + return ImmutableList.of(body); + } + + private void appendBodySourceCode(StringBuilder sb) { + if (body instanceof Alternation || body instanceof Concatenation + || body instanceof Repetition + || (body instanceof Text && ((Text) body).text.length() > 1)) { + sb.append("(?:"); + body.appendSourceCode(sb); + sb.append(')'); + } else { + body.appendSourceCode(sb); + } + } + + private static int suffixLen(int min, int max) { + // This mirrors the branches that renders a suffix in appendSourceCode below. + if (max == Integer.MAX_VALUE) { + switch (min) { + case 0: return 1; // * + case 1: return 1; // + + default: return 3 + numDecimalDigits(min); // {3,} + } + } + if (min == 0 && max == 1) { + return 1; // ? + } + if (min == max) { + if (min == 1) { + return 0; // No suffix needed for {1}. + } + return 2 + numDecimalDigits(min); // {4} + } + return 3 + numDecimalDigits(min) + numDecimalDigits(max); // {2,7} + } + + private static int numDecimalDigits(int n) { + if (n < 0) { + // Negative values should not be passed in. + throw new AssertionError(); + // If changing this code to support negative values, + // Integer.MIN_VALUE is a corner-case.. + } + int nDigits = 1; + while (n >= 10) { + ++nDigits; + n /= 10; + } + return nDigits; + } + + @Override + protected void appendSourceCode(StringBuilder sb) { + int bodyStart = sb.length(); + appendBodySourceCode(sb); + int bodyEnd = sb.length(); + int bodyLen = bodyEnd - bodyStart; + int min = this.min; + int max = this.max; + if (min >= 2 && max == Integer.MAX_VALUE || max - min <= 1) { + int expanded = + // If min == max then we want to try expanding to the limit and + // attach the empty suffix, which is equivalent to min = max = 1, + // i.e. /a/ vs /a{1}/. + min == max + // Give aa+ preference over aaa*. + || max == Integer.MAX_VALUE + ? min - 1 + : min; + int expandedMin = min - expanded; + int expandedMax = max == Integer.MAX_VALUE ? max : max - expanded; + int suffixLen = suffixLen(min, max); + int expandedSuffixLen = suffixLen(expandedMin, expandedMax); + if (bodyLen * expanded + expandedSuffixLen < suffixLen + && !body.hasCapturingGroup()) { + // a{2} -> aa + // a{2,} -> aa+ + // a{2,3} -> aaa? + while (--expanded >= 0) { + sb.append(sb, bodyStart, bodyEnd); + } + min = expandedMin; + max = expandedMax; + } + } + + if (max == Integer.MAX_VALUE) { + switch (min) { + case 0: sb.append('*'); break; + case 1: sb.append('+'); break; + default: + sb.append('{').append(min).append(",}"); + } + } else if (min == 0 && max == 1) { + sb.append('?'); + } else if (min == max) { + if (min != 1) { + sb.append('{').append(min).append('}'); + } + } else { + sb.append('{').append(min).append(',').append(max).append('}'); + } + if (!greedy) { + sb.append('?'); + } + } + + @Override + protected void appendDebugInfo(StringBuilder sb) { + sb.append(" min=").append(min).append(", max=").append(max); + if (!greedy) { sb.append(" not_greedy"); } + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Repetition)) { return false; } + Repetition that = (Repetition) o; + return this.body.equals(that.body) + && this.min == that.min + && this.max == that.max + && this.greedy == that.greedy; + } + + @Override + public int hashCode() { + return min + 31 * (max + 31 * ((greedy ? 1 : 0) + 31 * body.hashCode())); + } + } + + static final class Alternation extends RegExpTree { + final ImmutableList alternatives; + + Alternation(List alternatives) { + this.alternatives = ImmutableList.copyOf(alternatives); + } + + @Override + public RegExpTree simplify(String flags) { + List alternatives = Lists.newArrayList(); + for (RegExpTree alternative : this.alternatives) { + alternative = alternative.simplify(flags); + if (alternative instanceof Alternation) { + alternatives.addAll(((Alternation) alternative).alternatives); + } else { + alternatives.add(alternative); + } + } + // Remove duplicates + RegExpTree last = null; + for (Iterator it = alternatives.iterator(); it.hasNext();) { + RegExpTree alternative = it.next(); + if (alternative.equals(NEVER_MATCHES)) { continue; } + if (alternative.equals(last) && !alternative.hasCapturingGroup()) { + it.remove(); + } else { + last = alternative; + } + } + // Collapse character alternatives into character sets. + for (int i = 0, n = alternatives.size(); i < n; ++i) { + RegExpTree alternative = alternatives.get(i); + if ((alternative instanceof Text + && ((Text) alternative).text.length() == 1) + || alternative instanceof Charset) { + int end = i; + int nCharsets = 0; + while (end < n) { + RegExpTree follower = alternatives.get(end); + if (follower instanceof Charset) { + ++nCharsets; + } else if (!(follower instanceof Text + && ((Text) follower).text.length() == 1)) { + break; + } + ++end; + } + if (end - i >= 3 || (nCharsets != 0 && end - i >= 2)) { + int[] members = new int[end - i - nCharsets]; + int memberIdx = 0; + CharRanges chars = CharRanges.EMPTY; + CharRanges ieExplicits = CharRanges.EMPTY; + List charAlternatives = alternatives.subList(i, end); + for (RegExpTree charAlternative : charAlternatives) { + if (charAlternative instanceof Text) { + char ch = ((Text) charAlternative).text.charAt(0); + members[memberIdx++] = ch; + if (IE_SPEC_ERRORS.contains(ch)) { + ieExplicits = ieExplicits.union(CharRanges.inclusive(ch, ch)); + } + } else if (charAlternative instanceof Charset) { + Charset cs = (Charset) charAlternative; + chars = chars.union(cs.ranges); + ieExplicits = ieExplicits.union(cs.ieExplicits); + } + } + chars = chars.union(CharRanges.withMembers(members)); + charAlternatives.clear(); + charAlternatives.add( + new Charset(chars, ieExplicits).simplify(flags)); + n = alternatives.size(); + } + } + } + switch (alternatives.size()) { + case 0: return Empty.INSTANCE; + case 1: return alternatives.get(0); + case 2: + if (alternatives.get(1) instanceof Empty) { // (?:a|) -> a? + return new Repetition(alternatives.get(0), 0, 1, true); + } else if (alternatives.get(0) instanceof Empty) { + return new Repetition(alternatives.get(1), 0, 1, false); + } + break; + } + // TODO: maybe pull out common prefix or suffix + return alternatives.equals(this.alternatives) + ? this : new Alternation(alternatives); + } + + @Override + public boolean isCaseSensitive() { + for (RegExpTree alternative : alternatives) { + if (alternative.isCaseSensitive()) { return true; } + } + return false; + } + + @Override + public boolean containsAnchor() { + for (RegExpTree alternative : alternatives) { + if (alternative.containsAnchor()) { return true; } + } + return false; + } + + @Override + public int numCapturingGroups() { + int n = 0; + for (RegExpTree alternative : alternatives) { + n += alternative.numCapturingGroups(); + } + return n; + } + + @Override + public List children() { + return alternatives; + } + + @Override + protected void appendSourceCode(StringBuilder sb) { + for (int i = 0, n = alternatives.size(); i < n; ++i) { + if (i != 0) { + sb.append('|'); + } + alternatives.get(i).appendSourceCode(sb); + } + } + + @Override + protected void appendDebugInfo(StringBuilder sb) { + // Nothing besides children. + } + + @Override + public boolean equals(Object o) { + return this == o || ( + (o instanceof Alternation) + && alternatives.equals(((Alternation) o).alternatives)); + } + + @Override + public int hashCode() { + return 0x51b57cd1 ^ alternatives.hashCode(); + } + } + + private static final RegExpTree NEVER_MATCHES = new LookaheadAssertion( + Empty.INSTANCE, false); + + static final class LookaheadAssertion extends RegExpTree { + final RegExpTree body; + final boolean positive; + + LookaheadAssertion(RegExpTree body, boolean positive) { + this.body = body; + this.positive = positive; + } + + @Override + public RegExpTree simplify(String flags) { + RegExpTree simpleBody = body.simplify(flags); + if (simpleBody instanceof Empty) { + if (positive) { // Always true + return simpleBody; + } + } + return new LookaheadAssertion(simpleBody, positive); + } + + @Override + public boolean isCaseSensitive() { + return body.isCaseSensitive(); + } + + @Override + public boolean containsAnchor() { + return body.containsAnchor(); + } + + @Override + public int numCapturingGroups() { + return body.numCapturingGroups(); + } + + @Override + public List children() { + return ImmutableList.of(body); + } + + @Override + protected void appendSourceCode(StringBuilder sb) { + sb.append(positive ? "(?=" : "(?!"); + body.appendSourceCode(sb); + sb.append(')'); + } + + @Override + protected void appendDebugInfo(StringBuilder sb) { + sb.append(positive ? "positive" : "negative"); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof LookaheadAssertion)) { return false; } + LookaheadAssertion that = (LookaheadAssertion) o; + return this.positive == that.positive && this.body.equals(that.body); + } + + @Override + public int hashCode() { + return 0x723aba9 ^ body.hashCode(); + } + } + + static final class CapturingGroup extends RegExpTree { + final RegExpTree body; + + CapturingGroup(RegExpTree body) { + this.body = body; + } + + @Override + public RegExpTree simplify(String flags) { + return new CapturingGroup(body.simplify(flags)); + } + + @Override + public boolean isCaseSensitive() { + return body.isCaseSensitive(); + } + + @Override + public boolean containsAnchor() { + return body.containsAnchor(); + } + + @Override + public int numCapturingGroups() { + return 1; + } + + @Override + public List children() { + return ImmutableList.of(body); + } + + @Override + protected void appendSourceCode(StringBuilder sb) { + sb.append('('); + body.appendSourceCode(sb); + sb.append(')'); + } + + @Override + protected void appendDebugInfo(StringBuilder sb) { + // Nothing besides children. + } + + @Override + public boolean equals(Object o) { + return o instanceof CapturingGroup + && body.equals(((CapturingGroup) o).body); + } + + @Override + public int hashCode() { + return 0x55781738 ^ body.hashCode(); + } + } + + private static final CharRanges DIGITS = CharRanges.inclusive('0', '9'); + + private static final CharRanges UCASE_LETTERS + = CharRanges.inclusive('A', 'Z'); + + private static final CharRanges LCASE_LETTERS + = CharRanges.inclusive('a', 'z'); + + private static final CharRanges LETTERS = UCASE_LETTERS.union(LCASE_LETTERS); + + private static final CharRanges WORD_CHARS = DIGITS + .union(LETTERS).union(CharRanges.withMembers('_')); + + private static final CharRanges INVERSE_WORD_CHARS + = CharRanges.ALL_CODE_UNITS.difference(WORD_CHARS); + + private static final CharRanges SPACE_CHARS = CharRanges.withMembers( + '\t', '\n', '\u000b', '\u000c', '\r', ' ', '\u00a0', + // Unicode 3.0 Zs + '\u1680', '\u180e', '\u2000', '\u2001', + '\u2002', '\u2003', '\u2004', '\u2005', + '\u2006', '\u2007', '\u2008', '\u2009', + '\u200a', + // Line terminator chars + '\u2028', '\u2029', + // Unicode 3.0 Zs + '\u202f', '\u205f', '\u3000', + // Byte order marker is a space character in ES5 but not ES3. + '\ufeff' + ); + + /** IE is broken around \s. IE (6, 7, 8 at least), only recognize these. */ + private static final CharRanges IE_SPACE_CHARS = CharRanges.withMembers( + '\t', '\n', '\u000b', '\u000c', '\r', ' ' + ); + + /** IE is broken around \s. IE (6, 7, 8 at least), only recognize these. */ + private static final CharRanges IE_SPEC_ERRORS = SPACE_CHARS.difference( + IE_SPACE_CHARS); + + private static final ImmutableMap NAMED_CHAR_GROUPS + = ImmutableMap.builder() + .put('d', DIGITS) + .put('D', CharRanges.ALL_CODE_UNITS.difference(DIGITS)) + .put('s', SPACE_CHARS) + .put('S', CharRanges.ALL_CODE_UNITS.difference(SPACE_CHARS)) + .put('w', WORD_CHARS) + .put('W', INVERSE_WORD_CHARS) + .build(); + + private static final Charset DOT_CHARSET = new Charset( + CharRanges.ALL_CODE_UNITS.difference( + CharRanges.withMembers('\n', '\r', '\u2028', '\u2029')), + CharRanges.EMPTY); + + static final class Charset extends RegExpTreeAtom { + final CharRanges ranges; + /** + * Code units that were mentioned explicitly and that might be matched by + * a group according to ECMAScript 5 but would not because of specification + * violations in IE. + */ + final CharRanges ieExplicits; + + Charset(CharRanges ranges, CharRanges ieExplicits) { + this.ranges = ranges; + this.ieExplicits = ieExplicits; + } + + private static int complexityWordFolded(CharRanges ranges) { + return Math.min( + complexityWordFoldedHelper(ranges), + 1 + complexityWordFoldedHelper( + CharRanges.ALL_CODE_UNITS.difference(ranges))); + } + + private static int complexityWordFoldedHelper(CharRanges ranges) { + int complexity = DecomposedCharset.complexity(ranges); + if (ranges.containsAll(WORD_CHARS)) { + complexity = Math.min( + complexity, + 1 + DecomposedCharset.complexity(ranges.difference(WORD_CHARS))); + } + if (ranges.containsAll(INVERSE_WORD_CHARS)) { + complexity = Math.min( + complexity, + 1 + DecomposedCharset.complexity( + ranges.difference(INVERSE_WORD_CHARS))); + } + return complexity; + } + + @Override + public RegExpTree simplify(String flags) { + if (ranges.isEmpty()) { + return NEVER_MATCHES; + } + CharRanges best = ranges; + if (flags.indexOf('i') >= 0) { + Set options = Sets.newLinkedHashSet(); + options.add(CaseCanonicalize.expandToAllMatched(ranges)); + options.add(CaseCanonicalize.reduceToMinimum(ranges)); + + CharRanges lcaseLetters = ranges.intersection(LCASE_LETTERS); + CharRanges ucaseLetters = ranges.intersection(UCASE_LETTERS); + + CharRanges lcaseLettersToUpper = lcaseLetters.shift(-32); + CharRanges ucaseLettersToLower = ucaseLetters.shift(32); + + options.add(ranges.union(ucaseLettersToLower)); + options.add(ranges.union(lcaseLettersToUpper)); + options.add(ranges.union(lcaseLettersToUpper) + .union(ucaseLettersToLower)); + + options.add(ranges.union(ucaseLettersToLower).difference(ucaseLetters)); + options.add(ranges.union(lcaseLettersToUpper).difference(lcaseLetters)); + + int bestComplexity = complexityWordFolded(ranges); + + for (CharRanges option : options) { + int complexity = complexityWordFolded(option); + if (complexity < bestComplexity) { + bestComplexity = complexity; + best = option; + } + } + } + + if (best.getNumRanges() == 1 + && best.end(0) - best.start(0) == 1) { + return new Text(Character.toString((char) best.start(0))); + } + + if (!best.equals(ranges)) { + return new Charset(best, ieExplicits); + } + + return this; + } + + @Override + public boolean isCaseSensitive() { + // We could test + // !ranges.equals(CaseCanonicalize.expandToAllMatched(ranges)) + // but we get better optimizations by leaving the 'i' flag on in most + // cases. + + // Check whether skipping all the character groups that are known + // case-insensitive leaves us with something that matches the above + // definition. + CharRanges withoutNamedGroups = decompose().ranges; + return !withoutNamedGroups.equals( + CaseCanonicalize.expandToAllMatched(withoutNamedGroups)); + } + + private DecomposedCharset decompose(CharRanges ranges, boolean inverted) { + StringBuilder namedGroups = new StringBuilder(); + CharRanges rangesInterIeExplicits = ranges.intersection(ieExplicits); + while (true) { + char groupName = 0; + CharRanges simplest = null; + int minComplexity = DecomposedCharset.complexity(ranges); + for (Map.Entry namedGroup + : NAMED_CHAR_GROUPS.entrySet()) { + CharRanges group = namedGroup.getValue(); + if (ranges.containsAll(group)) { + CharRanges withoutGroup = ranges.difference(group).union( + rangesInterIeExplicits); + int complexity = DecomposedCharset.complexity(withoutGroup); + if (complexity < minComplexity) { + simplest = withoutGroup; + groupName = namedGroup.getKey().charValue(); + minComplexity = complexity; + } + } + } + if (simplest != null) { + namedGroups.append('\\').append(groupName); + ranges = simplest; + } else { + break; + } + } + return new DecomposedCharset(inverted, ranges, namedGroups.toString()); + } + + @Override + protected void appendSourceCode(StringBuilder sb) { + if (DOT_CHARSET.ranges.equals(ranges)) { + sb.append('.'); + return; + } + decompose().appendSourceCode(sb); + } + + DecomposedCharset decompose() { + CharRanges negRanges = CharRanges.ALL_CODE_UNITS.difference(ranges); + if (!ieExplicits.isEmpty()) { + if (negRanges.intersection(ieExplicits).isEmpty()) { + return decompose(ranges, false); + } else if (ranges.intersection(ieExplicits).isEmpty()) { + return decompose(negRanges, true); + } + } + DecomposedCharset positive = decompose(ranges, false); + DecomposedCharset negative = decompose(negRanges, true); + return positive.complexity() <= negative.complexity() + ? positive : negative; + } + + @Override + protected void appendDebugInfo(StringBuilder sb) { + sb.append(ranges); + } + + @Override + public boolean equals(Object o) { + return o instanceof Charset && ranges.equals(((Charset) o).ranges); + } + + @Override + public int hashCode() { + return ranges.hashCode() ^ 0xdede2246; + } + } + + static final class DecomposedCharset { + boolean inverted; + final CharRanges ranges; + final String namedGroups; + + DecomposedCharset( + boolean inverted, CharRanges ranges, String namedGroups) { + this.inverted = inverted; + this.ranges = ranges; + this.namedGroups = namedGroups; + } + + int complexity() { + return (inverted ? 1 : 0) + namedGroups.length() + complexity(ranges); + } + + void appendSourceCode(StringBuilder sb) { + if (ranges.isEmpty()) { + if (!inverted && namedGroups.length() == 2) { + sb.append(namedGroups); + return; + } else if (ranges.isEmpty() && namedGroups.length() == 0) { + sb.append(inverted ? "[\\S\\s]" : "(?!)"); + return; + } + } + sb.append('['); + if (inverted) { sb.append('^'); } + sb.append(namedGroups); + boolean rangesStartCharset = !inverted && namedGroups.length() == 0; + boolean emitDashAtEnd = false; + for (int i = 0, n = ranges.getNumRanges(); i < n; ++i) { + char start = (char) ranges.start(i); + char end = (char) (ranges.end(i) - 1); + switch (end - start) { + case 0: + if (start == '-') { + // Put it at the end where it doesn't need escaping. + emitDashAtEnd = true; + } else { + escapeRangeCharOnto( + start, rangesStartCharset, i == 0, i + 1 == n, sb); + } + break; + case 1: + escapeRangeCharOnto(start, rangesStartCharset, i == 0, false, sb); + escapeRangeCharOnto( + end, rangesStartCharset, false, i + 1 == n, sb); + break; + default: + escapeRangeCharOnto(start, rangesStartCharset, i == 0, false, sb); + sb.append('-'); + escapeRangeCharOnto(end, rangesStartCharset, false, true, sb); + break; + } + } + if (emitDashAtEnd) { sb.append('-'); } + sb.append(']'); + } + + static void escapeRangeCharOnto( + char ch, boolean startIsFlush, boolean atStart, boolean atEnd, + StringBuilder sb) { + switch (ch) { + case '\b': + sb.append("\\b"); + break; + case '^': + sb.append(atStart && startIsFlush ? "\\^" : "^"); + break; + case '-': + sb.append(atStart || atEnd ? "-" : "\\-"); + break; + case '\\': + case ']': + sb.append('\\').append(ch); + break; + default: + escapeCharOnto(ch, sb); + } + } + + static int complexity(CharRanges ranges) { + int complexity = 0; + for (int i = 0, n = ranges.getNumRanges(); i < n; ++i) { + int start = ranges.start(i); + int end = ranges.end(i) - 1; + if (start < 0x20 || start >= 0x7f) { + complexity += start >= 0x100 ? 6 : 4; + } else { + ++complexity; + } + switch (end - start) { + case 0: continue; + case 1: break; + default: complexity += 1; + } + if (end < 0x20 || end >= 0x7f) { + complexity += end >= 0x100 ? 6 : 4; + } else { + ++complexity; + } + } + return complexity; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof DecomposedCharset)) { + return false; + } + DecomposedCharset that = (DecomposedCharset) o; + return this.inverted = that.inverted + && this.ranges.equals(that.ranges) + && this.namedGroups.equals(that.namedGroups); + } + + @Override + public int hashCode() { + return ranges.hashCode() + + 31 * (namedGroups.hashCode() + (inverted ? 1 : 0)); + } + } + + static final class Concatenation extends RegExpTree { + final ImmutableList elements; + + Concatenation(RegExpTree a, RegExpTree b) { + elements = ImmutableList.of(a, b); + } + + Concatenation(List elements) { + this.elements = ImmutableList.copyOf(elements); + } + + @Override + public RegExpTree simplify(final String flags) { + class Simplifier { + final List simplified = Lists.newArrayList(); + + void simplify(RegExpTree t) { + if (t instanceof Concatenation) { + for (RegExpTree child : ((Concatenation) t).elements) { + simplify(child); + } + } else if (t instanceof Empty) { + // Do nothing + } else { + int lastIndex = simplified.size() - 1; + if (lastIndex >= 0) { + RegExpTree pairwise = simplifyPairwise( + simplified.get(lastIndex), t); + if (pairwise != null) { + simplified.set(lastIndex, pairwise); + return; + } + } + simplified.add(t); + } + } + + RegExpTree simplifyPairwise(RegExpTree before, RegExpTree after) { + if (before instanceof Text && after instanceof Text) { + return new Text( + ((Text) before).text + ((Text) after).text).simplify(flags); + } + // Fold adjacent repetitions. + int beforeMin = 1, beforeMax = 1; + RegExpTree beforeBody = before; + boolean beforeGreedy = false; + if (before instanceof Repetition) { + Repetition r = (Repetition) before; + beforeMin = r.min; + beforeMax = r.max; + beforeBody = r.body; + beforeGreedy = r.greedy; + } + int afterMin = 1, afterMax = 1; + RegExpTree afterBody = after; + boolean afterGreedy = false; + if (after instanceof Repetition) { + Repetition r = (Repetition) after; + afterMin = r.min; + afterMax = r.max; + afterBody = r.body; + afterGreedy = r.greedy; + } + if (beforeBody.equals(afterBody) + && !beforeBody.hasCapturingGroup()) { + long lmin = ((long) beforeMin) + afterMin; + long lmax = ((long) beforeMax) + afterMax; + if (lmin < Integer.MAX_VALUE) { + int min = (int) lmin; + int max = lmax >= Integer.MAX_VALUE + ? Integer.MAX_VALUE : (int) lmax; + return new Repetition( + beforeBody, min, max, + beforeGreedy || afterGreedy || min == max); + } + } + return null; + } + } + + Simplifier s = new Simplifier(); + for (RegExpTree element : elements) { + s.simplify(element.simplify(flags)); + } + + switch (s.simplified.size()) { + case 0: return Empty.INSTANCE; + case 1: return s.simplified.get(0); + default: return new Concatenation(s.simplified); + } + } + + @Override + public boolean isCaseSensitive() { + for (RegExpTree element : elements) { + if (element.isCaseSensitive()) { + return true; + } + } + return false; + } + + @Override + public boolean containsAnchor() { + for (RegExpTree element : elements) { + if (element.containsAnchor()) { + return true; + } + } + return false; + } + + @Override + public int numCapturingGroups() { + int n = 0; + for (RegExpTree element : elements) { + n += element.numCapturingGroups(); + } + return n; + } + + @Override + public List children() { + return elements; + } + + @Override + protected void appendSourceCode(StringBuilder sb) { + // True if the last content written might consume + // decimal digits written subsequently. + boolean digitsMightBleed = false; + for (RegExpTree element : elements) { + boolean parenthesize = false; + if (element instanceof Alternation + || element instanceof Concatenation) { + parenthesize = true; + } + if (parenthesize) { + sb.append("(?:"); + element.appendSourceCode(sb); + sb.append(')'); + } else { + int start = sb.length(); + element.appendSourceCode(sb); + if (digitsMightBleed && sb.length() > start) { + char firstChar = sb.charAt(start); + if ('0' <= firstChar && firstChar <= '9') { + // Bleeding happened. + // If the last character would be ambiguous + // with a repetition, escape it. + if (sb.charAt(start - 1) == '{') { + // Concatenation from optimization of + // /{(?:0,}/ -> /\{0,}/ + sb.insert(start - 1, '\\'); + } else { + // Or parenthesize otherwise. + // Concatenation from optimization of + // /(.)\1(?:0)/ -> /(.)\1(?:0)/. + sb.insert(start, "(?:").append(')'); + } + } + } + } + digitsMightBleed = ( + // \1(?:0) bleeds if there are 10 or more + // capturing groups preceding. + (element instanceof BackReference + && ((BackReference) element).groupIndex < 10) + // foo{(?:10}) bleeds. + || (element instanceof Text + && ((Text) element).text.endsWith("{"))); + } + } + + @Override + protected void appendDebugInfo(StringBuilder sb) { + // Nothing besides children. + } + + @Override + public boolean equals(Object o) { + return o instanceof Concatenation + && elements.equals(((Concatenation) o).elements); + } + + @Override + public int hashCode() { + return 0x20997e3e ^ elements.hashCode(); + } + } + + static void escapeCharOnto(char ch, StringBuilder sb) { + switch (ch) { + case '\u0000': + sb.append("\\0"); + break; + case '\f': + sb.append("\\f"); + break; + case '\t': + sb.append("\\t"); + break; + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '\\': + sb.append("\\\\"); + break; + default: + if (ch < 0x20 || ch >= 0x7f) { + if (ch >= 0x100) { + sb.append("\\u"); + sb.append("0123456789abcdef".charAt((ch >> 12) & 0xf)); + sb.append("0123456789abcdef".charAt((ch >> 8) & 0xf)); + sb.append("0123456789abcdef".charAt((ch >> 4) & 0xf)); + sb.append("0123456789abcdef".charAt((ch) & 0xf)); + } else { + sb.append("\\x"); + sb.append("0123456789abcdef".charAt((ch >> 4) & 0xf)); + sb.append("0123456789abcdef".charAt((ch) & 0xf)); + } + } else { + sb.append(ch); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/testing/SimpleSourceExcerptProvider.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/testing/SimpleSourceExcerptProvider.java new file mode 100644 index 0000000..4284a5f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/testing/SimpleSourceExcerptProvider.java @@ -0,0 +1,46 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.testing; + +import com.google.javascript.jscomp.Region; +import com.google.javascript.jscomp.SourceExcerptProvider; +import com.google.javascript.jscomp.SourceFile; + + + +/** + * A simple source excerpt provider for testing. + * @author nicksantos@google.com (Nick Santos) + */ +public class SimpleSourceExcerptProvider implements SourceExcerptProvider { + + private final SourceFile sourceFile; + + public SimpleSourceExcerptProvider(String source) { + sourceFile = SourceFile.fromCode("input", source); + } + + @Override + public String getSourceLine(String sourceName, int lineNumber) { + return sourceFile.getLine(lineNumber); + } + + @Override + public Region getSourceRegion(String sourceName, int lineNumber) { + return sourceFile.getRegion(lineNumber); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/testing/TestErrorReporter.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/testing/TestErrorReporter.java new file mode 100644 index 0000000..c02d4e0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/testing/TestErrorReporter.java @@ -0,0 +1,91 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.testing; + +import com.google.javascript.rhino.head.ErrorReporter; +import com.google.javascript.rhino.head.EvaluatorException; + +import junit.framework.Assert; + +/** + *

      An error reporter for testing that verifies that messages reported to the + * reporter are expected.

      + * + *

      Sample use

      + *
      + * TestErrorReporter e =
      + *   new TestErrorReporter(null, new String[] { "first warning" });
      + * ...
      + * assertTrue(e.hasEncounteredAllWarnings());
      + * 
      + * + */ +public final class TestErrorReporter extends Assert implements ErrorReporter { + private final String[] errors; + private final String[] warnings; + private int errorsIndex = 0; + private int warningsIndex = 0; + + public TestErrorReporter(String[] errors, String[] warnings) { + this.errors = errors; + this.warnings = warnings; + } + + @Override + public void error(String message, String sourceName, int line, + String lineSource, int lineOffset) { + if (errors != null && errorsIndex < errors.length) { + assertEquals(errors[errorsIndex++], message); + } else { + fail("extra error: " + message); + } + } + + @Override + public void warning(String message, String sourceName, int line, + String lineSource, int lineOffset) { + if (warnings != null && warningsIndex < warnings.length) { + assertEquals(warnings[warningsIndex++], message); + } else { + fail("extra warning: " + message); + } + } + + @Override + public EvaluatorException runtimeError(String message, String sourceName, + int line, String lineSource, int lineOffset) { + return new EvaluatorException("JSCompiler test code: " + message); + } + + /** + * Returns whether all warnings were reported to this reporter. + */ + public boolean hasEncounteredAllWarnings() { + return (warnings == null) ? + warningsIndex == 0 : + warnings.length == warningsIndex; + } + + /** + * Returns whether all errors were reported to this reporter. + */ + public boolean hasEncounteredAllErrors() { + return (errors == null) ? + errorsIndex == 0 : + errors.length == errorsIndex; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/ChainableReverseAbstractInterpreter.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/ChainableReverseAbstractInterpreter.java new file mode 100644 index 0000000..fa7d47d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/ChainableReverseAbstractInterpreter.java @@ -0,0 +1,718 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.type; + +import static com.google.javascript.rhino.jstype.JSTypeNative.ALL_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.CHECKED_UNKNOWN_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NO_OBJECT_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NO_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NULL_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.U2U_CONSTRUCTOR_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; + +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.CodingConvention; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.EnumElementType; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.ParameterizedType; +import com.google.javascript.rhino.jstype.StaticSlot; +import com.google.javascript.rhino.jstype.TemplateType; +import com.google.javascript.rhino.jstype.UnionType; +import com.google.javascript.rhino.jstype.Visitor; + +/** + * Chainable reverse abstract interpreter providing basic functionality. + * + */ +public abstract class ChainableReverseAbstractInterpreter + implements ReverseAbstractInterpreter { + protected final CodingConvention convention; + final JSTypeRegistry typeRegistry; + private ChainableReverseAbstractInterpreter firstLink; + private ChainableReverseAbstractInterpreter nextLink; + + /** + * Constructs an interpreter, which is the only link in a chain. Interpreters + * can be appended using {@link #append}. + */ + public ChainableReverseAbstractInterpreter(CodingConvention convention, + JSTypeRegistry typeRegistry) { + Preconditions.checkNotNull(convention); + this.convention = convention; + this.typeRegistry = typeRegistry; + firstLink = this; + nextLink = null; + } + + /** + * Appends a link to {@code this}, returning the updated last link. + *

      + * The pattern {@code new X().append(new Y())...append(new Z())} forms a + * chain starting with X, then Y, then ... Z. + * @param lastLink a chainable interpreter, with no next link + * @return the updated last link + */ + public ChainableReverseAbstractInterpreter append( + ChainableReverseAbstractInterpreter lastLink) { + Preconditions.checkArgument(lastLink.nextLink == null); + this.nextLink = lastLink; + lastLink.firstLink = this.firstLink; + return lastLink; + } + + /** + * Gets the first link of this chain. + */ + public ChainableReverseAbstractInterpreter getFirst() { + return firstLink; + } + + /** + * Calculates the preciser scope starting with the first link. + */ + protected FlowScope firstPreciserScopeKnowingConditionOutcome(Node condition, + FlowScope blindScope, boolean outcome) { + return firstLink.getPreciserScopeKnowingConditionOutcome( + condition, blindScope, outcome); + } + + /** + * Delegates the calculation of the preciser scope to the next link. + * If there is no next link, returns the blind scope. + */ + protected FlowScope nextPreciserScopeKnowingConditionOutcome(Node condition, + FlowScope blindScope, boolean outcome) { + return nextLink != null ? nextLink.getPreciserScopeKnowingConditionOutcome( + condition, blindScope, outcome) : blindScope; + } + + /** + * Returns the type of a node in the given scope if the node corresponds to a + * name whose type is capable of being refined. + * @return The current type of the node if it can be refined, null otherwise. + */ + protected JSType getTypeIfRefinable(Node node, FlowScope scope) { + switch (node.getType()) { + case Token.NAME: + StaticSlot nameVar = scope.getSlot(node.getString()); + if (nameVar != null) { + JSType nameVarType = nameVar.getType(); + if (nameVarType == null) { + nameVarType = node.getJSType(); + } + return nameVarType; + } + return null; + + case Token.GETPROP: + String qualifiedName = node.getQualifiedName(); + if (qualifiedName == null) { + return null; + } + StaticSlot propVar = scope.getSlot(qualifiedName); + JSType propVarType = null; + if (propVar != null) { + propVarType = propVar.getType(); + } + if (propVarType == null) { + propVarType = node.getJSType(); + } + if (propVarType == null) { + propVarType = getNativeType(UNKNOWN_TYPE); + } + return propVarType; + } + return null; + } + + /** + * Declares a refined type in {@code scope} for the name represented by + * {@code node}. It must be possible to refine the type of the given node in + * the given scope, as determined by {@link #getTypeIfRefinable}. + */ + protected void declareNameInScope(FlowScope scope, Node node, JSType type) { + switch (node.getType()) { + case Token.NAME: + scope.inferSlotType(node.getString(), type); + break; + + case Token.GETPROP: + String qualifiedName = node.getQualifiedName(); + Preconditions.checkNotNull(qualifiedName); + + JSType origType = node.getJSType(); + origType = origType == null ? getNativeType(UNKNOWN_TYPE) : origType; + scope.inferQualifiedSlot(node, qualifiedName, origType, type); + break; + + case Token.THIS: + // "this" references aren't currently modeled in the CFG. + break; + + default: + throw new IllegalArgumentException("Node cannot be refined. \n" + + node.toStringTree()); + } + } + + /** + * @see #getRestrictedWithoutUndefined(JSType) + */ + private final Visitor restrictUndefinedVisitor = + new Visitor() { + @Override + public JSType caseEnumElementType(EnumElementType enumElementType) { + JSType type = enumElementType.getPrimitiveType().visit(this); + if (type != null && enumElementType.getPrimitiveType().isEquivalentTo(type)) { + return enumElementType; + } else { + return type; + } + } + + @Override + public JSType caseAllType() { + return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE, + STRING_TYPE, BOOLEAN_TYPE, NULL_TYPE); + } + + @Override + public JSType caseNoObjectType() { + return getNativeType(NO_OBJECT_TYPE); + } + + @Override + public JSType caseNoType() { + return getNativeType(NO_TYPE); + } + + @Override + public JSType caseBooleanType() { + return getNativeType(BOOLEAN_TYPE); + } + + @Override + public JSType caseFunctionType(FunctionType type) { + return type; + } + + @Override + public JSType caseNullType() { + return getNativeType(NULL_TYPE); + } + + @Override + public JSType caseNumberType() { + return getNativeType(NUMBER_TYPE); + } + + @Override + public JSType caseObjectType(ObjectType type) { + return type; + } + + @Override + public JSType caseStringType() { + return getNativeType(STRING_TYPE); + } + + @Override + public JSType caseUnionType(UnionType type) { + return type.getRestrictedUnion(getNativeType(VOID_TYPE)); + } + + @Override + public JSType caseUnknownType() { + return getNativeType(UNKNOWN_TYPE); + } + + @Override + public JSType caseVoidType() { + return null; + } + + @Override + public JSType caseParameterizedType(ParameterizedType type) { + return caseObjectType(type); + } + + @Override + public JSType caseTemplateType(TemplateType templateType) { + return caseObjectType(templateType); + } + }; + + + /** + * @see #getRestrictedWithoutNull(JSType) + */ + private final Visitor restrictNullVisitor = + new Visitor() { + @Override + public JSType caseEnumElementType(EnumElementType enumElementType) { + JSType type = enumElementType.getPrimitiveType().visit(this); + if (type != null && + enumElementType.getPrimitiveType().isEquivalentTo(type)) { + return enumElementType; + } else { + return type; + } + } + + @Override + public JSType caseAllType() { + return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE, + STRING_TYPE, BOOLEAN_TYPE, VOID_TYPE); + } + + @Override + public JSType caseNoObjectType() { + return getNativeType(NO_OBJECT_TYPE); + } + + @Override + public JSType caseNoType() { + return getNativeType(NO_TYPE); + } + + @Override + public JSType caseBooleanType() { + return getNativeType(BOOLEAN_TYPE); + } + + @Override + public JSType caseFunctionType(FunctionType type) { + return type; + } + + @Override + public JSType caseNullType() { + return null; + } + + @Override + public JSType caseNumberType() { + return getNativeType(NUMBER_TYPE); + } + + @Override + public JSType caseObjectType(ObjectType type) { + return type; + } + + @Override + public JSType caseStringType() { + return getNativeType(STRING_TYPE); + } + + @Override + public JSType caseUnionType(UnionType type) { + return type.getRestrictedUnion(getNativeType(NULL_TYPE)); + } + + @Override + public JSType caseUnknownType() { + return getNativeType(UNKNOWN_TYPE); + } + + @Override + public JSType caseVoidType() { + return getNativeType(VOID_TYPE); + } + + @Override + public JSType caseParameterizedType(ParameterizedType type) { + return caseObjectType(type); + } + + @Override + public JSType caseTemplateType(TemplateType templateType) { + return caseObjectType(templateType); + } + }; + + /** + * A class common to all visitors that need to restrict the type based on + * {@code typeof}-like conditions. + */ + abstract class RestrictByTypeOfResultVisitor + implements Visitor { + + /** + * Abstracts away the similarities between visiting the unknown type and the + * all type. + * @param topType {@code UNKNOWN_TYPE} or {@code ALL_TYPE} + * @return the restricted type + * @see #caseAllType + * @see #caseUnknownType + */ + protected abstract JSType caseTopType(JSType topType); + + @Override + public JSType caseAllType() { + return caseTopType(getNativeType(ALL_TYPE)); + } + + @Override + public JSType caseUnknownType() { + return caseTopType(getNativeType(CHECKED_UNKNOWN_TYPE)); + } + + @Override + public JSType caseUnionType(UnionType type) { + JSType restricted = null; + for (JSType alternate : type.getAlternates()) { + JSType restrictedAlternate = alternate.visit(this); + if (restrictedAlternate != null) { + if (restricted == null) { + restricted = restrictedAlternate; + } else { + restricted = restrictedAlternate.getLeastSupertype(restricted); + } + } + } + return restricted; + } + + @Override + public JSType caseNoType() { + return getNativeType(NO_TYPE); + } + + @Override + public JSType caseEnumElementType(EnumElementType enumElementType) { + // NOTE(nicksantos): This is a white lie. Suppose we have: + // /** @enum {string|number} */ var MyEnum = ...; + // if (goog.isNumber(myEnumInstance)) { + // /* what is myEnumInstance here? */ + // } + // There is no type that represents {MyEnum - string}. What we really + // need is a notion of "enum subtyping", so that we could dynamically + // create a subtype of MyEnum restricted by string. In any case, + // this should catch the common case. + JSType type = enumElementType.getPrimitiveType().visit(this); + if (type != null && + enumElementType.getPrimitiveType().isEquivalentTo(type)) { + return enumElementType; + } else { + return type; + } + } + + @Override + public JSType caseParameterizedType(ParameterizedType type) { + return caseObjectType(type); + } + + @Override + public JSType caseTemplateType(TemplateType templateType) { + return caseObjectType(templateType); + } + } + + /** + * A class common to all visitors that need to restrict the type based on + * some {@code typeof}-like condition being true. All base cases return + * {@code null}. It is up to the subclasses to override the appropriate ones. + */ + abstract class RestrictByTrueTypeOfResultVisitor + extends RestrictByTypeOfResultVisitor { + @Override + public JSType caseNoObjectType() { + return null; + } + + @Override + public JSType caseBooleanType() { + return null; + } + + @Override + public JSType caseFunctionType(FunctionType type) { + return null; + } + + @Override + public JSType caseNullType() { + return null; + } + + @Override + public JSType caseNumberType() { + return null; + } + + @Override + public JSType caseObjectType(ObjectType type) { + return null; + } + + @Override + public JSType caseStringType() { + return null; + } + + @Override + public JSType caseVoidType() { + return null; + } + } + + /** + * A class common to all visitors that need to restrict the type based on + * some {@code typeof}-like condition being false. All base cases return + * their type. It is up to the subclasses to override the appropriate ones. + */ + abstract class RestrictByFalseTypeOfResultVisitor + extends RestrictByTypeOfResultVisitor { + @Override + protected JSType caseTopType(JSType topType) { + return topType; + } + + @Override + public JSType caseNoObjectType() { + return getNativeType(NO_OBJECT_TYPE); + } + + @Override + public JSType caseBooleanType() { + return getNativeType(BOOLEAN_TYPE); + } + + @Override + public JSType caseFunctionType(FunctionType type) { + return type; + } + + @Override + public JSType caseNullType() { + return getNativeType(NULL_TYPE); + } + + @Override + public JSType caseNumberType() { + return getNativeType(NUMBER_TYPE); + } + + @Override + public JSType caseObjectType(ObjectType type) { + return type; + } + + @Override + public JSType caseStringType() { + return getNativeType(STRING_TYPE); + } + + @Override + public JSType caseVoidType() { + return getNativeType(VOID_TYPE); + } + } + + /** + * @see ChainableReverseAbstractInterpreter#getRestrictedByTypeOfResult + */ + private class RestrictByOneTypeOfResultVisitor + extends RestrictByTypeOfResultVisitor { + /** + * A value known to be equal or not equal to the result of the + * {@code typeOf} operation. + */ + private final String value; + + /** + * {@code true} if the {@code typeOf} result is known to equal + * {@code value}; {@code false} if it is known not to equal + * {@code value}. + */ + private final boolean resultEqualsValue; + + RestrictByOneTypeOfResultVisitor(String value, boolean resultEqualsValue) { + this.value = value; + this.resultEqualsValue = resultEqualsValue; + } + + /** + * Computes whether the given result of a {@code typeof} operator matches + * expectations, i.e. whether a type that gives such a result should be + * kept. + */ + private boolean matchesExpectation(String result) { + return result.equals(value) == resultEqualsValue; + } + + @Override + protected JSType caseTopType(JSType topType) { + JSType result = topType; + if (resultEqualsValue) { + JSType typeByName = getNativeTypeForTypeOf(value); + if (typeByName != null) { + result = typeByName; + } + } + return result; + } + + @Override + public JSType caseNoObjectType() { + return (value.equals("object") || value.equals("function")) == + resultEqualsValue ? getNativeType(NO_OBJECT_TYPE) : null; + } + + @Override + public JSType caseBooleanType() { + return matchesExpectation("boolean") ? getNativeType(BOOLEAN_TYPE) : null; + } + + @Override + public JSType caseFunctionType(FunctionType type) { + return matchesExpectation("function") ? type : null; + } + + @Override + public JSType caseNullType() { + return matchesExpectation("object") ? getNativeType(NULL_TYPE) : null; + } + + @Override + public JSType caseNumberType() { + return matchesExpectation("number") ? getNativeType(NUMBER_TYPE) : null; + } + + @Override + public JSType caseObjectType(ObjectType type) { + if (value.equals("function")) { + JSType ctorType = getNativeType(U2U_CONSTRUCTOR_TYPE); + if (resultEqualsValue) { + // Objects are restricted to "Function", subtypes are left + return ctorType.getGreatestSubtype(type); + } else { + // Only filter out subtypes of "function" + return type.isSubtype(ctorType) ? null : type; + } + } + return matchesExpectation("object") ? type : null; + } + + @Override + public JSType caseStringType() { + return matchesExpectation("string") ? getNativeType(STRING_TYPE) : null; + } + + @Override + public JSType caseVoidType() { + return matchesExpectation("undefined") ? getNativeType(VOID_TYPE) : null; + } + } + + /** + * Returns a version of type where undefined is not present. + */ + protected final JSType getRestrictedWithoutUndefined(JSType type) { + return type == null ? null : type.visit(restrictUndefinedVisitor); + } + + /** + * Returns a version of type where null is not present. + */ + protected final JSType getRestrictedWithoutNull(JSType type) { + return type == null ? null : type.visit(restrictNullVisitor); + } + + /** + * Returns a version of {@code type} that is restricted by some knowledge + * about the result of the {@code typeof} operation. + *

      + * The behavior of the {@code typeof} operator can be summarized by the + * following table: + * + * + * + * + * + * + * + * + * + * + * + *
      typeresult
      {@code undefined}"undefined"
      {@code null}"object"
      {@code boolean}"boolean"
      {@code number}"number"
      {@code string}"string"
      {@code Object} (which doesn't implement [[Call]])"object"
      {@code Object} (which implements [[Call]])"function"
      + * @param type the type to restrict + * @param value A value known to be equal or not equal to the result of the + * {@code typeof} operation + * @param resultEqualsValue {@code true} if the {@code typeOf} result is known + * to equal {@code value}; {@code false} if it is known not to + * equal {@code value} + * @return the restricted type or null if no version of the type matches the + * restriction + */ + JSType getRestrictedByTypeOfResult(JSType type, String value, + boolean resultEqualsValue) { + if (type == null) { + if (resultEqualsValue) { + JSType result = getNativeTypeForTypeOf(value); + return result == null ? getNativeType(CHECKED_UNKNOWN_TYPE) : result; + } else { + return null; + } + } + return type.visit( + new RestrictByOneTypeOfResultVisitor(value, resultEqualsValue)); + } + + JSType getNativeType(JSTypeNative typeId) { + return typeRegistry.getNativeType(typeId); + } + + /** + * If we definitely know what a type is based on the typeof result, + * return it. Otherwise, return null. + * + * The typeof operation in JS is poorly defined, and this function works + * for both the native typeof and goog.typeOf. It should not be made public, + * because its semantics are informally defined, and would be wrong in + * the general case. + */ + private JSType getNativeTypeForTypeOf(String value) { + if (value.equals("number")) { + return getNativeType(NUMBER_TYPE); + } else if (value.equals("boolean")) { + return getNativeType(BOOLEAN_TYPE); + } else if (value.equals("string")) { + return getNativeType(STRING_TYPE); + } else if (value.equals("undefined")) { + return getNativeType(VOID_TYPE); + } else if (value.equals("function")) { + return getNativeType(U2U_CONSTRUCTOR_TYPE); + } else { + return null; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/ClosureReverseAbstractInterpreter.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/ClosureReverseAbstractInterpreter.java new file mode 100644 index 0000000..8e69edf --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/ClosureReverseAbstractInterpreter.java @@ -0,0 +1,268 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.type; + +import static com.google.javascript.rhino.jstype.JSTypeNative.ARRAY_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NO_OBJECT_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NULL_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NULL_VOID; +import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_STRING_BOOLEAN; +import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableMap; +import com.google.javascript.jscomp.CodingConvention; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.Visitor; + +import java.util.Map; + +/** + * A reverse abstract interpreter (RAI) for specific closure patterns such as + * {@code goog.isDef}. + * + */ +public class ClosureReverseAbstractInterpreter + extends ChainableReverseAbstractInterpreter { + + /** + * For when {@code goog.isArray} returns true. + */ + private final Visitor restrictToArrayVisitor = + new RestrictByTrueTypeOfResultVisitor() { + @Override + protected JSType caseTopType(JSType topType) { + // Ideally, we would like to return any subtype of Array. + // Since that's not possible, we don't restrict the type. + return topType; + } + + @Override + public JSType caseObjectType(ObjectType type) { + JSType arrayType = getNativeType(ARRAY_TYPE); + return arrayType.isSubtype(type) ? arrayType : null; + } + }; + + /** + * For when {@code goog.isArray} returns false. + */ + private final Visitor restrictToNotArrayVisitor = + new RestrictByFalseTypeOfResultVisitor() { + @Override + public JSType caseObjectType(ObjectType type) { + return type.isSubtype(getNativeType(ARRAY_TYPE)) ? null : type; + } + }; + + /** + * For when {@code goog.isObject} returns true. This includes functions, but + * not {@code null}. + */ + private final Visitor restrictToObjectVisitor = + new RestrictByTrueTypeOfResultVisitor() { + @Override + protected JSType caseTopType(JSType topType) { + return getNativeType(NO_OBJECT_TYPE); + } + + @Override + public JSType caseObjectType(ObjectType type) { + return type; + } + + @Override + public JSType caseFunctionType(FunctionType type) { + return type; + } + }; + + /** + * For when {@code goog.isObject} returns false. + */ + private final Visitor restrictToNotObjectVisitor = + new RestrictByFalseTypeOfResultVisitor() { + + @Override + public JSType caseAllType() { + return typeRegistry.createUnionType( + getNativeType(NUMBER_STRING_BOOLEAN), getNativeType(NULL_VOID)); + } + + @Override + public JSType caseObjectType(ObjectType type) { + return null; + } + + @Override + public JSType caseFunctionType(FunctionType type) { + return null; + } + }; + + /** Functions used to restrict types. */ + private Map> restricters; + + /** + * Creates a {@link ClosureReverseAbstractInterpreter}. + */ + public ClosureReverseAbstractInterpreter(CodingConvention convention, + final JSTypeRegistry typeRegistry) { + super(convention, typeRegistry); + this.restricters = + new ImmutableMap.Builder>() + .put("isDef", new Function() { + @Override + public JSType apply(TypeRestriction p) { + if (p.outcome) { + return getRestrictedWithoutUndefined(p.type); + } else { + return p.type != null ? + getNativeType(VOID_TYPE).getGreatestSubtype(p.type) : null; + } + } + }) + .put("isNull", new Function() { + @Override + public JSType apply(TypeRestriction p) { + if (p.outcome) { + return p.type != null ? + getNativeType(NULL_TYPE).getGreatestSubtype(p.type) : null; + } else { + return getRestrictedWithoutNull(p.type); + } + } + }) + .put("isDefAndNotNull", new Function() { + @Override + public JSType apply(TypeRestriction p) { + if (p.outcome) { + return getRestrictedWithoutUndefined( + getRestrictedWithoutNull(p.type)); + } else { + return p.type != null ? + getNativeType(NULL_VOID).getGreatestSubtype(p.type) : null; + } + } + }) + .put("isString", new Function() { + @Override + public JSType apply(TypeRestriction p) { + return getRestrictedByTypeOfResult(p.type, "string", p.outcome); + } + }) + .put("isBoolean", new Function() { + @Override + public JSType apply(TypeRestriction p) { + return getRestrictedByTypeOfResult(p.type, "boolean", p.outcome); + } + }) + .put("isNumber", new Function() { + @Override + public JSType apply(TypeRestriction p) { + return getRestrictedByTypeOfResult(p.type, "number", p.outcome); + } + }) + .put("isFunction", new Function() { + @Override + public JSType apply(TypeRestriction p) { + return getRestrictedByTypeOfResult(p.type, "function", p.outcome); + } + }) + .put("isArray", new Function() { + @Override + public JSType apply(TypeRestriction p) { + if (p.type == null) { + return p.outcome ? getNativeType(ARRAY_TYPE) : null; + } + + Visitor visitor = p.outcome ? restrictToArrayVisitor : + restrictToNotArrayVisitor; + return p.type.visit(visitor); + } + }) + .put("isObject", new Function() { + @Override + public JSType apply(TypeRestriction p) { + if (p.type == null) { + return p.outcome ? getNativeType(OBJECT_TYPE) : null; + } + + Visitor visitor = p.outcome ? restrictToObjectVisitor : + restrictToNotObjectVisitor; + return p.type.visit(visitor); + } + }) + .build(); + } + + @Override + public FlowScope getPreciserScopeKnowingConditionOutcome(Node condition, + FlowScope blindScope, boolean outcome) { + if (condition.isCall() && condition.getChildCount() == 2) { + Node callee = condition.getFirstChild(); + Node param = condition.getLastChild(); + if (callee.isGetProp() && param.isQualifiedName()) { + JSType paramType = getTypeIfRefinable(param, blindScope); + Node left = callee.getFirstChild(); + Node right = callee.getLastChild(); + if (left.isName() && "goog".equals(left.getString()) && + right.isString()) { + Function restricter = + restricters.get(right.getString()); + if (restricter != null) { + return restrictParameter(param, paramType, blindScope, restricter, + outcome); + } + } + } + } + return nextPreciserScopeKnowingConditionOutcome( + condition, blindScope, outcome); + } + + private FlowScope restrictParameter(Node parameter, JSType type, + FlowScope blindScope, Function restriction, + boolean outcome) { + // restricting + type = restriction.apply(new TypeRestriction(type, outcome)); + + // changing the scope + if (type != null) { + FlowScope informed = blindScope.createChildFlowScope(); + declareNameInScope(informed, parameter, type); + return informed; + } else { + return blindScope; + } + } + + private static class TypeRestriction { + private final JSType type; + private final boolean outcome; + + private TypeRestriction(JSType type, boolean outcome) { + this.type = type; + this.outcome = outcome; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/FlowScope.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/FlowScope.java new file mode 100644 index 0000000..120e171 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/FlowScope.java @@ -0,0 +1,82 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.type; + +import com.google.javascript.jscomp.graph.LatticeElement; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.StaticScope; +import com.google.javascript.rhino.jstype.StaticSlot; + +/** + * A symbol table for inferring types during data flow analysis. + * + * Each flow scope represents the types of all variables in the scope at + * a particular point in the flow analysis. + * + * @author nicksantos@google.com (Nick Santos) + */ +public interface FlowScope extends StaticScope, LatticeElement { + + /** + * Creates a child of this flow scope, to represent an instruction + * directly following this one. + */ + FlowScope createChildFlowScope(); + + /** + * Defines the type of a symbol at this point in the flow. + * @throws IllegalArgumentException If no slot for this symbol exists. + */ + void inferSlotType(String symbol, JSType type); + + /** + * Infer the type of a qualified name. + * + * When traversing the control flow of a function, simple names are + * declared at the bottom of the flow lattice. But there are far too many + * qualified names to be able to do this and be performant. So the bottoms + * of qualified names are declared lazily. + * + * Therefore, when inferring a qualified slot, we need both the "bottom" + * type of the slot when we enter the scope, and the current type being + * inferred. + */ + void inferQualifiedSlot(Node node, String symbol, JSType bottomType, + JSType inferredType); + + /** + * Optimize this scope and return a new FlowScope with faster lookup. + */ + FlowScope optimize(); + + /** + * Tries to find a unique refined variable in the refined scope, up to the + * the blind scope. + * @param blindScope The scope before the refinement, i.e. some parent of the + * this scope or itself. + * @return The unique refined variable if found or null. + */ + StaticSlot findUniqueRefinedSlot(FlowScope blindScope); + + /** + * Look through the given scope, and try to find slots where it doesn't + * have enough type information. Then fill in that type information + * with stuff that we've inferred in the local flow. + */ + void completeScope(StaticScope scope); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/ReverseAbstractInterpreter.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/ReverseAbstractInterpreter.java new file mode 100644 index 0000000..0e9a247 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/ReverseAbstractInterpreter.java @@ -0,0 +1,43 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.type; + +import com.google.javascript.rhino.Node; + +/** + * This interface defines what reversed abstract interpreters provide. + *

      Abstract interpretation is the process of interpreting a program at an + * abstracted level (such as at the type level) instead of the concrete level + * (the flow of values). This reversed abstract interpreter reverses the + * abstract interpretation process by knowing the outcome of some computation + * and calculating a preciser view of the world than the view without knowing + * the outcome of the computation.

      + * + */ +public interface ReverseAbstractInterpreter { + /** + * Calculates a precise version of the scope knowing the outcome of the + * condition. + * + * @param condition the condition's expression + * @param blindScope the scope without knowledge about the outcome of the + * condition + * @param outcome the outcome of the condition + */ + FlowScope getPreciserScopeKnowingConditionOutcome(Node condition, + FlowScope blindScope, boolean outcome); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/SemanticReverseAbstractInterpreter.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/SemanticReverseAbstractInterpreter.java new file mode 100644 index 0000000..aa25ddd --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/SemanticReverseAbstractInterpreter.java @@ -0,0 +1,602 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.type; + +import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; + +import com.google.common.base.Function; +import com.google.javascript.jscomp.CodingConvention; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSType.TypePair; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.StaticSlot; +import com.google.javascript.rhino.jstype.UnionType; +import com.google.javascript.rhino.jstype.Visitor; + +/** + * A reverse abstract interpreter using the semantics of the JavaScript + * language as a means to reverse interpret computations. This interpreter + * expects the parse tree inputs to be typed. + * + */ +public class SemanticReverseAbstractInterpreter + extends ChainableReverseAbstractInterpreter { + + /** + * Merging function for equality between types. + */ + private static final Function EQ = + new Function() { + @Override + public TypePair apply(TypePair p) { + if (p.typeA == null || p.typeB == null) { + return null; + } + return p.typeA.getTypesUnderEquality(p.typeB); + } + }; + + /** + * Merging function for non-equality between types. + */ + private static final Function NE = + new Function() { + @Override + public TypePair apply(TypePair p) { + if (p.typeA == null || p.typeB == null) { + return null; + } + return p.typeA.getTypesUnderInequality(p.typeB); + } + }; + + /** + * Merging function for strict equality between types. + */ + private static final + Function SHEQ = + new Function() { + @Override + public TypePair apply(TypePair p) { + if (p.typeA == null || p.typeB == null) { + return null; + } + return p.typeA.getTypesUnderShallowEquality(p.typeB); + } + }; + + /** + * Merging function for strict non-equality between types. + */ + private static final + Function SHNE = + new Function() { + @Override + public TypePair apply(TypePair p) { + if (p.typeA == null || p.typeB == null) { + return null; + } + return p.typeA.getTypesUnderShallowInequality(p.typeB); + } + }; + + /** + * Merging function for inequality comparisons between types. + */ + private final + Function INEQ = + new Function() { + @Override + public TypePair apply(TypePair p) { + return new TypePair( + getRestrictedWithoutUndefined(p.typeA), + getRestrictedWithoutUndefined(p.typeB)); + } + }; + + /** + * Creates a semantic reverse abstract interpreter. + */ + public SemanticReverseAbstractInterpreter(CodingConvention convention, + JSTypeRegistry typeRegistry) { + super(convention, typeRegistry); + } + + @Override + public FlowScope getPreciserScopeKnowingConditionOutcome(Node condition, + FlowScope blindScope, boolean outcome) { + // Check for the typeof operator. + int operatorToken = condition.getType(); + switch (operatorToken) { + case Token.EQ: + case Token.NE: + case Token.SHEQ: + case Token.SHNE: + case Token.CASE: + Node left; + Node right; + if (operatorToken == Token.CASE) { + left = condition.getParent().getFirstChild(); // the switch condition + right = condition.getFirstChild(); + } else { + left = condition.getFirstChild(); + right = condition.getLastChild(); + } + + Node typeOfNode = null; + Node stringNode = null; + if (left.isTypeOf() && right.isString()) { + typeOfNode = left; + stringNode = right; + } else if (right.isTypeOf() && + left.isString()) { + typeOfNode = right; + stringNode = left; + } + if (typeOfNode != null && stringNode != null) { + Node operandNode = typeOfNode.getFirstChild(); + JSType operandType = getTypeIfRefinable(operandNode, blindScope); + if (operandType != null) { + boolean resultEqualsValue = operatorToken == Token.EQ || + operatorToken == Token.SHEQ || operatorToken == Token.CASE; + if (!outcome) { + resultEqualsValue = !resultEqualsValue; + } + return caseTypeOf(operandNode, operandType, stringNode.getString(), + resultEqualsValue, blindScope); + } + } + } + switch (operatorToken) { + case Token.AND: + if (outcome) { + return caseAndOrNotShortCircuiting(condition.getFirstChild(), + condition.getLastChild(), blindScope, true); + } else { + return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), + condition.getLastChild(), blindScope, true); + } + + case Token.OR: + if (!outcome) { + return caseAndOrNotShortCircuiting(condition.getFirstChild(), + condition.getLastChild(), blindScope, false); + } else { + return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), + condition.getLastChild(), blindScope, false); + } + + case Token.EQ: + if (outcome) { + return caseEquality(condition, blindScope, EQ); + } else { + return caseEquality(condition, blindScope, NE); + } + + case Token.NE: + if (outcome) { + return caseEquality(condition, blindScope, NE); + } else { + return caseEquality(condition, blindScope, EQ); + } + + case Token.SHEQ: + if (outcome) { + return caseEquality(condition, blindScope, SHEQ); + } else { + return caseEquality(condition, blindScope, SHNE); + } + + case Token.SHNE: + if (outcome) { + return caseEquality(condition, blindScope, SHNE); + } else { + return caseEquality(condition, blindScope, SHEQ); + } + + case Token.NAME: + case Token.GETPROP: + return caseNameOrGetProp(condition, blindScope, outcome); + + case Token.ASSIGN: + return firstPreciserScopeKnowingConditionOutcome( + condition.getFirstChild(), + firstPreciserScopeKnowingConditionOutcome( + condition.getFirstChild().getNext(), blindScope, outcome), + outcome); + + case Token.NOT: + return firstPreciserScopeKnowingConditionOutcome( + condition.getFirstChild(), blindScope, !outcome); + + case Token.LE: + case Token.LT: + case Token.GE: + case Token.GT: + if (outcome) { + return caseEquality(condition, blindScope, INEQ); + } + break; + + case Token.INSTANCEOF: + return caseInstanceOf( + condition.getFirstChild(), condition.getLastChild(), blindScope, + outcome); + + case Token.IN: + if (outcome && condition.getFirstChild().isString()) { + return caseIn(condition.getLastChild(), + condition.getFirstChild().getString(), blindScope); + } + break; + + case Token.CASE: + Node left = + condition.getParent().getFirstChild(); // the switch condition + Node right = condition.getFirstChild(); + if (outcome) { + return caseEquality(left, right, blindScope, SHEQ); + } else { + return caseEquality(left, right, blindScope, SHNE); + } + } + return nextPreciserScopeKnowingConditionOutcome( + condition, blindScope, outcome); + } + + private FlowScope caseEquality(Node condition, FlowScope blindScope, + Function merging) { + return caseEquality(condition.getFirstChild(), condition.getLastChild(), + blindScope, merging); + } + + private FlowScope caseEquality(Node left, Node right, FlowScope blindScope, + Function merging) { + // left type + JSType leftType = getTypeIfRefinable(left, blindScope); + boolean leftIsRefineable; + if (leftType != null) { + leftIsRefineable = true; + } else { + leftIsRefineable = false; + leftType = left.getJSType(); + } + + // right type + JSType rightType = getTypeIfRefinable(right, blindScope); + boolean rightIsRefineable; + if (rightType != null) { + rightIsRefineable = true; + } else { + rightIsRefineable = false; + rightType = right.getJSType(); + } + + // merged types + TypePair merged = merging.apply(new TypePair(leftType, rightType)); + + // creating new scope + if (merged != null) { + return maybeRestrictTwoNames( + blindScope, + left, leftType, leftIsRefineable ? merged.typeA : null, + right, rightType, rightIsRefineable ? merged.typeB : null); + } + return blindScope; + } + + private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, + FlowScope blindScope, boolean condition) { + // left type + JSType leftType = getTypeIfRefinable(left, blindScope); + boolean leftIsRefineable; + if (leftType != null) { + leftIsRefineable = true; + } else { + leftIsRefineable = false; + leftType = left.getJSType(); + blindScope = firstPreciserScopeKnowingConditionOutcome( + left, blindScope, condition); + } + + // restricting left type + JSType restrictedLeftType = (leftType == null) ? null : + leftType.getRestrictedTypeGivenToBooleanOutcome(condition); + if (restrictedLeftType == null) { + return firstPreciserScopeKnowingConditionOutcome( + right, blindScope, condition); + } + + // right type + JSType rightType = getTypeIfRefinable(right, blindScope); + boolean rightIsRefineable; + if (rightType != null) { + rightIsRefineable = true; + } else { + rightIsRefineable = false; + rightType = right.getJSType(); + blindScope = firstPreciserScopeKnowingConditionOutcome( + right, blindScope, condition); + } + + if (condition) { + JSType restrictedRightType = (rightType == null) ? null : + rightType.getRestrictedTypeGivenToBooleanOutcome(condition); + + // creating new scope + return maybeRestrictTwoNames( + blindScope, + left, leftType, leftIsRefineable ? restrictedLeftType : null, + right, rightType, rightIsRefineable ? restrictedRightType : null); + } + return blindScope; + } + + private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, + FlowScope blindScope, boolean condition) { + FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( + left, blindScope, !condition); + StaticSlot leftVar = leftScope.findUniqueRefinedSlot(blindScope); + if (leftVar == null) { + return blindScope; + } + FlowScope rightScope = firstPreciserScopeKnowingConditionOutcome( + left, blindScope, condition); + rightScope = firstPreciserScopeKnowingConditionOutcome( + right, rightScope, !condition); + StaticSlot rightVar = rightScope.findUniqueRefinedSlot(blindScope); + if (rightVar == null || !leftVar.getName().equals(rightVar.getName())) { + return blindScope; + } + JSType type = leftVar.getType().getLeastSupertype(rightVar.getType()); + FlowScope informed = blindScope.createChildFlowScope(); + informed.inferSlotType(leftVar.getName(), type); + return informed; + } + + /** + * If the restrictedType differs from the originalType, then we should + * branch the current flow scope and create a new flow scope with the name + * declared with the new type. + * + * We try not to create spurious child flow scopes as this makes type + * inference slower. + * + * We also do not want spurious slots around in type inference, because + * we use these as a signal for "checked unknown" types. A "checked unknown" + * type is a symbol that the programmer has already checked and verified that + * it's defined, even if we don't know what it is. + * + * It is OK to pass non-name nodes into this method, as long as you pass + * in {@code null} for a restricted type. + */ + private FlowScope maybeRestrictName( + FlowScope blindScope, Node node, JSType originalType, JSType restrictedType) { + if (restrictedType != null && restrictedType != originalType) { + FlowScope informed = blindScope.createChildFlowScope(); + declareNameInScope(informed, node, restrictedType); + return informed; + } + return blindScope; + } + + /** + * @see #maybeRestrictName + */ + private FlowScope maybeRestrictTwoNames( + FlowScope blindScope, + Node left, JSType originalLeftType, JSType restrictedLeftType, + Node right, JSType originalRightType, JSType restrictedRightType) { + boolean shouldRefineLeft = + restrictedLeftType != null && restrictedLeftType != originalLeftType; + boolean shouldRefineRight = + restrictedRightType != null && restrictedRightType != originalRightType; + if (shouldRefineLeft || shouldRefineRight) { + FlowScope informed = blindScope.createChildFlowScope(); + if (shouldRefineLeft) { + declareNameInScope(informed, left, restrictedLeftType); + } + if (shouldRefineRight) { + declareNameInScope(informed, right, restrictedRightType); + } + return informed; + } + return blindScope; + } + + private FlowScope caseNameOrGetProp(Node name, FlowScope blindScope, + boolean outcome) { + JSType type = getTypeIfRefinable(name, blindScope); + if (type != null) { + return maybeRestrictName( + blindScope, name, type, + type.getRestrictedTypeGivenToBooleanOutcome(outcome)); + } + return blindScope; + } + + private FlowScope caseTypeOf(Node node, JSType type, String value, + boolean resultEqualsValue, FlowScope blindScope) { + return maybeRestrictName( + blindScope, node, type, + getRestrictedByTypeOfResult(type, value, resultEqualsValue)); + } + + private FlowScope caseInstanceOf(Node left, Node right, FlowScope blindScope, + boolean outcome) { + JSType leftType = getTypeIfRefinable(left, blindScope); + if (leftType == null) { + return blindScope; + } + JSType rightType = right.getJSType(); + ObjectType targetType = + typeRegistry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); + if (rightType != null && rightType.isFunctionType()) { + targetType = rightType.toMaybeFunctionType(); + } + Visitor visitor; + if (outcome) { + visitor = new RestrictByTrueInstanceOfResultVisitor(targetType); + } else { + visitor = new RestrictByFalseInstanceOfResultVisitor(targetType); + } + return maybeRestrictName( + blindScope, left, leftType, leftType.visit(visitor)); + } + + /** + * Given 'property in object', ensures that the object has the property in the + * informed scope by defining it as a qualified name if the object type lacks + * the property and it's not in the blind scope. + * @param object The node of the right-side of the in. + * @param propertyName The string of the left-side of the in. + */ + private FlowScope caseIn(Node object, String propertyName, FlowScope blindScope) { + JSType jsType = object.getJSType(); + jsType = this.getRestrictedWithoutNull(jsType); + jsType = this.getRestrictedWithoutUndefined(jsType); + + boolean hasProperty = false; + ObjectType objectType = ObjectType.cast(jsType); + if (objectType != null) { + hasProperty = objectType.hasProperty(propertyName); + } + if (!hasProperty) { + String qualifiedName = object.getQualifiedName(); + if (qualifiedName != null) { + String propertyQualifiedName = qualifiedName + "." + propertyName; + if (blindScope.getSlot(propertyQualifiedName) == null) { + FlowScope informed = blindScope.createChildFlowScope(); + JSType unknownType = typeRegistry.getNativeType( + JSTypeNative.UNKNOWN_TYPE); + informed.inferQualifiedSlot( + object, propertyQualifiedName, unknownType, unknownType); + return informed; + } + } + } + return blindScope; + } + + /** + * @see SemanticReverseAbstractInterpreter#caseInstanceOf + */ + private class RestrictByTrueInstanceOfResultVisitor + extends RestrictByTrueTypeOfResultVisitor { + private final ObjectType target; + + RestrictByTrueInstanceOfResultVisitor(ObjectType target) { + this.target = target; + } + + @Override + protected JSType caseTopType(JSType type) { + return applyCommonRestriction(type); + } + + @Override + public JSType caseUnknownType() { + FunctionType funcTarget = JSType.toMaybeFunctionType(target); + if (funcTarget != null && funcTarget.hasInstanceType()) { + return funcTarget.getInstanceType(); + } + return getNativeType(UNKNOWN_TYPE); + } + + @Override + public JSType caseObjectType(ObjectType type) { + return applyCommonRestriction(type); + } + + @Override + public JSType caseUnionType(UnionType type) { + return applyCommonRestriction(type); + } + + @Override + public JSType caseFunctionType(FunctionType type) { + return caseObjectType(type); + } + + private JSType applyCommonRestriction(JSType type) { + if (target.isUnknownType()) { + return type; + } + + FunctionType funcTarget = target.toMaybeFunctionType(); + if (funcTarget.hasInstanceType()) { + return type.getGreatestSubtype(funcTarget.getInstanceType()); + } + + return null; + } + } + + /** + * @see SemanticReverseAbstractInterpreter#caseInstanceOf + */ + private class RestrictByFalseInstanceOfResultVisitor + extends RestrictByFalseTypeOfResultVisitor { + private final ObjectType target; + + RestrictByFalseInstanceOfResultVisitor(ObjectType target) { + this.target = target; + } + + @Override + public JSType caseObjectType(ObjectType type) { + if (target.isUnknownType()) { + return type; + } + + FunctionType funcTarget = target.toMaybeFunctionType(); + if (funcTarget.hasInstanceType()) { + if (type.isSubtype(funcTarget.getInstanceType())) { + return null; + } + + return type; + } + + return null; + } + + @Override + public JSType caseUnionType(UnionType type) { + if (target.isUnknownType()) { + return type; + } + + FunctionType funcTarget = target.toMaybeFunctionType(); + if (funcTarget.hasInstanceType()) { + return type.getRestrictedUnion(funcTarget.getInstanceType()); + } + + return null; + } + + @Override + public JSType caseFunctionType(FunctionType type) { + return caseObjectType(type); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/package.html b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/package.html new file mode 100644 index 0000000..e8dd270 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/type/package.html @@ -0,0 +1,10 @@ + + + + + + +Provides type-checking data structures and algorithms. + + + diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/webservice/common/AbstractWebServiceException.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/webservice/common/AbstractWebServiceException.java new file mode 100644 index 0000000..20afaf5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/webservice/common/AbstractWebServiceException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.webservice.common; + +/** + * All the exceptions that can be returned as error to the client of the API. + * + */ +public abstract class AbstractWebServiceException extends Exception { + public abstract ErrorCode getErrorCode(); + public abstract String getFormattedError(); + + @Override + public String toString() { + return String.format("Error(%d): %s", getErrorCode().getCode(), + getFormattedError()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/webservice/common/ErrorCode.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/webservice/common/ErrorCode.java new file mode 100644 index 0000000..ca524ad --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/webservice/common/ErrorCode.java @@ -0,0 +1,55 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.webservice.common; + +/** + * Enum of all the possible error described in the Web Service protocol. + * + */ +public enum ErrorCode { + UNKNOWN_OUTPUT_MODE(2), + UNKNOWN_API_KEY(3), + UNKNOWN_COMPILATION_LEVEL(4), + UNKNOWN_CHARSET(5), + POST_DATA_TOO_LARGE(8), + FILE_TOO_LARGE(9), + UNREACHABLE_URL(10), + MALFORMED_URL(12), + NO_OUTPUT_INFO(13), + UNKNOWN_OUTPUT_INFO(14), + MISSING_API_KEY(15), + UNKNOWN_WARNING_LEVEL(16), + UNKNOWN_FORMATTING_OPTION(17), + UNKNOWN_PARAMETER(18), + ILLEGAL_OUTPUT_FILE_NAME(19), + HASH_MISMATCH(20), + NO_CODE_FOUND_IN_CACHE(21), + ACCOUNT_OVER_QUOTA(22), + COMPILER_EXCEPTION(23), + UNSUPPORTED_INPUT_RESOURCE_TYPE(24), + DOWNLOAD_OVER_QUOTA(25), + ; + + private final int code; + ErrorCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/webservice/common/Protocol.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/webservice/common/Protocol.java new file mode 100644 index 0000000..bfc3b8e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/jscomp/webservice/common/Protocol.java @@ -0,0 +1,356 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.webservice.common; + +import com.google.common.collect.Sets; + +import java.util.Set; + +/** + * All the strings used by the webservice protocol. + * + */ +public class Protocol { + + private Protocol() {} + + /** + * All enums that need to be shared between the Java and JS code should + * implement this interface. + */ + public static interface ProtocolEnum { + /** + * @return A string representing the key or value specified by the + * protocol. + */ + public String getValue(); + } + + /** + * All the keys that can be part of the http request. + */ + public static enum RequestKey implements ProtocolEnum { + CODE_URL("code_url"), + JS_CODE("js_code"), + EXCLUDE_DEFAULT_EXTERNS("exclude_default_externs"), + EXTERNS_URL("externs_url"), + EXTERNS_CODE("js_externs"), + COMPILATION_LEVEL("compilation_level"), + OUTPUT_FORMAT("output_format"), + OUTPUT_INFO("output_info"), + OUTPUT_FILE_NAME("output_file_name"), + OUTPUT_WRAPPER("output_wrapper"), + API_KEY("api_key"), + FORMATTING("formatting"), + WARNING_LEVEL("warning_level"), + USER_ID("uid"), + USE_CLOSURE("use_closure_library"), + BUILD_DEBUG("debug"), + CHARSET("charset"), + LANGUAGE("language"), + USE_TYPES_FOR_OPTIMIZATIONS("use_types_for_optimization"), + + // Old ROBOCOMP urls. + RAWJS("rawjs"), + BASE("base"), + MODE("mode"), + SCRIPT("script"), + NOCACHE("nocache") // Ignored. + ; + + private static final Set permittedKeys = getPermittedKeys(); + + private static Set getPermittedKeys() { + Set keys = Sets.newHashSet(); + + for (RequestKey key : RequestKey.values()) { + keys.add(key.asGetParameter()); + } + return keys; + } + + private final String asGetParameter; + + private RequestKey(String asGetParameter) { + this.asGetParameter = asGetParameter; + } + + public String asGetParameter() { + return asGetParameter; + } + + @Override + public String toString() { + return asGetParameter; + } + + public static boolean isKeyValid(String key) { + return permittedKeys.contains(key.toLowerCase()); + } + + @Override + public String getValue() { + return asGetParameter; + } + } + + /** + * All the possible values for the OUTPUT_INFO key. + */ + public static enum OutputInfoKey implements ProtocolEnum { + VARIABLE_MAP("variable_map"), + COMPILED_CODE("compiled_code"), + WARNINGS("warnings"), + ERRORS("errors"), + STATISTICS("statistics"), + ; + + private final String value; + + private OutputInfoKey(String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } + } + + /** + * All the possible values for the FORMATTING key. + */ + public static enum FormattingKey implements ProtocolEnum { + PRETTY_PRINT("pretty_print"), + PRINT_INPUT_DELIMITER("print_input_delimiter"), + ; + + private final String value; + + private FormattingKey(String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } + } + + public static enum CompilationLevelKey implements ProtocolEnum { + WHITESPACE_ONLY("whitespace_only"), + SIMPLE_OPTIMIZATIONS("simple_optimizations"), + ADVANCED_OPTIMIZATIONS("advanced_optimizations"), + ; + + private final String value; + + CompilationLevelKey(String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } + } + + public static enum WarningLevelKey implements ProtocolEnum { + QUIET("quiet"), + DEFAULT("default"), + VERBOSE("verbose"), + ; + + private final String value; + + private WarningLevelKey(String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } + } + + public static enum OutputFormatKey implements ProtocolEnum { + TEXT("text"), + XML("xml"), + JSON("json"), + ; + + private final String value; + + private OutputFormatKey(String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } + + @Override + public String toString() { + return getValue(); + } + } + + /** + * Fields in the JSON response from the ApiKeyGenerationServlet. + */ + public static enum ApiKeyResponse implements ProtocolEnum { + API_KEY("api_key"), + ; + + private final String responseParam; + + ApiKeyResponse(String responseParam) { + this.responseParam = responseParam; + } + + /** + * Name of the key as it appears in the JSON. + */ + public String getResponseParam() { + return responseParam; + } + + @Override + public String toString() { + return getResponseParam(); + } + + @Override + public String getValue() { + return getResponseParam(); + } + } + + /** + * All the xml/json tags that can be returned by the backend if xml or json is + * selected as the output mode. + */ + public static enum ResponseTag implements ProtocolEnum { + ROOT_TAG("compilationResult"), + COMPILED_CODE_TAG("compiledCode"), + WARNINGS_TAG("warnings"), + WARNING_TAG("warning"), + ERRORS_TAG("errors"), + ERROR_TAG("error"), + ERROR_LINE_NO_ATTR("lineno"), + ERROR_LINE_ATTR("line"), + // Charno is negative if error occurred outside range of columns that + // JSCompiler records. Change the sign of the value to find the + // maximum column represented. + // Note that JSCompiler uses -1 as an "I don't know" state, and it can + // also turn up occasionally. + ERROR_CHAR_ATTR("charno"), + ERROR_FILE_ATTR("file"), + ERROR_TYPE_ATTR("type"), + STATS_TAG("statistics"), + ORIGINAL_SIZE_TAG("originalSize"), + ORIGINAL_GZIP_SIZE_TAG("originalGzipSize"), + COMPRESSED_SIZE_TAG("compressedSize"), + COMPRESSED_GZIP_SIZE_TAG("compressedGzipSize"), + COMPILE_TIME_TAG("compileTime"), + SERVER_ERRORS_TAG("serverErrors"), + SERVER_ERROR_TAG("error"), + SERVER_ERROR_CODE_ATTR("code"), + VARIABLE_MAP("variableMap"), + VARIABLE_MAP_ENTRY("variableMapEntry"), + ORIGINAL_NAME_ATTR("originalName"), + NEW_NAME_ATTR("newName"), + OUTPUT_FILE_PATH("outputFilePath"), + ; + + private final String responseTag; + + private ResponseTag(String responseTag) { + this.responseTag = responseTag; + } + + public String getResponseTag() { + return responseTag; + } + + @Override + public String toString() { + return getResponseTag(); + } + + @Override + public String getValue() { + return getResponseTag(); + } + } + + /** + * Properties key for getting the maximum input file size that may be + * compiled by the service. This is parameterized so we can have different + * values for inside and outside Google. + * The value should be a string representation of an integer representing + * the maximum input size in bytes. + */ + public static final String MAX_INPUT_SIZE_KEY = + "com.google.javascript.jscomp.webservice.maximumInputSize"; + + /** + * Fallback value in case no setting is provided. + */ + public static final int FALLBACK_MAX_INPUT_SIZE = + 500 * 1024; + + /** + * Hard limit on input size set at execution time from the MAX_INPUT_SIZE_KEY + * property. + */ + private static int maxInputSize; + + /** + * Initialize maxInputSize to the value from the MAX_INPUT_SIZE_KEY property + * at startup. + */ + static { + resetMaximumInputSize(); + } + + /** + * Find the maximum input size that this configuration of the web service + * allows. + * @return maximum input size permitted (in bytes) + */ + public static final int maximumInputSize() { + // Limit the number of files downloaded if they are too big to compile. + return maxInputSize; + } + + /** + * Reset the maximum input size so that the property key is rechecked. + * This is needed for testing code because we are caching the maximum + * input size value. + */ + public static final void resetMaximumInputSize() { + String maxInputSizeStr = System.getProperty(Protocol.MAX_INPUT_SIZE_KEY); + + if (maxInputSizeStr == null) { + maxInputSize = Protocol.FALLBACK_MAX_INPUT_SIZE; + } else { + maxInputSize = Integer.parseInt(maxInputSizeStr); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/ErrorReporter.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/ErrorReporter.java new file mode 100644 index 0000000..d3cf10d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/ErrorReporter.java @@ -0,0 +1,84 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +// API class + +package com.google.javascript.rhino; + +/** + * This is interface defines a protocol for the reporting of + * errors during JavaScript translation or execution. + * + */ + +public interface ErrorReporter { + + /** + * Report a warning. + * + * The implementing class may choose to ignore the warning + * if it desires. + * + * @param message a String describing the warning + * @param sourceName a String describing the JavaScript source + * where the warning occurred; typically a filename or URL + * @param line the line number associated with the warning + * @param lineOffset the offset into lineSource where problem was detected + */ + void warning(String message, String sourceName, int line, int lineOffset); + + /** + * Report an error. + * + * The implementing class is free to throw an exception if + * it desires. + * + * If execution has not yet begun, the JavaScript engine is + * free to find additional errors rather than terminating + * the translation. It will not execute a script that had + * errors, however. + * + * @param message a String describing the error + * @param sourceName a String describing the JavaScript source + * where the error occurred; typically a filename or URL + * @param line the line number associated with the error + * @param lineOffset the offset into lineSource where problem was detected + */ + void error(String message, String sourceName, int line, int lineOffset); + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/IR.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/IR.java new file mode 100644 index 0000000..fa97481 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/IR.java @@ -0,0 +1,618 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Lenz + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino; + +import com.google.common.base.Preconditions; + +import java.util.List; + +/** + * An AST construction helper class + * @author johnlenz@google.com (John Lenz) + */ +public class IR { + + private IR() {} + + public static Node empty() { + return new Node(Token.EMPTY); + } + + public static Node function(Node name, Node params, Node body) { + Preconditions.checkState(name.isName()); + Preconditions.checkState(params.isParamList()); + Preconditions.checkState(body.isBlock()); + return new Node(Token.FUNCTION, name, params, body); + } + + public static Node paramList() { + return new Node(Token.PARAM_LIST); + } + + public static Node paramList(Node param) { + Preconditions.checkState(param.isName()); + return new Node(Token.PARAM_LIST, param); + } + + public static Node paramList(Node ... params) { + Node paramList = paramList(); + for (Node param : params) { + Preconditions.checkState(param.isName()); + paramList.addChildToBack(param); + } + return paramList; + } + + public static Node paramList(List params) { + Node paramList = paramList(); + for (Node param : params) { + Preconditions.checkState(param.isName()); + paramList.addChildToBack(param); + } + return paramList; + } + + public static Node block() { + Node block = new Node(Token.BLOCK); + return block; + } + + public static Node block(Node stmt) { + Preconditions.checkState(mayBeStatement(stmt)); + Node block = new Node(Token.BLOCK, stmt); + return block; + } + + public static Node block(Node ... stmts) { + Node block = block(); + for (Node stmt : stmts) { + Preconditions.checkState(mayBeStatement(stmt)); + block.addChildToBack(stmt); + } + return block; + } + + public static Node block(List stmts) { + Node paramList = block(); + for (Node stmt : stmts) { + Preconditions.checkState(mayBeStatement(stmt)); + paramList.addChildToBack(stmt); + } + return paramList; + } + + private static Node blockUnchecked(Node stmt) { + return new Node(Token.BLOCK, stmt); + } + + public static Node script() { + // TODO(johnlenz): finish setting up the SCRIPT node + Node block = new Node(Token.SCRIPT); + return block; + } + + public static Node script(Node ... stmts) { + Node block = script(); + for (Node stmt : stmts) { + Preconditions.checkState(mayBeStatementNoReturn(stmt)); + block.addChildToBack(stmt); + } + return block; + } + + public static Node script(List stmts) { + Node paramList = script(); + for (Node stmt : stmts) { + Preconditions.checkState(mayBeStatementNoReturn(stmt)); + paramList.addChildToBack(stmt); + } + return paramList; + } + + public static Node var(Node name, Node value) { + Preconditions.checkState(name.isName() && !name.hasChildren()); + Preconditions.checkState(mayBeExpression(value)); + name.addChildToFront(value); + return var(name); + } + + public static Node var(Node name) { + Preconditions.checkState(name.isName()); + return new Node(Token.VAR, name); + } + + public static Node returnNode() { + return new Node(Token.RETURN); + } + + public static Node returnNode(Node expr) { + Preconditions.checkState(mayBeExpression(expr)); + return new Node(Token.RETURN, expr); + } + + public static Node throwNode(Node expr) { + Preconditions.checkState(mayBeExpression(expr)); + return new Node(Token.THROW, expr); + } + + public static Node exprResult(Node expr) { + Preconditions.checkState(mayBeExpression(expr)); + return new Node(Token.EXPR_RESULT, expr); + } + + public static Node ifNode(Node cond, Node then) { + Preconditions.checkState(mayBeExpression(cond)); + Preconditions.checkState(then.isBlock()); + return new Node(Token.IF, cond, then); + } + + public static Node ifNode(Node cond, Node then, Node elseNode) { + Preconditions.checkState(mayBeExpression(cond)); + Preconditions.checkState(then.isBlock()); + Preconditions.checkState(elseNode.isBlock()); + return new Node(Token.IF, cond, then, elseNode); + } + + public static Node doNode(Node body, Node cond) { + Preconditions.checkState(body.isBlock()); + Preconditions.checkState(mayBeExpression(cond)); + return new Node(Token.DO, body, cond); + } + + public static Node forIn(Node target, Node cond, Node body) { + Preconditions.checkState(target.isVar() || mayBeExpression(target)); + Preconditions.checkState(mayBeExpression(cond)); + Preconditions.checkState(body.isBlock()); + return new Node(Token.FOR, target, cond, body); + } + + public static Node forNode(Node init, Node cond, Node incr, Node body) { + Preconditions.checkState(init.isVar() || mayBeExpressionOrEmpty(init)); + Preconditions.checkState(mayBeExpressionOrEmpty(cond)); + Preconditions.checkState(mayBeExpressionOrEmpty(incr)); + Preconditions.checkState(body.isBlock()); + return new Node(Token.FOR, init, cond, incr, body); + } + + public static Node switchNode(Node cond, Node ... cases) { + Preconditions.checkState(mayBeExpression(cond)); + Node switchNode = new Node(Token.SWITCH, cond); + for (Node caseNode : cases) { + Preconditions.checkState(caseNode.isCase() || caseNode.isDefaultCase()); + switchNode.addChildToBack(caseNode); + } + return switchNode; + } + + public static Node caseNode(Node expr, Node body) { + Preconditions.checkState(mayBeExpression(expr)); + Preconditions.checkState(body.isBlock()); + body.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); + return new Node(Token.CASE, expr, body); + } + + public static Node defaultCase(Node body) { + Preconditions.checkState(body.isBlock()); + body.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); + return new Node(Token.DEFAULT_CASE, body); + } + + public static Node label(Node name, Node stmt) { + // TODO(johnlenz): additional validation here. + Preconditions.checkState(name.isLabelName()); + Preconditions.checkState(mayBeStatement(stmt)); + Node block = new Node(Token.LABEL, name, stmt); + return block; + } + + public static Node labelName(String name) { + Preconditions.checkState(!name.isEmpty()); + return Node.newString(Token.LABEL_NAME, name); + } + + public static Node tryFinally(Node tryBody, Node finallyBody) { + Preconditions.checkState(tryBody.isBlock()); + Preconditions.checkState(finallyBody.isBlock()); + Node catchBody = block().copyInformationFrom(tryBody); + return new Node(Token.TRY, tryBody, catchBody, finallyBody); + } + + public static Node tryCatch(Node tryBody, Node catchNode) { + Preconditions.checkState(tryBody.isBlock()); + Preconditions.checkState(catchNode.isCatch()); + Node catchBody = blockUnchecked(catchNode).copyInformationFrom(catchNode); + return new Node(Token.TRY, tryBody, catchBody); + } + + public static Node tryCatchFinally( + Node tryBody, Node catchNode, Node finallyBody) { + Preconditions.checkState(finallyBody.isBlock()); + Node tryNode = tryCatch(tryBody, catchNode); + tryNode.addChildToBack(finallyBody); + return tryNode; + } + + public static Node catchNode(Node expr, Node body) { + Preconditions.checkState(expr.isName()); + Preconditions.checkState(body.isBlock()); + return new Node(Token.CATCH, expr, body); + } + + public static Node breakNode() { + return new Node(Token.BREAK); + } + + public static Node breakNode(Node name) { + // TODO(johnlenz): additional validation here. + Preconditions.checkState(name.isLabelName()); + return new Node(Token.BREAK, name); + } + + public static Node continueNode() { + return new Node(Token.CONTINUE); + } + + public static Node continueNode(Node name) { + // TODO(johnlenz): additional validation here. + Preconditions.checkState(name.isLabelName()); + return new Node(Token.CONTINUE, name); + } + + + // + + public static Node call(Node target, Node ... args) { + Node call = new Node(Token.CALL, target); + for (Node arg : args) { + Preconditions.checkState(mayBeExpression(arg)); + call.addChildToBack(arg); + } + return call; + } + + public static Node newNode(Node target, Node ... args) { + Node newcall = new Node(Token.NEW, target); + for (Node arg : args) { + Preconditions.checkState(mayBeExpression(arg)); + newcall.addChildToBack(arg); + } + return newcall; + } + + public static Node name(String name) { + return Node.newString(Token.NAME, name); + } + + public static Node getprop(Node target, Node prop) { + Preconditions.checkState(mayBeExpression(target)); + Preconditions.checkState(prop.isString()); + return new Node(Token.GETPROP, target, prop); + } + + public static Node getelem(Node target, Node elem) { + Preconditions.checkState(mayBeExpression(target)); + Preconditions.checkState(mayBeExpression(elem)); + return new Node(Token.GETELEM, target, elem); + } + + public static Node assign(Node target, Node expr) { + Preconditions.checkState(isAssignmentTarget(target)); + Preconditions.checkState(mayBeExpression(expr)); + return new Node(Token.ASSIGN, target, expr); + } + + public static Node hook(Node cond, Node trueval, Node falseval) { + Preconditions.checkState(mayBeExpression(cond)); + Preconditions.checkState(mayBeExpression(trueval)); + Preconditions.checkState(mayBeExpression(falseval)); + return new Node(Token.HOOK, cond, trueval, falseval); + } + + public static Node comma(Node expr1, Node expr2) { + return binaryOp(Token.COMMA, expr1, expr2); + } + + public static Node and(Node expr1, Node expr2) { + return binaryOp(Token.AND, expr1, expr2); + } + + public static Node or(Node expr1, Node expr2) { + return binaryOp(Token.OR, expr1, expr2); + } + + public static Node not(Node expr1) { + return unaryOp(Token.NOT, expr1); + } + + /** + * "==" + */ + public static Node eq(Node expr1, Node expr2) { + return binaryOp(Token.EQ, expr1, expr2); + } + + /** + * "===" + */ + public static Node sheq(Node expr1, Node expr2) { + return binaryOp(Token.SHEQ, expr1, expr2); + } + + public static Node voidNode(Node expr1) { + return unaryOp(Token.VOID, expr1); + } + + public static Node neg(Node expr1) { + return unaryOp(Token.NEG, expr1); + } + + public static Node pos(Node expr1) { + return unaryOp(Token.POS, expr1); + } + + public static Node add(Node expr1, Node expr2) { + return binaryOp(Token.ADD, expr1, expr2); + } + + public static Node sub(Node expr1, Node expr2) { + return binaryOp(Token.SUB, expr1, expr2); + } + + // TODO(johnlenz): the rest of the ops + + // literals + public static Node objectlit(Node ... propdefs) { + Node objectlit = new Node(Token.OBJECTLIT); + for (Node propdef : propdefs) { + Preconditions.checkState( + propdef.isStringKey() || + propdef.isGetterDef() || propdef.isSetterDef()); + Preconditions.checkState(propdef.hasOneChild()); + objectlit.addChildToBack(propdef); + } + return objectlit; + } + + // TODO(johnlenz): quoted props + + public static Node propdef(Node string, Node value) { + Preconditions.checkState(string.isStringKey()); + Preconditions.checkState(!string.hasChildren()); + Preconditions.checkState(mayBeExpression(value)); + string.addChildToFront(value); + return string; + } + + public static Node arraylit(Node ... exprs) { + Node arraylit = new Node(Token.ARRAYLIT); + for (Node expr : exprs) { + Preconditions.checkState(mayBeExpressionOrEmpty(expr)); + arraylit.addChildToBack(expr); + } + return arraylit; + } + + public static Node regexp(Node expr) { + Preconditions.checkState(expr.isString()); + return new Node(Token.REGEXP, expr); + } + + public static Node regexp(Node expr, Node flags) { + Preconditions.checkState(expr.isString()); + Preconditions.checkState(flags.isString()); + return new Node(Token.REGEXP, expr, flags); + } + + public static Node string(String s) { + return Node.newString(s); + } + + public static Node stringKey(String s) { + return Node.newString(Token.STRING_KEY, s); + } + + public static Node number(double d) { + return Node.newNumber(d); + } + + public static Node thisNode() { + return new Node(Token.THIS); + } + + public static Node trueNode() { + return new Node(Token.TRUE); + } + + public static Node falseNode() { + return new Node(Token.FALSE); + } + + public static Node nullNode() { + return new Node(Token.NULL); + } + + // helper methods + + private static Node binaryOp(int token, Node expr1, Node expr2) { + Preconditions.checkState(mayBeExpression(expr1)); + Preconditions.checkState(mayBeExpression(expr2)); + return new Node(token, expr1, expr2); + } + + private static Node unaryOp(int token, Node expr) { + Preconditions.checkState(mayBeExpression(expr)); + return new Node(token, expr); + } + + private static boolean mayBeExpressionOrEmpty(Node n) { + return n.isEmpty() || mayBeExpression(n); + } + + private static boolean isAssignmentTarget(Node n) { + return n.isName() || n.isGetProp() || n.isGetElem(); + } + + // NOTE: some nodes are neither statements nor expression nodes: + // SCRIPT, LABEL_NAME, PARAM_LIST, CASE, DEFAULT_CASE, CATCH + // GETTER_DEF, SETTER_DEF + + /** + * It isn't possible to always determine if a detached node is a expression, + * so make a best guess. + */ + private static boolean mayBeStatementNoReturn(Node n) { + switch (n.getType()) { + case Token.EMPTY: + case Token.FUNCTION: + // EMPTY and FUNCTION are used both in expression and statement + // contexts + return true; + + case Token.BLOCK: + case Token.BREAK: + case Token.CONST: + case Token.CONTINUE: + case Token.DEBUGGER: + case Token.DO: + case Token.EXPR_RESULT: + case Token.FOR: + case Token.IF: + case Token.LABEL: + case Token.SWITCH: + case Token.THROW: + case Token.TRY: + case Token.VAR: + case Token.WHILE: + case Token.WITH: + return true; + + default: + return false; + } + } + + /** + * It isn't possible to always determine if a detached node is a expression, + * so make a best guess. + */ + private static boolean mayBeStatement(Node n) { + if (!mayBeStatementNoReturn(n)) { + return n.isReturn(); + } + return true; + } + + /** + * It isn't possible to always determine if a detached node is a expression, + * so make a best guess. + */ + private static boolean mayBeExpression(Node n) { + switch (n.getType()) { + case Token.FUNCTION: + // FUNCTION is used both in expression and statement + // contexts. + return true; + + case Token.ADD: + case Token.AND: + case Token.ARRAYLIT: + case Token.ASSIGN: + case Token.ASSIGN_BITOR: + case Token.ASSIGN_BITXOR: + case Token.ASSIGN_BITAND: + case Token.ASSIGN_LSH: + case Token.ASSIGN_RSH: + case Token.ASSIGN_URSH: + case Token.ASSIGN_ADD: + case Token.ASSIGN_SUB: + case Token.ASSIGN_MUL: + case Token.ASSIGN_DIV: + case Token.ASSIGN_MOD: + case Token.BITAND: + case Token.BITOR: + case Token.BITNOT: + case Token.BITXOR: + case Token.CALL: + case Token.COMMA: + case Token.DEC: + case Token.DELPROP: + case Token.DIV: + case Token.EQ: + case Token.FALSE: + case Token.GE: + case Token.GETPROP: + case Token.GETELEM: + case Token.GT: + case Token.HOOK: + case Token.IN: + case Token.INC: + case Token.INSTANCEOF: + case Token.LE: + case Token.LSH: + case Token.LT: + case Token.MOD: + case Token.MUL: + case Token.NAME: + case Token.NE: + case Token.NEG: + case Token.NEW: + case Token.NOT: + case Token.NUMBER: + case Token.NULL: + case Token.OBJECTLIT: + case Token.OR: + case Token.POS: + case Token.REGEXP: + case Token.RSH: + case Token.SHEQ: + case Token.SHNE: + case Token.STRING: + case Token.SUB: + case Token.THIS: + case Token.TYPEOF: + case Token.TRUE: + case Token.URSH: + case Token.VOID: + return true; + + default: + return false; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/InputId.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/InputId.java new file mode 100644 index 0000000..6fe166f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/InputId.java @@ -0,0 +1,76 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Lenz + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino; + +import java.io.Serializable; + +/** + * An id used uniquely identify a CompilerInput + * @author johnlenz@google.com (John Lenz) + */ +public class InputId implements Serializable { + public static final long serialVersionUID = 1L; + private final String id; + + public InputId(String id) { + this.id = id; + } + + public String getIdName() { + return id; + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + return id.equals(((InputId) obj).id); + } + + @Override + public String toString() { + return "InputId: " + getIdName(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/JSDocInfo.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/JSDocInfo.java new file mode 100644 index 0000000..d1e7604 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/JSDocInfo.java @@ -0,0 +1,1544 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + *

      JSDoc information describing JavaScript code. JSDoc is represented as a + * unified object with fields for each JSDoc annotation, even though some + * combinations are incorrect. For instance, if a JSDoc describes an enum, + * it cannot have information about a return type. This implementation + * takes advantage of such incompatibilities to reuse fields for multiple + * purposes, reducing memory consumption.

      + * + *

      Constructing {@link JSDocInfo} objects is simplified by + * {@link JSDocInfoBuilder} which provides early incompatibility detection.

      + * + */ +public class JSDocInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * Visibility categories. The {@link Visibility#ordinal()} can be used as a + * numerical indicator of privacy, where 0 is the most private. This means + * that the {@link Visibility#compareTo} method can be used to + * determine if a visibility is more permissive than another. + */ + public enum Visibility { + PRIVATE, + PROTECTED, + PUBLIC, + + // If visibility is not specified, we just assume that visibility + // is inherited from the super class. + INHERITED + } + + private static final class LazilyInitializedInfo implements Serializable { + private static final long serialVersionUID = 1L; + + // Function information + JSTypeExpression baseType = null; + List extendedInterfaces = null; + List implementedInterfaces = null; + Map parameters = null; + List thrownTypes = null; + ImmutableList templateTypeNames = null; + + // Other information + String description = null; + String meaning = null; + String deprecated = null; + String license = null; + Set suppressions = null; + Set modifies = null; + String lendsName = null; + } + + private static final class LazilyInitializedDocumentation { + String sourceComment = null; + List markers = null; + + Map parameters = null; + Map throwsDescriptions = null; + String blockDescription = null; + String fileOverview = null; + String returnDescription = null; + String version = null; + + List authors = null; + List sees = null; + } + + /** + * A piece of information (found in a marker) which contains a position + * with a string. + */ + public static class StringPosition extends SourcePosition { + } + + /** + * A piece of information (found in a marker) which contains a position + * with a string that has no leading or trailing whitespace. + */ + static class TrimmedStringPosition extends StringPosition { + @Override public void setItem(String item) { + Preconditions.checkArgument( + item.charAt(0) != ' ' && + item.charAt(item.length() - 1) != ' ', + "String has leading or trailing whitespace"); + super.setItem(item); + } + } + + /** + * A piece of information (found in a marker) which contains a position + * with a name node. + */ + public static class NamePosition extends SourcePosition {} + + /** + * A piece of information (found in a marker) which contains a position + * with a type expression syntax tree. + */ + public static class TypePosition extends SourcePosition { + private boolean brackets = false; + + /** Returns whether the type has curly braces around it. */ + public boolean hasBrackets() { + return brackets; + } + + void setHasBrackets(boolean newVal) { + brackets = newVal; + } + } + + /** + * Defines a class for containing the parsing information + * for this JSDocInfo. For each annotation found in the + * JsDoc, a marker will be created indicating the annotation + * itself, the name of the annotation (if any; for example, + * a @param has a name, but a @return does not), the + * textual description found on that annotation and, if applicable, + * the type declaration. All this information is only collected + * if documentation collection is turned on. + */ + public static final class Marker { + private TrimmedStringPosition annotation = null; + private TrimmedStringPosition name = null; + private SourcePosition nameNode = null; + private StringPosition description = null; + private TypePosition type = null; + + /** + * Gets the position information for the annotation name. (e.g., "param") + */ + public StringPosition getAnnotation() { + return annotation; + } + + void setAnnotation(TrimmedStringPosition p) { + annotation = p; + } + + /** + * Gets the position information for the name found + * in a @param tag. + * @deprecated Use #getNameNode + */ + @Deprecated + public StringPosition getName() { + return name; + } + + void setName(TrimmedStringPosition p) { + name = p; + } + + /** + * Gets the position information for the name found + * in an @param tag. + */ + public SourcePosition getNameNode() { + return nameNode; + } + + void setNameNode(SourcePosition p) { + nameNode = p; + } + + /** + * Gets the position information for the description found + * in a block tag. + */ + public StringPosition getDescription() { + return description; + } + + void setDescription(StringPosition p) { + description = p; + } + + /** + * Gets the position information for the type expression found + * in some block tags, like "@param" and "@return". + */ + public TypePosition getType() { + return type; + } + + void setType(TypePosition p) { + type = p; + } + } + + private LazilyInitializedInfo info = null; + + private LazilyInitializedDocumentation documentation = null; + + // The Node this JSDoc is associated with. + private Node associatedNode = null; + + private Visibility visibility = null; + + /** + * The {@link #isConstant()}, {@link #isConstructor()}, {@link #isInterface}, + * {@link #isHidden()} and {@link #shouldPreserveTry()} flags as well as + * whether the {@link #type} field stores a value for {@link #getType()}, + * {@link #getReturnType()} or {@link #getEnumParameterType()}. + * + * @see #setFlag(boolean, int) + * @see #getFlag(int) + * @see #setType(JSTypeExpression, int) + * @see #getType(int) + */ + private int bitset = 0x00; + + /** + * The type for {@link #getType()}, {@link #getReturnType()} or + * {@link #getEnumParameterType()}. The knowledge of which one is recorded is + * stored in the {@link #bitset} field. + * + * @see #setType(JSTypeExpression, int) + * @see #getType(int) + */ + private JSTypeExpression type = null; + + /** + * The type for {@link #getThisType()}. + */ + private JSTypeExpression thisType = null; + + /** + * Whether to include documentation. + * + * @see JSDocInfo.LazilyInitializedDocumentation + */ + private boolean includeDocumentation = false; + + // We use a bit map to represent whether or not the JSDoc contains + // one of the "boolean" annotation types (annotations like @constructor, + // for which the presence of the annotation alone is significant). + + // Mask all the boolean annotation types + private static final int MASK_FLAGS = 0x3FFFFFFF; + + private static final int MASK_CONSTANT = 0x00000001; // @const + private static final int MASK_CONSTRUCTOR = 0x00000002; // @constructor + private static final int MASK_DEFINE = 0x00000004; // @define + private static final int MASK_HIDDEN = 0x00000008; // @hidden + private static final int MASK_PRESERVETRY = 0x00000010; // @preserveTry + private static final int MASK_NOCHECK = 0x00000020; // @notypecheck + private static final int MASK_OVERRIDE = 0x00000040; // @override + private static final int MASK_NOALIAS = 0x00000080; // @noalias + private static final int MASK_DEPRECATED = 0x00000100; // @deprecated + private static final int MASK_INTERFACE = 0x00000200; // @interface + private static final int MASK_EXPORT = 0x00000400; // @export + private static final int MASK_NOSHADOW = 0x00000800; // @noshadow + private static final int MASK_FILEOVERVIEW = 0x00001000; // @fileoverview + private static final int MASK_IMPLICITCAST = 0x00002000; // @implicitCast + private static final int MASK_NOSIDEEFFECTS = 0x00004000; // @nosideeffects + private static final int MASK_EXTERNS = 0x00008000; // @externs + private static final int MASK_JAVADISPATCH = 0x00010000; // @javadispatch + private static final int MASK_NOCOMPILE = 0x00020000; // @nocompile + private static final int MASK_CONSISTIDGEN = 0x00040000; // @consistentIdGenerator + private static final int MASK_IDGEN = 0x00080000; // @idGenerator + private static final int MASK_EXPOSE = 0x00100000; // @expose + private static final int MASK_STRUCT = 0x00200000; // @struct + private static final int MASK_DICT = 0x00400000; // @dict + private static final int MASK_STALBEIDGEN = 0x00800000; // @stableIdGenerator + + // 3 bit type field stored in the top 3 bits of the most significant + // nibble. + private static final int MASK_TYPEFIELD = 0xE0000000; // 1110... + private static final int TYPEFIELD_TYPE = 0x20000000; // 0010... + private static final int TYPEFIELD_RETURN = 0x40000000; // 0100... + private static final int TYPEFIELD_ENUM = 0x60000000; // 0110... + private static final int TYPEFIELD_TYPEDEF = 0x80000000; // 1000... + + /** + * Creates a {@link JSDocInfo} object. This object should be created using + * a {@link JSDocInfoBuilder}. + */ + JSDocInfo(boolean includeDocumentation) { + this.includeDocumentation = includeDocumentation; + } + + // Visible for testing. + public JSDocInfo() {} + + void setConsistentIdGenerator(boolean value) { + setFlag(value, MASK_CONSISTIDGEN); + } + + void setStableIdGenerator(boolean value) { + setFlag(value, MASK_STALBEIDGEN); + } + + void setConstant(boolean value) { + setFlag(value, MASK_CONSTANT); + } + + void setConstructor(boolean value) { + setFlag(value, MASK_CONSTRUCTOR); + } + + void setStruct() { + setFlag(true, MASK_STRUCT); + } + + void setDict() { + setFlag(true, MASK_DICT); + } + + void setDefine(boolean value) { + setFlag(value, MASK_DEFINE); + } + + void setHidden(boolean value) { + setFlag(value, MASK_HIDDEN); + } + + void setNoCheck(boolean value) { + setFlag(value, MASK_NOCHECK); + } + + void setShouldPreserveTry(boolean value) { + setFlag(value, MASK_PRESERVETRY); + } + + void setOverride(boolean value) { + setFlag(value, MASK_OVERRIDE); + } + + void setNoAlias(boolean value) { + setFlag(value, MASK_NOALIAS); + } + + // Visible for testing. + public void setDeprecated(boolean value) { + setFlag(value, MASK_DEPRECATED); + } + + void setInterface(boolean value) { + setFlag(value, MASK_INTERFACE); + } + + void setExport(boolean value) { + setFlag(value, MASK_EXPORT); + } + + void setExpose(boolean value) { + setFlag(value, MASK_EXPOSE); + } + + void setNoShadow(boolean value) { + setFlag(value, MASK_NOSHADOW); + } + + void setIdGenerator(boolean value) { + setFlag(value, MASK_IDGEN); + } + + void setImplicitCast(boolean value) { + setFlag(value, MASK_IMPLICITCAST); + } + + void setNoSideEffects(boolean value) { + setFlag(value, MASK_NOSIDEEFFECTS); + } + + void setExterns(boolean value) { + setFlag(value, MASK_EXTERNS); + } + + void setJavaDispatch(boolean value) { + setFlag(value, MASK_JAVADISPATCH); + } + + void setNoCompile(boolean value) { + setFlag(value, MASK_NOCOMPILE); + } + + private void setFlag(boolean value, int mask) { + if (value) { + bitset |= mask; + } else { + bitset &= ~mask; + } + } + + /** + * @return whether the {@code @consistentIdGenerator} is present on + * this {@link JSDocInfo} + */ + public boolean isConsistentIdGenerator() { + return getFlag(MASK_CONSISTIDGEN); + } + + /** + * @return whether the {@code @stableIdGenerator} is present on this {@link JSDocInfo}. + */ + public boolean isStableIdGenerator() { + return getFlag(MASK_STALBEIDGEN); + } + + /** + * Returns whether the {@code @const} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean isConstant() { + return getFlag(MASK_CONSTANT) || isDefine(); + } + + /** + * Returns whether the {@code @constructor} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean isConstructor() { + return getFlag(MASK_CONSTRUCTOR); + } + + /** + * Returns whether the {@code @struct} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean makesStructs() { + return getFlag(MASK_STRUCT); + } + + /** + * Returns whether the {@code @dict} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean makesDicts() { + return getFlag(MASK_DICT); + } + + /** + * Returns whether the {@code @define} annotation is present on this + * {@link JSDocInfo}. If this annotation is present, then the + * {@link #getType()} method will retrieve the define type. + */ + public boolean isDefine() { + return getFlag(MASK_DEFINE); + } + + /** + * Returns whether the {@code @hidden} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean isHidden() { + return getFlag(MASK_HIDDEN); + } + + /** + * Returns whether the {@code @nocheck} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean isNoTypeCheck() { + return getFlag(MASK_NOCHECK); + } + + /** + * Returns whether the {@code @preserveTry} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean shouldPreserveTry() { + return getFlag(MASK_PRESERVETRY); + } + + /** + * Returns whether the {@code @override} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean isOverride() { + return getFlag(MASK_OVERRIDE); + } + + /** + * Returns whether the {@code @noalias} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean isNoAlias() { + return getFlag(MASK_NOALIAS); + } + + /** + * Returns whether the {@code @deprecated} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean isDeprecated() { + return getFlag(MASK_DEPRECATED); + } + + /** + * Returns whether the {@code @interface} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean isInterface() { + return getFlag(MASK_INTERFACE); + } + + /** + * Returns whether the {@code @export} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean isExport() { + return getFlag(MASK_EXPORT); + } + + /** + * Returns whether the {@code @expose} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean isExpose() { + return getFlag(MASK_EXPOSE); + } + + /** + * Returns whether the {@code @noshadow} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean isNoShadow() { + return getFlag(MASK_NOSHADOW); + } + + /** + * @return whether the {@code @idGenerator} is present on + * this {@link JSDocInfo} + */ + public boolean isIdGenerator() { + return getFlag(MASK_IDGEN); + } + + /** + * Returns whether the {@code @implicitCast} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean isImplicitCast() { + return getFlag(MASK_IMPLICITCAST); + } + + /** + * Returns whether the {@code @nosideeffects} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean isNoSideEffects() { + return getFlag(MASK_NOSIDEEFFECTS); + } + + /** + * Returns whether the {@code @externs} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean isExterns() { + return getFlag(MASK_EXTERNS); + } + + /** + * Returns whether the {@code @javadispatch} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean isJavaDispatch() { + return getFlag(MASK_JAVADISPATCH); + } + + /** + * Returns whether the {@code @nocompile} annotation is present on this + * {@link JSDocInfo}. + */ + public boolean isNoCompile() { + return getFlag(MASK_NOCOMPILE); + } + + /** + * @return Whether there is declaration present on this {@link JSDocInfo}. + */ + public boolean containsDeclaration() { + return (hasType() + || hasReturnType() + || hasEnumParameterType() + || hasTypedefType() + || hasThisType() + || getParameterCount() > 0 + || getFlag(MASK_CONSTANT + | MASK_CONSTRUCTOR + | MASK_DEFINE + | MASK_OVERRIDE + | MASK_NOALIAS + | MASK_DEPRECATED + | MASK_INTERFACE + | MASK_NOSHADOW + | MASK_IMPLICITCAST + | MASK_NOSIDEEFFECTS)); + } + + private boolean getFlag(int mask) { + return (bitset & mask) != 0x00; + } + + // Visible for testing. + public void setVisibility(Visibility visibility) { + this.visibility = visibility; + } + + private void lazyInitInfo() { + if (info == null) { + info = new LazilyInitializedInfo(); + } + } + + /** + * Lazily initializes the documentation information object, but only + * if the JSDocInfo was told to keep such information around. + */ + private boolean lazyInitDocumentation() { + if (!includeDocumentation) { + return false; + } + + if (documentation == null) { + documentation = new LazilyInitializedDocumentation(); + } + + return true; + } + + /** + * Adds a marker to the documentation (if it exists) and + * returns the marker. Returns null otherwise. + */ + Marker addMarker() { + if (!lazyInitDocumentation()) { + return null; + } + + if (documentation.markers == null) { + documentation.markers = Lists.newArrayList(); + } + + Marker marker = new Marker(); + documentation.markers.add(marker); + return marker; + } + + /** + * Sets the deprecation reason. + * + * @param reason The deprecation reason + */ + boolean setDeprecationReason(String reason) { + lazyInitInfo(); + + if (info.deprecated != null) { + return false; + } + + info.deprecated = reason; + return true; + } + + /** + * Add a suppressed warning. + */ + public void addSuppression(String suppression) { + lazyInitInfo(); + + if (info.suppressions == null) { + info.suppressions = Sets.newHashSet(); + } + info.suppressions.add(suppression); + } + + /** + * Sets suppressed warnings. + * @param suppressions A list of suppressed warning types. + */ + boolean setSuppressions(Set suppressions) { + lazyInitInfo(); + + if (info.suppressions != null) { + return false; + } + + info.suppressions = suppressions; + return true; + } + + /** + * Add modifies values. + */ + void addModifies(String modifies) { + lazyInitInfo(); + + if (info.modifies == null) { + info.modifies = Sets.newHashSet(); + } + info.modifies.add(modifies); + } + + /** + * Sets modifies values. + * @param modifies A list of modifies types. + */ + boolean setModifies(Set modifies) { + lazyInitInfo(); + + if (info.modifies != null) { + return false; + } + + info.modifies = modifies; + return true; + } + + /** + * Documents the version. + */ + boolean documentVersion(String version) { + if (!lazyInitDocumentation()) { + return true; + } + + if (documentation.version != null) { + return false; + } + + documentation.version = version; + return true; + } + + /** + * Documents a reference (i.e. adds a "see" reference to the list). + */ + boolean documentReference(String reference) { + if (!lazyInitDocumentation()) { + return true; + } + + if (documentation.sees == null) { + documentation.sees = Lists.newArrayList(); + } + + documentation.sees.add(reference); + return true; + } + + /** + * Documents the author (i.e. adds it to the author list). + */ + boolean documentAuthor(String author) { + if (!lazyInitDocumentation()) { + return true; + } + + if (documentation.authors == null) { + documentation.authors = Lists.newArrayList(); + } + + documentation.authors.add(author); + return true; + } + + /** + * Documents the throws (i.e. adds it to the throws list). + */ + boolean documentThrows(JSTypeExpression type, String throwsDescription) { + if (!lazyInitDocumentation()) { + return true; + } + + if (documentation.throwsDescriptions == null) { + documentation.throwsDescriptions = + new LinkedHashMap(); + } + + if (!documentation.throwsDescriptions.containsKey(type)) { + documentation.throwsDescriptions.put(type, throwsDescription); + return true; + } + + return false; + } + + + /** + * Documents a parameter. Parameters are described using the {@code @param} + * annotation. + * + * @param parameter the parameter's name + * @param description the parameter's description + */ + boolean documentParam(String parameter, String description) { + if (!lazyInitDocumentation()) { + return true; + } + + if (documentation.parameters == null) { + documentation.parameters = new LinkedHashMap(); + } + + if (!documentation.parameters.containsKey(parameter)) { + documentation.parameters.put(parameter, description); + return true; + } else { + return false; + } + } + + /** + * Documents the block-level comment/description. + * + * @param description the description + */ + boolean documentBlock(String description) { + if (!lazyInitDocumentation()) { + return true; + } + + if (documentation.blockDescription != null) { + return false; + } + + documentation.blockDescription = description; + return true; + } + + /** + * Documents the fileoverview comment/description. + * + * @param description the description + */ + boolean documentFileOverview(String description) { + setFlag(true, MASK_FILEOVERVIEW); + if (!lazyInitDocumentation()) { + return true; + } + + if (documentation.fileOverview != null) { + return false; + } + + documentation.fileOverview = description; + return true; + } + + /** + * Documents the return value. Return value is described using the + * {@code @return} annotation. + * + * @param description the return value's description + */ + boolean documentReturn(String description) { + if (!lazyInitDocumentation()) { + return true; + } + + if (documentation.returnDescription != null) { + return false; + } + + documentation.returnDescription = description; + return true; + } + + /** + * Declares a parameter. Parameters are described using the {@code @param} + * annotation. + * + * @param jsType the parameter's type, it may be {@code null} when the + * {@code @param} annotation did not specify a type. + * @param parameter the parameter's name + */ + boolean declareParam(JSTypeExpression jsType, String parameter) { + lazyInitInfo(); + if (info.parameters == null) { + info.parameters = new LinkedHashMap(); + } + if (!info.parameters.containsKey(parameter)) { + info.parameters.put(parameter, jsType); + return true; + } else { + return false; + } + } + + /** + * Declares a template type name. Template type names are described using the + * {@code @template} annotation. + * + * @param templateTypeNames the template type name. + */ + boolean declareTemplateTypeNames(List templateTypeNames) { + lazyInitInfo(); + + if (info.templateTypeNames != null) { + return false; + } + + info.templateTypeNames = ImmutableList.copyOf(templateTypeNames); + return true; + } + + /** + * Declares that the method throws a given type. + * + * @param jsType The type that can be thrown by the method. + */ + boolean declareThrows(JSTypeExpression jsType) { + lazyInitInfo(); + + if (info.thrownTypes == null) { + info.thrownTypes = Lists.newArrayList(); + } + + info.thrownTypes.add(jsType); + return true; + } + + /** + * Gets the visibility specified by {@code @private}, {@code @protected} or + * {@code @public} annotation. If no visibility is specified, visibility + * is inherited from the base class. + */ + public Visibility getVisibility() { + return visibility; + } + + /** + * Gets the parameter type. + * @param parameter the parameter's name + * @return the parameter's type or {@code null} if this parameter is not + * defined or has a {@code null} type + */ + public JSTypeExpression getParameterType(String parameter) { + if (info == null || info.parameters == null) { + return null; + } + return info.parameters.get(parameter); + } + + /** + * Returns whether the parameter is defined. + */ + public boolean hasParameter(String parameter) { + if (info == null || info.parameters == null) { + return false; + } + return info.parameters.containsKey(parameter); + } + + /** + * Returns whether the parameter has an attached type. + * + * @return {@code true} if the parameter has an attached type, {@code false} + * if the parameter has no attached type or does not exist. + */ + public boolean hasParameterType(String parameter) { + return getParameterType(parameter) != null; + } + + /** + * Returns the set of names of the defined parameters. The iteration order + * of the returned set is not the order in which parameters are defined. + * + * @return the set of names of the defined parameters. The returned set is + * immutable. + */ + public Set getParameterNames() { + if (info == null || info.parameters == null) { + return ImmutableSet.of(); + } + return ImmutableSet.copyOf(info.parameters.keySet()); + } + + /** + * Gets the number of parameters defined. + */ + public int getParameterCount() { + if (info == null || info.parameters == null) { + return 0; + } + return info.parameters.size(); + } + + void setType(JSTypeExpression type) { + setType(type, TYPEFIELD_TYPE); + } + + void setReturnType(JSTypeExpression type) { + setType(type, TYPEFIELD_RETURN); + } + + void setEnumParameterType(JSTypeExpression type) { + setType(type, TYPEFIELD_ENUM); + } + + void setTypedefType(JSTypeExpression type) { + setType(type, TYPEFIELD_TYPEDEF); + } + + private void setType(JSTypeExpression type, int mask) { + if ((bitset & MASK_TYPEFIELD) != 0) { + throw new IllegalStateException( + "API tried to add two incompatible type tags. " + + "This should have been blocked and emitted a warning."); + } + this.bitset = (bitset & MASK_FLAGS) | mask; + this.type = type; + } + + /** + * Returns the list of thrown types. + */ + public List getThrownTypes() { + if (info == null || info.thrownTypes == null) { + return ImmutableList.of(); + } + return Collections.unmodifiableList(info.thrownTypes); + } + + /** + * Returns whether a type, specified using the {@code @type} annotation, is + * present on this JSDoc. + */ + public boolean hasType() { + return hasType(TYPEFIELD_TYPE); + } + + /** + * Returns whether an enum parameter type, specified using the {@code @enum} + * annotation, is present on this JSDoc. + */ + public boolean hasEnumParameterType() { + return hasType(TYPEFIELD_ENUM); + } + + /** + * Returns whether a typedef parameter type, specified using the + * {@code @typedef} annotation, is present on this JSDoc. + */ + public boolean hasTypedefType() { + return hasType(TYPEFIELD_TYPEDEF); + } + + /** + * Returns whether this {@link JSDocInfo} contains a type for {@code @return} + * annotation. + */ + public boolean hasReturnType() { + return hasType(TYPEFIELD_RETURN); + } + + private boolean hasType(int mask) { + return (bitset & MASK_TYPEFIELD) == mask; + } + + /** + * Gets the type specified by the {@code @type} annotation. + */ + public JSTypeExpression getType() { + return getType(TYPEFIELD_TYPE); + } + + /** + * Gets the return type specified by the {@code @return} annotation. + */ + public JSTypeExpression getReturnType() { + return getType(TYPEFIELD_RETURN); + } + + /** + * Gets the enum parameter type specified by the {@code @enum} annotation. + */ + public JSTypeExpression getEnumParameterType() { + return getType(TYPEFIELD_ENUM); + } + + /** + * Gets the typedef type specified by the {@code @type} annotation. + */ + public JSTypeExpression getTypedefType() { + return getType(TYPEFIELD_TYPEDEF); + } + + private JSTypeExpression getType(int typefield) { + if ((MASK_TYPEFIELD & bitset) == typefield) { + return type; + } else { + return null; + } + } + + /** + * Gets the type specified by the {@code @this} annotation. + */ + public JSTypeExpression getThisType() { + return thisType; + } + + /** + * Sets the type specified by the {@code @this} annotation. + */ + void setThisType(JSTypeExpression type) { + this.thisType = type; + } + + /** + * Returns whether this {@link JSDocInfo} contains a type for {@code @this} + * annotation. + */ + public boolean hasThisType() { + return thisType != null; + } + + void setBaseType(JSTypeExpression type) { + lazyInitInfo(); + info.baseType = type; + } + + /** + * Gets the base type specified by the {@code @extends} annotation. + */ + public JSTypeExpression getBaseType() { + return (info == null) ? null : info.baseType; + } + + /** + * Gets the description specified by the {@code @desc} annotation. + */ + public String getDescription() { + return (info == null) ? null : info.description; + } + + void setDescription(String desc) { + lazyInitInfo(); + info.description = desc; + } + + /** + * Gets the meaning specified by the {@code @meaning} annotation. + * + * In localization systems, two messages with the same content but + * different "meanings" may be translated differently. By default, we + * use the name of the variable that the message is initialized to as + * the "meaning" of the message. + * + * But some code generators (like Closure Templates) inject their own + * meaning with the jsdoc {@code @meaning} annotation. + */ + public String getMeaning() { + return (info == null) ? null : info.meaning; + } + + void setMeaning(String meaning) { + lazyInitInfo(); + info.meaning = meaning; + } + + /** + * Gets the name we're lending to in a {@code @lends} annotation. + * + * In many reflection APIs, you pass an anonymous object to a function, + * and that function mixes the anonymous object into another object. + * The {@code @lends} annotation allows the type system to track + * those property assignments. + */ + public String getLendsName() { + return (info == null) ? null : info.lendsName; + } + + void setLendsName(String name) { + lazyInitInfo(); + info.lendsName = name; + } + + /** + * Gets the description specified by the {@code @license} annotation. + */ + public String getLicense() { + return (info == null) ? null : info.license; + } + + /** License directives can appear in multiple comments, and always + * apply to the entire file. Break protection and allow outsiders to + * update the license string so that we can attach the license text even + * when the JSDocInfo has been created and tagged with other information. + * @param license String containing new license text. + */ + + public void setLicense(String license) { + lazyInitInfo(); + info.license = license; + } + + @Override + public String toString() { + return "JSDocInfo"; + } + + /** + * Returns whether this {@link JSDocInfo} contains a type for {@code @extends} + * annotation. + */ + public boolean hasBaseType() { + return getBaseType() != null; + } + + /** + * Adds an implemented interface. Returns whether the interface was added. If + * the interface was already present in the list, it won't get added again. + */ + boolean addImplementedInterface(JSTypeExpression interfaceName) { + lazyInitInfo(); + if (info.implementedInterfaces == null) { + info.implementedInterfaces = Lists.newArrayListWithCapacity(2); + } + if (info.implementedInterfaces.contains(interfaceName)) { + return false; + } + info.implementedInterfaces.add(interfaceName); + return true; + } + + /** + * Returns the types specified by the {@code @implements} annotation. + * + * @return An immutable list of JSTypeExpression objects that can + * be resolved to types. + */ + public List getImplementedInterfaces() { + if (info == null || info.implementedInterfaces == null) { + return ImmutableList.of(); + } + return Collections.unmodifiableList(info.implementedInterfaces); + } + + /** + * Gets the number of interfaces specified by the {@code @implements} + * annotation. + */ + public int getImplementedInterfaceCount() { + if (info == null || info.implementedInterfaces == null) { + return 0; + } + return info.implementedInterfaces.size(); + } + + /** + * Adds an extended interface (for interface only). + * Returns whether the type was added. + * if the type was already present in the list, it won't get added again. + */ + boolean addExtendedInterface(JSTypeExpression type) { + lazyInitInfo(); + if (info.extendedInterfaces == null) { + info.extendedInterfaces = Lists.newArrayListWithCapacity(2); + } + if (info.extendedInterfaces.contains(type)) { + return false; + } + info.extendedInterfaces.add(type); + return true; + } + + /** + * Returns the interfaces extended by an interface + * + * @return An immutable list of JSTypeExpression objects that can + * be resolved to types. + */ + public List getExtendedInterfaces() { + if (info == null || info.extendedInterfaces == null) { + return ImmutableList.of(); + } + return Collections.unmodifiableList(info.extendedInterfaces); + } + + /** + * Gets the number of extended interfaces specified + */ + public int getExtendedInterfacesCount() { + if (info == null || info.extendedInterfaces == null) { + return 0; + } + return info.extendedInterfaces.size(); + } + + /** + * Returns the deprecation reason or null if none specified. + */ + public String getDeprecationReason() { + return info == null ? null : info.deprecated; + } + + /** + * Returns the set of suppressed warnings. + */ + public Set getSuppressions() { + Set suppressions = info == null ? null : info.suppressions; + return suppressions == null ? Collections.emptySet() : suppressions; + } + + /** + * Returns the set of sideeffect notations. + */ + public Set getModifies() { + Set modifies = info == null ? null : info.modifies; + return modifies == null ? Collections.emptySet() : modifies; + } + + /** + * Returns whether a description exists for the parameter with the specified + * name. + */ + public boolean hasDescriptionForParameter(String name) { + if (documentation == null || documentation.parameters == null) { + return false; + } + + return documentation.parameters.containsKey(name); + } + + /** + * Returns the description for the parameter with the given name, if its + * exists. + */ + public String getDescriptionForParameter(String name) { + if (documentation == null || documentation.parameters == null) { + return null; + } + + return documentation.parameters.get(name); + } + + /** + * Returns the list of authors or null if none. + */ + public Collection getAuthors() { + return documentation == null ? null : documentation.authors; + } + + /** + * Returns the list of references or null if none. + */ + public Collection getReferences() { + return documentation == null ? null : documentation.sees; + } + + /** + * Returns the version or null if none. + */ + public String getVersion() { + return documentation == null ? null : documentation.version; + } + + /** + * Returns the description of the returned object or null if none specified. + */ + public String getReturnDescription() { + return documentation == null ? null : documentation.returnDescription; + } + + /** + * Returns the block-level description or null if none specified. + */ + public String getBlockDescription() { + return documentation == null ? null : documentation.blockDescription; + } + + /** + * Returns whether this has a fileoverview flag. + */ + public boolean hasFileOverview() { + return getFlag(MASK_FILEOVERVIEW); + } + + /** + * Returns the file overview or null if none specified. + */ + public String getFileOverview() { + return documentation == null ? null : documentation.fileOverview; + } + + public Node getAssociatedNode() { + return this.associatedNode; + } + + /** + * Sets the node associated with this JSDoc. + * Notice that many nodes may have pointer to the same JSDocInfo + * object (because we propagate it across the type graph). But there + * is only one canonical "owner" node of the JSDocInfo, which corresponds + * to its original place in the syntax tree. + */ + public void setAssociatedNode(Node node) { + this.associatedNode = node; + } + + /** Gets the name of the source file that contains this JSDoc. */ + public String getSourceName() { + return this.associatedNode != null + ? this.associatedNode.getSourceFileName() : null; + } + + /** Gets the list of all markers for the documentation in this JSDoc. */ + public Collection getMarkers() { + return (documentation == null || documentation.markers == null) + ? ImmutableList.of() : documentation.markers; + } + + /** Gets the template type name. */ + public ImmutableList getTemplateTypeNames() { + if (info == null || info.templateTypeNames == null) { + return ImmutableList.of(); + } + return info.templateTypeNames; + } + + /** + * Returns a collection of all type nodes that are a part of this JSDocInfo. + * This includes @type, @this, @extends, @implements, @param, @throws, + * and @return. Any future type specific JSDoc should make sure to add the + * appropriate nodes here. + * @return collection of all type nodes + */ + public Collection getTypeNodes() { + List nodes = Lists.newArrayList(); + + if (type != null) { + nodes.add(type.getRoot()); + } + + if (thisType != null) { + nodes.add(thisType.getRoot()); + } + + if (info != null) { + if (info.baseType != null) { + nodes.add(info.baseType.getRoot()); + } + + if (info.extendedInterfaces != null) { + for (JSTypeExpression interfaceType : info.extendedInterfaces) { + nodes.add(interfaceType.getRoot()); + } + } + + if (info.implementedInterfaces != null) { + for (JSTypeExpression interfaceType : info.implementedInterfaces) { + nodes.add(interfaceType.getRoot()); + } + } + + if (info.parameters != null) { + for (JSTypeExpression parameterType : info.parameters.values()) { + if (parameterType != null) { + nodes.add(parameterType.getRoot()); + } + } + } + + if (info.thrownTypes != null) { + for (JSTypeExpression thrownType : info.thrownTypes) { + if (thrownType != null) { + nodes.add(thrownType.getRoot()); + } + } + } + } + + return nodes; + } + + public boolean hasModifies() { + return info != null && info.modifies != null; + } + + /** + * Returns the original JSDoc comment string. Returns null unless + * parseJsDocDocumentation is enabled via the ParserConfig. + */ + public String getOriginalCommentString() { + return documentation == null ? null : documentation.sourceComment; + } + + void setOriginalCommentString(String sourceComment) { + if (!lazyInitDocumentation()) { + return; + } + documentation.sourceComment = sourceComment; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/JSDocInfoBuilder.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/JSDocInfoBuilder.java new file mode 100644 index 0000000..2fe8dd2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/JSDocInfoBuilder.java @@ -0,0 +1,1045 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino; + +import com.google.javascript.rhino.JSDocInfo.Visibility; +import com.google.javascript.rhino.jstype.StaticSourceFile; + +import java.util.List; +import java.util.Set; + +/** + * A builder for {@link JSDocInfo} objects. This builder abstracts the + * construction process of {@link JSDocInfo} objects whilst minimizing the + * number of instances of {@link JSDocInfo} objects. It provides early + * incompatibility detection among properties stored on the {@code JSDocInfo} + * object being created. + * + */ +final public class JSDocInfoBuilder { + // the current JSDoc which is being populated + private JSDocInfo currentInfo; + + // whether the current JSDocInfo has valuable information + private boolean populated = false; + + // whether to include the documentation itself when parsing the JsDoc + private boolean parseDocumentation = false; + + // the current marker, if any. + private JSDocInfo.Marker currentMarker = null; + + public JSDocInfoBuilder(boolean parseDocumentation) { + this.currentInfo = new JSDocInfo(parseDocumentation); + this.parseDocumentation = parseDocumentation; + } + + /** + * Sets the original JSDoc comment string. This is a no-op if the builder + * isn't configured to record documentation. + */ + public void recordOriginalCommentString(String sourceComment) { + if (parseDocumentation) { + currentInfo.setOriginalCommentString(sourceComment); + } + } + + public boolean shouldParseDocumentation() { + return parseDocumentation; + } + + /** + * Returns whether this builder is populated with information that can be + * used to {@link #build} a {@link JSDocInfo} object. + */ + public boolean isPopulated() { + return populated; + } + + /** + * Returns whether this builder is populated with information that can be + * used to {@link #build} a {@link JSDocInfo} object that has a + * fileoverview tag. + */ + public boolean isPopulatedWithFileOverview() { + return isPopulated() && + (currentInfo.hasFileOverview() || currentInfo.isExterns() || + currentInfo.isNoCompile()); + } + + /** + * Returns whether this builder recorded a description. + */ + public boolean isDescriptionRecorded() { + return currentInfo.getDescription() != null; + } + + /** + * Builds a {@link JSDocInfo} object based on the populated information and + * returns it. Once this method is called, the builder can be reused to build + * another {@link JSDocInfo} object. + * + * @param associatedNode The source node containing the JSDoc. + * @return a {@link JSDocInfo} object populated with the values given to this + * builder. If no value was populated, this method simply returns + * {@code null} + */ + public JSDocInfo build(Node associatedNode) { + if (populated) { + JSDocInfo built = currentInfo; + built.setAssociatedNode(associatedNode); + populateDefaults(built); + populated = false; + currentInfo = new JSDocInfo(this.parseDocumentation); + return built; + } else { + return null; + } + } + + /** Generate defaults when certain parameters are not specified. */ + private static void populateDefaults(JSDocInfo info) { + if (info.getVisibility() == null) { + info.setVisibility(Visibility.INHERITED); + } + } + + /** + * Adds a marker to the current JSDocInfo and populates the marker with the + * annotation information. + */ + public void markAnnotation(String annotation, int lineno, int charno) { + JSDocInfo.Marker marker = currentInfo.addMarker(); + + if (marker != null) { + JSDocInfo.TrimmedStringPosition position = + new JSDocInfo.TrimmedStringPosition(); + position.setItem(annotation); + position.setPositionInformation(lineno, charno, lineno, + charno + annotation.length()); + marker.setAnnotation(position); + populated = true; + } + + currentMarker = marker; + } + + /** + * Adds a textual block to the current marker. + */ + public void markText(String text, int startLineno, int startCharno, + int endLineno, int endCharno) { + if (currentMarker != null) { + JSDocInfo.StringPosition position = new JSDocInfo.StringPosition(); + position.setItem(text); + position.setPositionInformation(startLineno, startCharno, + endLineno, endCharno); + currentMarker.setDescription(position); + } + } + + /** + * Adds a type declaration to the current marker. + */ + public void markTypeNode(Node typeNode, int lineno, int startCharno, + int endLineno, int endCharno, boolean hasLC) { + if (currentMarker != null) { + JSDocInfo.TypePosition position = new JSDocInfo.TypePosition(); + position.setItem(typeNode); + position.setHasBrackets(hasLC); + position.setPositionInformation(lineno, startCharno, + endLineno, endCharno); + currentMarker.setType(position); + } + } + + /** + * Adds a name declaration to the current marker. + * @deprecated Use #markName(String, StaticSourceFile, int, int) + */ + @Deprecated + public void markName(String name, int lineno, int charno) { + markName(name, null, lineno, charno); + } + + /** + * Adds a name declaration to the current marker. + */ + public void markName(String name, StaticSourceFile file, + int lineno, int charno) { + if (currentMarker != null) { + // Record the name as both a SourcePosition and a + // SourcePosition. The form is deprecated, + // because is more consistent with how other name + // references are handled (see #markTypeNode) + // + // TODO(nicksantos): Remove all uses of the Name position + // and replace them with the NameNode position. + JSDocInfo.TrimmedStringPosition position = + new JSDocInfo.TrimmedStringPosition(); + position.setItem(name); + position.setPositionInformation(lineno, charno, + lineno, charno + name.length()); + currentMarker.setName(position); + + SourcePosition nodePos = + new JSDocInfo.NamePosition(); + Node node = Node.newString(Token.NAME, name, lineno, charno); + node.setLength(name.length()); + node.setStaticSourceFile(file); + nodePos.setItem(node); + nodePos.setPositionInformation(lineno, charno, + lineno, charno + name.length()); + currentMarker.setNameNode(nodePos); + } + } + + /** + * Records a block-level description. + * + * @return {@code true} if the description was recorded. + */ + public boolean recordBlockDescription(String description) { + populated = true; + return currentInfo.documentBlock(description); + } + + /** + * Records a visibility. + * + * @return {@code true} if the visibility was recorded and {@code false} + * if it was already defined + */ + public boolean recordVisibility(Visibility visibility) { + if (currentInfo.getVisibility() == null) { + populated = true; + currentInfo.setVisibility(visibility); + return true; + } else { + return false; + } + } + + /** + * Records a typed parameter. + * + * @return {@code true} if the typed parameter was recorded and + * {@code false} if a parameter with the same name was already defined + */ + public boolean recordParameter(String parameterName, JSTypeExpression type) { + if (!hasAnySingletonTypeTags() && + currentInfo.declareParam(type, parameterName)) { + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records a parameter's description. + * + * @return {@code true} if the parameter's description was recorded and + * {@code false} if a parameter with the same name was already defined + */ + public boolean recordParameterDescription( + String parameterName, String description) { + if (currentInfo.documentParam(parameterName, description)) { + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records a template type name. + * + * @return {@code true} if the template type name was recorded and + * {@code false} if a template type name was already defined. + */ + public boolean recordTemplateTypeNames(List names) { + if (currentInfo.declareTemplateTypeNames(names)) { + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records a thrown type. + */ + public boolean recordThrowType(JSTypeExpression type) { + if (!hasAnySingletonTypeTags()) { + currentInfo.declareThrows(type); + populated = true; + return true; + } + return false; + } + + /** + * Records a throw type's description. + * + * @return {@code true} if the type's description was recorded and + * {@code false} if a description with the same type was already defined + */ + public boolean recordThrowDescription( + JSTypeExpression type, String description) { + if (currentInfo.documentThrows(type, description)) { + populated = true; + return true; + } else { + return false; + } + } + + + /** + * Adds an author to the current information. + */ + public boolean addAuthor(String author) { + if (currentInfo.documentAuthor(author)) { + populated = true; + return true; + } else { + return false; + } + } + + + /** + * Adds a reference ("@see") to the current information. + */ + public boolean addReference(String reference) { + if (currentInfo.documentReference(reference)) { + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isConsistentIdGenerator()} flag set to + * {@code true}. + * + * @return {@code true} if the consistentIdGenerator flag was recorded and + * {@code false} if it was already recorded + */ + public boolean recordConsistentIdGenerator() { + if (!currentInfo.isConsistentIdGenerator()) { + currentInfo.setConsistentIdGenerator(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should have its {@link + * JSDocInfo#isStableIdGenerator()} flag set to {@code true}. + * + * @return {@code true} if the stableIdGenerator flag was recorded and {@code false} if it was + * already recorded. + */ + public boolean recordStableIdGenerator() { + if (!currentInfo.isStableIdGenerator()) { + currentInfo.setStableIdGenerator(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records the version. + */ + public boolean recordVersion(String version) { + if (currentInfo.documentVersion(version)) { + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records the deprecation reason. + */ + public boolean recordDeprecationReason(String reason) { + if (currentInfo.setDeprecationReason(reason)) { + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records the list of suppressed warnings. + */ + public boolean recordSuppressions(Set suppressions) { + if (currentInfo.setSuppressions(suppressions)) { + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records the list of modifies warnings. + */ + public boolean recordModifies(Set modifies) { + if (!hasAnySingletonSideEffectTags() + && currentInfo.setModifies(modifies)) { + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records a type. + * + * @return {@code true} if the type was recorded and {@code false} if + * it is invalid or was already defined + */ + public boolean recordType(JSTypeExpression type) { + if (type != null && !hasAnyTypeRelatedTags()) { + currentInfo.setType(type); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should be populated + * with a {@code typedef}'d type. + */ + public boolean recordTypedef(JSTypeExpression type) { + if (type != null && !hasAnyTypeRelatedTags()) { + currentInfo.setTypedefType(type); + populated = true; + return true; + } + return false; + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isIdGenerator()} flag set to + * {@code true}. + * + * @return {@code true} if the idGenerator flag was recorded and {@code false} + * if it was already recorded + */ + public boolean recordIdGenerator() { + if (!currentInfo.isIdGenerator()) { + currentInfo.setIdGenerator(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records a return type. + * + * @return {@code true} if the return type was recorded and {@code false} if + * it is invalid or was already defined + */ + public boolean recordReturnType(JSTypeExpression jsType) { + if (jsType != null && currentInfo.getReturnType() == null && + !hasAnySingletonTypeTags()) { + currentInfo.setReturnType(jsType); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records a return description + * + * @return {@code true} if the return description was recorded and + * {@code false} if it is invalid or was already defined + */ + public boolean recordReturnDescription(String description) { + if (currentInfo.documentReturn(description)) { + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records the type of a define. + * + * 'Define' values are special constants that may be manipulated by + * the compiler. They are designed to mimic the #define command in + * the C preprocessor. + */ + public boolean recordDefineType(JSTypeExpression type) { + if (type != null && + !currentInfo.isConstant() && + !currentInfo.isDefine() && + recordType(type)) { + currentInfo.setDefine(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records a parameter type to an enum. + * + * @return {@code true} if the enum's parameter type was recorded and + * {@code false} if it was invalid or already defined + */ + public boolean recordEnumParameterType(JSTypeExpression type) { + if (type != null && !hasAnyTypeRelatedTags()) { + currentInfo.setEnumParameterType(type); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records a type for {@code @this} annotation. + * + * @return {@code true} if the type was recorded and + * {@code false} if it is invalid or if it collided with {@code @enum} or + * {@code @type} annotations + */ + public boolean recordThisType(JSTypeExpression type) { + if (type != null && !hasAnySingletonTypeTags() && + !currentInfo.hasThisType()) { + currentInfo.setThisType(type); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records a base type. + * + * @return {@code true} if the base type was recorded and {@code false} + * if it was already defined + */ + public boolean recordBaseType(JSTypeExpression jsType) { + if (jsType != null && !hasAnySingletonTypeTags() && + !currentInfo.hasBaseType()) { + currentInfo.setBaseType(jsType); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isConstant()} flag set to {@code true}. + * + * @return {@code true} if the constancy was recorded and {@code false} + * if it was already defined + */ + public boolean recordConstancy() { + if (!currentInfo.isConstant()) { + currentInfo.setConstant(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records a description giving context for translation (i18n). + * + * @return {@code true} if the description was recorded and {@code false} + * if the description was invalid or was already defined + */ + public boolean recordDescription(String description) { + if (description != null && currentInfo.getDescription() == null) { + currentInfo.setDescription(description); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records a meaning giving context for translation (i18n). Different + * meanings will result in different translations. + * + * @return {@code true} If the meaning was successfully updated. + */ + public boolean recordMeaning(String meaning) { + if (meaning != null && currentInfo.getMeaning() == null) { + currentInfo.setMeaning(meaning); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records a fileoverview description. + * + * @return {@code true} if the description was recorded and {@code false} + * if the description was invalid or was already defined. + */ + public boolean recordFileOverview(String description) { + if (currentInfo.documentFileOverview(description)) { + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isHidden()} flag set to {@code true}. + * + * @return {@code true} if the hiddenness was recorded and {@code false} + * if it was already defined + */ + public boolean recordHiddenness() { + if (!currentInfo.isHidden()) { + currentInfo.setHidden(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isNoCompile()} flag set to {@code true}. + * + * @return {@code true} if the no compile flag was recorded and {@code false} + * if it was already recorded + */ + public boolean recordNoCompile() { + if (!currentInfo.isNoCompile()) { + currentInfo.setNoCompile(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isNoTypeCheck()} flag set to {@code true}. + * + * @return {@code true} if the no check flag was recorded and {@code false} + * if it was already recorded + */ + public boolean recordNoTypeCheck() { + if (!currentInfo.isNoTypeCheck()) { + currentInfo.setNoCheck(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isConstructor()} flag set to {@code true}. + * + * @return {@code true} if the constructor was recorded and {@code false} + * if it was already defined or it was incompatible with the existing + * flags + */ + public boolean recordConstructor() { + if (!hasAnySingletonTypeTags() && + !currentInfo.isConstructor() && !currentInfo.isInterface()) { + currentInfo.setConstructor(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Whether the {@link JSDocInfo} being built will have its + * {@link JSDocInfo#isConstructor()} flag set to {@code true}. + */ + public boolean isConstructorRecorded() { + return currentInfo.isConstructor(); + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#makesStructs()} flag set to {@code true}. + * + * @return {@code true} if the struct was recorded and {@code false} + * if it was already defined or it was incompatible with the existing flags + */ + public boolean recordStruct() { + if (hasAnySingletonTypeTags() || currentInfo.isInterface() || + currentInfo.makesDicts() || currentInfo.makesStructs()) { + return false; + } + currentInfo.setStruct(); + populated = true; + return true; + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#makesDicts()} flag set to {@code true}. + * + * @return {@code true} if the dict was recorded and {@code false} + * if it was already defined or it was incompatible with the existing flags + */ + public boolean recordDict() { + if (hasAnySingletonTypeTags() || currentInfo.isInterface() || + currentInfo.makesDicts() || currentInfo.makesStructs()) { + return false; + } + currentInfo.setDict(); + populated = true; + return true; + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isJavaDispatch()} flag set to {@code true}. + * + * @return {@code true} if the javadispatch was recorded and {@code false} + * if it was already defined or it was incompatible with the existing + * flags + */ + public boolean recordJavaDispatch() { + if (!currentInfo.isJavaDispatch()) { + currentInfo.setJavaDispatch(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Whether the {@link JSDocInfo} being built will have its + * {@link JSDocInfo#isJavaDispatch()} flag set to {@code true}. + */ + public boolean isJavaDispatch() { + return currentInfo.isJavaDispatch(); + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#shouldPreserveTry()} flag set to {@code true}. + */ + public boolean recordPreserveTry() { + if (!currentInfo.shouldPreserveTry()) { + currentInfo.setShouldPreserveTry(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isOverride()} flag set to {@code true}. + */ + public boolean recordOverride() { + if (!currentInfo.isOverride()) { + currentInfo.setOverride(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isNoAlias()} flag set to {@code true}. + */ + public boolean recordNoAlias() { + if (!currentInfo.isNoAlias()) { + currentInfo.setNoAlias(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isDeprecated()} flag set to {@code true}. + */ + public boolean recordDeprecated() { + if (!currentInfo.isDeprecated()) { + currentInfo.setDeprecated(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isInterface()} flag set to {@code true}. + * + * @return {@code true} if the flag was recorded and {@code false} + * if it was already defined or it was incompatible with the existing flags + */ + public boolean recordInterface() { + if (hasAnySingletonTypeTags() || + currentInfo.makesStructs() || currentInfo.makesDicts() || + currentInfo.isConstructor() || currentInfo.isInterface()) { + return false; + } + currentInfo.setInterface(true); + populated = true; + return true; + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isExport()} flag set to {@code true}. + */ + public boolean recordExport() { + if (!currentInfo.isExport()) { + currentInfo.setExport(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isExpose()} flag set to {@code true}. + */ + public boolean recordExpose() { + if (!currentInfo.isExpose()) { + currentInfo.setExpose(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isNoShadow()} flag set to {@code true}. + */ + public boolean recordNoShadow() { + if (!currentInfo.isNoShadow()) { + currentInfo.setNoShadow(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isImplicitCast()} flag set to {@code true}. + */ + public boolean recordImplicitCast() { + if (!currentInfo.isImplicitCast()) { + currentInfo.setImplicitCast(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isNoSideEffects()} flag set to {@code true}. + */ + public boolean recordNoSideEffects() { + if (!hasAnySingletonSideEffectTags() + && !currentInfo.isNoSideEffects()) { + currentInfo.setNoSideEffects(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that the {@link JSDocInfo} being built should have its + * {@link JSDocInfo#isExterns()} flag set to {@code true}. + */ + public boolean recordExterns() { + if (!currentInfo.isExterns()) { + currentInfo.setExterns(true); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Whether the {@link JSDocInfo} being built will have its + * {@link JSDocInfo#isInterface()} flag set to {@code true}. + */ + public boolean isInterfaceRecorded() { + return currentInfo.isInterface(); + } + + /** + * @return Whether a parameter of the given name has already been recorded. + */ + public boolean hasParameter(String name) { + return currentInfo.hasParameter(name); + } + + /** + * Records an implemented interface. + */ + public boolean recordImplementedInterface(JSTypeExpression interfaceName) { + if (currentInfo.addImplementedInterface(interfaceName)) { + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records an extended interface type. + */ + public boolean recordExtendedInterface(JSTypeExpression interfaceType) { + if (currentInfo.addExtendedInterface(interfaceType)) { + populated = true; + return true; + } else { + return false; + } + } + + /** + * Records that we're lending to another name. + */ + public boolean recordLends(String name) { + if (!hasAnyTypeRelatedTags()) { + currentInfo.setLendsName(name); + populated = true; + return true; + } else { + return false; + } + } + + /** + * Whether the current doc info has other type tags, like + * {@code @param} or {@code @return} or {@code @type} or etc. + */ + private boolean hasAnyTypeRelatedTags() { + return currentInfo.isConstructor() || + currentInfo.isInterface() || + currentInfo.getParameterCount() > 0 || + currentInfo.hasReturnType() || + currentInfo.hasBaseType() || + currentInfo.getExtendedInterfacesCount() > 0 || + currentInfo.getLendsName() != null || + currentInfo.hasThisType() || + hasAnySingletonTypeTags(); + } + + /** + * Whether the current doc info has any of the singleton type + * tags that may not appear with other type tags, like + * {@code @type} or {@code @typedef}. + */ + private boolean hasAnySingletonTypeTags() { + return currentInfo.hasType() || + currentInfo.hasTypedefType() || + currentInfo.hasEnumParameterType(); + } + + /** + * Whether the current doc info has any of the singleton type + * tags that may not appear with other type tags, like + * {@code @type} or {@code @typedef}. + */ + private boolean hasAnySingletonSideEffectTags() { + return currentInfo.isNoSideEffects() || + currentInfo.hasModifies(); + } + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/JSTypeExpression.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/JSTypeExpression.java new file mode 100644 index 0000000..2d928e3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/JSTypeExpression.java @@ -0,0 +1,123 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino; + +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.StaticScope; + +import java.io.Serializable; + + +/** + * Represents a type expression as a miniature Rhino AST, so that the + * type expression can be evaluated later. + * + * @author nicksantos@google.com (Nick Santos) + */ +public final class JSTypeExpression implements Serializable { + private static final long serialVersionUID = 1L; + + /** The root of the AST. */ + private final Node root; + + /** The source name where the type expression appears. */ + private final String sourceName; + + public JSTypeExpression(Node root, String sourceName) { + this.root = root; + this.sourceName = sourceName; + } + + /** + * Make the given type expression into an optional type expression, + * if possible. + */ + public static JSTypeExpression makeOptionalArg(JSTypeExpression expr) { + if (expr.isOptionalArg() || expr.isVarArgs()) { + return expr; + } else { + return new JSTypeExpression( + new Node(Token.EQUALS, expr.root), expr.sourceName); + } + } + + /** + * @return Whether this expression denotes an optional {@code @param}. + */ + public boolean isOptionalArg() { + return root.getType() == Token.EQUALS; + } + + /** + * @return Whether this expression denotes a rest args {@code @param}. + */ + public boolean isVarArgs() { + return root.getType() == Token.ELLIPSIS; + } + + /** + * Evaluates the type expression into a {@code JSType} object. + */ + public JSType evaluate(StaticScope scope, JSTypeRegistry registry) { + JSType type = registry.createFromTypeNodes(root, sourceName, scope); + root.setJSType(type); + return type; + } + + @Override + public boolean equals(Object other) { + return other instanceof JSTypeExpression && + ((JSTypeExpression) other).root.isEquivalentTo(root); + } + + @Override + public int hashCode() { + return root.toStringTree().hashCode(); + } + + /** + * @return The source for this type expression. Note that it will not + * contain an expression if there's an @override tag. + */ + public Node getRoot() { + return root; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/Messages.properties b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/Messages.properties new file mode 100644 index 0000000..43413bf --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/Messages.properties @@ -0,0 +1,278 @@ +# +# Default JavaScript messages file. +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Rhino code, released +# May 6, 1999. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1997-1999 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Norris Boyd +# Bob Jervis +# Pascal-Louis Perez +# +# Alternatively, the contents of this file may be used under the terms of +# the GNU General Public License Version 2 or later (the "GPL"), in which +# case the provisions of the GPL are applicable instead of those above. If +# you wish to allow use of your version of this file only under the terms of +# the GPL and not to allow others to use your version of this file under the +# MPL, indicate your decision by deleting the provisions above and replacing +# them with the notice and other provisions required by the GPL. If you do +# not delete the provisions above, a recipient may use your version of this +# file under either the MPL or the GPL. +# +# ***** END LICENSE BLOCK ***** + +# This is replaced during jar assembly from property string +# and should not be translated +implementation.version = @IMPLEMENTATION.VERSION@ + +# +# To add JavaScript error messages for a particular locale, create a +# new Messages_[locale].properties file, where [locale] is the Java +# string abbreviation for that locale. For example, JavaScript +# messages for the Polish locale should be located in +# Messages_pl.properties, and messages for the Italian Swiss locale +# should be located in Messages_it_CH.properties. Message properties +# files should be accessible through the classpath under +# org.mozilla.javascript.resources +# +# See: +# java.util.ResourceBundle +# java.text.MessageFormat +# + +# SomeJavaClassWhereUsed + +# Codegen +msg.dup.parms =\ + Duplicate parameter name "{0}". + +msg.unexpected.eof =\ + Unexpected end of file + +msg.extra.trailing.comma =\ + Trailing comma is not legal in an ECMA-262 object initializer + +msg.end.annotation.expected =\ + expected end of line or comment + +msg.bad.jsdoc.tag =\ + illegal use of unknown JSDoc tag "{0}"; ignoring it + +msg.missing.variable.name =\ + expecting a variable name in a @param tag + +msg.dup.variable.name =\ + duplicate variable name "{0}" + +msg.jsdoc.incompat.type =\ + type annotation incompatible with other annotations + +msg.jsdoc.type.syntax =\ + type not recognized due to syntax error + +msg.jsdoc.override =\ + extra @override/@inheritDoc tag + +msg.jsdoc.preservertry =\ + extra @preserveTry tag + +msg.jsdoc.visibility.private =\ + extra @private tag + +msg.jsdoc.visibility.protected =\ + extra @protected tag + +msg.jsdoc.visibility.public =\ + extra @public tag + +msg.jsdoc.idgen =\ + extra @idGenerator tag + +msg.jsdoc.hidden =\ + extra @hidden tag + +msg.jsdoc.nocheck =\ + extra @notypecheck tag + +msg.jsdoc.consistidgen =\ + extra @consistentIdGenerator tag + +msg.jsdoc.const =\ + conflicting @const tag + +msg.jsdoc.desc.extra =\ + extra @desc tag + +msg.jsdoc.meaning.extra =\ + extra @meaning tag + +msg.jsdoc.fileoverview.extra =\ + extra @fileoverview tag + +msg.jsdoc.lends.incompatible =\ + @lends tag incompatible with other annotations + +msg.jsdoc.lends.missing =\ + missing object name in @lends tag + +msg.jsdoc.preserve.nobuilder =\ + @preserve or @license annotation without file to associate it with + +msg.jsdoc.missing.lp =\ + missing opening ( + +msg.jsdoc.missing.lb =\ + missing opening [ + +msg.jsdoc.missing.rc =\ + expected closing } + +msg.jsdoc.missing.rp =\ + missing closing ) + +msg.jsdoc.missing.gt =\ + missing closing > + +msg.jsdoc.missing.rb =\ + missing closing ] + +msg.jsdoc.missing.colon =\ + expecting colon after this + +msg.jsdoc.function.this =\ + expecting this but {0} found + +msg.jsdoc.function.thisnotobject =\ + this type must be an object type + +msg.jsdoc.function.newnotobject =\ + constructed type must be an object type + +msg.jsdoc.function.varargs =\ + variable length argument must be last + +msg.jsdoc.type.union =\ + union type element with bad syntax + +msg.jsdoc.enum =\ + conflicting @enum tag + +msg.jsdoc.constructor =\ + conflicting @constructor tag + +msg.jsdoc.deprecated =\ + extra @deprecated tag + +msg.jsdoc.interface =\ + extra @interface tag + +msg.jsdoc.interface.constructor =\ + cannot be both an interface and a constructor + +msg.jsdoc.implements.duplicate =\ + duplicate @implements tag + +msg.jsdoc.noalias =\ + extra @noalias tag + +msg.jsdoc.noshadow =\ + extra @noshadow tag + +msg.jsdoc.nosideeffects =\ + conflicting @nosideeffects tag + +msg.jsdoc.implicitcast =\ + extra @implicitCast tag + +msg.jsdoc.this =\ + conflicting @this tag + +msg.jsdoc.this.object =\ + @this must specify an object type + +msg.jsdoc.type =\ + conflicting @type tag + +msg.jsdoc.define =\ + conflicting @define tag + +msg.jsdoc.define.badtype =\ + @define tag only permits literal types + +msg.jsdoc.extends =\ + conflicting @extends tag + +msg.jsdoc.export =\ + extra @export tag + +msg.jsdoc.expose =\ + extra @expose tag + +msg.jsdoc.externs =\ + extra @externs tag + +msg.jsdoc.javadispatch =\ + extra @javadispatch tag + +msg.jsdoc.nocompile =\ + extra @nocompile tag + +msg.jsdoc.seemissing =\ + @see tag missing description + +msg.jsdoc.authormissing =\ + @author tag missing author + +msg.jsdoc.versionmissing =\ + @version tag missing version information + +msg.jsdoc.extraversion =\ + conflicting @version tag + +msg.jsdoc.suppress =\ + malformed @suppress tag + +msg.jsdoc.suppress.duplicate =\ + duplicate @suppress tag + +msg.jsdoc.suppress.unknown =\ + unknown @suppress parameter: {0} + +msg.jsdoc.modifies =\ + malformed @modifies tag + +msg.jsdoc.modifies.duplicate =\ + conflicting @modifies tag + +msg.jsdoc.modifies.unknown =\ + unknown @modifies parameter: {0} + +msg.jsdoc.stableidgen =\ + extra @stableIdGenerator tag + +msg.jsdoc.templatemissing =\ + @template tag missing type name + +msg.jsdoc.template.at.most.once =\ + @template tag at most once + +msg.no.type.name =\ + expecting a type name diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/Node.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/Node.java new file mode 100644 index 0000000..55aefbe --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/Node.java @@ -0,0 +1,2380 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Norris Boyd + * Roger Lawrence + * Mike McCabe + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.SimpleSourceFile; +import com.google.javascript.rhino.jstype.StaticSourceFile; + +import java.io.IOException; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Set; + +/** + * This class implements the root of the intermediate representation. + * + */ + +public class Node implements Cloneable, Serializable { + + private static final long serialVersionUID = 1L; + + public static final int + JSDOC_INFO_PROP = 29, // contains a TokenStream.JSDocInfo object + VAR_ARGS_NAME = 30, // the name node is a variable length + // argument placeholder. + INCRDECR_PROP = 32, // pre or post type of increment/decrement + QUOTED_PROP = 36, // set to indicate a quoted object lit key + OPT_ARG_NAME = 37, // The name node is an optional argument. + SYNTHETIC_BLOCK_PROP = 38, // A synthetic block. Used to make + // processing simpler, and does not + // represent a real block in the source. + EMPTY_BLOCK = 39, // Used to indicate BLOCK that replaced + // EMPTY nodes. + ORIGINALNAME_PROP = 40, // The original name of the node, before + // renaming. + SIDE_EFFECT_FLAGS = 42, // Function or constructor call side effect + // flags + // Coding convention props + IS_CONSTANT_NAME = 43, // The variable or property is constant. + IS_NAMESPACE = 46, // The variable creates a namespace. + IS_DISPATCHER = 47, // The function is a dispatcher function, + // probably generated from Java code, and + // should be resolved to the proper + // overload if possible. + DIRECTIVES = 48, // The ES5 directives on this node. + DIRECT_EVAL = 49, // ES5 distinguishes between direct and + // indirect calls to eval. + FREE_CALL = 50, // A CALL without an explicit "this" value. + STATIC_SOURCE_FILE = 51, // A StaticSourceFile indicating the file + // where this node lives. + LENGTH = 52, // The length of the code represented by + // this node. + INPUT_ID = 53, // The id of the input associated with this + // node. + SLASH_V = 54, // Whether a STRING node contains a \v + // vertical tab escape. This is a total hack. + // See comments in IRFactory about this. + INFERRED_FUNCTION = 55, // Marks a function whose parameter types + // have been inferred. + LAST_PROP = 55; + + public static final int // flags for INCRDECR_PROP + DECR_FLAG = 0x1, + POST_FLAG = 0x2; + + private static final String propToString(int propType) { + switch (propType) { + case VAR_ARGS_NAME: return "var_args_name"; + + case JSDOC_INFO_PROP: return "jsdoc_info"; + + case INCRDECR_PROP: return "incrdecr"; + case QUOTED_PROP: return "quoted"; + case OPT_ARG_NAME: return "opt_arg"; + + case SYNTHETIC_BLOCK_PROP: return "synthetic"; + case EMPTY_BLOCK: return "empty_block"; + case ORIGINALNAME_PROP: return "originalname"; + case SIDE_EFFECT_FLAGS: return "side_effect_flags"; + + case IS_CONSTANT_NAME: return "is_constant_name"; + case IS_NAMESPACE: return "is_namespace"; + case IS_DISPATCHER: return "is_dispatcher"; + case DIRECTIVES: return "directives"; + case DIRECT_EVAL: return "direct_eval"; + case FREE_CALL: return "free_call"; + case STATIC_SOURCE_FILE: return "source_file"; + case INPUT_ID: return "input_id"; + case LENGTH: return "length"; + case SLASH_V: return "slash_v"; + case INFERRED_FUNCTION: return "inferred"; + default: + throw new IllegalStateException("unexpect prop id " + propType); + } + } + + private static class NumberNode extends Node { + + private static final long serialVersionUID = 1L; + + NumberNode(double number) { + super(Token.NUMBER); + this.number = number; + } + + public NumberNode(double number, int lineno, int charno) { + super(Token.NUMBER, lineno, charno); + this.number = number; + } + + @Override + public double getDouble() { + return this.number; + } + + @Override + public void setDouble(double d) { + this.number = d; + } + + @Override + boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) { + boolean equivalent = super.isEquivalentTo(node, compareJsType, recurse); + if (equivalent) { + double thisValue = getDouble(); + double thatValue = ((NumberNode) node).getDouble(); + if (thisValue == thatValue) { + // detect the difference between 0.0 and -0.0. + return (thisValue != 0.0) || (1/thisValue == 1/thatValue); + } + } + return false; + } + + private double number; + } + + private static class StringNode extends Node { + + private static final long serialVersionUID = 1L; + + StringNode(int type, String str) { + super(type); + if (null == str) { + throw new IllegalArgumentException("StringNode: str is null"); + } + this.str = str; + } + + StringNode(int type, String str, int lineno, int charno) { + super(type, lineno, charno); + if (null == str) { + throw new IllegalArgumentException("StringNode: str is null"); + } + this.str = str; + } + + /** + * returns the string content. + * @return non null. + */ + @Override + public String getString() { + return this.str; + } + + /** + * sets the string content. + * @param str the new value. Non null. + */ + @Override + public void setString(String str) { + if (null == str) { + throw new IllegalArgumentException("StringNode: str is null"); + } + this.str = str; + } + + @Override + boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) { + return (super.isEquivalentTo(node, compareJsType, recurse) + && this.str.equals(((StringNode) node).str)); + } + + /** + * If the property is not defined, this was not a quoted key. The + * QUOTED_PROP int property is only assigned to STRING tokens used as + * object lit keys. + * @return true if this was a quoted string key in an object literal. + */ + @Override + public boolean isQuotedString() { + return getBooleanProp(QUOTED_PROP); + } + + /** + * This should only be called for STRING nodes created in object lits. + */ + @Override + public void setQuotedString() { + putBooleanProp(QUOTED_PROP, true); + } + + private String str; + } + + // PropListItems must be immutable so that they can be shared. + private interface PropListItem { + int getType(); + PropListItem getNext(); + PropListItem chain(PropListItem next); + Object getObjectValue(); + int getIntValue(); + } + + private static abstract class AbstractPropListItem + implements PropListItem, Serializable { + private static final long serialVersionUID = 1L; + + private final PropListItem next; + private final int propType; + + AbstractPropListItem(int propType, PropListItem next) { + this.propType = propType; + this.next = next; + } + + @Override + public int getType() { + return propType; + } + + @Override + public PropListItem getNext() { + return next; + } + + @Override + public abstract PropListItem chain(PropListItem next); + } + + // A base class for Object storing props + private static class ObjectPropListItem + extends AbstractPropListItem { + private static final long serialVersionUID = 1L; + + private final Object objectValue; + + ObjectPropListItem(int propType, Object objectValue, PropListItem next) { + super(propType, next); + this.objectValue = objectValue; + } + + @Override + public int getIntValue() { + throw new UnsupportedOperationException(); + } + + @Override + public Object getObjectValue() { + return objectValue; + } + + @Override + public String toString() { + return objectValue == null ? "null" : objectValue.toString(); + } + + @Override + public PropListItem chain(PropListItem next) { + return new ObjectPropListItem(getType(), objectValue, next); + } + } + + // A base class for int storing props + private static class IntPropListItem extends AbstractPropListItem { + private static final long serialVersionUID = 1L; + + final int intValue; + + IntPropListItem(int propType, int intValue, PropListItem next) { + super(propType, next); + this.intValue = intValue; + } + + @Override + public int getIntValue() { + return intValue; + } + + @Override + public Object getObjectValue() { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + return String.valueOf(intValue); + } + + @Override + public PropListItem chain(PropListItem next) { + return new IntPropListItem(getType(), intValue, next); + } + } + + public Node(int nodeType) { + type = nodeType; + parent = null; + sourcePosition = -1; + } + + public Node(int nodeType, Node child) { + Preconditions.checkArgument(child.parent == null, + "new child has existing parent"); + Preconditions.checkArgument(child.next == null, + "new child has existing sibling"); + + type = nodeType; + parent = null; + first = last = child; + child.next = null; + child.parent = this; + sourcePosition = -1; + } + + public Node(int nodeType, Node left, Node right) { + Preconditions.checkArgument(left.parent == null, + "first new child has existing parent"); + Preconditions.checkArgument(left.next == null, + "first new child has existing sibling"); + Preconditions.checkArgument(right.parent == null, + "second new child has existing parent"); + Preconditions.checkArgument(right.next == null, + "second new child has existing sibling"); + type = nodeType; + parent = null; + first = left; + last = right; + left.next = right; + left.parent = this; + right.next = null; + right.parent = this; + sourcePosition = -1; + } + + public Node(int nodeType, Node left, Node mid, Node right) { + Preconditions.checkArgument(left.parent == null); + Preconditions.checkArgument(left.next == null); + Preconditions.checkArgument(mid.parent == null); + Preconditions.checkArgument(mid.next == null); + Preconditions.checkArgument(right.parent == null); + Preconditions.checkArgument(right.next == null); + type = nodeType; + parent = null; + first = left; + last = right; + left.next = mid; + left.parent = this; + mid.next = right; + mid.parent = this; + right.next = null; + right.parent = this; + sourcePosition = -1; + } + + public Node(int nodeType, Node left, Node mid, Node mid2, Node right) { + Preconditions.checkArgument(left.parent == null); + Preconditions.checkArgument(left.next == null); + Preconditions.checkArgument(mid.parent == null); + Preconditions.checkArgument(mid.next == null); + Preconditions.checkArgument(mid2.parent == null); + Preconditions.checkArgument(mid2.next == null); + Preconditions.checkArgument(right.parent == null); + Preconditions.checkArgument(right.next == null); + type = nodeType; + parent = null; + first = left; + last = right; + left.next = mid; + left.parent = this; + mid.next = mid2; + mid.parent = this; + mid2.next = right; + mid2.parent = this; + right.next = null; + right.parent = this; + sourcePosition = -1; + } + + public Node(int nodeType, int lineno, int charno) { + type = nodeType; + parent = null; + sourcePosition = mergeLineCharNo(lineno, charno); + } + + public Node(int nodeType, Node child, int lineno, int charno) { + this(nodeType, child); + sourcePosition = mergeLineCharNo(lineno, charno); + } + + public Node(int nodeType, Node left, Node right, int lineno, int charno) { + this(nodeType, left, right); + sourcePosition = mergeLineCharNo(lineno, charno); + } + + public Node(int nodeType, Node left, Node mid, Node right, + int lineno, int charno) { + this(nodeType, left, mid, right); + sourcePosition = mergeLineCharNo(lineno, charno); + } + + public Node(int nodeType, Node left, Node mid, Node mid2, Node right, + int lineno, int charno) { + this(nodeType, left, mid, mid2, right); + sourcePosition = mergeLineCharNo(lineno, charno); + } + + public Node(int nodeType, Node[] children, int lineno, int charno) { + this(nodeType, children); + sourcePosition = mergeLineCharNo(lineno, charno); + } + + public Node(int nodeType, Node[] children) { + this.type = nodeType; + parent = null; + if (children.length != 0) { + this.first = children[0]; + this.last = children[children.length - 1]; + + for (int i = 1; i < children.length; i++) { + if (null != children[i - 1].next) { + // fail early on loops. implies same node in array twice + throw new IllegalArgumentException("duplicate child"); + } + children[i - 1].next = children[i]; + Preconditions.checkArgument(children[i - 1].parent == null); + children[i - 1].parent = this; + } + Preconditions.checkArgument(children[children.length - 1].parent == null); + children[children.length - 1].parent = this; + + if (null != this.last.next) { + // fail early on loops. implies same node in array twice + throw new IllegalArgumentException("duplicate child"); + } + } + } + + public static Node newNumber(double number) { + return new NumberNode(number); + } + + public static Node newNumber(double number, int lineno, int charno) { + return new NumberNode(number, lineno, charno); + } + + public static Node newString(String str) { + return new StringNode(Token.STRING, str); + } + + public static Node newString(int type, String str) { + return new StringNode(type, str); + } + + public static Node newString(String str, int lineno, int charno) { + return new StringNode(Token.STRING, str, lineno, charno); + } + + public static Node newString(int type, String str, int lineno, int charno) { + return new StringNode(type, str, lineno, charno); + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public boolean hasChildren() { + return first != null; + } + + public Node getFirstChild() { + return first; + } + + public Node getLastChild() { + return last; + } + + public Node getNext() { + return next; + } + + public Node getChildBefore(Node child) { + if (child == first) { + return null; + } + Node n = first; + while (n.next != child) { + n = n.next; + if (n == null) { + throw new RuntimeException("node is not a child"); + } + } + return n; + } + + public Node getChildAtIndex(int i) { + Node n = first; + while (i > 0) { + n = n.next; + i--; + } + return n; + } + + public int getIndexOfChild(Node child) { + Node n = first; + int i = 0; + while (n != null) { + if (child == n) { + return i; + } + + n = n.next; + i++; + } + return -1; + } + + public Node getLastSibling() { + Node n = this; + while (n.next != null) { + n = n.next; + } + return n; + } + + public void addChildToFront(Node child) { + Preconditions.checkArgument(child.parent == null); + Preconditions.checkArgument(child.next == null); + child.parent = this; + child.next = first; + first = child; + if (last == null) { + last = child; + } + } + + public void addChildToBack(Node child) { + Preconditions.checkArgument(child.parent == null); + Preconditions.checkArgument(child.next == null); + child.parent = this; + child.next = null; + if (last == null) { + first = last = child; + return; + } + last.next = child; + last = child; + } + + public void addChildrenToFront(Node children) { + for (Node child = children; child != null; child = child.next) { + Preconditions.checkArgument(child.parent == null); + child.parent = this; + } + Node lastSib = children.getLastSibling(); + lastSib.next = first; + first = children; + if (last == null) { + last = lastSib; + } + } + + public void addChildrenToBack(Node children) { + addChildrenAfter(children, getLastChild()); + } + + /** + * Add 'child' before 'node'. + */ + public void addChildBefore(Node newChild, Node node) { + Preconditions.checkArgument(node != null && node.parent == this, + "The existing child node of the parent should not be null."); + Preconditions.checkArgument(newChild.next == null, + "The new child node has siblings."); + Preconditions.checkArgument(newChild.parent == null, + "The new child node already has a parent."); + if (first == node) { + newChild.parent = this; + newChild.next = first; + first = newChild; + return; + } + Node prev = getChildBefore(node); + addChildAfter(newChild, prev); + } + + /** + * Add 'child' after 'node'. + */ + public void addChildAfter(Node newChild, Node node) { + Preconditions.checkArgument(newChild.next == null, + "The new child node has siblings."); + addChildrenAfter(newChild, node); + } + + /** + * Add all children after 'node'. + */ + public void addChildrenAfter(Node children, Node node) { + Preconditions.checkArgument(node == null || node.parent == this); + for (Node child = children; child != null; child = child.next) { + Preconditions.checkArgument(child.parent == null); + child.parent = this; + } + + Node lastSibling = children.getLastSibling(); + if (node != null) { + Node oldNext = node.next; + node.next = children; + lastSibling.next = oldNext; + if (node == last) { + last = lastSibling; + } + } else { + // Append to the beginning. + if (first != null) { + lastSibling.next = first; + } else { + last = lastSibling; + } + first = children; + } + } + + /** + * Detach a child from its parent and siblings. + */ + public void removeChild(Node child) { + Node prev = getChildBefore(child); + if (prev == null) + first = first.next; + else + prev.next = child.next; + if (child == last) last = prev; + child.next = null; + child.parent = null; + } + + /** + * Detaches child from Node and replaces it with newChild. + */ + public void replaceChild(Node child, Node newChild) { + Preconditions.checkArgument(newChild.next == null, + "The new child node has siblings."); + Preconditions.checkArgument(newChild.parent == null, + "The new child node already has a parent."); + + // Copy over important information. + newChild.copyInformationFrom(child); + + newChild.next = child.next; + newChild.parent = this; + if (child == first) { + first = newChild; + } else { + Node prev = getChildBefore(child); + prev.next = newChild; + } + if (child == last) + last = newChild; + child.next = null; + child.parent = null; + } + + public void replaceChildAfter(Node prevChild, Node newChild) { + Preconditions.checkArgument(prevChild.parent == this, + "prev is not a child of this node."); + + Preconditions.checkArgument(newChild.next == null, + "The new child node has siblings."); + Preconditions.checkArgument(newChild.parent == null, + "The new child node already has a parent."); + + // Copy over important information. + newChild.copyInformationFrom(prevChild); + + Node child = prevChild.next; + newChild.next = child.next; + newChild.parent = this; + prevChild.next = newChild; + if (child == last) + last = newChild; + child.next = null; + child.parent = null; + } + + @VisibleForTesting + PropListItem lookupProperty(int propType) { + PropListItem x = propListHead; + while (x != null && propType != x.getType()) { + x = x.getNext(); + } + return x; + } + + /** + * Clone the properties from the provided node without copying + * the property object. The receiving node may not have any + * existing properties. + * @param other The node to clone properties from. + * @return this node. + */ + public Node clonePropsFrom(Node other) { + Preconditions.checkState(this.propListHead == null, + "Node has existing properties."); + this.propListHead = other.propListHead; + return this; + } + + public void removeProp(int propType) { + PropListItem result = removeProp(propListHead, propType); + if (result != propListHead) { + propListHead = result; + } + } + + /** + * @param item The item to inspect + * @param propType The property to look for + * @return The replacement list if the property was removed, or + * 'item' otherwise. + */ + private PropListItem removeProp(PropListItem item, int propType) { + if (item == null) { + return null; + } else if (item.getType() == propType) { + return item.getNext(); + } else { + PropListItem result = removeProp(item.getNext(), propType); + if (result != item.getNext()) { + return item.chain(result); + } else { + return item; + } + } + } + + public Object getProp(int propType) { + PropListItem item = lookupProperty(propType); + if (item == null) { + return null; + } + return item.getObjectValue(); + } + + public boolean getBooleanProp(int propType) { + return getIntProp(propType) != 0; + } + + /** + * Returns the integer value for the property, or 0 if the property + * is not defined. + */ + public int getIntProp(int propType) { + PropListItem item = lookupProperty(propType); + if (item == null) { + return 0; + } + return item.getIntValue(); + } + + public int getExistingIntProp(int propType) { + PropListItem item = lookupProperty(propType); + if (item == null) { + throw new IllegalStateException("missing prop: " + propType); + } + return item.getIntValue(); + } + + public void putProp(int propType, Object value) { + removeProp(propType); + if (value != null) { + propListHead = createProp(propType, value, propListHead); + } + } + + public void putBooleanProp(int propType, boolean value) { + putIntProp(propType, value ? 1 : 0); + } + + public void putIntProp(int propType, int value) { + removeProp(propType); + if (value != 0) { + propListHead = createProp(propType, value, propListHead); + } + } + + PropListItem createProp(int propType, Object value, PropListItem next) { + return new ObjectPropListItem(propType, value, next); + } + + PropListItem createProp(int propType, int value, PropListItem next) { + return new IntPropListItem(propType, value, next); + } + + // Gets all the property types, in sorted order. + private int[] getSortedPropTypes() { + int count = 0; + for (PropListItem x = propListHead; x != null; x = x.getNext()) { + count++; + } + + int[] keys = new int[count]; + for (PropListItem x = propListHead; x != null; x = x.getNext()) { + count--; + keys[count] = x.getType(); + } + + Arrays.sort(keys); + return keys; + } + + /** Can only be called when getType() == TokenStream.NUMBER */ + public double getDouble() throws UnsupportedOperationException { + if (this.getType() == Token.NUMBER) { + throw new IllegalStateException( + "Number node not created with Node.newNumber"); + } else { + throw new UnsupportedOperationException(this + " is not a number node"); + } + } + + /** Can only be called when getType() == TokenStream.NUMBER */ + public void setDouble(double s) throws UnsupportedOperationException { + if (this.getType() == Token.NUMBER) { + throw new IllegalStateException( + "Number node not created with Node.newNumber"); + } else { + throw new UnsupportedOperationException(this + " is not a string node"); + } + } + + /** Can only be called when node has String context. */ + public String getString() throws UnsupportedOperationException { + if (this.getType() == Token.STRING) { + throw new IllegalStateException( + "String node not created with Node.newString"); + } else { + throw new UnsupportedOperationException(this + " is not a string node"); + } + } + + /** Can only be called when node has String context. */ + public void setString(String s) throws UnsupportedOperationException { + if (this.getType() == Token.STRING) { + throw new IllegalStateException( + "String node not created with Node.newString"); + } else { + throw new UnsupportedOperationException(this + " is not a string node"); + } + } + + @Override + public String toString() { + return toString(true, true, true); + } + + public String toString( + boolean printSource, + boolean printAnnotations, + boolean printType) { + StringBuilder sb = new StringBuilder(); + toString(sb, printSource, printAnnotations, printType); + return sb.toString(); + } + + private void toString( + StringBuilder sb, + boolean printSource, + boolean printAnnotations, + boolean printType) { + sb.append(Token.name(type)); + if (this instanceof StringNode) { + sb.append(' '); + sb.append(getString()); + } else if (type == Token.FUNCTION) { + sb.append(' '); + // In the case of JsDoc trees, the first child is often not a string + // which causes exceptions to be thrown when calling toString or + // toStringTree. + if (first == null || first.getType() != Token.NAME) { + sb.append(""); + } else { + sb.append(first.getString()); + } + } else if (type == Token.NUMBER) { + sb.append(' '); + sb.append(getDouble()); + } + if (printSource) { + int lineno = getLineno(); + if (lineno != -1) { + sb.append(' '); + sb.append(lineno); + } + } + + if (printAnnotations) { + int[] keys = getSortedPropTypes(); + for (int i = 0; i < keys.length; i++) { + int type = keys[i]; + PropListItem x = lookupProperty(type); + sb.append(" ["); + sb.append(propToString(type)); + sb.append(": "); + String value; + switch (type) { + default: + value = x.toString(); + break; + } + sb.append(value); + sb.append(']'); + } + } + + if (printType) { + if (jsType != null) { + String jsTypeString = jsType.toString(); + if (jsTypeString != null) { + sb.append(" : "); + sb.append(jsTypeString); + } + } + } + } + + + public String toStringTree() { + return toStringTreeImpl(); + } + + private String toStringTreeImpl() { + try { + StringBuilder s = new StringBuilder(); + appendStringTree(s); + return s.toString(); + } catch (IOException e) { + throw new RuntimeException("Should not happen\n" + e); + } + } + + public void appendStringTree(Appendable appendable) throws IOException { + toStringTreeHelper(this, 0, appendable); + } + + private static void toStringTreeHelper(Node n, int level, Appendable sb) + throws IOException { + for (int i = 0; i != level; ++i) { + sb.append(" "); + } + sb.append(n.toString()); + sb.append('\n'); + for (Node cursor = n.getFirstChild(); + cursor != null; + cursor = cursor.getNext()) { + toStringTreeHelper(cursor, level + 1, sb); + } + } + + int type; // type of the node; Token.NAME for example + Node next; // next sibling + private Node first; // first element of a linked list of children + private Node last; // last element of a linked list of children + + /** + * Linked list of properties. Since vast majority of nodes would have + * no more then 2 properties, linked list saves memory and provides + * fast lookup. If this does not holds, propListHead can be replaced + * by UintMap. + */ + private PropListItem propListHead; + + /** + * COLUMN_BITS represents how many of the lower-order bits of + * sourcePosition are reserved for storing the column number. + * Bits above these store the line number. + * This gives us decent position information for everything except + * files already passed through a minimizer, where lines might + * be longer than 4096 characters. + */ + public static final int COLUMN_BITS = 12; + + /** + * MAX_COLUMN_NUMBER represents the maximum column number that can + * be represented. JSCompiler's modifications to Rhino cause all + * tokens located beyond the maximum column to MAX_COLUMN_NUMBER. + */ + public static final int MAX_COLUMN_NUMBER = (1 << COLUMN_BITS) - 1; + + /** + * COLUMN_MASK stores a value where bits storing the column number + * are set, and bits storing the line are not set. It's handy for + * separating column number from line number. + */ + public static final int COLUMN_MASK = MAX_COLUMN_NUMBER; + + /** + * Source position of this node. The position is encoded with the + * column number in the low 12 bits of the integer, and the line + * number in the rest. Create some handy constants so we can change this + * size if we want. + */ + private int sourcePosition; + + private JSType jsType; + + private Node parent; + + //========================================================================== + // Source position management + + public void setStaticSourceFile(StaticSourceFile file) { + this.putProp(STATIC_SOURCE_FILE, file); + } + + /** Sets the source file to a non-extern file of the given name. */ + public void setSourceFileForTesting(String name) { + this.putProp(STATIC_SOURCE_FILE, new SimpleSourceFile(name, false)); + } + + public String getSourceFileName() { + StaticSourceFile file = getStaticSourceFile(); + return file == null ? null : file.getName(); + } + + /** Returns the source file associated with this input. May be null */ + public StaticSourceFile getStaticSourceFile() { + return ((StaticSourceFile) this.getProp(STATIC_SOURCE_FILE)); + } + + /** + * @param inputId + */ + public void setInputId(InputId inputId) { + this.putProp(INPUT_ID, inputId); + } + + /** + * @return The Id of the CompilerInput associated with this Node. + */ + public InputId getInputId() { + return ((InputId) this.getProp(INPUT_ID)); + } + + public boolean isFromExterns() { + StaticSourceFile file = getStaticSourceFile(); + return file == null ? false : file.isExtern(); + } + + public int getLength() { + return getIntProp(LENGTH); + } + + public void setLength(int length) { + putIntProp(LENGTH, length); + } + + public int getLineno() { + return extractLineno(sourcePosition); + } + + public int getCharno() { + return extractCharno(sourcePosition); + } + + public int getSourceOffset() { + StaticSourceFile file = getStaticSourceFile(); + if (file == null) { + return -1; + } + int lineno = getLineno(); + if (lineno == -1) { + return -1; + } + return file.getLineOffset(lineno) + getCharno(); + } + + public int getSourcePosition() { + return sourcePosition; + } + + public void setLineno(int lineno) { + int charno = getCharno(); + if (charno == -1) { + charno = 0; + } + sourcePosition = mergeLineCharNo(lineno, charno); + } + + public void setCharno(int charno) { + sourcePosition = mergeLineCharNo(getLineno(), charno); + } + + public void setSourceEncodedPosition(int sourcePosition) { + this.sourcePosition = sourcePosition; + } + + public void setSourceEncodedPositionForTree(int sourcePosition) { + this.sourcePosition = sourcePosition; + + for (Node child = getFirstChild(); + child != null; child = child.getNext()) { + child.setSourceEncodedPositionForTree(sourcePosition); + } + } + + /** + * Merges the line number and character number in one integer. The Character + * number takes the first 12 bits and the line number takes the rest. If + * the character number is greater than 212-1 it is + * adjusted to 212-1. + */ + protected static int mergeLineCharNo(int lineno, int charno) { + if (lineno < 0 || charno < 0) { + return -1; + } else if ((charno & ~COLUMN_MASK) != 0) { + return lineno << COLUMN_BITS | COLUMN_MASK; + } else { + return lineno << COLUMN_BITS | (charno & COLUMN_MASK); + } + } + + /** + * Extracts the line number and character number from a merged line char + * number (see {@link #mergeLineCharNo(int, int)}). + */ + protected static int extractLineno(int lineCharNo) { + if (lineCharNo == -1) { + return -1; + } else { + return lineCharNo >>> COLUMN_BITS; + } + } + + /** + * Extracts the character number and character number from a merged line + * char number (see {@link #mergeLineCharNo(int, int)}). + */ + protected static int extractCharno(int lineCharNo) { + if (lineCharNo == -1) { + return -1; + } else { + return lineCharNo & COLUMN_MASK; + } + } + + //========================================================================== + // Iteration + + /** + *

      Return an iterable object that iterates over this node's children. + * The iterator does not support the optional operation + * {@link Iterator#remove()}.

      + * + *

      To iterate over a node's siblings, one can write

      + *
      Node n = ...;
      +   * for (Node child : n.children()) { ...
      + */ + public Iterable children() { + if (first == null) { + return Collections.emptySet(); + } else { + return new SiblingNodeIterable(first); + } + } + + /** + *

      Return an iterable object that iterates over this node's siblings. + * The iterator does not support the optional operation + * {@link Iterator#remove()}.

      + * + *

      To iterate over a node's siblings, one can write

      + *
      Node n = ...;
      +   * for (Node sibling : n.siblings()) { ...
      + */ + public Iterable siblings() { + return new SiblingNodeIterable(this); + } + + /** + * @see Node#siblings() + */ + private static final class SiblingNodeIterable + implements Iterable, Iterator { + private final Node start; + private Node current; + private boolean used; + + SiblingNodeIterable(Node start) { + this.start = start; + this.current = start; + this.used = false; + } + + @Override + public Iterator iterator() { + if (!used) { + used = true; + return this; + } else { + // We have already used the current object as an iterator; + // we must create a new SiblingNodeIterable based on this + // iterable's start node. + // + // Since the primary use case for Node.children is in for + // loops, this branch is extremely unlikely. + return (new SiblingNodeIterable(start)).iterator(); + } + } + + @Override + public boolean hasNext() { + return current != null; + } + + @Override + public Node next() { + if (current == null) { + throw new NoSuchElementException(); + } + try { + return current; + } finally { + current = current.getNext(); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + // ========================================================================== + // Accessors + + PropListItem getPropListHeadForTesting() { + return propListHead; + } + + public Node getParent() { + return parent; + } + + /** + * Gets the ancestor node relative to this. + * + * @param level 0 = this, 1 = the parent, etc. + */ + public Node getAncestor(int level) { + Preconditions.checkArgument(level >= 0); + Node node = this; + while (node != null && level-- > 0) { + node = node.getParent(); + } + return node; + } + + /** + * Iterates all of the node's ancestors excluding itself. + */ + public AncestorIterable getAncestors() { + return new AncestorIterable(this.getParent()); + } + + /** + * Iterator to go up the ancestor tree. + */ + public static class AncestorIterable implements Iterable { + private Node cur; + + /** + * @param cur The node to start. + */ + AncestorIterable(Node cur) { + this.cur = cur; + } + + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return cur != null; + } + + @Override + public Node next() { + if (!hasNext()) throw new NoSuchElementException(); + Node n = cur; + cur = cur.getParent(); + return n; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + } + + /** + * Check for one child more efficiently than by iterating over all the + * children as is done with Node.getChildCount(). + * + * @return Whether the node has exactly one child. + */ + public boolean hasOneChild() { + return first != null && first == last; + } + + /** + * Check for more than one child more efficiently than by iterating over all + * the children as is done with Node.getChildCount(). + * + * @return Whether the node more than one child. + */ + public boolean hasMoreThanOneChild() { + return first != null && first != last; + } + + public int getChildCount() { + int c = 0; + for (Node n = first; n != null; n = n.next) + c++; + + return c; + } + + // Intended for testing and verification only. + public boolean hasChild(Node child) { + for (Node n = first; n != null; n = n.getNext()) { + if (child == n) { + return true; + } + } + return false; + } + + /** + * Checks if the subtree under this node is the same as another subtree. + * Returns null if it's equal, or a message describing the differences. + */ + public String checkTreeEquals(Node node2) { + NodeMismatch diff = checkTreeEqualsImpl(node2); + if (diff != null) { + return "Node tree inequality:" + + "\nTree1:\n" + toStringTree() + + "\n\nTree2:\n" + node2.toStringTree() + + "\n\nSubtree1: " + diff.nodeA.toStringTree() + + "\n\nSubtree2: " + diff.nodeB.toStringTree(); + } + return null; + } + + /** + * Compare this node to node2 recursively and return the first pair of nodes + * that differs doing a preorder depth-first traversal. Package private for + * testing. Returns null if the nodes are equivalent. + */ + NodeMismatch checkTreeEqualsImpl(Node node2) { + if (!isEquivalentTo(node2, false, false)) { + return new NodeMismatch(this, node2); + } + + NodeMismatch res = null; + Node n, n2; + for (n = first, n2 = node2.first; + res == null && n != null; + n = n.next, n2 = n2.next) { + if (node2 == null) { + throw new IllegalStateException(); + } + res = n.checkTreeEqualsImpl(n2); + if (res != null) { + return res; + } + } + return res; + } + + /** + * Compare this node to node2 recursively and return the first pair of nodes + * that differs doing a preorder depth-first traversal. Package private for + * testing. Returns null if the nodes are equivalent. + */ + NodeMismatch checkTreeTypeAwareEqualsImpl(Node node2) { + // Do a non-recursive equivalents check. + if (!isEquivalentTo(node2, true, false)) { + return new NodeMismatch(this, node2); + } + + NodeMismatch res = null; + Node n, n2; + for (n = first, n2 = node2.first; + res == null && n != null; + n = n.next, n2 = n2.next) { + res = n.checkTreeTypeAwareEqualsImpl(n2); + if (res != null) { + return res; + } + } + return res; + } + + /** Returns true if this node is equivalent semantically to another */ + public boolean isEquivalentTo(Node node) { + return isEquivalentTo(node, false, true); + } + + /** + * Returns true if this node is equivalent semantically to another and + * the types are equivalent. + */ + public boolean isEquivalentToTyped(Node node) { + return isEquivalentTo(node, true, true); + } + + /** + * @param compareJsType Whether to compare the JSTypes of the nodes. + * @param recurse Whether to compare the children of the current node, if + * not only the the count of the children are compared. + * @return Whether this node is equivalent semantically to the provided node. + */ + boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) { + if (type != node.getType() + || getChildCount() != node.getChildCount() + || this.getClass() != node.getClass()) { + return false; + } + + if (compareJsType && !JSType.isEquivalent(jsType, node.getJSType())) { + return false; + } + + if (type == Token.INC || type == Token.DEC) { + int post1 = this.getIntProp(INCRDECR_PROP); + int post2 = node.getIntProp(INCRDECR_PROP); + if (post1 != post2) { + return false; + } + } else if (type == Token.STRING || type == Token.STRING_KEY) { + if (type == Token.STRING_KEY) { + int quoted1 = this.getIntProp(QUOTED_PROP); + int quoted2 = node.getIntProp(QUOTED_PROP); + if (quoted1 != quoted2) { + return false; + } + } + + int slashV1 = this.getIntProp(SLASH_V); + int slashV2 = node.getIntProp(SLASH_V); + if (slashV1 != slashV2) { + return false; + } + } else if (type == Token.CALL) { + if (this.getBooleanProp(FREE_CALL) != node.getBooleanProp(FREE_CALL)) { + return false; + } + } + + if (recurse) { + Node n, n2; + for (n = first, n2 = node.first; + n != null; + n = n.next, n2 = n2.next) { + if (!n.isEquivalentTo(n2, compareJsType, true)) { + return false; + } + } + } + + return true; + } + + /** + * This function takes a set of GETPROP nodes and produces a string that is + * each property separated by dots. If the node ultimately under the left + * sub-tree is not a simple name, this is not a valid qualified name. + * + * @return a null if this is not a qualified name, or a dot-separated string + * of the name and properties. + */ + public String getQualifiedName() { + if (type == Token.NAME) { + String name = getString(); + return name.isEmpty() ? null : name; + } else if (type == Token.GETPROP) { + String left = getFirstChild().getQualifiedName(); + if (left == null) { + return null; + } + return left + "." + getLastChild().getString(); + } else if (type == Token.THIS) { + return "this"; + } else { + return null; + } + } + + /** + * Returns whether a node corresponds to a simple or a qualified name, such as + * x or a.b.c or this.a. + */ + public boolean isQualifiedName() { + switch (getType()) { + case Token.NAME: + return getString().isEmpty() ? false : true; + case Token.THIS: + return true; + case Token.GETPROP: + return getFirstChild().isQualifiedName(); + default: + return false; + } + } + + /** + * Returns whether a node corresponds to a simple or a qualified name without + * a "this" reference, such as a.b.c, but not this.a + * . + */ + public boolean isUnscopedQualifiedName() { + switch (getType()) { + case Token.NAME: + return getString().isEmpty() ? false : true; + case Token.GETPROP: + return getFirstChild().isUnscopedQualifiedName(); + default: + return false; + } + } + + // ========================================================================== + // Mutators + + /** + * Removes this node from its parent. Equivalent to: + * node.getParent().removeChild(); + */ + public Node detachFromParent() { + Preconditions.checkState(parent != null); + parent.removeChild(this); + return this; + } + + /** + * Removes the first child of Node. Equivalent to: + * node.removeChild(node.getFirstChild()); + * + * @return The removed Node. + */ + public Node removeFirstChild() { + Node child = first; + if (child != null) { + removeChild(child); + } + return child; + } + + /** + * @return A Node that is the head of the list of children. + */ + public Node removeChildren() { + Node children = first; + for (Node child = first; child != null; child = child.getNext()) { + child.parent = null; + } + first = null; + last = null; + return children; + } + + /** + * Removes all children from this node and isolates the children from each + * other. + */ + public void detachChildren() { + for (Node child = first; child != null;) { + Node nextChild = child.getNext(); + child.parent = null; + child.next = null; + child = nextChild; + } + first = null; + last = null; + } + + public Node removeChildAfter(Node prev) { + Preconditions.checkArgument(prev.parent == this, + "prev is not a child of this node."); + Preconditions.checkArgument(prev.next != null, + "no next sibling."); + + Node child = prev.next; + prev.next = child.next; + if (child == last) last = prev; + child.next = null; + child.parent = null; + return child; + } + + /** + * @return A detached clone of the Node, specifically excluding its children. + */ + public Node cloneNode() { + Node result; + try { + result = (Node) super.clone(); + // PropListItem lists are immutable and can be shared so there is no + // need to clone them here. + result.next = null; + result.first = null; + result.last = null; + result.parent = null; + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e.getMessage()); + } + return result; + } + + /** + * @return A detached clone of the Node and all its children. + */ + public Node cloneTree() { + Node result = cloneNode(); + for (Node n2 = getFirstChild(); n2 != null; n2 = n2.getNext()) { + Node n2clone = n2.cloneTree(); + n2clone.parent = result; + if (result.last != null) { + result.last.next = n2clone; + } + if (result.first == null) { + result.first = n2clone; + } + result.last = n2clone; + } + return result; + } + + /** + * Copies source file and name information from the other + * node given to the current node. Used for maintaining + * debug information across node append and remove operations. + * @return this + */ + // TODO(nicksantos): The semantics of this method are ill-defined. Delete it. + public Node copyInformationFrom(Node other) { + if (getProp(ORIGINALNAME_PROP) == null) { + putProp(ORIGINALNAME_PROP, other.getProp(ORIGINALNAME_PROP)); + } + + if (getProp(STATIC_SOURCE_FILE) == null) { + putProp(STATIC_SOURCE_FILE, other.getProp(STATIC_SOURCE_FILE)); + sourcePosition = other.sourcePosition; + } + + return this; + } + + /** + * Copies source file and name information from the other node to the + * entire tree rooted at this node. + * @return this + */ + // TODO(nicksantos): The semantics of this method are ill-defined. Delete it. + public Node copyInformationFromForTree(Node other) { + copyInformationFrom(other); + for (Node child = getFirstChild(); + child != null; child = child.getNext()) { + child.copyInformationFromForTree(other); + } + + return this; + } + + /** + * Overwrite all the source information in this node with + * that of {@code other}. + */ + public Node useSourceInfoFrom(Node other) { + putProp(ORIGINALNAME_PROP, other.getProp(ORIGINALNAME_PROP)); + putProp(STATIC_SOURCE_FILE, other.getProp(STATIC_SOURCE_FILE)); + sourcePosition = other.sourcePosition; + return this; + } + + public Node srcref(Node other) { + return useSourceInfoFrom(other); + } + + /** + * Overwrite all the source information in this node and its subtree with + * that of {@code other}. + */ + public Node useSourceInfoFromForTree(Node other) { + useSourceInfoFrom(other); + for (Node child = getFirstChild(); + child != null; child = child.getNext()) { + child.useSourceInfoFromForTree(other); + } + + return this; + } + + public Node srcrefTree(Node other) { + return useSourceInfoFromForTree(other); + } + + /** + * Overwrite all the source information in this node with + * that of {@code other} iff the source info is missing. + */ + public Node useSourceInfoIfMissingFrom(Node other) { + if (getProp(ORIGINALNAME_PROP) == null) { + putProp(ORIGINALNAME_PROP, other.getProp(ORIGINALNAME_PROP)); + } + + if (getProp(STATIC_SOURCE_FILE) == null) { + putProp(STATIC_SOURCE_FILE, other.getProp(STATIC_SOURCE_FILE)); + sourcePosition = other.sourcePosition; + } + + return this; + } + + /** + * Overwrite all the source information in this node and its subtree with + * that of {@code other} iff the source info is missing. + */ + public Node useSourceInfoIfMissingFromForTree(Node other) { + useSourceInfoIfMissingFrom(other); + for (Node child = getFirstChild(); + child != null; child = child.getNext()) { + child.useSourceInfoIfMissingFromForTree(other); + } + + return this; + } + + //========================================================================== + // Custom annotations + + public JSType getJSType() { + return jsType; + } + + public void setJSType(JSType jsType) { + this.jsType = jsType; + } + + public FileLevelJsDocBuilder getJsDocBuilderForNode() { + return new FileLevelJsDocBuilder(); + } + + /** + * An inner class that provides back-door access to the license + * property of the JSDocInfo property for this node. This is only + * meant to be used for top-level script nodes where the + * {@link com.google.javascript.jscomp.parsing.JsDocInfoParser} needs to + * be able to append directly to the top-level node, not just the + * current node. + */ + public class FileLevelJsDocBuilder { + public void append(String fileLevelComment) { + JSDocInfo jsDocInfo = getJSDocInfo(); + if (jsDocInfo == null) { + // TODO(user): Is there a way to determine whether to + // parse the JsDoc documentation from here? + jsDocInfo = new JSDocInfo(false); + } + String license = jsDocInfo.getLicense(); + if (license == null) { + license = ""; + } + jsDocInfo.setLicense(license + fileLevelComment); + setJSDocInfo(jsDocInfo); + } + } + + /** + * Get the {@link JSDocInfo} attached to this node. + * @return the information or {@code null} if no JSDoc is attached to this + * node + */ + public JSDocInfo getJSDocInfo() { + return (JSDocInfo) getProp(JSDOC_INFO_PROP); + } + + /** + * Sets the {@link JSDocInfo} attached to this node. + */ + public Node setJSDocInfo(JSDocInfo info) { + putProp(JSDOC_INFO_PROP, info); + return this; + } + + /** + * Sets whether this node is a variable length argument node. This + * method is meaningful only on {@link Token#NAME} nodes + * used to define a {@link Token#FUNCTION}'s argument list. + */ + public void setVarArgs(boolean varArgs) { + putBooleanProp(VAR_ARGS_NAME, varArgs); + } + + /** + * Returns whether this node is a variable length argument node. This + * method's return value is meaningful only on {@link Token#NAME} nodes + * used to define a {@link Token#FUNCTION}'s argument list. + */ + public boolean isVarArgs() { + return getBooleanProp(VAR_ARGS_NAME); + } + + /** + * Sets whether this node is an optional argument node. This + * method is meaningful only on {@link Token#NAME} nodes + * used to define a {@link Token#FUNCTION}'s argument list. + */ + public void setOptionalArg(boolean optionalArg) { + putBooleanProp(OPT_ARG_NAME, optionalArg); + } + + /** + * Returns whether this node is an optional argument node. This + * method's return value is meaningful only on {@link Token#NAME} nodes + * used to define a {@link Token#FUNCTION}'s argument list. + */ + public boolean isOptionalArg() { + return getBooleanProp(OPT_ARG_NAME); + } + + /** + * Sets whether this is a synthetic block that should not be considered + * a real source block. + */ + public void setIsSyntheticBlock(boolean val) { + putBooleanProp(SYNTHETIC_BLOCK_PROP, val); + } + + /** + * Returns whether this is a synthetic block that should not be considered + * a real source block. + */ + public boolean isSyntheticBlock() { + return getBooleanProp(SYNTHETIC_BLOCK_PROP); + } + + /** + * Sets the ES5 directives on this node. + */ + public void setDirectives(Set val) { + putProp(DIRECTIVES, val); + } + + /** + * Returns the set of ES5 directives for this node. + */ + @SuppressWarnings("unchecked") + public Set getDirectives() { + return (Set) getProp(DIRECTIVES); + } + + /** + * Adds a warning to be suppressed. This is indistinguishable + * from having a {@code @suppress} tag in the code. + */ + public void addSuppression(String warning) { + if (getJSDocInfo() == null) { + setJSDocInfo(new JSDocInfo(false)); + } + getJSDocInfo().addSuppression(warning); + } + + /** + * Sets whether this is a synthetic block that should not be considered + * a real source block. + */ + public void setWasEmptyNode(boolean val) { + putBooleanProp(EMPTY_BLOCK, val); + } + + /** + * Returns whether this is a synthetic block that should not be considered + * a real source block. + */ + public boolean wasEmptyNode() { + return getBooleanProp(EMPTY_BLOCK); + } + + // There are four values of interest: + // global state changes + // this state changes + // arguments state changes + // whether the call throws an exception + // locality of the result + // We want a value of 0 to mean "global state changes and + // unknown locality of result". + + final public static int FLAG_GLOBAL_STATE_UNMODIFIED = 1; + final public static int FLAG_THIS_UNMODIFIED = 2; + final public static int FLAG_ARGUMENTS_UNMODIFIED = 4; + final public static int FLAG_NO_THROWS = 8; + final public static int FLAG_LOCAL_RESULTS = 16; + + final public static int SIDE_EFFECTS_FLAGS_MASK = 31; + + final public static int SIDE_EFFECTS_ALL = 0; + final public static int NO_SIDE_EFFECTS = + FLAG_GLOBAL_STATE_UNMODIFIED + | FLAG_THIS_UNMODIFIED + | FLAG_ARGUMENTS_UNMODIFIED + | FLAG_NO_THROWS; + + /** + * Marks this function or constructor call's side effect flags. + * This property is only meaningful for {@link Token#CALL} and + * {@link Token#NEW} nodes. + */ + public void setSideEffectFlags(int flags) { + Preconditions.checkArgument( + getType() == Token.CALL || getType() == Token.NEW, + "setIsNoSideEffectsCall only supports CALL and NEW nodes, got " + + Token.name(getType())); + + putIntProp(SIDE_EFFECT_FLAGS, flags); + } + + public void setSideEffectFlags(SideEffectFlags flags) { + setSideEffectFlags(flags.valueOf()); + } + + /** + * Returns the side effects flags for this node. + */ + public int getSideEffectFlags() { + return getIntProp(SIDE_EFFECT_FLAGS); + } + + /** + * A helper class for getting and setting the side-effect flags. + * @author johnlenz@google.com (John Lenz) + */ + public static class SideEffectFlags { + private int value = Node.SIDE_EFFECTS_ALL; + + public SideEffectFlags() { + } + + public SideEffectFlags(int value) { + this.value = value; + } + + public int valueOf() { + return value; + } + + /** All side-effect occur and the returned results are non-local. */ + public void setAllFlags() { + value = Node.SIDE_EFFECTS_ALL; + } + + /** No side-effects occur and the returned results are local. */ + public void clearAllFlags() { + value = Node.NO_SIDE_EFFECTS | Node.FLAG_LOCAL_RESULTS; + } + + public boolean areAllFlagsSet() { + return value == Node.SIDE_EFFECTS_ALL; + } + + /** + * Preserve the return result flag, but clear the others: + * no global state change, no throws, no this change, no arguments change + */ + public void clearSideEffectFlags() { + value |= Node.NO_SIDE_EFFECTS; + } + + public void setMutatesGlobalState() { + // Modify global means everything must be assumed to be modified. + removeFlag(Node.FLAG_GLOBAL_STATE_UNMODIFIED); + removeFlag(Node.FLAG_ARGUMENTS_UNMODIFIED); + removeFlag(Node.FLAG_THIS_UNMODIFIED); + } + + public void setThrows() { + removeFlag(Node.FLAG_NO_THROWS); + } + + public void setMutatesThis() { + removeFlag(Node.FLAG_THIS_UNMODIFIED); + } + + public void setMutatesArguments() { + removeFlag(Node.FLAG_ARGUMENTS_UNMODIFIED); + } + + public void setReturnsTainted() { + removeFlag(Node.FLAG_LOCAL_RESULTS); + } + + private void removeFlag(int flag) { + value &= ~flag; + } + } + + /** + * @return Whether the only side-effect is "modifies this" + */ + public boolean isOnlyModifiesThisCall() { + return areBitFlagsSet( + getSideEffectFlags() & Node.NO_SIDE_EFFECTS, + Node.FLAG_GLOBAL_STATE_UNMODIFIED + | Node.FLAG_ARGUMENTS_UNMODIFIED + | Node.FLAG_NO_THROWS); + } + + /** + * Returns true if this node is a function or constructor call that + * has no side effects. + */ + public boolean isNoSideEffectsCall() { + return areBitFlagsSet(getSideEffectFlags(), NO_SIDE_EFFECTS); + } + + /** + * Returns true if this node is a function or constructor call that + * returns a primitive or a local object (an object that has no other + * references). + */ + public boolean isLocalResultCall() { + return areBitFlagsSet(getSideEffectFlags(), FLAG_LOCAL_RESULTS); + } + + /** + * returns true if all the flags are set in value. + */ + private boolean areBitFlagsSet(int value, int flags) { + return (value & flags) == flags; + } + + /** + * This should only be called for STRING nodes children of OBJECTLIT. + */ + public boolean isQuotedString() { + return false; + } + + /** + * This should only be called for STRING nodes children of OBJECTLIT. + */ + public void setQuotedString() { + throw new IllegalStateException("not a StringNode"); + } + + static class NodeMismatch { + final Node nodeA; + final Node nodeB; + + NodeMismatch(Node nodeA, Node nodeB) { + this.nodeA = nodeA; + this.nodeB = nodeB; + } + + @Override + public boolean equals(Object object) { + if (object instanceof NodeMismatch) { + NodeMismatch that = (NodeMismatch) object; + return that.nodeA.equals(this.nodeA) && that.nodeB.equals(this.nodeB); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(nodeA, nodeB); + } + } + + + /*** AST type check methods ***/ + + public boolean isAdd() { + return this.getType() == Token.ADD; + } + + public boolean isAnd() { + return this.getType() == Token.AND; + } + + public boolean isArrayLit() { + return this.getType() == Token.ARRAYLIT; + } + + public boolean isAssign() { + return this.getType() == Token.ASSIGN; + } + + public boolean isAssignAdd() { + return this.getType() == Token.ASSIGN_ADD; + } + + public boolean isBlock() { + return this.getType() == Token.BLOCK; + } + + public boolean isBreak() { + return this.getType() == Token.BREAK; + } + + public boolean isCall() { + return this.getType() == Token.CALL; + } + + public boolean isCase() { + return this.getType() == Token.CASE; + } + + public boolean isCast() { + return this.getType() == Token.CAST; + } + + public boolean isCatch() { + return this.getType() == Token.CATCH; + } + + public boolean isComma() { + return this.getType() == Token.COMMA; + } + + public boolean isContinue() { + return this.getType() == Token.CONTINUE; + } + + public boolean isDebugger() { + return this.getType() == Token.DEBUGGER; + } + + public boolean isDec() { + return this.getType() == Token.DEC; + } + + public boolean isDefaultCase() { + return this.getType() == Token.DEFAULT_CASE; + } + + public boolean isDelProp() { + return this.getType() == Token.DELPROP; + } + + public boolean isDo() { + return this.getType() == Token.DO; + } + + public boolean isEmpty() { + return this.getType() == Token.EMPTY; + } + + public boolean isExprResult() { + return this.getType() == Token.EXPR_RESULT; + } + + public boolean isFalse() { + return this.getType() == Token.FALSE; + } + + public boolean isFor() { + return this.getType() == Token.FOR; + } + + public boolean isFunction() { + return this.getType() == Token.FUNCTION; + } + + public boolean isGetterDef() { + return this.getType() == Token.GETTER_DEF; + } + + public boolean isGetElem() { + return this.getType() == Token.GETELEM; + } + + public boolean isGetProp() { + return this.getType() == Token.GETPROP; + } + + public boolean isHook() { + return this.getType() == Token.HOOK; + } + + public boolean isIf() { + return this.getType() == Token.IF; + } + + public boolean isIn() { + return this.getType() == Token.IN; + } + + public boolean isInc() { + return this.getType() == Token.INC; + } + + public boolean isInstanceOf() { + return this.getType() == Token.INSTANCEOF; + } + + public boolean isLabel() { + return this.getType() == Token.LABEL; + } + + public boolean isLabelName() { + return this.getType() == Token.LABEL_NAME; + } + + public boolean isName() { + return this.getType() == Token.NAME; + } + + public boolean isNE() { + return this.getType() == Token.NE; + } + + public boolean isNew() { + return this.getType() == Token.NEW; + } + + public boolean isNot() { + return this.getType() == Token.NOT; + } + + public boolean isNull() { + return this.getType() == Token.NULL; + } + + public boolean isNumber() { + return this.getType() == Token.NUMBER; + } + + public boolean isObjectLit() { + return this.getType() == Token.OBJECTLIT; + } + + public boolean isOr() { + return this.getType() == Token.OR; + } + + public boolean isParamList() { + return this.getType() == Token.PARAM_LIST; + } + + public boolean isRegExp() { + return this.getType() == Token.REGEXP; + } + + public boolean isReturn() { + return this.getType() == Token.RETURN; + } + + public boolean isScript() { + return this.getType() == Token.SCRIPT; + } + + public boolean isSetterDef() { + return this.getType() == Token.SETTER_DEF; + } + + public boolean isString() { + return this.getType() == Token.STRING; + } + + public boolean isStringKey() { + return this.getType() == Token.STRING_KEY; + } + + public boolean isSwitch() { + return this.getType() == Token.SWITCH; + } + + public boolean isThis() { + return this.getType() == Token.THIS; + } + + public boolean isThrow() { + return this.getType() == Token.THROW; + } + + public boolean isTrue() { + return this.getType() == Token.TRUE; + } + + public boolean isTry() { + return this.getType() == Token.TRY; + } + + public boolean isTypeOf() { + return this.getType() == Token.TYPEOF; + } + + public boolean isVar() { + return this.getType() == Token.VAR; + } + + public boolean isVoid() { + return this.getType() == Token.VOID; + } + + public boolean isWhile() { + return this.getType() == Token.WHILE; + } + + public boolean isWith() { + return this.getType() == Token.WITH; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/ScriptRuntime.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/ScriptRuntime.java new file mode 100644 index 0000000..fda639e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/ScriptRuntime.java @@ -0,0 +1,405 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Patrick Beard + * Norris Boyd + * Igor Bukanov + * Ethan Hugg + * Roger Lawrence + * Terry Lucas + * Frank Mitchell + * Milen Nankov + * Andrew Wason + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino; + +import java.text.MessageFormat; +import java.util.Locale; +import java.util.ResourceBundle; + +/** + * This is the class that implements the run-time. + * + */ + +public class ScriptRuntime { + + /** + * No instances should be created. + */ + protected ScriptRuntime() { + } + + // It is public so NativeRegExp can access it . + public static boolean isJSLineTerminator(int c) { + // Optimization for faster check for EOL character: + // they do not have 0xDFD0 bits set + if ((c & 0xDFD0) != 0) { + return false; + } + return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029; + } + + // Can not use Double.NaN defined as 0.0d / 0.0 as under the Microsoft VM, + // versions 2.01 and 3.0P1, that causes some uses (returns at least) of + // Double.NaN to be converted to 1.0. + // So we use ScriptRuntime.NaN instead of Double.NaN. + public static final double + NaN = Double.longBitsToDouble(0x7ff8000000000000L); + + // A similar problem exists for negative zero. + public static final double + negativeZero = Double.longBitsToDouble(0x8000000000000000L); + + /* + * Helper function for toNumber, parseInt, and TokenStream.getToken. + */ + @SuppressWarnings("fallthrough") + static double stringToNumber(String s, int start, int radix) { + char digitMax = '9'; + char lowerCaseBound = 'a'; + char upperCaseBound = 'A'; + int len = s.length(); + if (radix < 10) { + digitMax = (char) ('0' + radix - 1); + } + if (radix > 10) { + lowerCaseBound = (char) ('a' + radix - 10); + upperCaseBound = (char) ('A' + radix - 10); + } + int end; + double sum = 0.0; + for (end=start; end < len; end++) { + char c = s.charAt(end); + int newDigit; + if ('0' <= c && c <= digitMax) + newDigit = c - '0'; + else if ('a' <= c && c < lowerCaseBound) + newDigit = c - 'a' + 10; + else if ('A' <= c && c < upperCaseBound) + newDigit = c - 'A' + 10; + else + break; + sum = sum*radix + newDigit; + } + if (start == end) { + return NaN; + } + if (sum >= 9007199254740992.0) { + if (radix == 10) { + /* If we're accumulating a decimal number and the number + * is >= 2^53, then the result from the repeated multiply-add + * above may be inaccurate. Call Java to get the correct + * answer. + */ + try { + return Double.valueOf(s.substring(start, end)).doubleValue(); + } catch (NumberFormatException nfe) { + return NaN; + } + } else if (radix == 2 || radix == 4 || radix == 8 || + radix == 16 || radix == 32) { + /* The number may also be inaccurate for one of these bases. + * This happens if the addition in value*radix + digit causes + * a round-down to an even least significant mantissa bit + * when the first dropped bit is a one. If any of the + * following digits in the number (which haven't been added + * in yet) are nonzero then the correct action would have + * been to round up instead of down. An example of this + * occurs when reading the number 0x1000000000000081, which + * rounds to 0x1000000000000000 instead of 0x1000000000000100. + */ + int bitShiftInChar = 1; + int digit = 0; + + final int SKIP_LEADING_ZEROS = 0; + final int FIRST_EXACT_53_BITS = 1; + final int AFTER_BIT_53 = 2; + final int ZEROS_AFTER_54 = 3; + final int MIXED_AFTER_54 = 4; + + int state = SKIP_LEADING_ZEROS; + int exactBitsLimit = 53; + double factor = 0.0; + boolean bit53 = false; + // bit54 is the 54th bit (the first dropped from the mantissa) + boolean bit54 = false; + + for (;;) { + if (bitShiftInChar == 1) { + if (start == end) + break; + digit = s.charAt(start++); + if ('0' <= digit && digit <= '9') + digit -= '0'; + else if ('a' <= digit && digit <= 'z') + digit -= 'a' - 10; + else + digit -= 'A' - 10; + bitShiftInChar = radix; + } + bitShiftInChar >>= 1; + boolean bit = (digit & bitShiftInChar) != 0; + + switch (state) { + case SKIP_LEADING_ZEROS: + if (bit) { + --exactBitsLimit; + sum = 1.0; + state = FIRST_EXACT_53_BITS; + } + break; + case FIRST_EXACT_53_BITS: + sum *= 2.0; + if (bit) + sum += 1.0; + --exactBitsLimit; + if (exactBitsLimit == 0) { + bit53 = bit; + state = AFTER_BIT_53; + } + break; + case AFTER_BIT_53: + bit54 = bit; + factor = 2.0; + state = ZEROS_AFTER_54; + break; + case ZEROS_AFTER_54: + if (bit) { + state = MIXED_AFTER_54; + } + // fallthrough + case MIXED_AFTER_54: + factor *= 2; + break; + } + } + switch (state) { + case SKIP_LEADING_ZEROS: + sum = 0.0; + break; + case FIRST_EXACT_53_BITS: + case AFTER_BIT_53: + // do nothing + break; + case ZEROS_AFTER_54: + // x1.1 -> x1 + 1 (round up) + // x0.1 -> x0 (round down) + if (bit54 & bit53) + sum += 1.0; + sum *= factor; + break; + case MIXED_AFTER_54: + // x.100...1.. -> x + 1 (round up) + // x.0anything -> x (round down) + if (bit54) + sum += 1.0; + sum *= factor; + break; + } + } + /* We don't worry about inaccurate numbers for any other base. */ + } + return sum; + } + + public static String escapeString(String s) { + return escapeString(s, '"'); + } + + /** + * For escaping strings printed by object and array literals; not quite + * the same as 'escape.' + */ + public static String escapeString(String s, char escapeQuote) { + if (!(escapeQuote == '"' || escapeQuote == '\'')) { + throw new IllegalStateException("unexpected quote char:" + escapeQuote); + } + StringBuffer sb = null; + + for(int i = 0, L = s.length(); i != L; ++i) { + int c = s.charAt(i); + + if (' ' <= c && c <= '~' && c != escapeQuote && c != '\\') { + // an ordinary print character (like C isprint()) and not " + // or \ . + if (sb != null) { + sb.append((char)c); + } + continue; + } + if (sb == null) { + sb = new StringBuffer(L + 3); + sb.append(s); + sb.setLength(i); + } + + int escape = -1; + switch (c) { + case '\b': escape = 'b'; break; + case '\f': escape = 'f'; break; + case '\n': escape = 'n'; break; + case '\r': escape = 'r'; break; + case '\t': escape = 't'; break; + case 0xb: escape = 'v'; break; // Java lacks \v. + case ' ': escape = ' '; break; + case '\\': escape = '\\'; break; + } + if (escape >= 0) { + // an \escaped sort of character + sb.append('\\'); + sb.append((char)escape); + } else if (c == escapeQuote) { + sb.append('\\'); + sb.append(escapeQuote); + } else { + int hexSize; + if (c < 256) { + // 2-digit hex + sb.append("\\x"); + hexSize = 2; + } else { + // Unicode. + sb.append("\\u"); + hexSize = 4; + } + // append hexadecimal form of c left-padded with 0 + for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) { + int digit = 0xf & (c >> shift); + int hc = (digit < 10) ? '0' + digit : 'a' - 10 + digit; + sb.append((char)hc); + } + } + } + return (sb == null) ? s : sb.toString(); + } + + static boolean isValidIdentifierName(String s) { + int L = s.length(); + if (L == 0) + return false; + if (!Character.isJavaIdentifierStart(s.charAt(0))) + return false; + for (int i = 1; i != L; ++i) { + if (!Character.isJavaIdentifierPart(s.charAt(i))) + return false; + } + return !TokenStream.isKeyword(s); + } + + /** + * If str is a decimal presentation of Uint32 value, return it as long. + * Otherwise, return -1L; + */ + public static long testUint32String(String str) { + // The length of the decimal string representation of + // UINT32_MAX_VALUE, 4294967296 + final int MAX_VALUE_LENGTH = 10; + + int len = str.length(); + if (1 <= len && len <= MAX_VALUE_LENGTH) { + int c = str.charAt(0); + c -= '0'; + if (c == 0) { + // Note that 00,01 etc. are not valid Uint32 presentations + return (len == 1) ? 0L : -1L; + } + if (1 <= c && c <= 9) { + long v = c; + for (int i = 1; i != len; ++i) { + c = str.charAt(i) - '0'; + if (!(0 <= c && c <= 9)) { + return -1; + } + v = 10 * v + c; + } + // Check for overflow + if ((v >>> 32) == 0) { + return v; + } + } + } + return -1; + } + + static boolean isSpecialProperty(String s) { + return s.equals("__proto__") || s.equals("__parent__"); + } + + // ------------------ + // Statements + // ------------------ + + public static String getMessage0(String messageId) { + return getMessage(messageId, null); + } + + public static String getMessage1(String messageId, Object arg1) { + Object[] arguments = {arg1}; + return getMessage(messageId, arguments); + } + + /* OPT there's a noticeable delay for the first error! Maybe it'd + * make sense to use a ListResourceBundle instead of a properties + * file to avoid (synchronized) text parsing. + */ + public static String getMessage(String messageId, Object[] arguments) { + final String defaultResource + = "rhino_ast.java.com.google.javascript.rhino.Messages"; + + Locale locale = Locale.getDefault(); + + // ResourceBundle does caching. + ResourceBundle rb = ResourceBundle.getBundle(defaultResource, locale); + + String formatString; + try { + formatString = rb.getString(messageId); + } catch (java.util.MissingResourceException mre) { + throw new RuntimeException + ("no message resource found for message property "+ messageId); + } + + /* + * It's OK to format the string, even if 'arguments' is null; + * we need to format it anyway, to make double ''s collapse to + * single 's. + */ + // TODO: MessageFormat is not available on pJava + MessageFormat formatter = new MessageFormat(formatString); + return formatter.format(arguments); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/SimpleErrorReporter.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/SimpleErrorReporter.java new file mode 100644 index 0000000..f35e0f1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/SimpleErrorReporter.java @@ -0,0 +1,106 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino; + +import java.util.ArrayList; +import java.util.List; + +/** + * A simple {@link ErrorReporter} that collects warnings and errors and makes + * them accessible via {@link #errors()} and {@link #warnings()}. + * + */ +public class SimpleErrorReporter implements ErrorReporter { + private List warnings = null; + private List errors = null; + + @Override + public void warning(String message, String sourceName, int line, + int lineOffset) { + if (warnings == null) { + warnings = new ArrayList(); + } + warnings.add(formatDetailedMessage( + message, sourceName, line, lineOffset)); + } + + @Override + public void error(String message, String sourceName, int line, + int lineOffset) { + if (errors == null) { + errors = new ArrayList(); + } + errors.add(formatDetailedMessage( + message, sourceName, line, lineOffset)); + } + + /** + * Returns the list of errors, or {@code null} if there were none. + */ + public List errors() { + return errors; + } + + /** + * Returns the list of warnings, or {@code null} if there were none. + */ + public List warnings() { + return warnings; + } + + private String formatDetailedMessage( + String message, String sourceName, int lineNumber, int lineOffset) { + String details = message; + if (sourceName == null || lineNumber <= 0) { + return details; + } + StringBuilder buf = new StringBuilder(details); + buf.append(" ("); + if (sourceName != null) { + buf.append(sourceName); + } + if (lineNumber > 0) { + buf.append('#'); + buf.append(lineNumber); + } + buf.append(')'); + return buf.toString(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/SourcePosition.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/SourcePosition.java new file mode 100644 index 0000000..f4ff93d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/SourcePosition.java @@ -0,0 +1,141 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino; + +/** + * Represents a position in some piece of source code, with an associated + * item of type T found at that position. + * + */ +public abstract class SourcePosition { + /** + * The (well typed) item found at the source position. + */ + private T item = null; + + /** + * The starting line number. + */ + private int startLineno = 0; + + /** + * The character position on the starting line. + */ + private int startCharno = 0; + + /** + * The ending line number. + */ + private int endLineno = 0; + + /** + * The character position on the ending line. + */ + private int endCharno = 0; + + /** + * Sets the item that this source position references. + */ + public void setItem(T item) { + this.item = item; + } + + /** + * Sets the position information contained in this source position. + */ + public void setPositionInformation(int startLineno, int startCharno, + int endLineno, int endCharno) { + if (startLineno == endLineno) { + if (startCharno >= endCharno) { + throw new IllegalStateException( + "Recorded bad position information\n" + + "start-char: " + startCharno + "\n" + + "end-char: " + endCharno); + } + } else { + if (startLineno > endLineno) { + throw new IllegalStateException( + "Recorded bad position information\n" + + "start-line: " + startLineno + "\n" + + "end-line: " + endLineno); + } + } + + this.startLineno = startLineno; + this.startCharno = startCharno; + this.endLineno = endLineno; + this.endCharno = endCharno; + } + + /** + * Returns the item found at this source position. + */ + public T getItem() { + return item; + } + + /** + * Returns the starting line number of this position. + */ + public int getStartLine() { + return startLineno; + } + + /** + * Returns the character position on the starting line. + */ + public int getPositionOnStartLine() { + return startCharno; + } + + /** + * Returns the ending line number of this position. + */ + public int getEndLine() { + return endLineno; + } + + /** + * Returns the character position on the ending line. + */ + public int getPositionOnEndLine() { + return endCharno; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/Token.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/Token.java new file mode 100644 index 0000000..a4e6f2c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/Token.java @@ -0,0 +1,289 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Roger Lawrence + * Mike McCabe + * Igor Bukanov + * Milen Nankov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino; + +/** + * This class implements the JavaScript scanner. + * + * It is based on the C source files jsscan.c and jsscan.h + * in the jsref package. + * + */ + +public class Token { + + /** + * Token types. These values correspond to JSTokenType values in + * jsscan.c. + */ + public final static int + ERROR = -1, + + RETURN = 4, + BITOR = 9, + BITXOR = 10, + BITAND = 11, + EQ = 12, + NE = 13, + LT = 14, + LE = 15, + GT = 16, + GE = 17, + LSH = 18, + RSH = 19, + URSH = 20, + ADD = 21, + SUB = 22, + MUL = 23, + DIV = 24, + MOD = 25, + NOT = 26, + BITNOT = 27, + POS = 28, + NEG = 29, + NEW = 30, + DELPROP = 31, + TYPEOF = 32, + GETPROP = 33, + GETELEM = 35, + CALL = 37, + NAME = 38, + NUMBER = 39, + STRING = 40, + NULL = 41, + THIS = 42, + FALSE = 43, + TRUE = 44, + SHEQ = 45, // shallow equality (===) + SHNE = 46, // shallow inequality (!==) + REGEXP = 47, + THROW = 49, + IN = 51, + INSTANCEOF = 52, + ARRAYLIT = 63, // array literal + OBJECTLIT = 64, // object literal + + TRY = 77, + PARAM_LIST = 83, + COMMA = 85, // comma operator + + ASSIGN = 86, // simple assignment (=) + ASSIGN_BITOR = 87, // |= + ASSIGN_BITXOR = 88, // ^= + ASSIGN_BITAND = 89, // &= + ASSIGN_LSH = 90, // <<= + ASSIGN_RSH = 91, // >>= + ASSIGN_URSH = 92, // >>>= + ASSIGN_ADD = 93, // += + ASSIGN_SUB = 94, // -= + ASSIGN_MUL = 95, // *= + ASSIGN_DIV = 96, // /= + ASSIGN_MOD = 97, // %= + + HOOK = 98, // conditional (?:) + OR = 100, // logical or (||) + AND = 101, // logical and (&&) + INC = 102, // increment (++) + DEC = 103, // decrement (--) + FUNCTION = 105, // function keyword + IF = 108, // if keyword + SWITCH = 110, // switch keyword + CASE = 111, // case keyword + DEFAULT_CASE = 112, // default keyword + WHILE = 113, // while keyword + DO = 114, // do keyword + FOR = 115, // for keyword + BREAK = 116, // break keyword + CONTINUE = 117, // continue keyword + VAR = 118, // var keyword + WITH = 119, // with keyword + CATCH = 120, // catch keyword + VOID = 122, // void keyword + + EMPTY = 124, + + BLOCK = 125, // statement block + LABEL = 126, // label + EXPR_RESULT = 130, // expression statement in scripts + SCRIPT = 132, // top-level node for entire script + + GETTER_DEF = 147, + SETTER_DEF = 148, + + CONST = 149, // JS 1.5 const keyword + DEBUGGER = 152, + + // JSCompiler introduced tokens + LABEL_NAME = 153, + STRING_KEY = 154, // object literal key + CAST = 155, + + // JSDoc-only tokens + ANNOTATION = 300, + PIPE = 301, + STAR = 302, + EOC = 303, + QMARK = 304, + ELLIPSIS = 305, + BANG = 306, + EQUALS = 307, + LB = 308, // left brackets + LC = 309, // left curly braces + COLON = 310; + + // Transitional definitions + // TODO(johnlenz): remove these + public final static int + DEFAULT = DEFAULT_CASE, + GET = GETTER_DEF, + LP = PARAM_LIST, + SET = SETTER_DEF; + + public static String name(int token) { + switch (token) { + case ERROR: return "ERROR"; + case RETURN: return "RETURN"; + case BITOR: return "BITOR"; + case BITXOR: return "BITXOR"; + case BITAND: return "BITAND"; + case EQ: return "EQ"; + case NE: return "NE"; + case LT: return "LT"; + case LE: return "LE"; + case GT: return "GT"; + case GE: return "GE"; + case LSH: return "LSH"; + case RSH: return "RSH"; + case URSH: return "URSH"; + case ADD: return "ADD"; + case SUB: return "SUB"; + case MUL: return "MUL"; + case DIV: return "DIV"; + case MOD: return "MOD"; + case NOT: return "NOT"; + case BITNOT: return "BITNOT"; + case POS: return "POS"; + case NEG: return "NEG"; + case NEW: return "NEW"; + case DELPROP: return "DELPROP"; + case TYPEOF: return "TYPEOF"; + case GETPROP: return "GETPROP"; + case GETELEM: return "GETELEM"; + case CALL: return "CALL"; + case NAME: return "NAME"; + case LABEL_NAME: return "LABEL_NAME"; + case NUMBER: return "NUMBER"; + case STRING: return "STRING"; + case STRING_KEY: return "STRING_KEY"; + case NULL: return "NULL"; + case THIS: return "THIS"; + case FALSE: return "FALSE"; + case TRUE: return "TRUE"; + case SHEQ: return "SHEQ"; + case SHNE: return "SHNE"; + case REGEXP: return "REGEXP"; + case THROW: return "THROW"; + case IN: return "IN"; + case INSTANCEOF: return "INSTANCEOF"; + case ARRAYLIT: return "ARRAYLIT"; + case OBJECTLIT: return "OBJECTLIT"; + case TRY: return "TRY"; + case PARAM_LIST: return "PARAM_LIST"; + case COMMA: return "COMMA"; + case ASSIGN: return "ASSIGN"; + case ASSIGN_BITOR: return "ASSIGN_BITOR"; + case ASSIGN_BITXOR: return "ASSIGN_BITXOR"; + case ASSIGN_BITAND: return "ASSIGN_BITAND"; + case ASSIGN_LSH: return "ASSIGN_LSH"; + case ASSIGN_RSH: return "ASSIGN_RSH"; + case ASSIGN_URSH: return "ASSIGN_URSH"; + case ASSIGN_ADD: return "ASSIGN_ADD"; + case ASSIGN_SUB: return "ASSIGN_SUB"; + case ASSIGN_MUL: return "ASSIGN_MUL"; + case ASSIGN_DIV: return "ASSIGN_DIV"; + case ASSIGN_MOD: return "ASSIGN_MOD"; + case HOOK: return "HOOK"; + case OR: return "OR"; + case AND: return "AND"; + case INC: return "INC"; + case DEC: return "DEC"; + case FUNCTION: return "FUNCTION"; + case IF: return "IF"; + case SWITCH: return "SWITCH"; + case CASE: return "CASE"; + case DEFAULT_CASE: return "DEFAULT_CASE"; + case WHILE: return "WHILE"; + case DO: return "DO"; + case FOR: return "FOR"; + case BREAK: return "BREAK"; + case CONTINUE: return "CONTINUE"; + case VAR: return "VAR"; + case WITH: return "WITH"; + case CATCH: return "CATCH"; + case EMPTY: return "EMPTY"; + case BLOCK: return "BLOCK"; + case LABEL: return "LABEL"; + case EXPR_RESULT: return "EXPR_RESULT"; + case SCRIPT: return "SCRIPT"; + case GETTER_DEF: return "GETTER_DEF"; + case SETTER_DEF: return "SETTER_DEF"; + case CONST: return "CONST"; + case DEBUGGER: return "DEBUGGER"; + case CAST: return "CAST"; + case ANNOTATION: return "ANNOTATION"; + case PIPE: return "PIPE"; + case STAR: return "STAR"; + case EOC: return "EOC"; + case QMARK: return "QMARK"; + case ELLIPSIS: return "ELLIPSIS"; + case BANG: return "BANG"; + case VOID: return "VOID"; + case EQUALS: return "EQUALS"; + case LB: return "LB"; + case LC: return "LC"; + case COLON: return "COLON"; + } + + // Token without name + throw new IllegalStateException(String.valueOf(token)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/TokenStream.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/TokenStream.java new file mode 100644 index 0000000..73fed17 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/TokenStream.java @@ -0,0 +1,204 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Roger Lawrence + * Mike McCabe + * Igor Bukanov + * Ethan Hugg + * Bob Jervis + * Terry Lucas + * Milen Nankov + * Pascal-Louis Perez + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino; + +/** + * This class implements the JavaScript scanner. + * + * It is based on the C source files jsscan.c and jsscan.h + * in the jsref package. + * + */ + +public class TokenStream { + public static boolean isKeyword(String name) { + boolean id = false; + String s = name; + complete: { + String X = null; + int c; + partial: switch (s.length()) { + case 2: c=s.charAt(1); + if (c=='f') { + if (s.charAt(0)=='i') {id=true; break complete;} + } else if (c=='n') { + if (s.charAt(0)=='i') {id=true; break complete;} + } else if (c=='o') { + if (s.charAt(0)=='d') {id=true; break complete;} + } + break partial; + case 3: switch (s.charAt(0)) { + case 'f': + if (s.charAt(2)=='r' && s.charAt(1)=='o') { + id=true; break complete; + } break partial; + case 'i': + if (s.charAt(2)=='t' && s.charAt(1)=='n') { + id=true; break complete; + } break partial; + case 'n': + if (s.charAt(2)=='w' && s.charAt(1)=='e') { + id=true; break complete; + } break partial; + case 't': + if (s.charAt(2)=='y' && s.charAt(1)=='r') { + id=true; break complete; + } break partial; + case 'v': + if (s.charAt(2)=='r' && s.charAt(1)=='a') { + id=true; break complete; + } break partial; + } break partial; + case 4: switch (s.charAt(0)) { + case 'b': X="byte";id=true; break partial; + case 'c': c=s.charAt(3); + if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='a') { + id=true; break complete;} } + else if (c=='r') { + if (s.charAt(2)=='a' && s.charAt(1)=='h') { + id=true; break complete; + } + } + break partial; + case 'e': c=s.charAt(3); + if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='l') { + id=true; break complete;} } + else if (c=='m') { + if (s.charAt(2)=='u' && s.charAt(1)=='n') { + id=true; break complete;} } + break partial; + case 'g': X="goto";id=true; break partial; + case 'l': X="long";id=true; break partial; + case 'n': X="null";id=true; break partial; + case 't': c=s.charAt(3); + if (c=='e') { if (s.charAt(2)=='u' && s.charAt(1)=='r') { + id=true; break complete;} } + else if (c=='s') { + if (s.charAt(2)=='i' && s.charAt(1)=='h') { + id=true; break complete;} } + break partial; + case 'v': X="void";id=true; break partial; + case 'w': X="with";id=true; break partial; + } break partial; + case 5: switch (s.charAt(2)) { + case 'a': X="class";id=true; break partial; + case 'e': X="break";id=true; break partial; + case 'i': X="while";id=true; break partial; + case 'l': X="false";id=true; break partial; + case 'n': c=s.charAt(0); + if (c=='c') { X="const";id=true; } + else if (c=='f') { X="final";id=true; } + break partial; + case 'o': c=s.charAt(0); + if (c=='f') { X="float";id=true; } + else if (c=='s') { X="short";id=true; } + break partial; + case 'p': X="super";id=true; break partial; + case 'r': X="throw";id=true; break partial; + case 't': X="catch";id=true; break partial; + } break partial; + case 6: switch (s.charAt(1)) { + case 'a': X="native";id=true; break partial; + case 'e': c=s.charAt(0); + if (c=='d') { X="delete";id=true; } + else if (c=='r') { X="return";id=true; } + break partial; + case 'h': X="throws";id=true; break partial; + case 'm': X="import";id=true; break partial; + case 'o': X="double";id=true; break partial; + case 't': X="static";id=true; break partial; + case 'u': X="public";id=true; break partial; + case 'w': X="switch";id=true; break partial; + case 'x': X="export";id=true; break partial; + case 'y': X="typeof";id=true; break partial; + } break partial; + case 7: switch (s.charAt(1)) { + case 'a': X="package";id=true; break partial; + case 'e': X="default";id=true; break partial; + case 'i': X="finally";id=true; break partial; + case 'o': X="boolean";id=true; break partial; + case 'r': X="private";id=true; break partial; + case 'x': X="extends";id=true; break partial; + } break partial; + case 8: switch (s.charAt(0)) { + case 'a': X="abstract";id=true; break partial; + case 'c': X="continue";id=true; break partial; + case 'd': X="debugger";id=true; break partial; + case 'f': X="function";id=true; break partial; + case 'v': X="volatile";id=true; break partial; + } break partial; + case 9: c=s.charAt(0); + if (c=='i') { X="interface";id=true; } + else if (c=='p') { X="protected";id=true; } + else if (c=='t') { X="transient";id=true; } + break partial; + case 10: c=s.charAt(1); + if (c=='m') { X="implements";id=true; } + else if (c=='n') { X="instanceof";id=true; } + break partial; + case 12: X="synchronized";id=true; break partial; + } + // partial match validate the entire string the one possibility + if (X!=null && X!=s && !X.equals(s)) return false; + } + return id; + } + + public static boolean isJSIdentifier(String s) { + int length = s.length(); + + if (length == 0 || !Character.isJavaIdentifierStart(s.charAt(0))) + return false; + + for (int i = 1; i < length; i++) { + if (!Character.isJavaIdentifierPart(s.charAt(i))) { + return false; + } + } + + return true; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/AllType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/AllType.java new file mode 100644 index 0000000..a4372d7 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/AllType.java @@ -0,0 +1,116 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; + +import com.google.javascript.rhino.ErrorReporter; + +/** + * All type, representing all values. + */ +public final class AllType extends JSType { + private static final long serialVersionUID = 1L; + + AllType(JSTypeRegistry registry) { + super(registry); + } + + @Override + public boolean isAllType() { + return true; + } + + @Override + public boolean matchesStringContext() { + // Be lenient. + return true; + } + + @Override + public boolean matchesObjectContext() { + // Be lenient. + return true; + } + + @Override + public boolean canBeCalled() { + return false; + } + + @Override + public TernaryValue testForEquality(JSType that) { + return UNKNOWN; + } + + @Override + String toStringHelper(boolean forAnnotations) { + return "*"; + } + + @Override + public String getDisplayName() { + return ""; + } + + @Override + public boolean hasDisplayName() { + return true; + } + + @Override + public T visit(Visitor visitor) { + return visitor.caseAllType(); + } + + @Override T visit(RelationshipVisitor visitor, JSType that) { + return visitor.caseAllType(that); + } + + @Override + public BooleanLiteralSet getPossibleToBooleanOutcomes() { + return BooleanLiteralSet.BOTH; + } + + @Override + JSType resolveInternal(ErrorReporter t, StaticScope scope) { + return this; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ArrowType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ArrowType.java new file mode 100644 index 0000000..8b4825d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ArrowType.java @@ -0,0 +1,321 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; + +import com.google.javascript.rhino.ErrorReporter; +import com.google.javascript.rhino.Node; + +/** + * The arrow type is an internal type that models the functional arrow type + * seen in typical functional programming languages. It is used solely for + * separating the management of the arrow type from the complex + * {@link FunctionType} that models JavaScript's notion of functions. + */ +final class ArrowType extends JSType { + private static final long serialVersionUID = 1L; + + final Node parameters; + JSType returnType; + + // Whether the return type is inferred. + final boolean returnTypeInferred; + + ArrowType(JSTypeRegistry registry, Node parameters, + JSType returnType) { + this(registry, parameters, returnType, false); + } + + ArrowType(JSTypeRegistry registry, Node parameters, + JSType returnType, boolean returnTypeInferred) { + super(registry); + + this.parameters = parameters == null ? + registry.createParametersWithVarArgs(getNativeType(UNKNOWN_TYPE)) : + parameters; + this.returnType = returnType == null ? + getNativeType(UNKNOWN_TYPE) : returnType; + this.returnTypeInferred = returnTypeInferred; + } + + @Override + public boolean isSubtype(JSType other) { + if (!(other instanceof ArrowType)) { + return false; + } + + ArrowType that = (ArrowType) other; + + // This is described in Draft 2 of the ES4 spec, + // Section 3.4.7: Subtyping Function Types. + + // this.returnType <: that.returnType (covariant) + if (!this.returnType.isSubtype(that.returnType)) { + return false; + } + + // that.paramType[i] <: this.paramType[i] (contravariant) + // + // If this.paramType[i] is required, + // then that.paramType[i] is required. + // + // In theory, the "required-ness" should work in the other direction as + // well. In other words, if we have + // + // function f(number, number) {} + // function g(number) {} + // + // Then f *should* not be a subtype of g, and g *should* not be + // a subtype of f. But in practice, we do not implement it this way. + // We want to support the use case where you can pass g where f is + // expected, and pretend that g ignores the second argument. + // That way, you can have a single "no-op" function, and you don't have + // to create a new no-op function for every possible type signature. + // + // So, in this case, g < f, but f !< g + Node thisParam = parameters.getFirstChild(); + Node thatParam = that.parameters.getFirstChild(); + while (thisParam != null && thatParam != null) { + JSType thisParamType = thisParam.getJSType(); + JSType thatParamType = thatParam.getJSType(); + if (thisParamType != null) { + if (thatParamType == null || + !thatParamType.isSubtype(thisParamType)) { + return false; + } + } + + boolean thisIsVarArgs = thisParam.isVarArgs(); + boolean thatIsVarArgs = thatParam.isVarArgs(); + boolean thisIsOptional = thisIsVarArgs || thisParam.isOptionalArg(); + boolean thatIsOptional = thatIsVarArgs || thatParam.isOptionalArg(); + + // "that" can't be a supertype, because it's missing a required argument. + if (!thisIsOptional && thatIsOptional) { + // NOTE(nicksantos): In our type system, we use {function(...?)} and + // {function(...NoType)} to to indicate that arity should not be + // checked. Strictly speaking, this is not a correct formulation, + // because now a sub-function can required arguments that are var_args + // in the super-function. So we special-case this. + boolean isTopFunction = + thatIsVarArgs && + (thatParamType == null || + thatParamType.isUnknownType() || + thatParamType.isNoType()); + if (!isTopFunction) { + return false; + } + } + + // don't advance if we have variable arguments + if (!thisIsVarArgs) { + thisParam = thisParam.getNext(); + } + if (!thatIsVarArgs) { + thatParam = thatParam.getNext(); + } + + // both var_args indicates the end + if (thisIsVarArgs && thatIsVarArgs) { + thisParam = null; + thatParam = null; + } + } + + // "that" can't be a supertype, because it's missing a required argument. + if (thisParam != null + && !thisParam.isOptionalArg() && !thisParam.isVarArgs() + && thatParam == null) { + return false; + } + + return true; + } + + /** + * @return True if our parameter spec is equal to {@code that}'s parameter + * spec. + */ + boolean hasEqualParameters(ArrowType that, EquivalenceMethod eqMethod) { + Node thisParam = parameters.getFirstChild(); + Node otherParam = that.parameters.getFirstChild(); + while (thisParam != null && otherParam != null) { + JSType thisParamType = thisParam.getJSType(); + JSType otherParamType = otherParam.getJSType(); + if (thisParamType != null) { + // Both parameter lists give a type for this param, it should be equal + if (otherParamType != null && + !thisParamType.checkEquivalenceHelper( + otherParamType, eqMethod)) { + return false; + } + } else { + if (otherParamType != null) { + return false; + } + } + + // Check var_args/optionality + if (thisParam.isOptionalArg() != otherParam.isOptionalArg()) { + return false; + } + + if (thisParam.isVarArgs() != otherParam.isVarArgs()) { + return false; + } + + thisParam = thisParam.getNext(); + otherParam = otherParam.getNext(); + } + // One of the parameters is null, so the types are only equal if both + // parameter lists are null (they are equal). + return thisParam == otherParam; + } + + boolean checkArrowEquivalenceHelper( + ArrowType that, EquivalenceMethod eqMethod) { + // Please keep this method in sync with the hashCode() method below. + if (!returnType.checkEquivalenceHelper(that.returnType, eqMethod)) { + return false; + } + return hasEqualParameters(that, eqMethod); + } + + @Override + public int hashCode() { + int hashCode = 0; + if (returnType != null) { + hashCode += returnType.hashCode(); + } + if (returnTypeInferred) { + hashCode += 1; + } + if (parameters != null) { + Node param = parameters.getFirstChild(); + while (param != null) { + JSType paramType = param.getJSType(); + if (paramType != null) { + hashCode += paramType.hashCode(); + } + param = param.getNext(); + } + } + return hashCode; + } + + @Override + public JSType getLeastSupertype(JSType that) { + throw new UnsupportedOperationException(); + } + + @Override + public JSType getGreatestSubtype(JSType that) { + throw new UnsupportedOperationException(); + } + + @Override + public TernaryValue testForEquality(JSType that) { + throw new UnsupportedOperationException(); + } + + @Override + public T visit(Visitor visitor) { + throw new UnsupportedOperationException(); + } + + @Override T visit(RelationshipVisitor visitor, JSType that) { + throw new UnsupportedOperationException(); + } + + @Override + public BooleanLiteralSet getPossibleToBooleanOutcomes() { + return BooleanLiteralSet.TRUE; + } + + @Override + JSType resolveInternal(ErrorReporter t, StaticScope scope) { + returnType = safeResolve(returnType, t, scope); + if (parameters != null) { + for (Node paramNode = parameters.getFirstChild(); + paramNode != null; paramNode = paramNode.getNext()) { + paramNode.setJSType(paramNode.getJSType().resolve(t, scope)); + } + } + return this; + } + + boolean hasUnknownParamsOrReturn() { + if (parameters != null) { + for (Node paramNode = parameters.getFirstChild(); + paramNode != null; paramNode = paramNode.getNext()) { + JSType type = paramNode.getJSType(); + if (type == null || type.isUnknownType()) { + return true; + } + } + } + return returnType == null || returnType.isUnknownType(); + } + + @Override + String toStringHelper(boolean forAnnotations) { + return "[ArrowType]"; + } + + @Override + public boolean hasAnyTemplateTypesInternal() { + return returnType.hasAnyTemplateTypes() + || hasTemplatedParameterType(); + } + + private boolean hasTemplatedParameterType() { + if (parameters != null) { + for (Node paramNode = parameters.getFirstChild(); + paramNode != null; paramNode = paramNode.getNext()) { + JSType type = paramNode.getJSType(); + if (type != null && type.hasAnyTemplateTypes()) { + return true; + } + } + } + return false; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/BooleanLiteralSet.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/BooleanLiteralSet.java new file mode 100644 index 0000000..53d2263 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/BooleanLiteralSet.java @@ -0,0 +1,97 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +/** + * A set in the domain {true,false}. + * There are four possible sets: {}, {true}, {false}, {true,false}. + * + */ +public enum BooleanLiteralSet { + EMPTY, + TRUE, + FALSE, + BOTH; + + private BooleanLiteralSet fromOrdinal(int ordinal) { + switch (ordinal) { + case 0: return EMPTY; + case 1: return TRUE; + case 2: return FALSE; + case 3: return BOTH; + default: throw new IllegalArgumentException("Ordinal: " + ordinal); + } + } + + /** + * Computes the intersection of this set and {@code that}. + */ + public BooleanLiteralSet intersection(BooleanLiteralSet that) { + return fromOrdinal(this.ordinal() & that.ordinal()); + } + + /** + * Computes the union of this set and {@code that}. + */ + public BooleanLiteralSet union(BooleanLiteralSet that) { + return fromOrdinal(this.ordinal() | that.ordinal()); + } + + /** + * Returns whether {@code this} contains the given literal value. + */ + public boolean contains(boolean literalValue) { + switch (this.ordinal()) { + case 0: return false; + case 1: return literalValue; + case 2: return !literalValue; + case 3: return true; + default: throw new IndexOutOfBoundsException("Ordinal: " + + this.ordinal()); + } + } + + /** + * Returns the singleton set {literalValue}. + */ + public static BooleanLiteralSet get(boolean literalValue) { + return literalValue ? TRUE : FALSE; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/BooleanType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/BooleanType.java new file mode 100644 index 0000000..06c02db --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/BooleanType.java @@ -0,0 +1,120 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; +import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; + + +/** + * Boolean type. + */ +public class BooleanType extends ValueType { + private static final long serialVersionUID = 1L; + + BooleanType(JSTypeRegistry registry) { + super(registry); + } + + @Override + public boolean isNullable() { + return false; + } + + @Override + public TernaryValue testForEquality(JSType that) { + TernaryValue result = super.testForEquality(that); + if (result != null) { + return result; + } + if (that.isUnknownType() || that.isSubtype( + getNativeType(JSTypeNative.NUMBER_STRING_BOOLEAN)) || + that.isObject()) { + return UNKNOWN; + } + return FALSE; + } + + @Override + public boolean isBooleanValueType() { + return true; + } + + @Override + public boolean matchesNumberContext() { + return true; + } + + @Override + public boolean matchesStringContext() { + return true; + } + + @Override + public boolean matchesObjectContext() { + // TODO(user): Revisit this for ES4, which is stricter. + return true; + } + + @Override + public JSType autoboxesTo() { + return getNativeType(JSTypeNative.BOOLEAN_OBJECT_TYPE); + } + + @Override + String toStringHelper(boolean forAnnotations) { + return getDisplayName(); + } + + @Override + public String getDisplayName() { + return "boolean"; + } + + @Override + public BooleanLiteralSet getPossibleToBooleanOutcomes() { + return BooleanLiteralSet.BOTH; + } + + @Override + public T visit(Visitor visitor) { + return visitor.caseBooleanType(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/CanCastToVisitor.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/CanCastToVisitor.java new file mode 100644 index 0000000..c024f50 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/CanCastToVisitor.java @@ -0,0 +1,182 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Lenz + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +/** + * A "can cast to" relationship visitor. + */ +class CanCastToVisitor implements RelationshipVisitor { + + @Override + public Boolean caseUnknownType(JSType thisType, JSType thatType) { + return true; + } + + @Override + public Boolean caseNoType(JSType thatType) { + return true; + } + + @Override + public Boolean caseNoObjectType(JSType thatType) { + return true; // TODO(johnlenz): restrict to objects + } + + @Override + public Boolean caseAllType(JSType thatType) { + return true; + } + + boolean canCastToUnion(JSType thisType, UnionType unionType) { + for (JSType type : unionType.getAlternates()) { + if (thisType.visit(this, type)) { + return true; + } + } + return false; + } + + boolean canCastToFunction(JSType thisType, FunctionType functionType) { + if (thisType.isFunctionType()) { + // TODO(johnlenz): visit function parts + return true; + } else { + return thisType.isSubtype(functionType) + || functionType.isSubtype(thisType); + } + } + + private boolean isInterface(JSType type) { + ObjectType objType = type.toObjectType(); + if (objType != null) { + JSType constructor = objType.getConstructor(); + return constructor != null && constructor.isInterface(); + } + return false; + } + + Boolean castCastToHelper(JSType thisType, JSType thatType) { + if (thatType.isUnknownType() + || thatType.isAllType() + || thatType.isNoObjectType() // TODO(johnlenz): restrict to objects + || thatType.isNoType()) { + return true; + } else if (thisType.isRecordType() || thatType.isRecordType()) { + return true; // TODO(johnlenz): are there any misuses we can catch? + } else if (isInterface(thisType) || isInterface(thatType)) { + return true; // TODO(johnlenz): are there any misuses we can catch? + } else if (thatType.isEnumElementType()) { + return thisType.visit(this, + thatType.toMaybeEnumElementType().getPrimitiveType()); + } else if (thatType.isUnionType()) { + return canCastToUnion(thisType, thatType.toMaybeUnionType()); + } else if (thatType.isFunctionType()) { + return canCastToFunction(thisType, thatType.toMaybeFunctionType()); + } else if (thatType.isParameterizedType()) { + // TODO(johnlenz): once the templated type work is finished, + // restrict the type parameters. + return thisType.visit(this, + thatType.toMaybeParameterizedType().getReferencedTypeInternal()); + } + + return thisType.isSubtype(thatType) || thatType.isSubtype(thisType); + } + + @Override + public Boolean caseValueType(ValueType thisType, JSType thatType) { + return castCastToHelper(thisType, thatType); + } + + @Override + public Boolean caseObjectType(ObjectType thisType, JSType thatType) { + return castCastToHelper(thisType, thatType); + } + + @Override + public Boolean caseFunctionType(FunctionType thisType, JSType thatType) { + return castCastToHelper(thisType, thatType); + } + + @Override + public Boolean caseUnionType(UnionType thisType, JSType thatType) { + boolean visited = false; + for (JSType type : thisType.getAlternates()) { + if (type.isVoidType() || type.isNullType()) { + // Don't allow if the only match between the types is null or void, + // otherwise any nullable type would be castable to any other nullable + // type and we don't want that. + } else { + visited = true; + if (type.visit(this, thatType)) { + return true; + } + } + } + + // Special case the "null|undefined" union and allow it to be cast + // to any cast to any type containing allowing either null|undefined. + if (!visited) { + JSType NULL_TYPE = thisType.getNativeType(JSTypeNative.NULL_TYPE); + JSType VOID_TYPE = thisType.getNativeType(JSTypeNative.VOID_TYPE); + return NULL_TYPE.visit(this, thatType) || VOID_TYPE.visit(this, thatType); + } + + return false; + } + + @Override + public Boolean caseParameterizedType( + ParameterizedType thisType, JSType thatType) { + // TODO(johnlenz): once the templated type work is finished, + // restrict the type parameters. + return thisType.getReferencedTypeInternal().visit(this, thatType); + } + + @Override + public Boolean caseTemplateType(TemplateType thisType, JSType thatType) { + return true; + } + + @Override + public Boolean caseEnumElementType( + EnumElementType typeType, JSType thatType) { + return typeType.getPrimitiveType().visit(this, thatType); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/EnumElementType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/EnumElementType.java new file mode 100644 index 0000000..77840b0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/EnumElementType.java @@ -0,0 +1,246 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + + + +import com.google.javascript.rhino.ErrorReporter; +import com.google.javascript.rhino.Node; + +/** + * The type of individual elements of an enum type + * (see {@link EnumType}). + */ +public class EnumElementType extends ObjectType { + private static final long serialVersionUID = 1L; + + /** + * The primitive type this enum element type wraps. For instance, in + * the following code defining the {@code LOCAL_CODES} enum + *
      var LOCAL_CODES = {A: 3, B: 9, C: 8}
      + * the primitive type of the the constants is {@code number}. + */ + private JSType primitiveType; + + // The primitive type, if it is an object. + private ObjectType primitiveObjectType; + + private final String name; + + EnumElementType(JSTypeRegistry registry, JSType elementType, + String name) { + super(registry); + this.primitiveType = elementType; + this.primitiveObjectType = elementType.toObjectType(); + this.name = name; + } + + @Override public PropertyMap getPropertyMap() { + return primitiveObjectType == null + ? PropertyMap.immutableEmptyMap() + : primitiveObjectType.getPropertyMap(); + } + + @Override + public EnumElementType toMaybeEnumElementType() { + return this; + } + + @Override + public boolean matchesNumberContext() { + return primitiveType.matchesNumberContext(); + } + + @Override + public boolean matchesStringContext() { + return primitiveType.matchesStringContext(); + } + + @Override + public boolean matchesObjectContext() { + return primitiveType.matchesObjectContext(); + } + + @Override + public boolean canBeCalled() { + return primitiveType.canBeCalled(); + } + + @Override + public boolean isObject() { + return primitiveType.isObject(); + } + + @Override + public TernaryValue testForEquality(JSType that) { + return primitiveType.testForEquality(that); + } + + /** + * This predicate determines whether objects of this type can have the null + * value, and therefore can appear in contexts where null is expected. + * + * @return true for everything but Number and Boolean types. + */ + @Override + public boolean isNullable() { + return primitiveType.isNullable(); + } + + @Override + public boolean isNominalType() { + return hasReferenceName(); + } + + /** + * If this is equal to a NamedType object, its hashCode must be equal + * to the hashCode of the NamedType object. + */ + @Override + public int hashCode() { + if (hasReferenceName()) { + return getReferenceName().hashCode(); + } else { + return super.hashCode(); + } + } + + @Override + String toStringHelper(boolean forAnnotations) { + return forAnnotations ? + primitiveType.toString() : + (getReferenceName() + ".<" + primitiveType + ">"); + } + + @Override + public String getReferenceName() { + return name; + } + + @Override + public boolean hasReferenceName() { + return true; + } + + @Override + public boolean isSubtype(JSType that) { + if (JSType.isSubtypeHelper(this, that)) { + return true; + } else { + return primitiveType.isSubtype(that); + } + } + + @Override + public T visit(Visitor visitor) { + return visitor.caseEnumElementType(this); + } + + @Override T visit(RelationshipVisitor visitor, JSType that) { + return visitor.caseEnumElementType(this, that); + } + + @Override + boolean defineProperty(String propertyName, JSType type, + boolean inferred, Node propertyNode) { + // nothing + return true; + } + + @Override + public ObjectType getImplicitPrototype() { + return null; + } + + @Override + public JSType findPropertyType(String propertyName) { + return primitiveType.findPropertyType(propertyName); + } + + @Override + public FunctionType getConstructor() { + return primitiveObjectType == null ? + null : primitiveObjectType.getConstructor(); + } + + @Override + public JSType autoboxesTo() { + return primitiveType.autoboxesTo(); + } + + /** + * Gets the primitive type of this enum element. + */ + public JSType getPrimitiveType() { + return primitiveType; + } + + /** + * Returns the infimum of a enum element type and another type, or null + * if the infimum is empty. + * + * This can be a little bit weird. For example, suppose you have an enum + * of {(string|number)}, and you want the greatest subtype of the enum + * and a {number}. + * + * The infimum is non-empty. But at the same time, we don't really have + * a name for this infimum. It's equivalent to "elements of this enum that + * are numbers". + * + * The best we can do is make up a new type. This is similar to what + * we do in UnionType#meet, which kind-of-sort-of makes sense, because + * an EnumElementType is a union of instances of a type. + */ + JSType meet(JSType that) { + JSType meetPrimitive = primitiveType.getGreatestSubtype(that); + if (meetPrimitive.isEmptyType()) { + return null; + } else { + return new EnumElementType(registry, meetPrimitive, name); + } + } + + @Override + JSType resolveInternal(ErrorReporter t, StaticScope scope) { + primitiveType = primitiveType.resolve(t, scope); + primitiveObjectType = ObjectType.cast(primitiveType); + return this; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/EnumType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/EnumType.java new file mode 100644 index 0000000..4f411a5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/EnumType.java @@ -0,0 +1,187 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; +import static com.google.javascript.rhino.jstype.TernaryValue.TRUE; + +import com.google.javascript.rhino.ErrorReporter; +import com.google.javascript.rhino.Node; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * An enum type representing a branded collection of elements. Each element + * is referenced by its name, and has an {@link EnumElementType} type. + */ +public class EnumType extends PrototypeObjectType { + private static final long serialVersionUID = 1L; + + /** + * The object literal or alias which this type represents. + * It may be {@code null}. + */ + private final Node source; + + // the type of the individual elements + private EnumElementType elementsType; + // the elements' names (they all have the same type) + private final Set elements = new HashSet(); + + /** + * Creates an enum type. + * + * @param name the enum's name + * @param elementsType the base type of the individual elements + */ + EnumType(JSTypeRegistry registry, String name, Node source, + JSType elementsType) { + super(registry, "enum{" + name + "}", null); + this.source = source; + this.elementsType = new EnumElementType(registry, elementsType, name); + } + + /** + * Gets the source node or null if this is an unknown enum. + */ + public Node getSource() { + return source; + } + + @Override + public EnumType toMaybeEnumType() { + return this; + } + + @Override + public ObjectType getImplicitPrototype() { + return registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE); + } + + /** + * Gets the elements defined on this enum. + * @return the elements' names defined on this enum. The returned set is + * immutable. + */ + public Set getElements() { + return Collections.unmodifiableSet(elements); + } + + /** + * Defines a new element on this enum. + * @param name the name of the new element + * @param definingNode the {@code Node} that defines this new element + * @return true iff the new element is added successfully + */ + public boolean defineElement(String name, Node definingNode) { + elements.add(name); + return defineDeclaredProperty(name, elementsType, definingNode); + } + + /** + * Gets the elements' type. + */ + public EnumElementType getElementsType() { + return elementsType; + } + + @Override + public TernaryValue testForEquality(JSType that) { + TernaryValue result = super.testForEquality(that); + if (result != null) { + return result; + } + return this.isEquivalentTo(that) ? TRUE : FALSE; + } + + @Override + public boolean isSubtype(JSType that) { + return that.isEquivalentTo(getNativeType(JSTypeNative.OBJECT_TYPE)) || + that.isEquivalentTo(getNativeType(JSTypeNative.OBJECT_PROTOTYPE)) || + JSType.isSubtypeHelper(this, that); + } + + @Override + String toStringHelper(boolean forAnnotations) { + return forAnnotations ? "Object" : getReferenceName(); + } + + @Override + public String getDisplayName() { + return elementsType.getDisplayName(); + } + + @Override + public T visit(Visitor visitor) { + return visitor.caseObjectType(this); + } + + @Override T visit(RelationshipVisitor visitor, JSType that) { + return visitor.caseObjectType(this, that); + } + + @Override + public FunctionType getConstructor() { + return null; + } + + @Override + public boolean matchesNumberContext() { + return false; + } + + @Override + public boolean matchesStringContext() { + return true; + } + + @Override + public boolean matchesObjectContext() { + return true; + } + + @Override + JSType resolveInternal(ErrorReporter t, StaticScope scope) { + elementsType = (EnumElementType) elementsType.resolve(t, scope); + return super.resolveInternal(t, scope); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/EquivalenceMethod.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/EquivalenceMethod.java new file mode 100644 index 0000000..b6c3744 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/EquivalenceMethod.java @@ -0,0 +1,83 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +/** + * Represents different ways for comparing equality among types. + * @author nicksantos@google.com (Nick Santos) + */ +enum EquivalenceMethod { + /** + * Indicates that the two types should behave exactly the same under + * all type operations. + * + * Thus, {string} != {?} and {Unresolved} != {?} + */ + IDENTITY, + + /** + * Indicates that the two types are almost exactly the same, and that a + * data flow analysis algorithm comparing them should consider them equal. + * + * In traditional type inference, the types form a finite lattice, and this + * ensures that type inference will terminate. + * + * In our type system, the unknown types do not obey the lattice rules. So + * if we continue to perform inference over the unknown types, we may + * never terminate. + * + * By treating all unknown types as equivalent for the purposes of data + * flow analysis, we ensure that the algorithm will terminate. + * + * Thus, {string} != {?} and {Unresolved} ~= {?} + */ + DATA_FLOW, + + /** + * Indicates that two types are invariant. + * + * In a type system without unknown types, this would be the same + * as IDENTITY. But we always want to consider type A invariant with type B + * if B is unknown. + * + * Thus, {string} ~= {?} and {Unresolved} ~= {?} + */ + INVARIANT +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ErrorFunctionType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ErrorFunctionType.java new file mode 100644 index 0000000..458167c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ErrorFunctionType.java @@ -0,0 +1,75 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.JSTypeNative.ALL_TYPE; + +/** + * This type is for built-in error constructors. + */ +class ErrorFunctionType extends FunctionType { + private static final long serialVersionUID = 1L; + + ErrorFunctionType(JSTypeRegistry registry, String name) { + super( + registry, name, null, + registry.createArrowType( + registry.createOptionalParameters( + registry.getNativeType(ALL_TYPE), + registry.getNativeType(ALL_TYPE), + registry.getNativeType(ALL_TYPE)), + null), + null, null, true, true); + + // NOTE(nicksantos): Errors have the weird behavior in that they can + // be called as functions, and they will return instances of themselves. + // Error('x') instanceof Error => true + // + // In user-defined types, we would deal with this case by creating + // a NamedType with the name "Error" and then resolve it later. + // + // For native types, we don't really want the native types to + // depend on type-resolution. So we just set the return type manually + // at the end of construction. + // + // There's similar logic in JSTypeRegistry for Array and RegExp. + getInternalArrowType().returnType = getInstanceType(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/FunctionBuilder.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/FunctionBuilder.java new file mode 100644 index 0000000..1c3ef9c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/FunctionBuilder.java @@ -0,0 +1,165 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.common.collect.ImmutableList; +import com.google.javascript.rhino.Node; + +/** + * A builder class for function and arrow types. + * + * If you need to build an interface constructor, + * use {@link JSTypeRegistry#createInterfaceType}. + * + * @author nicksantos@google.com (Nick Santos) + */ +public final class FunctionBuilder { + private final JSTypeRegistry registry; + private String name = null; + private Node sourceNode = null; + private Node parametersNode = null; + private JSType returnType = null; + private JSType typeOfThis = null; + private ImmutableList templateKeys = ImmutableList.of(); + private boolean inferredReturnType = false; + private boolean isConstructor = false; + private boolean isNativeType = false; + + public FunctionBuilder(JSTypeRegistry registry) { + this.registry = registry; + } + + /** Set the name of the function type. */ + public FunctionBuilder withName(String name) { + this.name = name; + return this; + } + + /** Set the source node of the function type. */ + public FunctionBuilder withSourceNode(Node sourceNode) { + this.sourceNode = sourceNode; + return this; + } + + /** Set the parameters of the function type from a FunctionParamBuilder. */ + public FunctionBuilder withParams(FunctionParamBuilder params) { + this.parametersNode = params.build(); + return this; + } + + /** + * Set the parameters of the function type with a specially-formatted node. + */ + public FunctionBuilder withParamsNode(Node parametersNode) { + this.parametersNode = parametersNode; + return this; + } + + /** Set the return type. */ + public FunctionBuilder withReturnType(JSType returnType) { + this.returnType = returnType; + return this; + } + + /** Set the return type and whether it's inferred. */ + public FunctionBuilder withReturnType(JSType returnType, boolean inferred) { + this.returnType = returnType; + this.inferredReturnType = inferred; + return this; + } + + /** Sets an inferred return type. */ + public FunctionBuilder withInferredReturnType(JSType returnType) { + this.returnType = returnType; + this.inferredReturnType = true; + return this; + } + + /** Set the "this" type. */ + public FunctionBuilder withTypeOfThis(JSType typeOfThis) { + this.typeOfThis = typeOfThis; + return this; + } + + /** Set the template name. */ + public FunctionBuilder withTemplateKeys( + ImmutableList templateKeys) { + this.templateKeys = templateKeys; + return this; + } + + /** Make this a constructor. */ + public FunctionBuilder forConstructor() { + this.isConstructor = true; + return this; + } + + /** Set whether this is a constructor. */ + public FunctionBuilder setIsConstructor(boolean isConstructor) { + this.isConstructor = isConstructor; + return this; + } + + /** Make this a native type. */ + FunctionBuilder forNativeType() { + this.isNativeType = true; + return this; + } + + /** Copies all the information from another function type. */ + public FunctionBuilder copyFromOtherFunction(FunctionType otherType) { + this.name = otherType.getReferenceName(); + this.sourceNode = otherType.getSource(); + this.parametersNode = otherType.getParametersNode(); + this.returnType = otherType.getReturnType(); + this.typeOfThis = otherType.getTypeOfThis(); + this.templateKeys = otherType.getTemplateKeys(); + this.isConstructor = otherType.isConstructor(); + this.isNativeType = otherType.isNativeObjectType(); + return this; + } + + /** Construct a new function type. */ + public FunctionType build() { + return new FunctionType(registry, name, sourceNode, + new ArrowType(registry, parametersNode, returnType, inferredReturnType), + typeOfThis, templateKeys, isConstructor, isNativeType); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/FunctionParamBuilder.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/FunctionParamBuilder.java new file mode 100644 index 0000000..99fa366 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/FunctionParamBuilder.java @@ -0,0 +1,155 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * A builder for the Rhino Node representing Function parameters. + * @author nicksantos@google.com (Nick Santos) + */ +public class FunctionParamBuilder { + + private final JSTypeRegistry registry; + private final Node root = new Node(Token.PARAM_LIST); + + public FunctionParamBuilder(JSTypeRegistry registry) { + this.registry = registry; + } + + /** + * Add parameters of the given type to the end of the param list. + * @return False if this is called after optional params are added. + */ + public boolean addRequiredParams(JSType ...types) { + if (hasOptionalOrVarArgs()) { + return false; + } + + for (JSType type : types) { + newParameter(type); + } + return true; + } + + /** + * Add optional parameters of the given type to the end of the param list. + * @param types Types for each optional parameter. The builder will make them + * undefine-able. + * @return False if this is called after var args are added. + */ + public boolean addOptionalParams(JSType ...types) { + if (hasVarArgs()) { + return false; + } + + for (JSType type : types) { + newParameter(registry.createOptionalType(type)).setOptionalArg(true); + } + return true; + } + + /** + * Add variable arguments to the end of the parameter list. + * @return False if this is called after var args are added. + */ + public boolean addVarArgs(JSType type) { + if (hasVarArgs()) { + return false; + } + + // There are two types of variable argument functions: + // 1) Programmer-defined var args + // 2) Native bottom types that can accept any argument. + // For the first one, "undefined" is a valid value for all arguments. + // For the second, we do not want to cast it up to undefined. + if (!type.isEmptyType()) { + type = registry.createOptionalType(type); + } + newParameter(type).setVarArgs(true); + return true; + } + + /** + * Copies the parameter specification from the given node. + */ + public Node newParameterFromNode(Node n) { + Node newParam = newParameter(n.getJSType()); + newParam.setVarArgs(n.isVarArgs()); + newParam.setOptionalArg(n.isOptionalArg()); + return newParam; + } + + /** + * Copies the parameter specification from the given node, + * but makes sure it's optional. + */ + public Node newOptionalParameterFromNode(Node n) { + Node newParam = newParameterFromNode(n); + if (!newParam.isVarArgs() && !newParam.isOptionalArg()) { + newParam.setOptionalArg(true); + } + return newParam; + } + + // Add a parameter to the list with the given type. + private Node newParameter(JSType type) { + Node paramNode = Node.newString(Token.NAME, ""); + paramNode.setJSType(type); + root.addChildToBack(paramNode); + return paramNode; + } + + public Node build() { + return root; + } + + private boolean hasOptionalOrVarArgs() { + Node lastChild = root.getLastChild(); + return lastChild != null && + (lastChild.isOptionalArg() || lastChild.isVarArgs()); + } + + public boolean hasVarArgs() { + Node lastChild = root.getLastChild(); + return lastChild != null && lastChild.isVarArgs(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/FunctionType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/FunctionType.java new file mode 100644 index 0000000..7388448 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/FunctionType.java @@ -0,0 +1,1266 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.U2U_CONSTRUCTOR_TYPE; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.rhino.ErrorReporter; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * This derived type provides extended information about a function, including + * its return type and argument types.

      + * + * Note: the parameters list is the LP node that is the parent of the + * actual NAME node containing the parsed argument list (annotated with + * JSDOC_TYPE_PROP's for the compile-time type of each argument. + */ +public class FunctionType extends PrototypeObjectType { + private static final long serialVersionUID = 1L; + + private enum Kind { + ORDINARY, + CONSTRUCTOR, + INTERFACE + } + + // relevant only for constructors + private enum PropAccess { ANY, STRUCT, DICT } + + /** + * {@code [[Call]]} property. + */ + private ArrowType call; + + /** + * The {@code prototype} property. This field is lazily initialized by + * {@code #getPrototype()}. The most important reason for lazily + * initializing this field is that there are cycles in the native types + * graph, so some prototypes must temporarily be {@code null} during + * the construction of the graph. + * + * If non-null, the type must be a PrototypeObjectType. + */ + private Property prototypeSlot; + + /** + * Whether a function is a constructor, an interface, or just an ordinary + * function. + */ + private final Kind kind; + + /** + * Whether the instances are structs, dicts, or unrestricted. + */ + private PropAccess propAccess; + + /** + * The type of {@code this} in the scope of this function. + */ + private JSType typeOfThis; + + /** + * The function node which this type represents. It may be {@code null}. + */ + private Node source; + + /** + * The interfaces directly implemented by this function (for constructors) + * It is only relevant for constructors. May not be {@code null}. + */ + private List implementedInterfaces = ImmutableList.of(); + + /** + * The interfaces directly extended by this function (for interfaces) + * It is only relevant for constructors. May not be {@code null}. + */ + private List extendedInterfaces = ImmutableList.of(); + + /** + * The types which are subtypes of this function. It is only relevant for + * constructors and may be {@code null}. + */ + private List subTypes; + + /** Creates an instance for a function that might be a constructor. */ + FunctionType(JSTypeRegistry registry, String name, Node source, + ArrowType arrowType, JSType typeOfThis, + ImmutableList templateKeys, + boolean isConstructor, boolean nativeType) { + super(registry, name, + registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE), + nativeType, templateKeys, null); + setPrettyPrint(true); + + Preconditions.checkArgument(source == null || + Token.FUNCTION == source.getType()); + Preconditions.checkNotNull(arrowType); + this.source = source; + if (isConstructor) { + this.kind = Kind.CONSTRUCTOR; + this.propAccess = PropAccess.ANY; + this.typeOfThis = typeOfThis != null ? + typeOfThis : new InstanceObjectType(registry, this, nativeType, null); + } else { + this.kind = Kind.ORDINARY; + this.typeOfThis = typeOfThis != null ? + typeOfThis : + registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); + } + this.call = arrowType; + } + + /** Creates an instance for a function that is an interface. */ + private FunctionType(JSTypeRegistry registry, String name, Node source) { + super(registry, name, + registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE)); + setPrettyPrint(true); + + Preconditions.checkArgument(source == null || + Token.FUNCTION == source.getType()); + Preconditions.checkArgument(name != null); + this.source = source; + this.call = new ArrowType(registry, new Node(Token.PARAM_LIST), null); + this.kind = Kind.INTERFACE; + this.typeOfThis = new InstanceObjectType(registry, this); + } + + /** Creates an instance for a function that is an interface. */ + static FunctionType forInterface( + JSTypeRegistry registry, String name, Node source) { + return new FunctionType(registry, name, source); + } + + @Override + public boolean isInstanceType() { + // The universal constructor is its own instance, bizarrely. It overrides + // getConstructor() appropriately when it's declared. + return this == registry.getNativeType(U2U_CONSTRUCTOR_TYPE); + } + + @Override + public boolean isConstructor() { + return kind == Kind.CONSTRUCTOR; + } + + @Override + public boolean isInterface() { + return kind == Kind.INTERFACE; + } + + @Override + public boolean isOrdinaryFunction() { + return kind == Kind.ORDINARY; + } + + /** + * When a class B inherits from A and A is annotated as a struct, then B + * automatically gets the annotation, even if B's constructor is not + * explicitly annotated. + */ + public boolean makesStructs() { + if (!isConstructor()) { + return false; + } + if (propAccess == PropAccess.STRUCT) { + return true; + } + FunctionType superc = getSuperClassConstructor(); + if (superc != null && superc.makesStructs()) { + setStruct(); + return true; + } + return false; + } + + /** + * When a class B inherits from A and A is annotated as a dict, then B + * automatically gets the annotation, even if B's constructor is not + * explicitly annotated. + */ + public boolean makesDicts() { + if (!isConstructor()) { + return false; + } + if (propAccess == PropAccess.DICT) { + return true; + } + FunctionType superc = getSuperClassConstructor(); + if (superc != null && superc.makesDicts()) { + setDict(); + return true; + } + return false; + } + + public void setStruct() { + propAccess = PropAccess.STRUCT; + } + + public void setDict() { + propAccess = PropAccess.DICT; + } + + @Override + public FunctionType toMaybeFunctionType() { + return this; + } + + @Override + public boolean canBeCalled() { + return true; + } + + public boolean hasImplementedInterfaces() { + if (!implementedInterfaces.isEmpty()){ + return true; + } + FunctionType superCtor = isConstructor() ? + getSuperClassConstructor() : null; + if (superCtor != null) { + return superCtor.hasImplementedInterfaces(); + } + return false; + } + + public Iterable getParameters() { + Node n = getParametersNode(); + if (n != null) { + return n.children(); + } else { + return Collections.emptySet(); + } + } + + /** Gets an LP node that contains all params. May be null. */ + public Node getParametersNode() { + return call.parameters; + } + + /** Gets the minimum number of arguments that this function requires. */ + public int getMinArguments() { + // NOTE(nicksantos): There are some native functions that have optional + // parameters before required parameters. This algorithm finds the position + // of the last required parameter. + int i = 0; + int min = 0; + for (Node n : getParameters()) { + i++; + if (!n.isOptionalArg() && !n.isVarArgs()) { + min = i; + } + } + return min; + } + + /** + * Gets the maximum number of arguments that this function requires, + * or Integer.MAX_VALUE if this is a variable argument function. + */ + public int getMaxArguments() { + Node params = getParametersNode(); + if (params != null) { + Node lastParam = params.getLastChild(); + if (lastParam == null || !lastParam.isVarArgs()) { + return params.getChildCount(); + } + } + + return Integer.MAX_VALUE; + } + + public JSType getReturnType() { + return call.returnType; + } + + public boolean isReturnTypeInferred() { + return call.returnTypeInferred; + } + + /** Gets the internal arrow type. For use by subclasses only. */ + ArrowType getInternalArrowType() { + return call; + } + + @Override + public Property getSlot(String name) { + if ("prototype".equals(name)) { + // Lazy initialization of the prototype field. + getPrototype(); + return prototypeSlot; + } else { + return super.getSlot(name); + } + } + + /** + * Includes the prototype iff someone has created it. We do not want + * to expose the prototype for ordinary functions. + */ + @Override + public Set getOwnPropertyNames() { + if (prototypeSlot == null) { + return super.getOwnPropertyNames(); + } else { + Set names = Sets.newHashSet("prototype"); + names.addAll(super.getOwnPropertyNames()); + return names; + } + } + + /** + * Gets the {@code prototype} property of this function type. This is + * equivalent to {@code (ObjectType) getPropertyType("prototype")}. + */ + public ObjectType getPrototype() { + // lazy initialization of the prototype field + if (prototypeSlot == null) { + String refName = getReferenceName(); + if (refName == null) { + // Someone is trying to access the prototype of a structural function. + // We don't want to give real properties to this prototype, because + // then it would propagate to all structural functions. + setPrototypeNoCheck( + registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE), + null); + } else { + setPrototype( + new PrototypeObjectType( + registry, + getReferenceName() + ".prototype", + registry.getNativeObjectType(OBJECT_TYPE), + isNativeObjectType(), null, null), + null); + } + } + return (ObjectType) prototypeSlot.getType(); + } + + /** + * Sets the prototype, creating the prototype object from the given + * base type. + * @param baseType The base type. + */ + public void setPrototypeBasedOn(ObjectType baseType) { + setPrototypeBasedOn(baseType, null); + } + + void setPrototypeBasedOn(ObjectType baseType, Node propertyNode) { + // This is a bit weird. We need to successfully handle these + // two cases: + // Foo.prototype = new Bar(); + // and + // Foo.prototype = {baz: 3}; + // In the first case, we do not want new properties to get + // added to Bar. In the second case, we do want new properties + // to get added to the type of the anonymous object. + // + // We handle this by breaking it into two cases: + // + // In the first case, we create a new PrototypeObjectType and set + // its implicit prototype to the type being assigned. This ensures + // that Bar will not get any properties of Foo.prototype, but properties + // later assigned to Bar will get inherited properly. + // + // In the second case, we just use the anonymous object as the prototype. + if (baseType.hasReferenceName() || + isNativeObjectType() || + baseType.isFunctionPrototypeType()) { + baseType = new PrototypeObjectType( + registry, getReferenceName() + ".prototype", baseType); + } + setPrototype(baseType, propertyNode); + } + + /** + * Sets the prototype. + * @param prototype the prototype. If this value is {@code null} it will + * silently be discarded. + */ + boolean setPrototype(ObjectType prototype, Node propertyNode) { + if (prototype == null) { + return false; + } + // getInstanceType fails if the function is not a constructor + if (isConstructor() && prototype == getInstanceType()) { + return false; + } + return setPrototypeNoCheck(prototype, propertyNode); + } + + /** Set the prototype without doing any sanity checks. */ + private boolean setPrototypeNoCheck(ObjectType prototype, Node propertyNode) { + ObjectType oldPrototype = prototypeSlot == null + ? null : (ObjectType) prototypeSlot.getType(); + boolean replacedPrototype = oldPrototype != null; + + this.prototypeSlot = new Property("prototype", prototype, true, + propertyNode == null ? source : propertyNode); + prototype.setOwnerFunction(this); + + if (oldPrototype != null) { + // Disassociating the old prototype makes this easier to debug-- + // we don't have to worry about two prototypes running around. + oldPrototype.setOwnerFunction(null); + } + + if (isConstructor() || isInterface()) { + FunctionType superClass = getSuperClassConstructor(); + if (superClass != null) { + superClass.addSubType(this); + } + + if (isInterface()) { + for (ObjectType interfaceType : getExtendedInterfaces()) { + if (interfaceType.getConstructor() != null) { + interfaceType.getConstructor().addSubType(this); + } + } + } + } + + if (replacedPrototype) { + clearCachedValues(); + } + + return true; + } + + /** + * Returns all interfaces implemented by a class or its superclass and any + * superclasses for any of those interfaces. If this is called before all + * types are resolved, it may return an incomplete set. + */ + public Iterable getAllImplementedInterfaces() { + // Store them in a linked hash set, so that the compile job is + // deterministic. + Set interfaces = Sets.newLinkedHashSet(); + + for (ObjectType type : getImplementedInterfaces()) { + addRelatedInterfaces(type, interfaces); + } + return interfaces; + } + + private void addRelatedInterfaces(ObjectType instance, Set set) { + FunctionType constructor = instance.getConstructor(); + if (constructor != null) { + if (!constructor.isInterface()) { + return; + } + + set.add(instance); + + for (ObjectType interfaceType : instance.getCtorExtendedInterfaces()) { + addRelatedInterfaces(interfaceType, set); + } + } + } + + /** Returns interfaces implemented directly by a class or its superclass. */ + public Iterable getImplementedInterfaces() { + FunctionType superCtor = isConstructor() ? + getSuperClassConstructor() : null; + if (superCtor == null) { + return implementedInterfaces; + } else { + return Iterables.concat( + implementedInterfaces, superCtor.getImplementedInterfaces()); + } + } + + /** Returns interfaces directly implemented by the class. */ + public Iterable getOwnImplementedInterfaces() { + return implementedInterfaces; + } + + public void setImplementedInterfaces(List implementedInterfaces) { + if (isConstructor()) { + // Records this type for each implemented interface. + for (ObjectType type : implementedInterfaces) { + registry.registerTypeImplementingInterface(this, type); + } + this.implementedInterfaces = ImmutableList.copyOf(implementedInterfaces); + } else { + throw new UnsupportedOperationException(); + } + } + + /** + * Returns all extended interfaces declared by an interfaces or its super- + * interfaces. If this is called before all types are resolved, it may return + * an incomplete set. + */ + public Iterable getAllExtendedInterfaces() { + // Store them in a linked hash set, so that the compile job is + // deterministic. + Set extendedInterfaces = Sets.newLinkedHashSet(); + + for (ObjectType interfaceType : getExtendedInterfaces()) { + addRelatedExtendedInterfaces(interfaceType, extendedInterfaces); + } + return extendedInterfaces; + } + + private void addRelatedExtendedInterfaces(ObjectType instance, + Set set) { + FunctionType constructor = instance.getConstructor(); + if (constructor != null) { + set.add(instance); + + for (ObjectType interfaceType : constructor.getExtendedInterfaces()) { + addRelatedExtendedInterfaces(interfaceType, set); + } + } + } + + /** Returns interfaces directly extended by an interface */ + public Iterable getExtendedInterfaces() { + return extendedInterfaces; + } + + /** Returns the number of interfaces directly extended by an interface */ + public int getExtendedInterfacesCount() { + return extendedInterfaces.size(); + } + + public void setExtendedInterfaces(List extendedInterfaces) + throws UnsupportedOperationException { + if (isInterface()) { + this.extendedInterfaces = ImmutableList.copyOf(extendedInterfaces); + } else { + throw new UnsupportedOperationException(); + } + } + + @Override + public JSType getPropertyType(String name) { + if (!hasOwnProperty(name)) { + // Define the "call", "apply", and "bind" functions lazily. + boolean isCall = "call".equals(name); + boolean isBind = "bind".equals(name); + if (isCall || isBind) { + defineDeclaredProperty(name, getCallOrBindSignature(isCall), source); + } else if ("apply".equals(name)) { + // Define the "apply" function lazily. + FunctionParamBuilder builder = new FunctionParamBuilder(registry); + + // ECMA-262 says that apply's second argument must be an Array + // or an arguments object. We don't model the arguments object, + // so let's just be forgiving for now. + // TODO(nicksantos): Model the Arguments object. + builder.addOptionalParams( + registry.createNullableType(getTypeOfThis()), + registry.createNullableType( + registry.getNativeType(JSTypeNative.OBJECT_TYPE))); + + defineDeclaredProperty(name, + new FunctionBuilder(registry) + .withParams(builder) + .withReturnType(getReturnType()) + .withTemplateKeys(getTemplateKeys()) + .build(), + source); + } + } + + return super.getPropertyType(name); + } + + /** + * Get the return value of calling "bind" on this function + * with the specified number of arguments. + * + * If -1 is passed, then we will return a result that accepts + * any parameters. + */ + public FunctionType getBindReturnType(int argsToBind) { + FunctionBuilder builder = new FunctionBuilder(registry) + .withReturnType(getReturnType()) + .withTemplateKeys(getTemplateKeys()); + if (argsToBind >= 0) { + Node origParams = getParametersNode(); + if (origParams != null) { + Node params = origParams.cloneTree(); + for (int i = 1; i < argsToBind && params.getFirstChild() != null; i++) { + if (params.getFirstChild().isVarArgs()) { + break; + } + params.removeFirstChild(); + } + builder.withParamsNode(params); + } + } + return builder.build(); + } + + /** + * Notice that "call" and "bind" have the same argument signature, + * except that all the arguments of "bind" (except the first) + * are optional. + */ + private FunctionType getCallOrBindSignature(boolean isCall) { + boolean isBind = !isCall; + FunctionBuilder builder = new FunctionBuilder(registry) + .withReturnType(isCall ? getReturnType() : getBindReturnType(-1)) + .withTemplateKeys(getTemplateKeys()); + + Node origParams = getParametersNode(); + if (origParams != null) { + Node params = origParams.cloneTree(); + + Node thisTypeNode = Node.newString(Token.NAME, "thisType"); + thisTypeNode.setJSType( + registry.createOptionalNullableType(getTypeOfThis())); + params.addChildToFront(thisTypeNode); + + if (isBind) { + // The arguments of bind() are unique in that they are all + // optional but not undefinable. + for (Node current = thisTypeNode.getNext(); + current != null; current = current.getNext()) { + current.setOptionalArg(true); + } + } else if (isCall) { + // The first argument of call() is optional iff all the arguments + // are optional. It's sufficient to check the first argument. + Node firstArg = thisTypeNode.getNext(); + if (firstArg == null + || firstArg.isOptionalArg() + || firstArg.isVarArgs()) { + thisTypeNode.setOptionalArg(true); + } + } + + builder.withParamsNode(params); + } + + return builder.build(); + } + + @Override + boolean defineProperty(String name, JSType type, + boolean inferred, Node propertyNode) { + if ("prototype".equals(name)) { + ObjectType objType = type.toObjectType(); + if (objType != null) { + if (prototypeSlot != null && + objType.isEquivalentTo(prototypeSlot.getType())) { + return true; + } + setPrototypeBasedOn(objType, propertyNode); + return true; + } else { + return false; + } + } + return super.defineProperty(name, type, inferred, propertyNode); + } + + /** + * Computes the supremum or infimum of two functions. + * Because sup() and inf() share a lot of logic for functions, we use + * a single helper. + * @param leastSuper If true, compute the supremum of {@code this} with + * {@code that}. Otherwise, compute the infimum. + * @return The least supertype or greatest subtype. + */ + FunctionType supAndInfHelper(FunctionType that, boolean leastSuper) { + // NOTE(nicksantos): When we remove the unknown type, the function types + // form a lattice with the universal constructor at the top of the lattice, + // and the LEAST_FUNCTION_TYPE type at the bottom of the lattice. + // + // When we introduce the unknown type, it's much more difficult to make + // heads or tails of the partial ordering of types, because there's no + // clear hierarchy between the different components (parameter types and + // return types) in the ArrowType. + // + // Rather than make the situation more complicated by introducing new + // types (like unions of functions), we just fallback on the simpler + // approach of getting things right at the top and the bottom of the + // lattice. + // + // If there are unknown parameters or return types making things + // ambiguous, then sup(A, B) is always the top function type, and + // inf(A, B) is always the bottom function type. + Preconditions.checkNotNull(that); + + if (isEquivalentTo(that)) { + return this; + } + + // If these are ordinary functions, then merge them. + // Don't do this if any of the params/return + // values are unknown, because then there will be cycles in + // their local lattice and they will merge in weird ways. + if (isOrdinaryFunction() && that.isOrdinaryFunction() && + !this.call.hasUnknownParamsOrReturn() && + !that.call.hasUnknownParamsOrReturn()) { + + // Check for the degenerate case, but double check + // that there's not a cycle. + boolean isSubtypeOfThat = isSubtype(that); + boolean isSubtypeOfThis = that.isSubtype(this); + if (isSubtypeOfThat && !isSubtypeOfThis) { + return leastSuper ? that : this; + } else if (isSubtypeOfThis && !isSubtypeOfThat) { + return leastSuper ? this : that; + } + + // Merge the two functions component-wise. + FunctionType merged = tryMergeFunctionPiecewise(that, leastSuper); + if (merged != null) { + return merged; + } + } + + // The function instance type is a special case + // that lives above the rest of the lattice. + JSType functionInstance = registry.getNativeType( + JSTypeNative.FUNCTION_INSTANCE_TYPE); + if (functionInstance.isEquivalentTo(that)) { + return leastSuper ? that : this; + } else if (functionInstance.isEquivalentTo(this)) { + return leastSuper ? this : that; + } + + // In theory, we should be using the GREATEST_FUNCTION_TYPE as the + // greatest function. In practice, we don't because it's way too + // broad. The greatest function takes var_args None parameters, which + // means that all parameters register a type warning. + // + // Instead, we use the U2U ctor type, which has unknown type args. + FunctionType greatestFn = + registry.getNativeFunctionType(JSTypeNative.U2U_CONSTRUCTOR_TYPE); + FunctionType leastFn = + registry.getNativeFunctionType(JSTypeNative.LEAST_FUNCTION_TYPE); + return leastSuper ? greatestFn : leastFn; + } + + /** + * Try to get the sup/inf of two functions by looking at the + * piecewise components. + */ + private FunctionType tryMergeFunctionPiecewise( + FunctionType other, boolean leastSuper) { + Node newParamsNode = null; + if (call.hasEqualParameters(other.call, EquivalenceMethod.IDENTITY)) { + newParamsNode = call.parameters; + } else { + // If the parameters are not equal, don't try to merge them. + // Someday, we should try to merge the individual params. + return null; + } + + JSType newReturnType = leastSuper ? + call.returnType.getLeastSupertype(other.call.returnType) : + call.returnType.getGreatestSubtype(other.call.returnType); + + JSType newTypeOfThis = null; + if (isEquivalent(typeOfThis, other.typeOfThis)) { + newTypeOfThis = typeOfThis; + } else { + JSType maybeNewTypeOfThis = leastSuper ? + typeOfThis.getLeastSupertype(other.typeOfThis) : + typeOfThis.getGreatestSubtype(other.typeOfThis); + newTypeOfThis = maybeNewTypeOfThis; + } + + boolean newReturnTypeInferred = + call.returnTypeInferred || other.call.returnTypeInferred; + + return new FunctionType( + registry, null, null, + new ArrowType( + registry, newParamsNode, newReturnType, newReturnTypeInferred), + newTypeOfThis, null, false, false); + } + + /** + * Given a constructor or an interface type, get its superclass constructor + * or {@code null} if none exists. + */ + public FunctionType getSuperClassConstructor() { + Preconditions.checkArgument(isConstructor() || isInterface()); + ObjectType maybeSuperInstanceType = getPrototype().getImplicitPrototype(); + if (maybeSuperInstanceType == null) { + return null; + } + return maybeSuperInstanceType.getConstructor(); + } + + /** + * Given an interface and a property, finds the top-most super interface + * that has the property defined (including this interface). + */ + public static ObjectType getTopDefiningInterface(ObjectType type, + String propertyName) { + ObjectType foundType = null; + if (type.hasProperty(propertyName)) { + foundType = type; + } + for (ObjectType interfaceType : type.getCtorExtendedInterfaces()) { + if (interfaceType.hasProperty(propertyName)) { + foundType = getTopDefiningInterface(interfaceType, propertyName); + } + } + return foundType; + } + + /** + * Given a constructor or an interface type and a property, finds the + * top-most superclass that has the property defined (including this + * constructor). + */ + public ObjectType getTopMostDefiningType(String propertyName) { + Preconditions.checkState(isConstructor() || isInterface()); + Preconditions.checkArgument(getInstanceType().hasProperty(propertyName)); + FunctionType ctor = this; + + if (isInterface()) { + return getTopDefiningInterface(getInstanceType(), propertyName); + } + + ObjectType topInstanceType = null; + do { + topInstanceType = ctor.getInstanceType(); + ctor = ctor.getSuperClassConstructor(); + } while (ctor != null + && ctor.getPrototype().hasProperty(propertyName)); + + return topInstanceType; + } + + /** + * Two function types are equal if their signatures match. Since they don't + * have signatures, two interfaces are equal if their names match. + */ + boolean checkFunctionEquivalenceHelper( + FunctionType that, EquivalenceMethod eqMethod) { + if (isConstructor()) { + if (that.isConstructor()) { + return this == that; + } + return false; + } + if (isInterface()) { + if (that.isInterface()) { + return getReferenceName().equals(that.getReferenceName()); + } + return false; + } + if (that.isInterface()) { + return false; + } + + return typeOfThis.checkEquivalenceHelper(that.typeOfThis, eqMethod) && + call.checkArrowEquivalenceHelper(that.call, eqMethod); + } + + @Override + public int hashCode() { + return isInterface() ? getReferenceName().hashCode() : call.hashCode(); + } + + public boolean hasEqualCallType(FunctionType otherType) { + return this.call.checkArrowEquivalenceHelper( + otherType.call, EquivalenceMethod.IDENTITY); + } + + /** + * Informally, a function is represented by + * {@code function (params): returnType} where the {@code params} is a comma + * separated list of types, the first one being a special + * {@code this:T} if the function expects a known type for {@code this}. + */ + @Override + String toStringHelper(boolean forAnnotations) { + if (!isPrettyPrint() || + this == registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE)) { + return "Function"; + } + + setPrettyPrint(false); + + StringBuilder b = new StringBuilder(32); + b.append("function ("); + int paramNum = call.parameters.getChildCount(); + boolean hasKnownTypeOfThis = !(typeOfThis instanceof UnknownType); + if (hasKnownTypeOfThis) { + if (isConstructor()) { + b.append("new:"); + } else { + b.append("this:"); + } + b.append(typeOfThis.toStringHelper(forAnnotations)); + } + if (paramNum > 0) { + if (hasKnownTypeOfThis) { + b.append(", "); + } + Node p = call.parameters.getFirstChild(); + appendArgString(b, p, forAnnotations); + + p = p.getNext(); + while (p != null) { + b.append(", "); + appendArgString(b, p, forAnnotations); + p = p.getNext(); + } + } + b.append("): "); + b.append(call.returnType.toStringHelper(forAnnotations)); + + setPrettyPrint(true); + return b.toString(); + } + + private void appendArgString( + StringBuilder b, Node p, boolean forAnnotations) { + if (p.isVarArgs()) { + appendVarArgsString(b, p.getJSType(), forAnnotations); + } else if (p.isOptionalArg()) { + appendOptionalArgString(b, p.getJSType(), forAnnotations); + } else { + b.append(p.getJSType().toStringHelper(forAnnotations)); + } + } + + /** Gets the string representation of a var args param. */ + private void appendVarArgsString(StringBuilder builder, JSType paramType, + boolean forAnnotations) { + if (paramType.isUnionType()) { + // Remove the optionality from the var arg. + paramType = paramType.toMaybeUnionType().getRestrictedUnion( + registry.getNativeType(JSTypeNative.VOID_TYPE)); + } + builder.append("...[").append( + paramType.toStringHelper(forAnnotations)).append("]"); + } + + /** Gets the string representation of an optional param. */ + private void appendOptionalArgString( + StringBuilder builder, JSType paramType, boolean forAnnotations) { + if (paramType.isUnionType()) { + // Remove the optionality from the var arg. + paramType = paramType.toMaybeUnionType().getRestrictedUnion( + registry.getNativeType(JSTypeNative.VOID_TYPE)); + } + builder.append(paramType.toStringHelper(forAnnotations)).append("="); + } + + /** + * A function is a subtype of another if their call methods are related via + * subtyping and {@code this} is a subtype of {@code that} with regard to + * the prototype chain. + */ + @Override + public boolean isSubtype(JSType that) { + if (JSType.isSubtypeHelper(this, that)) { + return true; + } + + if (that.isFunctionType()) { + FunctionType other = that.toMaybeFunctionType(); + if (other.isInterface()) { + // Any function can be assigned to an interface function. + return true; + } + if (isInterface()) { + // An interface function cannot be assigned to anything. + return false; + } + + // If functionA is a subtype of functionB, then their "this" types + // should be contravariant. However, this causes problems because + // of the way we enforce overrides. Because function(this:SubFoo) + // is not a subtype of function(this:Foo), our override check treats + // this as an error. Let's punt on all this for now. + // TODO(nicksantos): fix this. + boolean treatThisTypesAsCovariant = + // An interface 'this'-type is non-restrictive. + // In practical terms, if C implements I, and I has a method m, + // then any m doesn't necessarily have to C#m's 'this' + // type doesn't need to match I. + (other.typeOfThis.toObjectType() != null && + other.typeOfThis.toObjectType().getConstructor() != null && + other.typeOfThis.toObjectType().getConstructor().isInterface()) || + + // If one of the 'this' types is covariant of the other, + // then we'll treat them as covariant (see comment above). + other.typeOfThis.isSubtype(this.typeOfThis) || + this.typeOfThis.isSubtype(other.typeOfThis); + return treatThisTypesAsCovariant && this.call.isSubtype(other.call); + } + + return getNativeType(JSTypeNative.FUNCTION_PROTOTYPE).isSubtype(that); + } + + @Override + public T visit(Visitor visitor) { + return visitor.caseFunctionType(this); + } + + @Override T visit(RelationshipVisitor visitor, JSType that) { + return visitor.caseFunctionType(this, that); + } + + /** + * Gets the type of instance of this function. + * @throws IllegalStateException if this function is not a constructor + * (see {@link #isConstructor()}). + */ + public ObjectType getInstanceType() { + Preconditions.checkState(hasInstanceType()); + return typeOfThis.toObjectType(); + } + + /** + * Sets the instance type. This should only be used for special + * native types. + */ + void setInstanceType(ObjectType instanceType) { + typeOfThis = instanceType; + } + + /** + * Returns whether this function type has an instance type. + */ + public boolean hasInstanceType() { + return isConstructor() || isInterface(); + } + + /** + * Gets the type of {@code this} in this function. + */ + @Override + public JSType getTypeOfThis() { + return typeOfThis.isEmptyType() ? + registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE) : typeOfThis; + } + + /** + * Gets the source node or null if this is an unknown function. + */ + public Node getSource() { + return source; + } + + /** + * Sets the source node. + */ + public void setSource(Node source) { + if (prototypeSlot != null) { + // NOTE(bashir): On one hand when source is null we want to drop any + // references to old nodes retained in prototypeSlot. On the other hand + // we cannot simply drop prototypeSlot, so we retain all information + // except the propertyNode for which we use an approximation! These + // details mostly matter in hot-swap passes. + if (source == null || prototypeSlot.getNode() == null) { + prototypeSlot = new Property(prototypeSlot.getName(), + prototypeSlot.getType(), prototypeSlot.isTypeInferred(), source); + } + } + this.source = source; + } + + /** Adds a type to the list of subtypes for this type. */ + private void addSubType(FunctionType subType) { + if (subTypes == null) { + subTypes = Lists.newArrayList(); + } + subTypes.add(subType); + } + + @Override + public void clearCachedValues() { + super.clearCachedValues(); + + if (subTypes != null) { + for (FunctionType subType : subTypes) { + subType.clearCachedValues(); + } + } + + if (!isNativeObjectType()) { + if (hasInstanceType()) { + getInstanceType().clearCachedValues(); + } + + if (prototypeSlot != null) { + ((ObjectType) prototypeSlot.getType()).clearCachedValues(); + } + } + } + + /** + * Returns a list of types that are subtypes of this type. This is only valid + * for constructor functions, and may be null. This allows a downward + * traversal of the subtype graph. + */ + public List getSubTypes() { + return subTypes; + } + + @Override + public boolean hasCachedValues() { + return prototypeSlot != null || super.hasCachedValues(); + } + + @Override + JSType resolveInternal(ErrorReporter t, StaticScope scope) { + setResolvedTypeInternal(this); + + call = (ArrowType) safeResolve(call, t, scope); + if (prototypeSlot != null) { + prototypeSlot.setType( + safeResolve(prototypeSlot.getType(), t, scope)); + } + + // Warning about typeOfThis if it doesn't resolve to an ObjectType + // is handled further upstream. + // + // TODO(nicksantos): Handle this correctly if we have a UnionType. + // + // TODO(nicksantos): In ES3, the run-time coerces "null" to the global + // activation object. In ES5, it leaves it as null. Just punt on this + // issue for now by coercing out null. This is complicated by the + // fact that when most people write @this {Foo}, they really don't + // mean "nullable Foo". For certain tags (like @extends) we de-nullify + // the name for them. + JSType maybeTypeOfThis = safeResolve(typeOfThis, t, scope); + if (maybeTypeOfThis != null) { + maybeTypeOfThis = maybeTypeOfThis.restrictByNotNullOrUndefined(); + } + if (maybeTypeOfThis instanceof ObjectType) { + typeOfThis = maybeTypeOfThis; + } + + boolean changed = false; + ImmutableList.Builder resolvedInterfaces = + ImmutableList.builder(); + for (ObjectType iface : implementedInterfaces) { + ObjectType resolvedIface = (ObjectType) iface.resolve(t, scope); + resolvedInterfaces.add(resolvedIface); + changed |= (resolvedIface != iface); + } + if (changed) { + implementedInterfaces = resolvedInterfaces.build(); + } + + if (subTypes != null) { + for (int i = 0; i < subTypes.size(); i++) { + subTypes.set( + i, JSType.toMaybeFunctionType(subTypes.get(i).resolve(t, scope))); + } + } + + return super.resolveInternal(t, scope); + } + + @Override + public String toDebugHashCodeString() { + if (this == registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE)) { + return super.toDebugHashCodeString(); + } + + StringBuilder b = new StringBuilder(32); + b.append("function ("); + int paramNum = call.parameters.getChildCount(); + boolean hasKnownTypeOfThis = !typeOfThis.isUnknownType(); + if (hasKnownTypeOfThis) { + b.append("this:"); + b.append(getDebugHashCodeStringOf(typeOfThis)); + } + if (paramNum > 0) { + if (hasKnownTypeOfThis) { + b.append(", "); + } + Node p = call.parameters.getFirstChild(); + b.append(getDebugHashCodeStringOf(p.getJSType())); + p = p.getNext(); + while (p != null) { + b.append(", "); + b.append(getDebugHashCodeStringOf(p.getJSType())); + p = p.getNext(); + } + } + b.append(")"); + b.append(": "); + b.append(getDebugHashCodeStringOf(call.returnType)); + return b.toString(); + } + + private String getDebugHashCodeStringOf(JSType type) { + if (type == this) { + return "me"; + } else { + return type.toDebugHashCodeString(); + } + } + + /** Create a new constructor with the parameters and return type stripped. */ + public FunctionType cloneWithoutArrowType() { + FunctionType result = new FunctionType( + registry, getReferenceName(), source, + registry.createArrowType(null, null), getInstanceType(), + null, true, false); + result.setPrototypeBasedOn(getInstanceType()); + return result; + } + + @Override + public boolean hasAnyTemplateTypesInternal() { + return !getTemplateKeys().isEmpty() + || typeOfThis.hasAnyTemplateTypes() + || call.hasAnyTemplateTypes(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/IndexedType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/IndexedType.java new file mode 100644 index 0000000..d33b9f8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/IndexedType.java @@ -0,0 +1,64 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +/** + * An object type with a declared default index type. + * + * For example, Object. can take only numbers as + * keys. + * + */ +final class IndexedType extends ProxyObjectType { + private static final long serialVersionUID = 1L; + + final JSType indexType; + + IndexedType( + JSTypeRegistry registry, ObjectType objectType, JSType indexType) { + super(registry, objectType); + this.indexType = indexType; + } + + @Override + public JSType getIndexType() { + return indexType; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/InstanceObjectType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/InstanceObjectType.java new file mode 100644 index 0000000..7210ed1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/InstanceObjectType.java @@ -0,0 +1,194 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + + +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.javascript.rhino.Node; + +/** + * An object type that is an instance of some function constructor. + */ +class InstanceObjectType extends PrototypeObjectType { + private static final long serialVersionUID = 1L; + + private final FunctionType constructor; + + InstanceObjectType(JSTypeRegistry registry, FunctionType constructor) { + this(registry, constructor, false, null); + } + + InstanceObjectType(JSTypeRegistry registry, FunctionType constructor, + boolean isNativeType, + ImmutableList templatizedTypes) { + super(registry, null, null, isNativeType, constructor.getTemplateKeys(), + templatizedTypes); + Preconditions.checkNotNull(constructor); + this.constructor = constructor; + } + + @Override + public String getReferenceName() { + return getConstructor().getReferenceName(); + } + + @Override + public boolean hasReferenceName() { + return getConstructor().hasReferenceName(); + } + + @Override + public ObjectType getImplicitPrototype() { + return getConstructor().getPrototype(); + } + + @Override + public FunctionType getConstructor() { + return constructor; + } + + @Override + boolean defineProperty(String name, JSType type, boolean inferred, + Node propertyNode) { + ObjectType proto = getImplicitPrototype(); + if (proto != null && proto.hasOwnDeclaredProperty(name)) { + return false; + } + return super.defineProperty(name, type, inferred, propertyNode); + } + + @Override + String toStringHelper(boolean forAnnotations) { + if (constructor.hasReferenceName()) { + String typeString = constructor.getReferenceName(); + + ImmutableList templatizedTypes = getTemplatizedTypes(); + if (!templatizedTypes.isEmpty()) { + typeString += ".<" + Joiner.on(",").join(templatizedTypes) + ">"; + } + + return typeString; + } else { + return super.toStringHelper(forAnnotations); + } + } + + @Override + boolean isTheObjectType() { + return getConstructor().isNativeObjectType() + && "Object".equals(getReferenceName()); + } + + @Override + public boolean isInstanceType() { + return true; + } + + @Override + public boolean isArrayType() { + return getConstructor().isNativeObjectType() + && "Array".equals(getReferenceName()); + } + + @Override + public boolean isStringObjectType() { + return getConstructor().isNativeObjectType() + && "String".equals(getReferenceName()); + } + + @Override + public boolean isBooleanObjectType() { + return getConstructor().isNativeObjectType() + && "Boolean".equals(getReferenceName()); + } + + @Override + public boolean isNumberObjectType() { + return getConstructor().isNativeObjectType() + && "Number".equals(getReferenceName()); + } + + @Override + public boolean isDateType() { + return getConstructor().isNativeObjectType() + && "Date".equals(getReferenceName()); + } + + @Override + public boolean isRegexpType() { + return getConstructor().isNativeObjectType() + && "RegExp".equals(getReferenceName()); + } + + @Override + public boolean isNominalType() { + return hasReferenceName(); + } + + /** + * If this is equal to a NamedType object, its hashCode must be equal + * to the hashCode of the NamedType object. + */ + @Override + public int hashCode() { + if (hasReferenceName()) { + return getReferenceName().hashCode(); + } else { + return super.hashCode(); + } + } + + @Override + public Iterable getCtorImplementedInterfaces() { + return getConstructor().getImplementedInterfaces(); + } + + @Override + public Iterable getCtorExtendedInterfaces() { + return getConstructor().getExtendedInterfaces(); + } + + // The owner will always be a resolved type, so there's no need to set + // the constructor in resolveInternal. + // (it would lead to infinite loops if we did). + // JSType resolveInternal(ErrorReporter t, StaticScope scope); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/JSType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/JSType.java new file mode 100644 index 0000000..11875e9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/JSType.java @@ -0,0 +1,1523 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.javascript.rhino.ErrorReporter; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.jstype.JSTypeRegistry.ResolveMode; + +import java.io.Serializable; +import java.util.Comparator; + +/** + * Represents JavaScript value types.

      + * + * Types are split into two separate families: value types and object types. + * + * A special {@link UnknownType} exists to represent a wildcard type on which + * no information can be gathered. In particular, it can assign to everyone, + * is a subtype of everyone (and everyone is a subtype of it).

      + * + * If you remove the {@link UnknownType}, the set of types in the type system + * forms a lattice with the {@link #isSubtype} relation defining the partial + * order of types. All types are united at the top of the lattice by the + * {@link AllType} and at the bottom by the {@link NoType}.

      + * + */ +public abstract class JSType implements Serializable { + private static final long serialVersionUID = 1L; + + private boolean resolved = false; + private JSType resolveResult = null; + private final ImmutableList templateKeys; + private final ImmutableList templatizedTypes; + + private boolean inTemplatedCheckVisit = false; + private static final CanCastToVisitor CAN_CAST_TO_VISITOR = + new CanCastToVisitor(); + + public static final String UNKNOWN_NAME = + "Unknown class name"; + + public static final String NOT_A_CLASS = + "Not declared as a constructor"; + + public static final String NOT_A_TYPE = + "Not declared as a type name"; + + public static final String EMPTY_TYPE_COMPONENT = + "Named type with empty name component"; + + /** + * Total ordering on types based on their textual representation. + * This is used to have a deterministic output of the toString + * method of the union type since this output is used in tests. + */ + static final Comparator ALPHA = new Comparator() { + @Override + public int compare(JSType t1, JSType t2) { + return t1.toString().compareTo(t2.toString()); + } + }; + + // A flag set on enum definition tree nodes + public static final int ENUMDECL = 1; + public static final int NOT_ENUMDECL = 0; + + final JSTypeRegistry registry; + + JSType(JSTypeRegistry registry) { + this(registry, null, null); + } + + JSType(JSTypeRegistry registry, ImmutableList templateKeys, + ImmutableList templatizedTypes) { + this.registry = registry; + + // Do sanity checking on the specified keys and templatized types. + int keysLength = templateKeys == null ? 0 : templateKeys.size(); + int typesLength = templatizedTypes == null ? 0 : templatizedTypes.size(); + if (typesLength > keysLength) { + throw new IllegalArgumentException( + "Cannot have more templatized types than template keys"); + } else if (typesLength < keysLength) { + // If there are fewer templatized types than keys, extend the templatized + // types list to match the number of keys, using UNKNOWN_TYPE for the + // unspecified types. + ImmutableList.Builder builder = ImmutableList.builder(); + if (typesLength > 0) { + builder.addAll(templatizedTypes); + } + for (int i = 0; i < keysLength - typesLength; i++) { + builder.add(registry.getNativeType(JSTypeNative.UNKNOWN_TYPE)); + } + templatizedTypes = builder.build(); + } else if (keysLength == 0 && typesLength == 0) { + // Ensure that both lists are non-null. + templateKeys = ImmutableList.of(); + templatizedTypes = ImmutableList.of(); + } + + this.templateKeys = templateKeys; + this.templatizedTypes = templatizedTypes; + } + + /** + * Utility method for less verbose code. + */ + JSType getNativeType(JSTypeNative typeId) { + return registry.getNativeType(typeId); + } + + /** + * Gets the docInfo for this type. By default, documentation cannot be + * attached to arbitrary types. This must be overridden for + * programmer-defined types. + */ + public JSDocInfo getJSDocInfo() { + return null; + } + + /** + * Returns a user meaningful label for the JSType instance. For example, + * Functions and Enums will return their declaration name (if they have one). + * Some types will not have a meaningful display name. Calls to + * hasDisplayName() will return true IFF getDisplayName() will return null + * or a zero length string. + * + * @return the display name of the type, or null if one is not available + */ + public String getDisplayName() { + return null; + } + + /** + * @return true if the JSType has a user meaningful label. + */ + public boolean hasDisplayName() { + String displayName = getDisplayName(); + return displayName != null && !displayName.isEmpty(); + } + + /** Checks whether the property pname is present on the object. */ + public boolean hasProperty(String pname) { + return false; + } + + public boolean isNoType() { + return false; + } + + public boolean isNoResolvedType() { + return false; + } + + public boolean isNoObjectType() { + return false; + } + + public final boolean isEmptyType() { + return isNoType() || isNoObjectType() || isNoResolvedType() || + (registry.getNativeFunctionType( + JSTypeNative.LEAST_FUNCTION_TYPE) == this); + } + + public boolean isNumberObjectType() { + return false; + } + + public boolean isNumberValueType() { + return false; + } + + /** Whether this is the prototype of a function. */ + public boolean isFunctionPrototypeType() { + return false; + } + + public boolean isStringObjectType() { + return false; + } + + boolean isTheObjectType() { + return false; + } + + public boolean isStringValueType() { + return false; + } + + /** + * Tests whether the type is a string (value or Object). + * @return {@code this <: (String, string)} + */ + public final boolean isString() { + return isSubtype( + getNativeType(JSTypeNative.STRING_VALUE_OR_OBJECT_TYPE)); + } + + /** + * Tests whether the type is a number (value or Object). + * @return {@code this <: (Number, number)} + */ + public final boolean isNumber() { + return isSubtype( + getNativeType(JSTypeNative.NUMBER_VALUE_OR_OBJECT_TYPE)); + } + + public boolean isArrayType() { + return false; + } + + public boolean isBooleanObjectType() { + return false; + } + + public boolean isBooleanValueType() { + return false; + } + + public boolean isRegexpType() { + return false; + } + + public boolean isDateType() { + return false; + } + + public boolean isNullType() { + return false; + } + + public boolean isVoidType() { + return false; + } + + public boolean isAllType() { + return false; + } + + public boolean isUnknownType() { + return false; + } + + public boolean isCheckedUnknownType() { + return false; + } + + public final boolean isUnionType() { + return toMaybeUnionType() != null; + } + + /** + * Returns true iff {@code this} can be a {@code struct}. + * UnionType overrides the method, assume {@code this} is not a union here. + */ + public boolean isStruct() { + if (isObject()) { + ObjectType objType = toObjectType(); + ObjectType iproto = objType.getImplicitPrototype(); + // For the case when a @struct constructor is assigned to a function's + // prototype property + if (iproto != null && iproto.isStruct()) { + return true; + } + FunctionType ctor = objType.getConstructor(); + // This test is true for object literals + if (ctor == null) { + JSDocInfo info = objType.getJSDocInfo(); + return info != null && info.makesStructs(); + } else { + return ctor.makesStructs(); + } + } + return false; + } + + /** + * Returns true iff {@code this} can be a {@code dict}. + * UnionType overrides the method, assume {@code this} is not a union here. + */ + public boolean isDict() { + if (isObject()) { + ObjectType objType = toObjectType(); + ObjectType iproto = objType.getImplicitPrototype(); + // For the case when a @dict constructor is assigned to a function's + // prototype property + if (iproto != null && iproto.isDict()) { + return true; + } + FunctionType ctor = objType.getConstructor(); + // This test is true for object literals + if (ctor == null) { + JSDocInfo info = objType.getJSDocInfo(); + return info != null && info.makesDicts(); + } else { + return ctor.makesDicts(); + } + } + return false; + } + + /** + * Downcasts this to a UnionType, or returns null if this is not a UnionType. + * + * Named in honor of Haskell's Maybe type constructor. + */ + public UnionType toMaybeUnionType() { + return null; + } + + /** Returns true if this is a global this type. */ + public final boolean isGlobalThisType() { + return this == registry.getNativeType(JSTypeNative.GLOBAL_THIS); + } + + /** Returns true if toMaybeFunctionType returns a non-null FunctionType. */ + public final boolean isFunctionType() { + return toMaybeFunctionType() != null; + } + + /** + * Downcasts this to a FunctionType, or returns null if this is not + * a function. + * + * For the purposes of this function, we define a MaybeFunctionType as any + * type in the sub-lattice + * { x | LEAST_FUNCTION_TYPE <= x <= GREATEST_FUNCTION_TYPE } + * This definition excludes bottom types like NoType and NoObjectType. + * + * This definition is somewhat arbitrary and axiomatic, but this is the + * definition that makes the most sense for the most callers. + */ + public FunctionType toMaybeFunctionType() { + return null; + } + + /** + * Null-safe version of toMaybeFunctionType(). + */ + public static FunctionType toMaybeFunctionType(JSType type) { + return type == null ? null : type.toMaybeFunctionType(); + } + + public final boolean isEnumElementType() { + return toMaybeEnumElementType() != null; + } + + /** + * Downcasts this to an EnumElementType, or returns null if this is not an EnumElementType. + */ + public EnumElementType toMaybeEnumElementType() { + return null; + } + + public boolean isEnumType() { + return toMaybeEnumType() != null; + } + + /** + * Downcasts this to an EnumType, or returns null if this is not an EnumType. + */ + public EnumType toMaybeEnumType() { + return null; + } + + boolean isNamedType() { + return false; + } + + public boolean isRecordType() { + return toMaybeRecordType() != null; + } + + /** + * Downcasts this to a RecordType, or returns null if this is not + * a RecordType. + */ + RecordType toMaybeRecordType() { + return null; + } + + public final boolean isParameterizedType() { + return toMaybeParameterizedType() != null; + } + + /** + * Downcasts this to a ParameterizedType, or returns null if this is not + * a function. + */ + public ParameterizedType toMaybeParameterizedType() { + return null; + } + + /** + * Null-safe version of toMaybeParameterizedType(). + */ + public static ParameterizedType toMaybeParameterizedType(JSType type) { + return type == null ? null : type.toMaybeParameterizedType(); + } + + public final boolean isTemplateType() { + return toMaybeTemplateType() != null; + } + + /** + * Downcasts this to a TemplateType, or returns null if this is not + * a function. + */ + public TemplateType toMaybeTemplateType() { + return null; + } + + /** + * Null-safe version of toMaybeTemplateType(). + */ + public static TemplateType toMaybeTemplateType(JSType type) { + return type == null ? null : type.toMaybeTemplateType(); + } + + public boolean hasAnyTemplateTypes() { + if (!this.inTemplatedCheckVisit) { + this.inTemplatedCheckVisit = true; + boolean result = hasAnyTemplateTypesInternal(); + this.inTemplatedCheckVisit = false; + return result; + } else { + // prevent infinite recursion, this is "not yet". + return false; + } + } + + boolean hasAnyTemplateTypesInternal() { + if (isTemplatized()) { + for (JSType templatizedType : templatizedTypes) { + if (templatizedType.hasAnyTemplateTypes()) { + return true; + } + } + } + return false; + } + + /** + * Returns true if this type is templatized; false otherwise. + */ + public boolean isTemplatized() { + return !templateKeys.isEmpty(); + } + + /** + * Returns the template keys associated with this type. + */ + public ImmutableList getTemplateKeys() { + return templateKeys; + } + + public ImmutableList getTemplatizedTypes() { + return templatizedTypes; + } + + /** + * Returns true if this type is templatized for the specified key; false + * otherwise. + */ + public boolean hasTemplatizedType(String key) { + return templateKeys.contains(key); + } + + /** + * Returns the type associated with a given template key. Will return + * the UNKNOWN_TYPE if there is no template type associated with that + * template key. + */ + public JSType getTemplatizedType(String key) { + int index = templateKeys.indexOf(key); + if (index < 0) { + return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); + } + return templatizedTypes.get(index); + } + + /** + * Determines if the two specified JSTypes have equivalent, invariant + * templatized types. + */ + static boolean hasEquivalentTemplateTypes( + JSType type1, JSType type2, EquivalenceMethod eqMethod) { + ImmutableList templatizedTypes1 = type1.getTemplatizedTypes(); + ImmutableList templatizedTypes2 = type2.getTemplatizedTypes(); + int nTemplatizedTypes1 = templatizedTypes1.size(); + int nTemplatizedTypes2 = templatizedTypes2.size(); + + if (nTemplatizedTypes1 != nTemplatizedTypes2) { + return false; + } + + for (int i = 0; i < nTemplatizedTypes1; i++) { + JSType templatizedType1 = templatizedTypes1.get(i); + JSType templatizedType2 = templatizedTypes2.get(i); + if (templatizedType1.checkEquivalenceHelper(templatizedType2, eqMethod)) { + return false; + } + } + + return true; + } + + + /** + * Tests whether this type is an {@code Object}, or any subtype thereof. + * @return {@code this <: Object} + */ + public boolean isObject() { + return false; + } + + /** + * Whether this type is a {@link FunctionType} that is a constructor or a + * named type that points to such a type. + */ + public boolean isConstructor() { + return false; + } + + /** + * Whether this type is a nominal type (a named instance object or + * a named enum). + */ + public boolean isNominalType() { + return false; + } + + /** + * Whether this type is the original constructor of a nominal type. + * Does not include structural constructors. + */ + public final boolean isNominalConstructor() { + if (isConstructor() || isInterface()) { + FunctionType fn = toMaybeFunctionType(); + if (fn == null) { + return false; + } + + // Programmer-defined constructors will have a link + // back to the original function in the source tree. + // Structural constructors will not. + if (fn.getSource() != null) { + return true; + } + + // Native constructors are always nominal. + return fn.isNativeObjectType(); + } + return false; + } + + /** + * Whether this type is an Instance object of some constructor. + * Does not necessarily mean this is an {@link InstanceObjectType}. + */ + public boolean isInstanceType() { + return false; + } + + /** + * Whether this type is a {@link FunctionType} that is an interface or a named + * type that points to such a type. + */ + public boolean isInterface() { + return false; + } + + /** + * Whether this type is a {@link FunctionType} that is an ordinary function or + * a named type that points to such a type. + */ + public boolean isOrdinaryFunction() { + return false; + } + + /** + * Checks if two types are equivalent. + */ + public final boolean isEquivalentTo(JSType that) { + return checkEquivalenceHelper(that, EquivalenceMethod.IDENTITY); + } + + /** + * Checks if two types are invariant. + * @see EquivalenceMethod + */ + public final boolean isInvariant(JSType that) { + return checkEquivalenceHelper(that, EquivalenceMethod.INVARIANT); + } + + /** + * Whether this type is meaningfully different from {@code that} type for + * the purposes of data flow analysis. + * + * This is a trickier check than pure equality, because it has to properly + * handle unknown types. See {@code EquivalenceMethod} for more info. + * + * @see Unknown + * unknowns + */ + public final boolean differsFrom(JSType that) { + return !checkEquivalenceHelper(that, EquivalenceMethod.DATA_FLOW); + } + + /** + * An equivalence visitor. + */ + boolean checkEquivalenceHelper(JSType that, EquivalenceMethod eqMethod) { + if (this == that) { + return true; + } + + boolean thisUnknown = isUnknownType(); + boolean thatUnknown = that.isUnknownType(); + if (thisUnknown || thatUnknown) { + if (eqMethod == EquivalenceMethod.INVARIANT) { + // If we're checking for invariance, the unknown type is invariant + // with everyone. + return true; + } else if (eqMethod == EquivalenceMethod.DATA_FLOW) { + // If we're checking data flow, then two types are the same if they're + // both unknown. + return thisUnknown && thatUnknown; + } else if (thisUnknown && thatUnknown && + (isNominalType() ^ that.isNominalType())) { + // If they're both unknown, but one is a nominal type and the other + // is not, then we should fail out immediately. This ensures that + // we won't unbox the unknowns further down. + return false; + } + } + + if (isUnionType() && that.isUnionType()) { + return toMaybeUnionType().checkUnionEquivalenceHelper( + that.toMaybeUnionType(), eqMethod); + } + + if (isFunctionType() && that.isFunctionType()) { + return toMaybeFunctionType().checkFunctionEquivalenceHelper( + that.toMaybeFunctionType(), eqMethod); + } + + if (isRecordType() && that.isRecordType()) { + return toMaybeRecordType().checkRecordEquivalenceHelper( + that.toMaybeRecordType(), eqMethod); + } + + ParameterizedType thisParamType = toMaybeParameterizedType(); + ParameterizedType thatParamType = that.toMaybeParameterizedType(); + if (thisParamType != null || thatParamType != null) { + // Check if one type is parameterized, but the other is not. + boolean paramsMatch = false; + if (thisParamType != null && thatParamType != null) { + paramsMatch = thisParamType.getParameterType().checkEquivalenceHelper( + thatParamType.getParameterType(), eqMethod); + } else if (eqMethod == EquivalenceMethod.IDENTITY) { + paramsMatch = false; + } else { + // If one of the type parameters is unknown, but the other is not, + // then we consider these the same for the purposes of data flow + // and invariance. + paramsMatch = true; + } + + JSType thisRootType = thisParamType == null ? + this : thisParamType.getReferencedTypeInternal(); + JSType thatRootType = thatParamType == null ? + that : thatParamType.getReferencedTypeInternal(); + return paramsMatch && + thisRootType.checkEquivalenceHelper(thatRootType, eqMethod); + } + + if (isNominalType() && that.isNominalType()) { + return toObjectType().getReferenceName().equals( + that.toObjectType().getReferenceName()); + } + + // Unbox other proxies. + if (this instanceof ProxyObjectType) { + return ((ProxyObjectType) this) + .getReferencedTypeInternal().checkEquivalenceHelper( + that, eqMethod); + } + + if (that instanceof ProxyObjectType) { + return checkEquivalenceHelper( + ((ProxyObjectType) that).getReferencedTypeInternal(), + eqMethod); + } + + // Relies on the fact that for the base {@link JSType}, only one + // instance of each sub-type will ever be created in a given registry, so + // there is no need to verify members. If the object pointers are not + // identical, then the type member must be different. + return this == that; + } + + public static boolean isEquivalent(JSType typeA, JSType typeB) { + return (typeA == null || typeB == null) ? + typeA == typeB : typeA.isEquivalentTo(typeB); + } + + @Override + public boolean equals(Object jsType) { + return (jsType instanceof JSType) ? + isEquivalentTo((JSType) jsType) : false; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + /** + * This predicate is used to test whether a given type can appear in a + * 'Int32' context. This context includes, for example, the operands of a + * bitwise or operator. Since we do not currently support integer types, + * this is a synonym for {@code Number}. + */ + public final boolean matchesInt32Context() { + return matchesNumberContext(); + } + + /** + * This predicate is used to test whether a given type can appear in a + * 'Uint32' context. This context includes the right-hand operand of a shift + * operator. + */ + public final boolean matchesUint32Context() { + return matchesNumberContext(); + } + + /** + * This predicate is used to test whether a given type can appear in a + * numeric context, such as an operand of a multiply operator. + */ + public boolean matchesNumberContext() { + return false; + } + + /** + * This predicate is used to test whether a given type can appear in a + * {@code String} context, such as an operand of a string concat (+) operator. + * + * All types have at least the potential for converting to {@code String}. + * When we add externally defined types, such as a browser OM, we may choose + * to add types that do not automatically convert to {@code String}. + */ + public boolean matchesStringContext() { + return false; + } + + /** + * This predicate is used to test whether a given type can appear in an + * {@code Object} context, such as the expression in a with statement. + * + * Most types we will encounter, except notably {@code null}, have at least + * the potential for converting to {@code Object}. Host defined objects can + * get peculiar. + */ + public boolean matchesObjectContext() { + return false; + } + + /** + * Coerces this type to an Object type, then gets the type of the property + * whose name is given. + * + * Unlike {@link ObjectType#getPropertyType}, returns null if the property + * is not found. + * + * @return The property's type. {@code null} if the current type cannot + * have properties, or if the type is not found. + */ + public JSType findPropertyType(String propertyName) { + ObjectType autoboxObjType = ObjectType.cast(autoboxesTo()); + if (autoboxObjType != null) { + return autoboxObjType.findPropertyType(propertyName); + } + + return null; + } + + /** + * This predicate is used to test whether a given type can be used as the + * 'function' in a function call. + * + * @return {@code true} if this type might be callable. + */ + public boolean canBeCalled() { + return false; + } + + /** + * Tests whether values of {@code this} type can be safely assigned + * to values of {@code that} type.

      + * + * The default implementation verifies that {@code this} is a subtype + * of {@code that}.

      + */ + public boolean canCastTo(JSType that) { + return this.visit(CAN_CAST_TO_VISITOR, that); + } + + /** + * Turn a scalar type to the corresponding object type. + * + * @return the auto-boxed type or {@code null} if this type is not a scalar. + */ + public JSType autoboxesTo() { + return null; + } + + /** + * Turn an object type to its corresponding scalar type. + * + * @return the unboxed type or {@code null} if this type does not unbox. + */ + public JSType unboxesTo() { + return null; + } + + /** + * Casts this to an ObjectType, or returns null if this is not an ObjectType. + * If this is a scalar type, it will *not* be converted to an object type. + * If you want to simulate JS autoboxing or dereferencing, you should use + * autoboxesTo() or dereference(). + */ + public ObjectType toObjectType() { + return this instanceof ObjectType ? (ObjectType) this : null; + } + + /** + * Dereference a type for property access. + * + * Filters null/undefined and autoboxes the resulting type. + * Never returns null. + */ + public JSType autobox() { + JSType restricted = restrictByNotNullOrUndefined(); + JSType autobox = restricted.autoboxesTo(); + return autobox == null ? restricted : autobox; + } + + /** + * Dereference a type for property access. + * + * Filters null/undefined, autoboxes the resulting type, and returns it + * iff it's an object. + */ + public final ObjectType dereference() { + return autobox().toObjectType(); + } + + /** + * Tests whether {@code this} and {@code that} are meaningfully + * comparable. By meaningfully, we mean compatible types that do not lead + * to step 22 of the definition of the Abstract Equality Comparison + * Algorithm (11.9.3, page 55–56) of the ECMA-262 specification.

      + */ + public final boolean canTestForEqualityWith(JSType that) { + return testForEquality(that).equals(UNKNOWN); + } + + /** + * Compares {@code this} and {@code that}. + * @return

        + *
      • {@link TernaryValue#TRUE} if the comparison of values of + * {@code this} type and {@code that} always succeed (such as + * {@code undefined} compared to {@code null})
      • + *
      • {@link TernaryValue#FALSE} if the comparison of values of + * {@code this} type and {@code that} always fails (such as + * {@code undefined} compared to {@code number})
      • + *
      • {@link TernaryValue#UNKNOWN} if the comparison can succeed or + * fail depending on the concrete values
      • + *
      + */ + public TernaryValue testForEquality(JSType that) { + return testForEqualityHelper(this, that); + } + + TernaryValue testForEqualityHelper(JSType aType, JSType bType) { + if (bType.isAllType() || bType.isUnknownType() || + bType.isNoResolvedType() || + aType.isAllType() || aType.isUnknownType() || + aType.isNoResolvedType()) { + return UNKNOWN; + } + + boolean aIsEmpty = aType.isEmptyType(); + boolean bIsEmpty = bType.isEmptyType(); + if (aIsEmpty || bIsEmpty) { + if (aIsEmpty && bIsEmpty) { + return TernaryValue.TRUE; + } else { + return UNKNOWN; + } + } + + if (aType.isFunctionType() || bType.isFunctionType()) { + JSType otherType = aType.isFunctionType() ? bType : aType; + // In theory, functions are comparable to anything except + // null/undefined. For example, on FF3: + // function() {} == 'function () {\n}' + // In practice, how a function serializes to a string is + // implementation-dependent, so it does not really make sense to test + // for equality with a string. + JSType meet = otherType.getGreatestSubtype( + getNativeType(JSTypeNative.OBJECT_TYPE)); + if (meet.isNoType() || meet.isNoObjectType()) { + return TernaryValue.FALSE; + } else { + return TernaryValue.UNKNOWN; + } + } + if (bType.isEnumElementType() || bType.isUnionType()) { + return bType.testForEquality(aType); + } + return null; + } + + /** + * Tests whether {@code this} and {@code that} are meaningfully + * comparable using shallow comparison. By meaningfully, we mean compatible + * types that are not rejected by step 1 of the definition of the Strict + * Equality Comparison Algorithm (11.9.6, page 56–57) of the + * ECMA-262 specification.

      + */ + public final boolean canTestForShallowEqualityWith(JSType that) { + if (isEmptyType() || that.isEmptyType()) { + return isSubtype(that) || that.isSubtype(this); + } + + JSType inf = getGreatestSubtype(that); + return !inf.isEmptyType() || + // Our getGreatestSubtype relation on functions is pretty bad. + // Let's just say it's always ok to compare two functions. + // Once the TODO in FunctionType is fixed, we should be able to + // remove this. + inf == registry.getNativeType(JSTypeNative.LEAST_FUNCTION_TYPE); + } + + /** + * Tests whether this type is nullable. + */ + public boolean isNullable() { + return isSubtype(getNativeType(JSTypeNative.NULL_TYPE)); + } + + /** + * Gets the least supertype of this that's not a union. + */ + public JSType collapseUnion() { + return this; + } + + /** + * Gets the least supertype of {@code this} and {@code that}. + * The least supertype is the join (∨) or supremum of both types in the + * type lattice.

      + * Examples: + *

        + *
      • {@code number ∨ *} = {@code *}
      • + *
      • {@code number ∨ Object} = {@code (number, Object)}
      • + *
      • {@code Number ∨ Object} = {@code Object}
      • + *
      + * @return {@code this ∨ that} + */ + public JSType getLeastSupertype(JSType that) { + if (that.isUnionType()) { + // Union types have their own implementation of getLeastSupertype. + return that.toMaybeUnionType().getLeastSupertype(this); + } + return getLeastSupertype(this, that); + } + + /** + * A generic implementation meant to be used as a helper for common + * getLeastSupertype implementations. + */ + static JSType getLeastSupertype(JSType thisType, JSType thatType) { + boolean areEquivalent = thisType.isEquivalentTo(thatType); + return areEquivalent ? thisType : + filterNoResolvedType( + thisType.registry.createUnionType(thisType, thatType)); + } + + /** + * Gets the greatest subtype of {@code this} and {@code that}. + * The greatest subtype is the meet (∧) or infimum of both types in the + * type lattice.

      + * Examples + *

        + *
      • {@code Number ∧ Any} = {@code Any}
      • + *
      • {@code number ∧ Object} = {@code Any}
      • + *
      • {@code Number ∧ Object} = {@code Number}
      • + *
      + * @return {@code this ∨ that} + */ + public JSType getGreatestSubtype(JSType that) { + return getGreatestSubtype(this, that); + } + + /** + * A generic implementation meant to be used as a helper for common + * getGreatestSubtype implementations. + */ + static JSType getGreatestSubtype(JSType thisType, JSType thatType) { + if (thisType.isFunctionType() && thatType.isFunctionType()) { + // The FunctionType sub-lattice is not well-defined. i.e., the + // proposition + // A < B => sup(A, B) == B + // does not hold because of unknown parameters and return types. + // See the comment in supAndInfHelper for more info on this. + return thisType.toMaybeFunctionType().supAndInfHelper( + thatType.toMaybeFunctionType(), false); + } else if (thisType.isEquivalentTo(thatType)) { + return thisType; + } else if (thisType.isUnknownType() || thatType.isUnknownType()) { + // The greatest subtype with any unknown type is the universal + // unknown type, unless the two types are equal. + return thisType.isEquivalentTo(thatType) ? thisType : + thisType.getNativeType(JSTypeNative.UNKNOWN_TYPE); + } else if (thisType.isUnionType()) { + return thisType.toMaybeUnionType().meet(thatType); + } else if (thatType.isUnionType()) { + return thatType.toMaybeUnionType().meet(thisType); + } else if (thisType.isParameterizedType()) { + return thisType.toMaybeParameterizedType().getGreatestSubtypeHelper( + thatType); + } else if (thatType.isParameterizedType()) { + return thatType.toMaybeParameterizedType().getGreatestSubtypeHelper( + thisType); + } else if (thisType.isSubtype(thatType)) { + return filterNoResolvedType(thisType); + } else if (thatType.isSubtype(thisType)) { + return filterNoResolvedType(thatType); + } else if (thisType.isRecordType()) { + return thisType.toMaybeRecordType().getGreatestSubtypeHelper(thatType); + } else if (thatType.isRecordType()) { + return thatType.toMaybeRecordType().getGreatestSubtypeHelper(thisType); + } + + if (thisType.isEnumElementType()) { + JSType inf = thisType.toMaybeEnumElementType().meet(thatType); + if (inf != null) { + return inf; + } + } else if (thatType.isEnumElementType()) { + JSType inf = thatType.toMaybeEnumElementType().meet(thisType); + if (inf != null) { + return inf; + } + } + + if (thisType.isObject() && thatType.isObject()) { + return thisType.getNativeType(JSTypeNative.NO_OBJECT_TYPE); + } + return thisType.getNativeType(JSTypeNative.NO_TYPE); + } + + /** + * When computing infima, we may get a situation like + * inf(Type1, Type2) + * where both types are unresolved, so they're technically + * subtypes of one another. + * + * If this happens, filter them down to NoResolvedType. + */ + static JSType filterNoResolvedType(JSType type) { + if (type.isNoResolvedType()) { + // inf(UnresolvedType1, UnresolvedType2) needs to resolve + // to the base unresolved type, so that the relation is symmetric. + return type.getNativeType(JSTypeNative.NO_RESOLVED_TYPE); + } else if (type.isUnionType()) { + UnionType unionType = type.toMaybeUnionType(); + boolean needsFiltering = false; + for (JSType alt : unionType.getAlternates()) { + if (alt.isNoResolvedType()) { + needsFiltering = true; + break; + } + } + + if (needsFiltering) { + UnionTypeBuilder builder = new UnionTypeBuilder(type.registry); + builder.addAlternate(type.getNativeType(JSTypeNative.NO_RESOLVED_TYPE)); + for (JSType alt : unionType.getAlternates()) { + if (!alt.isNoResolvedType()) { + builder.addAlternate(alt); + } + } + return builder.build(); + } + } + return type; + } + + /** + * Computes the restricted type of this type knowing that the + * {@code ToBoolean} predicate has a specific value. For more information + * about the {@code ToBoolean} predicate, see + * {@link #getPossibleToBooleanOutcomes}. + * + * @param outcome the value of the {@code ToBoolean} predicate + * + * @return the restricted type, or the Any Type if the underlying type could + * not have yielded this ToBoolean value + * + * TODO(user): Move this method to the SemanticRAI and use the visit + * method of types to get the restricted type. + */ + public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) { + if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) { + return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE); + } + + BooleanLiteralSet literals = getPossibleToBooleanOutcomes(); + if (literals.contains(outcome)) { + return this; + } else { + return getNativeType(JSTypeNative.NO_TYPE); + } + } + + /** + * Computes the set of possible outcomes of the {@code ToBoolean} predicate + * for this type. The {@code ToBoolean} predicate is defined by the ECMA-262 + * standard, 3rd edition. Its behavior for simple types can be + * summarized by the following table: + * + * + * + * + * + * + * + * + *
      typeresult
      {@code undefined}{false}
      {@code null}{false}
      {@code boolean}{true, false}
      {@code number}{true, false}
      {@code string}{true, false}
      {@code Object}{true}
      + * @return the set of boolean literals for this type + */ + public abstract BooleanLiteralSet getPossibleToBooleanOutcomes(); + + /** + * Computes the subset of {@code this} and {@code that} types if equality + * is observed. If a value {@code v1} of type {@code null} is equal to a value + * {@code v2} of type {@code (undefined,number)}, we can infer that the + * type of {@code v1} is {@code null} and the type of {@code v2} is + * {@code undefined}. + * + * @return a pair containing the restricted type of {@code this} as the first + * component and the restricted type of {@code that} as the second + * element. The returned pair is never {@code null} even though its + * components may be {@code null} + */ + public TypePair getTypesUnderEquality(JSType that) { + // unions types + if (that.isUnionType()) { + TypePair p = that.toMaybeUnionType().getTypesUnderEquality(this); + return new TypePair(p.typeB, p.typeA); + } + + // other types + switch (testForEquality(that)) { + case FALSE: + return new TypePair(null, null); + + case TRUE: + case UNKNOWN: + return new TypePair(this, that); + } + + // switch case is exhaustive + throw new IllegalStateException(); + } + + /** + * Computes the subset of {@code this} and {@code that} types if inequality + * is observed. If a value {@code v1} of type {@code number} is not equal to a + * value {@code v2} of type {@code (undefined,number)}, we can infer that the + * type of {@code v1} is {@code number} and the type of {@code v2} is + * {@code number} as well. + * + * @return a pair containing the restricted type of {@code this} as the first + * component and the restricted type of {@code that} as the second + * element. The returned pair is never {@code null} even though its + * components may be {@code null} + */ + public TypePair getTypesUnderInequality(JSType that) { + // unions types + if (that.isUnionType()) { + TypePair p = that.toMaybeUnionType().getTypesUnderInequality(this); + return new TypePair(p.typeB, p.typeA); + } + + // other types + switch (testForEquality(that)) { + case TRUE: + JSType noType = getNativeType(JSTypeNative.NO_TYPE); + return new TypePair(noType, noType); + + case FALSE: + case UNKNOWN: + return new TypePair(this, that); + } + + // switch case is exhaustive + throw new IllegalStateException(); + } + + /** + * Computes the subset of {@code this} and {@code that} types under shallow + * equality. + * + * @return a pair containing the restricted type of {@code this} as the first + * component and the restricted type of {@code that} as the second + * element. The returned pair is never {@code null} even though its + * components may be {@code null}. + */ + public TypePair getTypesUnderShallowEquality(JSType that) { + JSType commonType = getGreatestSubtype(that); + return new TypePair(commonType, commonType); + } + + /** + * Computes the subset of {@code this} and {@code that} types under + * shallow inequality. + * + * @return A pair containing the restricted type of {@code this} as the first + * component and the restricted type of {@code that} as the second + * element. The returned pair is never {@code null} even though its + * components may be {@code null} + */ + public TypePair getTypesUnderShallowInequality(JSType that) { + // union types + if (that.isUnionType()) { + TypePair p = that.toMaybeUnionType().getTypesUnderShallowInequality(this); + return new TypePair(p.typeB, p.typeA); + } + + // Other types. + // There are only two types whose shallow inequality is deterministically + // true -- null and undefined. We can just enumerate them. + if (isNullType() && that.isNullType() || + isVoidType() && that.isVoidType()) { + return new TypePair(null, null); + } else { + return new TypePair(this, that); + } + } + + /** + * If this is a union type, returns a union type that does not include + * the null or undefined type. + */ + public JSType restrictByNotNullOrUndefined() { + return this; + } + + /** + * Checks whether {@code this} is a subtype of {@code that}.

      + * + * Subtyping rules: + *

        + *
      • (unknown) — every type is a subtype of the Unknown type.
      • + *
      • (no) — the No type is a subtype of every type.
      • + *
      • (no-object) — the NoObject type is a subtype of every object + * type (i.e. subtypes of the Object type).
      • + *
      • (ref) — a type is a subtype of itself.
      • + *
      • (union-l) — A union type is a subtype of a type U if all the + * union type's constituents are a subtype of U. Formally
        + * {@code (T1, …, Tn) <: U} if and only + * {@code Tk <: U} for all {@code k ∈ 1..n}.
      • + *
      • (union-r) — A type U is a subtype of a union type if it is a + * subtype of one of the union type's constituents. Formally
        + * {@code U <: (T1, …, Tn)} if and only + * if {@code U <: Tk} for some index {@code k}.
      • + *
      • (objects) — an Object {@code O1} is a subtype + * of an object {@code O2} if it has more properties + * than {@code O2} and all common properties are + * pairwise subtypes.
      • + *
      + * + * @return {@code this <: that} + */ + public boolean isSubtype(JSType that) { + return isSubtypeHelper(this, that); + } + + /** + * A generic implementation meant to be used as a helper for common subtyping + * cases. + */ + static boolean isSubtypeHelper(JSType thisType, JSType thatType) { + // unknown + if (thatType.isUnknownType()) { + return true; + } + // all type + if (thatType.isAllType()) { + return true; + } + // equality + if (thisType.isEquivalentTo(thatType)) { + return true; + } + // unions + if (thatType.isUnionType()) { + UnionType union = thatType.toMaybeUnionType(); + for (JSType element : union.alternates) { + if (thisType.isSubtype(element)) { + return true; + } + } + return false; + } + + // parameterized types. + if (thisType.isParameterizedType()) { + return thisType.toMaybeParameterizedType().isParameterizeSubtypeOf( + thatType); + } + + // proxy types + if (thatType instanceof ProxyObjectType) { + return thisType.isSubtype( + ((ProxyObjectType) thatType).getReferencedTypeInternal()); + } + return false; + } + + /** + * Visit this type with the given visitor. + * @see com.google.javascript.rhino.jstype.Visitor + * @return the value returned by the visitor + */ + public abstract T visit(Visitor visitor); + + /** + * Visit the types with the given visitor. + * @see com.google.javascript.rhino.jstype.RelationshipVisitor + * @return the value returned by the visitor + */ + abstract T visit(RelationshipVisitor visitor, JSType that); + + /** + * Force this type to resolve, even if the registry is in a lazy + * resolving mode. + * @see #resolve + */ + public final JSType forceResolve(ErrorReporter t, StaticScope scope) { + ResolveMode oldResolveMode = registry.getResolveMode(); + registry.setResolveMode(ResolveMode.IMMEDIATE); + JSType result = resolve(t, scope); + registry.setResolveMode(oldResolveMode); + return result; + } + + + /** + * Resolve this type in the given scope. + * + * The returned value must be equal to {@code this}, as defined by + * {@link #isEquivalentTo}. It may or may not be the same object. This method + * may modify the internal state of {@code this}, as long as it does + * so in a way that preserves Object equality. + * + * For efficiency, we should only resolve a type once per compilation job. + * For incremental compilations, one compilation job may need the + * artifacts from a previous generation, so we will eventually need + * a generational flag instead of a boolean one. + */ + public final JSType resolve(ErrorReporter t, StaticScope scope) { + if (resolved) { + // TODO(nicksantos): Check to see if resolve() looped back on itself. + // Preconditions.checkNotNull(resolveResult); + if (resolveResult == null) { + return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); + } + return resolveResult; + } + resolved = true; + resolveResult = resolveInternal(t, scope); + resolveResult.setResolvedTypeInternal(resolveResult); + return resolveResult; + } + + /** + * @see #resolve + */ + abstract JSType resolveInternal(ErrorReporter t, StaticScope scope); + + void setResolvedTypeInternal(JSType type) { + resolveResult = type; + resolved = true; + } + + /** Whether the type has been resolved. */ + public final boolean isResolved() { + return resolved; + } + + /** Clears the resolved field. */ + public final void clearResolved() { + resolved = false; + resolveResult = null; + } + + /** + * A null-safe resolve. + * @see #resolve + */ + static final JSType safeResolve( + JSType type, ErrorReporter t, StaticScope scope) { + return type == null ? null : type.resolve(t, scope); + } + + /** + * Certain types have constraints on them at resolution-time. + * For example, a type in an {@code @extends} annotation must be an + * object. Clients should inject a validator that emits a warning + * if the type does not validate, and return false. + */ + public boolean setValidator(Predicate validator) { + return validator.apply(this); + } + + public static class TypePair { + public final JSType typeA; + public final JSType typeB; + + public TypePair(JSType typeA, JSType typeB) { + this.typeA = typeA; + this.typeB = typeB; + } + } + + /** + * A string representation of this type, suitable for printing + * in warnings. + */ + @Override + public String toString() { + return toStringHelper(false); + } + + /** + * A hash code function for diagnosing complicated issues + * around type-identity. + */ + public String toDebugHashCodeString() { + return "{" + hashCode() + "}"; + } + + /** + * A string representation of this type, suitable for printing + * in type annotations at code generation time. + */ + public final String toAnnotationString() { + return toStringHelper(true); + } + + /** + * @param forAnnotations Whether this is for use in code generator + * annotations. Otherwise, it's for warnings. + */ + abstract String toStringHelper(boolean forAnnotations); + + /** + * Modify this type so that it matches the specified type. + * + * This is useful for reverse type-inference, where we want to + * infer that an object literal matches its constraint (much like + * how the java compiler does reverse-inference to figure out generics). + */ + public void matchConstraint(JSType constraint) {} +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/JSTypeNative.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/JSTypeNative.java new file mode 100644 index 0000000..b3de3b6 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/JSTypeNative.java @@ -0,0 +1,170 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +/** + * Constants corresponding to types that are built into a JavaScript engine + * and other types that occur very often in the type system. See + * {@link com.google.javascript.rhino.jstype.JSTypeRegistry#getNativeType(JSTypeNative)}. + */ +public enum JSTypeNative { + // Built-in types (please keep alphabetized) + + ARRAY_TYPE, + ARRAY_FUNCTION_TYPE, + + BOOLEAN_TYPE, + BOOLEAN_OBJECT_TYPE, + BOOLEAN_OBJECT_FUNCTION_TYPE, + + /** + * A checked unknown type is a type that we know something about, + * but we're not really sure what we know about it. + * + * Examples of checked unknown types include: + * + * if (x) { // x is unknown + * alert(x); // x is checked unknown + * } + * + * + * + * /* @param {SomeForwardDeclaredType} x / + * function f(x) { + * // x is checked unknown. We know it's some type, but the type + * // has not been included in this binary. + * } + * + * + * This is useful for missing property warnings, where we don't + * want to emit warnings on things that have been checked. + */ + CHECKED_UNKNOWN_TYPE, + + DATE_TYPE, + DATE_FUNCTION_TYPE, + + ERROR_FUNCTION_TYPE, + ERROR_TYPE, + + EVAL_ERROR_FUNCTION_TYPE, + EVAL_ERROR_TYPE, + + FUNCTION_FUNCTION_TYPE, + FUNCTION_INSTANCE_TYPE, // equivalent to U2U_CONSTRUCTOR_TYPE + FUNCTION_PROTOTYPE, + + NULL_TYPE, + + NUMBER_TYPE, + NUMBER_OBJECT_TYPE, + NUMBER_OBJECT_FUNCTION_TYPE, + + OBJECT_TYPE, + OBJECT_FUNCTION_TYPE, + OBJECT_PROTOTYPE, + + RANGE_ERROR_FUNCTION_TYPE, + RANGE_ERROR_TYPE, + + REFERENCE_ERROR_FUNCTION_TYPE, + REFERENCE_ERROR_TYPE, + + REGEXP_TYPE, + REGEXP_FUNCTION_TYPE, + + STRING_OBJECT_TYPE, + STRING_OBJECT_FUNCTION_TYPE, + STRING_TYPE, + + SYNTAX_ERROR_FUNCTION_TYPE, + SYNTAX_ERROR_TYPE, + + TYPE_ERROR_FUNCTION_TYPE, + TYPE_ERROR_TYPE, + + UNKNOWN_TYPE, + + URI_ERROR_FUNCTION_TYPE, + URI_ERROR_TYPE, + + VOID_TYPE, + + // Commonly used types + + TOP_LEVEL_PROTOTYPE, + STRING_VALUE_OR_OBJECT_TYPE, + NUMBER_VALUE_OR_OBJECT_TYPE, + ALL_TYPE, + NO_TYPE, + NO_OBJECT_TYPE, + NO_RESOLVED_TYPE, + GLOBAL_THIS, + U2U_CONSTRUCTOR_TYPE, + U2U_FUNCTION_TYPE, + + LEAST_FUNCTION_TYPE, + GREATEST_FUNCTION_TYPE, + + /** + * (null, void) + */ + NULL_VOID, + + /** + * (Object,number,string) + */ + OBJECT_NUMBER_STRING, + + /** + * (Object,number,string,boolean) + */ + OBJECT_NUMBER_STRING_BOOLEAN, + + /** + * (number,string,boolean) + */ + NUMBER_STRING_BOOLEAN, + + /** + * (number,string) + */ + NUMBER_STRING, +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/JSTypeRegistry.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/JSTypeRegistry.java new file mode 100644 index 0000000..c8fbac8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/JSTypeRegistry.java @@ -0,0 +1,1751 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.JSTypeNative.ALL_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.ARRAY_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NO_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.javascript.rhino.ErrorReporter; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.ScriptRuntime; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.RecordTypeBuilder.RecordProperty; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * The type registry is used to resolve named types. + * + *

      This class is not thread-safe. + * + */ +public class JSTypeRegistry implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * The UnionTypeBuilder caps the maximum number of alternate types it + * remembers and then defaults to "?" (unknown type). By default this max + * is 20, but it's very easy for the same property to appear on more than 20 + * types. Use larger unions for property checking. 3000 was picked + * semi-randomly for use by the Google+ FE project. + */ + private static final int PROPERTY_CHECKING_UNION_SIZE = 3000; + + // TODO(user): An instance of this class should be used during + // compilation. We also want to make all types' constructors package private + // and force usage of this registry instead. This will allow us to evolve the + // types without being tied by an open API. + + private final transient ErrorReporter reporter; + + // We use an Array instead of an immutable list because this lookup needs + // to be very fast. When it was an immutable list, we were spending 5% of + // CPU time on bounds checking inside get(). + private final JSType[] nativeTypes; + + private final Map namesToTypes; + + // Set of namespaces in which types (or other namespaces) exist. + private final Set namespaces = new HashSet(); + + // NOTE(nicksantos): This is a terrible terrible hack. When type expressions + // are evaluated, we need to be able to decide whether that type name + // resolves to a nullable type or a non-nullable type. Object types are + // nullable, but enum types are not. + // + // Notice that it's not good enough to just declare enum types sooner. + // For example, if we have + // /** @enum {MyObject} */ var MyEnum = ...; + // we won't be to declare "MyEnum" without evaluating the expression + // {MyObject}, and following those dependencies starts to lead us into + // undecidable territory. Instead, we "pre-declare" enum types and typedefs, + // so that the expression resolver can decide whether a given name is + // nullable or not. + private final Set nonNullableTypeNames = new HashSet(); + + // Types that have been "forward-declared." + // If these types are not declared anywhere in the binary, we shouldn't + // try to type-check them at all. + private final Set forwardDeclaredTypes = new HashSet(); + + // A map of properties to the types on which those properties have been + // declared. + private final Map typesIndexedByProperty = + Maps.newHashMap(); + + // A map of properties to each reference type on which those + // properties have been declared. Each type has a unique name used + // for de-duping. + private final Map> + eachRefTypeIndexedByProperty = Maps.newHashMap(); + + // A map of properties to the greatest subtype on which those properties have + // been declared. This is filled lazily from the types declared in + // typesIndexedByProperty. + private final Map greatestSubtypeByProperty = + Maps.newHashMap(); + + // A map from interface name to types that implement it. + private final Multimap interfaceToImplementors = + LinkedHashMultimap.create(); + + // All the unresolved named types. + private final Multimap, NamedType> unresolvedNamedTypes = + ArrayListMultimap.create(); + + // All the resolved named types. + private final Multimap, NamedType> resolvedNamedTypes = + ArrayListMultimap.create(); + + // NamedType warns about unresolved types in the last generation. + private boolean lastGeneration = true; + + // The template type name. + private Map templateTypes = Maps.newHashMap(); + + private final boolean tolerateUndefinedValues; + + /** + * The type registry has three modes, which control how type ASTs are + * converted to types in {@link #createFromTypeNodes}. + */ + public static enum ResolveMode { + /** + * Expressions are converted into Unknown blobs that can be + * resolved into complex types. + */ + LAZY_EXPRESSIONS, + + /** + * Expressions are evaluated. If any names in the expression point to + * unknown types, then we create a proxy {@code NamedType} structure + * until the type can be resolved. + * + * This is the legacy way of resolving ways, and may not exist in the + * future. + */ + LAZY_NAMES, + + /** + * Expressions and type names are evaluated aggressively. A warning + * will be emitted if a type name fails to resolve to a real type. + */ + IMMEDIATE + } + + private ResolveMode resolveMode = ResolveMode.LAZY_NAMES; + + /** + * Constructs a new type registry populated with the built-in types. + */ + public JSTypeRegistry(ErrorReporter reporter) { + this(reporter, false); + } + + /** + * Constructs a new type registry populated with the built-in types. + */ + public JSTypeRegistry( + ErrorReporter reporter, boolean tolerateUndefinedValues) { + this.reporter = reporter; + nativeTypes = new JSType[JSTypeNative.values().length]; + namesToTypes = new HashMap(); + resetForTypeCheck(); + this.tolerateUndefinedValues = tolerateUndefinedValues; + } + + /** + * Set the current resolving mode of the type registry. + * @see ResolveMode + */ + public void setResolveMode(ResolveMode mode) { + this.resolveMode = mode; + } + + ResolveMode getResolveMode() { + return resolveMode; + } + + public ErrorReporter getErrorReporter() { + return reporter; + } + + public boolean shouldTolerateUndefinedValues() { + return tolerateUndefinedValues; + } + + /** + * Reset to run the TypeCheck pass. + */ + public void resetForTypeCheck() { + typesIndexedByProperty.clear(); + eachRefTypeIndexedByProperty.clear(); + initializeBuiltInTypes(); + namesToTypes.clear(); + namespaces.clear(); + initializeRegistry(); + } + + private void initializeBuiltInTypes() { + // These locals shouldn't be all caps. + BooleanType BOOLEAN_TYPE = new BooleanType(this); + registerNativeType(JSTypeNative.BOOLEAN_TYPE, BOOLEAN_TYPE); + + NullType NULL_TYPE = new NullType(this); + registerNativeType(JSTypeNative.NULL_TYPE, NULL_TYPE); + + NumberType NUMBER_TYPE = new NumberType(this); + registerNativeType(JSTypeNative.NUMBER_TYPE, NUMBER_TYPE); + + StringType STRING_TYPE = new StringType(this); + registerNativeType(JSTypeNative.STRING_TYPE, STRING_TYPE); + + UnknownType UNKNOWN_TYPE = new UnknownType(this, false); + registerNativeType(JSTypeNative.UNKNOWN_TYPE, UNKNOWN_TYPE); + UnknownType checkedUnknownType = new UnknownType(this, true); + registerNativeType( + JSTypeNative.CHECKED_UNKNOWN_TYPE, checkedUnknownType); + + VoidType VOID_TYPE = new VoidType(this); + registerNativeType(JSTypeNative.VOID_TYPE, VOID_TYPE); + + AllType ALL_TYPE = new AllType(this); + registerNativeType(JSTypeNative.ALL_TYPE, ALL_TYPE); + + // Top Level Prototype (the One) + // The initializations of TOP_LEVEL_PROTOTYPE and OBJECT_FUNCTION_TYPE + // use each other's results, so at least one of them will get null + // instead of an actual type; however, this seems to be benign. + PrototypeObjectType TOP_LEVEL_PROTOTYPE = + new PrototypeObjectType(this, null, null, true, null, null); + registerNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE, TOP_LEVEL_PROTOTYPE); + + // Object + FunctionType OBJECT_FUNCTION_TYPE = + new FunctionType(this, "Object", null, + createArrowType(createOptionalParameters(ALL_TYPE), UNKNOWN_TYPE), + null, null, true, true); + + OBJECT_FUNCTION_TYPE.setPrototype(TOP_LEVEL_PROTOTYPE, null); + registerNativeType(JSTypeNative.OBJECT_FUNCTION_TYPE, OBJECT_FUNCTION_TYPE); + + ObjectType OBJECT_TYPE = OBJECT_FUNCTION_TYPE.getInstanceType(); + registerNativeType(JSTypeNative.OBJECT_TYPE, OBJECT_TYPE); + + ObjectType OBJECT_PROTOTYPE = OBJECT_FUNCTION_TYPE.getPrototype(); + registerNativeType(JSTypeNative.OBJECT_PROTOTYPE, OBJECT_PROTOTYPE); + + // Function + FunctionType FUNCTION_FUNCTION_TYPE = + new FunctionType(this, "Function", null, + createArrowType( + createParametersWithVarArgs(ALL_TYPE), UNKNOWN_TYPE), + null, null, true, true); + FUNCTION_FUNCTION_TYPE.setPrototypeBasedOn(OBJECT_TYPE); + registerNativeType( + JSTypeNative.FUNCTION_FUNCTION_TYPE, FUNCTION_FUNCTION_TYPE); + + ObjectType FUNCTION_PROTOTYPE = FUNCTION_FUNCTION_TYPE.getPrototype(); + registerNativeType(JSTypeNative.FUNCTION_PROTOTYPE, FUNCTION_PROTOTYPE); + + NoType NO_TYPE = new NoType(this); + registerNativeType(JSTypeNative.NO_TYPE, NO_TYPE); + + NoObjectType NO_OBJECT_TYPE = new NoObjectType(this); + registerNativeType(JSTypeNative.NO_OBJECT_TYPE, NO_OBJECT_TYPE); + + NoObjectType NO_RESOLVED_TYPE = new NoResolvedType(this); + registerNativeType(JSTypeNative.NO_RESOLVED_TYPE, NO_RESOLVED_TYPE); + + // Array + FunctionType ARRAY_FUNCTION_TYPE = + new FunctionType(this, "Array", null, + createArrowType(createParametersWithVarArgs(ALL_TYPE), null), + null, null, true, true); + ARRAY_FUNCTION_TYPE.getInternalArrowType().returnType = + ARRAY_FUNCTION_TYPE.getInstanceType(); + + ObjectType arrayPrototype = ARRAY_FUNCTION_TYPE.getPrototype(); + registerNativeType(JSTypeNative.ARRAY_FUNCTION_TYPE, ARRAY_FUNCTION_TYPE); + + ObjectType ARRAY_TYPE = ARRAY_FUNCTION_TYPE.getInstanceType(); + registerNativeType(JSTypeNative.ARRAY_TYPE, ARRAY_TYPE); + + // Boolean + FunctionType BOOLEAN_OBJECT_FUNCTION_TYPE = + new FunctionType(this, "Boolean", null, + createArrowType(createOptionalParameters(ALL_TYPE), BOOLEAN_TYPE), + null, null, true, true); + ObjectType booleanPrototype = BOOLEAN_OBJECT_FUNCTION_TYPE.getPrototype(); + registerNativeType( + JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE, + BOOLEAN_OBJECT_FUNCTION_TYPE); + + ObjectType BOOLEAN_OBJECT_TYPE = + BOOLEAN_OBJECT_FUNCTION_TYPE.getInstanceType(); + registerNativeType(JSTypeNative.BOOLEAN_OBJECT_TYPE, BOOLEAN_OBJECT_TYPE); + + // Date + FunctionType DATE_FUNCTION_TYPE = + new FunctionType(this, "Date", null, + createArrowType( + createOptionalParameters(UNKNOWN_TYPE, UNKNOWN_TYPE, UNKNOWN_TYPE, + UNKNOWN_TYPE, UNKNOWN_TYPE, UNKNOWN_TYPE, UNKNOWN_TYPE), + STRING_TYPE), + null, null, true, true); + ObjectType datePrototype = DATE_FUNCTION_TYPE.getPrototype(); + registerNativeType(JSTypeNative.DATE_FUNCTION_TYPE, DATE_FUNCTION_TYPE); + + ObjectType DATE_TYPE = DATE_FUNCTION_TYPE.getInstanceType(); + registerNativeType(JSTypeNative.DATE_TYPE, DATE_TYPE); + + // Error + FunctionType ERROR_FUNCTION_TYPE = new ErrorFunctionType(this, "Error"); + registerNativeType(JSTypeNative.ERROR_FUNCTION_TYPE, ERROR_FUNCTION_TYPE); + + ObjectType ERROR_TYPE = ERROR_FUNCTION_TYPE.getInstanceType(); + registerNativeType(JSTypeNative.ERROR_TYPE, ERROR_TYPE); + + // EvalError + FunctionType EVAL_ERROR_FUNCTION_TYPE = + new ErrorFunctionType(this, "EvalError"); + EVAL_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE); + registerNativeType( + JSTypeNative.EVAL_ERROR_FUNCTION_TYPE, EVAL_ERROR_FUNCTION_TYPE); + + ObjectType EVAL_ERROR_TYPE = EVAL_ERROR_FUNCTION_TYPE.getInstanceType(); + registerNativeType(JSTypeNative.EVAL_ERROR_TYPE, EVAL_ERROR_TYPE); + + // RangeError + FunctionType RANGE_ERROR_FUNCTION_TYPE = + new ErrorFunctionType(this, "RangeError"); + RANGE_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE); + registerNativeType( + JSTypeNative.RANGE_ERROR_FUNCTION_TYPE, RANGE_ERROR_FUNCTION_TYPE); + + ObjectType RANGE_ERROR_TYPE = RANGE_ERROR_FUNCTION_TYPE.getInstanceType(); + registerNativeType(JSTypeNative.RANGE_ERROR_TYPE, RANGE_ERROR_TYPE); + + // ReferenceError + FunctionType REFERENCE_ERROR_FUNCTION_TYPE = + new ErrorFunctionType(this, "ReferenceError"); + REFERENCE_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE); + registerNativeType( + JSTypeNative.REFERENCE_ERROR_FUNCTION_TYPE, + REFERENCE_ERROR_FUNCTION_TYPE); + + ObjectType REFERENCE_ERROR_TYPE = + REFERENCE_ERROR_FUNCTION_TYPE.getInstanceType(); + registerNativeType(JSTypeNative.REFERENCE_ERROR_TYPE, REFERENCE_ERROR_TYPE); + + // SyntaxError + FunctionType SYNTAX_ERROR_FUNCTION_TYPE = + new ErrorFunctionType(this, "SyntaxError"); + SYNTAX_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE); + registerNativeType( + JSTypeNative.SYNTAX_ERROR_FUNCTION_TYPE, SYNTAX_ERROR_FUNCTION_TYPE); + + ObjectType SYNTAX_ERROR_TYPE = SYNTAX_ERROR_FUNCTION_TYPE.getInstanceType(); + registerNativeType(JSTypeNative.SYNTAX_ERROR_TYPE, SYNTAX_ERROR_TYPE); + + // TypeError + FunctionType TYPE_ERROR_FUNCTION_TYPE = + new ErrorFunctionType(this, "TypeError"); + TYPE_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE); + registerNativeType( + JSTypeNative.TYPE_ERROR_FUNCTION_TYPE, TYPE_ERROR_FUNCTION_TYPE); + + ObjectType TYPE_ERROR_TYPE = TYPE_ERROR_FUNCTION_TYPE.getInstanceType(); + registerNativeType(JSTypeNative.TYPE_ERROR_TYPE, TYPE_ERROR_TYPE); + + // URIError + FunctionType URI_ERROR_FUNCTION_TYPE = + new ErrorFunctionType(this, "URIError"); + URI_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE); + registerNativeType( + JSTypeNative.URI_ERROR_FUNCTION_TYPE, URI_ERROR_FUNCTION_TYPE); + + ObjectType URI_ERROR_TYPE = URI_ERROR_FUNCTION_TYPE.getInstanceType(); + registerNativeType(JSTypeNative.URI_ERROR_TYPE, URI_ERROR_TYPE); + + // Number + FunctionType NUMBER_OBJECT_FUNCTION_TYPE = + new FunctionType(this, "Number", null, + createArrowType(createOptionalParameters(ALL_TYPE), NUMBER_TYPE), + null, null, true, true); + ObjectType numberPrototype = NUMBER_OBJECT_FUNCTION_TYPE.getPrototype(); + registerNativeType( + JSTypeNative.NUMBER_OBJECT_FUNCTION_TYPE, NUMBER_OBJECT_FUNCTION_TYPE); + + ObjectType NUMBER_OBJECT_TYPE = + NUMBER_OBJECT_FUNCTION_TYPE.getInstanceType(); + registerNativeType(JSTypeNative.NUMBER_OBJECT_TYPE, NUMBER_OBJECT_TYPE); + + // RegExp + FunctionType REGEXP_FUNCTION_TYPE = + new FunctionType(this, "RegExp", null, + createArrowType(createOptionalParameters(ALL_TYPE, ALL_TYPE)), + null, null, true, true); + REGEXP_FUNCTION_TYPE.getInternalArrowType().returnType = + REGEXP_FUNCTION_TYPE.getInstanceType(); + + ObjectType regexpPrototype = REGEXP_FUNCTION_TYPE.getPrototype(); + registerNativeType(JSTypeNative.REGEXP_FUNCTION_TYPE, REGEXP_FUNCTION_TYPE); + + ObjectType REGEXP_TYPE = REGEXP_FUNCTION_TYPE.getInstanceType(); + registerNativeType(JSTypeNative.REGEXP_TYPE, REGEXP_TYPE); + + // String + FunctionType STRING_OBJECT_FUNCTION_TYPE = + new FunctionType(this, "String", null, + createArrowType(createOptionalParameters(ALL_TYPE), STRING_TYPE), + null, null, true, true); + ObjectType stringPrototype = STRING_OBJECT_FUNCTION_TYPE.getPrototype(); + registerNativeType( + JSTypeNative.STRING_OBJECT_FUNCTION_TYPE, STRING_OBJECT_FUNCTION_TYPE); + + ObjectType STRING_OBJECT_TYPE = + STRING_OBJECT_FUNCTION_TYPE.getInstanceType(); + registerNativeType( + JSTypeNative.STRING_OBJECT_TYPE, STRING_OBJECT_TYPE); + + // (null,void) + JSType NULL_VOID = + createUnionType(NULL_TYPE, VOID_TYPE); + registerNativeType(JSTypeNative.NULL_VOID, NULL_VOID); + + // (Object,string,number) + JSType OBJECT_NUMBER_STRING = + createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE); + registerNativeType(JSTypeNative.OBJECT_NUMBER_STRING, OBJECT_NUMBER_STRING); + + // (Object,string,number,boolean) + JSType OBJECT_NUMBER_STRING_BOOLEAN = + createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE); + registerNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN, + OBJECT_NUMBER_STRING_BOOLEAN); + + // (string,number,boolean) + JSType NUMBER_STRING_BOOLEAN = + createUnionType(NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE); + registerNativeType(JSTypeNative.NUMBER_STRING_BOOLEAN, + NUMBER_STRING_BOOLEAN); + + // (string,number) + JSType NUMBER_STRING = createUnionType(NUMBER_TYPE, STRING_TYPE); + registerNativeType(JSTypeNative.NUMBER_STRING, NUMBER_STRING); + + // Native object properties are filled in by externs... + + // (String, string) + JSType STRING_VALUE_OR_OBJECT_TYPE = + createUnionType(STRING_OBJECT_TYPE, STRING_TYPE); + registerNativeType( + JSTypeNative.STRING_VALUE_OR_OBJECT_TYPE, STRING_VALUE_OR_OBJECT_TYPE); + + // (Number, number) + JSType NUMBER_VALUE_OR_OBJECT_TYPE = + createUnionType(NUMBER_OBJECT_TYPE, NUMBER_TYPE); + registerNativeType( + JSTypeNative.NUMBER_VALUE_OR_OBJECT_TYPE, NUMBER_VALUE_OR_OBJECT_TYPE); + + // unknown function type, i.e. (?...) -> ? + FunctionType U2U_FUNCTION_TYPE = + createFunctionType(UNKNOWN_TYPE, true, UNKNOWN_TYPE); + registerNativeType(JSTypeNative.U2U_FUNCTION_TYPE, U2U_FUNCTION_TYPE); + + // unknown constructor type, i.e. (?...) -> ? with the Unknown type + // as instance type + FunctionType U2U_CONSTRUCTOR_TYPE = + // This is equivalent to + // createConstructorType(UNKNOWN_TYPE, true, UNKNOWN_TYPE), but, + // in addition, overrides getInstanceType() to return the NoObject type + // instead of a new anonymous object. + new FunctionType(this, "Function", null, + createArrowType( + createParametersWithVarArgs(UNKNOWN_TYPE), + UNKNOWN_TYPE), + UNKNOWN_TYPE, null, true, true) { + private static final long serialVersionUID = 1L; + + @Override public FunctionType getConstructor() { + return registry.getNativeFunctionType( + JSTypeNative.FUNCTION_FUNCTION_TYPE); + } + }; + + // The U2U_CONSTRUCTOR is weird, because it's the supertype of its + // own constructor. + registerNativeType(JSTypeNative.U2U_CONSTRUCTOR_TYPE, U2U_CONSTRUCTOR_TYPE); + registerNativeType( + JSTypeNative.FUNCTION_INSTANCE_TYPE, U2U_CONSTRUCTOR_TYPE); + + FUNCTION_FUNCTION_TYPE.setInstanceType(U2U_CONSTRUCTOR_TYPE); + U2U_CONSTRUCTOR_TYPE.setImplicitPrototype(FUNCTION_PROTOTYPE); + + // least function type, i.e. (All...) -> NoType + FunctionType LEAST_FUNCTION_TYPE = + createNativeFunctionTypeWithVarArgs(NO_TYPE, ALL_TYPE); + registerNativeType(JSTypeNative.LEAST_FUNCTION_TYPE, LEAST_FUNCTION_TYPE); + + // the 'this' object in the global scope + FunctionType GLOBAL_THIS_CTOR = + new FunctionType(this, "global this", null, + createArrowType(createParameters(false, ALL_TYPE), NUMBER_TYPE), + null, null, true, true); + ObjectType GLOBAL_THIS = GLOBAL_THIS_CTOR.getInstanceType(); + registerNativeType(JSTypeNative.GLOBAL_THIS, GLOBAL_THIS); + + // greatest function type, i.e. (NoType...) -> All + FunctionType GREATEST_FUNCTION_TYPE = + createNativeFunctionTypeWithVarArgs(ALL_TYPE, NO_TYPE); + registerNativeType(JSTypeNative.GREATEST_FUNCTION_TYPE, + GREATEST_FUNCTION_TYPE); + + // Register the prototype property. See the comments below in + // registerPropertyOnType about the bootstrapping process. + registerPropertyOnType("prototype", OBJECT_FUNCTION_TYPE); + } + + private void initializeRegistry() { + register(getNativeType(JSTypeNative.ARRAY_TYPE)); + register(getNativeType(JSTypeNative.BOOLEAN_OBJECT_TYPE)); + register(getNativeType(JSTypeNative.BOOLEAN_TYPE)); + register(getNativeType(JSTypeNative.DATE_TYPE)); + register(getNativeType(JSTypeNative.NULL_TYPE)); + register(getNativeType(JSTypeNative.NULL_TYPE), "Null"); + register(getNativeType(JSTypeNative.NUMBER_OBJECT_TYPE)); + register(getNativeType(JSTypeNative.NUMBER_TYPE)); + register(getNativeType(JSTypeNative.OBJECT_TYPE)); + register(getNativeType(JSTypeNative.ERROR_TYPE)); + register(getNativeType(JSTypeNative.URI_ERROR_TYPE)); + register(getNativeType(JSTypeNative.EVAL_ERROR_TYPE)); + register(getNativeType(JSTypeNative.TYPE_ERROR_TYPE)); + register(getNativeType(JSTypeNative.RANGE_ERROR_TYPE)); + register(getNativeType(JSTypeNative.REFERENCE_ERROR_TYPE)); + register(getNativeType(JSTypeNative.SYNTAX_ERROR_TYPE)); + register(getNativeType(JSTypeNative.REGEXP_TYPE)); + register(getNativeType(JSTypeNative.STRING_OBJECT_TYPE)); + register(getNativeType(JSTypeNative.STRING_TYPE)); + register(getNativeType(JSTypeNative.VOID_TYPE)); + register(getNativeType(JSTypeNative.VOID_TYPE), "Undefined"); + register(getNativeType(JSTypeNative.VOID_TYPE), "void"); + register(getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), "Function"); + } + + private void register(JSType type) { + register(type, type.toString()); + } + + private void register(JSType type, String name) { + Preconditions.checkArgument( + !name.contains("<"), "Type names cannot contain template annotations."); + + namesToTypes.put(name, type); + + // Add all the namespaces in which this name lives. + while (name.indexOf('.') > 0) { + name = name.substring(0, name.lastIndexOf('.')); + namespaces.add(name); + } + } + + private void registerNativeType(JSTypeNative typeId, JSType type) { + nativeTypes[typeId.ordinal()] = type; + } + + /** + * Tells the type system that {@code owner} may have a property named + * {@code propertyName}. This allows the registry to keep track of what + * types a property is defined upon. + * + * This is NOT the same as saying that {@code owner} must have a property + * named type. ObjectType#hasProperty attempts to minimize false positives + * ("if we're not sure, then don't type check this property"). The type + * registry, on the other hand, should attempt to minimize false negatives + * ("if this property is assigned anywhere in the program, it must + * show up in the type registry"). + */ + public void registerPropertyOnType(String propertyName, JSType type) { + UnionTypeBuilder typeSet = typesIndexedByProperty.get(propertyName); + if (typeSet == null) { + typeSet = new UnionTypeBuilder(this, PROPERTY_CHECKING_UNION_SIZE); + typesIndexedByProperty.put(propertyName, typeSet); + } + + typeSet.addAlternate(type); + addReferenceTypeIndexedByProperty(propertyName, type); + + // Clear cached values that depend on typesIndexedByProperty. + greatestSubtypeByProperty.remove(propertyName); + } + + private void addReferenceTypeIndexedByProperty( + String propertyName, JSType type) { + if (type instanceof ObjectType && ((ObjectType) type).hasReferenceName()) { + Map typeSet = + eachRefTypeIndexedByProperty.get(propertyName); + if (typeSet == null) { + typeSet = Maps.newHashMap(); + eachRefTypeIndexedByProperty.put(propertyName, typeSet); + } + ObjectType objType = (ObjectType) type; + typeSet.put(objType.getReferenceName(), objType); + } else if (type instanceof NamedType) { + addReferenceTypeIndexedByProperty( + propertyName, ((NamedType) type).getReferencedType()); + } else if (type.isUnionType()) { + for (JSType alternate : type.toMaybeUnionType().getAlternates()) { + addReferenceTypeIndexedByProperty(propertyName, alternate); + } + } + } + + /** + * Removes the index's reference to a property on the given type (if it is + * currently registered). If the property is not registered on the type yet, + * this method will not change internal state. + * + * @param propertyName the name of the property to unregister + * @param type the type to unregister the property on. + */ + public void unregisterPropertyOnType(String propertyName, JSType type) { + // TODO(bashir): typesIndexedByProperty should also be updated! + Map typeSet = + eachRefTypeIndexedByProperty.get(propertyName); + if (typeSet != null) { + typeSet.remove(type.toObjectType().getReferenceName()); + } + } + + /** + * Gets the greatest subtype of the {@code type} that has a property + * {@code propertyName} defined on it. + */ + public JSType getGreatestSubtypeWithProperty( + JSType type, String propertyName) { + if (greatestSubtypeByProperty.containsKey(propertyName)) { + return greatestSubtypeByProperty.get(propertyName) + .getGreatestSubtype(type); + } + if (typesIndexedByProperty.containsKey(propertyName)) { + JSType built = typesIndexedByProperty.get(propertyName).build(); + greatestSubtypeByProperty.put(propertyName, built); + return built.getGreatestSubtype(type); + } + return getNativeType(NO_TYPE); + } + + /** + * Returns whether the given property can possibly be set on the given type. + */ + public boolean canPropertyBeDefined(JSType type, String propertyName) { + if (typesIndexedByProperty.containsKey(propertyName)) { + for (JSType alt : + typesIndexedByProperty.get(propertyName).getAlternates()) { + JSType greatestSubtype = alt.getGreatestSubtype(type); + if (!greatestSubtype.isEmptyType()) { + // We've found a type with this property. Now we just have to make + // sure it's not a type used for internal bookkeeping. + RecordType maybeRecordType = greatestSubtype.toMaybeRecordType(); + if (maybeRecordType != null && maybeRecordType.isSynthetic()) { + continue; + } + + return true; + } + } + } + return false; + } + + /** + * Returns each type that has a property {@code propertyName} defined on it. + * + * Like most types in our type system, the collection of types returned + * will be collapsed. This means that if a type is defined on + * {@code Object} and on {@code Array}, it would be reasonable for this + * method to return either {@code [Object, Array]} or just {@code [Object]}. + */ + public Iterable getTypesWithProperty(String propertyName) { + if (typesIndexedByProperty.containsKey(propertyName)) { + return typesIndexedByProperty.get(propertyName).getAlternates(); + } else { + return ImmutableList.of(); + } + } + + /** + * Returns each reference type that has a property {@code propertyName} + * defined on it. + * + * Unlike most types in our type system, the collection of types returned + * will not be collapsed. This means that if a type is defined on + * {@code Object} and on {@code Array}, this method must return + * {@code [Object, Array]}. It would not be correct to collapse them to + * {@code [Object]}. + */ + public Iterable getEachReferenceTypeWithProperty( + String propertyName) { + if (eachRefTypeIndexedByProperty.containsKey(propertyName)) { + return eachRefTypeIndexedByProperty.get(propertyName).values(); + } else { + return ImmutableList.of(); + } + } + + /** + * Finds the common supertype of the two given object types. + */ + ObjectType findCommonSuperObject(ObjectType a, ObjectType b) { + List stackA = getSuperStack(a); + List stackB = getSuperStack(b); + + ObjectType result = getNativeObjectType(JSTypeNative.OBJECT_TYPE); + while (!stackA.isEmpty() && !stackB.isEmpty()) { + ObjectType currentA = stackA.remove(stackA.size() - 1); + ObjectType currentB = stackB.remove(stackB.size() - 1); + if (currentA.isEquivalentTo(currentB)) { + result = currentA; + } else { + return result; + } + } + return result; + } + + private static List getSuperStack(ObjectType a) { + List stack = Lists.newArrayListWithExpectedSize(5); + for (ObjectType current = a; + current != null; + current = current.getImplicitPrototype()) { + stack.add(current); + } + return stack; + } + + /** + * Increments the current generation. Clients must call this in order to + * move to the next generation of type resolution, allowing types to attempt + * resolution again. + */ + public void incrementGeneration() { + for (NamedType type : resolvedNamedTypes.values()) { + type.clearResolved(); + } + unresolvedNamedTypes.putAll(resolvedNamedTypes); + resolvedNamedTypes.clear(); + } + + boolean isLastGeneration() { + return lastGeneration; + } + + /** + * Sets whether this is the last generation. In the last generation, + * {@link NamedType} warns about unresolved types. + */ + public void setLastGeneration(boolean lastGeneration) { + this.lastGeneration = lastGeneration; + } + + /** + * Tells the type system that {@code type} implements interface {@code + * interfaceInstance}. + * {@code inter} must be an ObjectType for the instance of the interface as it + * could be a named type and not yet have the constructor. + */ + void registerTypeImplementingInterface( + FunctionType type, ObjectType interfaceInstance) { + interfaceToImplementors.put(interfaceInstance.getReferenceName(), type); + } + + /** + * Returns a collection of types that directly implement {@code + * interfaceInstance}. Subtypes of implementing types are not guaranteed to + * be returned. {@code interfaceInstance} must be an ObjectType for the + * instance of the interface. + */ + public Collection getDirectImplementors( + ObjectType interfaceInstance) { + return interfaceToImplementors.get(interfaceInstance.getReferenceName()); + } + + /** + * Records declared global type names. This makes resolution faster + * and more robust in the common case. + * + * @param name The name of the type to be recorded. + * @param t The actual type being associated with the name. + * @return True if this name is not already defined, false otherwise. + */ + public boolean declareType(String name, JSType t) { + if (namesToTypes.containsKey(name)) { + return false; + } + register(t, name); + return true; + } + + /** + * Overrides a declared global type name. Throws an exception if this + * type name hasn't been declared yet. + */ + public void overwriteDeclaredType(String name, JSType t) { + Preconditions.checkState(namesToTypes.containsKey(name)); + register(t, name); + } + + /** + * Records a forward-declared type name. We will not emit errors if this + * type name never resolves to anything. + */ + public void forwardDeclareType(String name) { + forwardDeclaredTypes.add(name); + } + + /** + * Whether this is a forward-declared type name. + */ + public boolean isForwardDeclaredType(String name) { + return forwardDeclaredTypes.contains(name); + } + + /** Determines whether the given JS package exists. */ + public boolean hasNamespace(String name) { + return namespaces.contains(name); + } + + /** + * Looks up a type by name. + * + * @param jsTypeName The name string. + * @return the corresponding JSType object or {@code null} it cannot be found + */ + public JSType getType(String jsTypeName) { + // TODO(user): Push every local type name out of namesToTypes so that + // NamedType#resolve is correct. + TemplateType templateType = templateTypes.get(jsTypeName); + if (templateType != null) { + return templateType; + } + return namesToTypes.get(jsTypeName); + } + + public JSType getNativeType(JSTypeNative typeId) { + return nativeTypes[typeId.ordinal()]; + } + + public ObjectType getNativeObjectType(JSTypeNative typeId) { + return (ObjectType) getNativeType(typeId); + } + + public FunctionType getNativeFunctionType(JSTypeNative typeId) { + return (FunctionType) getNativeType(typeId); + } + + /** + * Looks up a type by name. To allow for forward references to types, an + * unrecognized string has to be bound to a NamedType object that will be + * resolved later. + * + * @param scope A scope for doing type name resolution. + * @param jsTypeName The name string. + * @param sourceName The name of the source file where this reference appears. + * @param lineno The line number of the reference. + * @return a NamedType if the string argument is not one of the known types, + * otherwise the corresponding JSType object. + */ + public JSType getType(StaticScope scope, String jsTypeName, + String sourceName, int lineno, int charno) { + JSType type = getType(jsTypeName); + if (type == null) { + // TODO(user): Each instance should support named type creation using + // interning. + NamedType namedType = + new NamedType(this, jsTypeName, sourceName, lineno, charno); + unresolvedNamedTypes.put(scope, namedType); + type = namedType; + } + return type; + } + + /** + * Flushes out the current resolved and unresolved Named Types from + * the type registry. This is intended to be used ONLY before a + * compile is run. + */ + public void clearNamedTypes() { + resolvedNamedTypes.clear(); + unresolvedNamedTypes.clear(); + } + + /** + * Resolve all the unresolved types in the given scope. + */ + public void resolveTypesInScope(StaticScope scope) { + for (NamedType type : unresolvedNamedTypes.get(scope)) { + type.resolve(reporter, scope); + } + + resolvedNamedTypes.putAll(scope, unresolvedNamedTypes.removeAll(scope)); + + if (scope != null && scope.getParentScope() == null) { + // By default, the global "this" type is just an anonymous object. + // If the user has defined a Window type, make the Window the + // implicit prototype of "this". + PrototypeObjectType globalThis = (PrototypeObjectType) getNativeType( + JSTypeNative.GLOBAL_THIS); + JSType windowType = getType("Window"); + if (globalThis.isUnknownType()) { + ObjectType windowObjType = ObjectType.cast(windowType); + if (windowObjType != null) { + globalThis.setImplicitPrototype(windowObjType); + } else { + globalThis.setImplicitPrototype( + getNativeObjectType(JSTypeNative.OBJECT_TYPE)); + } + } + } + } + + /** + * Creates a type representing optional values of the given type. + * @return the union of the type and the void type + */ + public JSType createOptionalType(JSType type) { + if (type instanceof UnknownType || type.isAllType()) { + return type; + } else { + return createUnionType(type, getNativeType(JSTypeNative.VOID_TYPE)); + } + } + + /** + * Creates a type representing nullable values of the given type. + * @return the union of the type and the Null type + */ + public JSType createDefaultObjectUnion(JSType type) { + if (type.isTemplateType()) { + // Template types represent the substituted type exactly and should + // not be wrapped. + return type; + } else { + return shouldTolerateUndefinedValues() + ? createOptionalNullableType(type) + : createNullableType(type); + } + } + + /** + * Creates a type representing nullable values of the given type. + * @return the union of the type and the Null type + */ + public JSType createNullableType(JSType type) { + return createUnionType(type, getNativeType(JSTypeNative.NULL_TYPE)); + } + + /** + * Creates a nullable and undefine-able value of the given type. + * @return The union of the type and null and undefined. + */ + public JSType createOptionalNullableType(JSType type) { + return createUnionType(type, getNativeType(JSTypeNative.VOID_TYPE), + getNativeType(JSTypeNative.NULL_TYPE)); + } + + /** + * Creates a union type whose variants are the arguments. + */ + public JSType createUnionType(JSType... variants) { + UnionTypeBuilder builder = new UnionTypeBuilder(this); + for (JSType type : variants) { + builder.addAlternate(type); + } + return builder.build(); + } + + /** + * Creates a union type whose variants are the built-in types specified + * by the arguments. + */ + public JSType createUnionType(JSTypeNative... variants) { + UnionTypeBuilder builder = new UnionTypeBuilder(this); + for (JSTypeNative typeId : variants) { + builder.addAlternate(getNativeType(typeId)); + } + return builder.build(); + } + + /** + * Creates an enum type. + */ + public EnumType createEnumType( + String name, Node source, JSType elementsType) { + return new EnumType(this, name, source, elementsType); + } + + /** + * Creates an arrow type, an abstract representation of the parameters + * and return value of a function. + * + * @param parametersNode the parameters' types, formatted as a Node with + * param names and optionality info. + * @param returnType the function's return type + */ + ArrowType createArrowType(Node parametersNode, JSType returnType) { + return new ArrowType(this, parametersNode, returnType); + } + + /** + * Creates an arrow type with an unknown return type. + * + * @param parametersNode the parameters' types, formatted as a Node with + * param names and optionality info. + */ + ArrowType createArrowType(Node parametersNode) { + return new ArrowType(this, parametersNode, null); + } + + /** + * Creates a function type. + * + * @param returnType the function's return type + * @param parameterTypes the parameters' types + */ + public FunctionType createFunctionType( + JSType returnType, JSType... parameterTypes) { + return createFunctionType(returnType, createParameters(parameterTypes)); + } + + /** + * Creates a function type. The last parameter type of the function is + * considered a variable length argument. + * + * @param returnType the function's return type + * @param parameterTypes the parameters' types + */ + public FunctionType createFunctionTypeWithVarArgs( + JSType returnType, List parameterTypes) { + return createFunctionType( + returnType, createParametersWithVarArgs(parameterTypes)); + } + + /** + * Creates a function type. + * + * @param returnType the function's return type + * @param parameterTypes the parameters' types + */ + public FunctionType createFunctionType( + JSType returnType, List parameterTypes) { + return createFunctionType(returnType, createParameters(parameterTypes)); + } + + /** + * Creates a function type. The last parameter type of the function is + * considered a variable length argument. + * + * @param returnType the function's return type + * @param parameterTypes the parameters' types + */ + public FunctionType createFunctionTypeWithVarArgs( + JSType returnType, JSType... parameterTypes) { + return createFunctionType( + returnType, createParametersWithVarArgs(parameterTypes)); + } + + /** + * Creates a function type. The last parameter type of the function is + * considered a variable length argument. + * + * @param returnType the function's return type + * @param parameterTypes the parameters' types + */ + private FunctionType createNativeFunctionTypeWithVarArgs( + JSType returnType, JSType... parameterTypes) { + return createNativeFunctionType( + returnType, createParametersWithVarArgs(parameterTypes)); + } + + /** + * Creates a function type which can act as a constructor. + * + * @param returnType the function's return type + * @param parameterTypes the parameters' types + */ + public FunctionType createConstructorType( + JSType returnType, JSType... parameterTypes) { + return createConstructorType( + null, null, createParameters(parameterTypes), returnType, null); + } + + /** + * Creates a function type which can act as a constructor. The last + * parameter type of the constructor is considered a variable length argument. + * + * @param returnType the function's return type + * @param parameterTypes the parameters' types + */ + private FunctionType createConstructorTypeWithVarArgs( + JSType returnType, JSType... parameterTypes) { + return createConstructorType( + null, null, createParametersWithVarArgs(parameterTypes), returnType, + null); + } + + /** + * Creates a function type in which {@code this} refers to an object instance. + * + * @param instanceType the type of {@code this} + * @param returnType the function's return type + * @param parameterTypes the parameters' types + */ + public JSType createFunctionType(ObjectType instanceType, + JSType returnType, List parameterTypes) { + return new FunctionBuilder(this) + .withParamsNode(createParameters(parameterTypes)) + .withReturnType(returnType) + .withTypeOfThis(instanceType) + .build(); + } + + /** + * Creates a function type in which {@code this} refers to an object instance. + * The last parameter type of the function is considered a variable length + * argument. + * + * @param instanceType the type of {@code this} + * @param returnType the function's return type + * @param parameterTypes the parameters' types + */ + public JSType createFunctionTypeWithVarArgs(ObjectType instanceType, + JSType returnType, List parameterTypes) { + return new FunctionBuilder(this) + .withParamsNode(createParametersWithVarArgs(parameterTypes)) + .withReturnType(returnType) + .withTypeOfThis(instanceType) + .build(); + } + + /** + * Creates a tree hierarchy representing a typed argument list. + * + * @param parameterTypes the parameter types. + * @return a tree hierarchy representing a typed argument list. + */ + public Node createParameters(List parameterTypes) { + return createParameters( + parameterTypes.toArray(new JSType[parameterTypes.size()])); + } + + /** + * Creates a tree hierarchy representing a typed argument list. The last + * parameter type is considered a variable length argument. + * + * @param parameterTypes the parameter types. The last element of this array + * is considered a variable length argument. + * @return a tree hierarchy representing a typed argument list. + */ + public Node createParametersWithVarArgs(List parameterTypes) { + return createParametersWithVarArgs( + parameterTypes.toArray(new JSType[parameterTypes.size()])); + } + + /** + * Creates a tree hierarchy representing a typed argument list. + * + * @param parameterTypes the parameter types. + * @return a tree hierarchy representing a typed argument list. + */ + public Node createParameters(JSType... parameterTypes) { + return createParameters(false, parameterTypes); + } + + /** + * Creates a tree hierarchy representing a typed argument list. The last + * parameter type is considered a variable length argument. + * + * @param parameterTypes the parameter types. The last element of this array + * is considered a variable length argument. + * @return a tree hierarchy representing a typed argument list. + */ + public Node createParametersWithVarArgs(JSType... parameterTypes) { + return createParameters(true, parameterTypes); + } + + /** + * Creates a tree hierarchy representing a typed parameter list in which + * every parameter is optional. + */ + public Node createOptionalParameters(JSType... parameterTypes) { + FunctionParamBuilder builder = new FunctionParamBuilder(this); + builder.addOptionalParams(parameterTypes); + return builder.build(); + } + + /** + * Creates a tree hierarchy representing a typed argument list. + * + * @param lastVarArgs whether the last type should considered as a variable + * length argument. + * @param parameterTypes the parameter types. The last element of this array + * is considered a variable length argument is {@code lastVarArgs} is + * {@code true}. + * @return a tree hierarchy representing a typed argument list + */ + private Node createParameters(boolean lastVarArgs, JSType... parameterTypes) { + FunctionParamBuilder builder = new FunctionParamBuilder(this); + int max = parameterTypes.length - 1; + for (int i = 0; i <= max; i++) { + if (lastVarArgs && i == max) { + builder.addVarArgs(parameterTypes[i]); + } else { + builder.addRequiredParams(parameterTypes[i]); + } + } + return builder.build(); + } + + /** + * Creates a function type. + * @param returnType the function's return type + * @param lastVarArgs whether the last parameter type should be considered as + * an extensible var_args parameter + * @param parameterTypes the parameters' types + */ + public FunctionType createFunctionType(JSType returnType, + boolean lastVarArgs, JSType... parameterTypes) { + if (lastVarArgs) { + return createFunctionTypeWithVarArgs(returnType, parameterTypes); + } else { + return createFunctionType(returnType, parameterTypes); + } + } + + /** + * Creates a new function type based on an existing function type but + * with a new return type. + * @param existingFunctionType the existing function type. + * @param returnType the new return type. + */ + public FunctionType createFunctionTypeWithNewReturnType( + FunctionType existingFunctionType, JSType returnType) { + return new FunctionBuilder(this) + .copyFromOtherFunction(existingFunctionType) + .withReturnType(returnType) + .build(); + } + + /** + * Creates a new function type based on an existing function type but + * with a new {@code this} type. + * @param existingFunctionType the existing function type. + * @param thisType the new this type. + */ + public FunctionType createFunctionTypeWithNewThisType( + FunctionType existingFunctionType, ObjectType thisType) { + return new FunctionBuilder(this) + .copyFromOtherFunction(existingFunctionType) + .withTypeOfThis(thisType) + .build(); + } + + /** + * @param parameters the function's parameters or {@code null} + * to indicate that the parameter types are unknown. + * @param returnType the function's return type or {@code null} to indicate + * that the return type is unknown. + */ + public FunctionType createFunctionType( + JSType returnType, Node parameters) { + return new FunctionBuilder(this) + .withParamsNode(parameters) + .withReturnType(returnType) + .build(); + } + + private FunctionType createNativeFunctionType( + JSType returnType, Node parameters) { + return new FunctionBuilder(this) + .withParamsNode(parameters) + .withReturnType(returnType) + .forNativeType() + .build(); + } + + /** + * Creates a function type which can act as a constructor. + * @param returnType the function's return type + * @param lastVarArgs whether the last parameter type should be considered as + * an extensible var_args parameter + * @param parameterTypes the parameters' types + */ + public FunctionType createConstructorType(JSType returnType, + boolean lastVarArgs, JSType... parameterTypes) { + if (lastVarArgs) { + return createConstructorTypeWithVarArgs(returnType, parameterTypes); + } else { + return createConstructorType(returnType, parameterTypes); + } + } + + /** + * Create an object type. + */ + public ObjectType createObjectType(ObjectType implicitPrototype) { + return createObjectType(null, null, implicitPrototype); + } + + /** + * Creates a record type. + */ + public RecordType createRecordType(Map properties) { + return new RecordType(this, properties); + } + + /** + * Create an object type. + */ + public ObjectType createObjectType(String name, Node n, + ObjectType implicitPrototype) { + return new PrototypeObjectType(this, name, implicitPrototype); + } + + /** + * Create an anonymous object type. + * @param info Used to mark object literals as structs; can be {@code null} + */ + public ObjectType createAnonymousObjectType(JSDocInfo info) { + PrototypeObjectType type = new PrototypeObjectType(this, null, null); + type.setPrettyPrint(true); + type.setJSDocInfo(info); + return type; + } + + /** + * Set the implicit prototype if it's possible to do so. + * @return True if we were able to set the implicit prototype successfully, + * false if it was not possible to do so for some reason. There are + * a few different reasons why this could fail: for example, numbers + * can't be implicit prototypes, and we don't want to change the implicit + * prototype if other classes have already subclassed this one. + */ + public boolean resetImplicitPrototype( + JSType type, ObjectType newImplicitProto) { + if (type instanceof PrototypeObjectType) { + PrototypeObjectType poType = (PrototypeObjectType) type; + poType.clearCachedValues(); + poType.setImplicitPrototype(newImplicitProto); + return true; + } + return false; + } + + /** + * Create an anonymous object type for a native type. + */ + ObjectType createNativeAnonymousObjectType() { + PrototypeObjectType type = + new PrototypeObjectType(this, null, null, true, null, null); + type.setPrettyPrint(true); + return type; + } + + /** + * Creates a constructor function type. + * @param name the function's name or {@code null} to indicate that the + * function is anonymous. + * @param source the node defining this function. Its type + * ({@link Node#getType()}) must be {@link Token#FUNCTION}. + * @param parameters the function's parameters or {@code null} + * to indicate that the parameter types are unknown. + * @param returnType the function's return type or {@code null} to indicate + * that the return type is unknown. + * @param templateKeys the templatized type keys for the class. + */ + public FunctionType createConstructorType(String name, Node source, + Node parameters, JSType returnType, ImmutableList templateKeys) { + return new FunctionType(this, name, source, + createArrowType(parameters, returnType), null, + templateKeys, true, false); + } + + /** + * Creates an interface function type. + * @param name the function's name + * @param source the node defining this function. Its type + * ({@link Node#getType()}) must be {@link Token#FUNCTION}. + */ + public FunctionType createInterfaceType(String name, Node source) { + return FunctionType.forInterface(this, name, source); + } + + /** + * Creates a parameterized type. + */ + public ParameterizedType createParameterizedType( + ObjectType objectType, JSType parameterType) { + return new ParameterizedType(this, objectType, parameterType); + } + + /** + * Creates a templatized instance of the specified type. + * @param baseType the type to be templatized. + * @param templatizedTypes a list of the template JSTypes. Will be matched by + * list order to the template keys specified in the constructor function. + */ + public JSType createTemplatizedType( + JSType baseType, ImmutableList templatizedTypes) { + // Only instance object types can currently be templatized; extend this + // logic when more types can be templatized. + if (baseType instanceof InstanceObjectType) { + ObjectType baseObjType = baseType.toObjectType(); + return new InstanceObjectType( + this, baseObjType.getConstructor(), baseObjType.isNativeObjectType(), + templatizedTypes); + } else { + throw new IllegalArgumentException( + "Only instance object types can be templatized"); + } + } + + /** + * Creates a named type. + */ + @VisibleForTesting + public JSType createNamedType(String reference, + String sourceName, int lineno, int charno) { + return new NamedType(this, reference, sourceName, lineno, charno); + } + + /** + * Identifies the name of a typedef or enum before we actually declare it. + */ + public void identifyNonNullableName(String name) { + Preconditions.checkNotNull(name); + nonNullableTypeNames.add(name); + } + + /** + * Creates a JSType from the nodes representing a type. + * @param n The node with type info. + * @param sourceName The source file name. + * @param scope A scope for doing type name lookups. + */ + public JSType createFromTypeNodes(Node n, String sourceName, + StaticScope scope) { + if (resolveMode == ResolveMode.LAZY_EXPRESSIONS) { + // If the type expression doesn't contain any names, just + // resolve it anyway. + boolean hasNames = hasTypeName(n); + if (hasNames) { + return new UnresolvedTypeExpression(this, n, sourceName); + } + } + return createFromTypeNodesInternal(n, sourceName, scope); + } + + private boolean hasTypeName(Node n) { + if (n.getType() == Token.STRING) { + return true; + } + + for (Node child = n.getFirstChild(); + child != null; child = child.getNext()) { + if (hasTypeName(child)) { + return true; + } + } + + return false; + } + + /** @see #createFromTypeNodes(Node, String, StaticScope) */ + private JSType createFromTypeNodesInternal(Node n, String sourceName, + StaticScope scope) { + switch (n.getType()) { + case Token.LC: // Record type. + return createRecordTypeFromNodes( + n.getFirstChild(), sourceName, scope); + + case Token.BANG: // Not nullable + return createFromTypeNodesInternal( + n.getFirstChild(), sourceName, scope) + .restrictByNotNullOrUndefined(); + + case Token.QMARK: // Nullable or unknown + Node firstChild = n.getFirstChild(); + if (firstChild == null) { + return getNativeType(UNKNOWN_TYPE); + } + return createDefaultObjectUnion( + createFromTypeNodesInternal( + firstChild, sourceName, scope)); + + case Token.EQUALS: // Optional + return createOptionalType( + createFromTypeNodesInternal( + n.getFirstChild(), sourceName, scope)); + + case Token.ELLIPSIS: // Var args + return createOptionalType( + createFromTypeNodesInternal( + n.getFirstChild(), sourceName, scope)); + + case Token.STAR: // The AllType + return getNativeType(ALL_TYPE); + + case Token.LB: // Array type + // TODO(nicksantos): Enforce membership restrictions on the Array. + return getNativeType(ARRAY_TYPE); + + case Token.PIPE: // Union type + UnionTypeBuilder builder = new UnionTypeBuilder(this); + for (Node child = n.getFirstChild(); child != null; + child = child.getNext()) { + builder.addAlternate( + createFromTypeNodesInternal(child, sourceName, scope)); + } + return builder.build(); + + case Token.EMPTY: // When the return value of a function is not specified + return getNativeType(UNKNOWN_TYPE); + + case Token.VOID: // Only allowed in the return value of a function. + return getNativeType(VOID_TYPE); + + case Token.STRING: + JSType namedType = getType(scope, n.getString(), sourceName, + n.getLineno(), n.getCharno()); + if (resolveMode != ResolveMode.LAZY_NAMES) { + namedType = namedType.resolveInternal(reporter, scope); + } + if ((namedType instanceof ObjectType) && + !(nonNullableTypeNames.contains(n.getString()))) { + Node typeList = n.getFirstChild(); + if (typeList != null && + ("Array".equals(n.getString()) || + "Object".equals(n.getString()))) { + JSType parameterType = + createFromTypeNodesInternal( + typeList.getLastChild(), sourceName, scope); + namedType = new ParameterizedType( + this, (ObjectType) namedType, parameterType); + if (typeList.hasMoreThanOneChild()) { + JSType indexType = + createFromTypeNodesInternal( + typeList.getFirstChild(), sourceName, scope); + namedType = new IndexedType( + this, (ObjectType) namedType, indexType); + } + } + return createDefaultObjectUnion(namedType); + } else { + return namedType; + } + + case Token.FUNCTION: + ObjectType thisType = null; + boolean isConstructor = false; + Node current = n.getFirstChild(); + if (current.getType() == Token.THIS || + current.getType() == Token.NEW) { + Node contextNode = current.getFirstChild(); + thisType = + ObjectType.cast( + createFromTypeNodesInternal( + contextNode, sourceName, scope) + .restrictByNotNullOrUndefined()); + if (thisType == null) { + reporter.warning( + ScriptRuntime.getMessage0( + current.getType() == Token.THIS ? + "msg.jsdoc.function.thisnotobject" : + "msg.jsdoc.function.newnotobject"), + sourceName, + contextNode.getLineno(), contextNode.getCharno()); + } + + isConstructor = current.getType() == Token.NEW; + current = current.getNext(); + } + + FunctionParamBuilder paramBuilder = new FunctionParamBuilder(this); + + if (current.getType() == Token.PARAM_LIST) { + Node args = current.getFirstChild(); + for (Node arg = current.getFirstChild(); arg != null; + arg = arg.getNext()) { + if (arg.getType() == Token.ELLIPSIS) { + if (arg.getChildCount() == 0) { + paramBuilder.addVarArgs(getNativeType(UNKNOWN_TYPE)); + } else { + paramBuilder.addVarArgs( + createFromTypeNodesInternal( + arg.getFirstChild(), sourceName, scope)); + } + } else { + JSType type = createFromTypeNodesInternal( + arg, sourceName, scope); + if (arg.getType() == Token.EQUALS) { + boolean addSuccess = paramBuilder.addOptionalParams(type); + if (!addSuccess) { + reporter.warning( + ScriptRuntime.getMessage0("msg.jsdoc.function.varargs"), + sourceName, arg.getLineno(), arg.getCharno()); + } + } else { + paramBuilder.addRequiredParams(type); + } + } + } + current = current.getNext(); + } + + JSType returnType = + createFromTypeNodesInternal(current, sourceName, scope); + + return new FunctionBuilder(this) + .withParams(paramBuilder) + .withReturnType(returnType) + .withTypeOfThis(thisType) + .setIsConstructor(isConstructor) + .build(); + } + + throw new IllegalStateException( + "Unexpected node in type expression: " + n.toString()); + } + + /** + * Creates a RecordType from the nodes representing said record type. + * @param n The node with type info. + * @param sourceName The source file name. + * @param scope A scope for doing type name lookups. + */ + private JSType createRecordTypeFromNodes(Node n, String sourceName, + StaticScope scope) { + + RecordTypeBuilder builder = new RecordTypeBuilder(this); + + // For each of the fields in the record type. + for (Node fieldTypeNode = n.getFirstChild(); + fieldTypeNode != null; + fieldTypeNode = fieldTypeNode.getNext()) { + + // Get the property's name. + Node fieldNameNode = fieldTypeNode; + boolean hasType = false; + + if (fieldTypeNode.getType() == Token.COLON) { + fieldNameNode = fieldTypeNode.getFirstChild(); + hasType = true; + } + + String fieldName = fieldNameNode.getString(); + + // TODO(user): Move this into the lexer/parser. + // Remove the string literal characters around a field name, + // if any. + if (fieldName.startsWith("'") || fieldName.startsWith("\"")) { + fieldName = fieldName.substring(1, fieldName.length() - 1); + } + + // Get the property's type. + JSType fieldType = null; + + if (hasType) { + // We have a declared type. + fieldType = createFromTypeNodesInternal( + fieldTypeNode.getLastChild(), sourceName, scope); + } else { + // Otherwise, the type is UNKNOWN. + fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE); + } + + // Add the property to the record. + if (builder.addProperty(fieldName, fieldType, fieldNameNode) == null) { + // Duplicate field name, warning and skip + reporter.warning( + "Duplicate record field " + fieldName, + sourceName, + n.getLineno(), fieldNameNode.getCharno()); + } + } + + return builder.build(); + } + + /** + * Sets the template type name. + */ + public void setTemplateTypeNames(List names) { + Preconditions.checkNotNull(names); + for (String name : names) { + templateTypes.put(name, new TemplateType(this, name)); + } + } + + /** + * Clears the template type name. + */ + public void clearTemplateTypeNames() { + templateTypes.clear(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ModificationVisitor.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ModificationVisitor.java new file mode 100644 index 0000000..7d70968 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ModificationVisitor.java @@ -0,0 +1,230 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Lenz + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + + +package com.google.javascript.rhino.jstype; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.javascript.rhino.Node; + +import java.util.List; + +/** + * A visitor implementation that enables type substitutions. + * + * @author johnlenz@google.com (John Lenz) + */ +public class ModificationVisitor implements Visitor { + + private final JSTypeRegistry registry; + + public ModificationVisitor(JSTypeRegistry registry) { + this.registry = registry; + } + + @Override + public JSType caseNoType() { + return getNativeType(JSTypeNative.NO_TYPE); + } + + @Override + public JSType caseEnumElementType(EnumElementType type) { + return type; + } + + @Override + public JSType caseAllType() { + return getNativeType(JSTypeNative.ALL_TYPE); + } + + @Override + public JSType caseBooleanType() { + return getNativeType(JSTypeNative.BOOLEAN_TYPE); + } + + @Override + public JSType caseNoObjectType() { + return getNativeType(JSTypeNative.NO_OBJECT_TYPE); + } + + @Override + public JSType caseFunctionType(FunctionType type) { + if (isNativeFunctionType(type)) { + return type; + } + + // TODO(johnlenz): remove this simplifying assumption... + if (!type.isOrdinaryFunction()) { + return type; + } + + boolean changed = false; + + JSType beforeThis = type.getTypeOfThis(); + JSType afterThis = coerseToThisType(beforeThis.visit(this)); + if (beforeThis != afterThis) { + changed = true; + } + + JSType beforeReturn = type.getReturnType(); + JSType afterReturn = beforeReturn.visit(this); + if (beforeReturn != afterReturn) { + changed = true; + } + + FunctionParamBuilder paramBuilder = new FunctionParamBuilder(registry); + for (Node paramNode : type.getParameters()) { + JSType beforeParamType = paramNode.getJSType(); + JSType afterParamType = beforeParamType.visit(this); + if (beforeParamType != afterParamType) { + changed = true; + } + if (paramNode.isOptionalArg()) { + paramBuilder.addOptionalParams(afterParamType); + } else if (paramNode.isVarArgs()) { + paramBuilder.addVarArgs(afterParamType); + } else { + paramBuilder.addRequiredParams(afterParamType); + } + } + + if (changed) { + // TODO(johnlenz): should we support preserving template keys? + FunctionBuilder builder = new FunctionBuilder(registry); + builder.withParams(paramBuilder); + builder.withReturnType(afterReturn); + builder.withTypeOfThis(afterThis); + return builder.build(); + } + + return type; + } + + private JSType coerseToThisType(JSType type) { + return type != null ? type : registry.getNativeObjectType( + JSTypeNative.UNKNOWN_TYPE); + } + + @Override + public JSType caseObjectType(ObjectType objType) { + if (objType.isTemplatized()) { + ImmutableList.Builder builder = ImmutableList.builder(); + for (JSType templatizedType : objType.getTemplatizedTypes()) { + builder.add(templatizedType.visit(this)); + } + return registry.createTemplatizedType(objType, builder.build()); + } else { + return objType; + } + } + + @Override + public JSType caseParameterizedType(ParameterizedType type) { + ObjectType genericType = ObjectType.cast( + type.getReferencedTypeInternal().visit(this)); + JSType paramType = type.getParameterType().visit(this); + if (type.getReferencedTypeInternal() != genericType + || type.getParameterType() != paramType) { + type = registry.createParameterizedType(genericType, paramType); + } + return type; + } + + @Override + public JSType caseUnknownType() { + return getNativeType(JSTypeNative.UNKNOWN_TYPE); + } + + @Override + public JSType caseNullType() { + return getNativeType(JSTypeNative.NULL_TYPE); + } + + @Override + public JSType caseNumberType() { + return getNativeType(JSTypeNative.NUMBER_TYPE); + } + + @Override + public JSType caseStringType() { + return getNativeType(JSTypeNative.STRING_TYPE); + } + + @Override + public JSType caseVoidType() { + return getNativeType(JSTypeNative.VOID_TYPE); + } + + @Override + public JSType caseUnionType(UnionType type) { + boolean changed = false; + List results = Lists.newArrayList(); + for (JSType alternative : type.getAlternates()) { + JSType replacement = alternative.visit(this); + if (replacement != alternative) { + changed = true; + } + results.add(replacement); + } + + if (changed) { + UnionTypeBuilder builder = new UnionTypeBuilder(registry); + for (JSType alternate : results) { + builder.addAlternate(alternate); + } + return builder.build(); // maybe not a union + } + + return type; + } + + @Override + public JSType caseTemplateType(TemplateType type) { + return type; + } + + private JSType getNativeType(JSTypeNative nativeType) { + return registry.getNativeType(nativeType); + } + + private boolean isNativeFunctionType(FunctionType type) { + return type.isNativeObjectType(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NamedType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NamedType.java new file mode 100644 index 0000000..9fb2191 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NamedType.java @@ -0,0 +1,408 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Lists; +import com.google.javascript.rhino.ErrorReporter; +import com.google.javascript.rhino.Node; + +import java.util.List; + +/** + * A {@code NamedType} is a named reference to some other type. This provides + * a convenient mechanism for implementing forward references to types; a + * {@code NamedType} can be used as a placeholder until its reference is + * resolved. It is also useful for representing type names in JsDoc type + * annotations, some of which may never be resolved (as they may refer to + * types in host systems not yet supported by JSCompiler, such as the JVM.)

      + * + * An important distinction: {@code NamedType} is a type name reference, + * whereas {@link ObjectType} is a named type object, such as an Enum name. + * The Enum itself is typically used only in a dot operator to name one of its + * constants, or in a declaration, where its name will appear in a + * NamedType.

      + * + * A {@code NamedType} is not currently a full-fledged typedef, because it + * cannot resolve to any JavaScript type. It can only resolve to a named + * {@link JSTypeRegistry} type, or to {@link FunctionType} or + * {@link EnumType}.

      + * + * If full typedefs are to be supported, then each method on each type class + * needs to be reviewed to make sure that everything works correctly through + * typedefs. Alternatively, we would need to walk through the parse tree and + * unroll each reference to a {@code NamedType} to its resolved type before + * applying the rest of the analysis.

      + * + * TODO(user): Revisit all of this logic.

      + * + * The existing typing logic is hacky. Unresolved types should get processed + * in a more consistent way, but with the Rhino merge coming, there will be + * much that has to be changed.

      + * + */ +class NamedType extends ProxyObjectType { + private static final long serialVersionUID = 1L; + + private final String reference; + private final String sourceName; + private final int lineno; + private final int charno; + + /** + * Validates the type resolution. + */ + private Predicate validator; + + /** + * Property-defining continuations. + */ + private List propertyContinuations = null; + + /** + * Create a named type based on the reference. + */ + NamedType(JSTypeRegistry registry, String reference, + String sourceName, int lineno, int charno) { + super(registry, registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE)); + + Preconditions.checkNotNull(reference); + this.reference = reference; + this.sourceName = sourceName; + this.lineno = lineno; + this.charno = charno; + } + + @Override + boolean defineProperty(String propertyName, JSType type, + boolean inferred, Node propertyNode) { + if (!isResolved()) { + // If this is an unresolved object type, we need to save all its + // properties and define them when it is resolved. + if (propertyContinuations == null) { + propertyContinuations = Lists.newArrayList(); + } + propertyContinuations.add( + new PropertyContinuation( + propertyName, type, inferred, propertyNode)); + return true; + } else { + return super.defineProperty( + propertyName, type, inferred, propertyNode); + } + } + + private void finishPropertyContinuations() { + ObjectType referencedObjType = getReferencedObjTypeInternal(); + if (referencedObjType != null && !referencedObjType.isUnknownType()) { + if (propertyContinuations != null) { + for (PropertyContinuation c : propertyContinuations) { + c.commit(this); + } + } + } + propertyContinuations = null; + } + + /** Returns the type to which this refers (which is unknown if unresolved). */ + public JSType getReferencedType() { + return getReferencedTypeInternal(); + } + + @Override + public String getReferenceName() { + return reference; + } + + @Override + String toStringHelper(boolean forAnnotations) { + return reference; + } + + @Override + public boolean hasReferenceName() { + return true; + } + + @Override + boolean isNamedType() { + return true; + } + + @Override + public boolean isNominalType() { + return true; + } + + @Override + public int hashCode() { + return reference.hashCode(); + } + + /** + * Resolve the referenced type within the enclosing scope. + */ + @Override + JSType resolveInternal(ErrorReporter t, StaticScope enclosing) { + // TODO(user): Investigate whether it is really necessary to keep two + // different mechanisms for resolving named types, and if so, which order + // makes more sense. Now, resolution via registry is first in order to + // avoid triggering the warnings built into the resolution via properties. + boolean resolved = resolveViaRegistry(t, enclosing); + if (detectInheritanceCycle()) { + handleTypeCycle(t); + } + + if (resolved) { + super.resolveInternal(t, enclosing); + finishPropertyContinuations(); + return registry.isLastGeneration() ? + getReferencedType() : this; + } + + resolveViaProperties(t, enclosing); + if (detectInheritanceCycle()) { + handleTypeCycle(t); + } + + super.resolveInternal(t, enclosing); + if (isResolved()) { + finishPropertyContinuations(); + } + return registry.isLastGeneration() ? + getReferencedType() : this; + } + + /** + * Resolves a named type by looking it up in the registry. + * @return True if we resolved successfully. + */ + private boolean resolveViaRegistry( + ErrorReporter t, StaticScope enclosing) { + JSType type = registry.getType(reference); + if (type != null) { + setReferencedAndResolvedType(type, t, enclosing); + return true; + } + return false; + } + + /** + * Resolves a named type by looking up its first component in the scope, and + * subsequent components as properties. The scope must have been fully + * parsed and a symbol table constructed. + */ + private void resolveViaProperties(ErrorReporter t, + StaticScope enclosing) { + JSType value = lookupViaProperties(t, enclosing); + // last component of the chain + if (value != null && value.isFunctionType() && + (value.isConstructor() || value.isInterface())) { + FunctionType functionType = value.toMaybeFunctionType(); + setReferencedAndResolvedType( + functionType.getInstanceType(), t, enclosing); + } else if (value != null && value.isNoObjectType()) { + setReferencedAndResolvedType( + registry.getNativeFunctionType( + JSTypeNative.NO_OBJECT_TYPE).getInstanceType(), t, enclosing); + } else if (value instanceof EnumType) { + setReferencedAndResolvedType( + ((EnumType) value).getElementsType(), t, enclosing); + } else { + // We've been running into issues where people forward-declare + // non-named types. (This is legitimate...our dependency management + // code doubles as our forward-declaration code.) + // + // So if the type does resolve to an actual value, but it's not named, + // then don't respect the forward declaration. + handleUnresolvedType(t, value == null || value.isUnknownType()); + } + } + + /** + * Resolves a type by looking up its first component in the scope, and + * subsequent components as properties. The scope must have been fully + * parsed and a symbol table constructed. + * @return The type of the symbol, or null if the type could not be found. + */ + private JSType lookupViaProperties( ErrorReporter t, + StaticScope enclosing) { + String[] componentNames = reference.split("\\.", -1); + if (componentNames[0].length() == 0) { + return null; + } + StaticSlot slot = enclosing.getSlot(componentNames[0]); + if (slot == null) { + return null; + } + // If the first component has a type of 'Unknown', then any type + // names using it should be regarded as silently 'Unknown' rather than be + // noisy about it. + JSType slotType = slot.getType(); + if (slotType == null || slotType.isAllType() || slotType.isNoType()) { + return null; + } + JSType value = getTypedefType(t, slot, componentNames[0]); + if (value == null) { + return null; + } + + // resolving component by component + for (int i = 1; i < componentNames.length; i++) { + ObjectType parentClass = ObjectType.cast(value); + if (parentClass == null) { + return null; + } + if (componentNames[i].length() == 0) { + return null; + } + value = parentClass.getPropertyType(componentNames[i]); + } + return value; + } + + private void setReferencedAndResolvedType(JSType type, ErrorReporter t, + StaticScope enclosing) { + if (validator != null) { + validator.apply(type); + } + setReferencedType(type); + checkEnumElementCycle(t); + checkProtoCycle(t); + setResolvedTypeInternal(getReferencedType()); + } + + private void handleTypeCycle(ErrorReporter t) { + setReferencedType( + registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE)); + t.warning("Cycle detected in inheritance chain of type " + reference, + sourceName, lineno, charno); + setResolvedTypeInternal(getReferencedType()); + } + + private void checkEnumElementCycle(ErrorReporter t) { + JSType referencedType = getReferencedType(); + if (referencedType instanceof EnumElementType && + ((EnumElementType) referencedType).getPrimitiveType() == this) { + handleTypeCycle(t); + } + } + + private void checkProtoCycle(ErrorReporter t) { + JSType referencedType = getReferencedType(); + if (referencedType == this) { + handleTypeCycle(t); + } + } + + // Warns about this type being unresolved iff it's not a forward-declared + // type name. + private void handleUnresolvedType( + ErrorReporter t, boolean ignoreForwardReferencedTypes) { + if (registry.isLastGeneration()) { + boolean isForwardDeclared = + ignoreForwardReferencedTypes && + registry.isForwardDeclaredType(reference); + if (!isForwardDeclared && registry.isLastGeneration()) { + t.warning("Bad type annotation. Unknown type " + reference, + sourceName, lineno, charno); + } else { + setReferencedType( + registry.getNativeObjectType( + JSTypeNative.NO_RESOLVED_TYPE)); + + if (registry.isLastGeneration() && validator != null) { + validator.apply(getReferencedType()); + } + } + + setResolvedTypeInternal(getReferencedType()); + } else { + setResolvedTypeInternal(this); + } + } + + JSType getTypedefType(ErrorReporter t, StaticSlot slot, String name) { + JSType type = slot.getType(); + if (type != null) { + return type; + } + handleUnresolvedType(t, true); + return null; + } + + @Override + public boolean setValidator(Predicate validator) { + // If the type is already resolved, we can validate it now. If + // the type has not been resolved yet, we need to wait till its + // resolved before we can validate it. + if (this.isResolved()) { + return super.setValidator(validator); + } else { + this.validator = validator; + return true; + } + } + + /** Store enough information to define a property at a later time. */ + private static final class PropertyContinuation { + private final String propertyName; + private final JSType type; + private final boolean inferred; + private final Node propertyNode; + + private PropertyContinuation( + String propertyName, + JSType type, + boolean inferred, + Node propertyNode) { + this.propertyName = propertyName; + this.type = type; + this.inferred = inferred; + this.propertyNode = propertyNode; + } + + void commit(ObjectType target) { + target.defineProperty( + propertyName, type, inferred, propertyNode); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NoObjectType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NoObjectType.java new file mode 100644 index 0000000..e4ff441 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NoObjectType.java @@ -0,0 +1,163 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.ErrorReporter; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; + +/** + * The bottom Object type, representing the subclass of all objects. + * + * Although JavaScript programmers can't explicitly denote the bottom + * Object type, it comes up in static analysis. For example, if we have: + * + * var x = function() {}; + * if (x instanceof Array) { + * f(x); + * } + * + * We need to be able to assign {@code x} a type within the {@code f(x)} + * call. It has no possible type, but {@code x} would not be legal if f + * expected a string. So we assign it the {@code NoObjectType}. + * + * @see Bottom types + */ +public class NoObjectType extends FunctionType { + private static final long serialVersionUID = 1L; + + NoObjectType(JSTypeRegistry registry) { + super(registry, null, null, + registry.createArrowType(null, null), + null, null, true, true); + getInternalArrowType().returnType = this; + this.setInstanceType(this); + } + + @Override + public boolean isSubtype(JSType that) { + if (JSType.isSubtypeHelper(this, that)) { + return true; + } else { + return that.isObject() && !that.isNoType() && !that.isNoResolvedType(); + } + } + + @Override + public FunctionType toMaybeFunctionType() { + return null; + } + + @Override + public boolean isNoObjectType() { + return true; + } + + @Override + public ObjectType getImplicitPrototype() { + return null; + } + + @Override + public String getReferenceName() { + return null; + } + + @Override + public boolean matchesNumberContext() { + return true; + } + + @Override + public boolean matchesObjectContext() { + return true; + } + + @Override + public boolean matchesStringContext() { + return true; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + @Override + boolean defineProperty(String propertyName, JSType type, + boolean inferred, Node propertyNode) { + // nothing, all properties are defined + return true; + } + + @Override + public boolean removeProperty(String name) { + return false; + } + + @Override + public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) { + // Do nothing, specific properties do not have JSDocInfo. + } + + @Override + public T visit(Visitor visitor) { + return visitor.caseNoObjectType(); + } + + @Override T visit(RelationshipVisitor visitor, JSType that) { + return visitor.caseNoObjectType(that); + } + + @Override + String toStringHelper(boolean forAnnotations) { + return forAnnotations ? "?" : "NoObject"; + } + + @Override + public FunctionType getConstructor() { + return null; + } + + @Override + JSType resolveInternal(ErrorReporter t, StaticScope scope) { + return this; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NoResolvedType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NoResolvedType.java new file mode 100644 index 0000000..77004d0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NoResolvedType.java @@ -0,0 +1,85 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + + +/** + * An unresolved type that was forward declared. So we know it exists, + * but that it wasn't pulled into this binary. + * + * In most cases, it behaves like a bottom type in the type lattice: + * no real type should be assigned to a NoResolvedType, but the + * NoResolvedType is a subtype of everything. In a few cases, it behaves + * like the unknown type: properties of this type are also NoResolved types, + * and comparisons to other types always have an unknown result. + * + * @author nicksantos@google.com (Nick Santos) + */ +class NoResolvedType extends NoType { + private static final long serialVersionUID = 1L; + + NoResolvedType(JSTypeRegistry registry) { + super(registry); + } + + @Override + public boolean isNoResolvedType() { + return true; + } + + @Override + public boolean isNoType() { + return false; + } + + @Override + public boolean isSubtype(JSType that) { + if (JSType.isSubtypeHelper(this, that)) { + return true; + } else { + return !that.isNoType(); + } + } + + @Override + String toStringHelper(boolean forAnnotations) { + return forAnnotations ? "?" : "NoResolvedType"; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NoType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NoType.java new file mode 100644 index 0000000..0f972bc --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NoType.java @@ -0,0 +1,121 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + + +/** + * Bottom type, representing the subclass of any value or object. + * + * Although JavaScript programmers can't explicitly denote the bottom type, + * it comes up in static analysis. For example, if we have: + * + * var x = null; + * if (x) { + * f(x); + * } + * + * We need to be able to assign {@code x} a type within the {@code f(x)} + * call. Since it has no possible type, we assign {@code x} the NoType, + * so that {@code f(x)} is legal no matter what the type of {@code f}'s + * first argument is. + * + * @see Bottom types + */ +public class NoType extends NoObjectType { + private static final long serialVersionUID = 1L; + + NoType(JSTypeRegistry registry) { + super(registry); + } + + @Override + public boolean isNoObjectType() { + return false; + } + + @Override + public boolean isNoType() { + return true; + } + + @Override + public boolean isNullable() { + return true; + } + + @Override + public boolean isSubtype(JSType that) { + return true; + } + + @Override + public BooleanLiteralSet getPossibleToBooleanOutcomes() { + return BooleanLiteralSet.EMPTY; + } + + @Override + public boolean matchesNumberContext() { + return true; + } + + @Override + public boolean matchesObjectContext() { + return true; + } + + @Override + public boolean matchesStringContext() { + return true; + } + + @Override + public T visit(Visitor visitor) { + return visitor.caseNoType(); + } + + @Override T visit(RelationshipVisitor visitor, JSType that) { + return visitor.caseNoType(that); + } + + @Override + String toStringHelper(boolean forAnnotations) { + return forAnnotations ? "?" : "None"; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NullType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NullType.java new file mode 100644 index 0000000..95f0ae3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NullType.java @@ -0,0 +1,121 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; +import static com.google.javascript.rhino.jstype.TernaryValue.TRUE; +import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; + + +/** + * Null type. + */ +public final class NullType extends ValueType { + private static final long serialVersionUID = 1L; + + NullType(JSTypeRegistry registry) { + super(registry); + } + + @Override + public boolean isNullType() { + return true; + } + + @Override + public boolean isNullable() { + return true; + } + + @Override + public boolean matchesNumberContext() { + return true; + } + + @Override + public boolean matchesObjectContext() { + return false; + } + + @Override + public boolean matchesStringContext() { + return true; + } + + @Override + public JSType restrictByNotNullOrUndefined() { + return registry.getNativeType(JSTypeNative.NO_TYPE); + } + + @Override + public TernaryValue testForEquality(JSType that) { + TernaryValue result = super.testForEquality(that); + if (result != null) { + return result; + } + if (that.isNullType() || that.isVoidType()) { + return TRUE; + } + if (that.isUnknownType() || that.isNullable()) { + return UNKNOWN; + } + return FALSE; + } + + @Override + String toStringHelper(boolean forAnnotations) { + return getDisplayName(); + } + + @Override + public String getDisplayName() { + return "null"; + } + + @Override + public BooleanLiteralSet getPossibleToBooleanOutcomes() { + return BooleanLiteralSet.FALSE; + } + + @Override + public T visit(Visitor visitor) { + return visitor.caseNullType(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NumberType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NumberType.java new file mode 100644 index 0000000..3212dc3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/NumberType.java @@ -0,0 +1,119 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; +import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; + + +/** + * Number type. + */ +public class NumberType extends ValueType { + private static final long serialVersionUID = 1L; + + NumberType(JSTypeRegistry registry) { + super(registry); + } + + @Override + public boolean isNullable() { + return false; + } + + @Override + public TernaryValue testForEquality(JSType that) { + TernaryValue result = super.testForEquality(that); + if (result != null) { + return result; + } + if (that.isUnknownType() || that.isSubtype( + getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) { + return UNKNOWN; + } + return FALSE; + } + + @Override + public boolean isNumberValueType() { + return true; + } + + @Override + public boolean matchesNumberContext() { + return true; + } + + @Override + public boolean matchesStringContext() { + return true; + } + + @Override + public boolean matchesObjectContext() { + // TODO(user): Revisit this for ES4, which is stricter. + return true; + } + + @Override + String toStringHelper(boolean forAnnotations) { + return getDisplayName(); + } + + @Override + public String getDisplayName() { + return "number"; + } + + @Override + public BooleanLiteralSet getPossibleToBooleanOutcomes() { + return BooleanLiteralSet.BOTH; + } + + @Override + public T visit(Visitor visitor) { + return visitor.caseNumberType(); + } + + @Override + public JSType autoboxesTo() { + return getNativeType(JSTypeNative.NUMBER_OBJECT_TYPE); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ObjectType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ObjectType.java new file mode 100644 index 0000000..21e8aba --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ObjectType.java @@ -0,0 +1,642 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; +import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; + +import java.util.Set; + +/** + * Object type. + * + * In JavaScript, all object types have properties, and each of those + * properties has a type. Property types may be DECLARED, INFERRED, or + * UNKNOWN. + * + * DECLARED properties have an explicit type annotation, as in: + * + * /xx @type {number} x/ + * Foo.prototype.bar = 1; + * + * This property may only hold number values, and an assignment to any + * other type of value is an error. + * + * INFERRED properties do not have an explicit type annotation. Rather, + * we try to find all the possible types that this property can hold. + * + * Foo.prototype.bar = 1; + * + * If the programmer assigns other types of values to this property, + * the property will take on the union of all these types. + * + * UNKNOWN properties are properties on the UNKNOWN type. The UNKNOWN + * type has all properties, but we do not know whether they are + * declared or inferred. + * + */ +public abstract class ObjectType extends JSType implements StaticScope { + private boolean visited; + private JSDocInfo docInfo = null; + private boolean unknown = true; + + ObjectType(JSTypeRegistry registry) { + super(registry); + } + + ObjectType(JSTypeRegistry registry, ImmutableList templateKeys, + ImmutableList templatizedTypes) { + super(registry, templateKeys, templatizedTypes); + } + + @Override + public Node getRootNode() { return null; } + + @Override + public ObjectType getParentScope() { + return getImplicitPrototype(); + } + + /** + * Returns the property map that manages the set of properties for an object. + */ + PropertyMap getPropertyMap() { + return PropertyMap.immutableEmptyMap(); + } + + /** + * Default getSlot implementation. This gets overridden by FunctionType + * for lazily-resolved prototypes. + */ + @Override + public Property getSlot(String name) { + return getPropertyMap().getSlot(name); + } + + @Override + public Property getOwnSlot(String name) { + return getPropertyMap().getOwnProperty(name); + } + + @Override + public JSType getTypeOfThis() { + return null; + } + + /** + * Gets the declared default element type. + * @see ParameterizedType + */ + public JSType getParameterType() { + return null; + } + + /** + * Gets the declared default index type. + * @see IndexedType + */ + public JSType getIndexType() { + return null; + } + + /** + * Gets the docInfo for this type. + */ + @Override + public JSDocInfo getJSDocInfo() { + return docInfo; + } + + /** + * Sets the docInfo for this type from the given + * {@link JSDocInfo}. The {@code JSDocInfo} may be {@code null}. + */ + public void setJSDocInfo(JSDocInfo info) { + docInfo = info; + } + + /** + * Detects a cycle in the implicit prototype chain. This method accesses + * the {@link #getImplicitPrototype()} method and must therefore be + * invoked only after the object is sufficiently initialized to respond to + * calls to this method.

      + * + * @return True iff an implicit prototype cycle was detected. + */ + final boolean detectImplicitPrototypeCycle() { + // detecting cycle + this.visited = true; + ObjectType p = getImplicitPrototype(); + while (p != null) { + if (p.visited) { + return true; + } else { + p.visited = true; + } + p = p.getImplicitPrototype(); + } + + // clean up + p = this; + do { + p.visited = false; + p = p.getImplicitPrototype(); + } while (p != null); + return false; + } + + /** + * Detects cycles in either the implicit prototype chain, or the implemented/extended + * interfaces.

      + * + * @return True iff a cycle was detected. + */ + final boolean detectInheritanceCycle() { + // TODO(user): This should get moved to preventing cycles in FunctionTypeBuilder + // rather than removing them here after they have been created. + // Also, this doesn't do the right thing for extended interfaces, though that is + // masked by another bug. + return detectImplicitPrototypeCycle() + || Iterables.contains(this.getCtorImplementedInterfaces(), this) + || Iterables.contains(this.getCtorExtendedInterfaces(), this); + } + + /** + * Gets the reference name for this object. This includes named types + * like constructors, prototypes, and enums. It notably does not include + * literal types like strings and booleans and structural types. + * @return the object's name or {@code null} if this is an anonymous + * object + */ + public abstract String getReferenceName(); + + /** + * Due to the complexity of some of our internal type systems, sometimes + * we have different types constructed by the same constructor. + * In other parts of the type system, these are called delegates. + * We construct these types by appending suffixes to the constructor name. + * + * The normalized reference name does not have these suffixes, and as such, + * recollapses these implicit types back to their real type. + */ + public String getNormalizedReferenceName() { + String name = getReferenceName(); + if (name != null) { + int pos = name.indexOf("("); + if (pos != -1) { + return name.substring(0, pos); + } + } + return name; + } + + @Override + public String getDisplayName() { + return getNormalizedReferenceName(); + } + + /** + * Creates a suffix for a proxy delegate. + * @see #getNormalizedReferenceName + */ + public static String createDelegateSuffix(String suffix) { + return "(" + suffix + ")"; + } + + /** + * Returns true if the object is named. + * @return true if the object is named, false if it is anonymous + */ + public boolean hasReferenceName() { + return false; + } + + @Override + public TernaryValue testForEquality(JSType that) { + // super + TernaryValue result = super.testForEquality(that); + if (result != null) { + return result; + } + // objects are comparable to everything but null/undefined + if (that.isSubtype( + getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) { + return UNKNOWN; + } else { + return FALSE; + } + } + + /** + * Gets this object's constructor. + * @return this object's constructor or {@code null} if it is a native + * object (constructed natively v.s. by instantiation of a function) + */ + public abstract FunctionType getConstructor(); + + /** + * Gets the implicit prototype (a.k.a. the {@code [[Prototype]]} property). + */ + public abstract ObjectType getImplicitPrototype(); + + /** + * Defines a property whose type is explicitly declared by the programmer. + * @param propertyName the property's name + * @param type the type + * @param propertyNode the node corresponding to the declaration of property + * which might later be accessed using {@code getPropertyNode}. + */ + public final boolean defineDeclaredProperty(String propertyName, + JSType type, Node propertyNode) { + boolean result = defineProperty(propertyName, type, false, propertyNode); + // All property definitions go through this method + // or defineInferredProperty. Because the properties defined an an + // object can affect subtyping, it's slightly more efficient + // to register this after defining the property. + registry.registerPropertyOnType(propertyName, this); + return result; + } + + /** + * Defines a property whose type is on a synthesized object. These objects + * don't actually exist in the user's program. They're just used for + * bookkeeping in the type system. + */ + public final boolean defineSynthesizedProperty(String propertyName, + JSType type, Node propertyNode) { + return defineProperty(propertyName, type, false, propertyNode); + } + + /** + * Defines a property whose type is inferred. + * @param propertyName the property's name + * @param type the type + * @param propertyNode the node corresponding to the inferred definition of + * property that might later be accessed using {@code getPropertyNode}. + */ + public final boolean defineInferredProperty(String propertyName, + JSType type, Node propertyNode) { + StaticSlot originalSlot = getSlot(propertyName); + if (hasProperty(propertyName)) { + if (isPropertyTypeDeclared(propertyName)) { + // We never want to hide a declared property with an inferred property. + return true; + } + JSType originalType = getPropertyType(propertyName); + type = originalType == null ? type : + originalType.getLeastSupertype(type); + } + + boolean result = defineProperty(propertyName, type, true, + propertyNode); + + // All property definitions go through this method + // or defineDeclaredProperty. Because the properties defined an an + // object can affect subtyping, it's slightly more efficient + // to register this after defining the property. + registry.registerPropertyOnType(propertyName, this); + + return result; + } + + /** + * Defines a property.

      + * + * For clarity, callers should prefer {@link #defineDeclaredProperty} and + * {@link #defineInferredProperty}. + * + * @param propertyName the property's name + * @param type the type + * @param inferred {@code true} if this property's type is inferred + * @param propertyNode the node that represents the definition of property. + * Depending on the actual sub-type the node type might be different. + * The general idea is to have an estimate of where in the source code + * this property is defined. + * @return True if the property was registered successfully, false if this + * conflicts with a previous property type declaration. + */ + abstract boolean defineProperty(String propertyName, JSType type, + boolean inferred, Node propertyNode); + + /** + * Removes the declared or inferred property from this ObjectType. + * + * @param propertyName the property's name + * @return true if the property was removed successfully. False if the + * property did not exist, or could not be removed. + */ + public boolean removeProperty(String propertyName) { + return false; + } + + /** + * Gets the node corresponding to the definition of the specified property. + * This could be the node corresponding to declaration of the property or the + * node corresponding to the first reference to this property, e.g., + * "this.propertyName" in a constructor. Note this is mainly intended to be + * an estimate of where in the source code a property is defined. Sometime + * the returned node is not even part of the global AST but in the AST of the + * JsDoc that defines a type. + * + * @param propertyName the name of the property + * @return the {@code Node} corresponding to the property or null. + */ + public Node getPropertyNode(String propertyName) { + Property p = getSlot(propertyName); + return p == null ? null : p.getNode(); + } + + /** + * Gets the docInfo on the specified property on this type. This should not + * be implemented recursively, as you generally need to know exactly on + * which type in the prototype chain the JSDocInfo exists. + */ + public JSDocInfo getOwnPropertyJSDocInfo(String propertyName) { + Property p = getOwnSlot(propertyName); + return p == null ? null : p.getJSDocInfo(); + } + + /** + * Sets the docInfo for the specified property from the + * {@link JSDocInfo} on its definition. + * @param info {@code JSDocInfo} for the property definition. May be + * {@code null}. + */ + public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) { + // by default, do nothing + } + + @Override + public JSType findPropertyType(String propertyName) { + return hasProperty(propertyName) ? + getPropertyType(propertyName) : null; + } + + /** + * Gets the property type of the property whose name is given. If the + * underlying object does not have this property, the Unknown type is + * returned to indicate that no information is available on this property. + * + * This gets overridden by FunctionType for lazily-resolved call() and + * bind() functions. + * + * @return the property's type or {@link UnknownType}. This method never + * returns {@code null}. + */ + public JSType getPropertyType(String propertyName) { + StaticSlot slot = getSlot(propertyName); + if (slot == null) { + if (isNoResolvedType() || isCheckedUnknownType()) { + return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE); + } else if (isEmptyType()) { + return getNativeType(JSTypeNative.NO_TYPE); + } + return getNativeType(JSTypeNative.UNKNOWN_TYPE); + } + return slot.getType(); + } + + @Override + public boolean hasProperty(String propertyName) { + // Unknown types have all properties. + return isEmptyType() || isUnknownType() || getSlot(propertyName) != null; + } + + /** + * Checks whether the property whose name is given is present directly on + * the object. Returns false even if it is declared on a supertype. + */ + public boolean hasOwnProperty(String propertyName) { + return getOwnSlot(propertyName) != null; + } + + /** + * Returns the names of all the properties directly on this type. + * + * Overridden by FunctionType to add "prototype". + */ + public Set getOwnPropertyNames() { + return getPropertyMap().getOwnPropertyNames(); + } + + /** + * Checks whether the property's type is inferred. + */ + public boolean isPropertyTypeInferred(String propertyName) { + StaticSlot slot = getSlot(propertyName); + return slot == null ? false : slot.isTypeInferred(); + } + + /** + * Checks whether the property's type is declared. + */ + public boolean isPropertyTypeDeclared(String propertyName) { + StaticSlot slot = getSlot(propertyName); + return slot == null ? false : !slot.isTypeInferred(); + } + + /** + * Whether the given property is declared on this object. + */ + final boolean hasOwnDeclaredProperty(String name) { + return hasOwnProperty(name) && isPropertyTypeDeclared(name); + } + + /** Checks whether the property was defined in the externs. */ + public boolean isPropertyInExterns(String propertyName) { + Property p = getSlot(propertyName); + return p == null ? false : p.isFromExterns(); + } + + /** + * Gets the number of properties of this object. + */ + public int getPropertiesCount() { + return getPropertyMap().getPropertiesCount(); + } + + /** + * Returns a list of properties defined or inferred on this type and any of + * its supertypes. + */ + public Set getPropertyNames() { + Set props = Sets.newTreeSet(); + collectPropertyNames(props); + return props; + } + + /** + * Adds any properties defined on this type or its supertypes to the set. + */ + final void collectPropertyNames(Set props) { + getPropertyMap().collectPropertyNames(props); + } + + @Override + public T visit(Visitor visitor) { + return visitor.caseObjectType(this); + } + + @Override T visit(RelationshipVisitor visitor, JSType that) { + return visitor.caseObjectType(this, that); + } + + /** + * Checks that the prototype is an implicit prototype of this object. Since + * each object has an implicit prototype, an implicit prototype's + * implicit prototype is also this implicit prototype's. + * + * @param prototype any prototype based object + * + * @return {@code true} if {@code prototype} is {@code equal} to any + * object in this object's implicit prototype chain. + */ + final boolean isImplicitPrototype(ObjectType prototype) { + for (ObjectType current = this; + current != null; + current = current.getImplicitPrototype()) { + if (current.isEquivalentTo(prototype)) { + return true; + } + } + return false; + } + + @Override + public BooleanLiteralSet getPossibleToBooleanOutcomes() { + return BooleanLiteralSet.TRUE; + } + + /** + * We treat this as the unknown type if any of its implicit prototype + * properties is unknown. + */ + @Override + public boolean isUnknownType() { + // If the object is unknown now, check the supertype again, + // because it might have been resolved since the last check. + if (unknown) { + ObjectType implicitProto = getImplicitPrototype(); + if (implicitProto == null || + implicitProto.isNativeObjectType()) { + unknown = false; + for (ObjectType interfaceType : getCtorExtendedInterfaces()) { + if (interfaceType.isUnknownType()) { + unknown = true; + break; + } + } + } else { + unknown = implicitProto.isUnknownType(); + } + } + return unknown; + } + + @Override + public boolean isObject() { + return true; + } + + /** + * Returns true if any cached values have been set for this type. If true, + * then the prototype chain should not be changed, as it might invalidate the + * cached values. + */ + public boolean hasCachedValues() { + return !unknown; + } + + /** + * Clear cached values. Should be called before making changes to a prototype + * that may have been changed since creation. + */ + public void clearCachedValues() { + unknown = true; + } + + /** Whether this is a built-in object. */ + public boolean isNativeObjectType() { + return false; + } + + /** + * A null-safe version of JSType#toObjectType. + */ + public static ObjectType cast(JSType type) { + return type == null ? null : type.toObjectType(); + } + + @Override + public final boolean isFunctionPrototypeType() { + return getOwnerFunction() != null; + } + + /** Gets the owner of this if it's a function prototype. */ + public FunctionType getOwnerFunction() { + return null; + } + + /** Sets the owner function. By default, does nothing. */ + void setOwnerFunction(FunctionType type) {} + + /** + * Gets the interfaces implemented by the ctor associated with this type. + * Intended to be overridden by subclasses. + */ + public Iterable getCtorImplementedInterfaces() { + return ImmutableSet.of(); + } + + /** + * Gets the interfaces extended by the interface associated with this type. + * Intended to be overridden by subclasses. + */ + public Iterable getCtorExtendedInterfaces() { + return ImmutableSet.of(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ParameterizedType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ParameterizedType.java new file mode 100644 index 0000000..9845ce8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ParameterizedType.java @@ -0,0 +1,160 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.common.base.Preconditions; + +/** + * An object type with a declared default element type, such as + * Array.. + * + * // TODO(user): Define the subtyping relation for parameterized types. Also, + * take parameterized type into account for equality. + * + */ +public final class ParameterizedType extends ProxyObjectType { + private static final long serialVersionUID = 1L; + + final JSType parameterType; + + ParameterizedType( + JSTypeRegistry registry, ObjectType objectType, JSType parameterType) { + super(registry, objectType); + this.parameterType = parameterType; + } + + @Override + public JSType getParameterType() { + return parameterType; + } + + @Override + String toStringHelper(boolean forAnnotations) { + String result = super.toStringHelper(forAnnotations); + return result + ".<" + parameterType.toStringHelper(forAnnotations) + ">"; + } + + @Override + public T visit(Visitor visitor) { + return visitor.caseParameterizedType(this); + } + + @Override T visit(RelationshipVisitor visitor, JSType that) { + return visitor.caseParameterizedType(this, that); + } + + @Override + public ParameterizedType toMaybeParameterizedType() { + return this; + } + + @Override + public boolean hasAnyTemplateTypesInternal() { + return super.hasAnyTemplateTypes() || parameterType.hasAnyTemplateTypes(); + } + + @Override + public boolean isSubtype(JSType that) { + return isSubtypeHelper(this, that); + } + + boolean isParameterizeSubtypeOf(JSType thatType) { + if (thatType.isParameterizedType()) { + JSType thisParameter = this.parameterType; + JSType thatParameter = thatType.toMaybeParameterizedType().parameterType; + // Currently, there is no way to declare a parameterized type so we have + // no way to determine if the type parameters are in anyway related. + // + // Right now we fallback to the raw type relationship if the raw types + // are different. This is not great, and we'll figure out a better + // solution later. + if (this.wrapsSameRawType(thatType)) { + return (thisParameter.isSubtype(thatParameter) + || thatParameter.isSubtype(thisParameter)); + } + } + + return this.getReferencedTypeInternal().isSubtype(thatType); + } + + boolean wrapsSameRawType(JSType that) { + return that.isParameterizedType() && this.getReferencedTypeInternal() + .isEquivalentTo( + that.toMaybeParameterizedType().getReferencedTypeInternal()); + } + + boolean wrapsRawType(JSType that) { + return this.getReferencedTypeInternal().isEquivalentTo(that); + } + + /** + * Computes the greatest subtype of two related parameterized types. + * @return The greatest subtype. + */ + JSType getGreatestSubtypeHelper(JSType rawThat) { + Preconditions.checkNotNull(rawThat); + + if (!wrapsSameRawType(rawThat)) { + if (!rawThat.isParameterizedType()) { + if (this.isSubtype(rawThat)) { + return this; + } else if (rawThat.isSubtype(this)) { + return filterNoResolvedType(rawThat); + } + } + if (this.isObject() && rawThat.isObject()) { + return this.getNativeType(JSTypeNative.NO_OBJECT_TYPE); + } + return this.getNativeType(JSTypeNative.NO_TYPE); + } + + ParameterizedType that = rawThat.toMaybeParameterizedType(); + Preconditions.checkNotNull(that); + + if (this.parameterType.isEquivalentTo(that.parameterType)) { + return this; + } + + // For types that have the same raw type but different type parameters, + // we simply create a type has a "unknown" type parameter. This is + // equivalent to the raw type. + return getReferencedObjTypeInternal(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/Property.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/Property.java new file mode 100644 index 0000000..699512a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/Property.java @@ -0,0 +1,141 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; + +import java.io.Serializable; + +/** + * A property slot of an object. + * @author nicksantos@google.com (Nick Santos) + */ +public final class Property + implements Serializable, StaticSlot, StaticReference { + private static final long serialVersionUID = 1L; + + /** + * Property's name. + */ + private final String name; + + /** + * Property's type. + */ + private JSType type; + + /** + * Whether the property's type is inferred. + */ + private final boolean inferred; + + /** + * The node corresponding to this property, e.g., a GETPROP node that + * declares this property. + */ + private Node propertyNode; + + /** The JSDocInfo for this property. */ + private JSDocInfo docInfo = null; + + Property(String name, JSType type, boolean inferred, + Node propertyNode) { + this.name = name; + this.type = type; + this.inferred = inferred; + this.propertyNode = propertyNode; + } + + @Override + public String getName() { + return name; + } + + @Override + public Node getNode() { + return propertyNode; + } + + @Override + public StaticSourceFile getSourceFile() { + return propertyNode == null ? null : propertyNode.getStaticSourceFile(); + } + + @Override + public Property getSymbol() { + return this; + } + + @Override + public Property getDeclaration() { + return propertyNode == null ? null : this; + } + + @Override + public JSType getType() { + return type; + } + + @Override + public boolean isTypeInferred() { + return inferred; + } + + boolean isFromExterns() { + return propertyNode == null ? false : propertyNode.isFromExterns(); + } + + void setType(JSType type) { + this.type = type; + } + + @Override public JSDocInfo getJSDocInfo() { + return this.docInfo; + } + + void setJSDocInfo(JSDocInfo info) { + this.docInfo = info; + } + + public void setNode(Node n) { + this.propertyNode = n; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/PropertyMap.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/PropertyMap.java new file mode 100644 index 0000000..efbdb73 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/PropertyMap.java @@ -0,0 +1,205 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import java.io.Serializable; +import java.util.Map; +import java.util.Set; + +/** + * Representation for a collection of properties on an object. + * @author nicksantos@google.com (Nick Santos) + */ +class PropertyMap implements Serializable { + private static final long serialVersionUID = 1L; + + private static final PropertyMap EMPTY_MAP = new PropertyMap( + ImmutableMap.of()); + + private static final Function PROP_MAP_FROM_TYPE = + new Function() { + @Override public PropertyMap apply(ObjectType t) { + return t.getPropertyMap(); + } + }; + + // A place to get the inheritance structure. + // Because the extended interfaces are resolved dynamically, this gets + // messy :(. If type-resolution was more well-defined, we could + // just reference primary parents and secondary parents directly. + private ObjectType parentSource = null; + + // The map of our own properties. + private final Map properties; + + PropertyMap() { + this(Maps.newTreeMap()); + } + + private PropertyMap(Map underlyingMap) { + this.properties = underlyingMap; + } + + static PropertyMap immutableEmptyMap() { + return EMPTY_MAP; + } + + void setParentSource(ObjectType ownerType) { + if (this != EMPTY_MAP) { + this.parentSource = ownerType; + } + } + + /** Returns the direct parent of this property map. */ + PropertyMap getPrimaryParent() { + if (parentSource == null) { + return null; + } + ObjectType iProto = parentSource.getImplicitPrototype(); + return iProto == null ? null : iProto.getPropertyMap(); + } + + /** + * Returns the secondary parents of this property map, for interfaces that + * need multiple inheritance. + */ + Iterable getSecondaryParents() { + if (parentSource == null) { + return ImmutableList.of(); + } + Iterable extendedInterfaces = + parentSource.getCtorExtendedInterfaces(); + + // Most of the time, this will be empty. + if (Iterables.isEmpty(extendedInterfaces)) { + return ImmutableList.of(); + } + + return Iterables.transform(extendedInterfaces, PROP_MAP_FROM_TYPE); + } + + Property getSlot(String name) { + if (properties.containsKey(name)) { + return properties.get(name); + } + PropertyMap primaryParent = getPrimaryParent(); + if (primaryParent != null) { + Property prop = primaryParent.getSlot(name); + if (prop != null) { + return prop; + } + } + for (PropertyMap p : getSecondaryParents()) { + if (p != null) { + Property prop = p.getSlot(name); + if (prop != null) { + return prop; + } + } + } + return null; + } + + Property getOwnProperty(String propertyName) { + return properties.get(propertyName); + } + + int getPropertiesCount() { + PropertyMap primaryParent = getPrimaryParent(); + if (primaryParent == null) { + return this.properties.size(); + } + Set props = Sets.newHashSet(); + collectPropertyNames(props); + return props.size(); + } + + boolean hasOwnProperty(String propertyName) { + return properties.get(propertyName) != null; + } + + boolean hasProperty(String propertyName) { + return getSlot(propertyName) != null; + } + + Set getOwnPropertyNames() { + return properties.keySet(); + } + + void collectPropertyNames(Set props) { + for (String prop : properties.keySet()) { + props.add(prop); + } + PropertyMap primaryParent = getPrimaryParent(); + if (primaryParent != null) { + primaryParent.collectPropertyNames(props); + } + for (PropertyMap p : getSecondaryParents()) { + if (p != null) { + p.collectPropertyNames(props); + } + } + } + + boolean removeProperty(String name) { + return properties.remove(name) != null; + } + + void putProperty(String name, Property newProp) { + Property oldProp = properties.get(name); + if (oldProp != null) { + // This is to keep previously inferred JsDoc info, e.g., in a + // replaceScript scenario. + newProp.setJSDocInfo(oldProp.getJSDocInfo()); + } + properties.put(name, newProp); + } + + Iterable values() { + return properties.values(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/PrototypeObjectType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/PrototypeObjectType.java new file mode 100644 index 0000000..3da752b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/PrototypeObjectType.java @@ -0,0 +1,469 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Sets; +import com.google.javascript.rhino.ErrorReporter; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; + +import java.util.Set; + +/** + * The object type represents instances of JavaScript objects such as + * {@code Object}, {@code Date}, {@code Function}.

      + * + * Objects in JavaScript are unordered collections of properties. + * Each property consists of a name, a value and a set of attributes.

      + * + * Each instance has an implicit prototype property ({@code [[Prototype]]}) + * pointing to an object instance, which itself has an implicit property, thus + * forming a chain.

      + * + * A class begins life with no name. Later, a name may be provided once it + * can be inferred. Note that the name in this case is strictly for + * debugging purposes. Looking up type name references goes through the + * {@link JSTypeRegistry}.

      + */ +class PrototypeObjectType extends ObjectType { + private static final long serialVersionUID = 1L; + + private final String className; + private final PropertyMap properties; + private final boolean nativeType; + + // NOTE(nicksantos): The implicit prototype can change over time. + // Modeling this is a bear. Always call getImplicitPrototype(), because + // some subclasses override this to do special resolution handling. + private ObjectType implicitPrototypeFallback; + + // If this is a function prototype, then this is the owner. + // A PrototypeObjectType can only be the prototype of one function. If we try + // to do this for multiple functions, then we'll have to create a new one. + private FunctionType ownerFunction = null; + + // Whether the toString representation of this should be pretty-printed, + // by printing all properties. + private boolean prettyPrint = false; + + private static final int MAX_PRETTY_PRINTED_PROPERTIES = 4; + + /** + * Creates an object type. + * + * @param className the name of the class. May be {@code null} to + * denote an anonymous class. + * + * @param implicitPrototype the implicit prototype + * (a.k.a. {@code [[Prototype]]}) as defined by ECMA-262. If the + * implicit prototype is {@code null} the implicit prototype will be + * set to the {@link JSTypeNative#OBJECT_TYPE}. + */ + PrototypeObjectType(JSTypeRegistry registry, String className, + ObjectType implicitPrototype) { + this(registry, className, implicitPrototype, false, null, null); + } + + /** + * Creates an object type, allowing specification of the implicit prototype, + * whether the object is native, and any templatized types. + */ + PrototypeObjectType(JSTypeRegistry registry, String className, + ObjectType implicitPrototype, boolean nativeType, + ImmutableList templateKeys, + ImmutableList templatizedTypes) { + super(registry, templateKeys, templatizedTypes); + this.properties = new PropertyMap(); + this.properties.setParentSource(this); + + this.className = className; + this.nativeType = nativeType; + if (nativeType || implicitPrototype != null) { + setImplicitPrototype(implicitPrototype); + } else { + setImplicitPrototype( + registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE)); + } + } + + @Override + PropertyMap getPropertyMap() { + return properties; + } + + @Override + boolean defineProperty(String name, JSType type, boolean inferred, + Node propertyNode) { + if (hasOwnDeclaredProperty(name)) { + return false; + } + Property newProp = new Property( + name, type, inferred, propertyNode); + properties.putProperty(name, newProp); + return true; + } + + @Override + public boolean removeProperty(String name) { + return properties.removeProperty(name); + } + + @Override + public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) { + if (info != null) { + if (properties.getOwnProperty(propertyName) == null) { + // If docInfo was attached, but the type of the property + // was not defined anywhere, then we consider this an explicit + // declaration of the property. + defineInferredProperty(propertyName, getPropertyType(propertyName), + null); + } + + // The prototype property is not represented as a normal Property. + // We probably don't want to attach any JSDoc to it anyway. + Property property = properties.getOwnProperty(propertyName); + if (property != null) { + property.setJSDocInfo(info); + } + } + } + + @Override + public boolean matchesNumberContext() { + return isNumberObjectType() || isDateType() || isBooleanObjectType() || + isStringObjectType() || hasOverridenNativeProperty("valueOf"); + } + + @Override + public boolean matchesStringContext() { + return isTheObjectType() || isStringObjectType() || isDateType() || + isRegexpType() || isArrayType() || isNumberObjectType() || + isBooleanObjectType() || hasOverridenNativeProperty("toString"); + } + + /** + * Given the name of a native object property, checks whether the property is + * present on the object and different from the native one. + */ + private boolean hasOverridenNativeProperty(String propertyName) { + if (isNativeObjectType()) { + return false; + } + + JSType propertyType = getPropertyType(propertyName); + ObjectType nativeType = + isFunctionType() ? + registry.getNativeObjectType(JSTypeNative.FUNCTION_PROTOTYPE) : + registry.getNativeObjectType(JSTypeNative.OBJECT_PROTOTYPE); + JSType nativePropertyType = nativeType.getPropertyType(propertyName); + return propertyType != nativePropertyType; + } + + @Override + public JSType unboxesTo() { + if (isStringObjectType()) { + return getNativeType(JSTypeNative.STRING_TYPE); + } else if (isBooleanObjectType()) { + return getNativeType(JSTypeNative.BOOLEAN_TYPE); + } else if (isNumberObjectType()) { + return getNativeType(JSTypeNative.NUMBER_TYPE); + } else { + return super.unboxesTo(); + } + } + + @Override + public boolean matchesObjectContext() { + return true; + } + + @Override + public boolean canBeCalled() { + return isRegexpType(); + } + + @Override + String toStringHelper(boolean forAnnotations) { + if (hasReferenceName()) { + return getReferenceName(); + } else if (prettyPrint) { + // Don't pretty print recursively. + prettyPrint = false; + + // Use a tree set so that the properties are sorted. + Set propertyNames = Sets.newTreeSet(); + for (ObjectType current = this; + current != null && !current.isNativeObjectType() && + propertyNames.size() <= MAX_PRETTY_PRINTED_PROPERTIES; + current = current.getImplicitPrototype()) { + propertyNames.addAll(current.getOwnPropertyNames()); + } + + StringBuilder sb = new StringBuilder(); + sb.append("{"); + + int i = 0; + for (String property : propertyNames) { + if (i > 0) { + sb.append(", "); + } + + sb.append(property); + sb.append(": "); + sb.append(getPropertyType(property).toStringHelper(forAnnotations)); + + ++i; + if (!forAnnotations && i == MAX_PRETTY_PRINTED_PROPERTIES) { + sb.append(", ..."); + break; + } + } + + sb.append("}"); + + prettyPrint = true; + return sb.toString(); + } else { + return forAnnotations ? "?" : "{...}"; + } + } + + void setPrettyPrint(boolean prettyPrint) { + this.prettyPrint = prettyPrint; + } + + boolean isPrettyPrint() { + return prettyPrint; + } + + @Override + public FunctionType getConstructor() { + return null; + } + + @Override + public ObjectType getImplicitPrototype() { + return implicitPrototypeFallback; + } + + /** + * This should only be reset on the FunctionPrototypeType, only to fix an + * incorrectly established prototype chain due to the user having a mismatch + * in super class declaration, and only before properties on that type are + * processed. + */ + final void setImplicitPrototype(ObjectType implicitPrototype) { + checkState(!hasCachedValues()); + this.implicitPrototypeFallback = implicitPrototype; + } + + @Override + public String getReferenceName() { + if (className != null) { + return className; + } else if (ownerFunction != null) { + return ownerFunction.getReferenceName() + ".prototype"; + } else { + return null; + } + } + + @Override + public boolean hasReferenceName() { + return className != null || ownerFunction != null; + } + + @Override + public boolean isSubtype(JSType that) { + if (JSType.isSubtypeHelper(this, that)) { + return true; + } + + // Union types + if (that.isUnionType()) { + // The static {@code JSType.isSubtype} check already decomposed + // union types, so we don't need to check those again. + return false; + } + + // record types + if (that.isRecordType()) { + return RecordType.isSubtype(this, that.toMaybeRecordType()); + } + + // Interfaces + // Find all the interfaces implemented by this class and compare each one + // to the interface instance. + ObjectType thatObj = that.toObjectType(); + FunctionType thatCtor = thatObj == null ? null : thatObj.getConstructor(); + + if (getConstructor() != null && getConstructor().isInterface()) { + for (ObjectType thisInterface : getCtorExtendedInterfaces()) { + if (thisInterface.isSubtype(that)) { + return true; + } + } + } else if (thatCtor != null && thatCtor.isInterface()) { + Iterable thisInterfaces = getCtorImplementedInterfaces(); + for (ObjectType thisInterface : thisInterfaces) { + if (thisInterface.isSubtype(that)) { + return true; + } + } + } + + // other prototype based objects + if (isUnknownType() || implicitPrototypeChainIsUnknown()) { + // If unsure, say 'yes', to avoid spurious warnings. + // TODO(user): resolve the prototype chain completely in all cases, + // to avoid guessing. + return true; + } + return thatObj != null && isImplicitPrototype(thatObj); + } + + private boolean implicitPrototypeChainIsUnknown() { + ObjectType p = getImplicitPrototype(); + while (p != null) { + if (p.isUnknownType()) { + return true; + } + p = p.getImplicitPrototype(); + } + return false; + } + + @Override + public boolean hasCachedValues() { + return super.hasCachedValues(); + } + + /** Whether this is a built-in object. */ + @Override + public boolean isNativeObjectType() { + return nativeType; + } + + @Override + void setOwnerFunction(FunctionType type) { + Preconditions.checkState(ownerFunction == null || type == null); + ownerFunction = type; + } + + @Override + public FunctionType getOwnerFunction() { + return ownerFunction; + } + + @Override + public Iterable getCtorImplementedInterfaces() { + return isFunctionPrototypeType() + ? getOwnerFunction().getImplementedInterfaces() + : ImmutableList.of(); + } + + @Override + public Iterable getCtorExtendedInterfaces() { + return isFunctionPrototypeType() + ? getOwnerFunction().getExtendedInterfaces() + : ImmutableList.of(); + } + + @Override + JSType resolveInternal(ErrorReporter t, StaticScope scope) { + setResolvedTypeInternal(this); + + ObjectType implicitPrototype = getImplicitPrototype(); + if (implicitPrototype != null) { + implicitPrototypeFallback = + (ObjectType) implicitPrototype.resolve(t, scope); + } + for (Property prop : properties.values()) { + prop.setType(safeResolve(prop.getType(), t, scope)); + } + return this; + } + + @Override + public void matchConstraint(JSType constraint) { + // We only want to match constraints on anonymous types. + if (hasReferenceName()) { + return; + } + + // Handle the case where the constraint object is a record type. + // + // param constraint {{prop: (number|undefined)}} + // function f(constraint) {} + // f({}); + // + // We want to modify the object literal to match the constraint, by + // taking any each property on the record and trying to match + // properties on this object. + if (constraint.isRecordType()) { + matchRecordTypeConstraint(constraint.toObjectType()); + } else if (constraint.isUnionType()) { + for (JSType alt : constraint.toMaybeUnionType().getAlternates()) { + if (alt.isRecordType()) { + matchRecordTypeConstraint(alt.toObjectType()); + } + } + } + } + + public void matchRecordTypeConstraint(ObjectType constraintObj) { + for (String prop : constraintObj.getOwnPropertyNames()) { + JSType propType = constraintObj.getPropertyType(prop); + if (!isPropertyTypeDeclared(prop)) { + JSType typeToInfer = propType; + if (!hasProperty(prop)) { + typeToInfer = getNativeType(JSTypeNative.VOID_TYPE) + .getLeastSupertype(propType); + } + defineInferredProperty(prop, typeToInfer, null); + } + } + } + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ProxyObjectType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ProxyObjectType.java new file mode 100644 index 0000000..ec24701 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ProxyObjectType.java @@ -0,0 +1,371 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.ErrorReporter; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; + +import java.util.Collections; + +/** + * An object type which uses composition to delegate all calls. + * + * @see NamedType + * @see ParameterizedType + * + */ +class ProxyObjectType extends ObjectType { + private static final long serialVersionUID = 1L; + + private JSType referencedType; + private ObjectType referencedObjType; + + ProxyObjectType(JSTypeRegistry registry, JSType referencedType) { + super(registry); + setReferencedType(referencedType); + } + + @Override + PropertyMap getPropertyMap() { + return referencedObjType == null + ? PropertyMap.immutableEmptyMap() : referencedObjType.getPropertyMap(); + } + + JSType getReferencedTypeInternal() { + return referencedType; + } + + ObjectType getReferencedObjTypeInternal() { + return referencedObjType; + } + + void setReferencedType(JSType referencedType) { + this.referencedType = referencedType; + if (referencedType instanceof ObjectType) { + this.referencedObjType = (ObjectType) referencedType; + } else { + this.referencedObjType = null; + } + } + + @Override + public String getReferenceName() { + return referencedObjType == null ? + "" : referencedObjType.getReferenceName(); + } + + @Override + public boolean hasReferenceName() { + return referencedObjType == null ? + null : referencedObjType.hasReferenceName(); + } + + @Override + public boolean matchesNumberContext() { + return referencedType.matchesNumberContext(); + } + + @Override + public boolean matchesStringContext() { + return referencedType.matchesStringContext(); + } + + @Override + public boolean matchesObjectContext() { + return referencedType.matchesObjectContext(); + } + + @Override + public boolean canBeCalled() { + return referencedType.canBeCalled(); + } + + @Override + public boolean isNoType() { + return referencedType.isNoType(); + } + + @Override + public boolean isNoObjectType() { + return referencedType.isNoObjectType(); + } + + @Override + public boolean isNoResolvedType() { + return referencedType.isNoResolvedType(); + } + + @Override + public boolean isUnknownType() { + return referencedType.isUnknownType(); + } + + @Override + public boolean isCheckedUnknownType() { + return referencedType.isCheckedUnknownType(); + } + + @Override + public boolean isNullable() { + return referencedType.isNullable(); + } + + @Override + public EnumType toMaybeEnumType() { + return referencedType.toMaybeEnumType(); + } + + @Override + public boolean isConstructor() { + return referencedType.isConstructor(); + } + + @Override + public boolean isNominalType() { + return referencedType.isNominalType(); + } + + @Override + public boolean isInstanceType() { + return referencedType.isInstanceType(); + } + + @Override + public boolean isInterface() { + return referencedType.isInterface(); + } + + @Override + public boolean isOrdinaryFunction() { + return referencedType.isOrdinaryFunction(); + } + + @Override + public boolean isAllType() { + return referencedType.isAllType(); + } + + @Override + public boolean isStruct() { + return referencedType.isStruct(); + } + + @Override + public boolean isDict() { + return referencedType.isDict(); + } + + @Override + public boolean isNativeObjectType() { + return referencedObjType == null + ? false : referencedObjType.isNativeObjectType(); + } + + @Override + RecordType toMaybeRecordType() { + return referencedType.toMaybeRecordType(); + } + + @Override + public UnionType toMaybeUnionType() { + return referencedType.toMaybeUnionType(); + } + + @Override + public FunctionType toMaybeFunctionType() { + return referencedType.toMaybeFunctionType(); + } + + @Override + public EnumElementType toMaybeEnumElementType() { + return referencedType.toMaybeEnumElementType(); + } + + @Override + public TernaryValue testForEquality(JSType that) { + return referencedType.testForEquality(that); + } + + @Override + public boolean isSubtype(JSType that) { + return referencedType.isSubtype(that); + } + + @Override + public FunctionType getOwnerFunction() { + return referencedObjType == null + ? null : referencedObjType.getOwnerFunction(); + } + + @Override + public Iterable getCtorImplementedInterfaces() { + return referencedObjType == null ? Collections.emptyList() : + referencedObjType.getCtorImplementedInterfaces(); + } + + @Override + public int hashCode() { + return referencedType.hashCode(); + } + + @Override + String toStringHelper(boolean forAnnotations) { + return referencedType.toStringHelper(forAnnotations); + } + + @Override + public ObjectType getImplicitPrototype() { + return referencedObjType == null ? null : + referencedObjType.getImplicitPrototype(); + } + + @Override + boolean defineProperty(String propertyName, JSType type, + boolean inferred, Node propertyNode) { + return referencedObjType == null ? true : + referencedObjType.defineProperty( + propertyName, type, inferred, propertyNode); + } + + @Override + public boolean removeProperty(String name) { + return referencedObjType == null ? false : + referencedObjType.removeProperty(name); + } + + @Override + public JSType findPropertyType(String propertyName) { + return referencedType.findPropertyType(propertyName); + } + + @Override + public JSDocInfo getJSDocInfo() { + return referencedType.getJSDocInfo(); + } + + @Override + public void setJSDocInfo(JSDocInfo info) { + if (referencedObjType != null) { + referencedObjType.setJSDocInfo(info); + } + } + + @Override + public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) { + if (referencedObjType != null) { + referencedObjType.setPropertyJSDocInfo(propertyName, info); + } + } + + @Override + public FunctionType getConstructor() { + return referencedObjType == null ? null : + referencedObjType.getConstructor(); + } + + @Override + public JSType getParameterType() { + return referencedObjType == null ? null : + referencedObjType.getParameterType(); + } + + @Override + public JSType getIndexType() { + return referencedObjType == null ? null : + referencedObjType.getIndexType(); + } + + @Override + public T visit(Visitor visitor) { + return referencedType.visit(visitor); + } + + @Override T visit(RelationshipVisitor visitor, JSType that) { + return referencedType.visit(visitor, that); + } + + @Override + JSType resolveInternal(ErrorReporter t, StaticScope scope) { + setReferencedType(referencedType.resolve(t, scope)); + return this; + } + + @Override + public String toDebugHashCodeString() { + return "{proxy:" + referencedType.toDebugHashCodeString() + "}"; + } + + @Override + public JSType getTypeOfThis() { + if (referencedObjType != null) { + return referencedObjType.getTypeOfThis(); + } + return super.getTypeOfThis(); + } + + @Override + public JSType collapseUnion() { + if (referencedType.isUnionType()) { + return referencedType.collapseUnion(); + } + return this; + } + + @Override + public void matchConstraint(JSType constraint) { + referencedType.matchConstraint(constraint); + } + + @Override + public ParameterizedType toMaybeParameterizedType() { + return referencedType.toMaybeParameterizedType(); + } + + @Override + public TemplateType toMaybeTemplateType() { + return referencedType.toMaybeTemplateType(); + } + + @Override + public boolean hasAnyTemplateTypesInternal() { + return referencedType.hasAnyTemplateTypes(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/RecordType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/RecordType.java new file mode 100755 index 0000000..c2f0249 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/RecordType.java @@ -0,0 +1,275 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.RecordTypeBuilder.RecordProperty; + +import java.util.Map; +import java.util.Set; + +/** + * A record (structural) type. + * + * Subtyping: The subtyping of a record type is defined via structural + * comparison of a record type's properties. For example, a record + * type of the form { a : TYPE_1 } is a supertype of a record type + * of the form { b : TYPE_2, a : TYPE_1 } because B can be assigned to + * A and matches all constraints. Similarly, a defined type can be assigned + * to a record type so long as that defined type matches all property + * constraints of the record type. A record type of the form { a : A, b : B } + * can be assigned to a record of type { a : A }. + * + */ +class RecordType extends PrototypeObjectType { + private static final long serialVersionUID = 1L; + + private final boolean declared; + private boolean isFrozen = false; + + RecordType(JSTypeRegistry registry, Map properties) { + this(registry, properties, true); + } + + /** + * Creates a record type. + * + * @param registry The type registry under which this type lives. + * @param properties A map of all the properties of this record type. + * @param declared Whether this is a declared or synthesized type. + * A synthesized record type is just used for bookkeeping + * in the type system. A declared record type was actually used in the + * user's program. + * @throws IllegalStateException if the {@code RecordProperty} associated + * with a property is null. + */ + RecordType(JSTypeRegistry registry, Map properties, + boolean declared) { + super(registry, null, null); + setPrettyPrint(true); + this.declared = declared; + + for (String property : properties.keySet()) { + RecordProperty prop = properties.get(property); + if (prop == null) { + throw new IllegalStateException( + "RecordProperty associated with a property should not be null!"); + } + if (declared) { + defineDeclaredProperty( + property, prop.getType(), prop.getPropertyNode()); + } else { + defineSynthesizedProperty( + property, prop.getType(), prop.getPropertyNode()); + } + } + + // Freeze the record type. + isFrozen = true; + } + + /** @return Is this synthesized for internal bookkeeping? */ + boolean isSynthetic() { + return !declared; + } + + boolean checkRecordEquivalenceHelper( + RecordType otherRecord, EquivalenceMethod eqMethod) { + Set keySet = getOwnPropertyNames(); + Set otherKeySet = otherRecord.getOwnPropertyNames(); + if (!otherKeySet.equals(keySet)) { + return false; + } + for (String key : keySet) { + if (!otherRecord.getPropertyType(key).checkEquivalenceHelper( + getPropertyType(key), eqMethod)) { + return false; + } + } + return true; + } + + @Override + public ObjectType getImplicitPrototype() { + return registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE); + } + + @Override + boolean defineProperty(String propertyName, JSType type, + boolean inferred, Node propertyNode) { + if (isFrozen) { + return false; + } + + return super.defineProperty(propertyName, type, inferred, + propertyNode); + } + + JSType getGreatestSubtypeHelper(JSType that) { + if (that.isRecordType()) { + RecordType thatRecord = that.toMaybeRecordType(); + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.setSynthesized(true); + + // The greatest subtype consists of those *unique* properties of both + // record types. If any property conflicts, then the NO_TYPE type + // is returned. + for (String property : getOwnPropertyNames()) { + if (thatRecord.hasProperty(property) && + !thatRecord.getPropertyType(property).isInvariant( + getPropertyType(property))) { + return registry.getNativeObjectType(JSTypeNative.NO_TYPE); + } + + builder.addProperty(property, getPropertyType(property), + getPropertyNode(property)); + } + + for (String property : thatRecord.getOwnPropertyNames()) { + if (!hasProperty(property)) { + builder.addProperty(property, thatRecord.getPropertyType(property), + thatRecord.getPropertyNode(property)); + } + } + + return builder.build(); + } + + JSType greatestSubtype = registry.getNativeType( + JSTypeNative.NO_OBJECT_TYPE); + JSType thatRestrictedToObj = + registry.getNativeType(JSTypeNative.OBJECT_TYPE) + .getGreatestSubtype(that); + if (!thatRestrictedToObj.isEmptyType()) { + // In this branch, the other type is some object type. We find + // the greatest subtype with the following algorithm: + // 1) For each property "x" of this record type, take the union + // of all classes with a property "x" with a compatible property type. + // and which are a subtype of {@code that}. + // 2) Take the intersection of all of these unions. + for (String propName : getOwnPropertyNames()) { + JSType propType = getPropertyType(propName); + UnionTypeBuilder builder = new UnionTypeBuilder(registry); + for (ObjectType alt : + registry.getEachReferenceTypeWithProperty(propName)) { + JSType altPropType = alt.getPropertyType(propName); + if (altPropType != null && !alt.isEquivalentTo(this) && + alt.isSubtype(that) && + propType.isInvariant(altPropType)) { + builder.addAlternate(alt); + } + } + greatestSubtype = greatestSubtype.getLeastSupertype(builder.build()); + } + } + return greatestSubtype; + } + + @Override + RecordType toMaybeRecordType() { + return this; + } + + @Override + public boolean isSubtype(JSType that) { + if (JSType.isSubtypeHelper(this, that)) { + return true; + } + + // Top of the record types is the empty record, or OBJECT_TYPE. + if (registry.getNativeObjectType( + JSTypeNative.OBJECT_TYPE).isSubtype(that)) { + return true; + } + + // A type is a subtype of a record type if it itself is a record + // type and it has at least the same members as the parent record type + // with the same types. + if (!that.isRecordType()) { + return false; + } + + return RecordType.isSubtype(this, that.toMaybeRecordType()); + } + + /** Determines if typeA is a subtype of typeB */ + static boolean isSubtype(ObjectType typeA, RecordType typeB) { + // typeA is a subtype of record type typeB iff: + // 1) typeA has all the properties declared in typeB. + // 2) And for each property of typeB, + // 2a) if the property of typeA is declared, it must be equal + // to the type of the property of typeB, + // 2b) otherwise, it must be a subtype of the property of typeB. + // + // To figure out why this is true, consider the following pseudo-code: + // /** @type {{a: (Object,null)}} */ var x; + // /** @type {{a: !Object}} */ var y; + // var z = {a: {}}; + // x.a = null; + // + // y cannot be assigned to x, because line 4 would violate y's declared + // properties. But z can be assigned to x. Even though z and y are the + // same type, the properties of z are inferred--and so an assignment + // to the property of z would not violate any restrictions on it. + for (String property : typeB.getOwnPropertyNames()) { + if (!typeA.hasProperty(property)) { + return false; + } + + JSType propA = typeA.getPropertyType(property); + JSType propB = typeB.getPropertyType(property); + if (typeA.isPropertyTypeDeclared(property)) { + // If one declared property isn't invariant, + // then the whole record isn't covariant. + if (!propA.isInvariant(propB)) { + return false; + } + } else { + // If one inferred property isn't a subtype, + // then the whole record isn't covariant. + if (!propA.isSubtype(propB)) { + return false; + } + } + } + + return true; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/RecordTypeBuilder.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/RecordTypeBuilder.java new file mode 100755 index 0000000..60d1e10 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/RecordTypeBuilder.java @@ -0,0 +1,116 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.common.collect.Maps; +import com.google.javascript.rhino.Node; + +import java.util.Collections; +import java.util.HashMap; + +/** + * A builder for record types. + * + */ +public class RecordTypeBuilder { + private boolean isEmpty = true; + private boolean isDeclared = true; + private final JSTypeRegistry registry; + private final HashMap properties = Maps.newHashMap(); + + public RecordTypeBuilder(JSTypeRegistry registry) { + this.registry = registry; + } + + /** See the comments on RecordType about synthetic types. */ + void setSynthesized(boolean synthesized) { + isDeclared = !synthesized; + } + + /** + * Adds a property with the given name and type to the record type. + * @param name the name of the new property + * @param type the JSType of the new property + * @param propertyNode the node that holds this property definition + * @return The builder itself for chaining purposes, or null if there's + * a duplicate. + */ + public RecordTypeBuilder addProperty(String name, JSType type, Node + propertyNode) { + isEmpty = false; + if (properties.containsKey(name)) { + return null; + } + properties.put(name, new RecordProperty(type, propertyNode)); + return this; + } + + /** + * Creates a record. + * @return The record type. + */ + public JSType build() { + // If we have an empty record, simply return the object type. + if (isEmpty) { + return registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE); + } + + return new RecordType( + registry, Collections.unmodifiableMap(properties), isDeclared); + } + + static class RecordProperty { + private final JSType type; + private final Node propertyNode; + + RecordProperty(JSType type, Node propertyNode) { + this.type = type; + this.propertyNode = propertyNode; + } + + public JSType getType() { + return type; + } + + public Node getPropertyNode() { + return propertyNode; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/RelationshipVisitor.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/RelationshipVisitor.java new file mode 100644 index 0000000..043d9b3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/RelationshipVisitor.java @@ -0,0 +1,109 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Lenz + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + + + +/** + * A type relationship visitor.

      + * + * This code will calculate a specific value of type {@code T} from + * two types based on its structure. + * + * @author johnlenz@google.com (John Lenz) + */ +interface RelationshipVisitor { + + /** + * Unknown type's case. + */ + T caseUnknownType(JSType thisType, JSType thatType); + + /** + * Bottom type's case. + */ + T caseNoType(JSType thatType); + + /** + * Bottom Object type's case. + */ + T caseNoObjectType(JSType thatType); + + /** + * All type's case. + */ + T caseAllType(JSType thatType); + + /** + * Value type's case. + */ + T caseValueType(ValueType thisType, JSType thatType); + + /** + * Object type's case. + */ + T caseObjectType(ObjectType thisType, JSType thatType); + + /** + * Function type's case. + */ + T caseFunctionType(FunctionType thisType, JSType thatType); + + /** + * Union type's case. + */ + T caseUnionType(UnionType thisType, JSType thatType); + + /** + * Parameterized type's case. + */ + T caseParameterizedType(ParameterizedType thisType, JSType thatType); + + /** + * Template type's case. + */ + T caseTemplateType(TemplateType thisType, JSType thatType); + + /** + * Enum element type's case. + */ + T caseEnumElementType(EnumElementType typeType, JSType thatType); + +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/SimpleReference.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/SimpleReference.java new file mode 100644 index 0000000..76c3973 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/SimpleReference.java @@ -0,0 +1,79 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.Node; + +/** + * A simple immutable reference. + * @author nicksantos@google.com (Nick Santos) + */ +public class SimpleReference> + implements StaticReference { + private final T symbol; + private final Node node; + + public SimpleReference(T symbol, Node node) { + this.symbol = symbol; + this.node = node; + } + + @Override + final public T getSymbol() { + return symbol; + } + + @Override + final public Node getNode() { + return node; + } + + @Override + final public StaticSourceFile getSourceFile() { + return node.getStaticSourceFile(); + } + + @Override + public String toString() { + String sourceName = node == null ? null : node.getSourceFileName(); + int lineNo = node == null ? -1 : node.getLineno(); + return node.getQualifiedName() + "@" + sourceName + ":" + lineNo; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/SimpleSlot.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/SimpleSlot.java new file mode 100644 index 0000000..7a82da8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/SimpleSlot.java @@ -0,0 +1,88 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.JSDocInfo; + +import java.io.Serializable; + +/** + * The minimum implementation of StaticSlot. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class SimpleSlot implements StaticSlot, Serializable { + private static final long serialVersionUID = 1L; + + final String name; + final JSType type; + final boolean inferred; + + public SimpleSlot(String name, JSType type, boolean inferred) { + this.name = name; + this.type = type; + this.inferred = inferred; + } + + @Override + public String getName() { + return name; + } + + @Override + public JSType getType() { + return type; + } + + @Override + public boolean isTypeInferred() { + return inferred; + } + + @Override + public StaticReference getDeclaration() { + return null; + } + + @Override + public JSDocInfo getJSDocInfo() { + return null; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/SimpleSourceFile.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/SimpleSourceFile.java new file mode 100644 index 0000000..89fb8c5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/SimpleSourceFile.java @@ -0,0 +1,88 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +/** + * A simple implementation of {@code StaticSourceFile} for testing. + * + * @author nicksantos@google.com (Nick Santos) + */ +public final class SimpleSourceFile implements StaticSourceFile { + private final String name; + private final boolean extern; + + public SimpleSourceFile(String name, boolean extern) { + this.name = name; + this.extern = extern; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isExtern() { + return extern; + } + + @Override + public int getColumnOfOffset(int offset) { + return 0; + } + + @Override + public int getLineOfOffset(int offset) { + return 1; + } + + @Override + public int getLineOffset(int line) { + if (line < 1) { + throw new IllegalStateException( + "Should not call getLineOffset with line number " + line); + } + return Integer.MIN_VALUE; + } + + @Override + public String toString() { + return name; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StaticReference.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StaticReference.java new file mode 100644 index 0000000..8199327 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StaticReference.java @@ -0,0 +1,64 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.Node; + +/** + * The {@code StaticReference} tells us all the ways that a {@code StaticSlot} + * is used in a program. + * + * @author nicksantos@google.com (Nick Santos) + */ +public interface StaticReference { + /** + * The variable that this reference points to. + */ + StaticSlot getSymbol(); + + /** + * The node where the reference lives. + */ + Node getNode(); + + /** + * The source file where the reference lives. + */ + StaticSourceFile getSourceFile(); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StaticScope.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StaticScope.java new file mode 100644 index 0000000..577549a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StaticScope.java @@ -0,0 +1,76 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.Node; + +/** + * The {@code StaticScope} interface must be implemented by any object that + * defines variables for the purposes of static analysis. It is distinguished + * from the {@code Scriptable} class that Rhino normally uses to represent a + * run-time scope. + * + * @param The type of information stored about the slot + */ +public interface StaticScope { + /** + * Returns the root node associated with this scope. May be null. + */ + Node getRootNode(); + + /** Returns the scope enclosing this one or null if none. */ + StaticScope getParentScope(); + + /** + * Returns any defined slot within this scope for this name. This call + * continues searching through parent scopes if a slot with this name is not + * found in the current scope. + * @param name The name of the variable slot to look up. + * @return The defined slot for the variable, or {@code null} if no + * definition exists. + */ + StaticSlot getSlot(String name); + + /** Like {@code getSlot} but does not recurse into parent scopes. */ + StaticSlot getOwnSlot(String name); + + /** Returns the expected type of {@code this} in the current scope. */ + T getTypeOfThis(); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StaticSlot.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StaticSlot.java new file mode 100644 index 0000000..e3c2c3a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StaticSlot.java @@ -0,0 +1,72 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.JSDocInfo; + +/** + * The {@code StaticSlot} interface must be implemented by variables that can + * appear as members of a {@code StaticScope}. + * + * @param The type of information stored about the slot + */ +public interface StaticSlot { + /** + * Gets the name of the slot. + */ + String getName(); + + /** + * Returns the type information, if any, for this slot. + * @return The type or {@code null} if no type is declared for it. + */ + T getType(); + + /** + * Returns whether the type has been inferred (as opposed to declared). + */ + boolean isTypeInferred(); + + /** Gets the declaration of this symbol. May not exist. */ + StaticReference getDeclaration(); + + /** Gets the JSDoc for this slot. */ + JSDocInfo getJSDocInfo(); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StaticSourceFile.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StaticSourceFile.java new file mode 100644 index 0000000..d923a71 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StaticSourceFile.java @@ -0,0 +1,89 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +/** + * The {@code StaticSourceFile} contains information about a compiler input. + * + * @author nicksantos@google.com (Nick Santos) + */ +public interface StaticSourceFile { + /** + * The name of the file. Must be unique across all files in the compilation. + */ + String getName(); + + /** + * Returns whether this is an externs file. + */ + boolean isExtern(); + + /** + * Returns the offset of the given line number relative to the file start. + * Line number should be 1-based. + * + * If the source file doesn't have line information, it should return + * Integer.MIN_VALUE. The negative offsets will make it more obvious + * what happened. + * + * @param lineNumber the line of the input to get the absolute offset of. + * @return the absolute offset of the start of the provided line. + * @throws IllegalArgumentException if lineno is less than 1 or greater than + * the number of lines in the source. + */ + int getLineOffset(int lineNumber); + + /** + * Gets the 1-based line number of the given source offset. + * + * @param offset An absolute file offset. + * @return The 1-based line number of that offset. The behavior is + * undefined if this offset does not exist in the source file. + */ + int getLineOfOffset(int offset); + + /** + * Gets the 0-based column number of the given source offset. + * + * @param offset An absolute file offset. + * @return The 0-based column number of that offset. The behavior is + * undefined if this offset does not exist in the source file. + */ + int getColumnOfOffset(int offset); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StaticSymbolTable.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StaticSymbolTable.java new file mode 100644 index 0000000..ac129f6 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StaticSymbolTable.java @@ -0,0 +1,62 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +/** + * Lookup references by the symbols that they refer to. + * + * @author nicksantos@google.com (Nick Santos) + */ +public interface StaticSymbolTable + , R extends StaticReference> { + /** + * Returns the references that point to the given symbol. + */ + Iterable getReferences(S symbol); + + /** + * Returns the scope for a given symbol. + */ + StaticScope getScope(S symbol); + + /** + * Returns all variables in this symbol table. + */ + Iterable getAllSymbols(); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StringType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StringType.java new file mode 100644 index 0000000..650b6f7 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/StringType.java @@ -0,0 +1,114 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; +import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; + + +/** + * String type. + */ +public final class StringType extends ValueType { + private static final long serialVersionUID = 1L; + + StringType(JSTypeRegistry registry) { + super(registry); + } + + @Override + public TernaryValue testForEquality(JSType that) { + TernaryValue result = super.testForEquality(that); + if (result != null) { + return result; + } + if (that.isUnknownType() || that.isSubtype( + getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) { + return UNKNOWN; + } + return FALSE; + } + + @Override + public boolean isStringValueType() { + return true; + } + + @Override + public boolean matchesNumberContext() { + return true; + } + + @Override + public boolean matchesStringContext() { + return true; + } + + @Override + public boolean matchesObjectContext() { + // TODO(user): Revisit this for ES4, which is stricter. + return true; + } + + @Override + String toStringHelper(boolean forAnnotations) { + return getDisplayName(); + } + + @Override + public String getDisplayName() { + return "string"; + } + + @Override + public JSType autoboxesTo() { + return getNativeType(JSTypeNative.STRING_OBJECT_TYPE); + } + + @Override + public BooleanLiteralSet getPossibleToBooleanOutcomes() { + return BooleanLiteralSet.BOTH; + } + + @Override + public T visit(Visitor visitor) { + return visitor.caseStringType(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/TemplateType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/TemplateType.java new file mode 100644 index 0000000..9a5661d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/TemplateType.java @@ -0,0 +1,86 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +/** + * For functions with function(this: T, ...) and T as arguments, type inference + * will set the type of this on a function literal argument to the actual type + * of T. + * + */ +package com.google.javascript.rhino.jstype; + +public class TemplateType extends ProxyObjectType { + private static final long serialVersionUID = 1L; + + private final String name; + + TemplateType(JSTypeRegistry registry, String name) { + super(registry, registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE)); + this.name = name; + } + + @Override + public String getReferenceName() { + return name; + } + + @Override + String toStringHelper(boolean forAnnotations) { + return name; + } + + @Override + public TemplateType toMaybeTemplateType() { + return this; + } + + @Override + public boolean hasAnyTemplateTypesInternal() { + return true; + } + + @Override + public T visit(Visitor visitor) { + return visitor.caseTemplateType(this); + } + + @Override T visit(RelationshipVisitor visitor, JSType that) { + return visitor.caseTemplateType(this, that); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/TernaryValue.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/TernaryValue.java new file mode 100644 index 0000000..415990f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/TernaryValue.java @@ -0,0 +1,204 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +/** + *

      An enum for ternary logic. The {@link #TRUE} and {@link #FALSE} values + * are equivalent to typical booleans, and the {@link #UNKNOWN} value plays the + * role of a placeholder, which can be either {@link #TRUE} or + * {@link #FALSE}.

      + * + *

      A ternary value expression evaluates to {@link #TRUE} or + * {@link #FALSE} only if all replacements of {@link #UNKNOWN} in this + * expression yield the same result. Therefore, the ternary logic coincides + * with typical Boolean logic if the {@link #UNKNOWN} value is not + * present in an expression.

      + * + * @see Ternary Logic + */ +public enum TernaryValue { + /** + * {@code false} + */ + FALSE { + @Override + public TernaryValue and(TernaryValue that) { + return FALSE; + } + + @Override + public TernaryValue not() { + return TRUE; + } + + @Override + public TernaryValue or(TernaryValue that) { + return that; + } + + @Override + public TernaryValue xor(TernaryValue that) { + return that; + } + + @Override + public boolean toBoolean(boolean unknown) { + return false; + } + + @Override + public String toString() { + return "false"; + } + }, + + /** + * {@code true} + */ + TRUE { + @Override + public TernaryValue and(TernaryValue that) { + return that; + } + + @Override + public TernaryValue not() { + return FALSE; + } + + @Override + public TernaryValue or(TernaryValue that) { + return TRUE; + } + + @Override + public TernaryValue xor(TernaryValue that) { + return that.not(); + } + + @Override + public boolean toBoolean(boolean unknown) { + return true; + } + + @Override + public String toString() { + return "true"; + } + }, + + /** + * {@code unknown}, it represents lack of knowledge about whether this value + * is {@code true} or {@code false}. + */ + UNKNOWN { + @Override + public TernaryValue and(TernaryValue that) { + return (FALSE.equals(that)) ? FALSE : UNKNOWN; + } + + @Override + public TernaryValue not() { + return UNKNOWN; + } + + @Override + public TernaryValue or(TernaryValue that) { + return (TRUE.equals(that)) ? TRUE : UNKNOWN; + } + + @Override + public TernaryValue xor(TernaryValue that) { + return UNKNOWN; + } + + @Override + public boolean toBoolean(boolean unknown) { + return unknown; + } + + @Override + public String toString() { + return "unknown"; + } + }; + + /** + * Gets the {@code and} of {@code this} and {@code that}. + */ + public abstract TernaryValue and(TernaryValue that); + + /** + * Gets the {@code not} of {@code this}. + */ + public abstract TernaryValue not(); + + /** + * Gets the {@code or} of {@code this} and {@code that}. + */ + public abstract TernaryValue or(TernaryValue that); + + /** + * Gets the {@code xor} of {@code this} and {@code that}. + */ + public abstract TernaryValue xor(TernaryValue that); + + /** + * Converts {@code this} ternary value to boolean. The {@code #TRUE} and + * {@code #FALSE} values are simply converted to {@code true} and + * {@code false} respectively, whilst the {@link #UNKNOWN} is converted + * to the specified {@code unknown} value. + * + * @param unknown the boolean value to which the {@link #UNKNOWN} value is + * converted + * @return
      return
      +   *     this == TRUE ? true :
      +   *     this == FALSE ? false :
      +   *     unknown
      + */ + public abstract boolean toBoolean(boolean unknown); + + /** + * Gets the TernaryValue for the given boolean. + */ + public static TernaryValue forBoolean(boolean val) { + return val ? TernaryValue.TRUE : TernaryValue.FALSE; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/UnionType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/UnionType.java new file mode 100644 index 0000000..585bd8f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/UnionType.java @@ -0,0 +1,622 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; + +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.javascript.rhino.ErrorReporter; + +import java.util.Collection; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * The {@code UnionType} implements a common JavaScript idiom in which the + * code is specifically designed to work with multiple input types. Because + * JavaScript always knows the run-time type of an object value, this is safer + * than a C union.

      + * + * For instance, values of the union type {@code (String,boolean)} can be of + * type {@code String} or of type {@code boolean}. The commutativity of the + * statement is captured by making {@code (String,boolean)} and + * {@code (boolean,String)} equal.

      + * + * The implementation of this class prevents the creation of nested + * unions.

      + */ +public class UnionType extends JSType { + private static final long serialVersionUID = 1L; + + Collection alternates; + private final int hashcode; + + /** + * Creates a union type. + * + * @param alternates the alternates of the union + */ + UnionType(JSTypeRegistry registry, Collection alternates) { + super(registry); + this.alternates = alternates; + this.hashcode = this.alternates.hashCode(); + } + + /** + * Gets the alternate types of this union type. + * @return The alternate types of this union type. The returned set is + * immutable. + */ + public Iterable getAlternates() { + return alternates; + } + + /** + * This predicate is used to test whether a given type can appear in a + * numeric context, such as an operand of a multiply operator. + * + * @return true if the type can appear in a numeric context. + */ + @Override + public boolean matchesNumberContext() { + // TODO(user): Reverse this logic to make it correct instead of generous. + for (JSType t : alternates) { + if (t.matchesNumberContext()) { + return true; + } + } + return false; + } + + /** + * This predicate is used to test whether a given type can appear in a + * {@code String} context, such as an operand of a string concat ({@code +}) + * operator.

      + * + * All types have at least the potential for converting to {@code String}. + * When we add externally defined types, such as a browser OM, we may choose + * to add types that do not automatically convert to {@code String}. + * + * @return {@code true} if not {@link VoidType} + */ + @Override + public boolean matchesStringContext() { + // TODO(user): Reverse this logic to make it correct instead of generous. + for (JSType t : alternates) { + if (t.matchesStringContext()) { + return true; + } + } + return false; + } + + /** + * This predicate is used to test whether a given type can appear in an + * {@code Object} context, such as the expression in a {@code with} + * statement.

      + * + * Most types we will encounter, except notably {@code null}, have at least + * the potential for converting to {@code Object}. Host defined objects can + * get peculiar.

      + * + * VOID type is included here because while it is not part of the JavaScript + * language, functions returning 'void' type can't be used as operands of + * any operator or statement.

      + * + * @return {@code true} if the type is not {@link NullType} or + * {@link VoidType} + */ + @Override + public boolean matchesObjectContext() { + // TODO(user): Reverse this logic to make it correct instead of generous. + for (JSType t : alternates) { + if (t.matchesObjectContext()) { + return true; + } + } + return false; + } + + @Override + public JSType findPropertyType(String propertyName) { + JSType propertyType = null; + + for (JSType alternate : getAlternates()) { + // Filter out the null/undefined type. + if (alternate.isNullType() || alternate.isVoidType()) { + continue; + } + + JSType altPropertyType = alternate.findPropertyType(propertyName); + if (altPropertyType == null) { + continue; + } + + if (propertyType == null) { + propertyType = altPropertyType; + } else { + propertyType = propertyType.getLeastSupertype(altPropertyType); + } + } + + return propertyType; + } + + @Override + public boolean canBeCalled() { + for (JSType t : alternates) { + if (!t.canBeCalled()) { + return false; + } + } + return true; + } + + @Override + public JSType autobox() { + UnionTypeBuilder restricted = new UnionTypeBuilder(registry); + for (JSType t : alternates) { + restricted.addAlternate(t.autobox()); + } + return restricted.build(); + } + + @Override + public JSType restrictByNotNullOrUndefined() { + UnionTypeBuilder restricted = new UnionTypeBuilder(registry); + for (JSType t : alternates) { + restricted.addAlternate(t.restrictByNotNullOrUndefined()); + } + return restricted.build(); + } + + @Override + public TernaryValue testForEquality(JSType that) { + TernaryValue result = null; + for (JSType t : alternates) { + TernaryValue test = t.testForEquality(that); + if (result == null) { + result = test; + } else if (!result.equals(test)) { + return UNKNOWN; + } + } + return result; + } + + /** + * This predicate determines whether objects of this type can have the + * {@code null} value, and therefore can appear in contexts where + * {@code null} is expected. + * + * @return {@code true} for everything but {@code Number} and + * {@code Boolean} types. + */ + @Override + public boolean isNullable() { + for (JSType t : alternates) { + if (t.isNullable()) { + return true; + } + } + return false; + } + + @Override + public boolean isUnknownType() { + for (JSType t : alternates) { + if (t.isUnknownType()) { + return true; + } + } + return false; + } + + @Override + public boolean isStruct() { + for (JSType typ : getAlternates()) { + if (typ.isStruct()) { + return true; + } + } + return false; + } + + @Override + public boolean isDict() { + for (JSType typ : getAlternates()) { + if (typ.isDict()) { + return true; + } + } + return false; + } + + @Override + public JSType getLeastSupertype(JSType that) { + if (!that.isUnknownType() && !that.isUnionType()) { + for (JSType alternate : alternates) { + if (!alternate.isUnknownType() && that.isSubtype(alternate)) { + return this; + } + } + } + + return getLeastSupertype(this, that); + } + + JSType meet(JSType that) { + UnionTypeBuilder builder = new UnionTypeBuilder(registry); + for (JSType alternate : alternates) { + if (alternate.isSubtype(that)) { + builder.addAlternate(alternate); + } + } + + if (that.isUnionType()) { + for (JSType otherAlternate : that.toMaybeUnionType().alternates) { + if (otherAlternate.isSubtype(this)) { + builder.addAlternate(otherAlternate); + } + } + } else if (that.isSubtype(this)) { + builder.addAlternate(that); + } + JSType result = builder.build(); + if (!result.isNoType()) { + return result; + } else if (this.isObject() && (that.isObject() && !that.isNoType())) { + return getNativeType(JSTypeNative.NO_OBJECT_TYPE); + } else { + return getNativeType(JSTypeNative.NO_TYPE); + } + } + + /** + * Two union types are equal if they have the same number of alternates + * and all alternates are equal. + */ + boolean checkUnionEquivalenceHelper( + UnionType that, EquivalenceMethod eqMethod) { + if (eqMethod == EquivalenceMethod.IDENTITY + && alternates.size() != that.alternates.size()) { + return false; + } + for (JSType alternate : that.alternates) { + if (!hasAlternate(alternate, eqMethod)) { + return false; + } + } + return true; + } + + private boolean hasAlternate(JSType type, EquivalenceMethod eqMethod) { + for (JSType alternate : alternates) { + if (alternate.checkEquivalenceHelper(type, eqMethod)) { + return true; + } + } + return false; + } + + @Override + public boolean hasProperty(String pname) { + for (JSType alternate : alternates) { + if (alternate.hasProperty(pname)) { + return true; + } + } + return false; + } + + @Override + public int hashCode() { + return this.hashcode; + } + + @Override + public UnionType toMaybeUnionType() { + return this; + } + + @Override + public boolean isObject() { + for (JSType alternate : alternates) { + if (!alternate.isObject()) { + return false; + } + } + return true; + } + + /** + * A {@link UnionType} contains a given type (alternate) iff the member + * vector contains it. + * + * @param type The alternate which might be in this union. + * + * @return {@code true} if the alternate is in the union + */ + public boolean contains(JSType type) { + for (JSType alt : alternates) { + if (alt.isEquivalentTo(type)) { + return true; + } + } + return false; + } + + /** + * Returns a more restricted union type than {@code this} one, in which all + * subtypes of {@code type} have been removed.

      + * + * Examples: + *

        + *
      • {@code (number,string)} restricted by {@code number} is + * {@code string}
      • + *
      • {@code (null, EvalError, URIError)} restricted by + * {@code Error} is {@code null}
      • + *
      + * + * @param type the supertype of the types to remove from this union type + */ + public JSType getRestrictedUnion(JSType type) { + UnionTypeBuilder restricted = new UnionTypeBuilder(registry); + for (JSType t : alternates) { + // Keep all unknown/unresolved types. + if (t.isUnknownType() || t.isNoResolvedType() || !t.isSubtype(type)) { + restricted.addAlternate(t); + } + } + return restricted.build(); + } + + @Override String toStringHelper(boolean forAnnotations) { + StringBuilder result = new StringBuilder(); + boolean firstAlternate = true; + + result.append("("); + SortedSet sorted = new TreeSet(ALPHA); + sorted.addAll(alternates); + for (JSType t : sorted) { + if (!firstAlternate) { + result.append("|"); + } + result.append(t.toStringHelper(forAnnotations)); + firstAlternate = false; + } + result.append(")"); + return result.toString(); + } + + @Override + public boolean isSubtype(JSType that) { + // unknown + if (that.isUnknownType()) { + return true; + } + // all type + if (that.isAllType()) { + return true; + } + for (JSType element : alternates) { + if (!element.isSubtype(that)) { + return false; + } + } + return true; + } + + @Override + public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) { + // gather elements after restriction + UnionTypeBuilder restricted = new UnionTypeBuilder(registry); + for (JSType element : alternates) { + restricted.addAlternate( + element.getRestrictedTypeGivenToBooleanOutcome(outcome)); + } + return restricted.build(); + } + + @Override + public BooleanLiteralSet getPossibleToBooleanOutcomes() { + BooleanLiteralSet literals = BooleanLiteralSet.EMPTY; + for (JSType element : alternates) { + literals = literals.union(element.getPossibleToBooleanOutcomes()); + if (literals == BooleanLiteralSet.BOTH) { + break; + } + } + return literals; + } + + @Override + public TypePair getTypesUnderEquality(JSType that) { + UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry); + UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry); + for (JSType element : alternates) { + TypePair p = element.getTypesUnderEquality(that); + if (p.typeA != null) { + thisRestricted.addAlternate(p.typeA); + } + if (p.typeB != null) { + thatRestricted.addAlternate(p.typeB); + } + } + return new TypePair( + thisRestricted.build(), + thatRestricted.build()); + } + + @Override + public TypePair getTypesUnderInequality(JSType that) { + UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry); + UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry); + for (JSType element : alternates) { + TypePair p = element.getTypesUnderInequality(that); + if (p.typeA != null) { + thisRestricted.addAlternate(p.typeA); + } + if (p.typeB != null) { + thatRestricted.addAlternate(p.typeB); + } + } + return new TypePair( + thisRestricted.build(), + thatRestricted.build()); + } + + @Override + public TypePair getTypesUnderShallowInequality(JSType that) { + UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry); + UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry); + for (JSType element : alternates) { + TypePair p = element.getTypesUnderShallowInequality(that); + if (p.typeA != null) { + thisRestricted.addAlternate(p.typeA); + } + if (p.typeB != null) { + thatRestricted.addAlternate(p.typeB); + } + } + return new TypePair( + thisRestricted.build(), + thatRestricted.build()); + } + + @Override + public T visit(Visitor visitor) { + return visitor.caseUnionType(this); + } + + @Override T visit(RelationshipVisitor visitor, JSType that) { + return visitor.caseUnionType(this, that); + } + + @Override + JSType resolveInternal(ErrorReporter t, StaticScope scope) { + setResolvedTypeInternal(this); // for circularly defined types. + + boolean changed = false; + ImmutableList.Builder resolvedTypes = ImmutableList.builder(); + for (JSType alternate : alternates) { + JSType newAlternate = alternate.resolve(t, scope); + changed |= (alternate != newAlternate); + resolvedTypes.add(alternate); + } + if (changed) { + Collection newAlternates = resolvedTypes.build(); + Preconditions.checkState( + newAlternates.hashCode() == this.hashcode); + alternates = newAlternates; + } + return this; + } + + @Override + public String toDebugHashCodeString() { + List hashCodes = Lists.newArrayList(); + for (JSType a : alternates) { + hashCodes.add(a.toDebugHashCodeString()); + } + return "{(" + Joiner.on(",").join(hashCodes) + ")}"; + } + + @Override + public boolean setValidator(Predicate validator) { + for (JSType a : alternates) { + a.setValidator(validator); + } + return true; + } + + @Override + public JSType collapseUnion() { + JSType currentValue = null; + ObjectType currentCommonSuper = null; + for (JSType a : alternates) { + if (a.isUnknownType()) { + return getNativeType(JSTypeNative.UNKNOWN_TYPE); + } + + ObjectType obj = a.toObjectType(); + if (obj == null) { + if (currentValue == null && currentCommonSuper == null) { + // If obj is not an object, then it must be a value. + currentValue = a; + } else { + // Multiple values and objects will always collapse to the ALL_TYPE. + return getNativeType(JSTypeNative.ALL_TYPE); + } + } else if (currentValue != null) { + // Values and objects will always collapse to the ALL_TYPE. + return getNativeType(JSTypeNative.ALL_TYPE); + } else if (currentCommonSuper == null) { + currentCommonSuper = obj; + } else { + currentCommonSuper = + registry.findCommonSuperObject(currentCommonSuper, obj); + } + } + return currentCommonSuper; + } + + @Override + public void matchConstraint(JSType constraint) { + for (JSType alternate : alternates) { + alternate.matchConstraint(constraint); + } + } + + @Override + public boolean hasAnyTemplateTypesInternal() { + for (JSType alternate : alternates) { + if (alternate.hasAnyTemplateTypes()) { + return true; + } + } + return false; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/UnionTypeBuilder.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/UnionTypeBuilder.java new file mode 100644 index 0000000..69b6567 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/UnionTypeBuilder.java @@ -0,0 +1,329 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.JSTypeNative.ALL_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.CHECKED_UNKNOWN_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NO_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * A builder for union types. + * + * @author nicksantos@google.com (Nick Santos) + */ +class UnionTypeBuilder implements Serializable { + private static final long serialVersionUID = 1L; + + // If the best we can do is say "this object is one of twenty things", + // then we should just give up and admit that we have no clue. + private static final int DEFAULT_MAX_UNION_SIZE = 20; + + private final JSTypeRegistry registry; + private final List alternates = Lists.newArrayList(); + private boolean isAllType = false; + private boolean isNativeUnknownType = false; + private boolean areAllUnknownsChecked = true; + private final int maxUnionSize; + + // Every UnionType may have at most one structural function in it. + // + // NOTE(nicksantos): I've read some literature that says that type-inferenced + // languages are fundamentally incompatible with union types. I refuse + // to believe this. But they do make the type lattice much more complicated. + // + // For this reason, when we deal with function types, we actually merge some + // nodes on the lattice, and treat them as fundamentally equivalent. + // For example, we treat + // function(): string | function(): number + // as equivalent to + // function(): (string|number) + // and normalize the first type into the second type. + // + // To perform this normalization, we've modified UnionTypeBuilder to disallow + // multiple structural functions in a union. We always delegate to + // FunctionType::getLeastSupertype, which either merges the functions into + // one structural function, or just bails out and uses the top function type. + private int functionTypePosition = -1; + + // Memoize the result, in case build() is called multiple times. + private JSType result = null; + + UnionTypeBuilder(JSTypeRegistry registry) { + this(registry, DEFAULT_MAX_UNION_SIZE); + } + + UnionTypeBuilder(JSTypeRegistry registry, int maxUnionSize) { + this.registry = registry; + this.maxUnionSize = maxUnionSize; + } + + Iterable getAlternates() { + JSType specialCaseType = reduceAlternatesWithoutUnion(); + if (specialCaseType != null) { + return ImmutableList.of(specialCaseType); + } + return Collections.unmodifiableList(alternates); + } + + /** + * Adds an alternate to the union type under construction. Returns this + * for easy chaining. + */ + UnionTypeBuilder addAlternate(JSType alternate) { + // build() returns the bottom type by default, so we can + // just bail out early here. + if (alternate.isNoType()) { + return this; + } + + isAllType = isAllType || alternate.isAllType(); + + boolean isAlternateUnknown = alternate instanceof UnknownType; + isNativeUnknownType = isNativeUnknownType || isAlternateUnknown; + if (isAlternateUnknown) { + areAllUnknownsChecked = areAllUnknownsChecked && + alternate.isCheckedUnknownType(); + } + if (!isAllType && !isNativeUnknownType) { + if (alternate.isUnionType()) { + UnionType union = alternate.toMaybeUnionType(); + for (JSType unionAlt : union.getAlternates()) { + addAlternate(unionAlt); + } + } else { + if (alternates.size() > maxUnionSize) { + return this; + } + + // Function types are special, because they have their + // own bizarre sub-lattice. See the comments on + // FunctionType#supAndInf helper and above at functionTypePosition. + if (alternate.isFunctionType() && functionTypePosition != -1) { + // See the comments on functionTypePosition above. + FunctionType other = + alternates.get(functionTypePosition).toMaybeFunctionType(); + FunctionType supremum = + alternate.toMaybeFunctionType().supAndInfHelper(other, true); + alternates.set(functionTypePosition, supremum); + result = null; + return this; + } + + // Look through the alternates we've got so far, + // and check if any of them are duplicates of + // one another. + int currentIndex = 0; + Iterator it = alternates.iterator(); + while (it.hasNext()) { + boolean removeCurrent = false; + JSType current = it.next(); + + // Unknown and NoResolved types may just be names that haven't + // been resolved yet. So keep these in the union, and just use + // equality checking for simple de-duping. + if (alternate.isUnknownType() || + current.isUnknownType() || + alternate.isNoResolvedType() || + current.isNoResolvedType() || + alternate.hasAnyTemplateTypes() || + current.hasAnyTemplateTypes()) { + if (alternate.isEquivalentTo(current)) { + // Alternate is unnecessary. + return this; + } + } else { + + // Because "Foo" and "Foo." are roughly equivalent + // parameterized types, special care is needed when building the + // union. For example: + // Object is consider a subtype of Object. + // but we want to leave "Object" not "Object." when + // building the subtype. + // + + if (alternate.isParameterizedType() || current.isParameterizedType()) { + // Cases: + // 1) alternate:Array. and current:Object ==> Object + // 2) alternate:Array. and current:Array ==> Array + // 3) alternate:Object. and + // current:Array ==> Array|Object. + // 4) alternate:Object and current:Array. ==> Object + // 5) alternate:Array and current:Array. ==> Array + // 6) alternate:Array and + // current:Object. ==> Array|Object. + // 7) alternate:Array. and + // current:Array. ==> Array. + // 8) alternate:Array. and + // current:Array. ==> Array. + // 9) alternate:Array. and + // current:Object. ==> Object.|Array. + + if (!current.isParameterizedType()) { + if (alternate.isSubtype(current)) { + // case 1, 2 + return this; + } + // case 3: leave current, add alternate + } else if (!alternate.isParameterizedType()) { + if (current.isSubtype(alternate)) { + // case 4, 5 + removeCurrent = true; + } + // case 6: leave current, add alternate + } else { + Preconditions.checkState(current.isParameterizedType() + && alternate.isParameterizedType()); + ParameterizedType parameterizedAlternate = alternate.toMaybeParameterizedType(); + ParameterizedType parameterizedCurrent = current.toMaybeParameterizedType(); + + if (parameterizedCurrent.wrapsSameRawType(parameterizedAlternate)) { + JSType alternateTypeParameter = parameterizedAlternate.getParameterType(); + JSType currentTypeParameter = parameterizedCurrent.getParameterType(); + if (currentTypeParameter.isEquivalentTo(parameterizedCurrent)) { + // case 8 + return this; + } else { + // TODO(johnlenz): should we leave both types? + // case 7: add a merged alternate + // We currently merge to the parameterized types to "unknown" + // which is equivalent to the raw type. + JSType merged = parameterizedCurrent + .getReferencedObjTypeInternal(); + return addAlternate(merged); + } + } + // case 9: leave current, add alternate + } + // Otherwise leave both parameterized types. + } else if (alternate.isSubtype(current)) { + // Alternate is unnecessary. + return this; + } else if (current.isSubtype(alternate)) { + // Alternate makes current obsolete + removeCurrent = true; + } + } + + if (removeCurrent) { + it.remove(); + + if (currentIndex == functionTypePosition) { + functionTypePosition = -1; + } else if (currentIndex < functionTypePosition) { + functionTypePosition--; + currentIndex--; + } + } + currentIndex++; + } + + if (alternate.isFunctionType()) { + // See the comments on functionTypePosition above. + Preconditions.checkState(functionTypePosition == -1); + functionTypePosition = alternates.size(); + } + + alternates.add(alternate); + result = null; // invalidate the memoized result + } + } else { + result = null; + } + return this; + } + + /** + * Reduce the alternates into a non-union type. + * If the alternates can't be accurately represented with a non-union + * type, return null. + */ + private JSType reduceAlternatesWithoutUnion() { + if (isAllType) { + return registry.getNativeType(ALL_TYPE); + } else if (isNativeUnknownType) { + if (areAllUnknownsChecked) { + return registry.getNativeType(CHECKED_UNKNOWN_TYPE); + } else { + return registry.getNativeType(UNKNOWN_TYPE); + } + } else { + int size = alternates.size(); + if (size > maxUnionSize) { + return registry.getNativeType(UNKNOWN_TYPE); + } else if (size > 1) { + return null; + } else if (size == 1) { + return alternates.iterator().next(); + } else { + return registry.getNativeType(NO_TYPE); + } + } + } + + /** + * Creates a union. + * @return A UnionType if it has two or more alternates, the + * only alternate if it has one and otherwise {@code NO_TYPE}. + */ + JSType build() { + if (result == null) { + result = reduceAlternatesWithoutUnion(); + if (result == null) { + result = new UnionType(registry, getAlternateListCopy()); + } + } + return result; + } + + private Collection getAlternateListCopy() { + return ImmutableList.copyOf(alternates); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/UnknownType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/UnknownType.java new file mode 100644 index 0000000..4889cb1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/UnknownType.java @@ -0,0 +1,161 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; + +import com.google.javascript.rhino.ErrorReporter; +import com.google.javascript.rhino.Node; + +/** + * The {@code Unknown} type. + */ +public class UnknownType extends ObjectType { + private static final long serialVersionUID = 1L; + + // See the explanation of checked unknown types in JSTypeNative. + private final boolean isChecked; + + UnknownType(JSTypeRegistry registry, boolean isChecked) { + super(registry); + this.isChecked = isChecked; + } + + @Override + public boolean isUnknownType() { + return true; + } + + @Override + public boolean isCheckedUnknownType() { + return isChecked; + } + + @Override + public boolean canBeCalled() { + return true; + } + + @Override + public boolean matchesNumberContext() { + return true; + } + + @Override + public boolean matchesObjectContext() { + return true; + } + + @Override + public boolean matchesStringContext() { + return true; + } + + @Override + public TernaryValue testForEquality(JSType that) { + return UNKNOWN; + } + + @Override + public boolean isNullable() { + return true; + } + + @Override + public boolean isSubtype(JSType that) { + return true; + } + + @Override + public T visit(Visitor visitor) { + return visitor.caseUnknownType(); + } + + @Override T visit(RelationshipVisitor visitor, JSType that) { + return visitor.caseUnknownType(this, that); + } + + @Override + String toStringHelper(boolean forAnnotations) { + return getReferenceName(); + } + + @Override + boolean defineProperty(String propertyName, JSType type, + boolean inferred, Node propertyNode) { + // nothing to define + return true; + } + + @Override + public ObjectType getImplicitPrototype() { + return null; + } + + @Override + public FunctionType getConstructor() { + return null; + } + + @Override + public String getReferenceName() { + return isChecked ? "??" : "?"; + } + + @Override + public String getDisplayName() { + return "Unknown"; + } + + @Override + public boolean hasDisplayName() { + return true; + } + + @Override + public BooleanLiteralSet getPossibleToBooleanOutcomes() { + return BooleanLiteralSet.BOTH; + } + + @Override + JSType resolveInternal(ErrorReporter t, StaticScope scope) { + return this; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/UnresolvedTypeExpression.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/UnresolvedTypeExpression.java new file mode 100644 index 0000000..540b0f7 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/UnresolvedTypeExpression.java @@ -0,0 +1,83 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.common.base.Preconditions; +import com.google.javascript.rhino.ErrorReporter; +import com.google.javascript.rhino.Node; + +/** + * An {@code UnresolvedType} is a reference to some type expression. + * This provides a convenient mechanism for implementing forward + * references to types; a {@code UnresolvedType} can be used as a + * placeholder until its reference is resolved. + * + * The {@code UnresolvedType} will behave like an opaque unknown type. + * When its {@code #resolve} method is called, it will return the underlying + * type. The underlying type can resolve to any JS type.

      + * + * @author nicksantos@google.com (Nick Santos) + */ +class UnresolvedTypeExpression extends UnknownType { + private static final long serialVersionUID = 1L; + + private final Node typeExpr; + private final String sourceName; + + /** + * Create a named type based on the reference. + */ + UnresolvedTypeExpression(JSTypeRegistry registry, Node typeExpr, + String sourceName) { + super(registry, false); + + Preconditions.checkNotNull(typeExpr); + this.typeExpr = typeExpr; + this.sourceName = sourceName; + } + + /** + * Resolve the referenced type within the enclosing scope. + */ + @Override + JSType resolveInternal(ErrorReporter t, StaticScope enclosing) { + return registry.createFromTypeNodes(typeExpr, sourceName, enclosing); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ValueType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ValueType.java new file mode 100644 index 0000000..fdf7802 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/ValueType.java @@ -0,0 +1,65 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.ErrorReporter; + +/** + * Value types (null, void, number, boolean, string). + */ +abstract class ValueType extends JSType { + ValueType(JSTypeRegistry registry) { + super(registry); + } + + @Override + final JSType resolveInternal(ErrorReporter t, StaticScope scope) { + return this; + } + + @Override + public boolean hasDisplayName() { + return true; + } + + @Override T visit(RelationshipVisitor visitor, JSType that) { + return visitor.caseValueType(this, that); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/Visitor.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/Visitor.java new file mode 100644 index 0000000..bc077ee --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/Visitor.java @@ -0,0 +1,133 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + + + + + +/** + * A type visitor.

      + * + * This code will calculate a specific value of type {@code T} from a type + * based on its structure: + * + *

      JSType type = …;
      + * T value = type.visit(new Visitor<T>() {
      + *   …
      + * });
      + * + */ +public interface Visitor { + /** + * Bottom type's case. + */ + T caseNoType(); + + /** + * Enum element type's case. + */ + T caseEnumElementType(EnumElementType type); + + /** + * All type's case. + */ + T caseAllType(); + + /** + * Boolean value type's case. + */ + T caseBooleanType(); + + /** + * Bottom Object type's case. + */ + T caseNoObjectType(); + + /** + * Function type's case. + */ + T caseFunctionType(FunctionType type); + + /** + * Object type's case. + */ + T caseObjectType(ObjectType type); + + /** + * Unknown type's case. + */ + T caseUnknownType(); + + /** + * Null type's case. + */ + T caseNullType(); + + /** + * Number value type's case. + */ + T caseNumberType(); + + /** + * String value type's case. + */ + T caseStringType(); + + /** + * Void type's case. + */ + T caseVoidType(); + + /** + * Union type's case. + */ + T caseUnionType(UnionType type); + + /** + * Parameterized type's case. + */ + T caseParameterizedType(ParameterizedType type); + + /** + * Template type's case. + */ + T caseTemplateType(TemplateType templateType); +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/VoidType.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/VoidType.java new file mode 100644 index 0000000..e5956de --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/VoidType.java @@ -0,0 +1,113 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; +import static com.google.javascript.rhino.jstype.TernaryValue.TRUE; +import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; + + +/** + * Void type whose only element is the {@code undefined} value. + */ +public class VoidType extends ValueType { + private static final long serialVersionUID = 1L; + + VoidType(JSTypeRegistry registry) { + super(registry); + } + + @Override + public JSType restrictByNotNullOrUndefined() { + return registry.getNativeType(JSTypeNative.NO_TYPE); + } + + @Override + public TernaryValue testForEquality(JSType that) { + if (UNKNOWN.equals(super.testForEquality(that))) { + return UNKNOWN; + } + if (that.isSubtype(this) || + that.isSubtype(getNativeType(JSTypeNative.NULL_TYPE))) { + return TRUE; + } + return FALSE; + } + + @Override + public boolean matchesNumberContext() { + return false; + } + + @Override + public boolean matchesObjectContext() { + return false; + } + + @Override + public boolean matchesStringContext() { + return true; + } + + @Override + public boolean isVoidType() { + return true; + } + + @Override + String toStringHelper(boolean forAnnotations) { + return getDisplayName(); + } + + @Override + public String getDisplayName() { + return "undefined"; + } + + @Override + public BooleanLiteralSet getPossibleToBooleanOutcomes() { + return BooleanLiteralSet.FALSE; + } + + @Override + public T visit(Visitor visitor) { + return visitor.caseVoidType(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/package.html b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/package.html new file mode 100644 index 0000000..11da27e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/jstype/package.html @@ -0,0 +1,15 @@ + + + + + + + +Provides abstractions to represent types in JavaScript. + +Rhino is an open-source implementation of JavaScript written entirely in Java. +It is typically embedded into Java applications to provide scripting to end +users. + + + diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/package.html b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/package.html new file mode 100644 index 0000000..ad546df --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/package.html @@ -0,0 +1,15 @@ + + + + + + + +The core AST from Rhino. + +Rhino is an open-source implementation of JavaScript written entirely in Java. +It is typically embedded into Java applications to provide scripting to end +users. + + + diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/testing/AbstractStaticScope.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/testing/AbstractStaticScope.java new file mode 100644 index 0000000..0e7300a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/testing/AbstractStaticScope.java @@ -0,0 +1,73 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.testing; + +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.StaticScope; +import com.google.javascript.rhino.jstype.StaticSlot; + +/** + * A scope that just returns null for everything. + * @author nicksantos@google.com (Nick Santos) + */ +public abstract class AbstractStaticScope implements StaticScope { + + @Override + public Node getRootNode() { + return null; + } + + @Override + public StaticScope getParentScope() { + return null; + } + + @Override + public abstract StaticSlot getSlot(String name); + + @Override + public StaticSlot getOwnSlot(String name) { + return getSlot(name); + } + + @Override + public T getTypeOfThis() { + return null; + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/testing/Asserts.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/testing/Asserts.java new file mode 100644 index 0000000..6b29896 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/testing/Asserts.java @@ -0,0 +1,160 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.testing; + +import com.google.common.collect.Iterables; +import com.google.javascript.rhino.ErrorReporter; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.StaticScope; + +import junit.framework.Assert; + +import java.util.Iterator; + +/** + * Helper methods for making assertions about the validity of types. + * @author nicksantos@google.com (Nick Santos) + */ +public class Asserts { + private Asserts() {} // all static + + public static JSType assertResolvesToSame(JSType type) { + Assert.assertSame(type, assertValidResolve(type)); + return type; + } + + /** @return The resolved type */ + public static JSType assertValidResolve(JSType type) { + return assertValidResolve(type, MapBasedScope.emptyScope()); + } + + /** @return The resolved type */ + public static JSType assertValidResolve( + JSType type, StaticScope scope) { + ErrorReporter t = TestErrorReporter.forNoExpectedReports(); + JSType resolvedType = type.resolve(t, scope); + assertTypeEquals("JSType#resolve should not affect object equality", + type, resolvedType); + return resolvedType; + } + + public static void assertTypeNotEquals(JSType a, JSType b) { + assertTypeNotEquals("", a, b); + } + + public static void assertTypeNotEquals(String message, JSType a, JSType b) { + Assert.assertFalse( + message + + (message.isEmpty() ? "" : "\n") + + "Type: " + b + "\n", + a.isEquivalentTo(b)); + Assert.assertFalse( + message + + " Equals is not symmetric.\n" + + "Type: " + b + "\n", + b.isEquivalentTo(a)); + } + + public static void assertTypeEquals(JSType a, JSType b) { + assertTypeEquals("", a, b); + } + + public static void assertTypeEquals(String message, JSType a, JSType b) { + Assert.assertTrue( + "Both types must be null, or both must be non-null " + a + "," + b, + (a == null) == (b == null)); + if (a == null) { + return; + } + Assert.assertTrue( + message + + (message.isEmpty() ? "" : "\n") + + "Expected: " + a + "\n" + + "Actual : " + b, + a.isEquivalentTo(b)); + Assert.assertTrue( + message + + " Equals is not symmetric.\n" + + "Expected: " + b + "\n" + + "Actual : " + a, + b.isEquivalentTo(a)); + } + + public static void + assertTypeCollectionEquals(Iterable a, Iterable b) { + Assert.assertEquals(Iterables.size(a), Iterables.size(b)); + Iterator aIterator = a.iterator(); + Iterator bIterator = b.iterator(); + while (aIterator.hasNext()) { + assertTypeEquals(aIterator.next(), bIterator.next()); + } + } + + /** + * For the given equivalent types, run all type operations that + * should have trivial solutions (getGreatestSubtype, isEquivalentTo, etc) + */ + public static void assertEquivalenceOperations(JSType a, JSType b) { + Assert.assertTrue(a.isEquivalentTo(b)); + Assert.assertTrue(a.isEquivalentTo(a)); + Assert.assertTrue(b.isEquivalentTo(b)); + Assert.assertTrue(b.isEquivalentTo(a)); + + Assert.assertTrue(a.isSubtype(b)); + Assert.assertTrue(a.isSubtype(a)); + Assert.assertTrue(b.isSubtype(b)); + Assert.assertTrue(b.isSubtype(a)); + + assertTypeEquals(a, a.getGreatestSubtype(b)); + assertTypeEquals(a, a.getGreatestSubtype(a)); + assertTypeEquals(a, b.getGreatestSubtype(b)); + assertTypeEquals(a, b.getGreatestSubtype(a)); + + assertTypeEquals(a, a.getLeastSupertype(b)); + assertTypeEquals(a, a.getLeastSupertype(a)); + assertTypeEquals(a, b.getLeastSupertype(b)); + assertTypeEquals(a, b.getLeastSupertype(a)); + + Assert.assertTrue(a.canCastTo(b)); + Assert.assertTrue(a.canCastTo(a)); + Assert.assertTrue(b.canCastTo(b)); + Assert.assertTrue(b.canCastTo(a)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/testing/BaseJSTypeTestCase.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/testing/BaseJSTypeTestCase.java new file mode 100644 index 0000000..9e5ed6e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/testing/BaseJSTypeTestCase.java @@ -0,0 +1,613 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.testing; + +import com.google.common.collect.ImmutableList; +import com.google.javascript.rhino.JSTypeExpression; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionBuilder; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.ParameterizedType; +import com.google.javascript.rhino.jstype.RecordTypeBuilder; + +import junit.framework.TestCase; + +public abstract class BaseJSTypeTestCase extends TestCase { + protected JSTypeRegistry registry; + protected TestErrorReporter errorReporter; + + protected JSType ALL_TYPE; + protected ObjectType NO_OBJECT_TYPE; + protected ObjectType NO_TYPE; + protected ObjectType NO_RESOLVED_TYPE; + protected FunctionType ARRAY_FUNCTION_TYPE; + protected ObjectType ARRAY_TYPE; + protected JSType BOOLEAN_OBJECT_FUNCTION_TYPE; + protected ObjectType BOOLEAN_OBJECT_TYPE; + protected JSType BOOLEAN_TYPE; + protected ObjectType CHECKED_UNKNOWN_TYPE; + protected JSType DATE_FUNCTION_TYPE; + protected ObjectType DATE_TYPE; + protected JSType ERROR_FUNCTION_TYPE; + protected ObjectType ERROR_TYPE; + protected JSType EVAL_ERROR_FUNCTION_TYPE; + protected ObjectType EVAL_ERROR_TYPE; + protected FunctionType FUNCTION_FUNCTION_TYPE; + protected FunctionType FUNCTION_INSTANCE_TYPE; + protected ObjectType FUNCTION_PROTOTYPE; + protected JSType GREATEST_FUNCTION_TYPE; + protected JSType LEAST_FUNCTION_TYPE; + protected JSType MATH_TYPE; + protected JSType NULL_TYPE; + protected JSType NUMBER_OBJECT_FUNCTION_TYPE; + protected ObjectType NUMBER_OBJECT_TYPE; + protected JSType NUMBER_STRING_BOOLEAN; + protected JSType NUMBER_TYPE; + protected FunctionType OBJECT_FUNCTION_TYPE; + protected JSType NULL_VOID; + protected JSType OBJECT_NUMBER_STRING; + protected JSType OBJECT_NUMBER_STRING_BOOLEAN; + protected JSType OBJECT_PROTOTYPE; + protected ObjectType OBJECT_TYPE; + protected JSType RANGE_ERROR_FUNCTION_TYPE; + protected ObjectType RANGE_ERROR_TYPE; + protected JSType REFERENCE_ERROR_FUNCTION_TYPE; + protected ObjectType REFERENCE_ERROR_TYPE; + protected JSType REGEXP_FUNCTION_TYPE; + protected ObjectType REGEXP_TYPE; + protected JSType STRING_OBJECT_FUNCTION_TYPE; + protected ObjectType STRING_OBJECT_TYPE; + protected JSType STRING_TYPE; + protected JSType SYNTAX_ERROR_FUNCTION_TYPE; + protected ObjectType SYNTAX_ERROR_TYPE; + protected JSType TYPE_ERROR_FUNCTION_TYPE; + protected ObjectType TYPE_ERROR_TYPE; + protected FunctionType U2U_CONSTRUCTOR_TYPE; + protected FunctionType U2U_FUNCTION_TYPE; + protected ObjectType UNKNOWN_TYPE; + protected JSType URI_ERROR_FUNCTION_TYPE; + protected ObjectType URI_ERROR_TYPE; + protected JSType VOID_TYPE; + + protected int NATIVE_PROPERTIES_COUNT; + + @Override + protected void setUp() throws Exception { + super.setUp(); + errorReporter = new TestErrorReporter(null, null); + registry = new JSTypeRegistry(errorReporter); + initTypes(); + } + + protected void initTypes() { + ALL_TYPE = + registry.getNativeType(JSTypeNative.ALL_TYPE); + NO_OBJECT_TYPE = + registry.getNativeObjectType(JSTypeNative.NO_OBJECT_TYPE); + NO_TYPE = + registry.getNativeObjectType(JSTypeNative.NO_TYPE); + NO_RESOLVED_TYPE = + registry.getNativeObjectType(JSTypeNative.NO_RESOLVED_TYPE); + ARRAY_FUNCTION_TYPE = + registry.getNativeFunctionType(JSTypeNative.ARRAY_FUNCTION_TYPE); + ARRAY_TYPE = + registry.getNativeObjectType(JSTypeNative.ARRAY_TYPE); + BOOLEAN_OBJECT_FUNCTION_TYPE = + registry.getNativeType(JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE); + BOOLEAN_OBJECT_TYPE = + registry.getNativeObjectType(JSTypeNative.BOOLEAN_OBJECT_TYPE); + BOOLEAN_TYPE = + registry.getNativeType(JSTypeNative.BOOLEAN_TYPE); + CHECKED_UNKNOWN_TYPE = + registry.getNativeObjectType(JSTypeNative.CHECKED_UNKNOWN_TYPE); + DATE_FUNCTION_TYPE = + registry.getNativeType(JSTypeNative.DATE_FUNCTION_TYPE); + DATE_TYPE = + registry.getNativeObjectType(JSTypeNative.DATE_TYPE); + ERROR_FUNCTION_TYPE = + registry.getNativeType(JSTypeNative.ERROR_FUNCTION_TYPE); + ERROR_TYPE = + registry.getNativeObjectType(JSTypeNative.ERROR_TYPE); + EVAL_ERROR_FUNCTION_TYPE = + registry.getNativeType(JSTypeNative.EVAL_ERROR_FUNCTION_TYPE); + EVAL_ERROR_TYPE = + registry.getNativeObjectType(JSTypeNative.EVAL_ERROR_TYPE); + FUNCTION_FUNCTION_TYPE = + registry.getNativeFunctionType(JSTypeNative.FUNCTION_FUNCTION_TYPE); + FUNCTION_INSTANCE_TYPE = + registry.getNativeFunctionType(JSTypeNative.FUNCTION_INSTANCE_TYPE); + FUNCTION_PROTOTYPE = + registry.getNativeObjectType(JSTypeNative.FUNCTION_PROTOTYPE); + GREATEST_FUNCTION_TYPE = + registry.getNativeType(JSTypeNative.GREATEST_FUNCTION_TYPE); + LEAST_FUNCTION_TYPE = + registry.getNativeType(JSTypeNative.LEAST_FUNCTION_TYPE); + NULL_TYPE = + registry.getNativeType(JSTypeNative.NULL_TYPE); + NUMBER_OBJECT_FUNCTION_TYPE = + registry.getNativeType(JSTypeNative.NUMBER_OBJECT_FUNCTION_TYPE); + NUMBER_OBJECT_TYPE = + registry.getNativeObjectType(JSTypeNative.NUMBER_OBJECT_TYPE); + NUMBER_STRING_BOOLEAN = + registry.getNativeType(JSTypeNative.NUMBER_STRING_BOOLEAN); + NUMBER_TYPE = + registry.getNativeType(JSTypeNative.NUMBER_TYPE); + OBJECT_FUNCTION_TYPE = + registry.getNativeFunctionType(JSTypeNative.OBJECT_FUNCTION_TYPE); + NULL_VOID = + registry.getNativeType(JSTypeNative.NULL_VOID); + OBJECT_NUMBER_STRING = + registry.getNativeType(JSTypeNative.OBJECT_NUMBER_STRING); + OBJECT_NUMBER_STRING_BOOLEAN = + registry.getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN); + OBJECT_PROTOTYPE = + registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE); + OBJECT_TYPE = + registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE); + RANGE_ERROR_FUNCTION_TYPE = + registry.getNativeType(JSTypeNative.RANGE_ERROR_FUNCTION_TYPE); + RANGE_ERROR_TYPE = + registry.getNativeObjectType(JSTypeNative.RANGE_ERROR_TYPE); + REFERENCE_ERROR_FUNCTION_TYPE = + registry.getNativeType(JSTypeNative.REFERENCE_ERROR_FUNCTION_TYPE); + REFERENCE_ERROR_TYPE = + registry.getNativeObjectType(JSTypeNative.REFERENCE_ERROR_TYPE); + REGEXP_FUNCTION_TYPE = + registry.getNativeType(JSTypeNative.REGEXP_FUNCTION_TYPE); + REGEXP_TYPE = + registry.getNativeObjectType(JSTypeNative.REGEXP_TYPE); + STRING_OBJECT_FUNCTION_TYPE = + registry.getNativeType(JSTypeNative.STRING_OBJECT_FUNCTION_TYPE); + STRING_OBJECT_TYPE = + registry.getNativeObjectType(JSTypeNative.STRING_OBJECT_TYPE); + STRING_TYPE = + registry.getNativeType(JSTypeNative.STRING_TYPE); + SYNTAX_ERROR_FUNCTION_TYPE = + registry.getNativeType(JSTypeNative.SYNTAX_ERROR_FUNCTION_TYPE); + SYNTAX_ERROR_TYPE = + registry.getNativeObjectType(JSTypeNative.SYNTAX_ERROR_TYPE); + TYPE_ERROR_FUNCTION_TYPE = + registry.getNativeType(JSTypeNative.TYPE_ERROR_FUNCTION_TYPE); + TYPE_ERROR_TYPE = + registry.getNativeObjectType(JSTypeNative.TYPE_ERROR_TYPE); + U2U_CONSTRUCTOR_TYPE = + registry.getNativeFunctionType(JSTypeNative.U2U_CONSTRUCTOR_TYPE); + U2U_FUNCTION_TYPE = + registry.getNativeFunctionType(JSTypeNative.U2U_FUNCTION_TYPE); + UNKNOWN_TYPE = + registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); + URI_ERROR_FUNCTION_TYPE = + registry.getNativeType(JSTypeNative.URI_ERROR_FUNCTION_TYPE); + URI_ERROR_TYPE = + registry.getNativeObjectType(JSTypeNative.URI_ERROR_TYPE); + VOID_TYPE = + registry.getNativeType(JSTypeNative.VOID_TYPE); + + addNativeProperties(registry); + + NATIVE_PROPERTIES_COUNT = OBJECT_TYPE.getPropertiesCount(); + } + + /** Adds a basic set of properties to the native types. */ + public static void addNativeProperties(JSTypeRegistry registry) { + JSType booleanType = registry.getNativeType(JSTypeNative.BOOLEAN_TYPE); + JSType numberType = registry.getNativeType(JSTypeNative.NUMBER_TYPE); + JSType stringType = registry.getNativeType(JSTypeNative.STRING_TYPE); + JSType unknownType = registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); + + ObjectType objectType = + registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE); + ObjectType arrayType = + registry.getNativeObjectType(JSTypeNative.ARRAY_TYPE); + ObjectType dateType = + registry.getNativeObjectType(JSTypeNative.DATE_TYPE); + ObjectType regexpType = + registry.getNativeObjectType(JSTypeNative.REGEXP_TYPE); + ObjectType booleanObjectType = + registry.getNativeObjectType(JSTypeNative.BOOLEAN_OBJECT_TYPE); + ObjectType numberObjectType = + registry.getNativeObjectType(JSTypeNative.NUMBER_OBJECT_TYPE); + ObjectType stringObjectType = + registry.getNativeObjectType(JSTypeNative.STRING_OBJECT_TYPE); + + ObjectType objectPrototype = registry + .getNativeFunctionType(JSTypeNative.OBJECT_FUNCTION_TYPE) + .getPrototype(); + addMethod(registry, objectPrototype, "constructor", objectType); + addMethod(registry, objectPrototype, "toString", stringType); + addMethod(registry, objectPrototype, "toLocaleString", stringType); + addMethod(registry, objectPrototype, "valueOf", unknownType); + addMethod(registry, objectPrototype, "hasOwnProperty", booleanType); + addMethod(registry, objectPrototype, "isPrototypeOf", booleanType); + addMethod(registry, objectPrototype, "propertyIsEnumerable", booleanType); + + ObjectType arrayPrototype = registry + .getNativeFunctionType(JSTypeNative.ARRAY_FUNCTION_TYPE) + .getPrototype(); + addMethod(registry, arrayPrototype, "constructor", arrayType); + addMethod(registry, arrayPrototype, "toString", stringType); + addMethod(registry, arrayPrototype, "toLocaleString", stringType); + addMethod(registry, arrayPrototype, "concat", arrayType); + addMethod(registry, arrayPrototype, "join", stringType); + addMethod(registry, arrayPrototype, "pop", unknownType); + addMethod(registry, arrayPrototype, "push", numberType); + addMethod(registry, arrayPrototype, "reverse", arrayType); + addMethod(registry, arrayPrototype, "shift", unknownType); + addMethod(registry, arrayPrototype, "slice", arrayType); + addMethod(registry, arrayPrototype, "sort", arrayType); + addMethod(registry, arrayPrototype, "splice", arrayType); + addMethod(registry, arrayPrototype, "unshift", numberType); + arrayType.defineDeclaredProperty("length", numberType, null); + + ObjectType booleanPrototype = registry + .getNativeFunctionType(JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE) + .getPrototype(); + addMethod(registry, booleanPrototype, "constructor", booleanObjectType); + addMethod(registry, booleanPrototype, "toString", stringType); + addMethod(registry, booleanPrototype, "valueOf", booleanType); + + ObjectType datePrototype = registry + .getNativeFunctionType(JSTypeNative.DATE_FUNCTION_TYPE) + .getPrototype(); + addMethod(registry, datePrototype, "constructor", dateType); + addMethod(registry, datePrototype, "toString", stringType); + addMethod(registry, datePrototype, "toDateString", stringType); + addMethod(registry, datePrototype, "toTimeString", stringType); + addMethod(registry, datePrototype, "toLocaleString", stringType); + addMethod(registry, datePrototype, "toLocaleDateString", stringType); + addMethod(registry, datePrototype, "toLocaleTimeString", stringType); + addMethod(registry, datePrototype, "valueOf", numberType); + addMethod(registry, datePrototype, "getTime", numberType); + addMethod(registry, datePrototype, "getFullYear", numberType); + addMethod(registry, datePrototype, "getUTCFullYear", numberType); + addMethod(registry, datePrototype, "getMonth", numberType); + addMethod(registry, datePrototype, "getUTCMonth", numberType); + addMethod(registry, datePrototype, "getDate", numberType); + addMethod(registry, datePrototype, "getUTCDate", numberType); + addMethod(registry, datePrototype, "getDay", numberType); + addMethod(registry, datePrototype, "getUTCDay", numberType); + addMethod(registry, datePrototype, "getHours", numberType); + addMethod(registry, datePrototype, "getUTCHours", numberType); + addMethod(registry, datePrototype, "getMinutes", numberType); + addMethod(registry, datePrototype, "getUTCMinutes", numberType); + addMethod(registry, datePrototype, "getSeconds", numberType); + addMethod(registry, datePrototype, "getUTCSeconds", numberType); + addMethod(registry, datePrototype, "getMilliseconds", numberType); + addMethod(registry, datePrototype, "getUTCMilliseconds", numberType); + addMethod(registry, datePrototype, "getTimezoneOffset", numberType); + addMethod(registry, datePrototype, "setTime", numberType); + addMethod(registry, datePrototype, "setMilliseconds", numberType); + addMethod(registry, datePrototype, "setUTCMilliseconds", numberType); + addMethod(registry, datePrototype, "setSeconds", numberType); + addMethod(registry, datePrototype, "setUTCSeconds", numberType); + addMethod(registry, datePrototype, "setMinutes", numberType); + addMethod(registry, datePrototype, "setUTCMinutes", numberType); + addMethod(registry, datePrototype, "setHours", numberType); + addMethod(registry, datePrototype, "setUTCHours", numberType); + addMethod(registry, datePrototype, "setDate", numberType); + addMethod(registry, datePrototype, "setUTCDate", numberType); + addMethod(registry, datePrototype, "setMonth", numberType); + addMethod(registry, datePrototype, "setUTCMonth", numberType); + addMethod(registry, datePrototype, "setFullYear", numberType); + addMethod(registry, datePrototype, "setUTCFullYear", numberType); + addMethod(registry, datePrototype, "toUTCString", stringType); + addMethod(registry, datePrototype, "toGMTString", stringType); + + ObjectType numberPrototype = registry + .getNativeFunctionType(JSTypeNative.NUMBER_OBJECT_FUNCTION_TYPE) + .getPrototype(); + addMethod(registry, numberPrototype, "constructor", numberObjectType); + addMethod(registry, numberPrototype, "toString", stringType); + addMethod(registry, numberPrototype, "toLocaleString", stringType); + addMethod(registry, numberPrototype, "valueOf", numberType); + addMethod(registry, numberPrototype, "toFixed", stringType); + addMethod(registry, numberPrototype, "toExponential", stringType); + addMethod(registry, numberPrototype, "toPrecision", stringType); + + ObjectType regexpPrototype = registry + .getNativeFunctionType(JSTypeNative.REGEXP_FUNCTION_TYPE) + .getPrototype(); + addMethod(registry, regexpPrototype, "constructor", regexpType); + addMethod(registry, regexpPrototype, "exec", + registry.createNullableType(arrayType)); + addMethod(registry, regexpPrototype, "test", booleanType); + addMethod(registry, regexpPrototype, "toString", stringType); + regexpType.defineDeclaredProperty("source", stringType, null); + regexpType.defineDeclaredProperty("global", booleanType, null); + regexpType.defineDeclaredProperty("ignoreCase", booleanType, null); + regexpType.defineDeclaredProperty("multiline", booleanType, null); + regexpType.defineDeclaredProperty("lastIndex", numberType, null); + + ObjectType stringPrototype = registry + .getNativeFunctionType(JSTypeNative.STRING_OBJECT_FUNCTION_TYPE) + .getPrototype(); + addMethod(registry, stringPrototype, "constructor", stringObjectType); + addMethod(registry, stringPrototype, "toString", stringType); + addMethod(registry, stringPrototype, "valueOf", stringType); + addMethod(registry, stringPrototype, "charAt", stringType); + addMethod(registry, stringPrototype, "charCodeAt", numberType); + addMethod(registry, stringPrototype, "concat", stringType); + addMethod(registry, stringPrototype, "indexOf", numberType); + addMethod(registry, stringPrototype, "lastIndexOf", numberType); + addMethod(registry, stringPrototype, "localeCompare", numberType); + addMethod(registry, stringPrototype, "match", + registry.createNullableType(arrayType)); + addMethod(registry, stringPrototype, "replace", stringType); + addMethod(registry, stringPrototype, "search", numberType); + addMethod(registry, stringPrototype, "slice", stringType); + addMethod(registry, stringPrototype, "split", arrayType); + addMethod(registry, stringPrototype, "substring", stringType); + addMethod(registry, stringPrototype, "toLowerCase", stringType); + addMethod(registry, stringPrototype, "toLocaleLowerCase", stringType); + addMethod(registry, stringPrototype, "toUpperCase", stringType); + addMethod(registry, stringPrototype, "toLocaleUpperCase", stringType); + stringObjectType.defineDeclaredProperty("length", numberType, null); + } + + private static void addMethod( + JSTypeRegistry registry, ObjectType receivingType, String methodName, + JSType returnType) { + receivingType.defineDeclaredProperty(methodName, + new FunctionBuilder(registry).withReturnType(returnType).build(), + null); + } + + protected JSType createUnionType(JSType... variants) { + return registry.createUnionType(variants); + } + + protected RecordTypeBuilder createRecordTypeBuilder() { + return new RecordTypeBuilder(registry); + } + + protected JSType createNullableType(JSType type) { + return registry.createNullableType(type); + } + + protected JSType createOptionalType(JSType type) { + return registry.createOptionalType(type); + } + + protected JSType createTemplatizedType( + JSType baseType, ImmutableList templatizedTypes) { + return registry.createTemplatizedType(baseType, templatizedTypes); + } + + protected JSType createParameterizedType( + ObjectType type, JSType typeParameter) { + return registry.createParameterizedType(type, typeParameter); + } + + /** + * Asserts that a Node representing a type expression resolves to the + * correct {@code JSType}. + */ + protected void assertTypeEquals(JSType expected, Node actual) { + assertTypeEquals(expected, new JSTypeExpression(actual, "")); + } + + /** + * Asserts that a a type expression resolves to the correct {@code JSType}. + */ + protected void assertTypeEquals(JSType expected, JSTypeExpression actual) { + assertEquals(expected, resolve(actual)); + } + + /** + * Resolves a type expression, expecting the given warnings. + */ + protected JSType resolve(JSTypeExpression n, String... warnings) { + errorReporter.setWarnings(warnings); + return n.evaluate(null, registry); + } + + /** + * A definition of all extern types. This should be kept in sync with + * javascript/externs/es3.js. This is used to check that the built-in types + * declared in {@link JSTypeRegistry} have the same type as that in the + * externs. It can also be used for any tests that want to use built-in types + * in their externs. + */ + public static final String ALL_NATIVE_EXTERN_TYPES = + "/**\n" + + " * @constructor\n" + + " * @param {*=} opt_value\n" + + " */\n" + + "function Object(opt_value) {}\n" + + "\n" + + "/**\n" + + " * @constructor\n" + + " * @extends {Object}\n" + + " * @param {...*} var_args\n" + + " */\n" + + "\n" + + "function Function(var_args) {}\n" + + "/**\n" + + " * @constructor\n" + + " * @extends {Object}\n" + + " * @param {...*} var_args\n" + + " * @return {!Array}\n" + + " */\n" + + "function Array(var_args) {}\n" + + "\n" + + "/**\n" + + " * @constructor\n" + + " * @param {*=} opt_value\n" + + " * @return {boolean}\n" + + " */\n" + + "function Boolean(opt_value) {}\n" + + "\n" + + "/**\n" + + " * @constructor\n" + + " * @param {*=} opt_value\n" + + " * @return {number}\n" + + " */\n" + + "function Number(opt_value) {}\n" + + "\n" + + "/**\n" + + " * @constructor\n" + + " * @param {?=} opt_yr_num\n" + + " * @param {?=} opt_mo_num\n" + + " * @param {?=} opt_day_num\n" + + " * @param {?=} opt_hr_num\n" + + " * @param {?=} opt_min_num\n" + + " * @param {?=} opt_sec_num\n" + + " * @param {?=} opt_ms_num\n" + + " * @return {string}\n" + + " */\n" + + "function Date(opt_yr_num, opt_mo_num, opt_day_num, opt_hr_num," + + " opt_min_num, opt_sec_num, opt_ms_num) {}\n" + + "\n" + + "/**\n" + + " * @constructor\n" + + " * @extends {Object}\n" + + " * @param {*=} opt_str\n" + + " * @return {string}\n" + + " */\n" + + "function String(opt_str) {}\n" + + "\n" + + "/**\n" + + " * @constructor\n" + + " * @param {*=} opt_pattern\n" + + " * @param {*=} opt_flags\n" + + " * @return {!RegExp}\n" + + " */\n" + + "function RegExp(opt_pattern, opt_flags) {}\n" + + "\n" + + "/**\n" + + " * @constructor\n" + + " * @param {*=} opt_message\n" + + " * @param {*=} opt_file\n" + + " * @param {*=} opt_line\n" + + " * @return {!Error}\n" + + " */\n" + + "function Error(opt_message, opt_file, opt_line) {}\n" + + "\n" + + "/**\n" + + " * @constructor\n" + + " * @extends {Error}\n" + + " * @param {*=} opt_message\n" + + " * @param {*=} opt_file\n" + + " * @param {*=} opt_line\n" + + " * @return {!EvalError}\n" + + " */\n" + + "function EvalError(opt_message, opt_file, opt_line) {}\n" + + "\n" + + "/**\n" + + " * @constructor\n" + + " * @extends {Error}\n" + + " * @param {*=} opt_message\n" + + " * @param {*=} opt_file\n" + + " * @param {*=} opt_line\n" + + " * @return {!RangeError}\n" + + " */\n" + + "function RangeError(opt_message, opt_file, opt_line) {}\n" + + "\n" + + "/**\n" + + " * @constructor\n" + + " * @extends {Error}\n" + + " * @param {*=} opt_message\n" + + " * @param {*=} opt_file\n" + + " * @param {*=} opt_line\n" + + " * @return {!ReferenceError}\n" + + " */\n" + + "function ReferenceError(opt_message, opt_file, opt_line) {}\n" + + "\n" + + "/**\n" + + " * @constructor\n" + + " * @extends {Error}\n" + + " * @param {*=} opt_message\n" + + " * @param {*=} opt_file\n" + + " * @param {*=} opt_line\n" + + " * @return {!SyntaxError}\n" + + " */\n" + + "function SyntaxError(opt_message, opt_file, opt_line) {}\n" + + "\n" + + "/**\n" + + " * @constructor\n" + + " * @extends {Error}\n" + + " * @param {*=} opt_message\n" + + " * @param {*=} opt_file\n" + + " * @param {*=} opt_line\n" + + " * @return {!TypeError}\n" + + " */\n" + + "function TypeError(opt_message, opt_file, opt_line) {}\n" + + "\n" + + "/**\n" + + " * @constructor\n" + + " * @extends {Error}\n" + + " * @param {*=} opt_message\n" + + " * @param {*=} opt_file\n" + + " * @param {*=} opt_line\n" + + " * @return {!URIError}\n" + + " */\n" + + "function URIError(opt_message, opt_file, opt_line) {}\n" + + "\n" + + "/**\n" + + " * @param {string} progId\n" + + " * @param {string=} opt_location\n" + + " * @constructor\n" + + " */\n" + + "function ActiveXObject(progId, opt_location) {}\n"; + + protected final void assertTypeEquals(JSType a, JSType b) { + Asserts.assertTypeEquals(a, b); + } + + protected final void assertTypeEquals(String msg, JSType a, JSType b) { + Asserts.assertTypeEquals(msg, a, b); + } + + protected final void assertTypeNotEquals(JSType a, JSType b) { + Asserts.assertTypeNotEquals(a, b); + } + + protected final void assertTypeNotEquals(String msg, JSType a, JSType b) { + Asserts.assertTypeNotEquals(msg, a, b); + } + + protected final ParameterizedType parameterize(ObjectType objType, JSType t) { + return registry.createParameterizedType(objType, t); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/testing/MapBasedScope.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/testing/MapBasedScope.java new file mode 100644 index 0000000..1bc791f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/testing/MapBasedScope.java @@ -0,0 +1,72 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.testing; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.SimpleSlot; +import com.google.javascript.rhino.jstype.StaticSlot; + +import java.util.Map; + +/** + * A scope based on a simple hashmap. + * @author nicksantos@google.com (Nick Santos) + */ +public class MapBasedScope extends AbstractStaticScope { + private final Map> slots = Maps.newHashMap(); + + public MapBasedScope(Map namesToTypes) { + for (Map.Entry entry : namesToTypes.entrySet()) { + slots.put( + entry.getKey(), + new SimpleSlot(entry.getKey(), entry.getValue(), false)); + } + } + + public static MapBasedScope emptyScope() { + return new MapBasedScope(ImmutableMap.of()); + } + + @Override + public StaticSlot getSlot(String name) { + return slots.get(name); + } +} diff --git a/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/testing/TestErrorReporter.java b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/testing/TestErrorReporter.java new file mode 100644 index 0000000..1dbd81e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/src/com/google/javascript/rhino/testing/TestErrorReporter.java @@ -0,0 +1,120 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Jervis + * Google Inc. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.testing; + +import com.google.javascript.rhino.ErrorReporter; +import junit.framework.Assert; + +/** + *

      An error reporter for testing that verifies that messages reported to the + * reporter are expected.

      + * + *

      Sample use

      + *
      + * TestErrorReporter e =
      + *   new TestErrorReporter(null, new String[] { "first warning" });
      + * ...
      + * assertTrue(e.hasEncounteredAllWarnings());
      + * 
      + * + */ +public final class TestErrorReporter extends Assert implements ErrorReporter { + private String[] errors; + private String[] warnings; + private int errorsIndex = 0; + private int warningsIndex = 0; + + public TestErrorReporter(String[] errors, String[] warnings) { + this.errors = errors; + this.warnings = warnings; + } + + public static TestErrorReporter forNoExpectedReports() { + return new TestErrorReporter(null, null); + } + + public void setErrors(String[] errors) { + this.errors = errors; + errorsIndex = 0; + } + + public void setWarnings(String[] warnings) { + this.warnings = warnings; + warningsIndex = 0; + } + + @Override + public void error(String message, String sourceName, int line, + int lineOffset) { + if (errors != null && errorsIndex < errors.length) { + assertEquals(errors[errorsIndex++], message); + } else { + fail("extra error: " + message); + } + } + + @Override + public void warning(String message, String sourceName, int line, + int lineOffset) { + if (warnings != null && warningsIndex < warnings.length) { + assertEquals(warnings[warningsIndex++], message); + } else { + fail("extra warning: " + message); + } + } + + /** + * Returns whether all warnings were reported to this reporter. + */ + public boolean hasEncounteredAllWarnings() { + return (warnings == null) ? + warningsIndex == 0 : + warnings.length == warningsIndex; + } + + /** + * Returns whether all errors were reported to this reporter. + */ + public boolean hasEncounteredAllErrors() { + return (errors == null) ? + errorsIndex == 0 : + errors.length == errorsIndex; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/Base64Test.java b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/Base64Test.java new file mode 100644 index 0000000..212ef72 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/Base64Test.java @@ -0,0 +1,42 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import junit.framework.TestCase; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class Base64Test extends TestCase { + public void testBase64() { + for (int i = 0; i < 64; i++) { + testValue(i); + } + } + + public void testBase64EncodeInt() { + assertEquals("AAAAAA", Base64.base64EncodeInt(0)); + assertEquals("AAAAAQ", Base64.base64EncodeInt(1)); + assertEquals("AAAAKg", Base64.base64EncodeInt(42)); + assertEquals("////nA", Base64.base64EncodeInt(-100)); + assertEquals("/////w", Base64.base64EncodeInt(0xffffffff)); + } + + private void testValue(int value) { + assertEquals(value, Base64.fromBase64(Base64.toBase64(value))); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/Base64VLQTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/Base64VLQTest.java new file mode 100644 index 0000000..000b932 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/Base64VLQTest.java @@ -0,0 +1,133 @@ +/* + * Copyright 2011 The Closure Compiler Authors. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.debugging.sourcemap; + +import junit.framework.TestCase; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class Base64VLQTest extends TestCase { + public void testBase64VLQSelectedValues1() { + for (int i = 0; i < 63; i++) { + testValue(i); + } + } + + public void testBase64VLQSelectedValues2() { + int base = 1; + for (int i = 0; i < 30; i++) { + testValue(base-1); + testValue(base); + base *= 2; + } + } + + public void testBase64VLQSelectedSignedValues1() { + for (int i = -(64*64-1); i < (64*64-1); i++) { + testValue(i); + } + } + + public void testBase64VLQSelectedSignedValues2() { + int base = 1; + for (int i = 0; i < 30; i++) { + testValue(base-1); + testValue(base); + base *= 2; + } + base = -1; + for (int i = 0; i < 30; i++) { + testValue(base-1); + testValue(base); + base *= 2; + } + } + + static class CharIteratorImpl implements Base64VLQ.CharIterator { + private int current; + private int length; + private CharSequence cs; + + void set(CharSequence sb) { + this.current = 0; + this.length = sb.length(); + this.cs = sb; + } + + @Override + public boolean hasNext() { + return current < length; + } + + @Override + public char next() { + return cs.charAt(current++); + } + } + + // Disable this test if it is flaky. + public void testSpeed() {} +// Defects4J: flaky method +// public void testSpeed() { +// long start = System.currentTimeMillis(); +// CharIteratorImpl ci = new CharIteratorImpl(); +// try { +// StringBuilder sb = new StringBuilder(); +// for (int i = 0; i < 1000000; i++) { +// Base64VLQ.encode(sb, i); +// ci.set(sb); +// int result = Base64VLQ.decode(ci); +// assertEquals(i, result); +// sb.setLength(0); +// } +// } catch (Exception e) { +// throw new RuntimeException("failed.", e); +// } +// long end = System.currentTimeMillis(); +// // Was 200ms or less, use a larger number to prevent flakiness +// assertTrue("too slow", end-start < 1000); +// } + + private void testValue(int value) { + try { + StringBuilder sb = new StringBuilder(); + Base64VLQ.encode(sb, value); + CharIteratorImpl ci = new CharIteratorImpl(); + ci.set(sb); + int result = Base64VLQ.decode(ci); + assertEquals(value, result); + } catch (Exception e) { + throw new RuntimeException("failed for value " + value, e); + } + } + + +} \ No newline at end of file diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/Base64VLQTest.java.bak b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/Base64VLQTest.java.bak new file mode 100644 index 0000000..5899907 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/Base64VLQTest.java.bak @@ -0,0 +1,131 @@ +/* + * Copyright 2011 The Closure Compiler Authors. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.debugging.sourcemap; + +import junit.framework.TestCase; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class Base64VLQTest extends TestCase { + public void testBase64VLQSelectedValues1() { + for (int i = 0; i < 63; i++) { + testValue(i); + } + } + + public void testBase64VLQSelectedValues2() { + int base = 1; + for (int i = 0; i < 30; i++) { + testValue(base-1); + testValue(base); + base *= 2; + } + } + + public void testBase64VLQSelectedSignedValues1() { + for (int i = -(64*64-1); i < (64*64-1); i++) { + testValue(i); + } + } + + public void testBase64VLQSelectedSignedValues2() { + int base = 1; + for (int i = 0; i < 30; i++) { + testValue(base-1); + testValue(base); + base *= 2; + } + base = -1; + for (int i = 0; i < 30; i++) { + testValue(base-1); + testValue(base); + base *= 2; + } + } + + static class CharIteratorImpl implements Base64VLQ.CharIterator { + private int current; + private int length; + private CharSequence cs; + + void set(CharSequence sb) { + this.current = 0; + this.length = sb.length(); + this.cs = sb; + } + + @Override + public boolean hasNext() { + return current < length; + } + + @Override + public char next() { + return cs.charAt(current++); + } + } + + // Disable this test if it is flaky. + public void testSpeed() { + long start = System.currentTimeMillis(); + CharIteratorImpl ci = new CharIteratorImpl(); + try { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 1000000; i++) { + Base64VLQ.encode(sb, i); + ci.set(sb); + int result = Base64VLQ.decode(ci); + assertEquals(i, result); + sb.setLength(0); + } + } catch (Exception e) { + throw new RuntimeException("failed.", e); + } + long end = System.currentTimeMillis(); + // Was 200ms or less, use a larger number to prevent flakiness + assertTrue("too slow", end-start < 1000); + } + + private void testValue(int value) { + try { + StringBuilder sb = new StringBuilder(); + Base64VLQ.encode(sb, value); + CharIteratorImpl ci = new CharIteratorImpl(); + ci.set(sb); + int result = Base64VLQ.decode(ci); + assertEquals(value, result); + } catch (Exception e) { + throw new RuntimeException("failed for value " + value, e); + } + } + + +} \ No newline at end of file diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapConsumerV1Test.java b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapConsumerV1Test.java new file mode 100644 index 0000000..0a46421 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapConsumerV1Test.java @@ -0,0 +1,342 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import com.google.debugging.sourcemap.SourceMapConsumerV1; +import com.google.debugging.sourcemap.SourceMapParseException; +import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; + +import junit.framework.TestCase; + +public class SourceMapConsumerV1Test extends TestCase { + + public SourceMapConsumerV1Test() { + } + + public SourceMapConsumerV1Test(String name) { + super(name); + } + + public void testGetMappingForLine() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("/** Begin line maps. **/{ count : 2 }\n"); + sb.append("[0,,,,1,,2]\n"); + sb.append("[3,,,,,,,]\n"); + sb.append("/** Begin file information. **/\n"); + sb.append("['test.js']\n"); + sb.append("['foo.js']\n"); + sb.append("/** Begin mapping definitions. **/\n"); + sb.append("['test.js', 0, 1]\n"); + sb.append("['bleg.js', 5, 8, 'hello']\n"); + sb.append("['bleg.js', 12, 78]\n"); + sb.append("['foo.js', 15, 16, 'yo!']"); + + SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); + sourceMap.parse(sb.toString()); + + OriginalMapping mapping = sourceMap.getMappingForLine(1, 1); + + assertNotNull(mapping); + assertEquals("test.js", mapping.getOriginalFile()); + assertEquals(0, mapping.getLineNumber()); + assertEquals(1, mapping.getColumnPosition()); + assertEquals("", mapping.getIdentifier()); + + mapping = sourceMap.getMappingForLine(1, 6); + assertNotNull(mapping); + assertEquals("bleg.js", mapping.getOriginalFile()); + assertEquals(5, mapping.getLineNumber()); + assertEquals(8, mapping.getColumnPosition()); + assertEquals("hello", mapping.getIdentifier()); + + mapping = sourceMap.getMappingForLine(2, 4); + assertNotNull(mapping); + assertEquals("foo.js", mapping.getOriginalFile()); + assertEquals(15, mapping.getLineNumber()); + assertEquals(16, mapping.getColumnPosition()); + assertEquals("yo!", mapping.getIdentifier()); + + assertNull(sourceMap.getMappingForLine(Integer.MAX_VALUE, 1)); + assertNotNull(sourceMap.getMappingForLine(1, Integer.MAX_VALUE)); + } + + public void testLineEdges() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("/** Begin line maps. **/{ count : 2 }\n"); + sb.append("[100]\n"); + sb.append("[200]\n"); + sb.append("/** Begin file information. **/\n"); + sb.append("['test.js']\n"); + sb.append("['foo.js']\n"); + sb.append("/** Begin mapping definitions. **/\n"); + for (int i = 0; i <= 200; i++) { + sb.append("['foo.js', ").append(i).append(", 1]\n"); + } + + SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); + sourceMap.parse(sb.toString()); + + OriginalMapping mapping = sourceMap.getMappingForLine(-1, 1); + assertNull(mapping); + + mapping = sourceMap.getMappingForLine(0, 1); + assertNull(mapping); + + mapping = sourceMap.getMappingForLine(1, 1); + assertEquals(100, mapping.getLineNumber()); + + mapping = sourceMap.getMappingForLine(2, 1); + assertEquals(200, mapping.getLineNumber()); + + mapping = sourceMap.getMappingForLine(3, 1); + assertNull(mapping); + } + + public void testColumnEdges() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("/** Begin line maps. **/{ count : 1 }\n"); + sb.append("[100, 101]\n"); + sb.append("/** Begin file information. **/\n"); + sb.append("\n"); + sb.append("/** Begin mapping definitions. **/\n"); + for (int i = 0; i <= 200; i++) { + sb.append("['foo.js', ").append(i).append(", 1]\n"); + } + + SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); + sourceMap.parse(sb.toString()); + + OriginalMapping mapping = sourceMap.getMappingForLine(1, -1); + assertNull(mapping); + + mapping = sourceMap.getMappingForLine(1, 0); + assertNull(mapping); + + mapping = sourceMap.getMappingForLine(1, 1); + assertEquals(100, mapping.getLineNumber()); + + mapping = sourceMap.getMappingForLine(1, 2); + assertEquals(101, mapping.getLineNumber()); + + // Columns beyond the end of the line are treated the same + // as the last column. + mapping = sourceMap.getMappingForLine(1, 3); + assertEquals(101, mapping.getLineNumber()); + } + + public void testNegativeOneInLineMap() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("/** Begin line maps. **/{ count : 1 }\n"); + sb.append("[200,-1,199,-1]\n"); + sb.append("/** Begin file information. **/\n"); + sb.append("\n"); + sb.append("/** Begin mapping definitions. **/\n"); + for (int i = 0; i <= 200; i++) { + sb.append("['foo.js', 1, ").append(i).append("]\n"); + } + + SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); + sourceMap.parse(sb.toString()); + + OriginalMapping mapping = sourceMap.getMappingForLine(1, 2); + assertNull(mapping); + + mapping = sourceMap.getMappingForLine(1, 4); + assertNull(mapping); + } + + public void testSimpleParse() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("/** Begin line maps. **/{ count : 1 }\n"); + sb.append("[0,,,,1,2]\n"); + sb.append("/** Begin file information. **/\n"); + sb.append("['test.js']\n"); + sb.append("/** Begin mapping definitions. **/\n"); + sb.append("['test.js', 0, 1]\n"); + sb.append("['test.js', 0, 1, 'hello']\n"); + sb.append("['test.js', 0, 1]"); + + SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); + sourceMap.parse(sb.toString()); + } + + public void testBlankLine() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("/** Begin line maps. **/{ count : 1 }\n"); + sb.append("\n"); + sb.append("/** Begin file information. **/\n"); + sb.append("\n"); + sb.append("/** Begin mapping definitions. **/\n"); + + SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); + sourceMap.parse(sb.toString()); + } + + public void testCountFailure() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("/** Begin line maps. **/{ counter : 1 }\n"); + + assertException("Missing 'count'", sb); + } + + public void testInvalidCountFailure() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("/** Begin line maps. **/{ count : 0 }\n"); + + assertException("Count must be >= 1", sb); + } + + public void testInvalidJSONFailure() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("/** Begin line maps. **/{ count : 2 }\n"); + sb.append("[0,,,,2\n"); + + assertExceptionStartsWith("JSON parse exception: org.json.JSONException: " + + "Expected a ',' or ']' at ", sb); + } + + public void testInvalidHeaderFailure() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("/** Begin line maps. **/{ count : 1 }\n"); + sb.append("[0,,,1]\n"); + sb.append("[3,,,4]\n"); + + assertException( + "Expected /** Begin file information. **/ got [3,,,4]", sb); + } + + public void testInvalidPostHeaderToken() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("/** Begin line maps. **/{ count : 1 }\n"); + sb.append("[0,,,1]\n"); + sb.append("/** Begin file information. **/f\n"); + + assertException("Expected /** Begin file information. **/" + + " got /** Begin file information. **/f", sb); + } + + public void testInvalidMappingArrayFailure() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("/** Begin line maps. **/{ count : 1 }\n"); + sb.append("[0,,,,1,2]\n"); + sb.append("/** Begin file information. **/\n"); + sb.append("['test.js']\n"); + sb.append("/** Begin mapping definitions. **/\n"); + sb.append("['test.js', 0]\n"); + + assertException("Invalid mapping array", sb); + } + + public void testMultipleLineFragments() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("/** Begin line maps. **/{ count : 1 }\n"); + sb.append("[0,1,2,3,260,261,262]\n"); + sb.append("/** Begin file information. **/\n"); + sb.append("\n"); + sb.append("/** Begin mapping definitions. **/\n"); + for (int i = 0; i < 262; i++) { + sb.append("['frog/test" + i + ".js', " + i + ", 1]\n"); + } + sb.append("['frog/testigloo.js', 500, 1]"); + + SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); + sourceMap.parse(sb.toString()); + + OriginalMapping mapping = sourceMap.getMappingForLine(1, 1); + + assertNotNull(mapping); + assertEquals("frog/test0.js", mapping.getOriginalFile()); + assertEquals(0, mapping.getLineNumber()); + assertEquals(1, mapping.getColumnPosition()); + assertEquals("", mapping.getIdentifier()); + + mapping = sourceMap.getMappingForLine(1, 6); + assertNotNull(mapping); + assertEquals("frog/test261.js", mapping.getOriginalFile()); + assertEquals(261, mapping.getLineNumber()); + assertEquals(1, mapping.getColumnPosition()); + } + + public void testMultipleMappingFragments() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("/** Begin line maps. **/{ count : 1 }\n"); + sb.append("[0,1,2,3,260,261,262,1023]\n"); + sb.append("/** Begin file information. **/\n"); + sb.append("\n"); + sb.append("/** Begin mapping definitions. **/\n"); + for (int i = 0; i < 2000; i++) { + sb.append("['frog/test" + (i / 100) + ".js', " + i + ", 1]\n"); + } + sb.append("['frog/testigloo.js', 500, 1]"); + + SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); + sourceMap.parse(sb.toString()); + + OriginalMapping mapping = sourceMap.getMappingForLine(1, 1); + + assertNotNull(mapping); + assertEquals("frog/test0.js", mapping.getOriginalFile()); + assertEquals(0, mapping.getLineNumber()); + assertEquals(1, mapping.getColumnPosition()); + assertEquals("", mapping.getIdentifier()); + + mapping = sourceMap.getMappingForLine(1, 6); + assertNotNull(mapping); + assertEquals("frog/test2.js", mapping.getOriginalFile()); + assertEquals(261, mapping.getLineNumber()); + assertEquals(1, mapping.getColumnPosition()); + + mapping = sourceMap.getMappingForLine(1, 8); + assertNotNull(mapping); + assertEquals("frog/test10.js", mapping.getOriginalFile()); + assertEquals(1023, mapping.getLineNumber()); + assertEquals(1, mapping.getColumnPosition()); + } + + private void assertException(String exception, StringBuilder sb) { + boolean exceptionRaised = false; + + try { + SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); + sourceMap.parse(sb.toString()); + + } catch (SourceMapParseException pe) { + assertEquals(exception, pe.getMessage()); + exceptionRaised = true; + } + + assertTrue(exceptionRaised); + } + + private void assertExceptionStartsWith(String exception, StringBuilder sb) { + boolean exceptionRaised = false; + + try { + SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); + sourceMap.parse(sb.toString()); + + } catch (SourceMapParseException pe) { + assertTrue( + "expected <" + exception +"> but was <"+ pe.getMessage() +">", + pe.getMessage().startsWith(exception)); + exceptionRaised = true; + } + + assertTrue(exceptionRaised); + } + +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapConsumerV2Test.java b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapConsumerV2Test.java new file mode 100644 index 0000000..4eac92c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapConsumerV2Test.java @@ -0,0 +1,184 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import com.google.debugging.sourcemap.SourceMapConsumerV2; +import com.google.debugging.sourcemap.SourceMapParseException; +import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; + +import junit.framework.TestCase; + +public class SourceMapConsumerV2Test extends TestCase { + + public SourceMapConsumerV2Test() { + } + + public SourceMapConsumerV2Test(String name) { + super(name); + } + + public void testEmptyMap() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("{\n"); + sb.append("\"version\": 2,\n"); + sb.append("\"file\": \"somefile.js\",\n"); + sb.append("\"lineCount\": 0,\n"); + sb.append("\"lineMaps\": [],\n"); + sb.append("\"sources\": [],\n"); + sb.append("\"mappings\": []\n"); + sb.append("}\n"); + + SourceMapConsumerV2 sourceMap = new SourceMapConsumerV2(); + sourceMap.parse(sb.toString()); + } + + public void testGetMappingForLine() throws Exception { + // Input Code: function f(foo, bar) { foo = foo + bar + 2; return foo; } + String mapData = + "{\n" + + "\"version\":2,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"lineMaps\":" + + "[\"cAEBABIBA/ICA+ADICA/ICA+IDA9AEYBMBA5\"],\n" + + "\"sources\":[\"testcode\"],\n" + + "\"mappings\":[[0,1,9,\"f\"],\n" + + "[0,1,9,\"f\"],\n" + + "[0,1,10],\n" + + "[0,1,11,\"foo\"],\n" + + "[0,1,16,\"bar\"],\n" + + "[0,1,21],\n" + + "[0,1,23],\n" + + "[0,1,23,\"foo\"],\n" + + "[0,1,29,\"foo\"],\n" + + "[0,1,35,\"bar\"],\n" + + "[0,1,41],\n" + + "[0,1,44],\n" + + "[0,1,51,\"foo\"],\n" + + "]\n" + + "}\n"; + + SourceMapConsumerV2 sourceMap = new SourceMapConsumerV2(); + sourceMap.parse(mapData); + + OriginalMapping mapping = sourceMap.getMappingForLine(1, 10); + + assertNotNull(mapping); + assertEquals("testcode", mapping.getOriginalFile()); + assertEquals(1, mapping.getLineNumber()); + assertEquals(9, mapping.getColumnPosition()); + assertEquals("f", mapping.getIdentifier()); + + mapping = sourceMap.getMappingForLine(1, 40); + + assertNotNull(mapping); + assertEquals("testcode", mapping.getOriginalFile()); + assertEquals(1, mapping.getLineNumber()); + assertEquals(44, mapping.getColumnPosition()); + assertEquals("", mapping.getIdentifier()); + + mapping = sourceMap.getMappingForLine(1, 42); + assertNotNull(mapping); + assertEquals("testcode", mapping.getOriginalFile()); + assertEquals(1, mapping.getLineNumber()); + assertEquals(51, mapping.getColumnPosition()); + assertEquals("foo", mapping.getIdentifier()); + + assertNull(sourceMap.getMappingForLine(Integer.MAX_VALUE, 1)); + assertNull(sourceMap.getMappingForLine(1, Integer.MAX_VALUE)); + } + + public void testGetMappingForLineWithNameIndex() throws Exception { + String mapData = + "{\n" + + "\"version\":2,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"lineMaps\":" + + "[\"cAEBABIBA/ICA+ADICA/ICA+IDA9AEYBMBA5\"],\n" + + "\"sources\":[\"testcode\"],\n" + + "\"names\": [\"f\"],\n" + + "\"mappings\":[[0,1,9,0],\n" + + "[0,1,9,0]\n" + + "]\n" + + "}\n"; + + SourceMapConsumerV2 sourceMap = new SourceMapConsumerV2(); + sourceMap.parse(mapData); + + OriginalMapping mapping = sourceMap.getMappingForLine(1, 10); + + assertNotNull(mapping); + assertEquals("testcode", mapping.getOriginalFile()); + assertEquals(1, mapping.getLineNumber()); + assertEquals(9, mapping.getColumnPosition()); + assertEquals("f", mapping.getIdentifier()); + } + + public void testInvalidJSONFailure() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("notjson"); + + assertExceptionStartsWith("JSON parse exception: org.json.JSONException: " + + "A JSONObject text must begin " + + "with '{' at character 1", sb); + } + + public void testUnknownVersion() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("{\"version\": 3}"); + assertException("Unknown version: 3", sb); + } + + public void testMissingFile() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("{\"version\": 2, \"file\": \"\"}"); + assertException("File entry is missing or empty", sb); + } + + private void assertException(String exception, StringBuilder sb) { + boolean exceptionRaised = false; + + try { + SourceMapConsumerV2 sourceMap = new SourceMapConsumerV2(); + sourceMap.parse(sb.toString()); + + } catch (SourceMapParseException pe) { + assertEquals(exception, pe.getMessage()); + exceptionRaised = true; + } + + assertTrue(exceptionRaised); + } + + private void assertExceptionStartsWith(String exception, StringBuilder sb) { + boolean exceptionRaised = false; + + try { + SourceMapConsumerV2 sourceMap = new SourceMapConsumerV2(); + sourceMap.parse(sb.toString()); + + } catch (SourceMapParseException pe) { + assertTrue( + "expected <" + exception +"> but was <"+ pe.getMessage() +">", + pe.getMessage().startsWith(exception)); + exceptionRaised = true; + } + + assertTrue(exceptionRaised); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapGeneratorV1Test.java b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapGeneratorV1Test.java new file mode 100644 index 0000000..1d3c3a3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapGeneratorV1Test.java @@ -0,0 +1,458 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import com.google.javascript.jscomp.SourceMap; +import com.google.javascript.jscomp.SourceMap.Format; + +/** + * Tests for {@link SourceMap}. + * + */ +public class SourceMapGeneratorV1Test extends SourceMapTestCase { + + public SourceMapGeneratorV1Test() { + disableColumnValidation(); + } + + @Override + protected SourceMapConsumer getSourceMapConsumer() { + return new SourceMapConsumerV1(); + } + + @Override + protected Format getSourceMapFormat() { + return SourceMap.Format.V1; + } + + @Override + public void setUp() { + detailLevel = SourceMap.DetailLevel.ALL; + } + + public void testBasicMapping() throws Exception { + compileAndCheck("function __BASIC__() { }"); + } + + public void testLiteralMappings() throws Exception { + compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) { " + + "var __VAR__ = '__STR__'; }"); + } + + public void testMultilineMapping() throws Exception { + compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) {\n" + + "var __VAR__ = '__STR__';\n" + + "var __ANO__ = \"__STR2__\";\n" + + "}"); + } + + public void testMultiFunctionMapping() throws Exception { + compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) {\n" + + "var __VAR__ = '__STR__';\n" + + "var __ANO__ = \"__STR2__\";\n" + + "}\n\n" + + + "function __BASIC2__(__PARAM3__, __PARAM4__) {\n" + + "var __VAR2__ = '__STR2__';\n" + + "var __ANO2__ = \"__STR3__\";\n" + + "}\n\n"); + } + + public void testGoldenOutput0() throws Exception { + // Empty source map test + checkSourceMap("", + + "/** Begin line maps. **/{ \"file\" : \"testcode\"," + + " \"count\": 1 }\n" + + + "[]\n" + + + "/** Begin file information. **/\n" + + "[]\n" + + + "/** Begin mapping definitions. **/\n"); + } + + public void testFunctionNameOutput1() throws Exception { + checkSourceMap("function f() {}", + "/** Begin line maps. **/{ \"file\" : \"testcode\", " + + "\"count\": 1 }\n" + + + "[0,0,0,0,0,0,0,0,1,1,2,2,3,3]\n" + + + "/** Begin file information. **/\n" + + "[]\n" + + + "/** Begin mapping definitions. **/\n" + + "[\"testcode\",1,0,\"f\"]\n" + + "[\"testcode\",1,9,\"f\"]\n" + + "[\"testcode\",1,10]\n" + + "[\"testcode\",1,13]\n"); + } + + public void testFunctionNameOutput2() throws Exception { + checkSourceMap("a.b.c = function () {};", + + "/** Begin line maps. **/{ \"file\" : \"testcode\", " + + "\"count\": 1 }\n" + + + "[3,2,2,1,1,0,4,4,4,4,4,4,4,4,5,5,6,6]\n" + + + "/** Begin file information. **/\n" + + "[]\n" + + + "/** Begin mapping definitions. **/\n" + + "[\"testcode\",1,0]\n" + + "[\"testcode\",1,0,\"c\"]\n" + + "[\"testcode\",1,0,\"b\"]\n" + + "[\"testcode\",1,0,\"a\"]\n" + + "[\"testcode\",1,8,\"a.b.c\"]\n" + + "[\"testcode\",1,17]\n" + + "[\"testcode\",1,20]\n"); + } + + public void testFunctionNameOutput3() throws Exception { + checkSourceMap("var q = function () {};", + + "/** Begin line maps. **/{ \"file\" : \"testcode\", " + + "\"count\": 1 }\n" + + + "[0,0,0,0,1,1,2,2,2,2,2,2,2,2,3,3,4,4]\n" + + + "/** Begin file information. **/\n" + + "[]\n" + + + "/** Begin mapping definitions. **/\n" + + "[\"testcode\",1,0]\n" + + "[\"testcode\",1,4,\"q\"]\n" + + "[\"testcode\",1,8,\"q\"]\n" + + "[\"testcode\",1,17]\n" + + "[\"testcode\",1,20]\n"); + } + + public void testFunctionNameOutput4() throws Exception { + checkSourceMap("({ 'q' : function () {} })", + + "/** Begin line maps. **/{ \"file\" : \"testcode\", " + + "\"count\": 1 }\n" + + + "[0,0,1,1,1,0,2,2,2,2,2,2,2,2,3,3,4,4,0,0]\n" + + + "/** Begin file information. **/\n" + + "[]\n" + + + "/** Begin mapping definitions. **/\n" + + "[\"testcode\",1,1]\n" + + "[\"testcode\",1,3]\n" + + "[\"testcode\",1,9,\"q\"]\n" + + "[\"testcode\",1,18]\n" + + "[\"testcode\",1,21]\n"); + } + + public void testGoldenOutput1() throws Exception { + detailLevel = SourceMap.DetailLevel.ALL; + + checkSourceMap("function f(foo, bar) { foo = foo + bar + 2; return foo; }", + + "/** Begin line maps. **/{ \"file\" : \"testcode\", " + + "\"count\": 1 }\n" + + + "[0,0,0,0,0,0,0,0,1,1,2,3,3,3,2,4,4,4,2,5,7,7,7,6,8,8,8,6," + + "9,9,9,6,10,11,11,11,11,11,11,11,12,12,12,12,5]\n" + + + "/** Begin file information. **/\n" + + "[]\n" + + + "/** Begin mapping definitions. **/\n" + + "[\"testcode\",1,0,\"f\"]\n" + + "[\"testcode\",1,9,\"f\"]\n" + + "[\"testcode\",1,10]\n" + + "[\"testcode\",1,11,\"foo\"]\n" + + "[\"testcode\",1,16,\"bar\"]\n" + + "[\"testcode\",1,21]\n" + + "[\"testcode\",1,23]\n" + + "[\"testcode\",1,23,\"foo\"]\n" + + "[\"testcode\",1,29,\"foo\"]\n" + + "[\"testcode\",1,35,\"bar\"]\n" + + "[\"testcode\",1,41]\n" + + "[\"testcode\",1,44]\n" + + "[\"testcode\",1,51,\"foo\"]\n"); + + + detailLevel = SourceMap.DetailLevel.SYMBOLS; + checkSourceMap("function f(foo, bar) { foo = foo + bar + 2; return foo; }", + + "/** Begin line maps. **/{ \"file\" : \"testcode\", " + + "\"count\": 1 }\n" + + + "[0,0,0,0,0,0,0,0,1,1,0,2,2,2,0,3,3,3,0,0,4,4,4,0,5,5,5,0," + + "6,6,6,0,0,0,0,0,0,0,0,0,7,7,7,7,0]\n" + + + "/** Begin file information. **/\n" + + "[]\n" + + + "/** Begin mapping definitions. **/\n" + + "[\"testcode\",1,0,\"f\"]\n" + + "[\"testcode\",1,9,\"f\"]\n" + + "[\"testcode\",1,11,\"foo\"]\n" + + "[\"testcode\",1,16,\"bar\"]\n" + + "[\"testcode\",1,23,\"foo\"]\n" + + "[\"testcode\",1,29,\"foo\"]\n" + + "[\"testcode\",1,35,\"bar\"]\n" + + "[\"testcode\",1,51,\"foo\"]\n"); + } + + public void testGoldenOutput2() throws Exception { + checkSourceMap("function f(foo, bar) {\r\n\n\n\nfoo = foo + bar + foo;" + + "\nreturn foo;\n}", + + "/** Begin line maps. **/{ \"file\" : \"testcode\", " + + "\"count\": 1 }\n" + + + "[0,0,0,0,0,0,0,0,1,1,2,3,3,3,2,4,4,4,2,5,7,7,7,6,8,8,8," + + "6,9,9,9,6,10,10,10,11,11,11,11,11,11,11,12,12,12," + + "12,5]\n" + + + "/** Begin file information. **/\n" + + "[]\n" + + "/** Begin mapping definitions. **/\n" + + "[\"testcode\",1,0,\"f\"]\n" + + "[\"testcode\",1,9,\"f\"]\n" + + "[\"testcode\",1,10]\n" + + "[\"testcode\",1,11,\"foo\"]\n" + + "[\"testcode\",1,16,\"bar\"]\n" + + "[\"testcode\",1,21]\n" + + "[\"testcode\",5,0]\n" + + "[\"testcode\",5,0,\"foo\"]\n" + + "[\"testcode\",5,6,\"foo\"]\n" + + "[\"testcode\",5,12,\"bar\"]\n" + + "[\"testcode\",5,18,\"foo\"]\n" + + "[\"testcode\",6,0]\n" + + "[\"testcode\",6,7,\"foo\"]\n"); + } + + public void testGoldenOutput3() throws Exception { + checkSourceMap("c:\\myfile.js", + "foo;", + + "/** Begin line maps. **/{ \"file\" : \"testcode\", " + + "\"count\": 1 }\n" + + + "[0,0,0]\n" + + + "/** Begin file information. **/\n" + + "[]\n" + + "/** Begin mapping definitions. **/\n" + + "[\"c:\\\\myfile.js\",1,0,\"foo\"]\n"); + } + + public void testGoldenOutput4() throws Exception { + checkSourceMap("c:\\myfile.js", + "foo; boo; goo;", + + "/** Begin line maps. **/" + + "{ \"file\" : \"testcode\", \"count\": 1 }\n" + + "[0,0,0,1,1,1,1,2,2,2,2]\n" + + + "/** Begin file information. **/\n" + + "[]\n" + + + "/** Begin mapping definitions. **/\n" + + "[\"c:\\\\myfile.js\",1,0,\"foo\"]\n" + + "[\"c:\\\\myfile.js\",1,7,\"boo\"]\n" + + "[\"c:\\\\myfile.js\",1,14,\"goo\"]\n"); + } + + public void testGoldenOutput5() throws Exception { + detailLevel = SourceMap.DetailLevel.ALL; + + checkSourceMap("c:\\myfile.js", + "/** @preserve\n" + + " * this is a test.\n" + + " */\n" + + "var foo=a + 'this is a really long line that will force the" + + " mapping to span multiple lines 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + "' + c + d + e;", + + "/** Begin line maps. **/" + + "{ \"file\" : \"testcode\", \"count\": 6 }\n" + + "[]\n" + + "[]\n" + + "[]\n" + + "[]\n" + + "[0,0,0,0,1,1,1,1,2,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3]\n" + + "[4,1,5,1,6]\n" + + "/** Begin file information. **/\n" + + "[]\n" + + "[]\n" + + "[]\n" + + "[]\n" + + "[]\n" + + "[]\n" + + "/** Begin mapping definitions. **/\n" + + "[\"c:\\\\myfile.js\",4,0]\n" + + "[\"c:\\\\myfile.js\",4,4,\"foo\"]\n" + + "[\"c:\\\\myfile.js\",4,8,\"a\"]\n" + + "[\"c:\\\\myfile.js\",4,12]\n" + + "[\"c:\\\\myfile.js\",4,1314,\"c\"]\n" + + "[\"c:\\\\myfile.js\",4,1318,\"d\"]\n" + + "[\"c:\\\\myfile.js\",4,1322,\"e\"]\n"); + + detailLevel = SourceMap.DetailLevel.SYMBOLS; + + checkSourceMap("c:\\myfile.js", + "/** @preserve\n" + + " * this is a test.\n" + + " */\n" + + "var foo=a + 'this is a really long line that will force the" + + " mapping to span multiple lines 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + "' + c + d + e;", + + "/** Begin line maps. **/" + + "{ \"file\" : \"testcode\", \"count\": 6 }\n" + + "[]\n" + + "[]\n" + + "[]\n" + + "[]\n" + + "[-1,-1,-1,-1,0,0,0,0,1]\n" + + "[2,0,3,0,4]\n" + + "/** Begin file information. **/\n" + + "[]\n" + + "[]\n" + + "[]\n" + + "[]\n" + + "[]\n" + + "[]\n" + + "/** Begin mapping definitions. **/\n" + + "[\"c:\\\\myfile.js\",4,4,\"foo\"]\n" + + "[\"c:\\\\myfile.js\",4,8,\"a\"]\n" + + "[\"c:\\\\myfile.js\",4,1314,\"c\"]\n" + + "[\"c:\\\\myfile.js\",4,1318,\"d\"]\n" + + "[\"c:\\\\myfile.js\",4,1322,\"e\"]\n"); + } + + public void testBasicDeterminism() throws Exception { + RunResult result1 = compile("file1", "foo;", "file2", "bar;"); + RunResult result2 = compile("file2", "foo;", "file1", "bar;"); + + String map1 = getSourceMap(result1); + String map2 = getSourceMap(result2); + + // Assert that the files section of the maps are the same. The actual + // entries will differ, so we cannot do a simple full comparison. + + // Line 5 has the file information. + String files1 = map1.split("\n")[4]; + String files2 = map2.split("\n")[4]; + + assertEquals(files1, files2); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapGeneratorV2Test.java b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapGeneratorV2Test.java new file mode 100644 index 0000000..0932b54 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapGeneratorV2Test.java @@ -0,0 +1,446 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import com.google.debugging.sourcemap.SourceMapGeneratorV2.LineMapEncoder; +import com.google.javascript.jscomp.SourceMap; +import com.google.javascript.jscomp.SourceMap.Format; + +import java.io.IOException; + +/** + * Tests for {@link SourceMap}. + * + */ +public class SourceMapGeneratorV2Test extends SourceMapTestCase { + + public SourceMapGeneratorV2Test() { + disableColumnValidation(); + } + + @Override + protected SourceMapConsumer getSourceMapConsumer() { + return new SourceMapConsumerV2(); + } + + @Override + protected Format getSourceMapFormat() { + return SourceMap.Format.V2; + } + + @Override + public void setUp() { + detailLevel = SourceMap.DetailLevel.ALL; + } + + public void testBasicMapping() throws Exception { + compileAndCheck("function __BASIC__() { }"); + } + + public void testBasicMappingGoldenOutput() throws Exception { + // Empty source map test + checkSourceMap("function __BASIC__() { }", + + //"/** Source Map **/\n" + + "{\n" + + "\"version\":2,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"lineMaps\":[\"cAkBEBEB\"],\n" + + "\"mappings\":[[0,1,0,0],\n" + + "[0,1,9,0],\n" + + "[0,1,18],\n" + + "[0,1,21],\n" + + "],\n" + + "\"sources\":[\"testcode\"],\n" + + "\"names\":[\"__BASIC__\"]\n" + + "}\n"); + } + + public void testLiteralMappings() throws Exception { + compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) { " + + "var __VAR__ = '__STR__'; }"); + } + + public void testLiteralMappingsGoldenOutput() throws Exception { + // Empty source map test + checkSourceMap("function __BASIC__(__PARAM1__, __PARAM2__) { " + + "var __VAR__ = '__STR__'; }", + + //"/** Source Map **/\n" + + "{\n" + + "\"version\":2,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"lineMaps\":[\"cAkBABkBA/kCA+ADMBcBgBA9\"],\n" + + "\"mappings\":[[0,1,0,0],\n" + + "[0,1,9,0],\n" + + "[0,1,18],\n" + + "[0,1,19,1],\n" + + "[0,1,31,2],\n" + + "[0,1,43],\n" + + "[0,1,45],\n" + + "[0,1,49,3],\n" + + "[0,1,59],\n" + + "],\n" + + "\"sources\":[\"testcode\"],\n" + + "\"names\":[" + + "\"__BASIC__\",\"__PARAM1__\",\"__PARAM2__\"," + + "\"__VAR__\"]\n" + + "}\n"); + } + + public void testMultilineMapping() throws Exception { + compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) {\n" + + "var __VAR__ = '__STR__';\n" + + "var __ANO__ = \"__STR2__\";\n" + + "}"); + } + + public void testMultiFunctionMapping() throws Exception { + compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) {\n" + + "var __VAR__ = '__STR__';\n" + + "var __ANO__ = \"__STR2__\";\n" + + "}\n\n" + + + "function __BASIC2__(__PARAM3__, __PARAM4__) {\n" + + "var __VAR2__ = '__STR2__';\n" + + "var __ANO2__ = \"__STR3__\";\n" + + "}\n\n"); + } + + public void testGoldenOutput0() throws Exception { + // Empty source map test + checkSourceMap("", + + "{\n" + + "\"version\":2,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"lineMaps\":[\"\"],\n" + + "\"mappings\":[],\n" + + "\"sources\":[\"testcode\"],\n" + + "\"names\":[]\n" + + "}\n"); + } + + public void testGoldenOutput1() throws Exception { + detailLevel = SourceMap.DetailLevel.ALL; + + checkSourceMap( + "function f(foo, bar) { foo = foo + bar + 2; return foo; }", + + "{\n" + + "\"version\":2,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"lineMaps\":[\"cAEBABIBA/ICA+ADICA/ICA+IDA9AEYBMBA5\"],\n" + + "\"mappings\":[[0,1,0,0],\n" + + "[0,1,9,0],\n" + + "[0,1,10],\n" + + "[0,1,11,1],\n" + + "[0,1,16,2],\n" + + "[0,1,21],\n" + + "[0,1,23],\n" + + "[0,1,23,1],\n" + + "[0,1,29,1],\n" + + "[0,1,35,2],\n" + + "[0,1,41],\n" + + "[0,1,44],\n" + + "[0,1,51,1],\n" + + "],\n" + + "\"sources\":[\"testcode\"],\n" + + "\"names\":[\"f\",\"foo\",\"bar\"]\n" + + "}\n"); + + detailLevel = SourceMap.DetailLevel.SYMBOLS; + + checkSourceMap("function f(foo, bar) { foo = foo + bar + 2; return foo; }", + + "{\n" + + "\"version\":2,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"lineMaps\":[\"cAEBA/ICA+IDE9IEA8IFA7IGg6MHA5\"],\n" + + "\"mappings\":[[0,1,0,0],\n" + + "[0,1,9,0],\n" + + "[0,1,11,1],\n" + + "[0,1,16,2],\n" + + "[0,1,23,1],\n" + + "[0,1,29,1],\n" + + "[0,1,35,2],\n" + + "[0,1,51,1],\n" + + "],\n" + + "\"sources\":[\"testcode\"],\n" + + "\"names\":[\"f\",\"foo\",\"bar\"]\n" + + "}\n"); + } + + public void testGoldenOutput2() throws Exception { + checkSourceMap("function f(foo, bar) {\r\n\n\n\nfoo = foo + bar + foo;" + + "\nreturn foo;\n}", + + "{\n" + + "\"version\":2,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"lineMaps\":[" + + "\"cAEBABIBA/ICA+ADICA/ICA+IDA9IEYBMBA5\"],\n" + + "\"mappings\":[[0,1,0,0],\n" + + "[0,1,9,0],\n" + + "[0,1,10],\n" + + "[0,1,11,1],\n" + + "[0,1,16,2],\n" + + "[0,1,21],\n" + + "[0,5,0],\n" + + "[0,5,0,1],\n" + + "[0,5,6,1],\n" + + "[0,5,12,2],\n" + + "[0,5,18,1],\n" + + "[0,6,0],\n" + + "[0,6,7,1],\n" + + "],\n" + + "\"sources\":[\"testcode\"],\n" + + "\"names\":[\"f\",\"foo\",\"bar\"]\n" + + "}\n"); + } + + public void testGoldenOutput3() throws Exception { + checkSourceMap("c:\\myfile.js", + "foo;", + + "{\n" + + "\"version\":2,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"lineMaps\":[\"IA\"],\n" + + "\"mappings\":[[0,1,0,0],\n" + + "],\n" + + "\"sources\":[\"c:\\\\myfile.js\"],\n" + + "\"names\":[\"foo\"]\n" + + "}\n"); + } + + public void testGoldenOutput4() throws Exception { + checkSourceMap("c:\\myfile.js", + "foo; boo; goo;", + + "{\n" + + "\"version\":2,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"lineMaps\":[\"IAMBMB\"],\n" + + "\"mappings\":[[0,1,0,0],\n" + + "[0,1,7,1],\n" + + "[0,1,14,2],\n" + + "],\n" + + "\"sources\":[\"c:\\\\myfile.js\"],\n" + + "\"names\":[\"foo\",\"boo\",\"goo\"]\n" + + "}\n"); + } + + public void testGoldenOutput5() throws Exception { + detailLevel = SourceMap.DetailLevel.ALL; + + checkSourceMap("c:\\myfile.js", + "/** @preserve\n" + + " * this is a test.\n" + + " */\n" + + "var foo=a + 'this is a really long line that will force the" + + " mapping to span multiple lines 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + "' + c + d + e;", + + "{\n" + + "\"version\":2,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":6,\n" + + "\"lineMaps\":[\"\",\n" + + "\"\",\n" + + "\"\",\n" + + "\"\",\n" + + "\"MAMBABA/!!AUSC\",\n" + + "\"AEA9AEA8AF\"],\n" + + "\"mappings\":[[0,4,0],\n" + + "[0,4,4,0],\n" + + "[0,4,8,1],\n" + + "[0,4,12],\n" + + "[0,4,1314,2],\n" + + "[0,4,1318,3],\n" + + "[0,4,1322,4],\n" + + "],\n" + + "\"sources\":[\"c:\\\\myfile.js\"],\n" + + "\"names\":[\"foo\",\"a\",\"c\",\"d\",\"e\"]\n" + + "}\n"); + + detailLevel = SourceMap.DetailLevel.SYMBOLS; + + checkSourceMap("c:\\myfile.js", + "/** @preserve\n" + + " * this is a test.\n" + + " */\n" + + "var foo=a + 'this is a really long line that will force the" + + " mapping to span multiple lines 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + "' + c + d + e;", + + "{\n" + + "\"version\":2,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":6,\n" + + "\"lineMaps\":[\"\",\n" + + "\"\",\n" + + "\"\",\n" + + "\"\",\n" + + "\"M/MBAB\",\n" + + "\"ACA+ADA9AE\"],\n" + + "\"mappings\":[[0,4,4,0],\n" + + "[0,4,8,1],\n" + + "[0,4,1314,2],\n" + + "[0,4,1318,3],\n" + + "[0,4,1322,4],\n" + + "],\n" + + "\"sources\":[\"c:\\\\myfile.js\"],\n" + + "\"names\":[\"foo\",\"a\",\"c\",\"d\",\"e\"]\n" + + "}\n"); + } + + public void testBasicDeterminism() throws Exception { + RunResult result1 = compile("file1", "foo;", "file2", "bar;"); + RunResult result2 = compile("file2", "foo;", "file1", "bar;"); + + String map1 = getSourceMap(result1); + String map2 = getSourceMap(result2); + + // Assert that the files section of the maps are the same. The actual + // entries will differ, so we cannot do a simple full comparison. + + // Line 5 has the file information. + String files1 = map1.split("\n")[4]; + String files2 = map2.split("\n")[4]; + + assertEquals(files1, files2); + } + + private int getRelativeId(int id, int lastId) { + int length = LineMapEncoder.getRelativeMappingIdLength(id, lastId); + int result = LineMapEncoder.getRelativeMappingId(id, length, lastId); + int inverse = SourceMapLineDecoder.getIdFromRelativeId( + result, length, lastId); + assertEquals(id, inverse); + return result; + } + + public void testEncodingRelativeId() { + assertEquals(0, getRelativeId(0, 0)); + assertEquals(64 + (-1), getRelativeId(-1, 0)); + assertEquals(64 + (-32), getRelativeId(0, 32)); + assertEquals(31, getRelativeId(31, 0)); + assertEquals(4096 + (-33), getRelativeId(0, 33)); + assertEquals(32, getRelativeId(32, 0)); + } + + public void testEncodingIdLength() { + assertEquals(1, LineMapEncoder.getRelativeMappingIdLength(0, 0)); + assertEquals(1, LineMapEncoder.getRelativeMappingIdLength(-1, 0)); + assertEquals(1, LineMapEncoder.getRelativeMappingIdLength(0, 32)); + assertEquals(1, LineMapEncoder.getRelativeMappingIdLength(31, 0)); + assertEquals(2, LineMapEncoder.getRelativeMappingIdLength(0, 33)); + assertEquals(2, LineMapEncoder.getRelativeMappingIdLength(32, 0)); + + assertEquals(2, LineMapEncoder.getRelativeMappingIdLength(2047, 0)); + assertEquals(3, LineMapEncoder.getRelativeMappingIdLength(2048, 0)); + assertEquals(2, LineMapEncoder.getRelativeMappingIdLength(0, 2048)); + assertEquals(3, LineMapEncoder.getRelativeMappingIdLength(0, 2049)); + } + + private String getEntry(int id, int lastId, int reps) throws IOException { + StringBuilder sb = new StringBuilder(); + LineMapEncoder.encodeEntry(sb, id, lastId, reps); + return sb.toString(); + } + + public void testEncoding() throws IOException { + assertEquals("AA", getEntry(0, 0, 1)); + assertEquals("EA", getEntry(0, 0, 2)); + assertEquals("8A", getEntry(0, 0, 16)); + assertEquals("!AQA", getEntry(0, 0, 17)); + assertEquals("!ARA", getEntry(0, 0, 18)); + assertEquals("!A+A", getEntry(0, 0, 63)); + assertEquals("!A/A", getEntry(0, 0, 64)); + assertEquals("!!ABAA", getEntry(0, 0, 65)); + assertEquals("!!A//A", getEntry(0, 0, 4096)); + assertEquals("!!!ABAAA", getEntry(0, 0, 4097)); + + assertEquals("Af", getEntry(31, 0, 1)); + assertEquals("BAg", getEntry(32, 0, 1)); + assertEquals("AB", getEntry(32, 31, 1)); + + assertEquals("!AQf", getEntry(31, 0, 17)); + assertEquals("!BQAg", getEntry(32, 0, 17)); + assertEquals("!AQB", getEntry(32, 31, 17)); + + assertEquals("!A/B", getEntry(32, 31, 64)); + assertEquals("!!ABAB", getEntry(32, 31, 65)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapGeneratorV3Test.java b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapGeneratorV3Test.java new file mode 100644 index 0000000..6e2f45e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapGeneratorV3Test.java @@ -0,0 +1,500 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.SourceMap; +import com.google.javascript.jscomp.SourceMap.Format; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.LinkedHashMap; +import java.util.List; + + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class SourceMapGeneratorV3Test extends SourceMapTestCase { + + public SourceMapGeneratorV3Test() { + } + + @Override + protected SourceMapConsumer getSourceMapConsumer() { + return new SourceMapConsumerV3(); + } + + @Override + protected Format getSourceMapFormat() { + return SourceMap.Format.V3; + } + + public void testBasicMapping1() throws Exception { + compileAndCheck("function __BASIC__() { }"); + } + + public void testBasicMappingGoldenOutput() throws Exception { + // Empty source map test + checkSourceMap("function __BASIC__() { }", + + "{\n" + + "\"version\":3,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"mappings\":\"AAAAA,QAASA,UAAS,EAAG;\",\n" + + "\"sources\":[\"testcode\"],\n" + + "\"names\":[\"__BASIC__\"]\n" + + "}\n"); + } + + public void testBasicMapping2() throws Exception { + compileAndCheck("function __BASIC__(__PARAM1__) {}"); + } + + public void testLiteralMappings() throws Exception { + compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) { " + + "var __VAR__ = '__STR__'; }"); + } + + public void testLiteralMappingsGoldenOutput() throws Exception { + // Empty source map test + checkSourceMap("function __BASIC__(__PARAM1__, __PARAM2__) { " + + "var __VAR__ = '__STR__'; }", + + "{\n" + + "\"version\":3,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"mappings\":\"AAAAA,QAASA,UAAS,CAACC,UAAD,CAAaC,UAAb," + + "CAAyB,CAAE,IAAIC,QAAU,SAAhB;\",\n" + + "\"sources\":[\"testcode\"],\n" + + "\"names\":[\"__BASIC__\",\"__PARAM1__\",\"__PARAM2__\"," + + "\"__VAR__\"]\n" + + "}\n"); + } + + public void testMultilineMapping() throws Exception { + compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) {\n" + + "var __VAR__ = '__STR__';\n" + + "var __ANO__ = \"__STR2__\";\n" + + "}"); + } + + public void testMultilineMapping2() throws Exception { + compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) {\n" + + "var __VAR__ = 1;\n" + + "var __ANO__ = 2;\n" + + "}"); + } + + public void testMultiFunctionMapping() throws Exception { + compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) {\n" + + "var __VAR__ = '__STR__';\n" + + "var __ANO__ = \"__STR2__\";\n" + + "}\n" + + + "function __BASIC2__(__PARAM3__, __PARAM4__) {\n" + + "var __VAR2__ = '__STR2__';\n" + + "var __ANO2__ = \"__STR3__\";\n" + + "}\n"); + } + + public void testGoldenOutput0() throws Exception { + // Empty source map test + checkSourceMap("", + + "{\n" + + "\"version\":3,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"mappings\":\";\",\n" + + "\"sources\":[],\n" + + "\"names\":[]\n" + + "}\n"); + } + + public void testGoldenOutput0a() throws Exception { + // Empty source map test + checkSourceMap("a;", + + "{\n" + + "\"version\":3,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"mappings\":\"AAAAA;\",\n" + + "\"sources\":[\"testcode\"],\n" + + "\"names\":[\"a\"]\n" + + "}\n"); + } + + public void testGoldenOutput1() throws Exception { + detailLevel = SourceMap.DetailLevel.ALL; + + checkSourceMap("function f(foo, bar) { foo = foo + bar + 2; return foo; }", + + "{\n" + + "\"version\":3,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"mappings\":\"AAAAA,QAASA,EAAC,CAACC,GAAD,CAAMC,GAAN," + + "CAAW,CAAED,GAAA,CAAMA,GAAN,CAAYC,GAAZ,CAAkB,CAAG," + + "OAAOD,IAA9B;\",\n" + + "\"sources\":[\"testcode\"],\n" + + "\"names\":[\"f\",\"foo\",\"bar\"]\n" + + "}\n"); + + detailLevel = SourceMap.DetailLevel.SYMBOLS; + + checkSourceMap("function f(foo, bar) { foo = foo + bar + 2; return foo; }", + + "{\n" + + "\"version\":3,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"mappings\":\"AAAAA,QAASA,EAATA,CAAWC,GAAXD,CAAgBE," + + "GAAhBF,EAAuBC,GAAvBD,CAA6BC,GAA7BD,CAAmCE,GAAnCF," + + "SAAmDC,IAAnDD;\",\n" + + "\"sources\":[\"testcode\"],\n" + + "\"names\":[\"f\",\"foo\",\"bar\"]\n" + + "}\n"); + } + + public void testGoldenOutput2() throws Exception { + checkSourceMap("function f(foo, bar) {\r\n\n\n\nfoo = foo + bar + foo;" + + "\nreturn foo;\n}", + + "{\n" + + "\"version\":3,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"mappings\":\"AAAAA,QAASA,EAAC,CAACC,GAAD,CAAMC,GAAN," + + "CAAW,CAIrBD,GAAA,CAAMA,GAAN,CAAYC,GAAZ,CAAkBD," + + "GAClB,OAAOA,IALc;\",\n" + + "\"sources\":[\"testcode\"],\n" + + "\"names\":[\"f\",\"foo\",\"bar\"]\n" + + "}\n"); + } + + public void testGoldenOutput3() throws Exception { + checkSourceMap("c:\\myfile.js", + "foo;", + + "{\n" + + "\"version\":3,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"mappings\":\"AAAAA;\",\n" + + "\"sources\":[\"c:\\\\myfile.js\"],\n" + + "\"names\":[\"foo\"]\n" + + "}\n"); + } + + public void testGoldenOutput4() throws Exception { + checkSourceMap("c:\\myfile.js", + "foo; boo; goo;", + + "{\n" + + "\"version\":3,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":1,\n" + + "\"mappings\":\"AAAAA,GAAOC,IAAOC;\",\n" + + "\"sources\":[\"c:\\\\myfile.js\"],\n" + + "\"names\":[\"foo\",\"boo\",\"goo\"]\n" + + "}\n"); + } + + public void testGoldenOutput5() throws Exception { + detailLevel = SourceMap.DetailLevel.ALL; + + checkSourceMap( + "c:\\myfile.js", + "/** @preserve\n" + + " * this is a test.\n" + + " */\n" + + "var foo=a + 'this is a really long line that will force the" + + " mapping to span multiple lines 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + "' + c + d + e;", + + "{\n" + + "\"version\":3,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":6,\n" + + "\"mappings\":\"A;;;;AAGA,IAAIA,IAAIC,CAAJD,CAAQ,mxCAARA;AAA8xCE," + + "CAA9xCF,CAAkyCG,CAAlyCH,CAAsyCI;\",\n" + + "\"sources\":[\"c:\\\\myfile.js\"],\n" + + "\"names\":[\"foo\",\"a\",\"c\",\"d\",\"e\"]\n" + + "}\n"); + + detailLevel = SourceMap.DetailLevel.SYMBOLS; + + checkSourceMap("c:\\myfile.js", + "/** @preserve\n" + + " * this is a test.\n" + + " */\n" + + "var foo=a + 'this is a really long line that will force the" + + " mapping to span multiple lines 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + " 123456789 123456789 123456789 123456789 123456789" + + "' + c + d + e;", + + "{\n" + + "\"version\":3,\n" + + "\"file\":\"testcode\",\n" + + "\"lineCount\":6,\n" + + "\"mappings\":\"A;;;;IAGIA,IAAIC,CAAJD;AAA8xCE,CAA9xCF,CAAkyCG," + + "CAAlyCH,CAAsyCI;\",\n" + + "\"sources\":[\"c:\\\\myfile.js\"],\n" + + "\"names\":[\"foo\",\"a\",\"c\",\"d\",\"e\"]\n" + + "}\n"); + } + + public void testBasicDeterminism() throws Exception { + RunResult result1 = compile("file1", "foo;", "file2", "bar;"); + RunResult result2 = compile("file2", "foo;", "file1", "bar;"); + + String map1 = getSourceMap(result1); + String map2 = getSourceMap(result2); + + // Assert that the files section of the maps are the same. The actual + // entries will differ, so we cannot do a simple full comparison. + + // Line 5 has the file information. + String files1 = map1.split("\n")[4]; + String files2 = map2.split("\n")[4]; + + assertEquals(files1, files2); + } + + public void testWriteMetaMap() throws IOException { + StringWriter out = new StringWriter(); + String name = "./app.js"; + List appSections = Lists.newArrayList( + SourceMapSection.forURL("src1", 0, 0), + SourceMapSection.forURL("src2", 100, 10), + SourceMapSection.forURL("src3", 150, 5)); + + SourceMapGeneratorV3 generator = new SourceMapGeneratorV3(); + generator.appendIndexMapTo(out, name, appSections); + + assertEquals( + "{\n" + + "\"version\":3,\n" + + "\"file\":\"./app.js\",\n" + + "\"sections\":[\n" + + "{\n" + + "\"offset\":{\n" + + "\"line\":0,\n" + + "\"column\":0\n" + + "},\n" + + "\"url\":\"src1\"\n" + + "},\n" + + "{\n" + + "\"offset\":{\n" + + "\"line\":100,\n" + + "\"column\":10\n" + + "},\n" + + "\"url\":\"src2\"\n" + + "},\n" + + "{\n" + + "\"offset\":{\n" + + "\"line\":150,\n" + + "\"column\":5\n" + + "},\n" + + "\"url\":\"src3\"\n" + + "}\n" + + "]\n" + + "}\n", + out.toString()); + } + + private String getEmptyMapFor(String name) throws IOException { + StringWriter out = new StringWriter(); + SourceMapGeneratorV3 generator = new SourceMapGeneratorV3(); + generator.appendTo(out, name); + return out.toString(); + } + + public void testWriteMetaMap2() throws IOException { + StringWriter out = new StringWriter(); + String name = "./app.js"; + List appSections = Lists.newArrayList( + // Map and URLs can be mixed. + SourceMapSection.forMap(getEmptyMapFor("./part.js"), 0, 0), + SourceMapSection.forURL("src2", 100, 10)); + + SourceMapGeneratorV3 generator = new SourceMapGeneratorV3(); + generator.appendIndexMapTo(out, name, appSections); + + assertEquals( + "{\n" + + "\"version\":3,\n" + + "\"file\":\"./app.js\",\n" + + "\"sections\":[\n" + + "{\n" + + "\"offset\":{\n" + + "\"line\":0,\n" + + "\"column\":0\n" + + "},\n" + + "\"map\":{\n" + + "\"version\":3,\n" + + "\"file\":\"./part.js\",\n" + + "\"lineCount\":1,\n" + + "\"mappings\":\";\",\n" + + "\"sources\":[],\n" + + "\"names\":[]\n" + + "}\n" + + "\n" + + "},\n" + + "{\n" + + "\"offset\":{\n" + + "\"line\":100,\n" + + "\"column\":10\n" + + "},\n" + + "\"url\":\"src2\"\n" + + "}\n" + + "]\n" + + "}\n", + out.toString()); + } + + public void testParseSourceMetaMap() throws Exception { + final String INPUT1 = "file1"; + final String INPUT2 = "file2"; + LinkedHashMap inputs = Maps.newLinkedHashMap(); + inputs.put(INPUT1, "var __FOO__ = 1;"); + inputs.put(INPUT2, "var __BAR__ = 2;"); + RunResult result1 = compile(inputs.get(INPUT1), INPUT1); + RunResult result2 = compile(inputs.get(INPUT2), INPUT2); + + final String MAP1 = "map1"; + final String MAP2 = "map2"; + final LinkedHashMap maps = Maps.newLinkedHashMap(); + maps.put(MAP1, result1.sourceMapFileContent); + maps.put(MAP2, result2.sourceMapFileContent); + + List sections = Lists.newArrayList(); + + StringBuilder output = new StringBuilder(); + FilePosition offset = appendAndCount(output, result1.generatedSource); + sections.add(SourceMapSection.forURL(MAP1, 0, 0)); + output.append(result2.generatedSource); + sections.add( + SourceMapSection.forURL(MAP2, offset.getLine(), offset.getColumn())); + + SourceMapGeneratorV3 generator = new SourceMapGeneratorV3(); + StringBuilder mapContents = new StringBuilder(); + generator.appendIndexMapTo(mapContents, "out.js", sections); + + check(inputs, output.toString(), mapContents.toString(), + new SourceMapSupplier() { + @Override + public String getSourceMap(String url){ + return maps.get(url); + }}); + } + + public void testSourceMapMerging() throws Exception { + final String INPUT1 = "file1"; + final String INPUT2 = "file2"; + LinkedHashMap inputs = Maps.newLinkedHashMap(); + inputs.put(INPUT1, "var __FOO__ = 1;"); + inputs.put(INPUT2, "var __BAR__ = 2;"); + RunResult result1 = compile(inputs.get(INPUT1), INPUT1); + RunResult result2 = compile(inputs.get(INPUT2), INPUT2); + + StringBuilder output = new StringBuilder(); + FilePosition offset = appendAndCount(output, result1.generatedSource); + output.append(result2.generatedSource); + + SourceMapGeneratorV3 generator = new SourceMapGeneratorV3(); + + generator.mergeMapSection(0, 0, result1.sourceMapFileContent); + generator.mergeMapSection(offset.getLine(), offset.getColumn(), + result2.sourceMapFileContent); + + StringBuilder mapContents = new StringBuilder(); + generator.appendTo(mapContents, "out.js"); + + check(inputs, output.toString(), mapContents.toString()); + } + + FilePosition count(String js) { + int line = 0, column = 0; + for (int i = 0; i < js.length(); i++) { + if (js.charAt(i) == '\n') { + line++; + column = 0; + } else { + column++; + } + } + return new FilePosition(line, column); + } + + FilePosition appendAndCount(Appendable out, String js) throws IOException { + out.append(js); + return count(js); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapTestCase.java b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapTestCase.java new file mode 100644 index 0000000..8b0095e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/debugging/sourcemap/SourceMapTestCase.java @@ -0,0 +1,320 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.debugging.sourcemap; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Maps; +import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; +import com.google.javascript.jscomp.Compiler; +import com.google.javascript.jscomp.CompilerOptions; +import com.google.javascript.jscomp.Result; +import com.google.javascript.jscomp.SourceFile; +import com.google.javascript.jscomp.SourceMap; +import com.google.javascript.jscomp.SourceMap.DetailLevel; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public abstract class SourceMapTestCase extends TestCase { + + private boolean validateColumns = true; + + public SourceMapTestCase() { + } + + void disableColumnValidation() { + validateColumns = false; + } + + + static final List EXTERNS = ImmutableList.of( + SourceFile.fromCode("externs", "")); + + protected DetailLevel detailLevel = SourceMap.DetailLevel.ALL; + + protected static class RunResult { + String generatedSource; + SourceMap sourceMap; + public String sourceMapFileContent; + } + + protected static class Token { + final String tokenName; + final String inputName; + final FilePosition position; + Token(String tokenName, String inputName, FilePosition position) { + this.tokenName = tokenName; + this.inputName = inputName; + this.position = position; + } + } + + @Override + public void setUp() { + detailLevel = SourceMap.DetailLevel.ALL; + } + + /** + * Creates a source map for the given JS code and asserts it is + * equal to the expected golden map. + */ + protected void checkSourceMap(String js, String expectedMap) + throws IOException { + checkSourceMap("testcode", js, expectedMap); + } + + protected String getSourceMap(RunResult result) throws IOException { + StringBuilder sb = new StringBuilder(); + result.sourceMap.appendTo(sb, "testcode"); + return sb.toString(); + } + + protected void checkSourceMap(String fileName, String js, String expectedMap) + throws IOException { + RunResult result = compile(js, fileName); + assertEquals(expectedMap, result.sourceMapFileContent); + assertEquals(result.sourceMapFileContent, getSourceMap(result)); + } + + /** + * Finds the all the __XX__ tokens in the given JavaScript + * string. + */ + private Map findTokens(Map inputs) { + Map tokens = Maps.newLinkedHashMap(); + + for (Entry entry : inputs.entrySet()) { + findTokens(tokens, entry.getKey(), entry.getValue()); + } + + return tokens; + } + + /** + * Finds the all the __XX__ tokens in the given JavaScript + * string. + */ + private Map findTokens(String src) { + Map tokens = Maps.newLinkedHashMap(); + + findTokens(tokens, "", src); + + return tokens; + } + + /** + * Finds the all the __XX__ tokens in the given JavaScript + * string. + */ + private Map findTokens( + Map tokens, String inputName, String js) { + + int currentLine = 0; + int positionOffset = 0; + + for (int i = 0; i < js.length(); ++i) { + char current = js.charAt(i); + + if (current == '\n') { + positionOffset = i + 1; + currentLine++; + continue; + } + + if (current == '_' && (i < js.length() - 5)) { + // Check for the _ token. + if (js.charAt(i + 1) != '_') { + continue; + } + + // Loop until we have another _ token. + String tokenName = ""; + + int j = i + 2; + for (; j < js.length(); ++j) { + if (js.charAt(j) == '_') { + break; + } + + tokenName += js.charAt(j); + } + + if (tokenName.length() > 0) { + int currentPosition = i - positionOffset; + Token token = new Token( + tokenName, inputName, + new FilePosition(currentLine, currentPosition)); + tokens.put(tokenName, token); + } + + i = j; + } + } + + return tokens; + } + + abstract protected SourceMap.Format getSourceMapFormat(); + + abstract protected SourceMapConsumer getSourceMapConsumer(); + + protected void compileAndCheck(String js) { + String inputName = "testcode"; + RunResult result = compile(js, inputName); + check(inputName, js, result.generatedSource, result.sourceMapFileContent); + } + + protected void check( + String inputName, String input, String output, + String sourceMapFileContent) { + Map inputMap = new LinkedHashMap(); + inputMap.put(inputName, input); + check(inputMap, output, sourceMapFileContent); + } + + protected void check( + Map originalInputs, String generatedSource, + String sourceMapFileContent) { + check(originalInputs, generatedSource, sourceMapFileContent, null); + } + + protected void check( + Map originalInputs, String generatedSource, + String sourceMapFileContent, SourceMapSupplier supplier) { + // Find all instances of the __XXX__ pattern in the original + // source code. + Map originalTokens = findTokens(originalInputs); + + // Find all instances of the __XXX__ pattern in the generated + // source code. + Map resultTokens = findTokens(generatedSource); + + // Ensure that the generated instances match via the source map + // to the original source code. + + // Ensure the token counts match. + assertEquals(originalTokens.size(), resultTokens.size()); + + SourceMapping reader; + try { + reader = SourceMapConsumerFactory.parse(sourceMapFileContent, supplier); + } catch (SourceMapParseException e) { + throw new RuntimeException("unexpected exception", e); + } + + // Map the tokens from the generated source back to the + // input source and ensure that the map is correct. + for (Token token : resultTokens.values()) { + OriginalMapping mapping = reader.getMappingForLine( + token.position.getLine() + 1, + token.position.getColumn() + 1); + + assertNotNull(mapping); + + // Find the associated token in the input source. + Token inputToken = originalTokens.get(token.tokenName); + assertNotNull(inputToken); + assertEquals(mapping.getOriginalFile(), inputToken.inputName); + + // Ensure that the map correctly points to the token (we add 1 + // to normalize versus the Rhino line number indexing scheme). + assertEquals(mapping.getLineNumber(), + inputToken.position.getLine() + 1); + + int start = inputToken.position.getColumn() + 1; + if (inputToken.tokenName.startsWith("STR")) { + // include the preceding quote. + start--; + } + + if (validateColumns) { + assertEquals(start, mapping.getColumnPosition()); + } + + // Ensure that if the token name does not being with an 'STR' (meaning a + // string) it has an original name. + if (!inputToken.tokenName.startsWith("STR")) { + assertTrue("missing name for " + inputToken.tokenName, + !mapping.getIdentifier().isEmpty()); + } + + // Ensure that if the mapping has a name, it matches the token. + if (!mapping.getIdentifier().isEmpty()) { + assertEquals(mapping.getIdentifier(), + "__" + inputToken.tokenName + "__"); + } + } + } + + protected RunResult compile(String js, String fileName) { + return compile(js, fileName, null, null); + } + + protected CompilerOptions getCompilerOptions() { + CompilerOptions options = new CompilerOptions(); + options.sourceMapOutputPath = "testcode_source_map.out"; + options.sourceMapFormat = getSourceMapFormat(); + options.sourceMapDetailLevel = detailLevel; + return options; + } + + protected RunResult compile( + String js1, String fileName1, String js2, String fileName2) { + Compiler compiler = new Compiler(); + CompilerOptions options = getCompilerOptions(); + + // Turn on IDE mode to get rid of optimizations. + options.ideMode = true; + + List inputs = + ImmutableList.of(SourceFile.fromCode(fileName1, js1)); + + if (js2 != null && fileName2 != null) { + inputs = ImmutableList.of( + SourceFile.fromCode(fileName1, js1), + SourceFile.fromCode(fileName2, js2)); + } + + Result result = compiler.compile(EXTERNS, inputs, options); + + assertTrue("compilation failed", result.success); + String source = compiler.toSource(); + + StringBuilder sb = new StringBuilder(); + try { + result.sourceMap.validate(true); + result.sourceMap.appendTo(sb, "testcode"); + } catch (IOException e) { + throw new RuntimeException("unexpected exception", e); + } + + RunResult rr = new RunResult(); + rr.generatedSource = source; + rr.sourceMap = result.sourceMap; + rr.sourceMapFileContent = sb.toString(); + return rr; + } + +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AliasExternalsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AliasExternalsTest.java new file mode 100644 index 0000000..de72371 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AliasExternalsTest.java @@ -0,0 +1,396 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Tests for {@link AliasExternals}. + * + */ +public class AliasExternalsTest extends CompilerTestCase { + + private static String EXTERNS = + // Globals + "/** @const */ var window;" + + "/** @const */ var document;" + + "var arguments;var _USER_ID;var ActiveXObject;" + + "function eval(x) {}" + + // Properties + "window.setTimeout;" + + "window.eval;" + + "props.window;props.innerHTML;props.length;props.prototype;props.length;" + + // More globals + "/** @noalias */ var RangeObject; " + + "var /** @noalias */ RuntimeObject, SelectionObject;" + + "/** @noalias */ function NoAliasFunction() {};"; + + // Blacklist and whitelist of globals. Assign to these before running test + // if you want to factor them in to the test, otherwise they will be null. + private String unaliasableGlobals; + private String aliasableGlobals; + + public AliasExternalsTest() { + super(EXTERNS); + } + + @Override + protected int getNumRepetitions() { + // This pass only runs once. + return 1; + } + + @Override + public void setUp() { + super.enableLineNumberCheck(false); + super.enableNormalize(); + unaliasableGlobals = null; + aliasableGlobals = null; + } + + /** + * Test standard global aliasing. + */ + public void testGlobalAlias() { + test("window.setTimeout(function() {}, 0);" + + "var doc=window.document;" + + "window.alert(\"foo\");" + + "window.eval(\"1\");" + + "window.location.href=\"http://www.example.com\";" + + "function foo() {var window = \"bar\"; return window}foo();", + + "var GLOBAL_window=window;" + + formatPropNameDecl("setTimeout") + + "GLOBAL_window[$$PROP_setTimeout](function() {}, 0);" + + "var doc=GLOBAL_window.document;" + + "GLOBAL_window.alert(\"foo\");" + + "GLOBAL_window.eval(\"1\");" + + "GLOBAL_window.location.href=\"http://www.example.com\";" + + "function foo() {var window = \"bar\"; return window}foo();"); + } + + /** + * Some globals should not be aliased because they have special meaning + * within the language (like arguments). + */ + public void testUnaliasable() { + test("function foo() {" + + "var x=arguments.length;" + + "var y=arguments.length;" + + "var z=arguments.length;" + + "var w=arguments.length;" + + "return x + y + z + w" + + "};foo();", + + formatPropNameDecl("length") + + "function foo() {" + + "var x=arguments[$$PROP_length];" + + "var y=arguments[$$PROP_length];" + + "var z=arguments[$$PROP_length];" + + "var w=arguments[$$PROP_length];" + + "return x + y + z + w" + + "};foo();"); + + test("var x=new ActiveXObject();" + + "x.foo=\"bar\";" + + "var y=new ActiveXObject();" + + "y.foo=\"bar\";" + + "var z=new ActiveXObject();" + + "z.foo=\"bar\";", + + "var x=new ActiveXObject();" + + "x.foo=\"bar\";" + + "var y=new ActiveXObject();" + + "y.foo=\"bar\";" + + "var z=new ActiveXObject();" + + "z.foo=\"bar\";"); + + test("var _a=eval('foo'),_b=eval('foo'),_c=eval('foo'),_d=eval('foo')," + + "_e=eval('foo'),_f=eval('foo'),_g=eval('foo');", + "var _a=eval('foo'),_b=eval('foo'),_c=eval('foo'),_d=eval('foo')," + + "_e=eval('foo'),_f=eval('foo'),_g=eval('foo');"); + } + + /** + * Test using a whitelist to explicitly alias only specific + * identifiers. + */ + public void testAliasableGlobals() { + aliasableGlobals = "notused,length"; + test("function foo() {" + + "var x=arguments.length;" + + "var y=arguments.length;" + + "var z=arguments.length;" + + "var w=arguments.length;" + + "return x + y + z + w" + + "};foo();", + + formatPropNameDecl("length") + + "function foo() {" + + "var x=arguments[$$PROP_length];" + + "var y=arguments[$$PROP_length];" + + "var z=arguments[$$PROP_length];" + + "var w=arguments[$$PROP_length];" + + "return x + y + z + w" + + "};foo();"); + + aliasableGlobals = "notused,notlength"; + test("function foo() {" + + "var x=arguments.length;" + + "var y=arguments.length;" + + "var z=arguments.length;" + + "var w=arguments.length;" + + "return x + y + z + w" + + "};foo();", + + "function foo() {" + + "var x=arguments.length;" + + "var y=arguments.length;" + + "var z=arguments.length;" + + "var w=arguments.length;" + + "return x + y + z + w" + + "};foo();"); + } + + /** + * Test combined usage of aliasable and unaliasable global lists. + */ + public void testAliasableAndUnaliasableGlobals() { + // Only aliasable provided - OK + aliasableGlobals = "foo,bar"; + unaliasableGlobals = ""; + test("var x;", "var x;"); + + // Only unaliasable provided - OK + aliasableGlobals = ""; + unaliasableGlobals = "baz,qux"; + test("var x;", "var x;"); + + // Both provided - bad + aliasableGlobals = "foo,bar"; + unaliasableGlobals = "baz,qux"; + try { + test("var x;", "var x;"); + fail("Expected an IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // pass + } + } + + /** + * Global variables that get re-assigned should not be aliased. + */ + public void testGlobalAssigment() { + test("var x=_USER_ID+window;" + + "var y=_USER_ID+window;" + + "var z=_USER_ID+window;" + + "var w=x+y+z;" + + "_USER_ID = \"foo\";" + + "window++;", + + "var x=_USER_ID+window;" + + "var y=_USER_ID+window;" + + "var z=_USER_ID+window;" + + "var w=x+y+z;" + + "_USER_ID = \"foo\";" + + "window++"); + } + + public void testNewOperator() { + test("var x;new x(window);window;window;window;window;window", + + "var GLOBAL_window=window; var x;" + + " new x(GLOBAL_window);GLOBAL_window;GLOBAL_window;" + + " GLOBAL_window;GLOBAL_window;GLOBAL_window"); + } + + /** + * Test the standard replacement for GETPROP + */ + public void testGetProp() { + test("function foo(a,b){return a.length > b.length;}", + formatPropNameDecl("length") + + "function foo(a, b){return a[$$PROP_length] > b[$$PROP_length];}"); + test("Foo.prototype.bar = function() { return 'foo'; }", + formatPropNameDecl("prototype") + + "Foo[$$PROP_prototype].bar = function() { return 'foo'; }"); + test("Foo.notreplaced = 5", "Foo.notreplaced=5"); + } + + /** + * Ops that should be ignored + */ + public void testIgnoredOps() { + testSame("function foo() { this.length-- }"); + testSame("function foo() { this.length++ }"); + testSame("function foo() { this.length+=5 }"); + testSame("function foo() { this.length-=5 }"); + } + + /** + * Test property setting + */ + public void testSetProp() { + test("function foo() { this.innerHTML = 'hello!'; }", + formatSetPropFn("innerHTML") + + "function foo() { SETPROP_innerHTML(this, 'hello!'); }"); + } + + /** + * Test for modifying both parent and child, as all replacements + * are on a single pass and modifying both involves being careful with + * references. + */ + public void testParentChild() { + test("a.length = b.length = c.length;", formatSetPropFn("length") + + formatPropNameDecl("length") + + "SETPROP_length(a, SETPROP_length(b, c[$$PROP_length]))"); + } + + private static final String MODULE_SRC_ONE = + "a=b.length;a=b.length;a=b.length;"; + private static final String MODULE_SRC_TWO = "c=d.length;"; + + /** + * Test that the code is placed in the first module when there are no + * dependencies. + */ + public void testModulesWithoutDependencies() { + test(createModules(MODULE_SRC_ONE, MODULE_SRC_TWO), + new String[] { + "var $$PROP_length=\"length\";a=b[$$PROP_length];" + + "a=b[$$PROP_length];a=b[$$PROP_length];", + "c=d[$$PROP_length];"}); + } + + /** + * Test that the code is placed in the first module when the second module + * depends on the first. + */ + public void testModulesWithDependencies() { + test(createModuleChain(MODULE_SRC_ONE, MODULE_SRC_TWO), + new String[] { + "var $$PROP_length=\"length\";a=b[$$PROP_length];" + + "a=b[$$PROP_length];a=b[$$PROP_length];", + "c=d[$$PROP_length];"}); + } + + public void testPropAccessorPushedDeeper1() { + test(createModuleChain("var a = \"foo\";", "var b = a.length;"), + new String[] { + "var a = \"foo\";", + formatPropNameDecl("length") + "var b = a[$$PROP_length]" }); + } + + public void testPropAccessorPushedDeeper2() { + test(createModuleChain( + "var a = \"foo\";", "var b = a.length;", "var c = a.length;"), + new String[] { + "var a = \"foo\";", + formatPropNameDecl("length") + "var b = a[$$PROP_length]", + "var c = a[$$PROP_length]" }); + } + + public void testPropAccessorPushedDeeper3() { + test(createModuleStar( + "var a = \"foo\";", "var b = a.length;", "var c = a.length;"), + new String[] { + formatPropNameDecl("length") + "var a = \"foo\";", + "var b = a[$$PROP_length]", + "var c = a[$$PROP_length]" }); + } + + public void testPropAccessorNotPushedDeeper() { + test(createModuleChain("var a = \"foo\"; var b = a.length;", + "var c = a.length;"), + new String[] { + formatPropNameDecl("length") + + "var a = \"foo\"; var b = a[$$PROP_length]", + "var c = a[$$PROP_length]" }); + } + + public void testPropMutatorPushedDeeper() { + test(createModuleChain("var a = [1];", "a.length = 0;"), + new String[] { + "var a = [1];", + formatSetPropFn("length") + "SETPROP_length(a, 0);" }); + } + + public void testPropMutatorNotPushedDeeper() { + test(createModuleChain( + "var a = [1]; a.length = 1;", "a.length = 0;"), + new String[] { + formatSetPropFn("length") + "var a = [1]; SETPROP_length(a, 1);", + "SETPROP_length(a, 0);" }); + } + + public void testGlobalAliasPushedDeeper() { + test(createModuleChain( + "var a = 1;", + "var b = window, c = window, d = window, e = window;"), + new String[] { "var a = 1;", + "var GLOBAL_window = window;" + + "var b = GLOBAL_window, c = GLOBAL_window, " + + " d = GLOBAL_window, e = GLOBAL_window;" }); + } + + public void testGlobalAliasNotPushedDeeper() { + test(createModuleChain( + "var a = 1, b = window;", + "var c = window, d = window, e = window;"), + new String[] { "var GLOBAL_window = window;" + + "var a = 1, b = GLOBAL_window;", + "var c = GLOBAL_window, " + + " d = GLOBAL_window, e = GLOBAL_window;" }); + } + + public void testNoAliasAnnotationForSingleVar() { + testSame("[RangeObject, RangeObject, RangeObject]"); + } + + public void testNoAliasAnnotationForMultiVarDeclaration() { + test("[RuntimeObject, RuntimeObject, RuntimeObject," + + " SelectionObject, SelectionObject, SelectionObject]", + "var GLOBAL_SelectionObject = SelectionObject;" + + "[RuntimeObject, RuntimeObject, RuntimeObject," + + " GLOBAL_SelectionObject, GLOBAL_SelectionObject," + + " GLOBAL_SelectionObject]"); + } + + public void testNoAliasAnnotationForFunction() { + testSame("[NoAliasFunction(), NoAliasFunction(), NoAliasFunction()]"); + } + + private String formatPropNameDecl(String prop) { + return "var $$PROP_" + prop + "='" + prop + "';"; + } + + private String formatSetPropFn(String prop) { + String mutatorName = "SETPROP_" + prop; + String arg1 = mutatorName + "$a"; + String arg2 = mutatorName + "$b"; + return "function " + mutatorName + "(" + arg1 + "," + arg2 + ") {" + + "return " + arg1 + "." + prop + "=" + arg2 + ";}"; + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + AliasExternals ae = new AliasExternals( + compiler, compiler.getModuleGraph(), + unaliasableGlobals, aliasableGlobals); + ae.setRequiredUsage(1); + return ae; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AliasKeywordsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AliasKeywordsTest.java new file mode 100644 index 0000000..e5725eb --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AliasKeywordsTest.java @@ -0,0 +1,187 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Tests for {@link AliasKeywords}. + * + */ +public class AliasKeywordsTest extends CompilerTestCase { + private static final int ENOUGH_TO_ALIAS_LITERAL + = AliasKeywords.MIN_OCCURRENCES_REQUIRED_TO_ALIAS_LITERAL; + private static final int TOO_FEW_TO_ALIAS_LITERAL + = ENOUGH_TO_ALIAS_LITERAL - 1; + + private static final int ENOUGH_TO_ALIAS_THROW + = AliasKeywords.MIN_OCCURRENCES_REQUIRED_TO_ALIAS_THROW; + private static final int TOO_FEW_TO_ALIAS_THROW + = ENOUGH_TO_ALIAS_THROW - 1; + + @Override + public void setUp() { + super.enableLineNumberCheck(false); + super.enableNormalize(); + } + + @Override + public CompilerPass getProcessor(Compiler compiler) { + return new AliasKeywords(compiler); + } + + @Override + protected int getNumRepetitions() { + return 1; + } + + /** + * Generate code of the form 'if ();' repeated numReps + * times, with prepend prepended. + * + * For example, generateCode("true", 2, "var a=b;") generates + * var a=b;if (true);if (true); + */ + private static String generateCode( + String keyword, int numReps, String prepend) { + StringBuilder sb = new StringBuilder(prepend); + for (int i = 0; i < numReps; i++) { + sb.append("if ("); + sb.append(keyword); + sb.append(");"); + } + return sb.toString(); + } + + private static String generateCode(String keyword, int numReps) { + return generateCode(keyword, numReps, ""); + } + + private static String generatePreProcessThrowCode(int repititions, + String whatToThrow) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < repititions; i++) { + sb.append("throw "); + sb.append(whatToThrow); + sb.append(";"); + } + return sb.toString(); + } + + private static String generatePostProcessThrowCode( + int repetitions, String code, String whatToThrow) { + StringBuilder sb = new StringBuilder(); + sb.append("function "); + sb.append(AliasKeywords.ALIAS_THROW); + sb.append("(jscomp_throw_param){throw jscomp_throw_param;}"); + sb.append(code); + for (int i = 0; i < repetitions; i++) { + sb.append(AliasKeywords.ALIAS_THROW); + sb.append("("); + sb.append(whatToThrow); + sb.append(");"); + } + return sb.toString(); + } + + /** + * Don't generate aliases if the keyword is not referenced enough. + */ + public void testDontAlias() { + testSame(generateCode("true", TOO_FEW_TO_ALIAS_LITERAL)); + testSame(generateCode("false", TOO_FEW_TO_ALIAS_LITERAL)); + testSame(generateCode("null", TOO_FEW_TO_ALIAS_LITERAL)); + testSame(generateCode("void 0", TOO_FEW_TO_ALIAS_LITERAL)); + testSame(generatePreProcessThrowCode(TOO_FEW_TO_ALIAS_THROW, "1")); + + // Don't alias void nodes other than "void 0". + testSame(generateCode("void 1", ENOUGH_TO_ALIAS_LITERAL)); + testSame(generateCode("void x", ENOUGH_TO_ALIAS_LITERAL)); + testSame(generateCode("void f()", ENOUGH_TO_ALIAS_LITERAL)); + } + + /** + * Generate aliases if the keyword is referenced >= ENOUGH_TO_ALIAS + * times. + */ + public void testAlias() { + test(generateCode("true", ENOUGH_TO_ALIAS_LITERAL), + generateCode(AliasKeywords.ALIAS_TRUE, ENOUGH_TO_ALIAS_LITERAL, + "var JSCompiler_alias_TRUE=true;")); + + test(generateCode("false", ENOUGH_TO_ALIAS_LITERAL), + generateCode(AliasKeywords.ALIAS_FALSE, ENOUGH_TO_ALIAS_LITERAL, + "var JSCompiler_alias_FALSE=false;")); + + test(generateCode("null", ENOUGH_TO_ALIAS_LITERAL), + generateCode(AliasKeywords.ALIAS_NULL, ENOUGH_TO_ALIAS_LITERAL, + "var JSCompiler_alias_NULL=null;")); + + test(generateCode("void 0", ENOUGH_TO_ALIAS_LITERAL), + generateCode(AliasKeywords.ALIAS_VOID, ENOUGH_TO_ALIAS_LITERAL, + "var JSCompiler_alias_VOID=void 0;")); + + test(generatePreProcessThrowCode(ENOUGH_TO_ALIAS_THROW, "1"), + generatePostProcessThrowCode(ENOUGH_TO_ALIAS_THROW, "", "1")); + } + + public void testAliasTrueFalseNull() { + StringBuilder actual = new StringBuilder(); + actual.append(generateCode("true", ENOUGH_TO_ALIAS_LITERAL)); + actual.append(generateCode("false", ENOUGH_TO_ALIAS_LITERAL)); + actual.append(generateCode("null", ENOUGH_TO_ALIAS_LITERAL)); + actual.append(generateCode("void 0", ENOUGH_TO_ALIAS_LITERAL)); + + StringBuilder expected = new StringBuilder(); + expected.append( + "var JSCompiler_alias_VOID=void 0;" + + "var JSCompiler_alias_TRUE=true;" + + "var JSCompiler_alias_NULL=null;" + + "var JSCompiler_alias_FALSE=false;"); + expected.append( + generateCode(AliasKeywords.ALIAS_TRUE, ENOUGH_TO_ALIAS_LITERAL)); + expected.append( + generateCode(AliasKeywords.ALIAS_FALSE, ENOUGH_TO_ALIAS_LITERAL)); + expected.append( + generateCode(AliasKeywords.ALIAS_NULL, ENOUGH_TO_ALIAS_LITERAL)); + expected.append( + generateCode(AliasKeywords.ALIAS_VOID, ENOUGH_TO_ALIAS_LITERAL)); + + test(actual.toString(), expected.toString()); + } + + public void testAliasThrowKeywordLiteral() { + int repitions = Math.max(ENOUGH_TO_ALIAS_THROW, ENOUGH_TO_ALIAS_LITERAL); + String afterCode = generatePostProcessThrowCode( + repitions, "var JSCompiler_alias_TRUE=true;", + AliasKeywords.ALIAS_TRUE); + test(generatePreProcessThrowCode(repitions, "true"), afterCode); + } + + public void testExistingAliasDefinitionFails() { + try { + testSame("var JSCompiler_alias_TRUE='foo';"); + fail(); + } catch (RuntimeException expected) { + // expected exception + assertTrue(-1 != expected.getMessage().indexOf( + "Existing alias definition")); + } + } + + public void testWithNoInputs() { + testSame(new String[] {}); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AliasStringsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AliasStringsTest.java new file mode 100644 index 0000000..3499d97 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AliasStringsTest.java @@ -0,0 +1,355 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableSet; + +import java.util.*; + +/** + * Tests for {@link AliasStrings}. + * + */ +public class AliasStringsTest extends CompilerTestCase { + + private static final String EXTERNS = "alert"; + private static final Set ALL_STRINGS = null; + + private Set strings = ALL_STRINGS; + private JSModuleGraph moduleGraph = null; + private boolean hashReduction = false; + + public AliasStringsTest() { + super(EXTERNS); + } + + @Override + public void setUp() { + super.enableLineNumberCheck(false); + } + + @Override + public CompilerPass getProcessor(Compiler compiler) { + AliasStrings pass = + new AliasStrings(compiler, moduleGraph, strings, "(?i)secret", false); + if (hashReduction) + pass.unitTestHashReductionMask = 0; + return pass; + } + + public void testAssignment() { + strings = ImmutableSet.of("none", "width", "overimaginative"); + + // Strings not in alias list + testSame("var foo='foo'"); + testSame("a='titanium',b='titanium',c='titanium',d='titanium'"); + + // Not worth aliasing: + testSame("myStr='width'"); + testSame("Bar.prototype.start='none'"); + // Worth aliasing: + test("a='overimaginative';b='overimaginative'", + "var $$S_overimaginative='overimaginative';" + + "a=$$S_overimaginative;b=$$S_overimaginative"); + + testSame("var width=1234"); + testSame("width=1234;width=10000;width=9900;width=17;"); + } + + public void testSeveral() { + strings = ImmutableSet.of("", "px", "none", "width"); + + test("function f() {" + + "var styles=['width',100,'px','display','none'].join('')}", + "var $$S_='';" + + "var $$S_none='none';" + + "var $$S_px='px';" + + "var $$S_width='width';" + + "function f() {var styles=[$$S_width,100,$$S_px,'display'," + + "$$S_none].join($$S_)}"); + } + + public void testSortedOutput() { + strings = ImmutableSet.of("aba", "aaa", "aca", "bca", "bba"); + test("function f() {return ['aba', 'aaa', 'aca', 'bca', 'bba']}", + "var $$S_aaa='aaa';" + + "var $$S_aba='aba';" + + "var $$S_aca='aca';" + + "var $$S_bba='bba';" + + "var $$S_bca='bca';" + + "function f() {" + + " return [$$S_aba, $$S_aaa, $$S_aca, $$S_bca, $$S_bba]}"); + } + + public void testObjectLiterals() { + strings = ImmutableSet.of("px", "!@#$%^&*()"); + + test("var foo={px:435}", "var foo={px:435}"); + + // string as key + test("var foo={'px':435}", "var foo={'px':435}"); + test("bar=function f(){return {'px':435}}", + "bar=function f(){return {'px':435}}"); + + test("function f() {var foo={bar:'!@#$%^&*()'}}", + "var $$S_$21$40$23$24$25$5e$26$2a$28$29='!@#$%^&*()';" + + "function f() {var foo={bar:$$S_$21$40$23$24$25$5e$26$2a$28$29}}"); + + test("function f() {var foo={px:435,foo:'px',bar:'baz'}}", + "var $$S_px='px';" + + "function f() {var foo={px:435,foo:$$S_px,bar:'baz'}}"); + } + + public void testGetProp() { + strings = ImmutableSet.of("px", "width"); + + testSame("function f(){element.style.px=1234}"); + + test("function f(){shape.width.units='px'}", + "var $$S_px='px';function f(){shape.width.units=$$S_px}"); + + test("function f(){shape['width'].units='pt'}", + "var $$S_width='width';" + + "function f(){shape[$$S_width].units='pt'}"); + } + + public void testFunctionCalls() { + strings = ImmutableSet.of("", ",", "overimaginative"); + + // Not worth aliasing + testSame("alert('')"); + testSame("var a=[1,2,3];a.join(',')"); + // worth aliasing + test("f('overimaginative', 'overimaginative')", + "var $$S_overimaginative='overimaginative';" + + "f($$S_overimaginative,$$S_overimaginative)"); + } + + public void testRegularExpressions() { + strings = ImmutableSet.of("px"); + + testSame("/px/.match('10px')"); + } + + public void testBlackList() { + test("(function (){var f=\'sec ret\';g=\"TOPseCreT\"})", + "var $$S_sec$20ret='sec ret';" + + "(function (){var f=$$S_sec$20ret;g=\"TOPseCreT\"})"); + } + + public void testLongStableAlias() { + strings = ALL_STRINGS; + + // Check long strings get a hash code + + test("a='Antidisestablishmentarianism';" + + "b='Antidisestablishmentarianism';", + "var $$S_Antidisestablishment_506eaf9c=" + + " 'Antidisestablishmentarianism';" + + "a=$$S_Antidisestablishment_506eaf9c;" + + "b=$$S_Antidisestablishment_506eaf9c"); + + // Check that small changes give different hash codes + + test("a='AntidisestablishmentarianIsm';" + + "b='AntidisestablishmentarianIsm';", + "var $$S_Antidisestablishment_6823e97c=" + + " 'AntidisestablishmentarianIsm';" + + "a=$$S_Antidisestablishment_6823e97c;" + + "b=$$S_Antidisestablishment_6823e97c"); + + // TODO(user): check that hash code collisions are handled. + } + + public void testLongStableAliasHashCollision() { + strings = ALL_STRINGS; + hashReduction = true; + + // Check that hash code collisions generate different alias + // variable names + + test("f('Antidisestablishmentarianism');" + + "f('Antidisestablishmentarianism');" + + "f('Antidisestablishmentarianismo');" + + "f('Antidisestablishmentarianismo');", + + "var $$S_Antidisestablishment_0=" + + " 'Antidisestablishmentarianism';" + + "var $$S_Antidisestablishment_0_1=" + + " 'Antidisestablishmentarianismo';" + + + "f($$S_Antidisestablishment_0);" + + "f($$S_Antidisestablishment_0);" + + "f($$S_Antidisestablishment_0_1);" + + "f($$S_Antidisestablishment_0_1);"); + } + + + public void testStringsThatAreGlobalVarValues() { + strings = ALL_STRINGS; + + testSame("var foo='foo'; var bar='';"); + + // Regular array + testSame("var foo=['foo','bar'];"); + + // Nested array + testSame("var foo=['foo',['bar']];"); + + // Same string is in a global array and a local in a function + test("var foo=['foo', 'bar'];function bar() {return 'foo';}", + "var $$S_foo='foo';" + + "var foo=[$$S_foo, 'bar'];function bar() {return $$S_foo;}"); + + // Regular object literal + testSame("var foo={'foo': 'bar'};"); + + // Nested object literal + testSame("var foo={'foo': {'bar': 'baz'}};"); + + // Same string is in a global object literal (as key) and local in a + // function + test("var foo={'foo': 'bar'};function bar() {return 'foo';}", + "var $$S_foo='foo';var foo={'foo': 'bar'};" + + "function bar() {return $$S_foo;}"); + + // Same string is in a global object literal (as value) and local in a + // function + test("var foo={'foo': 'foo'};function bar() {return 'foo';}", + "var $$S_foo='foo';" + + "var foo={'foo': $$S_foo};function bar() {return $$S_foo;}"); + + } + + public void testStringsInModules() { + strings = ALL_STRINGS; + + // Aliases must be placed in the correct module. The alias for + // '------adios------' must be lifted from m2 and m3 and go in the + // common parent module m1 + + JSModule[] modules = + createModuleBush( + // m0 + "function f(a) { alert('f:' + a); }" + + "function g() { alert('ciao'); }", + // m1 + "f('-------hi-------');" + + "f('bye');" + + "function h(a) { alert('h:' + a); }", + // m2 + "f('-------hi-------');" + + "h('ciao' + '------adios------');" + + "(function() { alert('zzz'); })();", + // m3 + "f('-------hi-------'); alert('------adios------');" + + "h('-----peaches-----'); h('-----peaches-----');"); + + moduleGraph = new JSModuleGraph(modules); + + test(modules, + new String[] { + // m1 + "var $$S_ciao = 'ciao';" + + "var $$S_f$3a = 'f:';" + + "function f(a) { alert($$S_f$3a + a); }" + + "function g() { alert($$S_ciao); }", + // m2 + "var $$S_$2d$2d$2d$2d$2d$2d$2dhi$2d$2d$2d$2d$2d$2d$2d" + + " = '-------hi-------';" + + "var $$S_$2d$2d$2d$2d$2d$2d_adios$2d$2d$2d$2d$2d$2d" + + " = '------adios------'; " + + "var $$S_h$3a = 'h:';" + + "f($$S_$2d$2d$2d$2d$2d$2d$2dhi$2d$2d$2d$2d$2d$2d$2d);" + + "f('bye');" + + "function h(a) { alert($$S_h$3a + a); }", + // m3 + "var $$S_zzz = 'zzz';" + + "f($$S_$2d$2d$2d$2d$2d$2d$2dhi$2d$2d$2d$2d$2d$2d$2d);" + + "h($$S_ciao + $$S_$2d$2d$2d$2d$2d$2d_adios$2d$2d$2d$2d$2d$2d);" + + "(function() { alert($$S_zzz) })();", + // m4 + "var $$S_$2d$2d$2d$2d$2dpeaches$2d$2d$2d$2d$2d" + + " = '-----peaches-----';" + + "f($$S_$2d$2d$2d$2d$2d$2d$2dhi$2d$2d$2d$2d$2d$2d$2d);" + + "alert($$S_$2d$2d$2d$2d$2d$2d_adios$2d$2d$2d$2d$2d$2d);" + + "h($$S_$2d$2d$2d$2d$2dpeaches$2d$2d$2d$2d$2d);" + + "h($$S_$2d$2d$2d$2d$2dpeaches$2d$2d$2d$2d$2d);", + }); + moduleGraph = null; + } + + public void testStringsInModules2() { + strings = ALL_STRINGS; + + // Aliases must be placed in the correct module. The alias for + // '------adios------' must be lifted from m2 and m3 and go in the + // common parent module m1 + + JSModule[] modules = + createModuleBush( + // m0 + "function g() { alert('ciao'); }", + // m1 + "function h(a) { alert('h:' + a); }", + // m2 + "h('ciao' + 'adios');", + // m3 + "g();"); + + moduleGraph = new JSModuleGraph(modules); + + test(modules, + new String[] { + // m1 + "var $$S_ciao = 'ciao';" + + "function g() { alert($$S_ciao); }", + // m2 + "var $$S_h$3a = 'h:';" + + "function h(a) { alert($$S_h$3a + a); }", + // m3 + "h($$S_ciao + 'adios');", + // m4 + "g();", + }); + moduleGraph = null; + } + + + public void testEmptyModules() { + JSModule[] modules = + createModuleStar( + // m0 + "", + // m1 + "function foo() { f('good') }", + // m2 + "function foo() { f('good') }"); + + moduleGraph = new JSModuleGraph(modules); + test(modules, + new String[] { + // m0 + "var $$S_good='good'", + // m1 + "function foo() {f($$S_good)}", + // m2 + "function foo() {f($$S_good)}",}); + + moduleGraph = null; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AmbiguatePropertiesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AmbiguatePropertiesTest.java new file mode 100644 index 0000000..5d58b3c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AmbiguatePropertiesTest.java @@ -0,0 +1,600 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Maps; + +import com.google.javascript.jscomp.CompilerOptions.LanguageMode; +import com.google.javascript.rhino.Node; + +import java.util.Map; + +/** + * Unit test for AmbiguateProperties Compiler pass. + * + */ +public class AmbiguatePropertiesTest extends CompilerTestCase { + private AmbiguateProperties lastPass; + + private static final String EXTERNS = + "Function.prototype.call=function(){};" + + "Function.prototype.inherits=function(){};" + + "prop.toString;" + + "var google = { gears: { factory: {}, workerPool: {} } };"; + + public AmbiguatePropertiesTest() { + super(EXTERNS); + enableNormalize(); + enableTypeCheck(CheckLevel.WARNING); + enableClosurePass(); + } + + @Override + public CompilerPass getProcessor(final Compiler compiler) { + return new CompilerPass() { + @Override + public void process(Node externs, Node root) { + lastPass = new AmbiguateProperties(compiler, new char[]{'$'}); + lastPass.process(externs, root); + } + }; + } + + @Override + protected int getNumRepetitions() { + return 1; + } + + @Override + protected CompilerOptions getOptions() { + // no missing properties check + CompilerOptions options = new CompilerOptions(); + options.setLanguageIn(LanguageMode.ECMASCRIPT5); + return options; + } + + public void testOneVar1() { + test("/** @constructor */ var Foo = function(){};Foo.prototype.b = 0;", + "var Foo = function(){};Foo.prototype.a = 0;"); + } + + public void testOneVar2() { + testSame("/** @constructor */ var Foo = function(){};" + + "Foo.prototype = {b: 0};"); + } + + public void testOneVar3() { + testSame("/** @constructor */ var Foo = function(){};" + + "Foo.prototype = {get b() {return 0}};"); + } + + public void testOneVar4() { + testSame("/** @constructor */ var Foo = function(){};" + + "Foo.prototype = {set b(a) {}};"); + } + + public void testTwoVar1() { + String js = "" + + "/** @constructor */ var Foo = function(){};\n" + + "Foo.prototype.z=0;\n" + + "Foo.prototype.z=0;\n" + + "Foo.prototype.x=0;"; + String output = "" + + "var Foo = function(){};\n" + + "Foo.prototype.a=0;\n" + + "Foo.prototype.a=0;\n" + + "Foo.prototype.b=0;"; + test(js, output); + } + + public void testTwoVar2() { + String js = "" + + "/** @constructor */ var Foo = function(){};\n" + + "Foo.prototype={z:0, z:1, x:0};\n"; + // TODO(johnlenz): It would be nice to handle this type of declaration. + testSame(js); + } + + public void testTwoIndependentVar() { + String js = "" + + "/** @constructor */ var Foo = function(){};\n" + + "Foo.prototype.b = 0;\n" + + "/** @constructor */ var Bar = function(){};\n" + + "Bar.prototype.c = 0;"; + String output = "" + + "var Foo = function(){};" + + "Foo.prototype.a=0;" + + "var Bar = function(){};" + + "Bar.prototype.a=0;"; + test(js, output); + } + + public void testTwoTypesTwoVar() { + String js = "" + + "/** @constructor */ var Foo = function(){};\n" + + "Foo.prototype.r = 0;\n" + + "Foo.prototype.g = 0;\n" + + "/** @constructor */ var Bar = function(){};\n" + + "Bar.prototype.c = 0;" + + "Bar.prototype.r = 0;"; + String output = "" + + "var Foo = function(){};" + + "Foo.prototype.a=0;" + + "Foo.prototype.b=0;" + + "var Bar = function(){};" + + "Bar.prototype.b=0;" + + "Bar.prototype.a=0;"; + test(js, output); + } + + public void testUnion() { + String js = "" + + "/** @constructor */ var Foo = function(){};\n" + + "/** @constructor */ var Bar = function(){};\n" + + "Foo.prototype.foodoo=0;\n" + + "Bar.prototype.bardoo=0;\n" + + "/** @type {Foo|Bar} */\n" + + "var U;\n" + + "U.joint;" + + "U.joint"; + String output = "" + + "var Foo = function(){};\n" + + "var Bar = function(){};\n" + + "Foo.prototype.b=0;\n" + + "Bar.prototype.b=0;\n" + + "var U;\n" + + "U.a;" + + "U.a"; + test(js, output); + } + + public void testUnions() { + String js = "" + + "/** @constructor */ var Foo = function(){};\n" + + "/** @constructor */ var Bar = function(){};\n" + + "/** @constructor */ var Baz = function(){};\n" + + "/** @constructor */ var Bat = function(){};\n" + + "Foo.prototype.lone1=0;\n" + + "Bar.prototype.lone2=0;\n" + + "Baz.prototype.lone3=0;\n" + + "Bat.prototype.lone4=0;\n" + + "/** @type {Foo|Bar} */\n" + + "var U1;\n" + + "U1.j1;" + + "U1.j2;" + + "/** @type {Baz|Bar} */\n" + + "var U2;\n" + + "U2.j3;" + + "U2.j4;" + + "/** @type {Baz|Bat} */\n" + + "var U3;" + + "U3.j5;" + + "U3.j6"; + String output = "" + + "var Foo = function(){};\n" + + "var Bar = function(){};\n" + + "var Baz = function(){};\n" + + "var Bat = function(){};\n" + + "Foo.prototype.c=0;\n" + + "Bar.prototype.e=0;\n" + + "Baz.prototype.e=0;\n" + + "Bat.prototype.c=0;\n" + + "var U1;\n" + + "U1.a;" + + "U1.b;" + + "var U2;\n" + + "U2.c;" + + "U2.d;" + + "var U3;" + + "U3.a;" + + "U3.b"; + test(js, output); + } + + public void testExtends() { + String js = "" + + "/** @constructor */ var Foo = function(){};\n" + + "Foo.prototype.x=0;\n" + + "/** @constructor \n @extends Foo */ var Bar = function(){};\n" + + "goog.inherits(Bar, Foo);\n" + + "Bar.prototype.y=0;\n" + + "Bar.prototype.z=0;\n" + + "/** @constructor */ var Baz = function(){};\n" + + "Baz.prototype.l=0;\n" + + "Baz.prototype.m=0;\n" + + "Baz.prototype.n=0;\n" + + "(new Baz).m\n"; + String output = "" + + "/** @constructor */ var Foo = function(){};\n" + + "Foo.prototype.a=0;\n" + + "/** @constructor \n @extends Foo */ var Bar = function(){};\n" + + "goog.inherits(Bar, Foo);\n" + + "Bar.prototype.b=0;\n" + + "Bar.prototype.c=0;\n" + + "/** @constructor */ var Baz = function(){};\n" + + "Baz.prototype.b=0;\n" + + "Baz.prototype.a=0;\n" + + "Baz.prototype.c=0;\n" + + "(new Baz).a\n"; + test(js, output); + } + + public void testLotsOfVars() { + StringBuilder js = new StringBuilder(); + StringBuilder output = new StringBuilder(); + js.append("/** @constructor */ var Foo = function(){};\n"); + js.append("/** @constructor */ var Bar = function(){};\n"); + output.append(js.toString()); + + int vars = 10; + for (int i = 0; i < vars; i++) { + js.append("Foo.prototype.var" + i + " = 0;"); + js.append("Bar.prototype.var" + (i + 10000) + " = 0;"); + output.append("Foo.prototype." + (char) ('a' + i) + "=0;"); + output.append("Bar.prototype." + (char) ('a' + i) + "=0;"); + } + test(js.toString(), output.toString()); + } + + public void testLotsOfClasses() { + StringBuilder b = new StringBuilder(); + int classes = 10; + for (int i = 0; i < classes; i++) { + String c = "Foo" + i; + b.append("/** @constructor */ var " + c + " = function(){};\n"); + b.append(c + ".prototype.varness" + i + " = 0;"); + } + String js = b.toString(); + test(js, js.replaceAll("varness\\d+", "a")); + } + + public void testFunctionType() { + String js = "" + + "/** @constructor */ function Foo(){};\n" + + "/** @return {Bar} */\n" + + "Foo.prototype.fun = function() { return new Bar(); };\n" + + "/** @constructor */ function Bar(){};\n" + + "Bar.prototype.bazz;\n" + + "(new Foo).fun().bazz();"; + String output = "" + + "function Foo(){};\n" + + "Foo.prototype.a = function() { return new Bar(); };\n" + + "function Bar(){};\n" + + "Bar.prototype.a;\n" + + "(new Foo).a().a();"; + test(js, output); + } + + public void testPrototypePropertiesAsObjLitKeys1() { + test("/** @constructor */ function Bar() {};" + + "Bar.prototype = {2: function(){}, getA: function(){}};", + "/** @constructor */ function Bar() {};" + + "Bar.prototype = {2: function(){}, a: function(){}};"); + } + + public void testPrototypePropertiesAsObjLitKeys2() { + testSame("/** @constructor */ function Bar() {};" + + "Bar.prototype = {2: function(){}, 'getA': function(){}};"); + } + + public void testQuotedPrototypeProperty() { + testSame("/** @constructor */ function Bar() {};" + + "Bar.prototype['getA'] = function(){};" + + "var bar = new Bar();" + + "bar['getA']();"); + } + + public void testOverlappingOriginalAndGeneratedNames() { + test("/** @constructor */ function Bar(){};" + + "Bar.prototype.b = function(){};" + + "Bar.prototype.a = function(){};" + + "var bar = new Bar();" + + "bar.b();", + "function Bar(){};" + + "Bar.prototype.a = function(){};" + + "Bar.prototype.b = function(){};" + + "var bar = new Bar();" + + "bar.a();"); + } + + public void testPropertyAddedToObject() { + testSame("var foo = {}; foo.prop = '';"); + } + + public void testPropertyAddedToFunction() { + test("var foo = function(){}; foo.prop = '';", + "var foo = function(){}; foo.a = '';"); + } + + public void testPropertyOfObjectOfUnknownType() { + testSame("var foo = x(); foo.prop = '';"); + } + + public void testPropertyOnParamOfUnknownType() { + testSame("/** @constructor */ function Foo(){};\n" + + "Foo.prototype.prop = 0;" + + "function go(aFoo){\n" + + " aFoo.prop = 1;" + + "}"); + } + + public void testSetPropertyOfGlobalThis() { + testSame("this.prop = 'bar'"); + } + + public void testReadPropertyOfGlobalThis() { + testSame("f(this.prop);"); + } + + public void testSetQuotedPropertyOfThis() { + testSame("this['prop'] = 'bar';"); + } + + public void testExternedPropertyName() { + test("/** @constructor */ var Bar = function(){};" + + "/** @override */ Bar.prototype.toString = function(){};" + + "Bar.prototype.func = function(){};" + + "var bar = new Bar();" + + "bar.toString();", + "var Bar = function(){};" + + "Bar.prototype.toString = function(){};" + + "Bar.prototype.a = function(){};" + + "var bar = new Bar();" + + "bar.toString();"); + } + + public void testExternedPropertyNameDefinedByObjectLiteral() { + testSame("/**@constructor*/function Bar(){};Bar.prototype.factory"); + } + + public void testStaticAndInstanceMethodWithSameName() { + test("/** @constructor */function Bar(){}; Bar.getA = function(){}; " + + "Bar.prototype.getA = function(){}; Bar.getA();" + + "var bar = new Bar(); bar.getA();", + "function Bar(){}; Bar.a = function(){};" + + "Bar.prototype.a = function(){}; Bar.a();" + + "var bar = new Bar(); bar.a();"); + } + + public void testStaticAndInstanceProperties() { + test("/** @constructor */function Bar(){};" + + "Bar.getA = function(){}; " + + "Bar.prototype.getB = function(){};", + "function Bar(){}; Bar.a = function(){};" + + "Bar.prototype.a = function(){};"); + } + + public void testStaticAndSubInstanceProperties() { + String js = "" + + "/** @constructor */ var Foo = function(){};\n" + + "Foo.x=0;\n" + + "/** @constructor \n @extends Foo */ var Bar = function(){};\n" + + "goog.inherits(Bar, Foo);\n" + + "Bar.y=0;\n" + + "Bar.prototype.z=0;\n"; + String output = "" + + "/** @constructor */ var Foo = function(){};\n" + + "Foo.a=0;\n" + + "/** @constructor \n @extends Foo */ var Bar = function(){};\n" + + "goog.inherits(Bar, Foo);\n" + + "Bar.a=0;\n" + + "Bar.prototype.a=0;\n"; + test(js, output); + } + + public void testStaticWithFunctions() { + String js = "" + + "/** @constructor */ var Foo = function() {};\n" + + "Foo.x = 0;" + + "/** @param {!Function} x */ function f(x) { x.y = 1 }" + + "f(Foo)"; + String output = "" + + "/** @constructor */ var Foo = function() {};\n" + + "Foo.a = 0;" + + "/** @param {!Function} x */ function f(x) { x.y = 1 }" + + "f(Foo)"; + test(js, output); + + js = "" + + "/** @constructor */ var Foo = function() {};\n" + + "Foo.x = 0;" + + "/** @param {!Function} x */ function f(x) { x.y = 1; x.x = 2;}" + + "f(Foo)"; + test(js, js); + + js = "" + + "/** @constructor */ var Foo = function() {};\n" + + "Foo.x = 0;" + + "/** @constructor */ var Bar = function() {};\n" + + "Bar.y = 0;"; + + output = "" + + "/** @constructor */ var Foo = function() {};\n" + + "Foo.a = 0;" + + "/** @constructor */ var Bar = function() {};\n" + + "Bar.a = 0;"; + test(js, output); + + } + + public void testTypeMismatch() { + testSame(EXTERNS, "/** @constructor */var Foo = function(){};\n" + + "/** @constructor */var Bar = function(){};\n" + + "Foo.prototype.b = 0;\n" + + "/** @type {Foo} */\n" + + "var F = new Bar();", + TypeValidator.TYPE_MISMATCH_WARNING, + "initializing variable\n" + + "found : Bar\n" + + "required: (Foo|null)"); + } + + public void testRenamingMap() { + String js = "" + + "/** @constructor */ var Foo = function(){};\n" + + "Foo.prototype.z=0;\n" + + "Foo.prototype.z=0;\n" + + "Foo.prototype.x=0;\n" + + "Foo.prototype.y=0;"; + String output = "" + + "var Foo = function(){};\n" + + "Foo.prototype.a=0;\n" + + "Foo.prototype.a=0;\n" + + "Foo.prototype.b=0;\n" + + "Foo.prototype.c=0;"; + test(js, output); + + Map answerMap = Maps.newHashMap(); + answerMap.put("x", "b"); + answerMap.put("y", "c"); + answerMap.put("z", "a"); + assertEquals(answerMap, lastPass.getRenamingMap()); + } + + public void testInline() { + String js = "" + + "/** @interface */ function Foo(){}\n" + + "Foo.prototype.x = function(){};\n" + + "/**\n" + + " * @constructor\n" + + " * @implements {Foo}\n" + + " */\n" + + "function Bar(){}\n" + + "/** @inheritDoc */\n" + + "Bar.prototype.x = function() { return this.y; };\n" + + "Bar.prototype.z = function() {};\n" + // Simulates inline getters. + + "/** @type {Foo} */ (new Bar).y;"; + String output = "" + + "function Foo(){}\n" + + "Foo.prototype.a = function(){};\n" + + "function Bar(){}\n" + + "Bar.prototype.a = function() { return this.b; };\n" + + "Bar.prototype.c = function() {};\n" + // Simulates inline getters. + + "(new Bar).b;"; + test(js, output); + } + + public void testImplementsAndExtends() { + String js = "" + + "/** @interface */ function Foo() {}\n" + + "/**\n" + + " * @constructor\n" + + " */\n" + + "function Bar(){}\n" + + "Bar.prototype.y = function() { return 3; };\n" + + "/**\n" + + " * @constructor\n" + + " * @extends {Bar}\n" + + " * @implements {Foo}\n" + + " */\n" + + "function SubBar(){ }\n" + + "/** @param {Foo} x */ function f(x) { x.z = 3; }\n" + + "/** @param {SubBar} x */ function g(x) { x.z = 3; }"; + String output = "" + + "function Foo(){}\n" + + "function Bar(){}\n" + + "Bar.prototype.b = function() { return 3; };\n" + + "function SubBar(){}\n" + + "function f(x) { x.a = 3; }\n" + + "function g(x) { x.a = 3; }"; + test(js, output); + } + + public void testImplementsAndExtends2() { + String js = "" + + "/** @interface */ function A() {}\n" + + "/**\n" + + " * @constructor\n" + + " */\n" + + "function C1(){}\n" + + "/**\n" + + " * @constructor\n" + + " * @extends {C1}\n" + + " * @implements {A}\n" + + " */\n" + + "function C2(){}\n" + + "/** @param {C1} x */ function f(x) { x.y = 3; }\n" + + "/** @param {A} x */ function g(x) { x.z = 3; }\n"; + String output = "" + + "function A(){}\n" + + "function C1(){}\n" + + "function C2(){}\n" + + "function f(x) { x.a = 3; }\n" + + "function g(x) { x.b = 3; }\n"; + test(js, output); + } + + public void testExtendsInterface() { + String js = "" + + "/** @interface */ function A() {}\n" + + "/** @interface \n @extends {A} */ function B() {}\n" + + "/** @param {A} x */ function f(x) { x.y = 3; }\n" + + "/** @param {B} x */ function g(x) { x.z = 3; }\n"; + String output = "" + + "function A(){}\n" + + "function B(){}\n" + + "function f(x) { x.a = 3; }\n" + + "function g(x) { x.b = 3; }\n"; + test(js, output); + } + + public void testFunctionSubType() { + String js = "" + + "Function.prototype.a = 1;\n" + + "function f() {}\n" + + "f.y = 2;\n"; + String output = "" + + "Function.prototype.a = 1;\n" + + "function f() {}\n" + + "f.b = 2;\n"; + test(js, output); + } + + public void testFunctionSubType2() { + String js = "" + + "Function.prototype.a = 1;\n" + + "/** @constructor */ function F() {}\n" + + "F.y = 2;\n"; + String output = "" + + "Function.prototype.a = 1;\n" + + "function F() {}\n" + + "F.b = 2;\n"; + test(js, output); + } + + public void testPredeclaredType() { + String js = + "goog.addDependency('zzz.js', ['goog.Foo'], []);" + + "/** @constructor */ " + + "function A() {" + + " this.x = 3;" + + "}" + + "/** @param {goog.Foo} x */" + + "function f(x) { x.y = 4; }"; + String result = + "0;" + + "/** @constructor */ " + + "function A() {" + + " this.a = 3;" + + "}" + + "/** @param {goog.Foo} x */" + + "function f(x) { x.y = 4; }"; + test(js, result); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AstParallelizerTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AstParallelizerTest.java new file mode 100644 index 0000000..a5aa86e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AstParallelizerTest.java @@ -0,0 +1,149 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.javascript.rhino.Node; + +import junit.framework.TestCase; + +import java.util.List; + +/** + * Unit tests for {@link AstParallelizer}. + * + */ +public class AstParallelizerTest extends TestCase { + + private static final String HOLDER = AstParallelizer.TEMP_NAME; + + public void testNoSplit() { + splitFunctions("", ""); + splitFunctions("var x", "var x"); + splitFunctions("var x", "var x"); + splitFunctions("x()", "x()"); + } + + public void testSplitNamedFuntion() { + splitFunctions("function foo() { foo() } foo()", + "function " + HOLDER + "() {} foo()", + "function foo() { foo() }"); + } + + + public void testSplitNamedFuntionWithArgs() { + splitFunctions("function foo(x) { foo(1) } foo(1)", + "function " + HOLDER + "() {} foo(1)", + "function foo(x) { foo(1) }"); + } + + // TODO(johnlenz): This test is invalid it relies on allowing + // nameless function statements, which does not parse. + public void disable_testSplitAnonFuntion() { + splitFunctions("var foo = function(x) { foo(1) }; foo(1)", + "var foo = function " + HOLDER + "() {}; foo(1)", + "(function(x) { foo(1) })"); + } + + // TODO(johnlenz): This test is invalid it relies on allowing + // nameless function statements, which does not parse. + public void disable_testSplitInplaceCall() { + splitFunctions("(function() { print('hi') })()", + "(function " + HOLDER + "() {})()", + "(function() { print('hi') })"); + } + + // TODO(johnlenz): This test is invalid it relies on allowing + // nameless function statements, which does not parse. + public void disable_testSplitMupltiFuntions() { + splitFunctions("var foo = function(x) { foo(1) }; foo();" + + "var bar = function(x,y) { bar(1,2) }; bar(1,2)", + // Output Root + "var foo = function " + HOLDER + "() {}; foo();" + + "var bar = function " + HOLDER + "() {}; bar(1,2)", + // foo + "(function(x) { foo(1) })", + // bar + "(function(x,y) { bar(1,2) })"); + } + + // TODO(johnlenz): This test is invalid it relies on allowing + // nameless function statements, which does not parse. + public void disable_testInnerFunctions() { + splitFunctions("var foo = function() {var bar = function() {}}", + "var foo = function " + HOLDER + "() {}", + "function() {var bar = function() {}}"); + } + + public void testSplitFileLevel() { + splitFiles(new String[] { "var a", "var b", "var c"}); + splitFiles(new String[] { + "var a", "var b", "var c", "var d", "function e() {}"}); + } + + /** + * Splits at function level with {@link AstParallelizer#split()}, verify the + * output matches what is expected and then verify + * {@link AstParallelizer#join()} can reverse the whole process. + */ + private void splitFunctions(String input, String ... output) { + Compiler compiler = new Compiler(); + Node original = compiler.parseTestCode(input); + Node root = original.cloneTree(); + AstParallelizer parallelizer = + AstParallelizer.createNewFunctionLevelAstParallelizer(root, true); + List forest = parallelizer.split(); + assertEquals(output.length, forest.size()); + int i = 0; + for (Node n : forest) { + Node tree = compiler.parseTestCode(output[i++]); + assertEquals(compiler.toSource(tree), compiler.toSource(n)); + } + + parallelizer.join(); + assertTrue(original.isEquivalentTo(root)); + } + + private void splitFiles(String[] input) { + Compiler compiler = new Compiler(); + List files = Lists.newArrayList(); + + for (int i = 0; i < input.length; i ++) { + files.add(SourceFile.fromCode("file" + i, input[i])); + } + + compiler.init( + ImmutableList.of(), files, new CompilerOptions()); + compiler.parse(); + Node original = compiler.getRoot(); + Node root = original.cloneTree(); + + AstParallelizer parallelizer = + AstParallelizer.createNewFileLevelAstParallelizer(root); + List forest = parallelizer.split(); + assertEquals(input.length, forest.size()); + int i = 0; + for (Node n : forest) { + Node tree = compiler.parseTestCode(input[i++]); + assertEquals(compiler.toSource(tree), compiler.toSource(n)); + } + + parallelizer.join(); + assertTrue(original.isEquivalentTo(root)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AstValidatorTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AstValidatorTest.java new file mode 100644 index 0000000..7711531 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/AstValidatorTest.java @@ -0,0 +1,145 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.AstValidator.ViolationHandler; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.SimpleSourceFile; + + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class AstValidatorTest extends CompilerTestCase { + + private boolean lastCheckWasValid = true; + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return createValidator(); + } + + private AstValidator createValidator() { + lastCheckWasValid = true; + return new AstValidator(new ViolationHandler() { + @Override + public void handleViolation(String message, Node n) { + lastCheckWasValid = false; + } + }); + } + + @Override + protected int getNumRepetitions() { + return 1; + } + + @Override + protected void setUp() throws Exception { + super.enableAstValidation(false); + super.disableNormalize(); + super.enableLineNumberCheck(false); + super.setUp(); + } + + public void testForIn() { + valid("for(var a in b);"); + valid("for(var a = 1 in b);"); + valid("for(a in b);"); + valid("for(a in []);"); + valid("for(a in {});"); + } + + public void testDebugger() { + valid("debugger;"); + } + + public void testValidScript() { + Node n = new Node(Token.SCRIPT); + expectInvalid(n, Check.SCRIPT); + n.setInputId(new InputId("something_input")); + n.setStaticSourceFile(new SimpleSourceFile("something", false)); + expectValid(n, Check.SCRIPT); + expectInvalid(n, Check.STATEMENT); + expectInvalid(n, Check.EXPRESSION); + } + + public void testValidStatement1() { + Node n = new Node(Token.RETURN); + expectInvalid(n, Check.EXPRESSION); + expectValid(n, Check.STATEMENT); + expectInvalid(n, Check.SCRIPT); + } + + public void testValidExpression1() { + Node n = new Node(Token.ARRAYLIT, new Node(Token.EMPTY)); + expectValid(n, Check.EXPRESSION); + expectInvalid(n, Check.STATEMENT); + expectInvalid(n, Check.SCRIPT); + } + + public void testValidExpression2() { + Node n = new Node(Token.NOT, new Node(Token.TRUE)); + expectValid(n, Check.EXPRESSION); + expectInvalid(n, Check.STATEMENT); + expectInvalid(n, Check.SCRIPT); + } + + public void testInvalidEmptyStatement() { + Node n = new Node(Token.EMPTY, new Node(Token.TRUE)); + expectInvalid(n, Check.STATEMENT); + n.detachChildren(); + expectValid(n, Check.STATEMENT); + } + + private void valid(String code) { + testSame(code); + assertTrue(lastCheckWasValid); + } + + private enum Check { + SCRIPT, + STATEMENT, + EXPRESSION + } + + private boolean doCheck(Node n, Check level) { + AstValidator validator = createValidator(); + switch (level) { + case SCRIPT: + validator.validateScript(n); + break; + case STATEMENT: + validator.validateStatement(n); + break; + case EXPRESSION: + validator.validateExpression(n); + break; + } + return lastCheckWasValid; + } + + private void expectInvalid(Node n, Check level) { + assertFalse(doCheck(n, level)); + } + + private void expectValid(Node n, Check level) { + assertTrue(doCheck(n, level)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/BasicErrorManagerTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/BasicErrorManagerTest.java new file mode 100644 index 0000000..44b80da --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/BasicErrorManagerTest.java @@ -0,0 +1,119 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.BasicErrorManager.ErrorWithLevel; +import com.google.javascript.jscomp.BasicErrorManager.LeveledJSErrorComparator; +import com.google.javascript.jscomp.CheckLevel; + +import junit.framework.TestCase; + +/** + * Tests {@link BasicErrorManager}. + * + */ +public class BasicErrorManagerTest extends TestCase { + private static final String NULL_SOURCE = null; + + private LeveledJSErrorComparator comparator = new LeveledJSErrorComparator(); + + static final CheckLevel E = CheckLevel.ERROR; + + private static final DiagnosticType FOO_TYPE = + DiagnosticType.error("TEST_FOO", "Foo"); + + private static final DiagnosticType JOO_TYPE = + DiagnosticType.error("TEST_JOO", "Joo"); + + public void testOrderingBothNull() throws Exception { + assertEquals(0, comparator.compare(null, null)); + } + + public void testOrderingSourceName1() throws Exception { + JSError e1 = JSError.make(NULL_SOURCE, -1, -1, FOO_TYPE); + JSError e2 = JSError.make("a", -1, -1, FOO_TYPE); + + assertSmaller(error(e1), error(e2)); + } + + public void testOrderingSourceName2() throws Exception { + JSError e1 = JSError.make("a", -1, -1, FOO_TYPE); + JSError e2 = JSError.make("b", -1, -1, FOO_TYPE); + + assertSmaller(error(e1), error(e2)); + } + + public void testOrderingLineno1() throws Exception { + JSError e1 = JSError.make(NULL_SOURCE, -1, -1, FOO_TYPE); + JSError e2 = JSError.make(NULL_SOURCE, 2, -1, FOO_TYPE); + + assertSmaller(error(e1), error(e2)); + } + + public void testOrderingLineno2() throws Exception { + JSError e1 = JSError.make(NULL_SOURCE, 8, -1, FOO_TYPE); + JSError e2 = JSError.make(NULL_SOURCE, 56, -1, FOO_TYPE); + assertSmaller(error(e1), error(e2)); + } + + public void testOrderingCheckLevel() throws Exception { + JSError e1 = JSError.make(NULL_SOURCE, -1, -1, FOO_TYPE); + JSError e2 = JSError.make(NULL_SOURCE, -1, -1, FOO_TYPE); + + assertSmaller(warning(e1), error(e2)); + } + + public void testOrderingCharno1() throws Exception { + JSError e1 = JSError.make(NULL_SOURCE, 5, -1, FOO_TYPE); + JSError e2 = JSError.make(NULL_SOURCE, 5, 2, FOO_TYPE); + + assertSmaller(error(e1), error(e2)); + // CheckLevel preempts charno comparison + assertSmaller(warning(e1), error(e2)); + } + + public void testOrderingCharno2() throws Exception { + JSError e1 = JSError.make(NULL_SOURCE, 8, 7, FOO_TYPE); + JSError e2 = JSError.make(NULL_SOURCE, 8, 5, FOO_TYPE); + + assertSmaller(error(e2), error(e1)); + // CheckLevel preempts charno comparison + assertSmaller(warning(e2), error(e1)); + } + + public void testOrderingDescription() throws Exception { + JSError e1 = JSError.make(NULL_SOURCE, -1, -1, FOO_TYPE); + JSError e2 = JSError.make(NULL_SOURCE, -1, -1, JOO_TYPE); + + assertSmaller(error(e1), error(e2)); + } + + private ErrorWithLevel error(JSError e) { + return new ErrorWithLevel(e, CheckLevel.ERROR); + } + + private ErrorWithLevel warning(JSError e) { + return new ErrorWithLevel(e, CheckLevel.WARNING); + } + + private void assertSmaller(ErrorWithLevel p1, ErrorWithLevel p2) { + int p1p2 = comparator.compare(p1, p2); + assertTrue(Integer.toString(p1p2), p1p2 < 0); + int p2p1 = comparator.compare(p2, p1); + assertTrue(Integer.toString(p2p1), p2p1 > 0); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CallGraphTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CallGraphTest.java new file mode 100644 index 0000000..1138b3f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CallGraphTest.java @@ -0,0 +1,1157 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.CallGraph.Callsite; +import com.google.javascript.jscomp.CallGraph.Function; +import com.google.javascript.jscomp.graph.FixedPointGraphTraversal; +import com.google.javascript.jscomp.graph.FixedPointGraphTraversal.EdgeCallback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * Tests for CallGraph. + * + * @author dcc@google.com (Devin Coughlin) + */ +public class CallGraphTest extends CompilerTestCase { + + private CallGraph currentProcessor; + + private boolean createForwardCallGraph; + private boolean createBackwardCallGraph; + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + // We store the new callgraph so it can be tested later + currentProcessor = new CallGraph(compiler, createForwardCallGraph, + createBackwardCallGraph); + + return currentProcessor; + } + + static final String SHARED_EXTERNS = + "var ExternalFunction = function(a) {}\n" + + "var externalnamespace = {}\n" + + "externalnamespace.prop = function(){};\n"; + + public void testGetFunctionForAstNode() { + String source = "function A() {};\n"; + + CallGraph callgraph = compileAndRunForward(source); + + CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); + + Node functionANode = functionA.getAstNode(); + + assertEquals(functionA, callgraph.getFunctionForAstNode(functionANode)); + } + + public void testGetAllFunctions() { + String source = + "function A() {}\n" + + "var B = function() {\n" + + "(function C(){A()})()\n" + + "};\n"; + + CallGraph callgraph = compileAndRunForward(source); + + Collection functions = callgraph.getAllFunctions(); + + // 3 Functions, plus one for the main function + assertEquals(4, functions.size()); + + CallGraph.Function functionA = + callgraph.getUniqueFunctionWithName("A"); + CallGraph.Function functionB = + callgraph.getUniqueFunctionWithName("B"); + CallGraph.Function functionC = + callgraph.getUniqueFunctionWithName("C"); + + assertEquals("A", NodeUtil.getFunctionName(functionA.getAstNode())); + assertEquals("B", NodeUtil.getFunctionName(functionB.getAstNode())); + assertEquals("C", NodeUtil.getFunctionName(functionC.getAstNode())); + } + + public void testGetAllFunctionsContainsNormalFunction() { + String source = "function A(){}\n"; + + CallGraph callgraph = compileAndRunForward(source); + + Collection allFunctions = callgraph.getAllFunctions(); + + // 2 functions: one for A() and one for the main function + assertEquals(2, allFunctions.size()); + + assertTrue(allFunctions.contains(callgraph.getUniqueFunctionWithName("A"))); + assertTrue(allFunctions.contains(callgraph.getMainFunction())); + } + + public void testGetAllFunctionsContainsVarAssignedLiteralFunction() { + String source = "var A = function(){}\n"; + + CallGraph callgraph = compileAndRunForward(source); + + Collection allFunctions = callgraph.getAllFunctions(); + + // 2 functions: one for A() and one for the global function + assertEquals(2, allFunctions.size()); + + Function functionA = callgraph.getUniqueFunctionWithName("A"); + assertTrue(allFunctions.contains(functionA)); + assertTrue(allFunctions.contains(callgraph.getMainFunction())); + } + + public void testGetAllFunctionsContainsNamespaceAssignedLiteralFunction() { + String source = + "var namespace = {};\n" + + "namespace.A = function(){};\n"; + + CallGraph callgraph = compileAndRunForward(source); + + Collection allFunctions = callgraph.getAllFunctions(); + + // 2 functions: one for namespace.A() and one for the global function + assertEquals(2, allFunctions.size()); + + assertTrue(allFunctions.contains( + callgraph.getUniqueFunctionWithName("namespace.A"))); + assertTrue(allFunctions.contains(callgraph.getMainFunction())); + } + + public void testGetAllFunctionsContainsLocalFunction() { + String source = + "var A = function(){var B = function(){}};\n"; + + CallGraph callgraph = compileAndRunForward(source); + + Collection allFunctions = callgraph.getAllFunctions(); + + // 3 functions: one for A, B, and global function + assertEquals(3, allFunctions.size()); + + assertTrue(allFunctions.contains(callgraph.getUniqueFunctionWithName("A"))); + assertTrue(allFunctions.contains(callgraph.getUniqueFunctionWithName("B"))); + assertTrue(allFunctions.contains(callgraph.getMainFunction())); + } + + public void testGetAllFunctionsContainsAnonymousFunction() { + String source = + "var A = function(){(function(){})();};\n"; + + CallGraph callgraph = compileAndRunForward(source); + + Collection allFunctions = callgraph.getAllFunctions(); + + // 3 functions: A, anonymous, and global function + assertEquals(3, allFunctions.size()); + + assertTrue(allFunctions.contains(callgraph.getUniqueFunctionWithName("A"))); + assertTrue( + allFunctions.contains(callgraph.getUniqueFunctionWithName(null))); + assertTrue(allFunctions.contains(callgraph.getMainFunction())); + } + + public void testGetCallsiteForAstNode() { + String source = + "function A() {B()};\n" + + "function B(){};\n"; + + CallGraph callgraph = compileAndRunBackward(source); + + CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); + CallGraph.Callsite callToB = + functionA.getCallsitesInFunction().iterator().next(); + + Node callsiteNode = callToB.getAstNode(); + + assertEquals(callToB, callgraph.getCallsiteForAstNode(callsiteNode)); + } + + public void testFunctionGetCallsites() { + String source = + "function A() {var x; x()}\n" + + "var B = function() {\n" + + "(function C(){A()})()\n" + + "};\n"; + + CallGraph callgraph = compileAndRunForward(source); + + CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); + Collection callsitesInA = + functionA.getCallsitesInFunction(); + + assertEquals(1, callsitesInA.size()); + + CallGraph.Callsite firstCallsiteInA = + callsitesInA.iterator().next(); + + Node aTargetExpression = firstCallsiteInA.getAstNode().getFirstChild(); + assertEquals(Token.NAME, aTargetExpression.getType()); + assertEquals("x", aTargetExpression.getString()); + + CallGraph.Function functionB = + callgraph.getUniqueFunctionWithName("B"); + + Collection callsitesInB = + functionB.getCallsitesInFunction(); + + assertEquals(1, callsitesInB.size()); + + CallGraph.Callsite firstCallsiteInB = + callsitesInB.iterator().next(); + + Node bTargetExpression = firstCallsiteInB.getAstNode().getFirstChild(); + assertEquals(Token.FUNCTION, bTargetExpression.getType()); + assertEquals("C", NodeUtil.getFunctionName(bTargetExpression)); + + CallGraph.Function functionC = + callgraph.getUniqueFunctionWithName("C"); + + Collection callsitesInC = + functionC.getCallsitesInFunction(); + assertEquals(1, callsitesInC.size()); + + CallGraph.Callsite firstCallsiteInC = + callsitesInC.iterator().next(); + + Node cTargetExpression = firstCallsiteInC.getAstNode().getFirstChild(); + assertEquals(Token.NAME, aTargetExpression.getType()); + assertEquals("A", cTargetExpression.getString()); + } + + public void testFindNewInFunction() { + String source = "function A() {var x; new x(1,2)}\n;"; + + CallGraph callgraph = compileAndRunForward(source); + + CallGraph.Function functionA = + callgraph.getUniqueFunctionWithName("A"); + Collection callsitesInA = + functionA.getCallsitesInFunction(); + assertEquals(1, callsitesInA.size()); + + Node callsiteInA = callsitesInA.iterator().next().getAstNode(); + assertEquals(Token.NEW, callsiteInA.getType()); + + Node aTargetExpression = callsiteInA.getFirstChild(); + assertEquals(Token.NAME, aTargetExpression.getType()); + assertEquals("x", aTargetExpression.getString()); + } + + public void testFindCallsiteTargetGlobalName() { + String source = + "function A() {}\n" + + "function B() {}\n" + + "function C() {A()}\n"; + + CallGraph callgraph = compileAndRunForward(source); + + CallGraph.Function functionC = + callgraph.getUniqueFunctionWithName("C"); + assertNotNull(functionC); + + CallGraph.Callsite callsiteInC = + functionC.getCallsitesInFunction().iterator().next(); + assertNotNull(callsiteInC); + + Collection targetsOfCallsiteInC = + callsiteInC.getPossibleTargets(); + + assertNotNull(targetsOfCallsiteInC); + assertEquals(1, targetsOfCallsiteInC.size()); + } + + public void testFindCallsiteTargetAliasedGlobalProperty() { + String source = + "var namespace = {};\n" + + "namespace.A = function() {};\n" + + "function C() {namespace.A()}\n"; + + CallGraph callgraph = compileAndRunForward(source); + + CallGraph.Function functionC = + callgraph.getUniqueFunctionWithName("C"); + assertNotNull(functionC); + + CallGraph.Callsite callsiteInC = + functionC.getCallsitesInFunction().iterator().next(); + + assertNotNull(callsiteInC); + + Collection targetsOfCallsiteInC = + callsiteInC.getPossibleTargets(); + + assertNotNull(targetsOfCallsiteInC); + assertEquals(1, targetsOfCallsiteInC.size()); + } + + public void testGetAllCallsitesContainsMultiple() { + String source = + "function A() {}\n" + + "var B = function() {\n" + + "(function (){A()})()\n" + + "};\n" + + "A();\n" + + "B();\n"; + + CallGraph callgraph = compileAndRunBackward(source); + + Collection allCallsites = callgraph.getAllCallsites(); + + assertEquals(4, allCallsites.size()); + } + + public void testGetAllCallsitesContainsGlobalSite() { + String source = + "function A(){}\n" + + "A();\n"; + + CallGraph callgraph = compileAndRunBackward(source); + + Collection allCallsites = callgraph.getAllCallsites(); + assertEquals(1, allCallsites.size()); + + Node callsiteNode = allCallsites.iterator().next().getAstNode(); + assertEquals(Token.CALL, callsiteNode.getType()); + assertEquals("A", callsiteNode.getFirstChild().getString()); + } + + public void testGetAllCallsitesContainsLocalSite() { + String source = + "function A(){}\n" + + "function B(){A();}\n"; + + CallGraph callgraph = compileAndRunBackward(source); + + Collection allCallsites = callgraph.getAllCallsites(); + assertEquals(1, allCallsites.size()); + + Node callsiteNode = allCallsites.iterator().next().getAstNode(); + assertEquals(Token.CALL, callsiteNode.getType()); + assertEquals("A", callsiteNode.getFirstChild().getString()); + } + + public void testGetAllCallsitesContainsLiteralSite() { + String source = "function A(){(function(a){})();}\n"; + + CallGraph callgraph = compileAndRunBackward(source); + + Collection allCallsites = callgraph.getAllCallsites(); + assertEquals(1, allCallsites.size()); + + Node callsiteNode = allCallsites.iterator().next().getAstNode(); + assertEquals(Token.CALL, callsiteNode.getType()); + assertEquals(Token.FUNCTION, callsiteNode.getFirstChild().getType()); + } + + public void testGetAllCallsitesContainsConstructorSite() { + String source = + "function A(){}\n" + + "function B(){new A();}\n"; + + CallGraph callgraph = compileAndRunBackward(source); + + Collection allCallsites = callgraph.getAllCallsites(); + assertEquals(1, allCallsites.size()); + + Node callsiteNode = allCallsites.iterator().next().getAstNode(); + assertEquals(Token.NEW, callsiteNode.getType()); + assertEquals("A", callsiteNode.getFirstChild().getString()); + } + + /** + * Test getting a backward directed graph on a backward call graph + * and propagating over it. + */ + public void testGetDirectedGraph_backwardOnBackward() { + // For this test we create a simple callback that, when applied until a + // fixedpoint, computes whether a function is "poisoned" by an extern. + // A function is poisoned if it calls an extern or if it calls another + // poisoned function. + + String source = + "function A(){};\n" + + "function B(){ExternalFunction(6); C(); D();}\n" + + "function C(){B(); A();};\n" + + "function D(){A();};\n" + + "function E(){C()};\n" + + "A();\n"; + + CallGraph callgraph = compileAndRunBackward(source); + + final Set poisonedFunctions = Sets.newHashSet(); + + // Set up initial poisoned functions + for (Callsite callsite : callgraph.getAllCallsites()) { + if (callsite.hasExternTarget()) { + poisonedFunctions.add(callsite.getContainingFunction()); + } + } + + // Propagate poison from callees to callers + EdgeCallback edgeCallback = + new EdgeCallback() { + @Override + public boolean traverseEdge(Function callee, Callsite callsite, + Function caller) { + boolean changed; + + if (poisonedFunctions.contains(callee)) { + changed = poisonedFunctions.add(caller); // Returns true if added + } else { + changed = false; + } + + return changed; + } + }; + + FixedPointGraphTraversal.newTraversal(edgeCallback) + .computeFixedPoint(callgraph.getBackwardDirectedGraph()); + + // We expect B, C, and E to poisoned. + assertEquals(3, poisonedFunctions.size()); + + assertTrue(poisonedFunctions.contains( + callgraph.getUniqueFunctionWithName("B"))); + assertTrue(poisonedFunctions.contains( + callgraph.getUniqueFunctionWithName("C"))); + assertTrue(poisonedFunctions.contains( + callgraph.getUniqueFunctionWithName("E"))); + } + + /** + * Test getting a backward directed graph on a forward call graph + * and propagating over it. + */ + public void testGetDirectedGraph_backwardOnForward() { + // For this test we create a simple callback that, when applied until a + // fixedpoint, computes whether a function is "poisoned" by an extern. + // A function is poisoned if it calls an extern or if it calls another + // poisoned function. + + String source = + "function A(){};\n" + + "function B(){ExternalFunction(6); C(); D();}\n" + + "function C(){B(); A();};\n" + + "function D(){A();};\n" + + "function E(){C()};\n" + + "A();\n"; + + CallGraph callgraph = compileAndRunForward(source); + + final Set poisonedFunctions = Sets.newHashSet(); + + // Set up initial poisoned functions + for (Callsite callsite : callgraph.getAllCallsites()) { + if (callsite.hasExternTarget()) { + poisonedFunctions.add(callsite.getContainingFunction()); + } + } + + // Propagate poison from callees to callers + EdgeCallback edgeCallback = + new EdgeCallback() { + @Override + public boolean traverseEdge(Function callee, Callsite callsite, + Function caller) { + boolean changed; + + if (poisonedFunctions.contains(callee)) { + changed = poisonedFunctions.add(caller); // Returns true if added + } else { + changed = false; + } + + return changed; + } + }; + + FixedPointGraphTraversal.newTraversal(edgeCallback) + .computeFixedPoint(callgraph.getBackwardDirectedGraph()); + + // We expect B, C, and E to poisoned. + assertEquals(3, poisonedFunctions.size()); + + assertTrue(poisonedFunctions.contains( + callgraph.getUniqueFunctionWithName("B"))); + assertTrue(poisonedFunctions.contains( + callgraph.getUniqueFunctionWithName("C"))); + assertTrue(poisonedFunctions.contains( + callgraph.getUniqueFunctionWithName("E"))); + } + + /** + * Test getting a forward directed graph on a forward call graph + * and propagating over it. + */ + public void testGetDirectedGraph_forwardOnForward() { + // For this test we create a simple callback that, when applied until a + // fixedpoint, computes whether a function is reachable from an initial + // set of "root" nodes. + + String source = + "function A(){B()};\n" + + "function B(){C();D()}\n" + + "function C(){B()};\n" + + "function D(){};\n" + + "function E(){C()};\n" + + "function X(){Y()};\n" + + "function Y(){Z()};\n" + + "function Z(){};" + + "B();\n"; + + CallGraph callgraph = compileAndRunForward(source); + + final Set reachableFunctions = Sets.newHashSet(); + + // We assume the main function and X are our roots + reachableFunctions.add(callgraph.getMainFunction()); + reachableFunctions.add(callgraph.getUniqueFunctionWithName("X")); + + // Propagate reachability from callers to callees + + EdgeCallback edgeCallback = + new EdgeCallback() { + @Override + public boolean traverseEdge(Function caller, Callsite callsite, + Function callee) { + boolean changed; + + if (reachableFunctions.contains(caller)) { + changed = reachableFunctions.add(callee); // Returns true if added + } else { + changed = false; + } + + return changed; + } + }; + + FixedPointGraphTraversal.newTraversal(edgeCallback) + .computeFixedPoint(callgraph.getForwardDirectedGraph()); + + // We expect B, C, D, X, Y, Z and the main function should be reachable. + // A and E should not be reachable. + + assertEquals(7, reachableFunctions.size()); + + assertTrue(reachableFunctions.contains( + callgraph.getUniqueFunctionWithName("B"))); + assertTrue(reachableFunctions.contains( + callgraph.getUniqueFunctionWithName("C"))); + assertTrue(reachableFunctions.contains( + callgraph.getUniqueFunctionWithName("D"))); + assertTrue(reachableFunctions.contains( + callgraph.getUniqueFunctionWithName("X"))); + assertTrue(reachableFunctions.contains( + callgraph.getUniqueFunctionWithName("Y"))); + assertTrue(reachableFunctions.contains( + callgraph.getUniqueFunctionWithName("Z"))); + assertTrue(reachableFunctions.contains( + callgraph.getMainFunction())); + + assertFalse(reachableFunctions.contains( + callgraph.getUniqueFunctionWithName("A"))); + assertFalse(reachableFunctions.contains( + callgraph.getUniqueFunctionWithName("E"))); + } + + /** + * Test getting a backward directed graph on a forward call graph + * and propagating over it. + */ + public void testGetDirectedGraph_forwardOnBackward() { + // For this test we create a simple callback that, when applied until a + // fixedpoint, computes whether a function is reachable from an initial + // set of "root" nodes. + + String source = + "function A(){B()};\n" + + "function B(){C();D()}\n" + + "function C(){B()};\n" + + "function D(){};\n" + + "function E(){C()};\n" + + "function X(){Y()};\n" + + "function Y(){Z()};\n" + + "function Z(){};" + + "B();\n"; + + CallGraph callgraph = compileAndRunBackward(source); + + final Set reachableFunctions = Sets.newHashSet(); + + // We assume the main function and X are our roots + reachableFunctions.add(callgraph.getMainFunction()); + reachableFunctions.add(callgraph.getUniqueFunctionWithName("X")); + + // Propagate reachability from callers to callees + + EdgeCallback edgeCallback = + new EdgeCallback() { + @Override + public boolean traverseEdge(Function caller, Callsite callsite, + Function callee) { + boolean changed; + + if (reachableFunctions.contains(caller)) { + changed = reachableFunctions.add(callee); // Returns true if added + } else { + changed = false; + } + + return changed; + } + }; + + FixedPointGraphTraversal.newTraversal(edgeCallback) + .computeFixedPoint(callgraph.getForwardDirectedGraph()); + + // We expect B, C, D, X, Y, Z and the main function should be reachable. + // A and E should not be reachable. + + assertEquals(7, reachableFunctions.size()); + + assertTrue(reachableFunctions.contains( + callgraph.getUniqueFunctionWithName("B"))); + assertTrue(reachableFunctions.contains( + callgraph.getUniqueFunctionWithName("C"))); + assertTrue(reachableFunctions.contains( + callgraph.getUniqueFunctionWithName("D"))); + assertTrue(reachableFunctions.contains( + callgraph.getUniqueFunctionWithName("X"))); + assertTrue(reachableFunctions.contains( + callgraph.getUniqueFunctionWithName("Y"))); + assertTrue(reachableFunctions.contains( + callgraph.getUniqueFunctionWithName("Z"))); + assertTrue(reachableFunctions.contains( + callgraph.getMainFunction())); + + assertFalse(reachableFunctions.contains( + callgraph.getUniqueFunctionWithName("A"))); + assertFalse(reachableFunctions.contains( + callgraph.getUniqueFunctionWithName("E"))); + } + + public void testFunctionIsMain() { + String source = + "function A(){};\n" + + "A();\n"; + + CallGraph callgraph = compileAndRunForward(source); + + CallGraph.Function mainFunction = callgraph.getMainFunction(); + + assertTrue(mainFunction.isMain()); + assertNotNull(mainFunction.getBodyNode()); + assertTrue(mainFunction.getBodyNode().isBlock()); + + CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); + + assertFalse(functionA.isMain()); + } + + public void testFunctionGetAstNode() { + String source = + "function A(){};\n" + + "A();\n"; + + CallGraph callgraph = compileAndRunForward(source); + + CallGraph.Function mainFunction = callgraph.getMainFunction(); + + // Main function's AST node should be the global block + assertTrue(mainFunction.getAstNode().isBlock()); + + CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); + + // Regular function's AST node should be the function for A + assertTrue(functionA.getAstNode().isFunction()); + assertEquals("A", NodeUtil.getFunctionName(functionA.getAstNode())); + } + + public void testFunctionGetBodyNode() { + String source = + "function A(){};\n" + + "A();\n"; + + CallGraph callgraph = compileAndRunForward(source); + + CallGraph.Function mainFunction = callgraph.getMainFunction(); + + // Main function's body node should its AST node + assertEquals(mainFunction.getAstNode(), mainFunction.getBodyNode()); + + CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); + + // Regular function's body node should be the block for A + assertTrue(functionA.getBodyNode().isBlock()); + assertEquals(NodeUtil.getFunctionBody(functionA.getAstNode()), + functionA.getBodyNode()); + } + + public void testFunctionGetName() { + String source = + "function A(){};\n" + + "A();\n"; + + CallGraph callgraph = compileAndRunForward(source); + + CallGraph.Function mainFunction = callgraph.getMainFunction(); + + // Main function's name should be CallGraph.MAIN_FUNCTION_NAME + assertEquals(CallGraph.MAIN_FUNCTION_NAME, mainFunction.getName()); + + CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); + + // Regular function's name should be its name + assertEquals(NodeUtil.getFunctionName(functionA.getAstNode()), + functionA.getName()); + } + + public void testFunctionGetCallsitesInFunction() { + String source = + "function A(){};\n" + + "function B(){A()};\n" + + "A();\n" + + "B();\n"; + + CallGraph callgraph = compileAndRunForward(source); + + // Main function calls A and B + CallGraph.Function mainFunction = callgraph.getMainFunction(); + List callsiteNamesInMain = + getCallsiteTargetNames(mainFunction.getCallsitesInFunction()); + + assertEquals(2, callsiteNamesInMain.size()); + assertTrue(callsiteNamesInMain.contains("A")); + assertTrue(callsiteNamesInMain.contains("B")); + + // A calls no functions + CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); + assertEquals(0, functionA.getCallsitesInFunction().size()); + + // B calls A + CallGraph.Function functionB = callgraph.getUniqueFunctionWithName("B"); + List callsiteNamesInB = + getCallsiteTargetNames(functionB.getCallsitesInFunction()); + + assertEquals(1, callsiteNamesInB.size()); + assertTrue(callsiteNamesInMain.contains("A")); + } + + public void testFunctionGetCallsitesInFunction_ignoreInnerFunction() { + String source = + "function A(){var B = function(){C();}};\n" + + "function C(){};\n"; + + CallGraph callgraph = compileAndRunForward(source); + + // A calls no functions (and especially not C) + CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); + assertEquals(0, functionA.getCallsitesInFunction().size()); + } + + public void testFunctionGetCallsitesPossiblyTargetingFunction() { + String source = + "function A(){B()};\n" + + "function B(){C();C();};\n" + + "function C(){C()};\n" + + "A();\n"; + + CallGraph callgraph = compileAndRunBackward(source); + + Function main = callgraph.getMainFunction(); + Function functionA = callgraph.getUniqueFunctionWithName("A"); + Function functionB = callgraph.getUniqueFunctionWithName("B"); + Function functionC = callgraph.getUniqueFunctionWithName("C"); + + assertEquals(0, main.getCallsitesPossiblyTargetingFunction().size()); + + Collection callsitesTargetingA = + functionA.getCallsitesPossiblyTargetingFunction(); + + // A is called only from the main function + assertEquals(1, callsitesTargetingA.size()); + assertEquals(main, + callsitesTargetingA.iterator().next().getContainingFunction()); + + Collection callsitesTargetingB = + functionB.getCallsitesPossiblyTargetingFunction(); + + // B is called only from A + assertEquals(1, callsitesTargetingB.size()); + assertEquals(functionA, + callsitesTargetingB.iterator().next().getContainingFunction()); + + Collection callsitesTargetingC = + functionC.getCallsitesPossiblyTargetingFunction(); + + // C is called 3 times: twice from B and once from C + assertEquals(3, callsitesTargetingC.size()); + + Collection expectedFunctionsCallingC = + Sets.newHashSet(functionB.getCallsitesInFunction()); + expectedFunctionsCallingC.addAll(functionC.getCallsitesInFunction()); + + assertTrue(callsitesTargetingC.containsAll(expectedFunctionsCallingC)); + } + + public void testFunctionGetCallsitesInFunction_newIsCallsite() { + String source = + "function A(){};\n" + + "function C(){new A()};\n"; + + CallGraph callgraph = compileAndRunForward(source); + + // The call to new A() in C() should count as a callsite + CallGraph.Function functionC = callgraph.getUniqueFunctionWithName("C"); + assertEquals(1, functionC.getCallsitesInFunction().size()); + } + + public void testFunctionGetIsAliased() { + // Aliased by VAR assignment + String source = + "function A(){};\n" + + "var ns = {};\n" + + "ns.B = function() {};\n" + + "var C = function() {}\n" + + "var D = function() {}\n" + + "var aliasA = A;\n" + + "var aliasB = ns.B;\n" + + "var aliasC = C;\n" + + "D();"; + + compileAndRunForward(source); + + assertFunctionAliased(true, "A"); + assertFunctionAliased(true, "ns.B"); + assertFunctionAliased(true, "C"); + assertFunctionAliased(false, "D"); + + // Aliased by normal assignment + source = + "function A(){};\n" + + "var ns = {};\n" + + "ns.B = function() {};\n" + + "var C = function() {}\n" + + "ns.D = function() {}\n" + + "var aliasA;\n" + + "aliasA = A;\n" + + "var aliasB = {};\n" + + "aliasB.foo = ns.B;\n" + + "var aliasC;\n" + + "aliasC = C;\n" + + "ns.D();"; + + compileAndRunForward(source); + + assertFunctionAliased(true, "A"); + assertFunctionAliased(true, "ns.B"); + assertFunctionAliased(true, "C"); + assertFunctionAliased(false, "ns.D"); + + // Aliased by passing as parameter + source = + "function A(){};\n" + + "var ns = {};\n" + + "ns.B = function() {};\n" + + "var C = function() {}\n" + + "function D() {}\n" + + "var foo = function(a) {}\n" + + "foo(A);\n" + + "foo(ns.B)\n" + + "foo(C);\n" + + "D();"; + + compileAndRunForward(source); + + assertFunctionAliased(true, "A"); + assertFunctionAliased(true, "ns.B"); + assertFunctionAliased(true, "C"); + assertFunctionAliased(false, "D"); + + // Not aliased by being target of call + source = + "function A(){};\n" + + "var ns = {};\n" + + "ns.B = function() {};\n" + + "var C = function() {}\n" + + "A();\n" + + "ns.B();\n" + + "C();\n"; + + compileAndRunForward(source); + + assertFunctionAliased(false, "A"); + assertFunctionAliased(false, "ns.B"); + assertFunctionAliased(false, "C"); + + // Not aliased by GET{PROP,ELEM} + source = + "function A(){};\n" + + "var ns = {};\n" + + "ns.B = function() {};\n" + + "var C = function() {}\n" + + "A.foo;\n" + + "ns.B.prototype;\n" + + "C[0];\n"; + + compileAndRunForward(source); + + assertFunctionAliased(false, "A"); + assertFunctionAliased(false, "ns.B"); + assertFunctionAliased(false, "C"); + } + + public void testFunctionGetIsExposedToCallOrApply() { + // Exposed to call + String source = + "function A(){};\n" + + "function B(){};\n" + + "function C(){};\n" + + "var x;\n" + + "A.call(x);\n" + + "B.apply(x);\n" + + "C();\n"; + + CallGraph callGraph = compileAndRunForward(source); + + Function functionA = callGraph.getUniqueFunctionWithName("A"); + Function functionB = callGraph.getUniqueFunctionWithName("B"); + Function functionC = callGraph.getUniqueFunctionWithName("C"); + + assertTrue(functionA.isExposedToCallOrApply()); + assertTrue(functionB.isExposedToCallOrApply()); + assertFalse(functionC.isExposedToCallOrApply()); + } + + public void testCallsiteGetAstNode() { + String source = + "function A(){B()};\n" + + "function B(){};\n"; + + CallGraph callgraph = compileAndRunForward(source); + + Function functionA = callgraph.getUniqueFunctionWithName("A"); + Callsite callToB = functionA.getCallsitesInFunction().iterator().next(); + + assertTrue(callToB.getAstNode().isCall()); + } + + public void testCallsiteGetContainingFunction() { + String source = + "function A(){B()};\n" + + "function B(){};\n" + + "A();\n"; + + CallGraph callgraph = compileAndRunForward(source); + + Function mainFunction = callgraph.getMainFunction(); + Callsite callToA = mainFunction.getCallsitesInFunction().iterator().next(); + assertEquals(mainFunction, callToA.getContainingFunction()); + + Function functionA = callgraph.getUniqueFunctionWithName("A"); + Callsite callToB = functionA.getCallsitesInFunction().iterator().next(); + assertEquals(functionA, callToB.getContainingFunction()); + } + + public void testCallsiteGetKnownTargets() { + String source = + "function A(){B()};\n" + + "function B(){};\n" + + "A();\n"; + + CallGraph callgraph = compileAndRunForward(source); + + Function mainFunction = callgraph.getMainFunction(); + Function functionA = callgraph.getUniqueFunctionWithName("A"); + Function functionB = callgraph.getUniqueFunctionWithName("B"); + + Callsite callInMain = mainFunction.getCallsitesInFunction().iterator() + .next(); + + Collection targetsOfCallInMain = callInMain.getPossibleTargets(); + + assertEquals(1, targetsOfCallInMain.size()); + assertTrue(targetsOfCallInMain.contains(functionA)); + + Callsite callInA = functionA.getCallsitesInFunction().iterator().next(); + Collection targetsOfCallInA = callInA.getPossibleTargets(); + + assertTrue(targetsOfCallInA.contains(functionB)); + } + + public void testCallsiteHasUnknownTarget() { + String source = + "var A = externalnamespace.prop;\n" + + "function B(){A();};\n" + + "B();\n"; + + CallGraph callgraph = compileAndRunForward(source); + + Function mainFunction = callgraph.getMainFunction(); + Function functionB = callgraph.getUniqueFunctionWithName("B"); + + Callsite callInMain = + mainFunction.getCallsitesInFunction().iterator().next(); + + // B()'s target function is known, and it is functionB + assertFalse(callInMain.hasUnknownTarget()); + assertEquals("B", callInMain.getAstNode().getFirstChild().getString()); + + Callsite callInB = functionB.getCallsitesInFunction().iterator().next(); + + // A() has an unknown target and no known targets + assertTrue(callInB.hasUnknownTarget()); + assertEquals(0, callInB.getPossibleTargets().size()); + } + + public void testCallsiteHasExternTarget() { + String source = + "var A = function(){}\n" + + "function B(){ExternalFunction(6);};\n" + + "A();\n"; + + CallGraph callgraph = compileAndRunForward(source); + + Function mainFunction = callgraph.getMainFunction(); + Function functionB = callgraph.getUniqueFunctionWithName("B"); + + Callsite callInMain = + mainFunction.getCallsitesInFunction().iterator().next(); + + // A()'s target function is not an extern + assertFalse(callInMain.hasExternTarget()); + + Callsite callInB = functionB.getCallsitesInFunction().iterator().next(); + + assertEquals("ExternalFunction", + callInB.getAstNode().getFirstChild().getString()); + + // ExternalFunction(6) is a call to an extern function + assertTrue(callInB.hasExternTarget()); + assertEquals(0, callInB.getPossibleTargets().size()); + } + + public void testThrowForBackwardOpOnForwardGraph() { + String source = + "function A(){B()};\n" + + "function B(){C();C();};\n" + + "function C(){C()};\n" + + "A();\n"; + + CallGraph callgraph = compileAndRunForward(source); + + Function functionA = callgraph.getUniqueFunctionWithName("A"); + + UnsupportedOperationException caughtException = null; + + try { + functionA.getCallsitesPossiblyTargetingFunction(); + } catch (UnsupportedOperationException e) { + caughtException = e; + } + + assertNotNull(caughtException); + } + + public void testThrowForForwardOpOnBackwardGraph() { + String source = + "function A(){B()};\n" + + "function B(){};\n" + + "A();\n"; + + CallGraph callgraph = compileAndRunBackward(source); + + Function mainFunction = callgraph.getMainFunction(); + Function functionA = callgraph.getUniqueFunctionWithName("A"); + + Callsite callInMain = mainFunction.getCallsitesInFunction().iterator() + .next(); + + UnsupportedOperationException caughtException = null; + + try { + callInMain.getPossibleTargets(); + } catch (UnsupportedOperationException e) { + return; + } + fail(); + } + + /** + * Helper function that, given a collection of callsites, returns a + * collection of the names of the target expression nodes, e.g. + * if the callsites are [A(), B.b()], the collection returned is + * ["A", "B"]. + * + * This makes it easier to test methods that return collections of callsites. + * + * An exception is thrown if the callsite target is not a simple name + * (e.g. "a.bar()"). + */ + private List getCallsiteTargetNames(Collection + callsites) { + List result = Lists.newArrayList(); + + for (Callsite callsite : callsites) { + Node targetExpressionNode = callsite.getAstNode().getFirstChild(); + if (targetExpressionNode.isName()) { + result.add(targetExpressionNode.getString()); + } else { + throw new IllegalStateException("Called getCallsiteTargetNames() on " + + "a complex callsite."); + } + } + + return result; + } + + private void assertFunctionAliased(boolean aliased, String name) { + Function function = currentProcessor.getUniqueFunctionWithName(name); + + assertEquals(aliased, function.isAliased()); + } + + private CallGraph compileAndRunBackward(String js) { + return compileAndRun(SHARED_EXTERNS, js, false, true); + } + + private CallGraph compileAndRunForward(String js) { + return compileAndRun(SHARED_EXTERNS, js, true, false); + } + + private CallGraph compileAndRun(String externs, + String js, + boolean forward, + boolean backward) { + + createBackwardCallGraph = backward; + createForwardCallGraph = forward; + + testSame(externs, js, null); + + return currentProcessor; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ChainCallsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ChainCallsTest.java new file mode 100644 index 0000000..16a0078 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ChainCallsTest.java @@ -0,0 +1,114 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + + +/** + * Tests for {@link ChainCalls} + * + */ +public class ChainCallsTest extends CompilerTestCase { + @Override + protected CompilerPass getProcessor(final Compiler compiler) { + return new ChainCalls(compiler); + } + + @Override + protected int getNumRepetitions() { + return 1; + } + + public void testUnchainedCalls() { + test( + "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.bar = function() { return this; };\n" + + "var f = new Foo();\n" + + "f.bar();\n" + + "f.bar();\n", + "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.bar = function() { return this; };\n" + + "var f = new Foo();\n" + + "f.bar().bar();\n"); + + } + + public void testSecondCallReturnNotThis() { + test( + "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.bar = function() { return this; };\n" + + "Foo.prototype.baz = function() {};\n" + + "var f = new Foo();\n" + + "f.bar();\n" + + "f.baz();\n", + "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.bar = function() { return this; };\n" + + "Foo.prototype.baz = function() {};\n" + + "var f = new Foo();\n" + + "f.bar().baz();\n"); + } + + public void testDifferentInstance() { + testSame( + "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.bar = function() { return this; };\n" + + "new Foo().bar();\n" + + "new Foo().bar();\n"); + } + + public void testSubclass() { + testSame( + "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.bar = function() { return this; };\n" + + "/** @constructor\n@extends {Foo} */ function Baz() {}\n" + + "Baz.prototype.bar = function() {};\n" + + "(/** @type {Foo} */ new Baz()).bar();\n" + + "(/** @type {Foo} */ new Baz()).bar();\n"); + } + + public void testSimpleDefinitionFinder() { + String defs = + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a = function() { return this; };" + + "/** @constructor */ function Bar() {}\n" + + "Bar.prototype.a = function() {};"; + testSame( + defs + + "var o = new Foo; o.a(); o.a();"); + testSame( + defs + + "var o = new Bar; o.a(); o.a();"); + } + + public void testSimpleDefinitionFinder2() { + String defs = + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a = function() { return this; };" + + "/** @constructor */ function Bar() {}\n" + + "Bar.prototype.a = function() { return this; };"; + testSame( + defs + + "var o = new Foo; o.a().a();"); + testSame( + defs + + "var o = new Bar; o.a().a();"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckAccessControlsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckAccessControlsTest.java new file mode 100644 index 0000000..2ecc02d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckAccessControlsTest.java @@ -0,0 +1,838 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import static com.google.javascript.jscomp.CheckAccessControls.BAD_PRIVATE_GLOBAL_ACCESS; +import static com.google.javascript.jscomp.CheckAccessControls.BAD_PRIVATE_PROPERTY_ACCESS; +import static com.google.javascript.jscomp.CheckAccessControls.BAD_PROTECTED_PROPERTY_ACCESS; +import static com.google.javascript.jscomp.CheckAccessControls.CONST_PROPERTY_DELETED; +import static com.google.javascript.jscomp.CheckAccessControls.CONST_PROPERTY_REASSIGNED_VALUE; +import static com.google.javascript.jscomp.CheckAccessControls.DEPRECATED_CLASS; +import static com.google.javascript.jscomp.CheckAccessControls.DEPRECATED_CLASS_REASON; +import static com.google.javascript.jscomp.CheckAccessControls.DEPRECATED_NAME; +import static com.google.javascript.jscomp.CheckAccessControls.DEPRECATED_NAME_REASON; +import static com.google.javascript.jscomp.CheckAccessControls.DEPRECATED_PROP; +import static com.google.javascript.jscomp.CheckAccessControls.DEPRECATED_PROP_REASON; +import static com.google.javascript.jscomp.CheckAccessControls.EXTEND_FINAL_CLASS; +import static com.google.javascript.jscomp.CheckAccessControls.PRIVATE_OVERRIDE; +import static com.google.javascript.jscomp.CheckAccessControls.VISIBILITY_MISMATCH; + +/** + * Tests for {@link CheckAccessControls}. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class CheckAccessControlsTest extends CompilerTestCase { + + public CheckAccessControlsTest() { + super(CompilerTypeTestCase.DEFAULT_EXTERNS); + parseTypeInfo = true; + enableTypeCheck(CheckLevel.WARNING); + } + + @Override + protected CompilerPass getProcessor(final Compiler compiler) { + return new CheckAccessControls(compiler); + } + + @Override + protected CompilerOptions getOptions() { + CompilerOptions options = super.getOptions(); + options.setWarningLevel(DiagnosticGroups.ACCESS_CONTROLS, CheckLevel.ERROR); + options.setWarningLevel(DiagnosticGroups.CONSTANT_PROPERTY, + CheckLevel.ERROR); + return options; + } + + /** + * Tests that the given JavaScript code has a @deprecated marker + * somewhere in it which raises an error. Also tests that the + * deprecated marker works with a message. The JavaScript should + * have a JsDoc of the form "@deprecated %s\n". + * + * @param js The JavaScript code to parse and test. + * @param reason A simple deprecation reason string, used for testing + * the addition of a deprecation reason to the @deprecated tag. + * @param error The deprecation error expected when no reason is given. + * @param errorWithMessage The deprecation error expected when a reason + * message is given. + */ + private void testDep(String js, String reason, + DiagnosticType error, + DiagnosticType errorWithMessage) { + + // Test without a reason. + test(String.format(js, ""), null, error); + + // Test with a reason. + test(String.format(js, reason), null, errorWithMessage, null, reason); + } + + public void testDeprecatedFunction() { + testDep("/** @deprecated %s */ function f() {} function g() { f(); }", + "Some Reason", + DEPRECATED_NAME, DEPRECATED_NAME_REASON); + } + + public void testWarningOnDeprecatedConstVariable() { + testDep("/** @deprecated %s */ var f = 4; function g() { alert(f); }", + "Another reason", + DEPRECATED_NAME, DEPRECATED_NAME_REASON); + } + + public void testThatNumbersArentDeprecated() { + testSame("/** @deprecated */ var f = 4; var h = 3; " + + "function g() { alert(h); }"); + } + + public void testDeprecatedFunctionVariable() { + testDep("/** @deprecated %s */ var f = function() {}; " + + "function g() { f(); }", "I like g...", + DEPRECATED_NAME, DEPRECATED_NAME_REASON); + } + + public void testNoWarningInGlobalScope() { + testSame("var goog = {}; goog.makeSingleton = function(x) {};" + + "/** @deprecated */ function f() {} goog.makeSingleton(f);"); + } + + public void testNoWarningInGlobalScopeForCall() { + testDep("/** @deprecated %s */ function f() {} f();", + "Some global scope", DEPRECATED_NAME, DEPRECATED_NAME_REASON); + } + + public void testNoWarningInDeprecatedFunction() { + testSame("/** @deprecated */ function f() {} " + + "/** @deprecated */ function g() { f(); }"); + } + + public void testWarningInNormalClass() { + testDep("/** @deprecated %s */ function f() {}" + + "/** @constructor */ var Foo = function() {}; " + + "Foo.prototype.bar = function() { f(); }", + "FooBar", DEPRECATED_NAME, DEPRECATED_NAME_REASON); + } + + public void testWarningForProperty1() { + testDep("/** @constructor */ function Foo() {}" + + "/** @deprecated %s */ Foo.prototype.bar = 3;" + + "Foo.prototype.baz = function() { alert((new Foo()).bar); };", + "A property is bad", + DEPRECATED_PROP, DEPRECATED_PROP_REASON); + } + + public void testWarningForProperty2() { + testDep("/** @constructor */ function Foo() {}" + + "/** @deprecated %s */ Foo.prototype.bar = 3;" + + "Foo.prototype.baz = function() { alert(this.bar); };", + "Zee prop, it is deprecated!", + DEPRECATED_PROP, + DEPRECATED_PROP_REASON); + } + + public void testWarningForDeprecatedClass() { + testDep("/** @constructor \n* @deprecated %s */ function Foo() {} " + + "function f() { new Foo(); }", + "Use the class 'Bar'", + DEPRECATED_CLASS, + DEPRECATED_CLASS_REASON); + } + + public void testNoWarningForDeprecatedClassInstance() { + testSame("/** @constructor \n * @deprecated */ function Foo() {} " + + "/** @param {Foo} x */ function f(x) { return x; }"); + } + + public void testWarningForDeprecatedSuperClass() { + testDep("/** @constructor \n * @deprecated %s */ function Foo() {} " + + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + + "function f() { new SubFoo(); }", + "Superclass to the rescue!", + DEPRECATED_CLASS, + DEPRECATED_CLASS_REASON); + } + + public void testWarningForDeprecatedSuperClass2() { + testDep("/** @constructor \n * @deprecated %s */ function Foo() {} " + + "var namespace = {}; " + + "/** @constructor \n * @extends {Foo} */ " + + "namespace.SubFoo = function() {}; " + + "function f() { new namespace.SubFoo(); }", + "Its only weakness is Kryptoclass", + DEPRECATED_CLASS, + DEPRECATED_CLASS_REASON); + } + + public void testWarningForPrototypeProperty() { + testDep("/** @constructor */ function Foo() {}" + + "/** @deprecated %s */ Foo.prototype.bar = 3;" + + "Foo.prototype.baz = function() { alert(Foo.prototype.bar); };", + "It is now in production, use that model...", + DEPRECATED_PROP, + DEPRECATED_PROP_REASON); + } + + public void testNoWarningForNumbers() { + testSame("/** @constructor */ function Foo() {}" + + "/** @deprecated */ Foo.prototype.bar = 3;" + + "Foo.prototype.baz = function() { alert(3); };"); + } + + public void testWarningForMethod1() { + testDep("/** @constructor */ function Foo() {}" + + "/** @deprecated %s */ Foo.prototype.bar = function() {};" + + "Foo.prototype.baz = function() { this.bar(); };", + "There is a madness to this method", + DEPRECATED_PROP, + DEPRECATED_PROP_REASON); + } + + public void testWarningForMethod2() { + testDep("/** @constructor */ function Foo() {} " + + "/** @deprecated %s */ Foo.prototype.bar; " + + "Foo.prototype.baz = function() { this.bar(); };", + "Stop the ringing!", + DEPRECATED_PROP, + DEPRECATED_PROP_REASON); + } + + public void testNoWarningInDeprecatedClass() { + testSame("/** @deprecated */ function f() {} " + + "/** @constructor \n * @deprecated */ " + + "var Foo = function() {}; " + + "Foo.prototype.bar = function() { f(); }"); + } + + public void testNoWarningInDeprecatedClass2() { + testSame("/** @deprecated */ function f() {} " + + "/** @constructor \n * @deprecated */ " + + "var Foo = function() {}; " + + "Foo.bar = function() { f(); }"); + } + + public void testNoWarningInDeprecatedStaticMethod() { + testSame("/** @deprecated */ function f() {} " + + "/** @constructor */ " + + "var Foo = function() {}; " + + "/** @deprecated */ Foo.bar = function() { f(); }"); + } + + public void testWarningInStaticMethod() { + testDep("/** @deprecated %s */ function f() {} " + + "/** @constructor */ " + + "var Foo = function() {}; " + + "Foo.bar = function() { f(); }", + "crazy!", + DEPRECATED_NAME, + DEPRECATED_NAME_REASON); + } + + public void testDeprecatedObjLitKey() { + testDep("var f = {}; /** @deprecated %s */ f.foo = 3; " + + "function g() { return f.foo; }", + "It is literally not used anymore", + DEPRECATED_PROP, + DEPRECATED_PROP_REASON); + } + + public void testWarningForSubclassMethod() { + testDep("/** @constructor */ function Foo() {}" + + "Foo.prototype.bar = function() {};" + + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + + "/** @deprecated %s */ SubFoo.prototype.bar = function() {};" + + "function f() { (new SubFoo()).bar(); };", + "I have a parent class!", + DEPRECATED_PROP, + DEPRECATED_PROP_REASON); + } + + public void testWarningForSuperClassWithDeprecatedSubclassMethod() { + testSame("/** @constructor */ function Foo() {}" + + "Foo.prototype.bar = function() {};" + + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + + "/** @deprecated \n * @override */ SubFoo.prototype.bar = " + + "function() {};" + + "function f() { (new Foo()).bar(); };"); + } + + public void testWarningForSuperclassMethod() { + testDep("/** @constructor */ function Foo() {}" + + "/** @deprecated %s */ Foo.prototype.bar = function() {};" + + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + + "SubFoo.prototype.bar = function() {};" + + "function f() { (new SubFoo()).bar(); };", + "I have a child class!", + DEPRECATED_PROP, + DEPRECATED_PROP_REASON); + } + + public void testWarningForSuperclassMethod2() { + testDep("/** @constructor */ function Foo() {}" + + "/** @deprecated %s \n* @protected */" + + "Foo.prototype.bar = function() {};" + + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + + "/** @protected */SubFoo.prototype.bar = function() {};" + + "function f() { (new SubFoo()).bar(); };", + "I have another child class...", + DEPRECATED_PROP, + DEPRECATED_PROP_REASON); + } + + public void testWarningForBind() { + testDep("/** @deprecated %s */ Function.prototype.bind = function() {};" + + "(function() {}).bind();", + "I'm bound to this method...", + DEPRECATED_PROP, + DEPRECATED_PROP_REASON); + } + + public void testWarningForDeprecatedClassInGlobalScope() { + testDep("/** @constructor \n * @deprecated %s */ var Foo = function() {};" + + "new Foo();", + "I'm a very worldly object!", + DEPRECATED_CLASS, + DEPRECATED_CLASS_REASON); + } + + public void testNoWarningForPrototypeCopying() { + testSame("/** @constructor */ var Foo = function() {};" + + "Foo.prototype.bar = function() {};" + + "/** @deprecated */ Foo.prototype.baz = Foo.prototype.bar;" + + "(new Foo()).bar();"); + } + + public void testNoWarningOnDeprecatedPrototype() { + // This used to cause an NPE. + testSame("/** @constructor */ var Foo = function() {};" + + "/** @deprecated */ Foo.prototype = {};" + + "Foo.prototype.bar = function() {};"); + } + + public void testPrivateAccessForNames() { + testSame("/** @private */ function foo_() {}; foo_();"); + test(new String[] { + "/** @private */ function foo_() {};", + "foo_();" + }, null, BAD_PRIVATE_GLOBAL_ACCESS); + } + + public void testPrivateAccessForProperties1() { + testSame("/** @constructor */ function Foo() {}" + + "/** @private */ Foo.prototype.bar_ = function() {};" + + "Foo.prototype.baz = function() { this.bar_(); }; (new Foo).bar_();"); + } + + public void testPrivateAccessForProperties2() { + testSame(new String[] { + "/** @constructor */ function Foo() {}", + "/** @private */ Foo.prototype.bar_ = function() {};" + + "Foo.prototype.baz = function() { this.bar_(); }; (new Foo).bar_();" + }); + } + + public void testPrivateAccessForProperties3() { + testSame(new String[] { + "/** @constructor */ function Foo() {}" + + "/** @private */ Foo.prototype.bar_ = function() {}; (new Foo).bar_();", + "Foo.prototype.baz = function() { this.bar_(); };" + }); + } + + public void testPrivateAccessForProperties4() { + testSame(new String[] { + "/** @constructor */ function Foo() {}" + + "/** @private */ Foo.prototype.bar_ = function() {};", + "Foo.prototype['baz'] = function() { (new Foo()).bar_(); };" + }); + } + + public void testNoPrivateAccessForProperties1() { + test(new String[] { + "/** @constructor */ function Foo() {} (new Foo).bar_();", + "/** @private */ Foo.prototype.bar_ = function() {};" + + "Foo.prototype.baz = function() { this.bar_(); };" + }, null, BAD_PRIVATE_PROPERTY_ACCESS); + } + + public void testNoPrivateAccessForProperties2() { + test(new String[] { + "/** @constructor */ function Foo() {} " + + "/** @private */ Foo.prototype.bar_ = function() {};" + + "Foo.prototype.baz = function() { this.bar_(); };", + "(new Foo).bar_();" + }, null, BAD_PRIVATE_PROPERTY_ACCESS); + } + + public void testNoPrivateAccessForProperties3() { + test(new String[] { + "/** @constructor */ function Foo() {} " + + "/** @private */ Foo.prototype.bar_ = function() {};", + "/** @constructor */ function OtherFoo() { (new Foo).bar_(); }" + }, null, BAD_PRIVATE_PROPERTY_ACCESS); + } + + public void testNoPrivateAccessForProperties4() { + test(new String[] { + "/** @constructor */ function Foo() {} " + + "/** @private */ Foo.prototype.bar_ = function() {};", + "/** @constructor \n * @extends {Foo} */ " + + "function SubFoo() { this.bar_(); }" + }, null, BAD_PRIVATE_PROPERTY_ACCESS); + } + + public void testNoPrivateAccessForProperties5() { + test(new String[] { + "/** @constructor */ function Foo() {} " + + "/** @private */ Foo.prototype.bar_ = function() {};", + "/** @constructor \n * @extends {Foo} */ " + + "function SubFoo() {};" + + "SubFoo.prototype.baz = function() { this.bar_(); }" + }, null, BAD_PRIVATE_PROPERTY_ACCESS); + } + + public void testNoPrivateAccessForProperties6() { + // Overriding a private property with a non-private property + // in a different file causes problems. + test(new String[] { + "/** @constructor */ function Foo() {} " + + "/** @private */ Foo.prototype.bar_ = function() {};", + "/** @constructor \n * @extends {Foo} */ " + + "function SubFoo() {};" + + "SubFoo.prototype.bar_ = function() {};" + }, null, BAD_PRIVATE_PROPERTY_ACCESS); + } + + public void testNoPrivateAccessForProperties7() { + // It's OK to override a private property with a non-private property + // in the same file, but you'll get yelled at when you try to use it. + test(new String[] { + "/** @constructor */ function Foo() {} " + + "/** @private */ Foo.prototype.bar_ = function() {};" + + "/** @constructor \n * @extends {Foo} */ " + + "function SubFoo() {};" + + "SubFoo.prototype.bar_ = function() {};", + "SubFoo.prototype.baz = function() { this.bar_(); }" + }, null, BAD_PRIVATE_PROPERTY_ACCESS); + } + + public void testNoPrivateAccessForProperties8() { + test(new String[] { + "/** @constructor */ function Foo() { /** @private */ this.bar_ = 3; }", + "/** @constructor \n * @extends {Foo} */ " + + "function SubFoo() { /** @private */ this.bar_ = 3; };" + }, null, PRIVATE_OVERRIDE); + } + + public void testProtectedAccessForProperties1() { + testSame(new String[] { + "/** @constructor */ function Foo() {}" + + "/** @protected */ Foo.prototype.bar = function() {};" + + "(new Foo).bar();", + "Foo.prototype.baz = function() { this.bar(); };" + }); + } + + public void testProtectedAccessForProperties2() { + testSame(new String[] { + "/** @constructor */ function Foo() {}" + + "/** @protected */ Foo.prototype.bar = function() {};" + + "(new Foo).bar();", + "/** @constructor \n * @extends {Foo} */" + + "function SubFoo() { this.bar(); }" + }); + } + + public void testProtectedAccessForProperties3() { + testSame(new String[] { + "/** @constructor */ function Foo() {}" + + "/** @protected */ Foo.prototype.bar = function() {};" + + "(new Foo).bar();", + "/** @constructor \n * @extends {Foo} */" + + "function SubFoo() { }" + + "SubFoo.baz = function() { (new Foo).bar(); }" + }); + } + + public void testProtectedAccessForProperties4() { + testSame(new String[] { + "/** @constructor */ function Foo() {}" + + "/** @protected */ Foo.bar = function() {};", + "/** @constructor \n * @extends {Foo} */" + + "function SubFoo() { Foo.bar(); }" + }); + } + + public void testProtectedAccessForProperties5() { + testSame(new String[] { + "/** @constructor */ function Foo() {}" + + "/** @protected */ Foo.prototype.bar = function() {};" + + "(new Foo).bar();", + "/** @constructor \n * @extends {Foo} */" + + "var SubFoo = function() { this.bar(); }" + }); + } + + public void testProtectedAccessForProperties6() { + testSame(new String[] { + "var goog = {};" + + "/** @constructor */ goog.Foo = function() {};" + + "/** @protected */ goog.Foo.prototype.bar = function() {};", + "/** @constructor \n * @extends {goog.Foo} */" + + "goog.SubFoo = function() { this.bar(); };" + }); + } + + public void testNoProtectedAccessForProperties1() { + test(new String[] { + "/** @constructor */ function Foo() {} " + + "/** @protected */ Foo.prototype.bar = function() {};", + "(new Foo).bar();" + }, null, BAD_PROTECTED_PROPERTY_ACCESS); + } + + public void testNoProtectedAccessForProperties2() { + test(new String[] { + "/** @constructor */ function Foo() {} " + + "/** @protected */ Foo.prototype.bar = function() {};", + "/** @constructor */ function OtherFoo() { (new Foo).bar(); }" + }, null, BAD_PROTECTED_PROPERTY_ACCESS); + } + + public void testNoProtectedAccessForProperties3() { + test(new String[] { + "/** @constructor */ function Foo() {} " + + "/** @constructor \n * @extends {Foo} */ " + + "function SubFoo() {}" + + "/** @protected */ SubFoo.prototype.bar = function() {};", + "/** @constructor \n * @extends {Foo} */ " + + "function SubberFoo() { (new SubFoo).bar(); }" + }, null, BAD_PROTECTED_PROPERTY_ACCESS); + } + + public void testNoProtectedAccessForProperties4() { + test(new String[] { + "/** @constructor */ function Foo() { (new SubFoo).bar(); } ", + "/** @constructor \n * @extends {Foo} */ " + + "function SubFoo() {}" + + "/** @protected */ SubFoo.prototype.bar = function() {};", + }, null, BAD_PROTECTED_PROPERTY_ACCESS); + } + + public void testNoProtectedAccessForProperties5() { + test(new String[] { + "var goog = {};" + + "/** @constructor */ goog.Foo = function() {};" + + "/** @protected */ goog.Foo.prototype.bar = function() {};", + "/** @constructor */" + + "goog.NotASubFoo = function() { (new goog.Foo).bar(); };" + }, null, BAD_PROTECTED_PROPERTY_ACCESS); + } + + public void testNoExceptionsWithBadConstructors1() { + testSame(new String[] { + "function Foo() { (new SubFoo).bar(); } " + + "/** @constructor */ function SubFoo() {}" + + "/** @protected */ SubFoo.prototype.bar = function() {};" + }); + } + + public void testNoExceptionsWithBadConstructors2() { + testSame(new String[] { + "/** @constructor */ function Foo() {} " + + "Foo.prototype.bar = function() {};" + + "/** @constructor */" + + "function SubFoo() {}" + + "/** @protected */ " + + "SubFoo.prototype.bar = function() { (new Foo).bar(); };" + }); + } + + public void testGoodOverrideOfProtectedProperty() { + testSame(new String[] { + "/** @constructor */ function Foo() { } " + + "/** @protected */ Foo.prototype.bar = function() {};", + "/** @constructor \n * @extends {Foo} */ " + + "function SubFoo() {}" + + "/** @inheritDoc */ SubFoo.prototype.bar = function() {};", + }); + } + + public void testBadOverrideOfProtectedProperty() { + test(new String[] { + "/** @constructor */ function Foo() { } " + + "/** @protected */ Foo.prototype.bar = function() {};", + "/** @constructor \n * @extends {Foo} */ " + + "function SubFoo() {}" + + "/** @private */ SubFoo.prototype.bar = function() {};", + }, null, VISIBILITY_MISMATCH); + } + + public void testBadOverrideOfPrivateProperty() { + test(new String[] { + "/** @constructor */ function Foo() { } " + + "/** @private */ Foo.prototype.bar = function() {};", + "/** @constructor \n * @extends {Foo} */ " + + "function SubFoo() {}" + + "/** @protected */ SubFoo.prototype.bar = function() {};", + }, null, PRIVATE_OVERRIDE); + + testSame(new String[] { + "/** @constructor */ function Foo() { } " + + "/** @private */ Foo.prototype.bar = function() {};", + "/** @constructor \n * @extends {Foo} */ " + + "function SubFoo() {}" + + "/** @override \n *@suppress{visibility} */\n" + + " SubFoo.prototype.bar = function() {};", + }); + } + + public void testAccessOfStaticMethodOnPrivateConstructor() { + testSame(new String[] { + "/** @constructor \n * @private */ function Foo() { } " + + "Foo.create = function() { return new Foo(); };", + "Foo.create()", + }); + } + + public void testAccessOfStaticMethodOnPrivateQualifiedConstructor() { + testSame(new String[] { + "var goog = {};" + + "/** @constructor \n * @private */ goog.Foo = function() { }; " + + "goog.Foo.create = function() { return new goog.Foo(); };", + "goog.Foo.create()", + }); + } + + public void testInstanceofOfPrivateConstructor() { + testSame(new String[] { + "var goog = {};" + + "/** @constructor \n * @private */ goog.Foo = function() { }; " + + "goog.Foo.create = function() { return new goog.Foo(); };", + "goog instanceof goog.Foo", + }); + } + + public void testOkAssignmentOfDeprecatedProperty() { + testSame( + "/** @constructor */ function Foo() {" + + " /** @deprecated */ this.bar = 3;" + + "}"); + } + + public void testBadReadOfDeprecatedProperty() { + testDep( + "/** @constructor */ function Foo() {" + + " /** @deprecated %s */ this.bar = 3;" + + " this.baz = this.bar;" + + "}", + "GRR", + DEPRECATED_PROP, + DEPRECATED_PROP_REASON); + } + + public void testAutoboxedDeprecatedProperty() { + test( + "", // no externs + "/** @constructor */ function String() {}" + + "/** @deprecated %s */ String.prototype.length;" + + "function f() { return 'x'.length; }", + "GRR", + DEPRECATED_PROP_REASON, + null); + } + + public void testAutoboxedPrivateProperty() { + test( + "/** @constructor */ function String() {}" + + "/** @private */ String.prototype.length;", // externs + "function f() { return 'x'.length; }", + "", // output + BAD_PRIVATE_PROPERTY_ACCESS, + null); + } + + public void testNullableDeprecatedProperty() { + testDep( + "/** @constructor */ function Foo() {}" + + "/** @deprecated %s */ Foo.prototype.length;" + + "/** @param {?Foo} x */ function f(x) { return x.length; }", + "GRR", + DEPRECATED_PROP, + DEPRECATED_PROP_REASON); + } + + public void testNullablePrivateProperty() { + test(new String[] { + "/** @constructor */ function Foo() {}" + + "/** @private */ Foo.prototype.length;", + "/** @param {?Foo} x */ function f(x) { return x.length; }" + }, null, BAD_PRIVATE_PROPERTY_ACCESS); + } + + public void testConstantProperty1() { + test("/** @constructor */ function A() {" + + "/** @const */ this.bar = 3;}" + + "/** @constructor */ function B() {" + + "/** @const */ this.bar = 3;this.bar += 4;}", + null, CONST_PROPERTY_REASSIGNED_VALUE); + } + + public void testConstantProperty2() { + test("/** @constructor */ function Foo() {}" + + "/** @const */ Foo.prototype.prop = 2;" + + "var foo = new Foo();" + + "foo.prop = 3;", + null , CONST_PROPERTY_REASSIGNED_VALUE); + } + + public void testConstantProperty3() { + testSame("var o = { /** @const */ x: 1 };" + + "o.x = 2;"); + } + + public void testConstantProperty4() { + test("/** @constructor */ function cat(name) {}" + + "/** @const */ cat.test = 1;" + + "cat.test *= 2;", + null, CONST_PROPERTY_REASSIGNED_VALUE); + } + + public void testConstantProperty5() { + test("/** @constructor */ function Foo() { this.prop = 1;}" + + "/** @const */ Foo.prototype.prop;" + + "Foo.prototype.prop = 2", + null , CONST_PROPERTY_REASSIGNED_VALUE); + } + + public void testConstantProperty6() { + test("/** @constructor */ function Foo() { this.prop = 1;}" + + "/** @const */ Foo.prototype.prop = 2;", + null , CONST_PROPERTY_REASSIGNED_VALUE); + } + + public void testConstantProperty7() { + testSame("/** @constructor */ function Foo() {} " + + "Foo.prototype.bar_ = function() {};" + + "/** @constructor \n * @extends {Foo} */ " + + "function SubFoo() {};" + + "/** @const */ /** @override */ SubFoo.prototype.bar_ = function() {};" + + "SubFoo.prototype.baz = function() { this.bar_(); }"); + } + + public void testConstantProperty8() { + testSame("var o = { /** @const */ x: 1 };" + + "var y = o.x;"); + } + + public void testConstantProperty9() { + testSame("/** @constructor */ function A() {" + + "/** @const */ this.bar = 3;}" + + "/** @constructor */ function B() {" + + "this.bar = 4;}"); + } + + public void testConstantProperty10() { + testSame("/** @constructor */ function Foo() { this.prop = 1;}" + + "/** @const */ Foo.prototype.prop;"); + } + + public void testConstantProperty11() { + test("/** @constructor */ function Foo() {}" + + "/** @const */ Foo.prototype.bar;" + + "/**\n" + + " * @constructor\n" + + " * @extends {Foo}\n" + + " */ function SubFoo() { this.bar = 5; this.bar = 6; }", + null , CONST_PROPERTY_REASSIGNED_VALUE); + } + + public void testConstantProperty12() { + testSame("/** @constructor */ function Foo() {}" + + "/** @const */ Foo.prototype.bar;" + + "/**\n" + + " * @constructor\n" + + " * @extends {Foo}\n" + + " */ function SubFoo() { this.bar = 5; }" + + "/**\n" + + " * @constructor\n" + + " * @extends {Foo}\n" + + " */ function SubFoo2() { this.bar = 5; }"); + } + + public void testConstantProperty13() { + test("/** @constructor */ function Foo() {}" + + "/** @const */ Foo.prototype.bar;" + + "/**\n" + + " * @constructor\n" + + " * @extends {Foo}\n" + + " */ function SubFoo() { this.bar = 5; }" + + "/**\n" + + " * @constructor\n" + + " * @extends {SubFoo}\n" + + " */ function SubSubFoo() { this.bar = 5; }", + null , CONST_PROPERTY_REASSIGNED_VALUE); + } + + public void testConstantProperty14() { + test("/** @constructor */ function Foo() {" + + "/** @const */ this.bar = 3; delete this.bar; }", + null, CONST_PROPERTY_DELETED); + } + + public void testSuppressConstantProperty() { + testSame("/** @constructor */ function A() {" + + "/** @const */ this.bar = 3;}" + + "/**\n" + + " * @suppress {constantProperty}\n" + + " * @constructor\n" + + " */ function B() {" + + "/** @const */ this.bar = 3;this.bar += 4;}"); + } + + public void testSuppressConstantProperty2() { + testSame("/** @constructor */ function A() {" + + "/** @const */ this.bar = 3;}" + + "/**\n" + + " * @suppress {const}\n" + + " * @constructor\n" + + " */ function B() {" + + "/** @const */ this.bar = 3;this.bar += 4;}"); + } + + public void testFinalClassCannotBeSubclassed() { + test( + "/**\n" + + " * @constructor\n" + + " * @const\n" + + " */ Foo = function() {};\n" + + "/**\n" + + " * @constructor\n" + + " * @extends {Foo}\n*" + + " */ Bar = function() {};", + null, EXTEND_FINAL_CLASS); + test( + "/**\n" + + " * @constructor\n" + + " * @const\n" + + " */ function Foo() {};\n" + + "/**\n" + + " * @constructor\n" + + " * @extends {Foo}\n*" + + " */ function Bar() {};", + null, EXTEND_FINAL_CLASS); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckDebuggerStatementTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckDebuggerStatementTest.java new file mode 100644 index 0000000..e2b6925 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckDebuggerStatementTest.java @@ -0,0 +1,87 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.CompilerOptions; +import com.google.javascript.jscomp.DiagnosticGroups; + +/** + * {@link CheckDebuggerStatementTest} is a unit test for + * {@link CheckDebuggerStatement}. + * + * @author bolinfest@google.com (Michael Bolin) + */ +public class CheckDebuggerStatementTest extends CompilerTestCase { + + private CheckLevel checkLevel; + + @Override + public void tearDown() { + checkLevel = null; + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new CheckDebuggerStatement(compiler); + } + + @Override + protected CompilerOptions getOptions() { + CompilerOptions options = super.getOptions(); + if (checkLevel != null) { + options.setWarningLevel( + DiagnosticGroups.DEBUGGER_STATEMENT_PRESENT, + checkLevel); + } + return options; + } + + public void testCheckDebuggerStatement() { + checkLevel = CheckLevel.WARNING; + + testSame("debugger;", CheckDebuggerStatement.DEBUGGER_STATEMENT_PRESENT); + testSame("function foo() { debugger; }", + CheckDebuggerStatement.DEBUGGER_STATEMENT_PRESENT); + } + + public void testCheckIsDisabledByDefault() { + checkLevel = null; + + testSame("debugger;"); + testSame("function foo() { debugger; }"); + } + + public void testNoWarningWhenExplicitlyDisabled() { + checkLevel = CheckLevel.OFF; + + testSame("debugger;"); + testSame("function foo() { debugger; }"); + } + + public void testCheckDebuggerKeywordMayAppearInComments() { + checkLevel = CheckLevel.WARNING; + + test("// I like the debugger; it is helpful.", ""); + } + + public void testCheckDebuggerStatementInEval() { + checkLevel = CheckLevel.WARNING; + + testSame("eval('debugger');"); + } + +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckGlobalNamesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckGlobalNamesTest.java new file mode 100644 index 0000000..1050872 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckGlobalNamesTest.java @@ -0,0 +1,339 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import static com.google.javascript.jscomp.CheckGlobalNames.NAME_DEFINED_LATE_WARNING; +import static com.google.javascript.jscomp.CheckGlobalNames.UNDEFINED_NAME_WARNING; +import static com.google.javascript.jscomp.CheckGlobalNames.STRICT_MODULE_DEP_QNAME; + +import com.google.javascript.rhino.Node; + +/** + * Tests for {@code CheckGlobalNames.java}. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class CheckGlobalNamesTest extends CompilerTestCase { + + private boolean injectNamespace = false; + + public CheckGlobalNamesTest() { + super("function alert() {}" + + "/** @constructor */ function Object(){}" + + "Object.prototype.hasOwnProperty = function() {};" + + "/** @constructor */ function Function(){}" + + "Function.prototype.call = function() {};"); + } + + @Override + protected CompilerPass getProcessor(final Compiler compiler) { + final CheckGlobalNames checkGlobalNames = new CheckGlobalNames( + compiler, CheckLevel.WARNING); + if (injectNamespace) { + return new CompilerPass() { + @Override + public void process(Node externs, Node js) { + checkGlobalNames.injectNamespace( + new GlobalNamespace(compiler, externs, js)) + .process(externs, js); + } + }; + } else { + return checkGlobalNames; + } + } + + @Override + public void setUp() { + injectNamespace = false; + STRICT_MODULE_DEP_QNAME.level = CheckLevel.WARNING; + } + + private static final String GET_NAMES = + "var a = {get d() {return 1}}; a.b = 3; a.c = {get e() {return 5}};"; + private static final String SET_NAMES = + "var a = {set d(x) {}}; a.b = 3; a.c = {set e(y) {}};"; + private static final String NAMES = "var a = {d: 1}; a.b = 3; a.c = {e: 5};"; + + public void testRefToDefinedProperties1() { + testSame(NAMES + "alert(a.b); alert(a.c.e);"); + testSame(GET_NAMES + "alert(a.b); alert(a.c.e);"); + testSame(SET_NAMES + "alert(a.b); alert(a.c.e);"); + } + + public void testRefToDefinedProperties2() { + testSame(NAMES + "a.x={}; alert(a.c);"); + testSame(GET_NAMES + "a.x={}; alert(a.c);"); + testSame(SET_NAMES + "a.x={}; alert(a.c);"); + } + + public void testRefToDefinedProperties3() { + testSame(NAMES + "alert(a.d);"); + testSame(GET_NAMES + "alert(a.d);"); + testSame(SET_NAMES + "alert(a.d);"); + } + + public void testRefToMethod1() { + testSame("function foo() {}; foo.call();"); + } + + public void testRefToMethod2() { + testSame("function foo() {}; foo.call.call();"); + } + + public void testCallUndefinedFunctionGivesNoWaring() { + // We don't bother checking undeclared variables--there's another + // pass that does this already. + testSame("foo();"); + } + + public void testRefToPropertyOfAliasedName() { + // this is OK, because "a" was aliased + testSame(NAMES + "alert(a); alert(a.x);"); + } + + public void testRefToUndefinedProperty1() { + testSame(NAMES + "alert(a.x);", UNDEFINED_NAME_WARNING); + } + + public void testRefToUndefinedProperty2() { + testSame(NAMES + "a.x();", UNDEFINED_NAME_WARNING); + } + + public void testRefToUndefinedProperty3() { + testSame(NAMES + "alert(a.c.x);", UNDEFINED_NAME_WARNING); + testSame(GET_NAMES + "alert(a.c.x);", UNDEFINED_NAME_WARNING); + testSame(SET_NAMES + "alert(a.c.x);", UNDEFINED_NAME_WARNING); + } + + public void testRefToUndefinedProperty4() { + testSame(NAMES + "alert(a.d.x);"); + testSame(GET_NAMES + "alert(a.d.x);"); + testSame(SET_NAMES + "alert(a.d.x);"); + } + + public void testRefToDescendantOfUndefinedProperty1() { + testSame(NAMES + "var c = a.x.b;", UNDEFINED_NAME_WARNING); + } + + public void testRefToDescendantOfUndefinedProperty2() { + testSame(NAMES + "a.x.b();", UNDEFINED_NAME_WARNING); + } + + public void testRefToDescendantOfUndefinedProperty3() { + testSame(NAMES + "a.x.b = 3;", UNDEFINED_NAME_WARNING); + } + + public void testUndefinedPrototypeMethodRefGivesNoWarning() { + testSame("function Foo() {} var a = new Foo(); a.bar();"); + } + + public void testComplexPropAssignGivesNoWarning() { + testSame("var a = {}; var b = a.b = 3;"); + } + + public void testTypedefGivesNoWarning() { + testSame("var a = {}; /** @typedef {number} */ a.b;"); + } + + public void testRefToDescendantOfUndefinedPropertyGivesCorrectWarning() { + testSame("", NAMES + "a.x.b = 3;", UNDEFINED_NAME_WARNING, + UNDEFINED_NAME_WARNING.format("a.x")); + } + + public void testNamespaceInjection() { + injectNamespace = true; + testSame(NAMES + "var c = a.x.b;", UNDEFINED_NAME_WARNING); + } + + public void testSuppressionOfUndefinedNamesWarning() { + testSame(new String[] { + NAMES + + "/** @constructor */ function Foo() { };" + + "/** @suppress {undefinedNames} */" + + "Foo.prototype.bar = function() {" + + " alert(a.x);" + + " alert(a.x.b());" + + " a.x();" + + " var c = a.x.b;" + + " var c = a.x.b();" + + " a.x.b();" + + " a.x.b = 3;" + + "};", + }); + } + + public void testNoWarningForSimpleVarModuleDep1() { + testSame(createModuleChain( + NAMES, + "var c = a;" + )); + } + + public void testNoWarningForSimpleVarModuleDep2() { + testSame(createModuleChain( + "var c = a;", + NAMES + )); + } + public void testNoWarningForGoodModuleDep1() { + testSame(createModuleChain( + NAMES, + "var c = a.b;" + )); + } + + public void testBadModuleDep1() { + testSame(createModuleChain( + "var c = a.b;", + NAMES + ), STRICT_MODULE_DEP_QNAME); + } + + public void testBadModuleDep2() { + testSame(createModuleStar( + NAMES, + "a.xxx = 3;", + "var x = a.xxx;" + ), STRICT_MODULE_DEP_QNAME); + } + + public void testSelfModuleDep() { + testSame(createModuleChain( + NAMES + "var c = a.b;" + )); + } + + public void testUndefinedModuleDep1() { + testSame(createModuleChain( + "var c = a.xxx;", + NAMES + ), UNDEFINED_NAME_WARNING); + } + + public void testLateDefinedName1() { + testSame("x.y = {}; var x = {};", NAME_DEFINED_LATE_WARNING); + } + + public void testLateDefinedName2() { + testSame("var x = {}; x.y.z = {}; x.y = {};", NAME_DEFINED_LATE_WARNING); + } + + public void testLateDefinedName3() { + testSame("var x = {}; x.y.z = {}; x.y = {z: {}};", + NAME_DEFINED_LATE_WARNING); + } + + public void testLateDefinedName4() { + testSame("var x = {}; x.y.z.bar = {}; x.y = {z: {}};", + NAME_DEFINED_LATE_WARNING); + } + + public void testLateDefinedName5() { + testSame("var x = {}; /** @typedef {number} */ x.y.z; x.y = {};", + NAME_DEFINED_LATE_WARNING); + } + + public void testLateDefinedName6() { + testSame( + "var x = {}; x.y.prototype.z = 3;" + + "/** @constructor */ x.y = function() {};", + NAME_DEFINED_LATE_WARNING); + } + + public void testOkLateDefinedName1() { + testSame("function f() { x.y = {}; } var x = {};"); + } + + public void testOkLateDefinedName2() { + testSame("var x = {}; function f() { x.y.z = {}; } x.y = {};"); + } + + public void testPathologicalCaseThatsOkAnyway() { + testSame( + "var x = {};" + + "switch (x) { " + + " default: x.y.z = {}; " + + " case (x.y = {}): break;" + + "}", NAME_DEFINED_LATE_WARNING); + } + + public void testOkGlobalDeclExpr() { + testSame("var x = {}; /** @type {string} */ x.foo;"); + } + + public void testBadInterfacePropRef() { + testSame( + "/** @interface */ function F() {}" + + "F.bar();", + UNDEFINED_NAME_WARNING); + } + + public void testInterfaceFunctionPropRef() { + testSame( + "/** @interface */ function F() {}" + + "F.call(); F.hasOwnProperty('z');"); + } + + public void testObjectPrototypeProperties() { + testSame("var x = {}; var y = x.hasOwnProperty('z');"); + } + + public void testCustomObjectPrototypeProperties() { + testSame("Object.prototype.seal = function() {};" + + "var x = {}; x.seal();"); + } + + public void testFunctionPrototypeProperties() { + testSame("var x = {}; var y = x.hasOwnProperty('z');"); + } + + public void testIndirectlyDeclaredProperties() { + testSame( + "Function.prototype.inherits = function(ctor) {" + + " this.superClass_ = ctor;" + + "};" + + "/** @constructor */ function Foo() {}" + + "Foo.prototype.bar = function() {};" + + "/** @constructor */ function SubFoo() {}" + + "SubFoo.inherits(Foo);" + + "SubFoo.superClass_.bar();"); + } + + public void testGoogInheritsAlias() { + testSame( + "Function.prototype.inherits = function(ctor) {" + + " this.superClass_ = ctor;" + + "};" + + "/** @constructor */ function Foo() {}" + + "Foo.prototype.bar = function() {};" + + "/** @constructor */ function SubFoo() {}" + + "SubFoo.inherits(Foo);" + + "SubFoo.superClass_.bar();"); + } + + public void testGoogInheritsAlias2() { + testSame( + CompilerTypeTestCase.CLOSURE_DEFS + + "/** @constructor */ function Foo() {}" + + "Foo.prototype.bar = function() {};" + + "/** @constructor */ function SubFoo() {}" + + "goog.inherits(SubFoo, Foo);" + + "SubFoo.superClazz();", + UNDEFINED_NAME_WARNING); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckGlobalThisTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckGlobalThisTest.java new file mode 100644 index 0000000..4232649 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckGlobalThisTest.java @@ -0,0 +1,244 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Tests {@link CheckGlobalThis}. + */ +public class CheckGlobalThisTest extends CompilerTestCase { + public CheckGlobalThisTest() { + this.parseTypeInfo = true; + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new CombinedCompilerPass( + compiler, new CheckGlobalThis(compiler)); + } + + private void testFailure(String js) { + testSame(js, CheckGlobalThis.GLOBAL_THIS); + } + + public void testGlobalThis1() throws Exception { + testSame("var a = this;"); + } + + public void testGlobalThis2() { + testFailure("this.foo = 5;"); + } + + public void testGlobalThis3() { + testFailure("this[foo] = 5;"); + } + + public void testGlobalThis4() { + testFailure("this['foo'] = 5;"); + } + + public void testGlobalThis5() { + testFailure("(a = this).foo = 4;"); + } + + public void testGlobalThis6() { + testSame("a = this;"); + } + + public void testGlobalThis7() { + testFailure("var a = this.foo;"); + } + + public void testStaticFunction1() { + testSame("function a() { return this; }"); + } + + public void testStaticFunction2() { + testFailure("function a() { this.complex = 5; }"); + } + + public void testStaticFunction3() { + testSame("var a = function() { return this; }"); + } + + public void testStaticFunction4() { + testFailure("var a = function() { this.foo.bar = 6; }"); + } + + public void testStaticFunction5() { + testSame("function a() { return function() { return this; } }"); + } + + public void testStaticFunction6() { + testSame("function a() { return function() { this.x = 8; } }"); + } + + public void testStaticFunction7() { + testSame("var a = function() { return function() { this.x = 8; } }"); + } + + public void testStaticFunction8() { + testFailure("var a = function() { return this.foo; };"); + } + + public void testConstructor1() { + testSame("/** @constructor */function A() { this.m2 = 5; }"); + } + + public void testConstructor2() { + testSame("/** @constructor */var A = function() { this.m2 = 5; }"); + } + + public void testConstructor3() { + testSame("/** @constructor */a.A = function() { this.m2 = 5; }"); + } + + public void testInterface1() { + testSame( + "/** @interface */function A() { /** @type {string} */ this.m2; }"); + } + + public void testOverride1() { + testSame("/** @constructor */function A() { } var a = new A();" + + "/** @override */ a.foo = function() { this.bar = 5; };"); + } + + public void testThisJSDoc1() throws Exception { + testSame("/** @this whatever */function h() { this.foo = 56; }"); + } + + public void testThisJSDoc2() throws Exception { + testSame("/** @this whatever */var h = function() { this.foo = 56; }"); + } + + public void testThisJSDoc3() throws Exception { + testSame("/** @this whatever */foo.bar = function() { this.foo = 56; }"); + } + + public void testThisJSDoc4() throws Exception { + testSame("/** @this whatever */function f() { this.foo = 56; }"); + } + + public void testThisJSDoc5() throws Exception { + testSame("function a() { /** @this x */function f() { this.foo = 56; } }"); + } + + public void testMethod1() { + testSame("A.prototype.m1 = function() { this.m2 = 5; }"); + } + + public void testMethod2() { + testSame("a.B.prototype.m1 = function() { this.m2 = 5; }"); + } + + public void testMethod3() { + testSame("a.b.c.D.prototype.m1 = function() { this.m2 = 5; }"); + } + + public void testMethod4() { + testSame("a.prototype['x' + 'y'] = function() { this.foo = 3; };"); + } + + public void testPropertyOfMethod() { + testFailure("a.protoype.b = {}; " + + "a.prototype.b.c = function() { this.foo = 3; };"); + } + + public void testStaticMethod1() { + testFailure("a.b = function() { this.m2 = 5; }"); + } + + public void testStaticMethod2() { + testSame("a.b = function() { return function() { this.m2 = 5; } }"); + } + + public void testStaticMethod3() { + testSame("a.b.c = function() { return function() { this.m2 = 5; } }"); + } + + public void testMethodInStaticFunction() { + testSame("function f() { A.prototype.m1 = function() { this.m2 = 5; } }"); + } + + public void testStaticFunctionInMethod1() { + testSame("A.prototype.m1 = function() { function me() { this.m2 = 5; } }"); + } + + public void testStaticFunctionInMethod2() { + testSame("A.prototype.m1 = function() {" + + " function me() {" + + " function myself() {" + + " function andI() { this.m2 = 5; } } } }"); + } + + public void testInnerFunction1() { + testFailure("function f() { function g() { return this.x; } }"); + } + + public void testInnerFunction2() { + testFailure("function f() { var g = function() { return this.x; } }"); + } + + public void testInnerFunction3() { + testFailure( + "function f() { var x = {}; x.y = function() { return this.x; } }"); + } + + public void testInnerFunction4() { + testSame( + "function f() { var x = {}; x.y(function() { return this.x; }); }"); + } + + public void testIssue182a() { + testFailure("var NS = {read: function() { return this.foo; }};"); + } + + public void testIssue182b() { + testFailure("var NS = {write: function() { this.foo = 3; }};"); + } + + public void testIssue182c() { + testFailure("var NS = {}; NS.write2 = function() { this.foo = 3; };"); + } + + public void testIssue182d() { + testSame("function Foo() {} " + + "Foo.prototype = {write: function() { this.foo = 3; }};"); + } + + public void testLendsAnnotation1() { + testFailure("/** @constructor */ function F() {}" + + "dojo.declare(F, {foo: function() { return this.foo; }});"); + } + + public void testLendsAnnotation2() { + testFailure("/** @constructor */ function F() {}" + + "dojo.declare(F, /** @lends {F.bar} */ (" + + " {foo: function() { return this.foo; }}));"); + } + + public void testLendsAnnotation3() { + testSame("/** @constructor */ function F() {}" + + "dojo.declare(F, /** @lends {F.prototype} */ (" + + " {foo: function() { return this.foo; }}));"); + } + + public void testSuppressWarning() { + testFailure("var x = function() { this.complex = 5; };"); + testSame("/** @suppress {globalThis} */" + + "var x = function() { this.complex = 5; };"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckMissingGetCssNameTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckMissingGetCssNameTest.java new file mode 100644 index 0000000..1b789f4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckMissingGetCssNameTest.java @@ -0,0 +1,70 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * @author mkretzschmar@google.com (Martin Kretzschmar) + */ +public class CheckMissingGetCssNameTest extends CompilerTestCase { + @Override + protected CompilerPass getProcessor(final Compiler compiler) { + return new CombinedCompilerPass( + compiler, + new CheckMissingGetCssName(compiler, CheckLevel.ERROR, "goog-[a-z-]*")); + } + + public void testMissingGetCssName() { + testMissing("var s = 'goog-inline-block'"); + testMissing("var s = 'CSS_FOO goog-menu'"); + testMissing("alert('goog-inline-block ' + goog.getClassName('CSS_FOO'))"); + testMissing("html = '
      Hello
      '"); + } + + public void testRecognizeGetCssName() { + testNotMissing("var s = goog.getCssName('goog-inline-block')"); + } + + public void testIgnoreGetUniqueIdArguments() { + testNotMissing("var s = goog.events.getUniqueId('goog-some-event')"); + testNotMissing("var s = joe.random.getUniqueId('joe-is-a-goob')"); + } + + public void testIgnoreAssignmentsToIdConstant() { + testNotMissing("SOME_ID = 'goog-some-id'"); + testNotMissing("SOME_PRIVATE_ID_ = 'goog-some-id'"); + testNotMissing("var SOME_ID_ = 'goog-some-id'"); + } + + public void testNotMissingGetCssName() { + testNotMissing("s = 'not-a-css-name'"); + testNotMissing("s = 'notagoog-css-name'"); + } + + public void testDontCrashIfTheresNoQualifiedName() { + testMissing("things[2].DONT_CARE_ABOUT_THIS_KIND_OF_ID = " + + "'goog-inline-block'"); + testMissing("objects[3].doSomething('goog-inline-block')"); + } + + private void testMissing(String js) { + test(js, js, CheckMissingGetCssName.MISSING_GETCSSNAME); + } + + private void testNotMissing(String js) { + test(js, js); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckMissingReturnTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckMissingReturnTest.java new file mode 100644 index 0000000..3a33aae --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckMissingReturnTest.java @@ -0,0 +1,222 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.CheckLevel; + +/** + * Tests for {@link CheckMissingReturn}. + * + */ +public class CheckMissingReturnTest extends CompilerTestCase { + + public CheckMissingReturnTest() { + enableTypeCheck(CheckLevel.OFF); + } + + @Override + protected CompilerPass getProcessor(final Compiler compiler) { + return new CombinedCompilerPass(compiler, + new CheckMissingReturn(compiler, CheckLevel.ERROR)); + } + + public void testMissingReturn() { + // Requires control flow analysis. + testMissing("if (a) { return 1; }"); + + // Switch statement. + testMissing("switch(1) { case 12: return 5; }"); + + // Test try catch finally. + testMissing("try { foo() } catch (e) { return 5; } finally { }"); + + // Nested scope. + testMissing("/** @return {number} */ function f() { var x; }; return 1;"); + testMissing("/** @return {number} */ function f() { return 1; };"); + } + + public void testReturnNotMissing() { + // Empty function body. Ignore this case. The remainder of the functions in + // this test have non-empty bodies. + testNotMissing(""); + + // Simple cases. + testSame("function f() { var x; }"); + testNotMissing("return 1;"); + + // Returning void and undefined. + testNotMissing("void", "var x;"); + testNotMissing("undefined", "var x;"); + + // Returning a union that includes void or undefined. + testNotMissing("number|undefined", "var x;"); + testNotMissing("number|void", "var x;"); + testNotMissing("(number,void)", "var x;"); + testNotMissing("(number,undefined)", "var x;"); + testNotMissing("*", "var x;"); + + // Test try catch finally. + testNotMissing("try { return foo() } catch (e) { } finally { }"); + + // Nested function. + testNotMissing( + "/** @return {number} */ function f() { return 1; }; return 1;"); + + // Strange tests that come up when reviewing closure code. + testNotMissing("try { return 12; } finally { return 62; }"); + testNotMissing("try { } finally { return 1; }"); + testNotMissing("switch(1) { default: return 1; }"); + testNotMissing("switch(g) { case 1: return 1; default: return 2; }"); + } + + public void testFinallyStatements() { + // The control flow analysis (CFA) treats finally blocks somewhat strangely. + // The CFA might indicate that a finally block implicitly returns. However, + // if entry into the finally block is normally caused by an explicit return + // statement, then a return statement isn't missing: + // + // try { + // return 1; + // } finally { + // // CFA determines implicit return. However, return not missing + // // because of return statement in try block. + // } + // + // Hence extra tests are warranted for various cases involving finally + // blocks. + + // Simple finally case. + testNotMissing("try { return 1; } finally { }"); + testNotMissing("try { } finally { return 1; }"); + testMissing("try { } finally { }"); + + // Cycles in the CFG within the finally block were causing problems before. + testNotMissing("try { return 1; } finally { while (true) { } }"); + testMissing("try { } finally { while (x) { } }"); + testMissing("try { } finally { while (x) { if (x) { break; } } }"); + testNotMissing( + "try { return 2; } finally { while (x) { if (x) { break; } } }"); + + // Test various cases with nested try statements. + testMissing("try { } finally { try { } finally { } }"); + testNotMissing("try { } finally { try { return 1; } finally { } }"); + testNotMissing("try { return 1; } finally { try { } finally { } }"); + + // Calling a function potentially causes control flow to transfer to finally + // block. However, the function will not return in this case as the + // exception will unwind the stack. Hence this function isn't missing a + // return statement (i.e., the running program will not expect a return + // value from the function if an exception is thrown). + testNotMissing("try { g(); return 1; } finally { }"); + + // Closures within try ... finally affect missing return statement analysis + // because of the nested scopes. The following tests check for missing + // return statements in the three possible configurations: both scopes + // return; enclosed doesn't return; enclosing doesn't return. + testNotMissing( + "try {" + + " /** @return {number} */ function f() {" + + " try { return 1; }" + + " finally { }" + + " };" + + " return 1;" + + "}" + + "finally { }"); + testMissing( + "try {" + + " /** @return {number} */ function f() {" + + " try { }" + + " finally { }" + + " };" + + " return 1;" + + "}" + + "finally { }"); + testMissing( + "try {" + + " /** @return {number} */ function f() {" + + " try { return 1; }" + + " finally { }" + + " };" + + "}" + + "finally { }"); + } + + public void testKnownConditions() { + testNotMissing("if (true) return 1"); + testMissing("if (true) {} else {return 1}"); + + testMissing("if (false) return 1"); + testNotMissing("if (false) {} else {return 1}"); + + testNotMissing("if (1) return 1"); + testMissing("if (1) {} else {return 1}"); + + testMissing("if (0) return 1"); + testNotMissing("if (0) {} else {return 1}"); + + testNotMissing("if (3) return 1"); + testMissing("if (3) {} else {return 1}"); + } + + public void testKnownWhileLoop() { + testNotMissing("while (1) return 1"); + testNotMissing("while (1) { if (x) {return 1} else {return 1}}"); + testNotMissing("while (0) {} return 1"); + + // TODO(user): The current algorithm will not detect this case. It is + // still computable in most cases. + testNotMissing("while (1) {} return 0"); + testMissing("while (false) return 1"); + + // Not known. + testMissing("while(x) { return 1 }"); + } + + public void testMultiConditions() { + testMissing("if (a) { } else { while (1) {return 1} }"); + testNotMissing("if (a) { return 1} else { while (1) {return 1} }"); + } + + public void testIssue779() { + testNotMissing( + "var a = f(); try { alert(); if (a > 0) return 1; }" + + "finally { a = 5; } return 2;"); + } + + private static String createFunction(String returnType, String body) { + return "/** @return {" + returnType + "} */ function foo() {" + body + "}"; + } + + private void testMissing(String returnType, String body) { + String js = createFunction(returnType, body); + test(js, js, CheckMissingReturn.MISSING_RETURN_STATEMENT); + } + + private void testNotMissing(String returnType, String body) { + testSame(createFunction(returnType, body)); + } + + /** Creates function with return type {number} */ + private void testNotMissing(String body) { + testNotMissing("number", body); + } + + /** Creates function with return type {number} */ + private void testMissing(String body) { + testMissing("number", body); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckPathsBetweenNodesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckPathsBetweenNodesTest.java new file mode 100644 index 0000000..6b765aa --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckPathsBetweenNodesTest.java @@ -0,0 +1,340 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.javascript.jscomp.graph.LinkedDirectedGraph; +import com.google.javascript.jscomp.graph.DiGraph; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; + +import junit.framework.TestCase; + +/** + * Tests for {@link CheckPathsBetweenNodes}. + * + */ +public class CheckPathsBetweenNodesTest extends TestCase { + + /** + * Predicate satisfied by strings with a given prefix. + */ + private static class PrefixPredicate implements Predicate { + String prefix; + + PrefixPredicate(String prefix) { + this.prefix = prefix; + } + + @Override + public boolean apply(String input) { + return input.startsWith(prefix); + } + } + + private static final Predicate FALSE = Predicates.alwaysFalse(); + + private static final Predicate> ALL_EDGE = + Predicates.alwaysTrue(); + + private static final Predicate> NO_EDGE = + Predicates.alwaysFalse(); + + /** Tests straight-line graphs. */ + public void testSimple() { + DiGraph g = LinkedDirectedGraph.create(); + g.createDirectedGraphNode("a"); + g.createDirectedGraphNode("b"); + g.createDirectedGraphNode("c"); + g.createDirectedGraphNode("d"); + + g.connect("a", "-", "b"); + g.connect("b", "-", "c"); + g.connect("c", "-", "d"); + g.connect("a", "x", "d"); + + CheckPathsBetweenNodes test = null; + + // Simple case: the sole path from a to d has a matching node. + assertGood(createTest(g, "a", "d", Predicates.equalTo("b"), edgeIs("-"))); + //Test two edge cases where satisfying node is the first and last node on + // the path. + assertGood(createTest(g, "a", "d", Predicates.equalTo("a"), edgeIs("-"))); + assertGood(createTest(g, "a", "d", Predicates.equalTo("d"), edgeIs("-"))); + + // Traverse no edges, so no paths. + assertGood(createTest(g, "a", "d", FALSE, NO_EDGE)); + + // No path with matching edges contains b. + assertBad(createTest(g, "a", "d", Predicates.equalTo("b"), edgeIs("x"))); + } + + /** + * Tests a graph where some paths between the nodes are valid and others + * are invalid. + */ + public void testSomeValidPaths() { + DiGraph g = LinkedDirectedGraph.create(); + g.createDirectedGraphNode("a"); + g.createDirectedGraphNode("b"); + g.createDirectedGraphNode("c"); + g.createDirectedGraphNode("d"); + g.createDirectedGraphNode("e"); + + g.connect("a", "1", "b"); + g.connect("b", "2", "c"); + g.connect("b", "3", "e"); + g.connect("e", "4", "d"); + g.connect("c", "5", "d"); + + assertBad(createTest(g, "a", "d", Predicates.equalTo("c"), ALL_EDGE)); + assertBad(createTest(g, "a", "d", Predicates.equalTo("z"), ALL_EDGE)); + } + + /** Tests a graph with many valid paths. */ + public void testManyValidPaths() { + DiGraph g = LinkedDirectedGraph.create(); + g.createDirectedGraphNode("a"); + g.createDirectedGraphNode("b"); + g.createDirectedGraphNode("c1"); + g.createDirectedGraphNode("c2"); + g.createDirectedGraphNode("c3"); + DiGraphNode d = g.createDirectedGraphNode("d"); + + g.connect("a", "-", "b"); + g.connect("b", "-", "c1"); + g.connect("b", "-", "c2"); + g.connect("c2", "-", "d"); + g.connect("c1", "-", "d"); + g.connect("a", "-", "c3"); + g.connect("c3", "-", "d"); + + assertGood(createTest(g, "a", "d", new PrefixPredicate("c"), ALL_EDGE)); + } + + /** Tests a graph with some cycles. */ + public void testCycles1() { + DiGraph g = LinkedDirectedGraph.create(); + g.createDirectedGraphNode("a"); + g.createDirectedGraphNode("b"); + g.createDirectedGraphNode("c"); + g.createDirectedGraphNode("d"); + g.createDirectedGraphNode("e"); + g.createDirectedGraphNode("f"); + + g.connect("a", "-", "b"); + g.connect("b", "-", "c"); + g.connect("c", "-", "d"); + g.connect("d", "-", "e"); + g.connect("e", "-", "f"); + g.connect("f", "-", "b"); + + assertGood(createTest(g, "a", "e", Predicates.equalTo("b"), ALL_EDGE)); + assertGood(createTest(g, "a", "e", Predicates.equalTo("c"), ALL_EDGE)); + assertGood(createTest(g, "a", "e", Predicates.equalTo("d"), ALL_EDGE)); + assertGood(createTest(g, "a", "e", Predicates.equalTo("e"), ALL_EDGE)); + assertBad(createTest(g, "a", "e", Predicates.equalTo("f"), ALL_EDGE)); + } + + /** + * Tests another graph with cycles. The topology of this graph was inspired + * by a control flow graph that was being incorrectly analyzed by an early + * version of CheckPathsBetweenNodes. + */ + public void testCycles2() { + DiGraph g = LinkedDirectedGraph.create(); + g.createDirectedGraphNode("a"); + g.createDirectedGraphNode("b"); + g.createDirectedGraphNode("c"); + g.createDirectedGraphNode("d"); + + g.connect("a", "-", "b"); + g.connect("b", "-", "c"); + g.connect("c", "-", "b"); + g.connect("b", "-", "d"); + + assertGood(createTest(g, "a", "d", Predicates.equalTo("a"), ALL_EDGE)); + assertBad(createTest(g, "a", "d", Predicates.equalTo("z"), ALL_EDGE)); + } + + /** + * Tests another graph with cycles. The topology of this graph was inspired + * by a control flow graph that was being incorrectly analyzed by an early + * version of CheckPathsBetweenNodes. + */ + public void testCycles3() { + DiGraph g = LinkedDirectedGraph.create(); + g.createDirectedGraphNode("a"); + g.createDirectedGraphNode("b"); + g.createDirectedGraphNode("c"); + g.createDirectedGraphNode("d"); + + g.connect("a", "-", "b"); + g.connect("b", "-", "c"); + g.connect("c", "-", "b"); + g.connect("b", "-", "d"); + g.connect("c", "-", "d"); + + assertGood(createTest(g, "a", "d", Predicates.equalTo("a"), ALL_EDGE)); + assertBad(createTest(g, "a", "d", Predicates.equalTo("z"), ALL_EDGE)); + } + + + /** + * Much of the tests are done by testing all paths. We quickly verified + * that some paths are indeed correct for the some path case. + */ + public void testSomePath1() { + DiGraph g = LinkedDirectedGraph.create(); + g.createDirectedGraphNode("a"); + g.createDirectedGraphNode("b"); + g.createDirectedGraphNode("c"); + g.createDirectedGraphNode("d"); + + g.connect("a", "-", "b"); + g.connect("a", "-", "c"); + g.connect("b", "-", "d"); + g.connect("c", "-", "d"); + + assertTrue(createTest(g, "a", "d", Predicates.equalTo("b"), ALL_EDGE) + .somePathsSatisfyPredicate()); + assertTrue(createTest(g, "a", "d", Predicates.equalTo("c"), ALL_EDGE) + .somePathsSatisfyPredicate()); + assertTrue(createTest(g, "a", "d", Predicates.equalTo("a"), ALL_EDGE) + .somePathsSatisfyPredicate()); + assertTrue(createTest(g, "a", "d", Predicates.equalTo("d"), ALL_EDGE) + .somePathsSatisfyPredicate()); + assertFalse(createTest(g, "a", "d", Predicates.equalTo("NONE"), ALL_EDGE) + .somePathsSatisfyPredicate()); + } + + public void testSomePath2() { + // No Paths between nodes, by definition, always false. + DiGraph g = LinkedDirectedGraph.create(); + g.createDirectedGraphNode("a"); + g.createDirectedGraphNode("b"); + + assertFalse(createTest(g, "a", "b", Predicates.equalTo("b"), ALL_EDGE) + .somePathsSatisfyPredicate()); + assertFalse(createTest(g, "a", "b", Predicates.equalTo("d"), ALL_EDGE) + .somePathsSatisfyPredicate()); + assertTrue(createTest(g, "a", "b", Predicates.equalTo("a"), ALL_EDGE) + .somePathsSatisfyPredicate()); + } + + public void testSomePathRevisiting() { + DiGraph g = LinkedDirectedGraph.create(); + g.createDirectedGraphNode("1"); + g.createDirectedGraphNode("2a"); + g.createDirectedGraphNode("2b"); + g.createDirectedGraphNode("3"); + g.createDirectedGraphNode("4a"); + g.createDirectedGraphNode("4b"); + g.createDirectedGraphNode("5"); + g.connect("1", "-", "2a"); + g.connect("1", "-", "2b"); + g.connect("2a", "-", "3"); + g.connect("2b", "-", "3"); + g.connect("3", "-", "4a"); + g.connect("3", "-", "4b"); + g.connect("4a", "-", "5"); + g.connect("4b", "-", "5"); + + CountingPredicate p = + new CountingPredicate(Predicates.equalTo("4a")); + + assertTrue(createTest(g, "1", "5", p, ALL_EDGE) + .somePathsSatisfyPredicate()); + + // Make sure we are not doing more traversals than we have to. + assertEquals(4, p.count); + } + + public void testNonInclusive() { + // No Paths between nodes, by definition, always false. + DiGraph g = LinkedDirectedGraph.create(); + g.createDirectedGraphNode("a"); + g.createDirectedGraphNode("b"); + g.createDirectedGraphNode("c"); + g.connect("a", "-", "b"); + g.connect("b", "-", "c"); + assertFalse(createNonInclusiveTest(g, "a", "b", + Predicates.equalTo("a"), ALL_EDGE).somePathsSatisfyPredicate()); + assertFalse(createNonInclusiveTest(g, "a", "b", + Predicates.equalTo("b"), ALL_EDGE).somePathsSatisfyPredicate()); + assertTrue(createNonInclusiveTest(g, "a", "c", + Predicates.equalTo("b"), ALL_EDGE).somePathsSatisfyPredicate()); + } + + private static void assertGood(CheckPathsBetweenNodes test) { + assertTrue(test.allPathsSatisfyPredicate()); + } + + private static void assertBad(CheckPathsBetweenNodes test) { + assertFalse(test.allPathsSatisfyPredicate()); + } + + private static CheckPathsBetweenNodes createTest( + DiGraph graph, + String entry, + String exit, + Predicate nodePredicate, + Predicate> edgePredicate) { + return new CheckPathsBetweenNodes(graph, + graph.getDirectedGraphNode(entry), graph.getDirectedGraphNode(exit), + nodePredicate, edgePredicate); + } + + private static CheckPathsBetweenNodes + createNonInclusiveTest( + DiGraph graph, + String entry, + String exit, + Predicate nodePredicate, + Predicate> edgePredicate) { + return new CheckPathsBetweenNodes(graph, + graph.getDirectedGraphNode(entry), graph.getDirectedGraphNode(exit), + nodePredicate, edgePredicate, false); + } + + private static Predicate> + edgeIs(final Object val) { + return new Predicate>() { + @Override + public boolean apply(DiGraphEdge input) { + return input.getValue().equals(val); + } + }; + } + + private static class CountingPredicate implements Predicate { + + private int count = 0; + private final Predicate delegate; + + private CountingPredicate(Predicate delegate) { + this.delegate = delegate; + } + @Override + public boolean apply(T input) { + count ++; + return delegate.apply(input); + } + + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckProvidesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckProvidesTest.java new file mode 100644 index 0000000..b6b49b5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckProvidesTest.java @@ -0,0 +1,90 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import static com.google.javascript.jscomp.CheckProvides.MISSING_PROVIDE_WARNING; + +import com.google.javascript.jscomp.CheckLevel; + +/** + * Tests for {@link CheckProvides}. + * + */ +public class CheckProvidesTest extends CompilerTestCase { + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new CheckProvides(compiler, CheckLevel.WARNING); + } + + public void testIrrelevant() { + testSame("var str = 'g4';"); + } + + public void testHarmlessProcedural() { + testSame("goog.provide('X'); /** @constructor */ function X(){};"); + } + + public void testHarmless() { + String js = "goog.provide('X'); /** @constructor */ X = function(){};"; + testSame(js); + } + + public void testNoProvideInnerClass() { + testSame( + "goog.provide('X');\n" + + "/** @constructor */ function X(){};" + + "/** @constructor */ X.Y = function(){};"); + } + + public void testMissingGoogProvide(){ + String[] js = new String[]{"/** @constructor */ X = function(){};"}; + String warning = "missing goog.provide('X')"; + test(js, js, null, MISSING_PROVIDE_WARNING, warning); + } + + public void testMissingGoogProvideWithNamespace(){ + String[] js = new String[]{"goog = {}; " + + "/** @constructor */ goog.X = function(){};"}; + String warning = "missing goog.provide('goog.X')"; + test(js, js, null, MISSING_PROVIDE_WARNING, warning); + } + + public void testGoogProvideInWrongFileShouldCreateWarning(){ + String bad = "/** @constructor */ X = function(){};"; + String good = "goog.provide('X'); goog.provide('Y');" + + "/** @constructor */ X = function(){};" + + "/** @constructor */ Y = function(){};"; + String[] js = new String[] {good, bad}; + String warning = "missing goog.provide('X')"; + test(js, js, null, MISSING_PROVIDE_WARNING, warning); + } + + public void testGoogProvideMissingConstructorIsOkForNow(){ + // TODO(user) to prevent orphan goog.provide calls, the pass would have to + // account for enums, static functions and constants + testSame(new String[]{"goog.provide('Y'); X = function(){};"}); + } + + public void testIgnorePrivateConstructor() { + String js = "/** @constructor*/ X_ = function(){};"; + testSame(js); + } + + public void testIgnorePrivatelyAnnotatedConstructor() { + testSame("/** @private\n@constructor */ X = function(){};"); + testSame("/** @constructor\n@private */ X = function(){};"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckRegExpTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckRegExpTest.java new file mode 100644 index 0000000..3f95526 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckRegExpTest.java @@ -0,0 +1,72 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class CheckRegExpTest extends CompilerTestCase { + CheckRegExp last = null; + + public CheckRegExpTest() { + super("var RegExp;", true); + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + compiler.options.setWarningLevel( + DiagnosticGroups.CHECK_REGEXP, CheckLevel.WARNING); + last = new CheckRegExp(compiler); + return last; + } + + private void testReference(String code, boolean expected) { + testSame(code, (expected) ? CheckRegExp.REGEXP_REFERENCE : null); + assertEquals(expected, last.isGlobalRegExpPropertiesUsed()); + } + + public void testRegExp() { + // Creating regexp's is ok + testReference("RegExp();", false); + testReference("var x = RegExp();", false); + testReference("new RegExp();", false); + testReference("var x = new RegExp();", false); + + // Checking for RegExp instances is OK, as well. + testReference("x instanceof RegExp;", false); + + // Any other reference isn't + testReference("RegExp.test();", true); + testReference("var x = RegExp.test();", true); + testReference("RegExp.exec();", true); + testReference("RegExp.$1;", true); + testReference("RegExp.foobar;", true); + testReference("delete RegExp;", true); + + // Aliases aren't allowed + testReference("var x = RegExp;", true); + testReference("f(RegExp);", true); + testReference("new f(RegExp);", true); + testReference("var x = RegExp; x.test()", true); + + // No RegExp reference is ok + testReference("var x;", false); + + // Local RegExp is ok + testReference("function f() {var RegExp; RegExp.test();}", false); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckRequiresForConstructorsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckRequiresForConstructorsTest.java new file mode 100644 index 0000000..793262b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckRequiresForConstructorsTest.java @@ -0,0 +1,258 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import static com.google.javascript.jscomp.CheckRequiresForConstructors.MISSING_REQUIRE_WARNING; + +import com.google.common.collect.ImmutableList; +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.Result; + +/** + * Tests for {@link CheckRequiresForConstructors}. + * + */ +public class CheckRequiresForConstructorsTest extends CompilerTestCase { + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new CheckRequiresForConstructors(compiler, CheckLevel.WARNING); + } + + public void testPassWithNoNewNodes() { + String js = "var str = 'g4'; /* does not use new */"; + testSame(js); + } + + public void testPassWithOneNew() { + String js = + "var goog = {};" + + "goog.require('foo.bar.goo'); var bar = new foo.bar.goo();"; + testSame(js); + } + + public void testPassWithOneNewOuterClass() { + String js = + "var goog = {};" + + "goog.require('goog.foo.Bar'); var bar = new goog.foo.Bar.Baz();"; + testSame(js); + } + + public void testPassWithOneNewOuterClassWithUpperPrefix() { + String js = + "var goog = {};" + + "goog.require('goog.foo.IDBar'); var bar = new goog.foo.IDBar.Baz();"; + testSame(js); + } + + public void testFailWithOneNew() { + String[] js = new String[] {"var foo = {}; var bar = new foo.bar();"}; + String warning = "'foo.bar' used but not goog.require'd"; + test(js, js, null, MISSING_REQUIRE_WARNING, warning); + } + + public void testPassWithTwoNewNodes() { + String js = + "var goog = {};" + + "goog.require('goog.foo.Bar');goog.require('goog.foo.Baz');" + + "var str = new goog.foo.Bar('g4'), num = new goog.foo.Baz(5); "; + testSame(js); + } + + public void testPassWithNestedNewNodes() { + String js = + "var goog = {}; goog.require('goog.foo.Bar'); " + + "var str = new goog.foo.Bar(new goog.foo.Bar('5')); "; + testSame(js); + } + + public void testFailWithNestedNewNodes() { + String[] js = + new String[] {"var goog = {}; goog.require('goog.foo.Bar'); " + + "var str = new goog.foo.Bar(new goog.foo.Baz('5')); "}; + String warning = "'goog.foo.Baz' used but not goog.require'd"; + test(js, js, null, MISSING_REQUIRE_WARNING, warning); + } + + public void testPassWithLocalFunctions() { + String js = + "/** @constructor */ function tempCtor() {}; var foo = new tempCtor();"; + testSame(js); + } + + public void testPassWithLocalVariables() { + String js = + "/** @constructor */ var nodeCreator = function() {};" + + "var newNode = new nodeCreator();"; + testSame(js); + } + + public void testFailWithLocalVariableInMoreThanOneFile() { + // there should be a warning for the 2nd script because it is only declared + // in the 1st script + String localVar = + "/** @constructor */ function tempCtor() {}" + + "function baz(){" + " /** @constructor */ function tempCtor() {}; " + + "var foo = new tempCtor();}"; + String[] js = new String[] {localVar, " var foo = new tempCtor();"}; + String warning = "'tempCtor' used but not goog.require'd"; + test(js, js, null, MISSING_REQUIRE_WARNING, warning); + } + + public void testNewNodesMetaTraditionalFunctionForm() { + // the class in this script creates an instance of itself + // there should be no warning because the class should not have to + // goog.require itself . + String js = + "/** @constructor */ function Bar(){}; " + + "Bar.prototype.bar = function(){ return new Bar();};"; + testSame(js); + } + + public void testNewNodesMeta() { + String js = + "var goog = {};" + + "/** @constructor */goog.ui.Option = function(){};" + + "goog.ui.Option.optionDecorator = function(){" + + " return new goog.ui.Option(); };"; + testSame(js); + } + + public void testShouldWarnWhenInstantiatingObjectsDefinedInGlobalScope() { + // there should be a warning for the 2nd script because + // Bar was declared in the 1st file, not the 2nd + String good = + "/** @constructor */ function Bar(){}; " + + "Bar.prototype.bar = function(){return new Bar();};"; + String bad = "/** @constructor */ function Foo(){ var bar = new Bar();}"; + String[] js = new String[] {good, bad}; + String warning = "'Bar' used but not goog.require'd"; + test(js, js, null, MISSING_REQUIRE_WARNING, warning); + } + + public void testShouldWarnWhenInstantiatingGlobalClassesFromGlobalScope() { + // there should be a warning for the 2nd script because Baz + // was declared in the first file, not the 2nd + String good = + "/** @constructor */ function Baz(){}; " + + "Baz.prototype.bar = function(){return new Baz();};"; + String bad = "var baz = new Baz()"; + String[] js = new String[] {good, bad}; + String warning = "'Baz' used but not goog.require'd"; + test(js, js, null, MISSING_REQUIRE_WARNING, warning); + } + + public void testIgnoresNativeObject() { + String externs = "/** @constructor */ function String(val) {}"; + String js = "var str = new String('4');"; + test(externs, js, js, null, null); + } + + public void testNewNodesWithMoreThanOneFile() { + // Bar is created, and goog.require()ed, but in different files. + String[] js = new String[] { + "var goog = {};" + + "/** @constructor */ function Bar() {}" + + "goog.require('Bar');", + "var bar = new Bar();"}; + String warning = "'Bar' used but not goog.require'd"; + test(js, js, null, MISSING_REQUIRE_WARNING, warning); + } + + public void testPassWithoutWarningsAndMultipleFiles() { + String[] js = new String[] { + "var goog = {};" + + "goog.require('Foo'); var foo = new Foo();", + "goog.require('Bar'); var bar = new Bar();"}; + testSame(js); + } + + public void testFailWithWarningsAndMultipleFiles() { + /* goog.require is in the code base, but not in the correct file */ + String[] js = new String[] { + "var goog = {};" + + "/** @constructor */ function Bar() {}" + + "goog.require('Bar');", + "var bar = new Bar();"}; + String warning = "'Bar' used but not goog.require'd"; + test(js, js, null, MISSING_REQUIRE_WARNING, warning); + } + + public void testCanStillCallNumberWithoutNewOperator() { + String externs = "/** @constructor */ function Number(opt_value) {}"; + String js = "var n = Number('42');"; + test(externs, js, js, null, null); + js = "var n = Number();"; + test(externs, js, js, null, null); + } + + public void testRequiresAreCaughtBeforeProcessed() { + String js = "var foo = {}; var bar = new foo.bar.goo();"; + SourceFile input = SourceFile.fromCode("foo.js", js); + Compiler compiler = new Compiler(); + CompilerOptions opts = new CompilerOptions(); + opts.checkRequires = CheckLevel.WARNING; + opts.closurePass = true; + + Result result = compiler.compile(ImmutableList.of(), + ImmutableList.of(input), opts); + JSError[] warnings = result.warnings; + assertNotNull(warnings); + assertTrue(warnings.length > 0); + + String expectation = "'foo.bar.goo' used but not goog.require'd"; + + for (JSError warning : warnings) { + if (expectation.equals(warning.description)) { + return; + } + } + + fail("Could not find the following warning:" + expectation); + } + + public void testNoWarningsForThisConstructor() { + String js = + "var goog = {};" + + "/** @constructor */goog.Foo = function() {};" + + "goog.Foo.bar = function() {" + + " return new this.constructor; " + + "};"; + testSame(js); + } + + public void testBug2062487() { + testSame( + "var goog = {};" + + "/** @constructor */goog.Foo = function() {" + + " /** @constructor */ this.x_ = function() {};" + + " this.y_ = new this.x_();" + + "};"); + } + + public void testIgnoreDuplicateWarningsForSingleClasses(){ + // no use telling them the same thing twice + String[] js = new String[]{ + "var goog = {};" + + "/** @constructor */goog.Foo = function() {};" + + "goog.Foo.bar = function(){" + + " var first = new goog.Forgot();" + + " var second = new goog.Forgot();" + + "};"}; + String warning = "'goog.Forgot' used but not goog.require'd"; + test(js, js, null, MISSING_REQUIRE_WARNING, warning); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckSideEffectsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckSideEffectsTest.java new file mode 100644 index 0000000..e6ba218 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckSideEffectsTest.java @@ -0,0 +1,125 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.CheckLevel; + +public class CheckSideEffectsTest extends CompilerTestCase { + public CheckSideEffectsTest() { + this.parseTypeInfo = true; + allowExternsChanges(true); + } + + @Override + protected int getNumRepetitions() { + return 1; + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new CheckSideEffects(compiler, CheckLevel.WARNING, true); + } + + @Override + public void test(String js, String expected, DiagnosticType warning) { + test(js, expected, null, warning); + } + + public void test(String js, DiagnosticType warning) { + test(js, js, null, warning); + } + + final DiagnosticType e = CheckSideEffects.USELESS_CODE_ERROR; + final DiagnosticType ok = null; // no warning + + public void testUselessCode() { + test("function f(x) { if(x) return; }", ok); + test("function f(x) { if(x); }", "function f(x) { if(x); }", e); + + test("if(x) x = y;", ok); + test("if(x) x == bar();", "if(x) JSCOMPILER_PRESERVE(x == bar());", e); + + test("x = 3;", ok); + test("x == 3;", "JSCOMPILER_PRESERVE(x == 3);", e); + + test("var x = 'test'", ok); + test("var x = 'test'\n'str'", + "var x = 'test'\nJSCOMPILER_PRESERVE('str')", e); + + test("", ok); + test("foo();;;;bar();;;;", ok); + + test("var a, b; a = 5, b = 6", ok); + test("var a, b; a = 5, b == 6", + "var a, b; a = 5, JSCOMPILER_PRESERVE(b == 6)", e); + test("var a, b; a = (5, 6)", + "var a, b; a = (JSCOMPILER_PRESERVE(5), 6)", e); + test("var a, b; a = (bar(), 6, 7)", + "var a, b; a = (bar(), JSCOMPILER_PRESERVE(6), 7)", e); + test("var a, b; a = (bar(), bar(), 7, 8)", + "var a, b; a = (bar(), bar(), JSCOMPILER_PRESERVE(7), 8)", e); + test("var a, b; a = (b = 7, 6)", ok); + test("function x(){}\nfunction f(a, b){}\nf(1,(x(), 2));", ok); + test("function x(){}\nfunction f(a, b){}\nf(1,(2, 3));", + "function x(){}\nfunction f(a, b){}\n" + + "f(1,(JSCOMPILER_PRESERVE(2), 3));", e); + } + + public void testUselessCodeInFor() { + test("for(var x = 0; x < 100; x++) { foo(x) }", ok); + test("for(; true; ) { bar() }", ok); + test("for(foo(); true; foo()) { bar() }", ok); + test("for(void 0; true; foo()) { bar() }", + "for(JSCOMPILER_PRESERVE(void 0); true; foo()) { bar() }", e); + test("for(foo(); true; void 0) { bar() }", + "for(foo(); true; JSCOMPILER_PRESERVE(void 0)) { bar() }", e); + test("for(foo(); true; (1, bar())) { bar() }", + "for(foo(); true; (JSCOMPILER_PRESERVE(1), bar())) { bar() }", e); + + test("for(foo in bar) { foo() }", ok); + test("for (i = 0; el = el.previousSibling; i++) {}", ok); + test("for (i = 0; el = el.previousSibling; i++);", ok); + } + + public void testTypeAnnotations() { + test("x;", "JSCOMPILER_PRESERVE(x);", e); + test("a.b.c.d;", "JSCOMPILER_PRESERVE(a.b.c.d);", e); + test("/** @type Number */ a.b.c.d;", ok); + test("if (true) { /** @type Number */ a.b.c.d; }", ok); + + test("function A() { this.foo; }", + "function A() { JSCOMPILER_PRESERVE(this.foo); }", e); + test("function A() { /** @type Number */ this.foo; }", ok); + } + + public void testJSDocComments() { + test("function A() { /** This is a JsDoc comment */ this.foo; }", ok); + test("function A() { /* This is a normal comment */ this.foo; }", + "function A() { " + + " /* This is a normal comment */ JSCOMPILER_PRESERVE(this.foo); }", e); + } + + public void testIssue80() { + test("(0, eval)('alert');", ok); + test("(0, foo)('alert');", "(JSCOMPILER_PRESERVE(0), foo)('alert');", e); + } + + public void testIsue504() { + test("void f();", "JSCOMPILER_PRESERVE(void f());", null, e, + "Suspicious code. The result of the 'void' operator is not being used."); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckSuspiciousCodeTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckSuspiciousCodeTest.java new file mode 100644 index 0000000..419bc29 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckSuspiciousCodeTest.java @@ -0,0 +1,117 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Tests for CheckSuspiciousCode + */ +public class CheckSuspiciousCodeTest extends CompilerTestCase { + public CheckSuspiciousCodeTest() { + this.parseTypeInfo = true; + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new CombinedCompilerPass(compiler, + new CheckSuspiciousCode()); + } + + @Override + protected int getNumRepetitions() { + return 1; + } + + public void test(String js, DiagnosticType error) { + test(js, js, null, error); + } + + public void testSuspiciousSemi() { + final DiagnosticType e = CheckSuspiciousCode.SUSPICIOUS_SEMICOLON; + final DiagnosticType ok = null; // code is 'ok', verify no warning + + test("if(x()) x = y;", ok); + test("if(x()); x = y;", e); // I've had this bug, damned ; + test("if(x()){} x = y;", ok); + + test("if(x()) x = y; else y=z;", ok); + test("if(x()); else y=z;", e); + test("if(x()){} else y=z;", ok); + test("if(x()) x = y; else;", e); + test("if(x()) x = y; else {}", ok); + + test("while(x()) x = y;", ok); + test("while(x()); x = y;", e); + test("while(x()){} x = y;", ok); + test("while(x()); {x = y}", e); + test("while(x()){} {x = y}", ok); + + test("for(;;) x = y;", ok); + test("for(;;); x = y;", e); + test("for(;;){} x = y;", ok); + test("for(x in y) x = y;", ok); + test("for(x in y); x = y;", e); + test("for(x in y){} x = y;", ok); + } + + private void testReportNaN(String js) { + testSame(js, CheckSuspiciousCode.SUSPICIOUS_COMPARISON_WITH_NAN); + } + + public void testComparison1() { + testReportNaN("x == NaN"); + testReportNaN("x != NaN"); + testReportNaN("x === NaN"); + testReportNaN("x !== NaN"); + testReportNaN("x < NaN"); + testReportNaN("x <= NaN"); + testReportNaN("x > NaN"); + testReportNaN("x >= NaN"); + } + + public void testComparison2() { + testReportNaN("NaN == x"); + testReportNaN("NaN != x"); + testReportNaN("NaN === x"); + testReportNaN("NaN !== x"); + testReportNaN("NaN < x"); + testReportNaN("NaN <= x"); + testReportNaN("NaN > x"); + testReportNaN("NaN >= x"); + } + + public void testComparison3() { + testReportNaN("x == 0/0"); + testReportNaN("x != 0/0"); + testReportNaN("x === 0/0"); + testReportNaN("x !== 0/0"); + testReportNaN("x < 0/0"); + testReportNaN("x <= 0/0"); + testReportNaN("x > 0/0"); + testReportNaN("x >= 0/0"); + } + + public void testComparison4() { + testReportNaN("0/0 == x"); + testReportNaN("0/0 != x"); + testReportNaN("0/0 === x"); + testReportNaN("0/0 !== x"); + testReportNaN("0/0 < x"); + testReportNaN("0/0 <= x"); + testReportNaN("0/0 > x"); + testReportNaN("0/0 >= x"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckUnreachableCodeTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckUnreachableCodeTest.java new file mode 100644 index 0000000..2a1cd06 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CheckUnreachableCodeTest.java @@ -0,0 +1,215 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.CheckLevel; + +/** + * Tests for {@link CheckUnreachableCode}. + * + */ +public class CheckUnreachableCodeTest extends CompilerTestCase { + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new CombinedCompilerPass(compiler, + new CheckUnreachableCode(compiler, CheckLevel.ERROR)); + } + + public void testCorrectSimple() { + testSame("var x"); + testSame("var x = 1"); + testSame("var x = 1; x = 2;"); + testSame("if (x) { var x = 1 }"); + testSame("if (x) { var x = 1 } else { var y = 2 }"); + testSame("while(x) {}"); + } + + public void testIncorrectSimple() { + assertUnreachable("function f() { return; x=1; }"); + assertUnreachable("function f() { return; x=1; x=1; }"); + assertUnreachable("function f() { return; var x = 1; }"); + } + + public void testCorrectIfReturns() { + testSame("function f() { if (x) { return } }"); + testSame("function f() { if (x) { return } return }"); + testSame("function f() { if (x) { if (y) { return } } else { return }}"); + testSame("function f()" + + "{ if (x) { if (y) { return } return } else { return }}"); + } + + public void testInCorrectIfReturns() { + assertUnreachable( + "function f() { if (x) { return } else { return } return }"); + } + + public void testCorrectSwitchReturn() { + testSame("function f() { switch(x) { default: return; case 1: x++; }}"); + testSame("function f() {" + + "switch(x) { default: return; case 1: x++; } return }"); + testSame("function f() {" + + "switch(x) { default: return; case 1: return; }}"); + testSame("function f() {" + + "switch(x) { case 1: return; } return }"); + testSame("function f() {" + + "switch(x) { case 1: case 2: return; } return }"); + testSame("function f() {" + + "switch(x) { case 1: return; case 2: return; } return }"); + testSame("function f() {" + + "switch(x) { case 1 : return; case 2: return; } return }"); + } + + public void testInCorrectSwitchReturn() { + assertUnreachable("function f() {" + + "switch(x) { default: return; case 1: return; } return }"); + assertUnreachable("function f() {" + + "switch(x) { default: return; return; case 1: return; } }"); + } + + public void testCorrectLoopBreaksAndContinues() { + testSame("while(1) { foo(); break }"); + testSame("while(1) { foo(); continue }"); + testSame("for(;;) { foo(); break }"); + testSame("for(;;) { foo(); continue }"); + testSame("for(;;) { if (x) { break } }"); + testSame("for(;;) { if (x) { continue } }"); + testSame("do { foo(); continue} while(1)"); + } + + public void testInCorrectLoopBreaksAndContinues() { + assertUnreachable("while(1) { foo(); break; bar()}"); + assertUnreachable("while(1) { foo(); continue; bar() }"); + assertUnreachable("for(;;) { foo(); break; bar() }"); + assertUnreachable("for(;;) { foo(); continue; bar() }"); + assertUnreachable("for(;;) { if (x) { break; bar() } }"); + assertUnreachable("for(;;) { if (x) { continue; bar() } }"); + assertUnreachable("do { foo(); continue; bar()} while(1)"); + } + + public void testUncheckedWhileInDo() { + assertUnreachable("do { foo(); break} while(1)"); + } + + public void testUncheckedConditionInFor() { + assertUnreachable("for(var x = 0; x < 100; x++) { break };"); + } + + public void testFunctionDeclaration() { + // functions are not in our CFG. + testSame("function f() { return; function ff() { }}"); + } + + public void testVarDeclaration() { + assertUnreachable("function f() { return; var x = 1 }"); + // I think the user should fix this as well. + assertUnreachable("function f() { return; var x }"); + } + + public void testReachableTryCatchFinally() { + testSame("try { } finally { }"); + testSame("try { foo(); } finally bar(); "); + testSame("try { foo() } finally { bar() }"); + testSame("try { foo(); } catch (e) {e()} finally bar(); "); + testSame("try { foo() } catch (e) {e()} finally { bar() }"); + } + + public void testUnreachableCatch() { + assertUnreachable("try { var x = 0 } catch (e) { }"); + } + + public void testSpuriousBreak() { + testSame("switch (x) { default: throw x; break; }"); + } + + public void testInstanceOfThrowsException() { + testSame("function f() {try { if (value instanceof type) return true; } " + + "catch (e) { }}"); + } + + public void testFalseCondition() { + assertUnreachable("if(false) { }"); + assertUnreachable("if(0) { }"); + } + + public void testUnreachableLoop() { + assertUnreachable("while(false) {}"); + } + + public void testInfiniteLoop() { + testSame("while (true) { foo(); break; }"); + + // TODO(user): Have a infinite loop warning instead. + assertUnreachable("while(true) {} foo()"); + } + + public void testSuppression() { + assertUnreachable("if(false) { }"); + + testSame( + "/** @fileoverview\n" + + " * @suppress {uselessCode}\n" + + " */\n" + + "if(false) { }"); + + testSame( + "/** @fileoverview\n" + + " * @suppress {uselessCode}\n" + + " */\n" + + "function f() { if(false) { } }"); + + testSame( + "/**\n" + + " * @suppress {uselessCode}\n" + + " */\n" + + "function f() { if(false) { } }"); + + assertUnreachable( + "/**\n" + + " * @suppress {uselessCode}\n" + + " */\n" + + "function f() { if(false) { } }\n" + + "function g() { if(false) { } }\n"); + + testSame( + "/**\n" + + " * @suppress {uselessCode}\n" + + " */\n" + + "function f() {\n" + + " function g() { if(false) { } }\n" + + " if(false) { } }\n"); + + assertUnreachable( + "function f() {\n" + + " /**\n" + + " * @suppress {uselessCode}\n" + + " */\n" + + " function g() { if(false) { } }\n" + + " if(false) { } }\n"); + + testSame( + "function f() {\n" + + " /**\n" + + " * @suppress {uselessCode}\n" + + " */\n" + + " function g() { if(false) { } }\n" + + "}\n"); + } + + private void assertUnreachable(String js) { + test(js, js, CheckUnreachableCode.UNREACHABLE_CODE); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CleanupPassesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CleanupPassesTest.java new file mode 100644 index 0000000..a31708f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CleanupPassesTest.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import junit.framework.TestCase; + +import java.util.List; + +/** + * @author tylerg@google.com (Tyler Goodwin) + */ +public class CleanupPassesTest extends TestCase { + + private final AbstractCompiler compiler = new Compiler(); + private final CompilerOptions options = new CompilerOptions(); + + public void testCleanupPassOrder() { + + CleanupPasses config = new CleanupPasses(options); + + List checks = config.getChecks(); + + assertTrue("Pass 0 should be a FieldCleanupPass", + checks.get(0).create(compiler) instanceof FieldCleanupPass); + } + + public void testNoOptimizations() { + CompilerOptions options = new CompilerOptions(); + CleanupPasses config = new CleanupPasses(options); + assertTrue("Cleanup Passes unexpectedly contain optimization passes", + config.getOptimizations().isEmpty()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ClosureCodeRemovalTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ClosureCodeRemovalTest.java new file mode 100644 index 0000000..79b3ea2 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ClosureCodeRemovalTest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Tests for {@link ClosureCodeRemoval} + * + * @author robbyw@google.com (Robby Walker) + */ +public class ClosureCodeRemovalTest extends CompilerTestCase { + + private static String EXTERNS = "var window;"; + + public ClosureCodeRemovalTest() { + super(EXTERNS); + } + + public void testRemoveAbstract() { + test("function Foo() {}; Foo.prototype.doSomething = goog.abstractMethod;", + "function Foo() {};"); + } + + public void testRemoveMultiplySetAbstract() { + test("function Foo() {}; Foo.prototype.doSomething = " + + "Foo.prototype.doSomethingElse = Foo.prototype.oneMore = " + + "goog.abstractMethod;", + "function Foo() {};"); + } + + public void testDoNotRemoveNormal() { + testSame("function Foo() {}; Foo.prototype.doSomething = function() {};"); + } + + public void testDoNotRemoveOverride() { + test("function Foo() {}; Foo.prototype.doSomething = goog.abstractMethod;" + + "function Bar() {}; goog.inherits(Bar, Foo);" + + "Bar.prototype.doSomething = function() {}", + "function Foo() {}; function Bar() {}; goog.inherits(Bar, Foo);" + + "Bar.prototype.doSomething = function() {}"); + } + + public void testDoNotRemoveNonQualifiedName() { + testSame("document.getElementById('x').y = goog.abstractMethod;"); + } + + public void testStopRemovalAtNonQualifiedName() { + test("function Foo() {}; function Bar() {};" + + "Foo.prototype.x = document.getElementById('x').y = Bar.prototype.x" + + " = goog.abstractMethod;", + "function Foo() {}; function Bar() {};" + + "Foo.prototype.x = document.getElementById('x').y = " + + "goog.abstractMethod;"); + } + + public void testAssertionRemoval1() { + test("var x = goog.asserts.assert(y(), 'message');", "var x = y();"); + } + + public void testAssertionRemoval2() { + test("goog.asserts.assert(y(), 'message');", ""); + } + + public void testAssertionRemoval3() { + test("goog.asserts.assert();", ""); + } + + public void testAssertionRemoval4() { + test("var x = goog.asserts.assert();", "var x = void 0;"); + } + + @Override + protected ClosureCodeRemoval getProcessor(Compiler compiler) { + return new ClosureCodeRemoval(compiler, true, true); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ClosureCodingConventionTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ClosureCodingConventionTest.java new file mode 100644 index 0000000..a7d5b2b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ClosureCodingConventionTest.java @@ -0,0 +1,273 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.CodingConvention.SubclassRelationship; +import com.google.javascript.jscomp.CodingConvention.SubclassType; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSTypeRegistry; + +import junit.framework.TestCase; + +/** + * Test class for {@link GoogleCodingConvention}. + */ +public class ClosureCodingConventionTest extends TestCase { + private ClosureCodingConvention conv = new ClosureCodingConvention(); + + public void testVarAndOptionalParams() { + Node args = new Node(Token.PARAM_LIST, + Node.newString(Token.NAME, "a"), + Node.newString(Token.NAME, "b")); + Node optArgs = new Node(Token.PARAM_LIST, + Node.newString(Token.NAME, "opt_a"), + Node.newString(Token.NAME, "opt_b")); + + assertFalse(conv.isVarArgsParameter(args.getFirstChild())); + assertFalse(conv.isVarArgsParameter(args.getLastChild())); + assertFalse(conv.isVarArgsParameter(optArgs.getFirstChild())); + assertFalse(conv.isVarArgsParameter(optArgs.getLastChild())); + + assertFalse(conv.isOptionalParameter(args.getFirstChild())); + assertFalse(conv.isOptionalParameter(args.getLastChild())); + assertFalse(conv.isOptionalParameter(optArgs.getFirstChild())); + assertFalse(conv.isOptionalParameter(optArgs.getLastChild())); + } + + public void testInlineName() { + assertFalse(conv.isConstant("a")); + assertFalse(conv.isConstant("XYZ123_")); + assertFalse(conv.isConstant("ABC")); + assertFalse(conv.isConstant("ABCdef")); + assertFalse(conv.isConstant("aBC")); + assertFalse(conv.isConstant("A")); + assertFalse(conv.isConstant("_XYZ123")); + assertFalse(conv.isConstant("a$b$XYZ123_")); + assertFalse(conv.isConstant("a$b$ABC_DEF")); + assertFalse(conv.isConstant("a$b$A")); + assertFalse(conv.isConstant("a$b$a")); + assertFalse(conv.isConstant("a$b$ABCdef")); + assertFalse(conv.isConstant("a$b$aBC")); + assertFalse(conv.isConstant("a$b$")); + assertFalse(conv.isConstant("$")); + } + + public void testExportedName() { + assertFalse(conv.isExported("_a")); + assertFalse(conv.isExported("_a_")); + assertFalse(conv.isExported("a")); + + assertFalse(conv.isExported("$super", false)); + assertTrue(conv.isExported("$super", true)); + assertTrue(conv.isExported("$super")); + } + + public void testPrivateName() { + assertFalse(conv.isPrivate("a_")); + assertFalse(conv.isPrivate("a")); + assertFalse(conv.isPrivate("_a_")); + } + + public void testEnumKey() { + assertTrue(conv.isValidEnumKey("A")); + assertTrue(conv.isValidEnumKey("123")); + assertTrue(conv.isValidEnumKey("FOO_BAR")); + + assertTrue(conv.isValidEnumKey("a")); + assertTrue(conv.isValidEnumKey("someKeyInCamelCase")); + assertTrue(conv.isValidEnumKey("_FOO_BAR")); + } + + public void testInheritanceDetection1() { + assertNotClassDefining("goog.foo(A, B);"); + } + + public void testInheritanceDetection2() { + assertDefinesClasses("goog.inherits(A, B);", "A", "B"); + } + + public void testInheritanceDetection3() { + assertDefinesClasses("A.inherits(B);", "A", "B"); + } + + public void testInheritanceDetection4() { + assertDefinesClasses("goog.inherits(goog.A, goog.B);", "goog.A", "goog.B"); + } + + public void testInheritanceDetection5() { + assertDefinesClasses("goog.A.inherits(goog.B);", "goog.A", "goog.B"); + } + + public void testInheritanceDetection6() { + assertNotClassDefining("A.inherits(this.B);"); + } + + public void testInheritanceDetection7() { + assertNotClassDefining("this.A.inherits(B);"); + } + + public void testInheritanceDetection8() { + assertNotClassDefining("goog.inherits(A, B, C);"); + } + + public void testInheritanceDetection9() { + assertDefinesClasses("A.mixin(B.prototype);", + "A", "B"); + } + + public void testInheritanceDetection10() { + assertDefinesClasses("goog.mixin(A.prototype, B.prototype);", + "A", "B"); + } + + public void testInheritanceDetection11() { + assertNotClassDefining("A.mixin(B)"); + } + + public void testInheritanceDetection12() { + assertNotClassDefining("goog.mixin(A.prototype, B)"); + } + + public void testInheritanceDetection13() { + assertNotClassDefining("goog.mixin(A, B)"); + } + + public void testInheritanceDetection14() { + assertNotClassDefining("goog$mixin((function(){}).prototype)"); + } + + public void testInheritanceDetectionPostCollapseProperties() { + assertDefinesClasses("goog$inherits(A, B);", "A", "B"); + assertNotClassDefining("goog$inherits(A);"); + } + + public void testObjectLiteralCast() { + assertNotObjectLiteralCast("goog.reflect.object();"); + assertNotObjectLiteralCast("goog.reflect.object(A);"); + assertNotObjectLiteralCast("goog.reflect.object(1, {});"); + assertObjectLiteralCast("goog.reflect.object(A, {});"); + } + + public void testFunctionBind() { + assertNotFunctionBind("goog.bind()"); // invalid bind + assertFunctionBind("goog.bind(f)"); + assertFunctionBind("goog.bind(f, obj)"); + assertFunctionBind("goog.bind(f, obj, p1)"); + + assertNotFunctionBind("goog$bind()"); // invalid bind + assertFunctionBind("goog$bind(f)"); + assertFunctionBind("goog$bind(f, obj)"); + assertFunctionBind("goog$bind(f, obj, p1)"); + + assertNotFunctionBind("goog.partial()"); // invalid bind + assertFunctionBind("goog.partial(f)"); + assertFunctionBind("goog.partial(f, obj)"); + assertFunctionBind("goog.partial(f, obj, p1)"); + + assertNotFunctionBind("goog$partial()"); // invalid bind + assertFunctionBind("goog$partial(f)"); + assertFunctionBind("goog$partial(f, obj)"); + assertFunctionBind("goog$partial(f, obj, p1)"); + + assertFunctionBind("(function(){}).bind()"); + assertFunctionBind("(function(){}).bind(obj)"); + assertFunctionBind("(function(){}).bind(obj, p1)"); + + assertNotFunctionBind("Function.prototype.bind.call()"); + assertFunctionBind("Function.prototype.bind.call(obj)"); + assertFunctionBind("Function.prototype.bind.call(obj, p1)"); + } + + public void testRequire() { + assertRequire("goog.require('foo')"); + assertNotRequire("goog.require(foo)"); + assertNotRequire("goog.require()"); + assertNotRequire("foo()"); + } + + public void testApplySubclassRelationship() { + JSTypeRegistry registry = new JSTypeRegistry(null); + + Node nodeA = new Node(Token.FUNCTION); + FunctionType ctorA = registry.createConstructorType("A", nodeA, + new Node(Token.PARAM_LIST), null, null); + + Node nodeB = new Node(Token.FUNCTION); + FunctionType ctorB = registry.createConstructorType("B", nodeB, + new Node(Token.PARAM_LIST), null, null); + + conv.applySubclassRelationship(ctorA, ctorB, SubclassType.INHERITS); + + assertTrue(ctorB.getPrototype().hasOwnProperty("constructor")); + assertEquals(nodeB, ctorB.getPrototype().getPropertyNode("constructor")); + + assertTrue(ctorB.hasOwnProperty("superClass_")); + assertEquals(nodeB, ctorB.getPropertyNode("superClass_")); + } + + private void assertFunctionBind(String code) { + Node n = parseTestCode(code); + assertNotNull(conv.describeFunctionBind(n.getFirstChild())); + } + + private void assertNotFunctionBind(String code) { + Node n = parseTestCode(code); + assertNull(conv.describeFunctionBind(n.getFirstChild())); + } + + private void assertRequire(String code) { + Node n = parseTestCode(code); + assertNotNull(conv.extractClassNameIfRequire(n.getFirstChild(), n)); + } + + private void assertNotRequire(String code) { + Node n = parseTestCode(code); + assertNull(conv.extractClassNameIfRequire(n.getFirstChild(), n)); + } + + private void assertNotObjectLiteralCast(String code) { + Node n = parseTestCode(code); + assertNull(conv.getObjectLiteralCast(n.getFirstChild())); + } + + private void assertObjectLiteralCast(String code) { + Node n = parseTestCode(code); + assertNotNull(conv.getObjectLiteralCast(n.getFirstChild())); + } + + private void assertNotClassDefining(String code) { + Node n = parseTestCode(code); + assertNull(conv.getClassesDefinedByCall(n.getFirstChild())); + } + + private void assertDefinesClasses(String code, String subclassName, + String superclassName) { + Node n = parseTestCode(code); + SubclassRelationship classes = + conv.getClassesDefinedByCall(n.getFirstChild()); + assertNotNull(classes); + assertEquals(subclassName, classes.subclassName); + assertEquals(superclassName, classes.superclassName); + } + + private Node parseTestCode(String code) { + Compiler compiler = new Compiler(); + return compiler.parseTestCode(code).getFirstChild(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ClosureOptimizePrimitivesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ClosureOptimizePrimitivesTest.java new file mode 100644 index 0000000..3c4225f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ClosureOptimizePrimitivesTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Tests for {@link ClosureOptimizePrimitives}. + * + * @author agrieve@google.com (Andrew Grieve) + */ +public class ClosureOptimizePrimitivesTest extends CompilerTestCase { + + @Override public CompilerPass getProcessor(final Compiler compiler) { + return new ClosureOptimizePrimitives(compiler); + } + + public void testObjectCreateNonConstKey() { + testSame("goog.object.create('a',1,2,3,foo,bar);"); + } + + public void testObjectCreateOddParams() { + testSame("goog.object.create('a',1,2);"); + } + + public void testObjectCreate1() { + test("var a = goog.object.create()", "var a = {}"); + } + + public void testObjectCreate2() { + test("var a = goog$object$create('b',goog$object$create('c','d'))", + "var a = {'b':{'c':'d'}};"); + } + + public void testObjectCreate3() { + test("var a = goog.object.create(1,2)", "var a = {1:2}"); + } + + public void testObjectCreate4() { + test("alert(goog.object.create(1,2).toString())", + "alert({1:2}.toString())"); + } + + public void testObjectCreate5() { + test("goog.object.create('a',2).toString()", "({'a':2}).toString()"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ClosureReverseAbstractInterpreterTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ClosureReverseAbstractInterpreterTest.java new file mode 100644 index 0000000..e6e6f8c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ClosureReverseAbstractInterpreterTest.java @@ -0,0 +1,299 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.type.ClosureReverseAbstractInterpreter; +import com.google.javascript.jscomp.type.FlowScope; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.testing.Asserts; + +public class ClosureReverseAbstractInterpreterTest extends + CompilerTypeTestCase { + + public void testGoogIsDef1() throws Exception { + testClosureFunction("goog.isDef", + createOptionalType(NUMBER_TYPE), + NUMBER_TYPE, + VOID_TYPE); + } + + public void testGoogIsDef2() throws Exception { + testClosureFunction("goog.isDef", + createNullableType(NUMBER_TYPE), + createNullableType(NUMBER_TYPE), + NO_TYPE); + } + + public void testGoogIsDef3() throws Exception { + testClosureFunction("goog.isDef", + ALL_TYPE, + createUnionType(OBJECT_NUMBER_STRING_BOOLEAN,NULL_TYPE), + VOID_TYPE); + } + + public void testGoogIsDef4() throws Exception { + testClosureFunction("goog.isDef", + UNKNOWN_TYPE, + UNKNOWN_TYPE, // TODO(johnlenz): should be CHECKED_UNKNOWN_TYPE + UNKNOWN_TYPE); + } + + public void testGoogIsNull1() throws Exception { + testClosureFunction("goog.isNull", + createOptionalType(NUMBER_TYPE), + NO_TYPE, + createOptionalType(NUMBER_TYPE)); + } + + public void testGoogIsNull2() throws Exception { + testClosureFunction("goog.isNull", + createNullableType(NUMBER_TYPE), + NULL_TYPE, + NUMBER_TYPE); + } + + public void testGoogIsNull3() throws Exception { + testClosureFunction("goog.isNull", + ALL_TYPE, + NULL_TYPE, + createUnionType(OBJECT_NUMBER_STRING_BOOLEAN, VOID_TYPE)); + } + + public void testGoogIsNull4() throws Exception { + testClosureFunction("goog.isNull", + UNKNOWN_TYPE, + UNKNOWN_TYPE, + UNKNOWN_TYPE); // TODO(johnlenz): this should be CHECK_UNKNOWN + } + + public void testGoogIsDefAndNotNull1() throws Exception { + testClosureFunction("goog.isDefAndNotNull", + createOptionalType(NUMBER_TYPE), + NUMBER_TYPE, + VOID_TYPE); + } + + public void testGoogIsDefAndNotNull2() throws Exception { + testClosureFunction("goog.isDefAndNotNull", + createNullableType(NUMBER_TYPE), + NUMBER_TYPE, + NULL_TYPE); + } + + public void testGoogIsDefAndNotNull3() throws Exception { + testClosureFunction("goog.isDefAndNotNull", + createOptionalType(createNullableType(NUMBER_TYPE)), + NUMBER_TYPE, + NULL_VOID); + } + + public void testGoogIsDefAndNotNull4() throws Exception { + testClosureFunction("goog.isDefAndNotNull", + ALL_TYPE, + OBJECT_NUMBER_STRING_BOOLEAN, + NULL_VOID); + } + + public void testGoogIsDefAndNotNull5() throws Exception { + testClosureFunction("goog.isDefAndNotNull", + UNKNOWN_TYPE, + UNKNOWN_TYPE, // TODO(johnlenz): this should be "CHECKED_UNKNOWN" + UNKNOWN_TYPE); + } + + public void testGoogIsString1() throws Exception { + testClosureFunction("goog.isString", + createNullableType(STRING_TYPE), + STRING_TYPE, + NULL_TYPE); + } + + public void testGoogIsString2() throws Exception { + testClosureFunction("goog.isString", + createNullableType(NUMBER_TYPE), + createNullableType(NUMBER_TYPE), + createNullableType(NUMBER_TYPE)); + } + + public void testGoogIsBoolean1() throws Exception { + testClosureFunction("goog.isBoolean", + createNullableType(BOOLEAN_TYPE), + BOOLEAN_TYPE, + NULL_TYPE); + } + + public void testGoogIsBoolean2() throws Exception { + testClosureFunction("goog.isBoolean", + createUnionType(BOOLEAN_TYPE, STRING_TYPE, NO_OBJECT_TYPE), + BOOLEAN_TYPE, + createUnionType(STRING_TYPE, NO_OBJECT_TYPE)); + } + + public void testGoogIsBoolean3() throws Exception { + testClosureFunction("goog.isBoolean", + ALL_TYPE, + BOOLEAN_TYPE, + ALL_TYPE); // TODO(johnlenz): this should be: + // {Object|number|string|null|void} + } + + public void testGoogIsBoolean4() throws Exception { + testClosureFunction("goog.isBoolean", + UNKNOWN_TYPE, + BOOLEAN_TYPE, + CHECKED_UNKNOWN_TYPE); + } + + public void testGoogIsNumber() throws Exception { + testClosureFunction("goog.isNumber", + createNullableType(NUMBER_TYPE), + NUMBER_TYPE, + NULL_TYPE); + } + + public void testGoogIsFunction() throws Exception { + testClosureFunction("goog.isFunction", + createNullableType(OBJECT_FUNCTION_TYPE), + OBJECT_FUNCTION_TYPE, + NULL_TYPE); + } + + public void testGoogIsFunction2() throws Exception { + testClosureFunction("goog.isFunction", + OBJECT_NUMBER_STRING_BOOLEAN, + U2U_CONSTRUCTOR_TYPE, + OBJECT_NUMBER_STRING_BOOLEAN); + } + + public void testGoogIsFunction3() throws Exception { + testClosureFunction("goog.isFunction", + createUnionType(U2U_CONSTRUCTOR_TYPE, NUMBER_STRING_BOOLEAN), + U2U_CONSTRUCTOR_TYPE, + NUMBER_STRING_BOOLEAN); + } + + public void testGoogIsFunctionOnNull() throws Exception { + testClosureFunction("goog.isFunction", + null, + U2U_CONSTRUCTOR_TYPE, + null); + } + + public void testGoogIsArray1() throws Exception { + testClosureFunction("goog.isArray", + OBJECT_TYPE, + ARRAY_TYPE, + OBJECT_TYPE); + } + + public void testGoogIsArray2() throws Exception { + testClosureFunction("goog.isArray", + ALL_TYPE, + ALL_TYPE, // TODO(johnlenz): should be ARRAY_TYPE? + ALL_TYPE); + } + + public void testGoogIsArray3() throws Exception { + testClosureFunction("goog.isArray", + UNKNOWN_TYPE, + CHECKED_UNKNOWN_TYPE, + CHECKED_UNKNOWN_TYPE); + } + + public void testGoogIsArray4() throws Exception { + testClosureFunction("goog.isArray", + createUnionType(ARRAY_TYPE, NULL_TYPE), + ARRAY_TYPE, + NULL_TYPE); + } + + public void testGoogIsArrayOnNull() throws Exception { + testClosureFunction("goog.isArray", + null, + ARRAY_TYPE, + null); + } + + public void testGoogIsObjectOnNull() throws Exception { + testClosureFunction("goog.isObject", + null, + OBJECT_TYPE, + null); + } + + public void testGoogIsObject1() throws Exception { + testClosureFunction("goog.isObject", + ALL_TYPE, + NO_OBJECT_TYPE, + createUnionType(NUMBER_STRING_BOOLEAN, NULL_TYPE, VOID_TYPE)); + } + + public void testGoogIsObject2() throws Exception { + testClosureFunction("goog.isObject", + createUnionType(OBJECT_TYPE, NUMBER_STRING_BOOLEAN), + OBJECT_TYPE, + NUMBER_STRING_BOOLEAN); + } + + public void testGoogIsObject3() throws Exception { + testClosureFunction("goog.isObject", + createUnionType( + OBJECT_TYPE, NUMBER_STRING_BOOLEAN, NULL_TYPE, VOID_TYPE), + OBJECT_TYPE, + createUnionType(NUMBER_STRING_BOOLEAN, NULL_TYPE, VOID_TYPE)); + } + + public void testGoogIsObject4() throws Exception { + testClosureFunction("goog.isObject", + UNKNOWN_TYPE, + NO_OBJECT_TYPE, // ? Should this be CHECKED_UNKNOWN? + CHECKED_UNKNOWN_TYPE); + } + + private void testClosureFunction(String function, JSType type, + JSType trueType, JSType falseType) { + // function(a) where a : type + Node n = compiler.parseTestCode("var a; " + function + "(a)"); + Node call = n.getLastChild().getLastChild(); + Node name = call.getLastChild(); + + Scope scope = new SyntacticScopeCreator(compiler).createScope(n, null); + FlowScope flowScope = LinkedFlowScope.createEntryLattice(scope); + + assertEquals(Token.CALL, call.getType()); + assertEquals(Token.NAME, name.getType()); + + GoogleCodingConvention convention = new GoogleCodingConvention(); + flowScope.inferSlotType("a", type); + ClosureReverseAbstractInterpreter rai = + new ClosureReverseAbstractInterpreter(convention, registry); + + // trueScope + Asserts.assertTypeEquals( + trueType, + rai.getPreciserScopeKnowingConditionOutcome(call, flowScope, true) + .getSlot("a").getType()); + + // falseScope + Asserts.assertTypeEquals( + falseType, + rai.getPreciserScopeKnowingConditionOutcome(call, flowScope, false) + .getSlot("a").getType()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ClosureRewriteClassTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ClosureRewriteClassTest.java new file mode 100644 index 0000000..50cd016 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ClosureRewriteClassTest.java @@ -0,0 +1,275 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import static com.google.javascript.jscomp.ClosureRewriteClass.GOOG_CLASS_CONSTRUCTOR_MISING; +import static com.google.javascript.jscomp.ClosureRewriteClass.GOOG_CLASS_DESCRIPTOR_NOT_VALID; +import static com.google.javascript.jscomp.ClosureRewriteClass.GOOG_CLASS_STATICS_NOT_VALID; +import static com.google.javascript.jscomp.ClosureRewriteClass.GOOG_CLASS_SUPER_CLASS_NOT_VALID; +import static com.google.javascript.jscomp.ClosureRewriteClass.GOOG_CLASS_TARGET_INVALID; +import static com.google.javascript.jscomp.ClosureRewriteClass.GOOG_CLASS_UNEXPECTED_PARAMS; + + +/** + * Unit tests for ClosureRewriteGoogClass + * @author johnlenz@google.com (John Lenz) + */ +public class ClosureRewriteClassTest extends CompilerTestCase { + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new ClosureRewriteClass(compiler); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + this.enableEcmaScript5(false); + } + + @Override + protected int getNumRepetitions() { + return 1; + } + + public void testBasic1() { + test( + "var x = goog.defineClass(null, {\n" + + " constructor: function(){}\n" + + "});", + + "{var x = function() {};}"); + } + + public void testBasic2() { + test( + "var x = {};\n" + + "x.y = goog.defineClass(null, {\n" + + " constructor: function(){}\n" + + "});", + + "var x = {};" + + "{x.y = function() {};}"); + } + + public void testBasic3() { + test( + "var x = goog.labs.classdef.defineClass(null, {\n" + + " constructor: function(){}\n" + + "});", + + "{var x = function() {};}"); + } + + public void testInnerClass1() { + test( + "var x = goog.defineClass(some.Super, {\n" + + " constructor: function(){\n" + + " this.foo = 1;\n" + + " },\n" + + " statics: {\n" + + " inner: goog.defineClass(x,{\n" + + " constructor: function(){\n" + + " this.bar = 1;\n" + + " }\n" + + " })\n" + + " }\n" + + "});", + + "{" + + "var x=function(){this.foo=1};" + + "goog.inherits(x,some.Super);" + + "{" + + "x.inner=function(){this.bar=1};" + + "goog.inherits(x.inner,x);" + + "}" + + "}"); + } + + public void testComplete1() { + test( + "var x = goog.defineClass(some.Super, {\n" + + " constructor: function(){\n" + + " this.foo = 1;\n" + + " },\n" + + " statics: {\n" + + " prop1: 1,\n" + + " /** @const */\n" + + " PROP2: 2\n" + + " },\n" + + " anotherProp: 1,\n" + + " aMethod: function() {}\n" + + "});", + + "{" + + "var x=function(){this.foo=1};" + + "goog.inherits(x,some.Super);" + + "x.prop1=1;" + + "x.PROP2=2;" + + "x.prototype.anotherProp=1;" + + "x.prototype.aMethod=function(){};" + + "}"); + } + + public void testComplete2() { + test( + "x.y = goog.defineClass(some.Super, {\n" + + " constructor: function(){\n" + + " this.foo = 1;\n" + + " },\n" + + " statics: {\n" + + " prop1: 1,\n" + + " /** @const */\n" + + " PROP2: 2\n" + + " },\n" + + " anotherProp: 1,\n" + + " aMethod: function() {}\n" + + "});", + + "{\n" + + "/** @constructor */\n" + + "x.y=function(){this.foo=1};\n" + + "goog.inherits(x.y,some.Super);" + + "x.y.prop1=1;\n" + + "/** @const */\n" + + "x.y.PROP2=2;\n" + + "x.y.prototype.anotherProp=1;" + + "x.y.prototype.aMethod=function(){};" + + "}"); + } + + public void testClassWithStaticInitFn() { + test( + "x.y = goog.defineClass(some.Super, {\n" + + " constructor: function(){\n" + + " this.foo = 1;\n" + + " },\n" + + " statics: function(cls) {\n" + + " cls.prop1 = 1;\n" + + " /** @const */\n" + + " cls.PROP2 = 2;\n" + + " },\n" + + " anotherProp: 1,\n" + + " aMethod: function() {}\n" + + "});", + + "{\n" + + "/** @constructor */\n" + + "x.y=function(){this.foo=1};\n" + + "goog.inherits(x.y,some.Super);" + + "x.y.prototype.anotherProp=1;" + + "x.y.prototype.aMethod=function(){};" + + "(function(cls) {" + + " cls.prop1=1;\n" + + " /** @const */\n" + + " cls.PROP2=2;" + + "})(x.y);\n" + + "}"); + } + + public void testInvalid1() { + testSame( + "var x = goog.defineClass();", + GOOG_CLASS_SUPER_CLASS_NOT_VALID, true); + testSame( + "var x = goog.defineClass('foo');", + GOOG_CLASS_SUPER_CLASS_NOT_VALID, true); + testSame( + "var x = goog.defineClass(foo());", + GOOG_CLASS_SUPER_CLASS_NOT_VALID, true); + testSame( + "var x = goog.defineClass({'foo':1});", + GOOG_CLASS_SUPER_CLASS_NOT_VALID, true); + testSame( + "var x = goog.defineClass({1:1});", + GOOG_CLASS_SUPER_CLASS_NOT_VALID, true); + + this.enableEcmaScript5(true); + + testSame( + "var x = goog.defineClass({get foo() {return 1}});", + GOOG_CLASS_SUPER_CLASS_NOT_VALID, true); + testSame( + "var x = goog.defineClass({set foo(a) {}});", + GOOG_CLASS_SUPER_CLASS_NOT_VALID, true); + } + + public void testInvalid2() { + testSame( + "var x = goog.defineClass(null);", + GOOG_CLASS_DESCRIPTOR_NOT_VALID, true); + testSame( + "var x = goog.defineClass(null, null);", + GOOG_CLASS_DESCRIPTOR_NOT_VALID, true); + testSame( + "var x = goog.defineClass(null, foo());", + GOOG_CLASS_DESCRIPTOR_NOT_VALID, true); + } + + public void testInvalid3() { + testSame( + "var x = goog.defineClass(null, {});", + GOOG_CLASS_CONSTRUCTOR_MISING, true); + } + + public void testInvalid4() { + testSame( + "var x = goog.defineClass(null, {" + + " constructor: function(){}," + + " statics: null" + + "});", + GOOG_CLASS_STATICS_NOT_VALID, true); + testSame( + "var x = goog.defineClass(null, {" + + " constructor: function(){}," + + " statics: foo" + + "});", + GOOG_CLASS_STATICS_NOT_VALID, true); + testSame( + "var x = goog.defineClass(null, {" + + " constructor: function(){}," + + " statics: {'foo': 1}" + + "});", + GOOG_CLASS_STATICS_NOT_VALID, true); + testSame( + "var x = goog.defineClass(null, {" + + " constructor: function(){}," + + " statics: {1: 1}" + + "});", + GOOG_CLASS_STATICS_NOT_VALID, true); } + + public void testInvalid5() { + testSame( + "var x = goog.defineClass(null, {" + + " constructor: function(){}" + + "}, null);", + GOOG_CLASS_UNEXPECTED_PARAMS, true); + } + + public void testInvalid6() { + testSame( + "goog.defineClass();", + GOOG_CLASS_TARGET_INVALID, true); + + testSame( + "var x = goog.defineClass() || null;", + GOOG_CLASS_TARGET_INVALID, true); + + testSame( + "({foo: goog.defineClass()});", + GOOG_CLASS_TARGET_INVALID, true); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CoalesceVariableNamesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CoalesceVariableNamesTest.java new file mode 100644 index 0000000..776f3d7 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CoalesceVariableNamesTest.java @@ -0,0 +1,450 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + * Unit tests for {@link CoalesceVariableNames} + * + */ +public class CoalesceVariableNamesTest extends CompilerTestCase { + // The spacing in this file is not exactly standard but it greatly helps + // picking out which variable names are merged. + + private boolean usePseudoName = false; + + @Override + protected int getNumRepetitions() { + return 1; + } + + @Override + public void setUp() { + super.enableLineNumberCheck(true); + usePseudoName = false; + } + + @Override + public CompilerPass getProcessor(final Compiler compiler) { + return new CompilerPass() { + @Override + public void process(Node externs, Node js) { + NodeTraversal.traverse(compiler, js, + new CoalesceVariableNames(compiler, usePseudoName)); + } + }; + } + + public void testSimple() { + inFunction("var x; var y; x=1; x; y=1; y; return y", + "var x; x=1; x; x=1; x; return x"); + + inFunction("var x,y; x=1; x; y=1; y", + "var x ; x=1; x; x=1; x"); + + inFunction("var x,y; x=1; y=2; y; x"); + + inFunction("y=0; var x, y; y; x=0; x", + "y=0; var y ; y; y=0;y"); + + inFunction("var x,y; x=1; y=x; y", + "var x ; x=1; x=x; x"); + + inFunction("var x,y; x=1; y=x+1; y", + "var x ; x=1; x=x+1; x"); + + inFunction("x=1; x; y=2; y; var x; var y", + "x=1; x; x=2; x; var x"); + + inFunction("var x=1; var y=x+1; return y", + "var x=1; x=x+1; return x"); + + inFunction("var x=1; var y=0; x+=1; y"); + + inFunction("var x=1; x+=1; var y=0; y", + "var x=1; x+=1; x=0; x"); + + inFunction("var x=1; foo(bar(x+=1)); var y=0; y", + "var x=1; foo(bar(x+=1)); x=0; x"); + + inFunction("var y, x=1; f(x+=1, y)"); + + inFunction("var x; var y; y += 1, y, x = 1; x"); + } + + public void testMergeThreeVarNames() { + inFunction("var x,y,z; x=1; x; y=1; y; z=1; z", + "var x ; x=1; x; x=1; x; x=1; x"); + } + + public void testDifferentBlock() { + inFunction("if(1) { var x = 0; x } else { var y = 0; y }", + "if(1) { var x = 0; x } else { x = 0; x }"); + } + + public void testLoops() { + inFunction("var x; while(1) { x; x = 1; var y = 1; y }"); + inFunction("var y = 1; y; while(1) { var x = 1; x }", + "var y = 1; y; while(1) { y = 1; y }"); + } + + public void testEscaped() { + inFunction("var x = 1; x; function f() { x }; var y = 0; y; f()"); + } + + public void testFor() { + inFunction("var x = 1; x; for (;;) var y; y = 1; y", + "var x = 1; x; for (;;) ; x = 1; x"); + } + + public void testForIn() { + // We lose some precision here, unless we have "branched-backward-dataflow". + inFunction("var x = 1, k; x; ; for (var y in k) { y }", + "var x = 1, k; x; ; for (var y in k) { y }"); + + inFunction("var x = 1, k; x; y = 1; for (var y in k) { y }", + "var x = 1, k; x; x = 1; for ( x in k) { x }"); + } + + public void testLoopInductionVar() { + inFunction( + "for(var x = 0; x < 10; x++){}" + + "for(var y = 0; y < 10; y++){}" + + "for(var z = 0; z < 10; z++){}", + + "for(var x = 0; x < 10; x++){}" + + "for(x = 0; x < 10; x++){}" + + "for(x = 0; x < 10; x++){}"); + + inFunction( + "for(var x = 0; x < 10; x++){z}" + + "for(var y = 0, z = 0; y < 10; y++){z}", + + "for(var x = 0; x < 10; x++){z}" + + "for(var x = 0, z = 0; x < 10; x++){z}"); + + inFunction("var x = 1; x; for (var y; y=1; ) {y}", + "var x = 1; x; for ( ; x=1; ) {x}"); + + inFunction("var x = 1; x; y = 1; while(y) var y; y", + "var x = 1; x; x = 1; while(x); x"); + + inFunction("var x = 1; x; f:var y; y=1", + "var x = 1; x; x=1"); + } + + public void testSwitchCase() { + inFunction("var x = 1; switch(x) { case 1: var y; case 2: } y = 1; y", + "var x = 1; switch(x) { case 1: case 2: } x = 1; x"); + } + + public void testDuplicatedVar() { + // Is there a shorter version without multiple declarations? + inFunction("z = 1; var x = 0; x; z; var y = 2, z = 1; y; z;", + "z = 1; var x = 0; x; z; var x = 2, z = 1; x; z;"); + } + + public void testTryCatch() { + inFunction("try {} catch (e) { } var x = 4; x;", + "try {} catch (e) { } var x = 4; x;"); + inFunction("var x = 4; x; try {} catch (e) { }", + "var x = 4; x; try {} catch (e) { }"); + } + + public void testDeadAssignment() { + inFunction("var x = 6; var y; y = 4 ; x"); + inFunction("var y = 3; var y; y += 4; x"); + inFunction("var y = 3; var y; y ++ ; x"); + inFunction("y = 3; var x; var y = 1 ; x"); + } + + public void testParameter() { + test("function FUNC(param) {var x = 0; x}", + "function FUNC(param) {param = 0; param}"); + } + + public void testParameter2() { + // Make sure two formal parameter name never merges. + test("function FUNC(x,y) {x = 0; x; y = 0; y}"); + test("function FUNC(x,y,z) {x = 0; x; y = 0; z = 0; z}"); + } + + public void testParameter3() { + // Make sure the formal parameter declaration is consider a def. + test("function FUNC(x) {var y; y = 0; x; y}"); + } + + public void testParameter4() { + // Make sure that we do not merge two-arg functions because of the + // IE sort bug (see comments in computeEscaped) + test("function FUNC(x, y) {var a,b; y; a=0; a; x; b=0; b}", + "function FUNC(x, y) {var a; y; a=0; a; x; a=0; a}"); + } + + public void testParameter4b() { + // Merge parameters + test("function FUNC(x, y, z) {var a,b; y; a=0; a; x; b=0; b}", + "function FUNC(x, y, z) { y; y=0; y; x; x=0; x}"); + } + + public void testLiveRangeChangeWithinCfgNode() { + inFunction("var x, y; x = 1, y = 2, y, x"); + inFunction("var x, y; x = 1,x; y"); + + // We lose some precisions within the node itself. + inFunction("var x; var y; y = 1, y, x = 1; x"); + inFunction("var x; var y; y = 1; y, x = 1; x", "var x; x = 1; x, x = 1; x"); + inFunction("var x, y; y = 1, x = 1, x, y += 1, y"); + inFunction("var x, y; y = 1, x = 1, x, y ++, y"); + } + + public void testLiveRangeChangeWithinCfgNode2() { + inFunction("var x; var y; var a; var b;" + + "y = 1, a = 1, y, a, x = 1, b = 1; x; b"); + inFunction("var x; var y; var a; var b;" + + "y = 1, a = 1, y, a, x = 1; x; b = 1; b", + "var x; var y; var a; " + + "y = 1, a = 1, y, a, x = 1; x; x = 1; x"); + inFunction("var x; var y; var a; var b;" + + "y = 1, a = 1, y, x = 1; a; x; b = 1; b", + "var x; var y; var a; " + + "y = 1, a = 1, y, x = 1; a; x; x = 1; x"); + } + + public void testFunctionNameReuse() { +// TODO(user): Figure out why this increase code size most of the time. +// inFunction("function x() {}; x(); var y = 1; y", +// "function x() {}; x(); x = 1; x"); +// inFunction("x(); var y = 1; y; function x() {}", +// "x(); x = 1; x; function x() {}"); +// inFunction("x(); var y = 1; function x() {}; y", +// "x(); x = 1; function x() {}; x"); +// // Can't merge because of possible escape. +// inFunction("function x() {return x}; x(); var y = 1; y", +// "function x() {return x}; x(); var y = 1; y"); +// +// inFunction("var y = 1; y; x; function x() {}", +// "var y = 1; y; x; function x() {}"); +// inFunction("var y = 1; y; function x() {}; x", +// "var y = 1; y; function x() {}; x"); +// inFunction("var y = 1; y; function x() {}; x = 1; x", +// "var y = 1; y; function x() {}; y = 1; y"); +// inFunction("var y = 1; y; x = 1; function x() {}; x", +// "var y = 1; y; y = 1; function x() {}; y"); + } + + public void testBug1401831() { + // Verify that we don't wrongly merge "opt_a2" and "i" without considering + // arguments[0] aliasing it. + String src = "function f(opt_a2) {" + + " var buffer;" + + " if (opt_a2) {" + + " for(var i = 0; i < arguments.length; i++) {" + + " buffer += arguments[i];" + + " }" + + " }" + + " return buffer;" + + "}"; + test(src, src); + } + + public void testDeterministic() { + // Make the variable interference graph a pentagon. + // a - b + // / \ + // e c + // \ / + // d + // The coloring partitioning would be: + // a = { a, c } + // b = { b, d } + // e = { e } + inFunction("var a,b,c,d,e;" + + " a=1; b=1; a; b;" + + " b=1; c=1; b; c;" + + " c=1; d=1; c; d;" + + " d=1; e=1; d; e;" + + " e=1; a=1; e; a;", + + "var a,b, e;" + + " a=1; b=1; a; b;" + + " b=1; a=1; b; a;" + + " a=1; b=1; a; b;" + + " b=1; e=1; b; e;" + + " e=1; a=1; e; a;"); + + // If we favor "d" first by declaring "d" earlier, + // the coloring partitioning would be: + // b = { b, e } + // d = { d, a } + // c = { c } + inFunction("var d,a,b,c,e;" + + " a=1; b=1; a; b;" + + " b=1; c=1; b; c;" + + " c=1; d=1; c; d;" + + " d=1; e=1; d; e;" + + " e=1; a=1; e; a;", + + "var d, b,c ;" + + " d=1; b=1; d; b;" + + " b=1; c=1; b; c;" + + " c=1; d=1; c; d;" + + " d=1; b=1; d; b;" + + " b=1; d=1; b; d;"); + } + + // Sometimes live range can be cross even within a VAR declaration. + public void testVarLiveRangeCross() { + inFunction("var a={}; var b=a.S(); b", + "var a={}; a=a.S(); a"); + inFunction("var a={}; var b=a.S(), c=b.SS(); b; c", + "var a={}; var b=a.S(), a=b.SS(); b; a"); + inFunction("var a={}; var b=a.S(), c=a.SS(), d=a.SSS(); b; c; d", + "var a={}; var b=a.S(), c=a.SS(), a=a.SSS(); b; c; a"); + inFunction("var a={}; var b=a.S(), c=a.SS(), d=a.SSS(); b; c; d", + "var a={}; var b=a.S(), c=a.SS(), a=a.SSS(); b; c; a"); + inFunction("var a={}; d=1; d; var b=a.S(), c=a.SS(), d=a.SSS(); b; c; d"); + } + + public void testBug1445366() { + // An assignment might not be complete if the RHS throws an exception. + inFunction( + " var iframe = getFrame();" + + " try {" + + " var win = iframe.contentWindow;" + + " } catch (e) {" + + " } finally {" + + " if (win)" + + " this.setupWinUtil_();" + + " else" + + " this.load();" + + " }"); + + // Verify that we can still coalesce it if there are no handlers. + inFunction( + " var iframe = getFrame();" + + " var win = iframe.contentWindow;" + + " if (win)" + + " this.setupWinUtil_();" + + " else" + + " this.load();", + + " var iframe = getFrame();" + + " iframe = iframe.contentWindow;" + + " if (iframe)" + + " this.setupWinUtil_();" + + " else" + + " this.load();"); + } + + public void testCannotReuseAnyParamsBug() { + testSame("function handleKeyboardShortcut(e, key, isModifierPressed) {\n" + + " if (!isModifierPressed) {\n" + + " return false;\n" + + " }\n" + + " var command;\n" + + " switch (key) {\n" + + " case 'b': // Ctrl+B\n" + + " command = COMMAND.BOLD;\n" + + " break;\n" + + " case 'i': // Ctrl+I\n" + + " command = COMMAND.ITALIC;\n" + + " break;\n" + + " case 'u': // Ctrl+U\n" + + " command = COMMAND.UNDERLINE;\n" + + " break;\n" + + " case 's': // Ctrl+S\n" + + " return true;\n" + + " }\n" + + "\n" + + " if (command) {\n" + + " this.fieldObject.execCommand(command);\n" + + " return true;\n" + + " }\n" + + "\n" + + " return false;\n" + + "};"); + } + + public void testForInWithAssignment() { + inFunction( + "var _f = function (commands) {" + + " var k, v, ref;" + + " for (k in ref = commands) {" + + " v = ref[k];" + + " alert(k + ':' + v);" + + " }" + + "}", + + "var _f = function (commands) {" + + " var k,ref;" + + " for (k in ref = commands) {" + + " commands = ref[k];" + + " alert(k + ':' + commands);" + + " }" + + "}" + ); + } + + public void testUsePseduoNames() { + usePseudoName = true; + inFunction("var x = 0; print(x ); var y = 1; print( y)", + "var x_y = 0; print(x_y); x_y = 1; print(x_y)"); + + inFunction("var x_y = 1; var x = 0; print(x ); var y = 1;" + + "print( y); print(x_y);", + + "var x_y = 1; var x_y$ = 0; print(x_y$); x_y$ = 1;" + "" + + "print(x_y$); print(x_y);"); + + inFunction("var x_y = 1; function f() {" + + "var x = 0; print(x ); var y = 1; print( y);" + + "print(x_y);}", + + "var x_y = 1; function f() {" + + "var x_y$ = 0; print(x_y$); x_y$ = 1; print(x_y$);" + + "print(x_y);}"); + + inFunction("var x = 0; print(x ); var y = 1; print( y); " + + "var closure_var; function bar() { print(closure_var); }", + "var x_y = 0; print(x_y); x_y = 1; print(x_y); " + + "var closure_var; function bar() { print(closure_var); }"); + } + + public void testMaxVars() { + String code = ""; + for (int i = 0; + i < LiveVariablesAnalysis.MAX_VARIABLES_TO_ANALYZE + 1; i++) { + code += String.format("var x%d = 0; print(x%d);", i, i); + } + inFunction(code); + } + + private void inFunction(String src) { + inFunction(src, src); + } + + private void inFunction(String src, String expected) { + test("function FUNC(){" + src + "}", + "function FUNC(){" + expected + "}"); + } + + private void test(String src) { + test(src, src); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CodePrinterTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CodePrinterTest.java new file mode 100644 index 0000000..ac50493 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CodePrinterTest.java @@ -0,0 +1,1498 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.CompilerOptions.LanguageMode; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import junit.framework.TestCase; + +import java.util.List; + +public class CodePrinterTest extends TestCase { + boolean trustedStrings = true; + + @Override public void setUp() { + trustedStrings = true; + } + + Node parse(String js) { + return parse(js, false); + } + + Node parse(String js, boolean checkTypes) { + Compiler compiler = new Compiler(); + CompilerOptions options = new CompilerOptions(); + options.setTrustedStrings(trustedStrings); + + // Allow getters and setters. + options.setLanguageIn(LanguageMode.ECMASCRIPT5); + compiler.initOptions(options); + Node n = compiler.parseTestCode(js); + + if (checkTypes) { + DefaultPassConfig passConfig = new DefaultPassConfig(null); + CompilerPass typeResolver = passConfig.resolveTypes.create(compiler); + Node externs = new Node(Token.SCRIPT); + externs.setInputId(new InputId("externs")); + Node externAndJsRoot = new Node(Token.BLOCK, externs, n); + externAndJsRoot.setIsSyntheticBlock(true); + typeResolver.process(externs, n); + CompilerPass inferTypes = passConfig.inferTypes.create(compiler); + inferTypes.process(externs, n); + } + + checkUnexpectedErrorsOrWarnings(compiler, 0); + return n; + } + + private static void checkUnexpectedErrorsOrWarnings( + Compiler compiler, int expected) { + int actual = compiler.getErrors().length + compiler.getWarnings().length; + if (actual != expected) { + String msg = ""; + for (JSError err : compiler.getErrors()) { + msg += "Error:" + err.toString() + "\n"; + } + for (JSError err : compiler.getWarnings()) { + msg += "Warning:" + err.toString() + "\n"; + } + assertEquals("Unexpected warnings or errors.\n " + msg, expected, actual); + } + } + + String parsePrint(String js, boolean prettyprint, int lineThreshold) { + CompilerOptions options = new CompilerOptions(); + options.setTrustedStrings(trustedStrings); + options.setPrettyPrint(prettyprint); + options.setLineLengthThreshold(lineThreshold); + return new CodePrinter.Builder(parse(js)).setCompilerOptions(options) + .build(); + } + + String parsePrint(String js, boolean prettyprint, boolean lineBreak, + int lineThreshold) { + CompilerOptions options = new CompilerOptions(); + options.setTrustedStrings(trustedStrings); + options.setPrettyPrint(prettyprint); + options.setLineLengthThreshold(lineThreshold); + options.setLineBreak(lineBreak); + return new CodePrinter.Builder(parse(js)).setCompilerOptions(options) + .build(); + } + + String parsePrint(String js, boolean prettyprint, boolean lineBreak, + boolean preferLineBreakAtEof, int lineThreshold) { + CompilerOptions options = new CompilerOptions(); + options.setTrustedStrings(trustedStrings); + options.setPrettyPrint(prettyprint); + options.setLineLengthThreshold(lineThreshold); + options.setPreferLineBreakAtEndOfFile(preferLineBreakAtEof); + options.setLineBreak(lineBreak); + return new CodePrinter.Builder(parse(js)).setCompilerOptions(options) + .build(); + } + + String parsePrint(String js, boolean prettyprint, boolean lineBreak, + int lineThreshold, boolean outputTypes) { + CompilerOptions options = new CompilerOptions(); + options.setTrustedStrings(trustedStrings); + options.setPrettyPrint(prettyprint); + options.setLineLengthThreshold(lineThreshold); + options.setLineBreak(lineBreak); + return new CodePrinter.Builder(parse(js, true)).setCompilerOptions(options) + .setOutputTypes(outputTypes) + .build(); + } + + String parsePrint(String js, boolean prettyprint, boolean lineBreak, + int lineThreshold, boolean outputTypes, + boolean tagAsStrict) { + CompilerOptions options = new CompilerOptions(); + options.setTrustedStrings(trustedStrings); + options.setPrettyPrint(prettyprint); + options.setLineLengthThreshold(lineThreshold); + options.setLineBreak(lineBreak); + return new CodePrinter.Builder(parse(js, true)).setCompilerOptions(options) + .setOutputTypes(outputTypes) + .setTagAsStrict(tagAsStrict) + .build(); + } + + + String printNode(Node n) { + CompilerOptions options = new CompilerOptions(); + options.setLineLengthThreshold(CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD); + return new CodePrinter.Builder(n).setCompilerOptions(options).build(); + } + + void assertPrintNode(String expectedJs, Node ast) { + assertEquals(expectedJs, printNode(ast)); + } + + public void testPrint() { + assertPrint("10 + a + b", "10+a+b"); + assertPrint("10 + (30*50)", "10+30*50"); + assertPrint("with(x) { x + 3; }", "with(x)x+3"); + assertPrint("\"aa'a\"", "\"aa'a\""); + assertPrint("\"aa\\\"a\"", "'aa\"a'"); + assertPrint("function foo()\n{return 10;}", "function foo(){return 10}"); + assertPrint("a instanceof b", "a instanceof b"); + assertPrint("typeof(a)", "typeof a"); + assertPrint( + "var foo = x ? { a : 1 } : {a: 3, b:4, \"default\": 5, \"foo-bar\": 6}", + "var foo=x?{a:1}:{a:3,b:4,\"default\":5,\"foo-bar\":6}"); + + // Safari: needs ';' at the end of a throw statement + assertPrint("function foo(){throw 'error';}", + "function foo(){throw\"error\";}"); + // Safari 3 needs a "{" around a single function + assertPrint("if (true) function foo(){return}", + "if(true){function foo(){return}}"); + + assertPrint("var x = 10; { var y = 20; }", "var x=10;var y=20"); + + assertPrint("while (x-- > 0);", "while(x-- >0);"); + assertPrint("x-- >> 1", "x-- >>1"); + + assertPrint("(function () {})(); ", + "(function(){})()"); + + // Associativity + assertPrint("var a,b,c,d;a || (b&& c) && (a || d)", + "var a,b,c,d;a||b&&c&&(a||d)"); + assertPrint("var a,b,c; a || (b || c); a * (b * c); a | (b | c)", + "var a,b,c;a||b||c;a*b*c;a|b|c"); + assertPrint("var a,b,c; a / b / c;a / (b / c); a - (b - c);", + "var a,b,c;a/b/c;a/(b/c);a-(b-c)"); + assertPrint("var a,b; a = b = 3;", + "var a,b;a=b=3"); + assertPrint("var a,b,c,d; a = (b = c = (d = 3));", + "var a,b,c,d;a=b=c=d=3"); + assertPrint("var a,b,c; a += (b = c += 3);", + "var a,b,c;a+=b=c+=3"); + assertPrint("var a,b,c; a *= (b -= c);", + "var a,b,c;a*=b-=c"); + + // Precedence + assertPrint("a ? delete b[0] : 3", "a?delete b[0]:3"); + assertPrint("(delete a[0])/10", "delete a[0]/10"); + + // optional '()' for new + + // simple new + assertPrint("new A", "new A"); + assertPrint("new A()", "new A"); + assertPrint("new A('x')", "new A(\"x\")"); + + // calling instance method directly after new + assertPrint("new A().a()", "(new A).a()"); + assertPrint("(new A).a()", "(new A).a()"); + + // this case should be fixed + assertPrint("new A('y').a()", "(new A(\"y\")).a()"); + + // internal class + assertPrint("new A.B", "new A.B"); + assertPrint("new A.B()", "new A.B"); + assertPrint("new A.B('z')", "new A.B(\"z\")"); + + // calling instance method directly after new internal class + assertPrint("(new A.B).a()", "(new A.B).a()"); + assertPrint("new A.B().a()", "(new A.B).a()"); + // this case should be fixed + assertPrint("new A.B('w').a()", "(new A.B(\"w\")).a()"); + + // Operators: make sure we don't convert binary + and unary + into ++ + assertPrint("x + +y", "x+ +y"); + assertPrint("x - (-y)", "x- -y"); + assertPrint("x++ +y", "x++ +y"); + assertPrint("x-- -y", "x-- -y"); + assertPrint("x++ -y", "x++-y"); + + // Label + assertPrint("foo:for(;;){break foo;}", "foo:for(;;)break foo"); + assertPrint("foo:while(1){continue foo;}", "foo:while(1)continue foo"); + + // Object literals. + assertPrint("({})", "({})"); + assertPrint("var x = {};", "var x={}"); + assertPrint("({}).x", "({}).x"); + assertPrint("({})['x']", "({})[\"x\"]"); + assertPrint("({}) instanceof Object", "({})instanceof Object"); + assertPrint("({}) || 1", "({})||1"); + assertPrint("1 || ({})", "1||{}"); + assertPrint("({}) ? 1 : 2", "({})?1:2"); + assertPrint("0 ? ({}) : 2", "0?{}:2"); + assertPrint("0 ? 1 : ({})", "0?1:{}"); + assertPrint("typeof ({})", "typeof{}"); + assertPrint("f({})", "f({})"); + + // Anonymous function expressions. + assertPrint("(function(){})", "(function(){})"); + assertPrint("(function(){})()", "(function(){})()"); + assertPrint("(function(){})instanceof Object", + "(function(){})instanceof Object"); + assertPrint("(function(){}).bind().call()", + "(function(){}).bind().call()"); + assertPrint("var x = function() { };", "var x=function(){}"); + assertPrint("var x = function() { }();", "var x=function(){}()"); + assertPrint("(function() {}), 2", "(function(){}),2"); + + // Name functions expression. + assertPrint("(function f(){})", "(function f(){})"); + + // Function declaration. + assertPrint("function f(){}", "function f(){}"); + + // Make sure we don't treat non-Latin character escapes as raw strings. + assertPrint("({ 'a': 4, '\\u0100': 4 })", "({\"a\":4,\"\\u0100\":4})"); + assertPrint("({ a: 4, '\\u0100': 4 })", "({a:4,\"\\u0100\":4})"); + + // Test if statement and for statements with single statements in body. + assertPrint("if (true) { alert();}", "if(true)alert()"); + assertPrint("if (false) {} else {alert(\"a\");}", + "if(false);else alert(\"a\")"); + assertPrint("for(;;) { alert();};", "for(;;)alert()"); + + assertPrint("do { alert(); } while(true);", + "do alert();while(true)"); + assertPrint("myLabel: { alert();}", + "myLabel:alert()"); + assertPrint("myLabel: for(;;) continue myLabel;", + "myLabel:for(;;)continue myLabel"); + + // Test nested var statement + assertPrint("if (true) var x; x = 4;", "if(true)var x;x=4"); + + // Non-latin identifier. Make sure we keep them escaped. + assertPrint("\\u00fb", "\\u00fb"); + assertPrint("\\u00fa=1", "\\u00fa=1"); + assertPrint("function \\u00f9(){}", "function \\u00f9(){}"); + assertPrint("x.\\u00f8", "x.\\u00f8"); + assertPrint("x.\\u00f8", "x.\\u00f8"); + assertPrint("abc\\u4e00\\u4e01jkl", "abc\\u4e00\\u4e01jkl"); + + // Test the right-associative unary operators for spurious parens + assertPrint("! ! true", "!!true"); + assertPrint("!(!(true))", "!!true"); + assertPrint("typeof(void(0))", "typeof void 0"); + assertPrint("typeof(void(!0))", "typeof void!0"); + assertPrint("+ - + + - + 3", "+-+ +-+3"); // chained unary plus/minus + assertPrint("+(--x)", "+--x"); + assertPrint("-(++x)", "-++x"); + + // needs a space to prevent an ambiguous parse + assertPrint("-(--x)", "- --x"); + assertPrint("!(~~5)", "!~~5"); + assertPrint("~(a/b)", "~(a/b)"); + + // Preserve parens to overcome greedy binding of NEW + assertPrint("new (foo.bar()).factory(baz)", "new (foo.bar().factory)(baz)"); + assertPrint("new (bar()).factory(baz)", "new (bar().factory)(baz)"); + assertPrint("new (new foobar(x)).factory(baz)", + "new (new foobar(x)).factory(baz)"); + + // Make sure that HOOK is right associative + assertPrint("a ? b : (c ? d : e)", "a?b:c?d:e"); + assertPrint("a ? (b ? c : d) : e", "a?b?c:d:e"); + assertPrint("(a ? b : c) ? d : e", "(a?b:c)?d:e"); + + // Test nested ifs + assertPrint("if (x) if (y); else;", "if(x)if(y);else;"); + + // Test comma. + assertPrint("a,b,c", "a,b,c"); + assertPrint("(a,b),c", "a,b,c"); + assertPrint("a,(b,c)", "a,b,c"); + assertPrint("x=a,b,c", "x=a,b,c"); + assertPrint("x=(a,b),c", "x=(a,b),c"); + assertPrint("x=a,(b,c)", "x=a,b,c"); + assertPrint("x=a,y=b,z=c", "x=a,y=b,z=c"); + assertPrint("x=(a,y=b,z=c)", "x=(a,y=b,z=c)"); + assertPrint("x=[a,b,c,d]", "x=[a,b,c,d]"); + assertPrint("x=[(a,b,c),d]", "x=[(a,b,c),d]"); + assertPrint("x=[(a,(b,c)),d]", "x=[(a,b,c),d]"); + assertPrint("x=[a,(b,c,d)]", "x=[a,(b,c,d)]"); + assertPrint("var x=(a,b)", "var x=(a,b)"); + assertPrint("var x=a,b,c", "var x=a,b,c"); + assertPrint("var x=(a,b),c", "var x=(a,b),c"); + assertPrint("var x=a,b=(c,d)", "var x=a,b=(c,d)"); + assertPrint("foo(a,b,c,d)", "foo(a,b,c,d)"); + assertPrint("foo((a,b,c),d)", "foo((a,b,c),d)"); + assertPrint("foo((a,(b,c)),d)", "foo((a,b,c),d)"); + assertPrint("f(a+b,(c,d,(e,f,g)))", "f(a+b,(c,d,e,f,g))"); + assertPrint("({}) , 1 , 2", "({}),1,2"); + assertPrint("({}) , {} , {}", "({}),{},{}"); + + // EMPTY nodes + assertPrint("if (x){}", "if(x);"); + assertPrint("if(x);", "if(x);"); + assertPrint("if(x)if(y);", "if(x)if(y);"); + assertPrint("if(x){if(y);}", "if(x)if(y);"); + assertPrint("if(x){if(y){};;;}", "if(x)if(y);"); + assertPrint("if(x){;;function y(){};;}", "if(x){function y(){}}"); + } + + public void testBreakTrustedStrings() { + // Break scripts + assertPrint("''", "\"\\x3c/script>\""); + assertPrint("\" \"", "\"\\x3c/script> \\x3c/SCRIPT>\""); + + assertPrint("'-->'", "\"--\\x3e\""); + assertPrint("']]>'", "\"]]\\x3e\""); + assertPrint("' -->'", "\" --\\x3e\\x3c/script>\""); + + assertPrint("/--> <\\/script>/g", "/--\\x3e <\\/script>/g"); + + // Break HTML start comments. Certain versions of WebKit + // begin an HTML comment when they see this. + assertPrint("''", + "\"\\x3c!-- I am a string --\\x3e\""); + + assertPrint("'<=&>'", "\"<=&>\""); + } + + public void testBreakUntrustedStrings() { + trustedStrings = false; + + // Break scripts + assertPrint("''", "\"\\x3c/script\\x3e\""); + assertPrint("\" \"", "\"\\x3c/script\\x3e \\x3c/SCRIPT\\x3e\""); + + assertPrint("'-->'", "\"--\\x3e\""); + assertPrint("']]>'", "\"]]\\x3e\""); + assertPrint("' -->'", "\" --\\x3e\\x3c/script\\x3e\""); + + assertPrint("/--> <\\/script>/g", "/--\\x3e <\\/script>/g"); + + // Break HTML start comments. Certain versions of WebKit + // begin an HTML comment when they see this. + assertPrint("''", + "\"\\x3c!-- I am a string --\\x3e\""); + + assertPrint("'<=&>'", "\"\\x3c\\x3d\\x26\\x3e\""); + assertPrint("/(?=x)/", "/(?=x)/"); + } + + public void testPrintArray() { + assertPrint("[void 0, void 0]", "[void 0,void 0]"); + assertPrint("[undefined, undefined]", "[undefined,undefined]"); + assertPrint("[ , , , undefined]", "[,,,undefined]"); + assertPrint("[ , , , 0]", "[,,,0]"); + } + + public void testHook() { + assertPrint("a ? b = 1 : c = 2", "a?b=1:c=2"); + assertPrint("x = a ? b = 1 : c = 2", "x=a?b=1:c=2"); + assertPrint("(x = a) ? b = 1 : c = 2", "(x=a)?b=1:c=2"); + + assertPrint("x, a ? b = 1 : c = 2", "x,a?b=1:c=2"); + assertPrint("x, (a ? b = 1 : c = 2)", "x,a?b=1:c=2"); + assertPrint("(x, a) ? b = 1 : c = 2", "(x,a)?b=1:c=2"); + + assertPrint("a ? (x, b) : c = 2", "a?(x,b):c=2"); + assertPrint("a ? b = 1 : (x,c)", "a?b=1:(x,c)"); + + assertPrint("a ? b = 1 : c = 2 + x", "a?b=1:c=2+x"); + assertPrint("(a ? b = 1 : c = 2) + x", "(a?b=1:c=2)+x"); + assertPrint("a ? b = 1 : (c = 2) + x", "a?b=1:(c=2)+x"); + + assertPrint("a ? (b?1:2) : 3", "a?b?1:2:3"); + } + + public void testPrintInOperatorInForLoop() { + // Check for in expression in for's init expression. + // Check alone, with + (higher precedence), with ?: (lower precedence), + // and with conditional. + assertPrint("var a={}; for (var i = (\"length\" in a); i;) {}", + "var a={};for(var i=(\"length\"in a);i;);"); + assertPrint("var a={}; for (var i = (\"length\" in a) ? 0 : 1; i;) {}", + "var a={};for(var i=(\"length\"in a)?0:1;i;);"); + assertPrint("var a={}; for (var i = (\"length\" in a) + 1; i;) {}", + "var a={};for(var i=(\"length\"in a)+1;i;);"); + assertPrint("var a={};for (var i = (\"length\" in a|| \"size\" in a);;);", + "var a={};for(var i=(\"length\"in a)||(\"size\"in a);;);"); + assertPrint("var a={};for (var i = a || a || (\"size\" in a);;);", + "var a={};for(var i=a||a||(\"size\"in a);;);"); + + // Test works with unary operators and calls. + assertPrint("var a={}; for (var i = -(\"length\" in a); i;) {}", + "var a={};for(var i=-(\"length\"in a);i;);"); + assertPrint("var a={};function b_(p){ return p;};" + + "for(var i=1,j=b_(\"length\" in a);;) {}", + "var a={};function b_(p){return p}" + + "for(var i=1,j=b_(\"length\"in a);;);"); + + // Test we correctly handle an in operator in the test clause. + assertPrint("var a={}; for (;(\"length\" in a);) {}", + "var a={};for(;\"length\"in a;);"); + } + + public void testLiteralProperty() { + assertPrint("(64).toString()", "(64).toString()"); + } + + private void assertPrint(String js, String expected) { + parse(expected); // validate the expected string is valid JS + assertEquals(expected, + parsePrint(js, false, CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD)); + } + + private void assertPrintSame(String js) { + assertPrint(js, js); + } + + // Make sure that the code generator doesn't associate an + // else clause with the wrong if clause. + public void testAmbiguousElseClauses() { + assertPrintNode("if(x)if(y);else;", + new Node(Token.IF, + Node.newString(Token.NAME, "x"), + new Node(Token.BLOCK, + new Node(Token.IF, + Node.newString(Token.NAME, "y"), + new Node(Token.BLOCK), + + // ELSE clause for the inner if + new Node(Token.BLOCK))))); + + assertPrintNode("if(x){if(y);}else;", + new Node(Token.IF, + Node.newString(Token.NAME, "x"), + new Node(Token.BLOCK, + new Node(Token.IF, + Node.newString(Token.NAME, "y"), + new Node(Token.BLOCK))), + + // ELSE clause for the outer if + new Node(Token.BLOCK))); + + assertPrintNode("if(x)if(y);else{if(z);}else;", + new Node(Token.IF, + Node.newString(Token.NAME, "x"), + new Node(Token.BLOCK, + new Node(Token.IF, + Node.newString(Token.NAME, "y"), + new Node(Token.BLOCK), + new Node(Token.BLOCK, + new Node(Token.IF, + Node.newString(Token.NAME, "z"), + new Node(Token.BLOCK))))), + + // ELSE clause for the outermost if + new Node(Token.BLOCK))); + } + + public void testLineBreak() { + // line break after function if in a statement context + assertLineBreak("function a() {}\n" + + "function b() {}", + "function a(){}\n" + + "function b(){}\n"); + + // line break after ; after a function + assertLineBreak("var a = {};\n" + + "a.foo = function () {}\n" + + "function b() {}", + "var a={};a.foo=function(){};\n" + + "function b(){}\n"); + + // break after comma after a function + assertLineBreak("var a = {\n" + + " b: function() {},\n" + + " c: function() {}\n" + + "};\n" + + "alert(a);", + + "var a={b:function(){},\n" + + "c:function(){}};\n" + + "alert(a)"); + } + + private void assertLineBreak(String js, String expected) { + assertEquals(expected, + parsePrint(js, false, true, + CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD)); + } + + public void testPreferLineBreakAtEndOfFile() { + // short final line, no previous break, do nothing + assertLineBreakAtEndOfFile( + "\"1234567890\";", + "\"1234567890\"", + "\"1234567890\""); + + // short final line, shift previous break to end + assertLineBreakAtEndOfFile( + "\"123456789012345678901234567890\";\"1234567890\"", + "\"123456789012345678901234567890\";\n\"1234567890\"", + "\"123456789012345678901234567890\"; \"1234567890\";\n"); + assertLineBreakAtEndOfFile( + "var12345678901234567890123456 instanceof Object;", + "var12345678901234567890123456 instanceof\nObject", + "var12345678901234567890123456 instanceof Object;\n"); + + // long final line, no previous break, add a break at end + assertLineBreakAtEndOfFile( + "\"1234567890\";\"12345678901234567890\";", + "\"1234567890\";\"12345678901234567890\"", + "\"1234567890\";\"12345678901234567890\";\n"); + + // long final line, previous break, add a break at end + assertLineBreakAtEndOfFile( + "\"123456789012345678901234567890\";\"12345678901234567890\";", + "\"123456789012345678901234567890\";\n\"12345678901234567890\"", + "\"123456789012345678901234567890\";\n\"12345678901234567890\";\n"); + } + + private void assertLineBreakAtEndOfFile(String js, + String expectedWithoutBreakAtEnd, String expectedWithBreakAtEnd) { + assertEquals(expectedWithoutBreakAtEnd, + parsePrint(js, false, false, false, 30)); + assertEquals(expectedWithBreakAtEnd, + parsePrint(js, false, false, true, 30)); + } + + public void testPrettyPrinter() { + // Ensure that the pretty printer inserts line breaks at appropriate + // places. + assertPrettyPrint("(function(){})();","(function() {\n})();\n"); + assertPrettyPrint("var a = (function() {});alert(a);", + "var a = function() {\n};\nalert(a);\n"); + + // Check we correctly handle putting brackets around all if clauses so + // we can put breakpoints inside statements. + assertPrettyPrint("if (1) {}", + "if(1) {\n" + + "}\n"); + assertPrettyPrint("if (1) {alert(\"\");}", + "if(1) {\n" + + " alert(\"\")\n" + + "}\n"); + assertPrettyPrint("if (1)alert(\"\");", + "if(1) {\n" + + " alert(\"\")\n" + + "}\n"); + assertPrettyPrint("if (1) {alert();alert();}", + "if(1) {\n" + + " alert();\n" + + " alert()\n" + + "}\n"); + + // Don't add blocks if they weren't there already. + assertPrettyPrint("label: alert();", + "label:alert();\n"); + + // But if statements and loops get blocks automagically. + assertPrettyPrint("if (1) alert();", + "if(1) {\n" + + " alert()\n" + + "}\n"); + assertPrettyPrint("for (;;) alert();", + "for(;;) {\n" + + " alert()\n" + + "}\n"); + + assertPrettyPrint("while (1) alert();", + "while(1) {\n" + + " alert()\n" + + "}\n"); + + // Do we put else clauses in blocks? + assertPrettyPrint("if (1) {} else {alert(a);}", + "if(1) {\n" + + "}else {\n alert(a)\n}\n"); + + // Do we add blocks to else clauses? + assertPrettyPrint("if (1) alert(a); else alert(b);", + "if(1) {\n" + + " alert(a)\n" + + "}else {\n" + + " alert(b)\n" + + "}\n"); + + // Do we put for bodies in blocks? + assertPrettyPrint("for(;;) { alert();}", + "for(;;) {\n" + + " alert()\n" + + "}\n"); + assertPrettyPrint("for(;;) {}", + "for(;;) {\n" + + "}\n"); + assertPrettyPrint("for(;;) { alert(); alert(); }", + "for(;;) {\n" + + " alert();\n" + + " alert()\n" + + "}\n"); + + // How about do loops? + assertPrettyPrint("do { alert(); } while(true);", + "do {\n" + + " alert()\n" + + "}while(true);\n"); + + // label? + assertPrettyPrint("myLabel: { alert();}", + "myLabel: {\n" + + " alert()\n" + + "}\n"); + + // Don't move the label on a loop, because then break {label} and + // continue {label} won't work. + assertPrettyPrint("myLabel: for(;;) continue myLabel;", + "myLabel:for(;;) {\n" + + " continue myLabel\n" + + "}\n"); + + assertPrettyPrint("var a;", "var a;\n"); + } + + public void testPrettyPrinter2() { + assertPrettyPrint( + "if(true) f();", + "if(true) {\n" + + " f()\n" + + "}\n"); + + assertPrettyPrint( + "if (true) { f() } else { g() }", + "if(true) {\n" + + " f()\n" + + "}else {\n" + + " g()\n" + + "}\n"); + + assertPrettyPrint( + "if(true) f(); for(;;) g();", + "if(true) {\n" + + " f()\n" + + "}\n" + + "for(;;) {\n" + + " g()\n" + + "}\n"); + } + + public void testPrettyPrinter3() { + assertPrettyPrint( + "try {} catch(e) {}if (1) {alert();alert();}", + "try {\n" + + "}catch(e) {\n" + + "}\n" + + "if(1) {\n" + + " alert();\n" + + " alert()\n" + + "}\n"); + + assertPrettyPrint( + "try {} finally {}if (1) {alert();alert();}", + "try {\n" + + "}finally {\n" + + "}\n" + + "if(1) {\n" + + " alert();\n" + + " alert()\n" + + "}\n"); + + assertPrettyPrint( + "try {} catch(e) {} finally {} if (1) {alert();alert();}", + "try {\n" + + "}catch(e) {\n" + + "}finally {\n" + + "}\n" + + "if(1) {\n" + + " alert();\n" + + " alert()\n" + + "}\n"); + } + + public void testPrettyPrinter4() { + assertPrettyPrint( + "function f() {}if (1) {alert();}", + "function f() {\n" + + "}\n" + + "if(1) {\n" + + " alert()\n" + + "}\n"); + + assertPrettyPrint( + "var f = function() {};if (1) {alert();}", + "var f = function() {\n" + + "};\n" + + "if(1) {\n" + + " alert()\n" + + "}\n"); + + assertPrettyPrint( + "(function() {})();if (1) {alert();}", + "(function() {\n" + + "})();\n" + + "if(1) {\n" + + " alert()\n" + + "}\n"); + + assertPrettyPrint( + "(function() {alert();alert();})();if (1) {alert();}", + "(function() {\n" + + " alert();\n" + + " alert()\n" + + "})();\n" + + "if(1) {\n" + + " alert()\n" + + "}\n"); + } + + public void testTypeAnnotations() { + assertTypeAnnotations( + "/** @constructor */ function Foo(){}", + "/**\n * @return {undefined}\n * @constructor\n */\n" + + "function Foo() {\n}\n"); + } + + public void testTypeAnnotationsTypeDef() { + // TODO(johnlenz): It would be nice if there were some way to preserve + // typedefs but currently they are resolved into the basic types in the + // type registry. + assertTypeAnnotations( + "/** @typedef {Array.} */ goog.java.Long;\n" + + "/** @param {!goog.java.Long} a*/\n" + + "function f(a){};\n", + "goog.java.Long;\n" + + "/**\n" + + " * @param {(Array.|null)} a\n" + + " * @return {undefined}\n" + + " */\n" + + "function f(a) {\n}\n"); + } + + public void testTypeAnnotationsAssign() { + assertTypeAnnotations("/** @constructor */ var Foo = function(){}", + "/**\n * @return {undefined}\n * @constructor\n */\n" + + "var Foo = function() {\n};\n"); + } + + public void testTypeAnnotationsNamespace() { + assertTypeAnnotations("var a = {};" + + "/** @constructor */ a.Foo = function(){}", + "var a = {};\n" + + "/**\n * @return {undefined}\n * @constructor\n */\n" + + "a.Foo = function() {\n};\n"); + } + + public void testTypeAnnotationsMemberSubclass() { + assertTypeAnnotations("var a = {};" + + "/** @constructor */ a.Foo = function(){};" + + "/** @constructor \n @extends {a.Foo} */ a.Bar = function(){}", + "var a = {};\n" + + "/**\n * @return {undefined}\n * @constructor\n */\n" + + "a.Foo = function() {\n};\n" + + "/**\n * @return {undefined}\n * @extends {a.Foo}\n" + + " * @constructor\n */\n" + + "a.Bar = function() {\n};\n"); + } + + public void testTypeAnnotationsInterface() { + assertTypeAnnotations("var a = {};" + + "/** @interface */ a.Foo = function(){};" + + "/** @interface \n @extends {a.Foo} */ a.Bar = function(){}", + "var a = {};\n" + + "/**\n * @interface\n */\n" + + "a.Foo = function() {\n};\n" + + "/**\n * @extends {a.Foo}\n" + + " * @interface\n */\n" + + "a.Bar = function() {\n};\n"); + } + + public void testTypeAnnotationsMultipleInterface() { + assertTypeAnnotations("var a = {};" + + "/** @interface */ a.Foo1 = function(){};" + + "/** @interface */ a.Foo2 = function(){};" + + "/** @interface \n @extends {a.Foo1} \n @extends {a.Foo2} */" + + "a.Bar = function(){}", + "var a = {};\n" + + "/**\n * @interface\n */\n" + + "a.Foo1 = function() {\n};\n" + + "/**\n * @interface\n */\n" + + "a.Foo2 = function() {\n};\n" + + "/**\n * @extends {a.Foo1}\n" + + " * @extends {a.Foo2}\n" + + " * @interface\n */\n" + + "a.Bar = function() {\n};\n"); + } + + public void testTypeAnnotationsMember() { + assertTypeAnnotations("var a = {};" + + "/** @constructor */ a.Foo = function(){}" + + "/** @param {string} foo\n" + + " * @return {number} */\n" + + "a.Foo.prototype.foo = function(foo) { return 3; };" + + "/** @type {string|undefined} */" + + "a.Foo.prototype.bar = '';", + "var a = {};\n" + + "/**\n * @return {undefined}\n * @constructor\n */\n" + + "a.Foo = function() {\n};\n" + + "/**\n" + + " * @param {string} foo\n" + + " * @return {number}\n" + + " */\n" + + "a.Foo.prototype.foo = function(foo) {\n return 3\n};\n" + + "/** @type {string} */\n" + + "a.Foo.prototype.bar = \"\";\n"); + } + + public void testTypeAnnotationsImplements() { + assertTypeAnnotations("var a = {};" + + "/** @constructor */ a.Foo = function(){};\n" + + "/** @interface */ a.I = function(){};\n" + + "/** @interface */ a.I2 = function(){};\n" + + "/** @constructor \n @extends {a.Foo}\n" + + " * @implements {a.I} \n @implements {a.I2}\n" + + "*/ a.Bar = function(){}", + "var a = {};\n" + + "/**\n * @return {undefined}\n * @constructor\n */\n" + + "a.Foo = function() {\n};\n" + + "/**\n * @interface\n */\n" + + "a.I = function() {\n};\n" + + "/**\n * @interface\n */\n" + + "a.I2 = function() {\n};\n" + + "/**\n * @return {undefined}\n * @extends {a.Foo}\n" + + " * @implements {a.I}\n" + + " * @implements {a.I2}\n * @constructor\n */\n" + + "a.Bar = function() {\n};\n"); + } + + public void testTypeAnnotationsDispatcher1() { + assertTypeAnnotations( + "var a = {};\n" + + "/** \n" + + " * @constructor \n" + + " * @javadispatch \n" + + " */\n" + + "a.Foo = function(){}", + "var a = {};\n" + + "/**\n" + + " * @return {undefined}\n" + + " * @constructor\n" + + " * @javadispatch\n" + + " */\n" + + "a.Foo = function() {\n" + + "};\n"); + } + + public void testTypeAnnotationsDispatcher2() { + assertTypeAnnotations( + "var a = {};\n" + + "/** \n" + + " * @constructor \n" + + " */\n" + + "a.Foo = function(){}\n" + + "/**\n" + + " * @javadispatch\n" + + " */\n" + + "a.Foo.prototype.foo = function() {};", + + "var a = {};\n" + + "/**\n" + + " * @return {undefined}\n" + + " * @constructor\n" + + " */\n" + + "a.Foo = function() {\n" + + "};\n" + + "/**\n" + + " * @return {undefined}\n" + + " * @javadispatch\n" + + " */\n" + + "a.Foo.prototype.foo = function() {\n" + + "};\n"); + } + + public void testU2UFunctionTypeAnnotation() { + assertTypeAnnotations( + "/** @type {!Function} */ var x = function() {}", + "/**\n * @constructor\n */\nvar x = function() {\n};\n"); + } + + public void testEmitUnknownParamTypesAsAllType() { + assertTypeAnnotations( + "var a = function(x) {}", + "/**\n" + + " * @param {*} x\n" + + " * @return {undefined}\n" + + " */\n" + + "var a = function(x) {\n};\n"); + } + + public void testOptionalTypesAnnotation() { + assertTypeAnnotations( + "/**\n" + + " * @param {string=} x \n" + + " */\n" + + "var a = function(x) {}", + "/**\n" + + " * @param {string=} x\n" + + " * @return {undefined}\n" + + " */\n" + + "var a = function(x) {\n};\n"); + } + + public void testVariableArgumentsTypesAnnotation() { + assertTypeAnnotations( + "/**\n" + + " * @param {...string} x \n" + + " */\n" + + "var a = function(x) {}", + "/**\n" + + " * @param {...string} x\n" + + " * @return {undefined}\n" + + " */\n" + + "var a = function(x) {\n};\n"); + } + + public void testTempConstructor() { + assertTypeAnnotations( + "var x = function() {\n/**\n * @constructor\n */\nfunction t1() {}\n" + + " /**\n * @constructor\n */\nfunction t2() {}\n" + + " t1.prototype = t2.prototype}", + "/**\n * @return {undefined}\n */\nvar x = function() {\n" + + " /**\n * @return {undefined}\n * @constructor\n */\n" + + "function t1() {\n }\n" + + " /**\n * @return {undefined}\n * @constructor\n */\n" + + "function t2() {\n }\n" + + " t1.prototype = t2.prototype\n};\n" + ); + } + + public void testEnumAnnotation1() { + assertTypeAnnotations( + "/** @enum {string} */ var Enum = {FOO: 'x', BAR: 'y'};", + "/** @enum {string} */\nvar Enum = {FOO:\"x\", BAR:\"y\"};\n"); + } + + public void testEnumAnnotation2() { + assertTypeAnnotations( + "var goog = goog || {};" + + "/** @enum {string} */ goog.Enum = {FOO: 'x', BAR: 'y'};" + + "/** @const */ goog.Enum2 = goog.x ? {} : goog.Enum;", + "var goog = goog || {};\n" + + "/** @enum {string} */\ngoog.Enum = {FOO:\"x\", BAR:\"y\"};\n" + + "/** @type {(Object|{})} */\ngoog.Enum2 = goog.x ? {} : goog.Enum;\n"); + } + + private void assertPrettyPrint(String js, String expected) { + assertEquals(expected, + parsePrint(js, true, false, + CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD)); + } + + private void assertTypeAnnotations(String js, String expected) { + assertEquals(expected, + parsePrint(js, true, false, + CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD, true)); + } + + public void testSubtraction() { + Compiler compiler = new Compiler(); + Node n = compiler.parseTestCode("x - -4"); + assertEquals(0, compiler.getErrorCount()); + + assertEquals( + "x- -4", + printNode(n)); + } + + public void testFunctionWithCall() { + assertPrint( + "var user = new function() {" + + "alert(\"foo\")}", + "var user=new function(){" + + "alert(\"foo\")}"); + assertPrint( + "var user = new function() {" + + "this.name = \"foo\";" + + "this.local = function(){alert(this.name)};}", + "var user=new function(){" + + "this.name=\"foo\";" + + "this.local=function(){alert(this.name)}}"); + } + + public void testLineLength() { + // list + assertLineLength("var aba,bcb,cdc", + "var aba,bcb," + + "\ncdc"); + + // operators, and two breaks + assertLineLength( + "\"foo\"+\"bar,baz,bomb\"+\"whee\"+\";long-string\"\n+\"aaa\"", + "\"foo\"+\"bar,baz,bomb\"+" + + "\n\"whee\"+\";long-string\"+" + + "\n\"aaa\""); + + // assignment + assertLineLength("var abazaba=1234", + "var abazaba=" + + "\n1234"); + + // statements + assertLineLength("var abab=1;var bab=2", + "var abab=1;" + + "\nvar bab=2"); + + // don't break regexes + assertLineLength("var a=/some[reg](ex),with.*we?rd|chars/i;var b=a", + "var a=/some[reg](ex),with.*we?rd|chars/i;" + + "\nvar b=a"); + + // don't break strings + assertLineLength("var a=\"foo,{bar};baz\";var b=a", + "var a=\"foo,{bar};baz\";" + + "\nvar b=a"); + + // don't break before post inc/dec + assertLineLength("var a=\"a\";a++;var b=\"bbb\";", + "var a=\"a\";a++;\n" + + "var b=\"bbb\""); + } + + private void assertLineLength(String js, String expected) { + assertEquals(expected, + parsePrint(js, false, true, 10)); + } + + public void testParsePrintParse() { + testReparse("3;"); + testReparse("var a = b;"); + testReparse("var x, y, z;"); + testReparse("try { foo() } catch(e) { bar() }"); + testReparse("try { foo() } catch(e) { bar() } finally { stuff() }"); + testReparse("try { foo() } finally { stuff() }"); + testReparse("throw 'me'"); + testReparse("function foo(a) { return a + 4; }"); + testReparse("function foo() { return; }"); + testReparse("var a = function(a, b) { foo(); return a + b; }"); + testReparse("b = [3, 4, 'paul', \"Buchhe it\",,5];"); + testReparse("v = (5, 6, 7, 8)"); + testReparse("d = 34.0; x = 0; y = .3; z = -22"); + testReparse("d = -x; t = !x + ~y;"); + testReparse("'hi'; /* just a test */ stuff(a,b) \n" + + " foo(); // and another \n" + + " bar();"); + testReparse("a = b++ + ++c; a = b++-++c; a = - --b; a = - ++b;"); + testReparse("a++; b= a++; b = ++a; b = a--; b = --a; a+=2; b-=5"); + testReparse("a = (2 + 3) * 4;"); + testReparse("a = 1 + (2 + 3) + 4;"); + testReparse("x = a ? b : c; x = a ? (b,3,5) : (foo(),bar());"); + testReparse("a = b | c || d ^ e " + + "&& f & !g != h << i <= j < k >>> l > m * n % !o"); + testReparse("a == b; a != b; a === b; a == b == a;" + + " (a == b) == a; a == (b == a);"); + testReparse("if (a > b) a = b; if (b < 3) a = 3; else c = 4;"); + testReparse("if (a == b) { a++; } if (a == 0) { a++; } else { a --; }"); + testReparse("for (var i in a) b += i;"); + testReparse("for (var i = 0; i < 10; i++){ b /= 2;" + + " if (b == 2)break;else continue;}"); + testReparse("for (x = 0; x < 10; x++) a /= 2;"); + testReparse("for (;;) a++;"); + testReparse("while(true) { blah(); }while(true) blah();"); + testReparse("do stuff(); while(a>b);"); + testReparse("[0, null, , true, false, this];"); + testReparse("s.replace(/absc/, 'X').replace(/ab/gi, 'Y');"); + testReparse("new Foo; new Bar(a, b,c);"); + testReparse("with(foo()) { x = z; y = t; } with(bar()) a = z;"); + testReparse("delete foo['bar']; delete foo;"); + testReparse("var x = { 'a':'paul', 1:'3', 2:(3,4) };"); + testReparse("switch(a) { case 2: case 3: stuff(); break;" + + "case 4: morestuff(); break; default: done();}"); + testReparse("x = foo['bar'] + foo['my stuff'] + foo[bar] + f.stuff;"); + testReparse("a.v = b.v; x['foo'] = y['zoo'];"); + testReparse("'test' in x; 3 in x; a in x;"); + testReparse("'foo\"bar' + \"foo'c\" + 'stuff\\n and \\\\more'"); + testReparse("x.__proto__;"); + } + + private void testReparse(String code) { + Compiler compiler = new Compiler(); + Node parse1 = parse(code); + Node parse2 = parse(new CodePrinter.Builder(parse1).build()); + String explanation = parse1.checkTreeEquals(parse2); + assertNull("\nExpected: " + compiler.toSource(parse1) + + "\nResult: " + compiler.toSource(parse2) + + "\n" + explanation, explanation); + } + + public void testDoLoopIECompatiblity() { + // Do loops within IFs cause syntax errors in IE6 and IE7. + assertPrint("function f(){if(e1){do foo();while(e2)}else foo()}", + "function f(){if(e1){do foo();while(e2)}else foo()}"); + + assertPrint("function f(){if(e1)do foo();while(e2)else foo()}", + "function f(){if(e1){do foo();while(e2)}else foo()}"); + + assertPrint("if(x){do{foo()}while(y)}else bar()", + "if(x){do foo();while(y)}else bar()"); + + assertPrint("if(x)do{foo()}while(y);else bar()", + "if(x){do foo();while(y)}else bar()"); + + assertPrint("if(x){do{foo()}while(y)}", + "if(x){do foo();while(y)}"); + + assertPrint("if(x)do{foo()}while(y);", + "if(x){do foo();while(y)}"); + + assertPrint("if(x)A:do{foo()}while(y);", + "if(x){A:do foo();while(y)}"); + + assertPrint("var i = 0;a: do{b: do{i++;break b;} while(0);} while(0);", + "var i=0;a:do{b:do{i++;break b}while(0)}while(0)"); + } + + public void testFunctionSafariCompatiblity() { + // Functions within IFs cause syntax errors on Safari. + assertPrint("function f(){if(e1){function goo(){return true}}else foo()}", + "function f(){if(e1){function goo(){return true}}else foo()}"); + + assertPrint("function f(){if(e1)function goo(){return true}else foo()}", + "function f(){if(e1){function goo(){return true}}else foo()}"); + + assertPrint("if(e1){function goo(){return true}}", + "if(e1){function goo(){return true}}"); + + assertPrint("if(e1)function goo(){return true}", + "if(e1){function goo(){return true}}"); + + assertPrint("if(e1)A:function goo(){return true}", + "if(e1){A:function goo(){return true}}"); + } + + public void testExponents() { + assertPrintNumber("1", 1); + assertPrintNumber("10", 10); + assertPrintNumber("100", 100); + assertPrintNumber("1E3", 1000); + assertPrintNumber("1E4", 10000); + assertPrintNumber("1E5", 100000); + assertPrintNumber("-1", -1); + assertPrintNumber("-10", -10); + assertPrintNumber("-100", -100); + assertPrintNumber("-1E3", -1000); + assertPrintNumber("-12341234E4", -123412340000L); + assertPrintNumber("1E18", 1000000000000000000L); + assertPrintNumber("1E5", 100000.0); + assertPrintNumber("100000.1", 100000.1); + + assertPrintNumber("1E-6", 0.000001); + assertPrintNumber("-0x38d7ea4c68001", -0x38d7ea4c68001L); + assertPrintNumber("0x38d7ea4c68001", 0x38d7ea4c68001L); + } + + // Make sure to test as both a String and a Node, because + // negative numbers do not parse consistently from strings. + private void assertPrintNumber(String expected, double number) { + assertPrint(String.valueOf(number), expected); + assertPrintNode(expected, Node.newNumber(number)); + } + + private void assertPrintNumber(String expected, int number) { + assertPrint(String.valueOf(number), expected); + assertPrintNode(expected, Node.newNumber(number)); + } + + public void testDirectEval() { + assertPrint("eval('1');", "eval(\"1\")"); + } + + public void testIndirectEval() { + Node n = parse("eval('1');"); + assertPrintNode("eval(\"1\")", n); + n.getFirstChild().getFirstChild().getFirstChild().putBooleanProp( + Node.DIRECT_EVAL, false); + assertPrintNode("(0,eval)(\"1\")", n); + } + + public void testFreeCall1() { + assertPrint("foo(a);", "foo(a)"); + assertPrint("x.foo(a);", "x.foo(a)"); + } + + public void testFreeCall2() { + Node n = parse("foo(a);"); + assertPrintNode("foo(a)", n); + Node call = n.getFirstChild().getFirstChild(); + assertTrue(call.isCall()); + call.putBooleanProp(Node.FREE_CALL, true); + assertPrintNode("foo(a)", n); + } + + public void testFreeCall3() { + Node n = parse("x.foo(a);"); + assertPrintNode("x.foo(a)", n); + Node call = n.getFirstChild().getFirstChild(); + assertTrue(call.isCall()); + call.putBooleanProp(Node.FREE_CALL, true); + assertPrintNode("(0,x.foo)(a)", n); + } + + public void testPrintScript() { + // Verify that SCRIPT nodes not marked as synthetic are printed as + // blocks. + Node ast = new Node(Token.SCRIPT, + new Node(Token.EXPR_RESULT, Node.newString("f")), + new Node(Token.EXPR_RESULT, Node.newString("g"))); + String result = new CodePrinter.Builder(ast).setPrettyPrint(true).build(); + assertEquals("\"f\";\n\"g\";\n", result); + } + + public void testObjectLit() { + assertPrint("({x:1})", "({x:1})"); + assertPrint("var x=({x:1})", "var x={x:1}"); + assertPrint("var x={'x':1}", "var x={\"x\":1}"); + assertPrint("var x={1:1}", "var x={1:1}"); + assertPrint("({},42)+0", "({},42)+0"); + } + + public void testObjectLit2() { + assertPrint("var x={1:1}", "var x={1:1}"); + assertPrint("var x={'1':1}", "var x={1:1}"); + assertPrint("var x={'1.0':1}", "var x={\"1.0\":1}"); + assertPrint("var x={1.5:1}", "var x={\"1.5\":1}"); + + } + + public void testObjectLit3() { + assertPrint("var x={3E9:1}", + "var x={3E9:1}"); + assertPrint("var x={'3000000000':1}", // More than 31 bits + "var x={3E9:1}"); + assertPrint("var x={'3000000001':1}", + "var x={3000000001:1}"); + assertPrint("var x={'6000000001':1}", // More than 32 bits + "var x={6000000001:1}"); + assertPrint("var x={\"12345678901234567\":1}", // More than 53 bits + "var x={\"12345678901234567\":1}"); + } + + public void testObjectLit4() { + // More than 128 bits. + assertPrint( + "var x={\"123456789012345671234567890123456712345678901234567\":1}", + "var x={\"123456789012345671234567890123456712345678901234567\":1}"); + } + + public void testGetter() { + assertPrint("var x = {}", "var x={}"); + assertPrint("var x = {get a() {return 1}}", "var x={get a(){return 1}}"); + assertPrint( + "var x = {get a() {}, get b(){}}", + "var x={get a(){},get b(){}}"); + + assertPrint( + "var x = {get 'a'() {return 1}}", + "var x={get \"a\"(){return 1}}"); + + assertPrint( + "var x = {get 1() {return 1}}", + "var x={get 1(){return 1}}"); + + assertPrint( + "var x = {get \"()\"() {return 1}}", + "var x={get \"()\"(){return 1}}"); + } + + public void testSetter() { + assertPrint("var x = {}", "var x={}"); + assertPrint( + "var x = {set a(y) {return 1}}", + "var x={set a(y){return 1}}"); + + assertPrint( + "var x = {get 'a'() {return 1}}", + "var x={get \"a\"(){return 1}}"); + + assertPrint( + "var x = {set 1(y) {return 1}}", + "var x={set 1(y){return 1}}"); + + assertPrint( + "var x = {set \"(x)\"(y) {return 1}}", + "var x={set \"(x)\"(y){return 1}}"); + } + + public void testNegCollapse() { + // Collapse the negative symbol on numbers at generation time, + // to match the Rhino behavior. + assertPrint("var x = - - 2;", "var x=2"); + assertPrint("var x = - (2);", "var x=-2"); + } + + public void testStrict() { + String result = parsePrint("var x", false, false, 0, false, true); + assertEquals("'use strict';var x", result); + } + + public void testArrayLiteral() { + assertPrint("var x = [,];","var x=[,]"); + assertPrint("var x = [,,];","var x=[,,]"); + assertPrint("var x = [,s,,];","var x=[,s,,]"); + assertPrint("var x = [,s];","var x=[,s]"); + assertPrint("var x = [s,];","var x=[s]"); + } + + public void testZero() { + assertPrint("var x ='\\0';", "var x=\"\\x00\""); + assertPrint("var x ='\\x00';", "var x=\"\\x00\""); + assertPrint("var x ='\\u0000';", "var x=\"\\x00\""); + assertPrint("var x ='\\u00003';", "var x=\"\\x003\""); + } + + public void testUnicode() { + assertPrint("var x ='\\x0f';", "var x=\"\\u000f\""); + assertPrint("var x ='\\x68';", "var x=\"h\""); + assertPrint("var x ='\\x7f';", "var x=\"\\u007f\""); + } + + public void testUnicodeKeyword() { + // keyword "if" + assertPrint("var \\u0069\\u0066 = 1;", "var i\\u0066=1"); + // keyword "var" + assertPrint("var v\\u0061\\u0072 = 1;", "var va\\u0072=1"); + // all are keyword "while" + assertPrint("var w\\u0068\\u0069\\u006C\\u0065 = 1;" + + "\\u0077\\u0068il\\u0065 = 2;" + + "\\u0077h\\u0069le = 3;", + "var whil\\u0065=1;whil\\u0065=2;whil\\u0065=3"); + } + + public void testNumericKeys() { + assertPrint("var x = {010: 1};", "var x={8:1}"); + assertPrint("var x = {'010': 1};", "var x={\"010\":1}"); + + assertPrint("var x = {0x10: 1};", "var x={16:1}"); + assertPrint("var x = {'0x10': 1};", "var x={\"0x10\":1}"); + + // I was surprised at this result too. + assertPrint("var x = {.2: 1};", "var x={\"0.2\":1}"); + assertPrint("var x = {'.2': 1};", "var x={\".2\":1}"); + + assertPrint("var x = {0.2: 1};", "var x={\"0.2\":1}"); + assertPrint("var x = {'0.2': 1};", "var x={\"0.2\":1}"); + } + + public void testIssue582() { + assertPrint("var x = -0.0;", "var x=-0"); + } + + public void testIssue601() { + assertPrint("'\\v' == 'v'", "\"\\v\"==\"v\""); + assertPrint("'\\u000B' == '\\v'", "\"\\x0B\"==\"\\v\""); + assertPrint("'\\x0B' == '\\v'", "\"\\x0B\"==\"\\v\""); + } + + public void testIssue620() { + assertPrint("alert(/ / / / /);", "alert(/ // / /)"); + assertPrint("alert(/ // / /);", "alert(/ // / /)"); + } + + public void testIssue5746867() { + assertPrint("var a = { '$\\\\' : 5 };", "var a={\"$\\\\\":5}"); + } + + public void testCommaSpacing() { + assertPrint("var a = (b = 5, c = 5);", + "var a=(b=5,c=5)"); + assertPrettyPrint("var a = (b = 5, c = 5);", + "var a = (b = 5, c = 5);\n"); + } + + public void testManyCommas() { + int numCommas = 10000; + List numbers = Lists.newArrayList("0", "1"); + Node current = new Node(Token.COMMA, Node.newNumber(0), Node.newNumber(1)); + for (int i = 2; i < numCommas; i++) { + current = new Node(Token.COMMA, current); + + // 1000 is printed as 1E3, and screws up our test. + int num = i % 1000; + numbers.add(String.valueOf(num)); + current.addChildToBack(Node.newNumber(num)); + } + + String expected = Joiner.on(",").join(numbers); + String actual = printNode(current).replace("\n", ""); + assertEquals(expected, actual); + } + + public void testManyAdds() { + int numAdds = 10000; + List numbers = Lists.newArrayList("0", "1"); + Node current = new Node(Token.ADD, Node.newNumber(0), Node.newNumber(1)); + for (int i = 2; i < numAdds; i++) { + current = new Node(Token.ADD, current); + + // 1000 is printed as 1E3, and screws up our test. + int num = i % 1000; + numbers.add(String.valueOf(num)); + current.addChildToBack(Node.newNumber(num)); + } + + String expected = Joiner.on("+").join(numbers); + String actual = printNode(current).replace("\n", ""); + assertEquals(expected, actual); + } + + public void testMinusNegativeZero() { + // Negative zero is weird, because we have to be able to distinguish + // it from positive zero (there are some subtle differences in behavior). + assertPrint("x- -0", "x- -0"); + } + + public void testStringEscapeSequences() { + // From the SingleEscapeCharacter grammar production. + assertPrintSame("var x=\"\\b\""); + assertPrintSame("var x=\"\\f\""); + assertPrintSame("var x=\"\\n\""); + assertPrintSame("var x=\"\\r\""); + assertPrintSame("var x=\"\\t\""); + assertPrintSame("var x=\"\\v\""); + assertPrint("var x=\"\\\"\"", "var x='\"'"); + assertPrint("var x=\"\\\'\"", "var x=\"'\""); + + // From the LineTerminator grammar + assertPrint("var x=\"\\u000A\"", "var x=\"\\n\""); + assertPrint("var x=\"\\u000D\"", "var x=\"\\r\""); + assertPrintSame("var x=\"\\u2028\""); + assertPrintSame("var x=\"\\u2029\""); + + // Now with regular expressions. + assertPrintSame("var x=/\\b/"); + assertPrintSame("var x=/\\f/"); + assertPrintSame("var x=/\\n/"); + assertPrintSame("var x=/\\r/"); + assertPrintSame("var x=/\\t/"); + assertPrintSame("var x=/\\v/"); + assertPrintSame("var x=/\\u000A/"); + assertPrintSame("var x=/\\u000D/"); + assertPrintSame("var x=/\\u2028/"); + assertPrintSame("var x=/\\u2029/"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CollapseAnonymousFunctionsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CollapseAnonymousFunctionsTest.java new file mode 100644 index 0000000..e8d4c3a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CollapseAnonymousFunctionsTest.java @@ -0,0 +1,132 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + + +/** + * Tests for {@link CollapseAnonymousFunctions} + * + */ +public class CollapseAnonymousFunctionsTest extends CompilerTestCase { + public CollapseAnonymousFunctionsTest() { + this.enableNormalize(); + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new CollapseAnonymousFunctions(compiler); + } + + public void testGlobalScope() { + test("var f = function(){}", "function f(){}"); + } + + public void testLocalScope1() { + test("function f(){ var x = function(){}; x() }", + "function f(){ function x(){} x() }"); + } + + public void testLocalScope2() { + test("function f(){ var x = function(){}; return x }", + "function f(){ function x(){} return x }"); + } + + public void testVarNotImmediatelyBelowScriptOrBlock1() { + testSame("if (x) var f = function(){}"); + } + + public void testVarNotImmediatelyBelowScriptOrBlock2() { + testSame("var x = 1;" + + "if (x == 1) {" + + " var f = function () { alert('b')}" + + "} else {" + + " f = function() { alert('c')}" + + "}" + + "f();"); + } + + public void testVarNotImmediatelyBelowScriptOrBlock3() { + testSame("var x = 1; if (x) {var f = function(){return x}; f(); x--;}"); + } + + public void testMultipleVar() { + test("var f = function(){}; var g = f", "function f(){} var g = f"); + } + + public void testMultipleVar2() { + test("var f = function(){}; var g = f; var h = function(){}", + "function f(){}var g = f;function h(){}"); + } + + public void testBothScopes() { + test("var x = function() { var y = function(){} }", + "function x() { function y(){} }"); + } + + public void testLocalScopeOnly1() { + test("if (x) var f = function(){ var g = function(){} }", + "if (x) var f = function(){ function g(){} }"); + } + + public void testLocalScopeOnly2() { + test("if (x) var f = function(){ var g = function(){} };", + "if (x) var f = function(){ function g(){} }"); + } + + public void testReturn() { + test("var f = function(x){return 2*x}; var g = f(2);", + "function f(x){return 2*x} var g = f(2)"); + } + + public void testAlert() { + test("var x = 1; var f = function(){alert(x)};", + "var x = 1; function f(){alert(x)}"); + } + + public void testRecursiveInternal1() { + testSame("var f = function foo() { foo() }"); + } + + public void testRecursiveInternal2() { + testSame("var f = function foo() { function g(){foo()} g() }"); + } + + public void testRecursiveExternal1() { + test("var f = function foo() { f() }", + "function f() { f() }"); + } + + public void testRecursiveExternal2() { + test("var f = function foo() { function g(){f()} g() }", + "function f() { function g(){f()} g() }"); + } + + public void testConstantFunction1() { + test("var FOO = function(){};FOO()", + "function FOO(){}FOO()"); + } + + public void testInnerFunction1() { + test( + "function f() { " + + " var x = 3; var y = function() { return 4; }; return x + y();" + + "}", + "function f() { " + + " function y() { return 4; } var x = 3; return x + y();" + + "}"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CollapsePropertiesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CollapsePropertiesTest.java new file mode 100644 index 0000000..77e5e95 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CollapsePropertiesTest.java @@ -0,0 +1,1491 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import static com.google.javascript.jscomp.CollapseProperties.UNSAFE_THIS; + +import com.google.javascript.rhino.Node; + +/** + * Tests for {@link CollapseProperties}. + * + */ +public class CollapsePropertiesTest extends CompilerTestCase { + + private static String EXTERNS = + "var window; function alert(s) {} function parseInt(s) {}" + + "/** @constructor */ function String() {}"; + + private boolean collapsePropertiesOnExternTypes = false; + + public CollapsePropertiesTest() { + super(EXTERNS); + } + + @Override public CompilerPass getProcessor(Compiler compiler) { + return new CollapseProperties( + compiler, collapsePropertiesOnExternTypes, true); + } + + @Override + public void setUp() { + enableLineNumberCheck(true); + enableNormalize(true); + } + + @Override public int getNumRepetitions() { + return 1; + } + + public void testCollapse() { + test("var a = {}; a.b = {}; var c = a.b;", + "var a$b = {}; var c = a$b"); + } + + public void testMultiLevelCollapse() { + test("var a = {}; a.b = {}; a.b.c = {}; var d = a.b.c;", + "var a$b$c = {}; var d = a$b$c;"); + } + + public void testDecrement() { + test("var a = {}; a.b = 5; a.b--; a.b = 5", + "var a$b = 5; a$b--; a$b = 5"); + } + + public void testIncrement() { + test("var a = {}; a.b = 5; a.b++; a.b = 5", + "var a$b = 5; a$b++; a$b = 5"); + } + + public void testObjLitDeclaration() { + test("var a = {b: {}, c: {}}; var d = a.b; var e = a.c", + "var a$b = {}; var a$c = {}; var d = a$b; var e = a$c"); + } + + public void testObjLitDeclarationWithGet1() { + testSame("var a = {get b(){}};"); + } + + public void testObjLitDeclarationWithGet2() { + test("var a = {b: {}, get c(){}}; var d = a.b; var e = a.c", + "var a$b = {};var a = {get c(){}};var d = a$b; var e = a.c"); + } + + public void testObjLitDeclarationWithGet3() { + test("var a = {b: {get c() { return 3; }}};", + "var a$b = {get c() { return 3; }};"); + } + + public void testObjLitDeclarationWithSet1() { + testSame("var a = {set b(a){}};"); + } + + public void testObjLitDeclarationWithSet2() { + test("var a = {b: {}, set c(a){}}; var d = a.b; var e = a.c", + "var a$b = {};var a = {set c(a){}};var d = a$b; var e = a.c"); + } + + public void testObjLitDeclarationWithSet3() { + test("var a = {b: {set c(d) {}}};", + "var a$b = {set c(d) {}};"); + } + + public void testObjLitDeclarationWithGetAndSet1() { + test("var a = {b: {get c() { return 3; },set c(d) {}}};", + "var a$b = {get c() { return 3; },set c(d) {}};"); + } + + public void testObjLitDeclarationWithDuplicateKeys() { + test("var a = {b: 0, b: 1}; var c = a.b;", + "var a$b = 0; var a$b = 1; var c = a$b;", + SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR); + } + + public void testObjLitAssignmentDepth1() { + test("var a = {b: {}, c: {}}; var d = a.b; var e = a.c", + "var a$b = {}; var a$c = {}; var d = a$b; var e = a$c"); + } + + public void testObjLitAssignmentDepth2() { + test("var a = {}; a.b = {c: {}, d: {}}; var e = a.b.c; var f = a.b.d", + "var a$b$c = {}; var a$b$d = {}; var e = a$b$c; var f = a$b$d"); + } + + public void testObjLitAssignmentDepth3() { + test("var a = {}; a.b = {}; a.b.c = {d: 1, e: 2}; var f = a.b.c.d", + "var a$b$c$d = 1; var a$b$c$e = 2; var f = a$b$c$d"); + } + + public void testObjLitAssignmentDepth4() { + test("var a = {}; a.b = {}; a.b.c = {}; a.b.c.d = {e: 1, f: 2}; " + + "var g = a.b.c.d.e", + "var a$b$c$d$e = 1; var a$b$c$d$f = 2; var g = a$b$c$d$e"); + } + + public void testGlobalObjectDeclaredToPreserveItsPreviousValue1() { + test("var a = a ? a : {}; a.c = 1;", + "var a = a ? a : {}; var a$c = 1;"); + } + + public void testGlobalObjectDeclaredToPreserveItsPreviousValue2() { + test("var a = a || {}; a.c = 1;", + "var a = a || {}; var a$c = 1;"); + } + + public void testGlobalObjectDeclaredToPreserveItsPreviousValue3() { + test("var a = a || {get b() {}}; a.c = 1;", + "var a = a || {get b() {}}; var a$c = 1;"); + } + + public void testGlobalObjectNameInBooleanExpressionDepth1_1() { + test("var a = {b: 0}; a.c = 1; if (a) x();", + "var a$b = 0; var a = {}; var a$c = 1; if (a) x();"); + } + + public void testGlobalObjectNameInBooleanExpressionDepth1_2() { + test("var a = {b: 0}; a.c = 1; if (!(a && a.c)) x();", + "var a$b = 0; var a = {}; var a$c = 1; if (!(a && a$c)) x();"); + } + + public void testGlobalObjectNameInBooleanExpressionDepth1_3() { + test("var a = {b: 0}; a.c = 1; while (a || a.c) x();", + "var a$b = 0; var a = {}; var a$c = 1; while (a || a$c) x();"); + } + + public void testGlobalObjectNameInBooleanExpressionDepth1_4() { + testSame("var a = {}; a.c = 1; var d = a || {}; a.c;"); + } + + public void testGlobalObjectNameInBooleanExpressionDepth1_5() { + testSame("var a = {}; a.c = 1; var d = a.c || a; a.c;"); + } + + public void testGlobalObjectNameInBooleanExpressionDepth1_6() { + test("var a = {b: 0}; a.c = 1; var d = !(a.c || a); a.c;", + "var a$b = 0; var a = {}; var a$c = 1; var d = !(a$c || a); a$c;"); + } + + public void testGlobalObjectNameInBooleanExpressionDepth2() { + test("var a = {b: {}}; a.b.c = 1; if (a.b) x(a.b.c);", + "var a$b = {}; var a$b$c = 1; if (a$b) x(a$b$c);"); + } + + public void testGlobalObjectNameInBooleanExpressionDepth3() { + // TODO(user): Make CollapseProperties even more aggressive so that + // a$b.z gets collapsed. Right now, it doesn't get collapsed because the + // expression (a.b && a.b.c) could return a.b. But since it returns a.b iff + // a.b *is* safely collapsible, the Boolean logic should be smart enough to + // only consider the right side of the && as aliasing. + test("var a = {}; a.b = {}; /** @constructor */ a.b.c = function(){};" + + " a.b.z = 1; var d = a.b && a.b.c;", + "var a$b = {}; var a$b$c = function(){};" + + " a$b.z = 1; var d = a$b && a$b$c;", null, + CollapseProperties.UNSAFE_NAMESPACE_WARNING); + } + + public void testGlobalFunctionNameInBooleanExpressionDepth1() { + test("function a() {} a.c = 1; if (a) x(a.c);", + "function a() {} var a$c = 1; if (a) x(a$c);"); + } + + public void testGlobalFunctionNameInBooleanExpressionDepth2() { + test("var a = {b: function(){}}; a.b.c = 1; if (a.b) x(a.b.c);", + "var a$b = function(){}; var a$b$c = 1; if (a$b) x(a$b$c);"); + } + + public void testAliasCreatedForObjectDepth1_1() { + // An object's properties are not collapsed if the object is referenced + // in a such a way that an alias is created for it. + testSame("var a = {b: 0}; var c = a; c.b = 1; a.b == c.b;"); + } + + public void testAliasCreatedForObjectDepth1_2() { + testSame("var a = {b: 0}; f(a); a.b;"); + } + + public void testAliasCreatedForObjectDepth1_3() { + testSame("var a = {b: 0}; new f(a); a.b;"); + } + + public void testAliasCreatedForObjectDepth2_1() { + test("var a = {}; a.b = {c: 0}; var d = a.b; a.b.c == d.c;", + "var a$b = {c: 0}; var d = a$b; a$b.c == d.c;"); + } + + public void testAliasCreatedForObjectDepth2_2() { + test("var a = {}; a.b = {c: 0}; for (var p in a.b) { e(a.b[p]); }", + "var a$b = {c: 0}; for (var p in a$b) { e(a$b[p]); }"); + } + + public void testAliasCreatedForEnumDepth1_1() { + // An enum's values are always collapsed, even if the enum object is + // referenced in a such a way that an alias is created for it. + test("/** @enum */ var a = {b: 0}; var c = a; c.b = 1; a.b != c.b;", + "var a$b = 0; var a = {b: a$b}; var c = a; c.b = 1; a$b != c.b;"); + } + + public void testAliasCreatedForEnumDepth1_2() { + test("/** @enum */ var a = {b: 0}; f(a); a.b;", + "var a$b = 0; var a = {b: a$b}; f(a); a$b;"); + } + + public void testAliasCreatedForEnumDepth1_3() { + test("/** @enum */ var a = {b: 0}; new f(a); a.b;", + "var a$b = 0; var a = {b: a$b}; new f(a); a$b;"); + } + + public void testAliasCreatedForEnumDepth1_4() { + test("/** @enum */ var a = {b: 0}; for (var p in a) { f(a[p]); }", + "var a$b = 0; var a = {b: a$b}; for (var p in a) { f(a[p]); }"); + } + + public void testAliasCreatedForEnumDepth2_1() { + test("var a = {}; /** @enum */ a.b = {c: 0};" + + "var d = a.b; d.c = 1; a.b.c != d.c;", + "var a$b$c = 0; var a$b = {c: a$b$c};" + + "var d = a$b; d.c = 1; a$b$c != d.c;"); + } + + public void testAliasCreatedForEnumDepth2_2() { + test("var a = {}; /** @enum */ a.b = {c: 0};" + + "for (var p in a.b) { f(a.b[p]); }", + "var a$b$c = 0; var a$b = {c: a$b$c};" + + "for (var p in a$b) { f(a$b[p]); }"); + } + + public void testAliasCreatedForEnumDepth2_3() { + test("var a = {}; var d = a; /** @enum */ a.b = {c: 0};" + + "for (var p in a.b) { f(a.b[p]); }", + "var a = {}; var d = a; var a$b$c = 0; var a$b = {c: a$b$c};" + + "for (var p in a$b) { f(a$b[p]); }", + null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); + } + + public void testAliasCreatedForEnumOfObjects() { + test("var a = {}; " + + "/** @enum {Object} */ a.b = {c: {d: 1}}; a.b.c;" + + "searchEnum(a.b);", + "var a$b$c = {d: 1};var a$b = {c: a$b$c}; a$b$c; " + + "searchEnum(a$b)"); + } + + public void testAliasCreatedForEnumOfObjects2() { + test("var a = {}; " + + "/** @enum {Object} */ a.b = {c: {d: 1}}; a.b.c.d;" + + "searchEnum(a.b);", + "var a$b$c = {d: 1};var a$b = {c: a$b$c}; a$b$c.d; " + + "searchEnum(a$b)"); + } + + public void testAliasCreatedForPropertyOfEnumOfObjects() { + test("var a = {}; " + + "/** @enum {Object} */ a.b = {c: {d: 1}}; a.b.c;" + + "searchEnum(a.b.c);", + "var a$b$c = {d: 1}; a$b$c; searchEnum(a$b$c);"); + } + + public void testAliasCreatedForPropertyOfEnumOfObjects2() { + test("var a = {}; " + + "/** @enum {Object} */ a.b = {c: {d: 1}}; a.b.c.d;" + + "searchEnum(a.b.c);", + "var a$b$c = {d: 1}; a$b$c.d; searchEnum(a$b$c);"); + } + + public void testMisusedEnumTag() { + testSame("var a = {}; var d = a; a.b = function() {};" + + "/** @enum */ a.b.c = 0; a.b.c;"); + } + + public void testMisusedConstructorTag() { + testSame("var a = {}; var d = a; a.b = function() {};" + + "/** @constructor */ a.b.c = 0; a.b.c;"); + } + + public void testAliasCreatedForFunctionDepth1_1() { + testSame("var a = function(){}; a.b = 1; var c = a; c.b = 2; a.b != c.b;"); + } + + public void testAliasCreatedForCtorDepth1_1() { + // A constructor's properties *are* collapsed even if the function is + // referenced in a such a way that an alias is created for it, + // since a function with custom properties is considered a class and its + // non-prototype properties are considered static methods and variables. + // People don't typically iterate through static members of a class or + // refer to them using an alias for the class name. + test("/** @constructor */ var a = function(){}; a.b = 1; " + + "var c = a; c.b = 2; a.b != c.b;", + "var a = function(){}; var a$b = 1; var c = a; c.b = 2; a$b != c.b;"); + } + + public void testAliasCreatedForFunctionDepth1_2() { + testSame("var a = function(){}; a.b = 1; f(a); a.b;"); + } + + public void testAliasCreatedForCtorDepth1_2() { + test("/** @constructor */ var a = function(){}; a.b = 1; f(a); a.b;", + "var a = function(){}; var a$b = 1; f(a); a$b;"); + } + + public void testAliasCreatedForFunctionDepth1_3() { + testSame("var a = function(){}; a.b = 1; new f(a); a.b;"); + } + + public void testAliasCreatedForCtorDepth1_3() { + test("/** @constructor */ var a = function(){}; a.b = 1; new f(a); a.b;", + "var a = function(){}; var a$b = 1; new f(a); a$b;"); + } + + public void testAliasCreatedForFunctionDepth2() { + test( + "var a = {}; a.b = function() {}; a.b.c = 1; var d = a.b;" + + "a.b.c != d.c;", + "var a$b = function() {}; a$b.c = 1; var d = a$b;" + + "a$b.c != d.c;"); + } + + public void testAliasCreatedForCtorDepth2() { + test("var a = {}; /** @constructor */ a.b = function() {}; " + + "a.b.c = 1; var d = a.b;" + + "a.b.c != d.c;", + "var a$b = function() {}; var a$b$c = 1; var d = a$b;" + + "a$b$c != d.c;"); + } + + public void testAliasCreatedForClassDepth1_1() { + // A class's name is always collapsed, even if one of its prefixes is + // referenced in a such a way that an alias is created for it. + test("var a = {}; /** @constructor */ a.b = function(){};" + + "var c = a; c.b = 0; a.b != c.b;", + "var a = {}; var a$b = function(){};" + + "var c = a; c.b = 0; a$b != c.b;", null, + CollapseProperties.UNSAFE_NAMESPACE_WARNING); + } + + public void testAliasCreatedForClassDepth1_2() { + test("var a = {}; /** @constructor */ a.b = function(){}; f(a); a.b;", + "var a = {}; var a$b = function(){}; f(a); a$b;", + null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); + } + + public void testAliasCreatedForClassDepth1_3() { + test("var a = {}; /** @constructor */ a.b = function(){}; new f(a); a.b;", + "var a = {}; var a$b = function(){}; new f(a); a$b;", + null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); + } + + public void testAliasCreatedForClassDepth2_1() { + test("var a = {}; a.b = {}; /** @constructor */ a.b.c = function(){};" + + "var d = a.b; a.b.c != d.c;", + "var a$b = {}; var a$b$c = function(){};" + + "var d = a$b; a$b$c != d.c;", + null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); + } + + public void testAliasCreatedForClassDepth2_2() { + test("var a = {}; a.b = {}; /** @constructor */ a.b.c = function(){};" + + "f(a.b); a.b.c;", + "var a$b = {}; var a$b$c = function(){}; f(a$b); a$b$c;", + null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); + } + + public void testAliasCreatedForClassDepth2_3() { + test("var a = {}; a.b = {}; /** @constructor */ a.b.c = function(){};" + + "new f(a.b); a.b.c;", + "var a$b = {}; var a$b$c = function(){}; new f(a$b); a$b$c;", + null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); + } + + public void testAliasCreatedForClassProperty() { + test("var a = {}; /** @constructor */ a.b = function(){};" + + "a.b.c = {d: 3}; new f(a.b.c); a.b.c.d;", + "var a$b = function(){}; var a$b$c = {d:3}; new f(a$b$c); a$b$c.d;"); + } + + public void testNestedObjLit() { + test("var a = {}; a.b = {f: 0, c: {d: 1}}; var e = a.b.c.d", + "var a$b$f = 0; var a$b$c$d = 1; var e = a$b$c$d;"); + } + + public void testObjLitDeclarationUsedInSameVarList() { + // The collapsed properties must defined in the same place in the var list + // where they were originally defined (and not, for example, at the end). + test("var a = {b: {}, c: {}}; var d = a.b; var e = a.c;", + "var a$b = {}; var a$c = {}; var d = a$b; var e = a$c;"); + } + + public void testPropGetInsideAnObjLit() { + test("var x = {}; x.y = 1; var a = {}; a.b = {c: x.y}", + "var x$y = 1; var a$b$c = x$y;"); + } + + public void testObjLitWithQuotedKeyThatDoesNotGetRead() { + test("var a = {}; a.b = {c: 0, 'd': 1}; var e = a.b.c;", + "var a$b$c = 0; var a$b$d = 1; var e = a$b$c;"); + } + + public void testObjLitWithQuotedKeyThatGetsRead() { + test("var a = {}; a.b = {c: 0, 'd': 1}; var e = a.b['d'];", + "var a$b = {c: 0, 'd': 1}; var e = a$b['d'];"); + } + + public void testFunctionWithQuotedPropertyThatDoesNotGetRead() { + test("var a = {}; a.b = function() {}; a.b['d'] = 1;", + "var a$b = function() {}; a$b['d'] = 1;"); + } + + public void testFunctionWithQuotedPropertyThatGetsRead() { + test("var a = {}; a.b = function() {}; a.b['d'] = 1; f(a.b['d']);", + "var a$b = function() {}; a$b['d'] = 1; f(a$b['d']);"); + } + + public void testObjLitAssignedToMultipleNames1() { + // An object literal that's assigned to multiple names isn't collapsed. + testSame("var a = b = {c: 0, d: 1}; var e = a.c; var f = b.d;"); + } + + public void testObjLitAssignedToMultipleNames2() { + testSame("a = b = {c: 0, d: 1}; var e = a.c; var f = b.d;"); + } + + public void testObjLitRedefinedInGlobalScope() { + testSame("a = {b: 0}; a = {c: 1}; var d = a.b; var e = a.c;"); + } + + public void testObjLitRedefinedInLocalScope() { + test("var a = {}; a.b = {c: 0}; function d() { a.b = {c: 1}; } e(a.b.c);", + "var a$b = {c: 0}; function d() { a$b = {c: 1}; } e(a$b.c);"); + } + + public void testObjLitAssignedInTernaryExpression1() { + testSame("a = x ? {b: 0} : d; var c = a.b;"); + } + + public void testObjLitAssignedInTernaryExpression2() { + testSame("a = x ? {b: 0} : {b: 1}; var c = a.b;"); + } + + public void testGlobalVarSetToObjLitConditionally1() { + testSame("var a; if (x) a = {b: 0}; var c = x ? a.b : 0;"); + } + + public void testGlobalVarSetToObjLitConditionally1b() { + test("if (x) var a = {b: 0}; var c = x ? a.b : 0;", + "if (x) var a$b = 0; var c = x ? a$b : 0;"); + } + + public void testGlobalVarSetToObjLitConditionally2() { + test("if (x) var a = {b: 0}; var c = a.b; var d = a.c;", + "if (x){ var a$b = 0; var a = {}; }var c = a$b; var d = a.c;"); + } + + public void testGlobalVarSetToObjLitConditionally3() { + testSame("var a; if (x) a = {b: 0}; else a = {b: 1}; var c = a.b;"); + } + + public void testObjectPropertySetToObjLitConditionally() { + test("var a = {}; if (x) a.b = {c: 0}; var d = a.b ? a.b.c : 0;", + "if (x){ var a$b$c = 0; var a$b = {} } var d = a$b ? a$b$c : 0;"); + } + + public void testFunctionPropertySetToObjLitConditionally() { + test("function a() {} if (x) a.b = {c: 0}; var d = a.b ? a.b.c : 0;", + "function a() {} if (x){ var a$b$c = 0; var a$b = {} }" + + "var d = a$b ? a$b$c : 0;"); + } + + public void testPrototypePropertySetToAnObjectLiteral() { + test("var a = {b: function(){}}; a.b.prototype.c = {d: 0};", + "var a$b = function(){}; a$b.prototype.c = {d: 0};"); + } + + public void testObjectPropertyResetInLocalScope() { + test("var z = {}; z.a = 0; function f() {z.a = 5; return z.a}", + "var z$a = 0; function f() {z$a = 5; return z$a}"); + } + + public void testFunctionPropertyResetInLocalScope() { + test("function z() {} z.a = 0; function f() {z.a = 5; return z.a}", + "function z() {} var z$a = 0; function f() {z$a = 5; return z$a}"); + } + + public void testNamespaceResetInGlobalScope1() { + test("var a = {}; /** @constructor */a.b = function() {}; a = {};", + "var a = {}; var a$b = function() {}; a = {};", + null, CollapseProperties.NAMESPACE_REDEFINED_WARNING); + } + + public void testNamespaceResetInGlobalScope2() { + test("var a = {}; a = {}; /** @constructor */a.b = function() {};", + "var a = {}; a = {}; var a$b = function() {};", + null, CollapseProperties.NAMESPACE_REDEFINED_WARNING); + } + + public void testNamespaceResetInLocalScope1() { + test("var a = {}; /** @constructor */a.b = function() {};" + + " function f() { a = {}; }", + "var a = {};var a$b = function() {};" + + " function f() { a = {}; }", + null, CollapseProperties.NAMESPACE_REDEFINED_WARNING); + } + + public void testNamespaceResetInLocalScope2() { + test("var a = {}; function f() { a = {}; }" + + " /** @constructor */a.b = function() {};", + "var a = {}; function f() { a = {}; }" + + " var a$b = function() {};", + null, CollapseProperties.NAMESPACE_REDEFINED_WARNING); + } + + public void testNamespaceDefinedInLocalScope() { + test("var a = {}; (function() { a.b = {}; })();" + + " /** @constructor */a.b.c = function() {};", + "var a$b; (function() { a$b = {}; })(); var a$b$c = function() {};"); + } + + public void testAddPropertyToObjectInLocalScopeDepth1() { + test("var a = {b: 0}; function f() { a.c = 5; return a.c; }", + "var a$b = 0; var a$c; function f() { a$c = 5; return a$c; }"); + } + + public void testAddPropertyToObjectInLocalScopeDepth2() { + test("var a = {}; a.b = {}; (function() {a.b.c = 0;})(); x = a.b.c;", + "var a$b$c; (function() {a$b$c = 0;})(); x = a$b$c;"); + } + + public void testAddPropertyToFunctionInLocalScopeDepth1() { + test("function a() {} function f() { a.c = 5; return a.c; }", + "function a() {} var a$c; function f() { a$c = 5; return a$c; }"); + } + + public void testAddPropertyToFunctionInLocalScopeDepth2() { + test("var a = {}; a.b = function() {}; function f() {a.b.c = 0;}", + "var a$b = function() {}; var a$b$c; function f() {a$b$c = 0;}"); + } + + public void testAddPropertyToUncollapsibleObjectInLocalScopeDepth1() { + testSame("var a = {}; var c = a; (function() {a.b = 0;})(); a.b;"); + } + + public void testAddPropertyToUncollapsibleFunctionInLocalScopeDepth1() { + testSame("function a() {} var c = a; (function() {a.b = 0;})(); a.b;"); + } + + public void testAddPropertyToUncollapsibleNamedCtorInLocalScopeDepth1() { + testSame( + "/** @constructor */ function a() {} var a$b; var c = a; " + + "(function() {a$b = 0;})(); a$b;"); + } + + public void testAddPropertyToUncollapsibleCtorInLocalScopeDepth1() { + test("/** @constructor */ var a = function() {}; var c = a; " + + "(function() {a.b = 0;})(); a.b;", + "var a = function() {}; var a$b; " + + "var c = a; (function() {a$b = 0;})(); a$b;"); + } + + public void testAddPropertyToUncollapsibleObjectInLocalScopeDepth2() { + test("var a = {}; a.b = {}; var d = a.b;" + + "(function() {a.b.c = 0;})(); a.b.c;", + "var a$b = {}; var d = a$b;" + + "(function() {a$b.c = 0;})(); a$b.c;"); + } + + public void testAddPropertyToUncollapsibleFunctionInLocalScopeDepth2() { + test("var a = {}; a.b = function (){}; var d = a.b;" + + "(function() {a.b.c = 0;})(); a.b.c;", + "var a$b = function (){}; var d = a$b;" + + "(function() {a$b.c = 0;})(); a$b.c;"); + } + + public void testAddPropertyToUncollapsibleCtorInLocalScopeDepth2() { + test("var a = {}; /** @constructor */ a.b = function (){}; var d = a.b;" + + "(function() {a.b.c = 0;})(); a.b.c;", + "var a$b = function (){}; var a$b$c; var d = a$b;" + + "(function() {a$b$c = 0;})(); a$b$c;"); + } + + public void testPropertyOfChildFuncOfUncollapsibleObjectDepth1() { + testSame("var a = {}; var c = a; a.b = function (){}; a.b.x = 0; a.b.x;"); + } + + public void testPropertyOfChildFuncOfUncollapsibleObjectDepth2() { + test("var a = {}; a.b = {}; var c = a.b;" + + "a.b.c = function (){}; a.b.c.x = 0; a.b.c.x;", + "var a$b = {}; var c = a$b;" + + "a$b.c = function (){}; a$b.c.x = 0; a$b.c.x;"); + } + + public void testAddPropertyToChildFuncOfUncollapsibleObjectInLocalScope() { + testSame("var a = {}; a.b = function (){}; a.b.x = 0;" + + "var c = a; (function() {a.b.y = 1;})(); a.b.x; a.b.y;"); + } + + public void testAddPropertyToChildTypeOfUncollapsibleObjectInLocalScope() { + test("var a = {}; /** @constructor */ a.b = function (){}; a.b.x = 0;" + + "var c = a; (function() {a.b.y = 1;})(); a.b.x; a.b.y;", + "var a = {}; var a$b = function (){}; var a$b$y; var a$b$x = 0;" + + "var c = a; (function() {a$b$y = 1;})(); a$b$x; a$b$y;", + null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); + } + + public void testAddPropertyToChildOfUncollapsibleFunctionInLocalScope() { + testSame( + "function a() {} a.b = {x: 0}; var c = a;" + + "(function() {a.b.y = 0;})(); a.b.y;"); + } + + public void testAddPropertyToChildOfUncollapsibleCtorInLocalScope() { + test("/** @constructor */ var a = function() {}; a.b = {x: 0}; var c = a;" + + "(function() {a.b.y = 0;})(); a.b.y;", + "var a = function() {}; var a$b$x = 0; var a$b$y; var c = a;" + + "(function() {a$b$y = 0;})(); a$b$y;"); + } + + public void testResetObjectPropertyInLocalScope() { + test("var a = {b: 0}; a.c = 1; function f() { a.c = 5; }", + "var a$b = 0; var a$c = 1; function f() { a$c = 5; }"); + } + + public void testResetFunctionPropertyInLocalScope() { + test("function a() {}; a.c = 1; function f() { a.c = 5; }", + "function a() {}; var a$c = 1; function f() { a$c = 5; }"); + } + + public void testGlobalNameReferencedInLocalScopeBeforeDefined1() { + // Because referencing global names earlier in the source code than they're + // defined is such a common practice, we collapse them even though a runtime + // exception could result (in the off-chance that the function gets called + // before the alias variable is defined). + test("var a = {b: 0}; function f() { a.c = 5; } a.c = 1;", + "var a$b = 0; function f() { a$c = 5; } var a$c = 1;"); + } + + public void testGlobalNameReferencedInLocalScopeBeforeDefined2() { + test("var a = {b: 0}; function f() { return a.c; } a.c = 1;", + "var a$b = 0; function f() { return a$c; } var a$c = 1;"); + } + + public void testTwiceDefinedGlobalNameDepth1_1() { + testSame("var a = {}; function f() { a.b(); }" + + "a = function() {}; a.b = function() {};"); + } + + public void testTwiceDefinedGlobalNameDepth1_2() { + testSame("var a = {}; /** @constructor */ a = function() {};" + + "a.b = {}; a.b.c = 0; function f() { a.b.d = 1; }"); + } + + public void testTwiceDefinedGlobalNameDepth2() { + test("var a = {}; a.b = {}; function f() { a.b.c(); }" + + "a.b = function() {}; a.b.c = function() {};", + "var a$b = {}; function f() { a$b.c(); }" + + "a$b = function() {}; a$b.c = function() {};"); + } + + public void testFunctionCallDepth1() { + test("var a = {}; a.b = function(){}; var c = a.b();", + "var a$b = function(){}; var c = a$b()"); + } + + public void testFunctionCallDepth2() { + test("var a = {}; a.b = {}; a.b.c = function(){}; a.b.c();", + "var a$b$c = function(){}; a$b$c();"); + } + + public void testFunctionAlias() { + test("var a = {}; a.b = {}; a.b.c = function(){}; a.b.d = a.b.c;", + "var a$b$c = function(){}; var a$b$d = a$b$c;"); + } + + public void testCallToRedefinedFunction() { + test("var a = {}; a.b = function(){}; a.b = function(){}; a.b();", + "var a$b = function(){}; a$b = function(){}; a$b();"); + } + + public void testCollapsePrototypeName() { + test("var a = {}; a.b = {}; a.b.c = function(){}; " + + "a.b.c.prototype.d = function(){}; (new a.b.c()).d();", + "var a$b$c = function(){}; a$b$c.prototype.d = function(){}; " + + "new a$b$c().d();"); + } + + public void testReferencedPrototypeProperty() { + test("var a = {b: {}}; a.b.c = function(){}; a.b.c.prototype.d = {};" + + "e = a.b.c.prototype.d;", + "var a$b$c = function(){}; a$b$c.prototype.d = {};" + + "e = a$b$c.prototype.d;"); + } + + public void testSetStaticAndPrototypePropertiesOnFunction() { + test("var a = {}; a.b = function(){}; a.b.prototype.d = 0; a.b.c = 1;", + "var a$b = function(){}; a$b.prototype.d = 0; var a$b$c = 1;"); + } + + public void testReadUndefinedPropertyDepth1() { + test("var a = {b: 0}; var c = a.d;", + "var a$b = 0; var a = {}; var c = a.d;"); + } + + public void testReadUndefinedPropertyDepth2() { + test("var a = {b: {c: 0}}; f(a.b.c); f(a.b.d);", + "var a$b$c = 0; var a$b = {}; f(a$b$c); f(a$b.d);"); + } + + public void testCallUndefinedMethodOnObjLitDepth1() { + test("var a = {b: 0}; a.c();", + "var a$b = 0; var a = {}; a.c();"); + } + + public void testCallUndefinedMethodOnObjLitDepth2() { + test("var a = {b: {}}; a.b.c = function() {}; a.b.c(); a.b.d();", + "var a$b = {}; var a$b$c = function() {}; a$b$c(); a$b.d();"); + } + + public void testPropertiesOfAnUndefinedVar() { + testSame("a.document = d; f(a.document.innerHTML);"); + } + + public void testPropertyOfAnObjectThatIsNeitherFunctionNorObjLit() { + testSame("var a = window; a.document = d; f(a.document)"); + } + + public void testStaticFunctionReferencingThis1() { + // Note: Google's JavaScript Style Guide says to avoid using the 'this' + // keyword in a static function. + test("var a = {}; a.b = function() {this.c}; var d = a.b;", + "var a$b = function() {this.c}; var d = a$b;", null, UNSAFE_THIS); + } + + public void testStaticFunctionReferencingThis2() { + // This gives no warning, because "this" is in a scope whose name is not + // getting collapsed. + test("var a = {}; " + + "a.b = function() { return function(){ return this; }; };", + "var a$b = function() { return function(){ return this; }; };"); + } + + public void testStaticFunctionReferencingThis3() { + test("var a = {b: function() {this.c}};", + "var a$b = function() { this.c };", null, UNSAFE_THIS); + } + + public void testStaticFunctionReferencingThis4() { + test("var a = {/** @this {Element} */ b: function() {this.c}};", + "var a$b = function() { this.c };"); + } + + public void testPrototypeMethodReferencingThis() { + testSame("var A = function(){}; A.prototype = {b: function() {this.c}};"); + } + + public void testConstructorReferencingThis() { + test("var a = {}; " + + "/** @constructor */ a.b = function() { this.a = 3; };", + "var a$b = function() { this.a = 3; };"); + } + + public void testSafeReferenceOfThis() { + test("var a = {}; " + + "/** @this {Object} */ a.b = function() { this.a = 3; };", + "var a$b = function() { this.a = 3; };"); + } + + public void testGlobalFunctionReferenceOfThis() { + testSame("var a = function() { this.a = 3; };"); + } + + public void testFunctionGivenTwoNames() { + // It's okay to collapse f's properties because g is not added to the + // global scope as an alias for f. (Try it in your browser.) + test("var f = function g() {}; f.a = 1; h(f.a);", + "var f = function g() {}; var f$a = 1; h(f$a);"); + } + + public void testObjLitWithUsedNumericKey() { + testSame("a = {40: {}, c: {}}; var d = a[40]; var e = a.c;"); + } + + public void testObjLitWithUnusedNumericKey() { + test("var a = {40: {}, c: {}}; var e = a.c;", + "var a$1 = {}; var a$c = {}; var e = a$c"); + } + + public void testObjLitWithNonIdentifierKeys() { + testSame("a = {' ': 0, ',': 1}; var c = a[' '];"); + } + + public void testChainedAssignments1() { + test("var x = {}; x.y = a = 0;", + "var x$y = a = 0;"); + } + + public void testChainedAssignments2() { + test("var x = {}; x.y = a = b = c();", + "var x$y = a = b = c();"); + } + + public void testChainedAssignments3() { + test("var x = {y: 1}; a = b = x.y;", + "var x$y = 1; a = b = x$y;"); + } + + public void testChainedAssignments4() { + test("var x = {}; a = b = x.y;", + "var x = {}; a = b = x.y;"); + } + + public void testChainedAssignments5() { + test("var x = {}; a = x.y = 0;", "var x$y; a = x$y = 0;"); + } + + public void testChainedAssignments6() { + test("var x = {}; a = x.y = b = c();", + "var x$y; a = x$y = b = c();"); + } + + public void testChainedAssignments7() { + test("var x = {}; a = x.y = {}; /** @constructor */ x.y.z = function() {};", + "var x$y; a = x$y = {}; var x$y$z = function() {};", + null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); + } + + public void testChainedVarAssignments1() { + test("var x = {y: 1}; var a = x.y = 0;", + "var x$y = 1; var a = x$y = 0;"); + } + + public void testChainedVarAssignments2() { + test("var x = {y: 1}; var a = x.y = b = 0;", + "var x$y = 1; var a = x$y = b = 0;"); + } + + public void testChainedVarAssignments3() { + test("var x = {y: {z: 1}}; var b = 0; var a = x.y.z = 1; var c = 2;", + "var x$y$z = 1; var b = 0; var a = x$y$z = 1; var c = 2;"); + } + + public void testChainedVarAssignments4() { + test("var x = {}; var a = b = x.y = 0;", + "var x$y; var a = b = x$y = 0;"); + } + + public void testChainedVarAssignments5() { + test("var x = {y: {}}; var a = b = x.y.z = 0;", + "var x$y$z; var a = b = x$y$z = 0;"); + } + + public void testPeerAndSubpropertyOfUncollapsibleProperty() { + test("var x = {}; var a = x.y = 0; x.w = 1; x.y.z = 2;" + + "b = x.w; c = x.y.z;", + "var x$y; var a = x$y = 0; var x$w = 1; x$y.z = 2;" + + "b = x$w; c = x$y.z;"); + } + + public void testComplexAssignmentAfterInitialAssignment() { + test("var d = {}; d.e = {}; d.e.f = 0; a = b = d.e.f = 1;", + "var d$e$f = 0; a = b = d$e$f = 1;"); + } + + public void testRenamePrefixOfUncollapsibleProperty() { + test("var d = {}; d.e = {}; a = b = d.e.f = 0;", + "var d$e$f; a = b = d$e$f = 0;"); + } + + public void testNewOperator() { + // Using the new operator on a name doesn't prevent its (static) properties + // from getting collapsed. + test("var a = {}; a.b = function() {}; a.b.c = 1; var d = new a.b();", + "var a$b = function() {}; var a$b$c = 1; var d = new a$b();"); + } + + public void testMethodCall() { + test("var a = {}; a.b = function() {}; var d = a.b();", + "var a$b = function() {}; var d = a$b();"); + } + + public void testObjLitDefinedInLocalScopeIsLeftAlone() { + test("var a = {}; a.b = function() {};" + + "a.b.prototype.f_ = function() {" + + " var x = { p: '', q: '', r: ''}; var y = x.q;" + + "};", + "var a$b = function() {};" + + "a$b.prototype.f_ = function() {" + + " var x = { p: '', q: '', r: ''}; var y = x.q;" + + "};"); + } + + public void testPropertiesOnBothSidesOfAssignment() { + // This verifies that replacements are done in the right order. Collapsing + // the l-value in an assignment affects the parse tree immediately above + // the r-value, so we update all rvalues before any lvalues. + test("var a = {b: 0}; a.c = a.b;", "var a$b = 0; var a$c = a$b;"); + } + + public void testCallOnUndefinedProperty() { + // The "inherits" property is not explicitly defined on a.b anywhere, but + // it is accessed as though it certainly exists (it is called), so we infer + // that it must be an uncollapsible property that has come into existence + // some other way. + test("var a = {}; a.b = function(){}; a.b.inherits(x);", + "var a$b = function(){}; a$b.inherits(x);"); + } + + public void testGetPropOnUndefinedProperty() { + // The "superClass_" property is not explicitly defined on a.b anywhere, + // but it is accessed as though it certainly exists (a subproperty of it + // is accessed), so we infer that it must be an uncollapsible property that + // has come into existence some other way. + test("var a = {b: function(){}}; a.b.prototype.c =" + + "function() { a.b.superClass_.c.call(this); }", + "var a$b = function(){}; a$b.prototype.c =" + + "function() { a$b.superClass_.c.call(this); }"); + } + + public void testLocalAlias1() { + test("var a = {b: 3}; function f() { var x = a; f(x.b); }", + "var a$b = 3; function f() { var x = null; f(a$b); }"); + } + + public void testLocalAlias2() { + test("var a = {b: 3, c: 4}; function f() { var x = a; f(x.b); f(x.c);}", + "var a$b = 3; var a$c = 4; " + + "function f() { var x = null; f(a$b); f(a$c);}"); + } + + public void testLocalAlias3() { + test("var a = {b: 3, c: {d: 5}}; " + + "function f() { var x = a; f(x.b); f(x.c); f(x.c.d); }", + "var a$b = 3; var a$c = {d: 5}; " + + "function f() { var x = null; f(a$b); f(a$c); f(a$c.d);}"); + } + + public void testLocalAlias4() { + test("var a = {b: 3}; var c = {d: 5}; " + + "function f() { var x = a; var y = c; f(x.b); f(y.d); }", + "var a$b = 3; var c$d = 5; " + + "function f() { var x = null; var y = null; f(a$b); f(c$d);}"); + } + + public void testLocalAlias5() { + test("var a = {b: {c: 5}}; " + + "function f() { var x = a; var y = x.b; f(a.b.c); f(y.c); }", + "var a$b$c = 5; " + + "function f() { var x = null; var y = null; f(a$b$c); f(a$b$c);}"); + } + + public void testLocalAlias6() { + test("var a = {b: 3}; function f() { var x = a; if (x.b) { f(x.b); } }", + "var a$b = 3; function f() { var x = null; if (a$b) { f(a$b); } }"); + } + + public void testLocalAlias7() { + test("var a = {b: {c: 5}}; function f() { var x = a.b; f(x.c); }", + "var a$b$c = 5; function f() { var x = null; f(a$b$c); }"); + } + + public void testGlobalWriteToAncestor() { + testSame("var a = {b: 3}; function f() { var x = a; f(a.b); } a = 5;"); + } + + public void testGlobalWriteToNonAncestor() { + test("var a = {b: 3}; function f() { var x = a; f(a.b); } a.b = 5;", + "var a$b = 3; function f() { var x = null; f(a$b); } a$b = 5;"); + } + + public void testLocalWriteToAncestor() { + testSame("var a = {b: 3}; function f() { a = 5; var x = a; f(a.b); } "); + } + + public void testLocalWriteToNonAncestor() { + test("var a = {b: 3}; " + + "function f() { a.b = 5; var x = a; f(a.b); }", + "var a$b = 3; function f() { a$b = 5; var x = null; f(a$b); } "); + } + + public void testNonWellformedAlias1() { + testSame("var a = {b: 3}; function f() { f(x); var x = a; f(x.b); }"); + } + + public void testNonWellformedAlias2() { + testSame("var a = {b: 3}; " + + "function f() { if (false) { var x = a; f(x.b); } f(x); }"); + } + + public void testLocalAliasOfAncestor() { + testSame("var a = {b: {c: 5}}; function g() { f(a); } " + + "function f() { var x = a.b; f(x.c); }"); + } + + public void testGlobalAliasOfAncestor() { + testSame("var a = {b: {c: 5}}; var y = a; " + + "function f() { var x = a.b; f(x.c); }"); + } + + public void testLocalAliasOfOtherName() { + testSame("var foo = function() { return {b: 3}; };" + + "var a = foo(); a.b = 5; " + + "function f() { var x = a.b; f(x); }"); + } + + public void testLocalAliasOfFunction() { + test("var a = function() {}; a.b = 5; " + + "function f() { var x = a.b; f(x); }", + "var a = function() {}; var a$b = 5; " + + "function f() { var x = null; f(a$b); }"); + } + + public void testNoInlineGetpropIntoCall() { + test("var b = x; function f() { var a = b; a(); }", + "var b = x; function f() { var a = null; b(); }"); + test("var b = {}; b.c = x; function f() { var a = b.c; a(); }", + "var b$c = x; function f() { var a = null; b$c(); }"); + } + + public void testInlineAliasWithModifications() { + testSame("var x = 10; function f() { var y = x; x++; alert(y)} "); + testSame("var x = 10; function f() { var y = x; x+=1; alert(y)} "); + test("var x = {}; x.x = 10; function f() {var y=x.x; x.x++; alert(y)}", + "var x$x = 10; function f() {var y=x$x; x$x++; alert(y)}"); + test("var x = {}; x.x = 10; function f() {var y=x.x; x.x+=1; alert(y)}", + "var x$x = 10; function f() {var y=x$x; x$x+=1; alert(y)}"); + } + + public void testCollapsePropertyOnExternType() { + collapsePropertiesOnExternTypes = true; + test("String.myFunc = function() {}; String.myFunc();", + "var String$myFunc = function() {}; String$myFunc()"); + } + + public void testCollapseForEachWithoutExterns() { + collapsePropertiesOnExternTypes = true; + test("/** @constructor */function Array(){};\n", + "if (!Array.forEach) {\n" + + " Array.forEach = function() {};\n" + + "}", + "if (!Array$forEach) {\n" + + " var Array$forEach = function() {};\n" + + "}", null, null); + } + + public void testNoCollapseForEachInExterns() { + collapsePropertiesOnExternTypes = true; + test("/** @constructor */ function Array() {}" + + "Array.forEach = function() {}", + "if (!Array.forEach) {\n" + + " Array.forEach = function() {};\n" + + "}", + "if (!Array.forEach) {\n" + + " Array.forEach = function() {};\n" + + "}", null, null); + } + + public void testDoNotCollapsePropertyOnExternType() { + collapsePropertiesOnExternTypes = false; + test("String.myFunc = function() {}; String.myFunc()", + "String.myFunc = function() {}; String.myFunc()"); + } + + public void testBug1704733() { + String prelude = + "function protect(x) { return x; }" + + "function O() {}" + + "protect(O).m1 = function() {};" + + "protect(O).m2 = function() {};" + + "protect(O).m3 = function() {};"; + + testSame(prelude + + "alert(O.m1); alert(O.m2()); alert(!O.m3);"); + } + + public void testBug1956277() { + test("var CONST = {}; CONST.URL = 3;", + "var CONST$URL = 3;"); + } + + public void testBug1974371() { + test( + "/** @enum {Object} */ var Foo = {A: {c: 2}, B: {c: 3}};" + + "for (var key in Foo) {}", + "var Foo$A = {c: 2}; var Foo$B = {c: 3};" + + "var Foo = {A: Foo$A, B: Foo$B};" + + "for (var key in Foo) {}"); + } + + private final String COMMON_ENUM = + "/** @enum {Object} */ var Foo = {A: {c: 2}, B: {c: 3}};"; + + public void testEnumOfObjects1() { + test( + COMMON_ENUM + + "for (var key in Foo.A) {}", + "var Foo$A = {c: 2}; var Foo$B$c = 3; for (var key in Foo$A) {}"); + } + + public void testEnumOfObjects2() { + test( + COMMON_ENUM + + "foo(Foo.A.c);", + "var Foo$A$c = 2; var Foo$B$c = 3; foo(Foo$A$c);"); + } + + public void testEnumOfObjects3() { + test( + "var x = {c: 2}; var y = {c: 3};" + + "/** @enum {Object} */ var Foo = {A: x, B: y};" + + "for (var key in Foo) {}", + "var x = {c: 2}; var y = {c: 3};" + + "var Foo$A = x; var Foo$B = y; var Foo = {A: Foo$A, B: Foo$B};" + + "for (var key in Foo) {}"); + } + + public void testEnumOfObjects4() { + // Note that this produces bad code, but that's OK, because + // checkConsts will yell at you for reassigning an enum value. + // (enum values have to be constant). + test( + COMMON_ENUM + + "for (var key in Foo) {} Foo.A = 3; alert(Foo.A);", + "var Foo$A = {c: 2}; var Foo$B = {c: 3};" + + "var Foo = {A: Foo$A, B: Foo$B};" + + "for (var key in Foo) {} Foo$A = 3; alert(Foo$A);"); + } + + public void testObjectOfObjects1() { + // Basically the same as testEnumOfObjects4, but without the + // constant enum values. + testSame( + "var Foo = {a: {c: 2}, b: {c: 3}};" + + "for (var key in Foo) {} Foo.a = 3; alert(Foo.a);"); + } + + public void testReferenceInAnonymousObject0() { + test("var a = {};" + + "a.b = function(){};" + + "a.b.prototype.c = function(){};" + + "var d = a.b.prototype.c;", + "var a$b = function(){};" + + "a$b.prototype.c = function(){};" + + "var d = a$b.prototype.c;"); + } + + public void testReferenceInAnonymousObject1() { + test("var a = {};" + + "a.b = function(){};" + + "var d = a.b.prototype.c;", + "var a$b = function(){};" + + "var d = a$b.prototype.c;"); + } + + public void testReferenceInAnonymousObject2() { + test("var a = {};" + + "a.b = function(){};" + + "a.b.prototype.c = function(){};" + + "var d = {c: a.b.prototype.c};", + "var a$b = function(){};" + + "a$b.prototype.c = function(){};" + + "var d$c = a$b.prototype.c;"); + } + + public void testReferenceInAnonymousObject3() { + test("function CreateClass(a$$1) {}" + + "var a = {};" + + "a.b = function(){};" + + "a.b.prototype.c = function(){};" + + "a.d = CreateClass({c: a.b.prototype.c});", + "function CreateClass(a$$1) {}" + + "var a$b = function(){};" + + "a$b.prototype.c = function(){};" + + "var a$d = CreateClass({c: a$b.prototype.c});"); + } + + public void testReferenceInAnonymousObject4() { + test("function CreateClass(a) {}" + + "var a = {};" + + "a.b = CreateClass({c: function() {}});" + + "a.d = CreateClass({c: a.b.c});", + "function CreateClass(a$$1) {}" + + "var a$b = CreateClass({c: function() {}});" + + "var a$d = CreateClass({c: a$b.c});"); + } + + public void testReferenceInAnonymousObject5() { + test("function CreateClass(a) {}" + + "var a = {};" + + "a.b = CreateClass({c: function() {}});" + + "a.d = CreateClass({c: a.b.prototype.c});", + "function CreateClass(a$$1) {}" + + "var a$b = CreateClass({c: function() {}});" + + "var a$d = CreateClass({c: a$b.prototype.c});"); + } + + public void testCrashInCommaOperator() { + test("var a = {}; a.b = function() {},a.b();", + "var a$b; a$b=function() {},a$b();"); + } + + public void testCrashInNestedAssign() { + test("var a = {}; if (a.b = function() {}) a.b();", + "var a$b; if (a$b=function() {}) { a$b(); }"); + } + + public void testTwinReferenceCancelsChildCollapsing() { + test("var a = {}; if (a.b = function() {}) { a.b.c = 3; a.b(a.b.c); }", + "var a$b; if (a$b = function() {}) { a$b.c = 3; a$b(a$b.c); }"); + } + + public void testPropWithDollarSign() { + test("var a = {$: 3}", "var a$$0 = 3;"); + } + + public void testPropWithDollarSign2() { + test("var a = {$: function(){}}", "var a$$0 = function(){};"); + } + + public void testPropWithDollarSign3() { + test("var a = {b: {c: 3}, b$c: function(){}}", + "var a$b$c = 3; var a$b$0c = function(){};"); + } + + public void testPropWithDollarSign4() { + test("var a = {$$: {$$$: 3}};", "var a$$0$0$$0$0$0 = 3;"); + } + + public void testPropWithDollarSign5() { + test("var a = {b: {$0c: true}, b$0c: false};", + "var a$b$$00c = true; var a$b$00c = false;"); + } + + public void testConstKey() { + test("var foo = {A: 3};", "var foo$A = 3;"); + } + + public void testPropertyOnGlobalCtor() { + test("/** @constructor */ function Map() {} Map.foo = 3; Map;", + "function Map() {} var Map$foo = 3; Map;"); + } + + public void testPropertyOnGlobalInterface() { + test("/** @interface */ function Map() {} Map.foo = 3; Map;", + "function Map() {} var Map$foo = 3; Map;"); + } + + public void testPropertyOnGlobalFunction() { + testSame("function Map() {} Map.foo = 3; Map;"); + } + + public void testIssue389() { + test( + "function alias() {}" + + "var dojo = {};" + + "dojo.gfx = {};" + + "dojo.declare = function() {};" + + "/** @constructor */" + + "dojo.gfx.Shape = function() {};" + + "dojo.gfx.Shape = dojo.declare('dojo.gfx.Shape');" + + "alias(dojo);", + "function alias() {}" + + "var dojo = {};" + + "dojo.gfx = {};" + + "dojo.declare = function() {};" + + "/** @constructor */" + + "var dojo$gfx$Shape = function() {};" + + "dojo$gfx$Shape = dojo.declare('dojo.gfx.Shape');" + + "alias(dojo);", + null, + CollapseProperties.UNSAFE_NAMESPACE_WARNING); + } + + public void testAliasedTopLevelName() { + testSame( + "function alias() {}" + + "var dojo = {};" + + "dojo.gfx = {};" + + "dojo.declare = function() {};" + + "dojo.gfx.Shape = {SQUARE: 2};" + + "dojo.gfx.Shape = dojo.declare('dojo.gfx.Shape');" + + "alias(dojo);" + + "alias(dojo$gfx$Shape$SQUARE);"); + } + + public void testAliasedTopLevelEnum() { + test( + "function alias() {}" + + "var dojo = {};" + + "dojo.gfx = {};" + + "dojo.declare = function() {};" + + "/** @enum {number} */" + + "dojo.gfx.Shape = {SQUARE: 2};" + + "dojo.gfx.Shape = dojo.declare('dojo.gfx.Shape');" + + "alias(dojo);" + + "alias(dojo.gfx.Shape.SQUARE);", + "function alias() {}" + + "var dojo = {};" + + "dojo.gfx = {};" + + "dojo.declare = function() {};" + + "/** @constructor */" + + "var dojo$gfx$Shape = {SQUARE: 2};" + + "dojo$gfx$Shape = dojo.declare('dojo.gfx.Shape');" + + "alias(dojo);" + + "alias(dojo$gfx$Shape.SQUARE);", + null, + CollapseProperties.UNSAFE_NAMESPACE_WARNING); + } + + public void testAssignFunctionBeforeDefinition() { + testSame( + "f = function() {};" + + "var f = null;"); + } + + public void testObjectLitBeforeDefinition() { + testSame( + "a = {b: 3};" + + "var a = null;" + + "this.c = a.b;"); + } + + public void testTypedef1() { + test("var foo = {};" + + "/** @typedef {number} */ foo.Baz;", + "var foo = {}; var foo$Baz;"); + } + + public void testTypedef2() { + test("var foo = {};" + + "/** @typedef {number} */ foo.Bar.Baz;" + + "foo.Bar = function() {};", + "var foo$Bar$Baz; var foo$Bar = function(){};"); + } + + public void testDelete1() { + testSame( + "var foo = {};" + + "foo.bar = 3;" + + "delete foo.bar;"); + } + + public void testDelete2() { + test( + "var foo = {};" + + "foo.bar = 3;" + + "foo.baz = 3;" + + "delete foo.bar;", + "var foo = {};" + + "foo.bar = 3;" + + "var foo$baz = 3;" + + "delete foo.bar;"); + } + + public void testDelete3() { + testSame( + "var foo = {bar: 3};" + + "delete foo.bar;"); + } + + public void testDelete4() { + test( + "var foo = {bar: 3, baz: 3};" + + "delete foo.bar;", + "var foo$baz=3;var foo={bar:3};delete foo.bar"); + } + + public void testDelete5() { + test( + "var x = {};" + + "x.foo = {};" + + "x.foo.bar = 3;" + + "delete x.foo.bar;", + "var x$foo = {};" + + "x$foo.bar = 3;" + + "delete x$foo.bar;"); + } + + public void testDelete6() { + test( + "var x = {};" + + "x.foo = {};" + + "x.foo.bar = 3;" + + "x.foo.baz = 3;" + + "delete x.foo.bar;", + "var x$foo = {};" + + "x$foo.bar = 3;" + + "var x$foo$baz = 3;" + + "delete x$foo.bar;"); + } + + public void testDelete7() { + test( + "var x = {};" + + "x.foo = {bar: 3};" + + "delete x.foo.bar;", + "var x$foo = {bar: 3};" + + "delete x$foo.bar;"); + } + + public void testDelete8() { + test( + "var x = {};" + + "x.foo = {bar: 3, baz: 3};" + + "delete x.foo.bar;", + "var x$foo$baz = 3; var x$foo = {bar: 3};" + + "delete x$foo.bar;"); + } + + public void testDelete9() { + testSame( + "var x = {};" + + "x.foo = {};" + + "x.foo.bar = 3;" + + "delete x.foo;"); + } + + public void testDelete10() { + testSame( + "var x = {};" + + "x.foo = {bar: 3};" + + "delete x.foo;"); + } + + public void testDelete11() { + // Constructors are always collapsed. + test( + "var x = {};" + + "x.foo = {};" + + "/** @constructor */ x.foo.Bar = function() {};" + + "delete x.foo;", + "var x = {};" + + "x.foo = {};" + + "var x$foo$Bar = function() {};" + + "delete x.foo;", + null, + CollapseProperties.NAMESPACE_REDEFINED_WARNING); + } + + public void testPreserveConstructorDoc() { + test("var foo = {};" + + "/** @constructor */\n" + + "foo.bar = function() {}", + "var foo$bar = function() {}"); + + Node root = getLastCompiler().getRoot(); + + Node fooBarNode = findQualifiedNameNode("foo$bar", root); + Node varNode = fooBarNode.getParent(); + assertTrue(varNode.isVar()); + assertTrue(varNode.getJSDocInfo().isConstructor()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CollapseVariableDeclarationsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CollapseVariableDeclarationsTest.java new file mode 100644 index 0000000..5e318e1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CollapseVariableDeclarationsTest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Tests for variable declaration collapsing. + * + */ +public class CollapseVariableDeclarationsTest extends CompilerTestCase { + public void testCollapsing() throws Exception { + // Basic collapsing + test("var a;var b;", + "var a,b;"); + // With initial values + test("var a = 1;var b = 1;", + "var a=1,b=1;"); + // Already collapsed + test("var a, b;", + "var a,b;"); + // Already collapsed with values + test("var a = 1, b = 1;", + "var a=1,b=1;"); + // Some already collapsed + test("var a;var b, c;var d;", + "var a,b,c,d;"); + // Some already collapsed with values + test("var a = 1;var b = 2, c = 3;var d = 4;", + "var a=1,b=2,c=3,d=4;"); + } + + public void testIssue820() throws Exception { + // Don't redeclare function parameters, this is incompatible with + // strict mode. + testSame("function f(a){ var b=1; a=2; var c; }"); + } + + public void testIfElseVarDeclarations() throws Exception { + testSame("if (x) var a = 1; else var b = 2;"); + } + + public void testAggressiveRedeclaration() { + test("var x = 2; foo(x); x = 3; var y = 2;", + "var x = 2; foo(x); var x = 3, y = 2;"); + + test("var x = 2; foo(x); x = 3; x = 1; var y = 2;", + "var x = 2; foo(x); var x = 3, x = 1, y = 2;"); + + test("var x = 2; foo(x); x = 3; x = 1; var y = 2; var z = 4", + "var x = 2; foo(x); var x = 3, x = 1, y = 2, z = 4"); + + test("var x = 2; foo(x); x = 3; x = 1; var y = 2; var z = 4; x = 5", + "var x = 2; foo(x); var x = 3, x = 1, y = 2, z = 4, x = 5"); + } + + public void testAggressiveRedeclarationInFor() { + testSame("for(var x = 1; x = 2; x = 3) {x = 4}"); + testSame("for(var x = 1; y = 2; z = 3) {var a = 4}"); + testSame("var x; for(x = 1; x = 2; z = 3) {x = 4}"); + } + + public void testIssue397() { + test("var x; var y = 3; x = 5;", + "var x, y = 3; x = 5;"); + testSame("var x; x = 5; var z = 7;"); + test("var x; var y = 3; x = 5; var z = 7;", + "var x, y = 3; x = 5; var z = 7;"); + test("var a = 1; var x; var y = 3; x = 5;", + "var a = 1, x, y = 3; x = 5;"); + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new CollapseVariableDeclarations(compiler); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CombinedCompilerPassTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CombinedCompilerPassTest.java new file mode 100644 index 0000000..f85d158 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CombinedCompilerPassTest.java @@ -0,0 +1,260 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import junit.framework.TestCase; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + */ +public class CombinedCompilerPassTest extends TestCase { + + private Compiler compiler; + + /** + * Returns a Node tree with the post-order traversal a b c d e f g h i j k l m + * and the in-order traversal m d a b c h e f g l i j k: + * + * m + * ,---------|---------. + * d h l + * ,--|--. ,--|--. ,--|--. + * a b c e f g i j k + * + */ + private static Node createPostOrderAlphabet() { + Node a = Node.newString("a"); + Node b = Node.newString("b"); + Node c = Node.newString("c"); + Node d = Node.newString("d"); + Node e = Node.newString("e"); + Node f = Node.newString("f"); + Node g = Node.newString("g"); + Node h = Node.newString("h"); + Node i = Node.newString("i"); + Node j = Node.newString("j"); + Node k = Node.newString("k"); + Node l = Node.newString("l"); + Node m = Node.newString("m"); + + d.addChildToBack(a); + d.addChildToBack(b); + d.addChildToBack(c); + + h.addChildrenToBack(e); + h.addChildrenToBack(f); + h.addChildrenToBack(g); + + l.addChildToBack(i); + l.addChildToBack(j); + l.addChildToBack(k); + + m.addChildToBack(d); + m.addChildToBack(h); + m.addChildToBack(l); + + return m; + } + + @Override + public void setUp() throws Exception { + super.setUp(); + compiler = new Compiler(); + } + + /** + * Concatenates contents of string nodes encountered in pre-order + * and post-order traversals. Abbreviates traversals by ignoring subtrees + * rooted with specified strings. + */ + private static class ConcatTraversal implements Callback { + private StringBuilder visited = new StringBuilder(); + private StringBuilder shouldTraversed = new StringBuilder(); + private Set ignoring = Sets.newHashSet(); + + ConcatTraversal ignore(String s) { + ignoring.add(s); + return this; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + assertEquals(Token.STRING, n.getType()); + visited.append(n.getString()); + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + assertEquals(Token.STRING, n.getType()); + shouldTraversed.append(n.getString()); + return !ignoring.contains(n.getString()); + } + + /** Returns strings concatenated during post-order traversal. */ + String getVisited() { + return visited.toString(); + } + + /** Returns strings concatenated during pre-order traversal. */ + String getShouldTraversed() { + return shouldTraversed.toString(); + } + + Collection getIgnoring() { + return ignoring; + } + } + + /** + * Collection of data for a traversal test. Contains the traversal callback + * and the expected pre- and post-order traversal results. + */ + private static class TestHelper { + private ConcatTraversal traversal; + private String expectedVisited; + private String shouldTraverseExpected; + + TestHelper(ConcatTraversal traversal, String expectedVisited, + String shouldTraverseExpected) { + this.traversal = traversal; + this.expectedVisited = expectedVisited; + this.shouldTraverseExpected = shouldTraverseExpected; + } + + ConcatTraversal getTraversal() { + return traversal; + } + + void checkResults() { + assertEquals("ConcatTraversal ignoring " + + traversal.getIgnoring().toString() + + " has unexpected visiting order", + expectedVisited, traversal.getVisited()); + + assertEquals("ConcatTraversal ignoring " + + traversal.getIgnoring().toString() + + " has unexpected traversal order", + shouldTraverseExpected, traversal.getShouldTraversed()); + } + } + + private static List createStringTests() { + List tests = Lists.newArrayList(); + + tests.add(new TestHelper( + new ConcatTraversal(), "abcdefghijklm", "mdabchefglijk")); + + tests.add(new TestHelper( + new ConcatTraversal().ignore("d"), "efghijklm", "mdhefglijk")); + + tests.add(new TestHelper( + new ConcatTraversal().ignore("f"), "abcdeghijklm", "mdabchefglijk")); + + tests.add(new TestHelper(new ConcatTraversal().ignore("m"), "", "m")); + + return tests; + } + + public void testIndividualPasses() { + for (TestHelper test : createStringTests()) { + CombinedCompilerPass pass = + new CombinedCompilerPass(compiler, test.getTraversal()); + pass.process(null, createPostOrderAlphabet()); + test.checkResults(); + } + } + + public void testCombinedPasses() { + List tests = createStringTests(); + Callback[] callbacks = new Callback[tests.size()]; + int i = 0; + for (TestHelper test : tests) { + callbacks[i++] = test.getTraversal(); + } + CombinedCompilerPass pass = + new CombinedCompilerPass(compiler, callbacks); + pass.process(null, createPostOrderAlphabet()); + for (TestHelper test : tests) { + test.checkResults(); + } + } + + /** + * Records the scopes visited during an AST traversal. Abbreviates traversals + * by ignoring subtrees rooted with specified NAME nodes. + */ + private static class ScopeRecordingCallback implements ScopedCallback { + + Set visitedScopes = Sets.newHashSet(); + Set ignoring = Sets.newHashSet(); + + void ignore(String name) { + ignoring.add(name); + } + + @Override + public void enterScope(NodeTraversal t) { + visitedScopes.add(t.getScopeRoot()); + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + return !n.isName() || !ignoring.contains(n.getString()); + } + + Set getVisitedScopes() { + return visitedScopes; + } + + @Override + public void exitScope(NodeTraversal t) { + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + } + + } + + public void testScopes() { + Node root = + compiler.parseTestCode("var y = function() { var x = function() { };}"); + + ScopeRecordingCallback c1 = new ScopeRecordingCallback(); + c1.ignore("y"); + ScopeRecordingCallback c2 = new ScopeRecordingCallback(); + c2.ignore("x"); + ScopeRecordingCallback c3 = new ScopeRecordingCallback(); + + CombinedCompilerPass pass = new CombinedCompilerPass(compiler, c1, c2, c3); + pass.process(null, root); + + assertEquals(1, c1.getVisitedScopes().size()); + assertEquals(2, c2.getVisitedScopes().size()); + assertEquals(3, c3.getVisitedScopes().size()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CommandLineRunnerTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CommandLineRunnerTest.java new file mode 100644 index 0000000..3b72929 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CommandLineRunnerTest.java @@ -0,0 +1,1338 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.AbstractCommandLineRunner.FlagUsageException; +import com.google.javascript.jscomp.CompilerOptions.LanguageMode; +import com.google.javascript.rhino.Node; + +import junit.framework.TestCase; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.List; +import java.util.Map; + +/** + * Tests for {@link CommandLineRunner}. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class CommandLineRunnerTest extends TestCase { + + private Compiler lastCompiler = null; + private CommandLineRunner lastCommandLineRunner = null; + private List exitCodes = null; + private ByteArrayOutputStream outReader = null; + private ByteArrayOutputStream errReader = null; + private Map filenames; + + // If set, this will be appended to the end of the args list. + // For testing args parsing. + private String lastArg = null; + + // If set to true, uses comparison by string instead of by AST. + private boolean useStringComparison = false; + + private ModulePattern useModules = ModulePattern.NONE; + + private enum ModulePattern { + NONE, + CHAIN, + STAR + } + + private List args = Lists.newArrayList(); + + /** Externs for the test */ + private final List DEFAULT_EXTERNS = ImmutableList.of( + SourceFile.fromCode("externs", + "var arguments;" + + "/**\n" + + " * @constructor\n" + + " * @param {...*} var_args\n" + + " * @nosideeffects\n" + + " * @throws {Error}\n" + + " */\n" + + "function Function(var_args) {}\n" + + "/**\n" + + " * @param {...*} var_args\n" + + " * @return {*}\n" + + " */\n" + + "Function.prototype.call = function(var_args) {};" + + "/**\n" + + " * @constructor\n" + + " * @param {...*} var_args\n" + + " * @return {!Array}\n" + + " */\n" + + "function Array(var_args) {}" + + "/**\n" + + " * @param {*=} opt_begin\n" + + " * @param {*=} opt_end\n" + + " * @return {!Array}\n" + + " * @this {Object}\n" + + " */\n" + + "Array.prototype.slice = function(opt_begin, opt_end) {};" + + "/** @constructor */ function Window() {}\n" + + "/** @type {string} */ Window.prototype.name;\n" + + "/** @type {Window} */ var window;" + + "/** @constructor */ function Element() {}" + + "Element.prototype.offsetWidth;" + + "/** @nosideeffects */ function noSideEffects() {}\n" + + "/** @param {...*} x */ function alert(x) {}\n") + ); + + private List externs; + + @Override + public void setUp() throws Exception { + super.setUp(); + externs = DEFAULT_EXTERNS; + filenames = Maps.newHashMap(); + lastCompiler = null; + lastArg = null; + outReader = new ByteArrayOutputStream(); + errReader = new ByteArrayOutputStream(); + useStringComparison = false; + useModules = ModulePattern.NONE; + args.clear(); + exitCodes = Lists.newArrayList(); + } + + @Override + public void tearDown() throws Exception { + super.tearDown(); + } + + public void testWarningGuardOrdering1() { + args.add("--jscomp_error=globalThis"); + args.add("--jscomp_off=globalThis"); + testSame("function f() { this.a = 3; }"); + } + + public void testWarningGuardOrdering2() { + args.add("--jscomp_off=globalThis"); + args.add("--jscomp_error=globalThis"); + test("function f() { this.a = 3; }", CheckGlobalThis.GLOBAL_THIS); + } + + public void testWarningGuardOrdering3() { + args.add("--jscomp_warning=globalThis"); + args.add("--jscomp_off=globalThis"); + testSame("function f() { this.a = 3; }"); + } + + public void testWarningGuardOrdering4() { + args.add("--jscomp_off=globalThis"); + args.add("--jscomp_warning=globalThis"); + test("function f() { this.a = 3; }", CheckGlobalThis.GLOBAL_THIS); + } + + public void testSimpleModeLeavesUnusedParams() { + args.add("--compilation_level=SIMPLE_OPTIMIZATIONS"); + testSame("window.f = function(a) {};"); + } + + public void testAdvancedModeRemovesUnusedParams() { + args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); + test("window.f = function(a) {};", "window.a = function() {};"); + } + + public void testCheckGlobalThisOffByDefault() { + testSame("function f() { this.a = 3; }"); + } + + public void testCheckGlobalThisOnWithAdvancedMode() { + args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); + test("function f() { this.a = 3; }", CheckGlobalThis.GLOBAL_THIS); + } + + public void testCheckGlobalThisOnWithErrorFlag() { + args.add("--jscomp_error=globalThis"); + test("function f() { this.a = 3; }", CheckGlobalThis.GLOBAL_THIS); + } + + public void testCheckGlobalThisOff() { + args.add("--warning_level=VERBOSE"); + args.add("--jscomp_off=globalThis"); + testSame("function f() { this.a = 3; }"); + } + + public void testTypeCheckingOffByDefault() { + test("function f(x) { return x; } f();", + "function f(a) { return a; } f();"); + } + + public void testReflectedMethods() { + args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); + test( + "/** @constructor */" + + "function Foo() {}" + + "Foo.prototype.handle = function(x, y) { alert(y); };" + + "var x = goog.reflect.object(Foo, {handle: 1});" + + "for (var i in x) { x[i].call(x); }" + + "window['Foo'] = Foo;", + "function a() {}" + + "a.prototype.a = function(e, d) { alert(d); };" + + "var b = goog.c.b(a, {a: 1}),c;" + + "for (c in b) { b[c].call(b); }" + + "window.Foo = a;"); + } + + public void testInlineVariables() { + args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); + test( + "/** @constructor */ function F() { this.a = 0; }" + + "F.prototype.inc = function() { this.a++; return 10; };" + + "F.prototype.bar = function() { " + + " var c = 3; var val = inc(); this.a += val + c;" + + "};" + + "window['f'] = new F();" + + "window['f']['bar'] = window['f'].bar;", + "function a(){ this.a = 0; }" + + "a.prototype.b = function(){ var b=inc(); this.a += b + 3; };" + + "window.f = new a;" + + "window.f.bar = window.f.b"); + } + + public void testTypedAdvanced() { + args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); + args.add("--use_types_for_optimization"); + test( + "/** @constructor */\n" + + "function Foo() {}\n" + + "Foo.prototype.handle1 = function(x, y) { alert(y); };\n" + + "/** @constructor */\n" + + "function Bar() {}\n" + + "Bar.prototype.handle1 = function(x, y) {};\n" + + "new Foo().handle1(1, 2);\n" + + "new Bar().handle1(1, 2);\n", + "alert(2)"); + } + + public void testTypeCheckingOnWithVerbose() { + args.add("--warning_level=VERBOSE"); + test("function f(x) { return x; } f();", TypeCheck.WRONG_ARGUMENT_COUNT); + } + + public void testTypeParsingOffByDefault() { + testSame("/** @return {number */ function f(a) { return a; }"); + } + + public void testTypeParsingOnWithVerbose() { + args.add("--warning_level=VERBOSE"); + test("/** @return {number */ function f(a) { return a; }", + RhinoErrorReporter.TYPE_PARSE_ERROR); + test("/** @return {n} */ function f(a) { return a; }", + RhinoErrorReporter.TYPE_PARSE_ERROR); + } + + public void testTypeCheckOverride1() { + args.add("--warning_level=VERBOSE"); + args.add("--jscomp_off=checkTypes"); + testSame("var x = x || {}; x.f = function() {}; x.f(3);"); + } + + public void testTypeCheckOverride2() { + args.add("--warning_level=DEFAULT"); + testSame("var x = x || {}; x.f = function() {}; x.f(3);"); + + args.add("--jscomp_warning=checkTypes"); + test("var x = x || {}; x.f = function() {}; x.f(3);", + TypeCheck.WRONG_ARGUMENT_COUNT); + } + + public void testCheckSymbolsOffForDefault() { + args.add("--warning_level=DEFAULT"); + test("x = 3; var y; var y;", "x=3; var y;"); + } + + public void testCheckSymbolsOnForVerbose() { + args.add("--warning_level=VERBOSE"); + test("x = 3;", VarCheck.UNDEFINED_VAR_ERROR); + test("var y; var y;", SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR); + } + + public void testCheckSymbolsOverrideForVerbose() { + args.add("--warning_level=VERBOSE"); + args.add("--jscomp_off=undefinedVars"); + testSame("x = 3;"); + } + + public void testCheckSymbolsOverrideForQuiet() { + args.add("--warning_level=QUIET"); + args.add("--jscomp_error=undefinedVars"); + test("x = 3;", VarCheck.UNDEFINED_VAR_ERROR); + } + + public void testCheckUndefinedProperties1() { + args.add("--warning_level=VERBOSE"); + args.add("--jscomp_error=missingProperties"); + test("var x = {}; var y = x.bar;", TypeCheck.INEXISTENT_PROPERTY); + } + + public void testCheckUndefinedProperties2() { + args.add("--warning_level=VERBOSE"); + args.add("--jscomp_off=missingProperties"); + test("var x = {}; var y = x.bar;", CheckGlobalNames.UNDEFINED_NAME_WARNING); + } + + public void testCheckUndefinedProperties3() { + args.add("--warning_level=VERBOSE"); + test("function f() {var x = {}; var y = x.bar;}", + TypeCheck.INEXISTENT_PROPERTY); + } + + public void testDuplicateParams() { + test("function f(a, a) {}", RhinoErrorReporter.DUPLICATE_PARAM); + assertTrue(lastCompiler.hasHaltingErrors()); + } + + public void testDefineFlag() { + args.add("--define=FOO"); + args.add("--define=\"BAR=5\""); + args.add("--D"); args.add("CCC"); + args.add("-D"); args.add("DDD"); + test("/** @define {boolean} */ var FOO = false;" + + "/** @define {number} */ var BAR = 3;" + + "/** @define {boolean} */ var CCC = false;" + + "/** @define {boolean} */ var DDD = false;", + "var FOO = !0, BAR = 5, CCC = !0, DDD = !0;"); + } + + public void testDefineFlag2() { + args.add("--define=FOO='x\"'"); + test("/** @define {string} */ var FOO = \"a\";", + "var FOO = \"x\\\"\";"); + } + + public void testDefineFlag3() { + args.add("--define=FOO=\"x'\""); + test("/** @define {string} */ var FOO = \"a\";", + "var FOO = \"x'\";"); + } + + public void testScriptStrictModeNoWarning() { + test("'use strict';", ""); + test("'no use strict';", CheckSideEffects.USELESS_CODE_ERROR); + } + + public void testFunctionStrictModeNoWarning() { + test("function f() {'use strict';}", "function f() {}"); + test("function f() {'no use strict';}", + CheckSideEffects.USELESS_CODE_ERROR); + } + + public void testQuietMode() { + args.add("--warning_level=DEFAULT"); + test("/** @const \n * @const */ var x;", + RhinoErrorReporter.PARSE_ERROR); + args.add("--warning_level=QUIET"); + testSame("/** @const \n * @const */ var x;"); + } + + public void testProcessClosurePrimitives() { + test("var goog = {}; goog.provide('goog.dom');", + "var goog = {dom:{}};"); + args.add("--process_closure_primitives=false"); + testSame("var goog = {}; goog.provide('goog.dom');"); + } + + public void testGetMsgWiring() throws Exception { + test("var goog = {}; goog.getMsg = function(x) { return x; };" + + "/** @desc A real foo. */ var MSG_FOO = goog.getMsg('foo');", + "var goog={getMsg:function(a){return a}}, " + + "MSG_FOO=goog.getMsg('foo');"); + args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); + test("var goog = {}; goog.getMsg = function(x) { return x; };" + + "/** @desc A real foo. */ var MSG_FOO = goog.getMsg('foo');" + + "window['foo'] = MSG_FOO;", + "window.foo = 'foo';"); + } + + public void testCssNameWiring() throws Exception { + test("var goog = {}; goog.getCssName = function() {};" + + "goog.setCssNameMapping = function() {};" + + "goog.setCssNameMapping({'goog': 'a', 'button': 'b'});" + + "var a = goog.getCssName('goog-button');" + + "var b = goog.getCssName('css-button');" + + "var c = goog.getCssName('goog-menu');" + + "var d = goog.getCssName('css-menu');", + "var goog = { getCssName: function() {}," + + " setCssNameMapping: function() {} }," + + " a = 'a-b'," + + " b = 'css-b'," + + " c = 'a-menu'," + + " d = 'css-menu';"); + } + + + ////////////////////////////////////////////////////////////////////////////// + // Integration tests + + public void testIssue70a() { + test("function foo({}) {}", RhinoErrorReporter.PARSE_ERROR); + } + + public void testIssue70b() { + test("function foo([]) {}", RhinoErrorReporter.PARSE_ERROR); + } + + public void testIssue81() { + args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); + useStringComparison = true; + test("eval('1'); var x = eval; x('2');", + "eval(\"1\");(0,eval)(\"2\");"); + } + + public void testIssue115() { + args.add("--compilation_level=SIMPLE_OPTIMIZATIONS"); + args.add("--jscomp_off=es5Strict"); + args.add("--warning_level=VERBOSE"); + test("function f() { " + + " var arguments = Array.prototype.slice.call(arguments, 0);" + + " return arguments[0]; " + + "}", + "function f() { " + + " arguments = Array.prototype.slice.call(arguments, 0);" + + " return arguments[0]; " + + "}"); + } + + public void testIssue297() { + args.add("--compilation_level=SIMPLE_OPTIMIZATIONS"); + test("function f(p) {" + + " var x;" + + " return ((x=p.id) && (x=parseInt(x.substr(1))) && x>0);" + + "}", + "function f(b) {" + + " var a;" + + " return ((a=b.id) && (a=parseInt(a.substr(1))) && 0 node1 [weight=1];\n" + + " node1 -> RETURN [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> RETURN [label=\"SYN_BLOCK\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> node1 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + "}\n\n", + new String(outReader.toByteArray())); + } + + public void testSyntheticExterns() { + externs = ImmutableList.of( + SourceFile.fromCode("externs", "myVar.property;")); + test("var theirVar = {}; var myVar = {}; var yourVar = {};", + VarCheck.UNDEFINED_EXTERN_VAR_ERROR); + + args.add("--jscomp_off=externsValidation"); + args.add("--warning_level=VERBOSE"); + test("var theirVar = {}; var myVar = {}; var yourVar = {};", + "var theirVar={},myVar={},yourVar={};"); + + args.add("--jscomp_off=externsValidation"); + args.add("--warning_level=VERBOSE"); + test("var theirVar = {}; var myVar = {}; var myVar = {};", + SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR); + } + + public void testGoogAssertStripping() { + args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); + test("goog.asserts.assert(false)", + ""); + args.add("--debug"); + test("goog.asserts.assert(false)", "goog.$asserts$.$assert$(!1)"); + } + + public void testMissingReturnCheckOnWithVerbose() { + args.add("--warning_level=VERBOSE"); + test("/** @return {number} */ function f() {f()} f();", + CheckMissingReturn.MISSING_RETURN_STATEMENT); + } + + public void testGenerateExports() { + args.add("--generate_exports=true"); + test("/** @export */ foo.prototype.x = function() {};", + "foo.prototype.x=function(){};"+ + "goog.exportSymbol(\"foo.prototype.x\",foo.prototype.x);"); + } + + public void testDepreciationWithVerbose() { + args.add("--warning_level=VERBOSE"); + test("/** @deprecated */ function f() {}; f()", + CheckAccessControls.DEPRECATED_NAME); + } + + public void testTwoParseErrors() { + // If parse errors are reported in different files, make + // sure all of them are reported. + Compiler compiler = compile(new String[] { + "var a b;", + "var b c;" + }); + assertEquals(2, compiler.getErrors().length); + } + + public void testES3ByDefault() { + test("var x = f.function", RhinoErrorReporter.PARSE_ERROR); + } + + public void testES5ChecksByDefault() { + testSame("var x = 3; delete x;"); + } + + public void testES5ChecksInVerbose() { + args.add("--warning_level=VERBOSE"); + test("function f(x) { delete x; }", StrictModeCheck.DELETE_VARIABLE); + } + + public void testES5() { + args.add("--language_in=ECMASCRIPT5"); + test("var x = f.function", "var x = f.function"); + test("var let", "var let"); + } + + public void testES5Strict() { + args.add("--language_in=ECMASCRIPT5_STRICT"); + test("var x = f.function", "'use strict';var x = f.function"); + test("var let", RhinoErrorReporter.PARSE_ERROR); + test("function f(x) { delete x; }", StrictModeCheck.DELETE_VARIABLE); + } + + public void testES5StrictUseStrict() { + args.add("--language_in=ECMASCRIPT5_STRICT"); + Compiler compiler = compile(new String[] {"var x = f.function"}); + String outputSource = compiler.toSource(); + assertEquals("'use strict'", outputSource.substring(0, 12)); + } + + public void testES5StrictUseStrictMultipleInputs() { + args.add("--language_in=ECMASCRIPT5_STRICT"); + Compiler compiler = compile(new String[] {"var x = f.function", + "var y = f.function", "var z = f.function"}); + String outputSource = compiler.toSource(); + assertEquals("'use strict'", outputSource.substring(0, 12)); + assertEquals(outputSource.substring(13).indexOf("'use strict'"), -1); + } + + public void testWithKeywordDefault() { + test("var x = {}; with (x) {}", ControlStructureCheck.USE_OF_WITH); + } + + public void testWithKeywordWithEs5ChecksOff() { + args.add("--jscomp_off=es5Strict"); + testSame("var x = {}; with (x) {}"); + } + + public void testNoSrCFilesWithManifest() throws IOException { + args.add("--use_only_custom_externs=true"); + args.add("--output_manifest=test.MF"); + CommandLineRunner runner = createCommandLineRunner(new String[0]); + String expectedMessage = ""; + try { + runner.doRun(); + } catch (FlagUsageException e) { + expectedMessage = e.getMessage(); + } + assertEquals(expectedMessage, "Bad --js flag. " + + "Manifest files cannot be generated when the input is from stdin."); + } + + public void testTransformAMD() { + args.add("--transform_amd_modules"); + test("define({test: 1})", "exports = {test: 1}"); + } + + public void testProcessCJS() { + useStringComparison = true; + args.add("--process_common_js_modules"); + args.add("--common_js_entry_module=foo/bar"); + setFilename(0, "foo/bar.js"); + String expected = "var module$foo$bar={test:1};"; + test("exports.test = 1", expected); + assertEquals(expected + "\n", outReader.toString()); + } + + public void testProcessCJSWithModuleOutput() { + useStringComparison = true; + args.add("--process_common_js_modules"); + args.add("--common_js_entry_module=foo/bar"); + args.add("--module=auto"); + setFilename(0, "foo/bar.js"); + test("exports.test = 1", + "var module$foo$bar={test:1};"); + // With modules=auto no direct output is created. + assertEquals("", outReader.toString()); + } + + public void testFormattingSingleQuote() { + testSame("var x = '';"); + assertEquals("var x=\"\";", lastCompiler.toSource()); + + args.add("--formatting=SINGLE_QUOTES"); + testSame("var x = '';"); + assertEquals("var x='';", lastCompiler.toSource()); + } + + public void testTransformAMDAndProcessCJS() { + useStringComparison = true; + args.add("--transform_amd_modules"); + args.add("--process_common_js_modules"); + args.add("--common_js_entry_module=foo/bar"); + setFilename(0, "foo/bar.js"); + test("define({foo: 1})", + "var module$foo$bar={},module$foo$bar={foo:1};"); + } + + public void testModuleJSON() { + useStringComparison = true; + args.add("--transform_amd_modules"); + args.add("--process_common_js_modules"); + args.add("--common_js_entry_module=foo/bar"); + args.add("--output_module_dependencies=test.json"); + setFilename(0, "foo/bar.js"); + test("define({foo: 1})", + "var module$foo$bar={},module$foo$bar={foo:1};"); + } + + public void testOutputSameAsInput() { + args.add("--js_output_file=" + getFilename(0)); + test("", AbstractCommandLineRunner.OUTPUT_SAME_AS_INPUT_ERROR); + } + + /* Helper functions */ + + private void testSame(String original) { + testSame(new String[] { original }); + } + + private void testSame(String[] original) { + test(original, original); + } + + private void test(String original, String compiled) { + test(new String[] { original }, new String[] { compiled }); + } + + /** + * Asserts that when compiling with the given compiler options, + * {@code original} is transformed into {@code compiled}. + */ + private void test(String[] original, String[] compiled) { + test(original, compiled, null); + } + + /** + * Asserts that when compiling with the given compiler options, + * {@code original} is transformed into {@code compiled}. + * If {@code warning} is non-null, we will also check if the given + * warning type was emitted. + */ + private void test(String[] original, String[] compiled, + DiagnosticType warning) { + Compiler compiler = compile(original); + + if (warning == null) { + assertEquals("Expected no warnings or errors\n" + + "Errors: \n" + Joiner.on("\n").join(compiler.getErrors()) + + "Warnings: \n" + Joiner.on("\n").join(compiler.getWarnings()), + 0, compiler.getErrors().length + compiler.getWarnings().length); + } else { + assertEquals(1, compiler.getWarnings().length); + assertEquals(warning, compiler.getWarnings()[0].getType()); + } + + Node root = compiler.getRoot().getLastChild(); + if (useStringComparison) { + assertEquals(Joiner.on("").join(compiled), compiler.toSource()); + } else { + Node expectedRoot = parse(compiled); + String explanation = expectedRoot.checkTreeEquals(root); + assertNull("\nExpected: " + compiler.toSource(expectedRoot) + + "\nResult: " + compiler.toSource(root) + + "\n" + explanation, explanation); + } + } + + /** + * Asserts that when compiling, there is an error or warning. + */ + private void test(String original, DiagnosticType warning) { + test(new String[] { original }, warning); + } + + private void test(String original, String expected, DiagnosticType warning) { + test(new String[] { original }, new String[] { expected }, warning); + } + + /** + * Asserts that when compiling, there is an error or warning. + */ + private void test(String[] original, DiagnosticType warning) { + Compiler compiler = compile(original); + assertEquals("Expected exactly one warning or error " + + "Errors: \n" + Joiner.on("\n").join(compiler.getErrors()) + + "Warnings: \n" + Joiner.on("\n").join(compiler.getWarnings()), + 1, compiler.getErrors().length + compiler.getWarnings().length); + + assertTrue(exitCodes.size() > 0); + int lastExitCode = exitCodes.get(exitCodes.size() - 1); + + if (compiler.getErrors().length > 0) { + assertEquals(1, compiler.getErrors().length); + assertEquals(warning, compiler.getErrors()[0].getType()); + assertEquals(1, lastExitCode); + } else { + assertEquals(1, compiler.getWarnings().length); + assertEquals(warning, compiler.getWarnings()[0].getType()); + assertEquals(0, lastExitCode); + } + } + + private CommandLineRunner createCommandLineRunner(String[] original) { + for (int i = 0; i < original.length; i++) { + args.add("--js"); + args.add("/path/to/input" + i + ".js"); + if (useModules == ModulePattern.CHAIN) { + args.add("--module"); + args.add("m" + i + ":1" + (i > 0 ? (":m" + (i - 1)) : "")); + } else if (useModules == ModulePattern.STAR) { + args.add("--module"); + args.add("m" + i + ":1" + (i > 0 ? ":m0" : "")); + } + } + + if (lastArg != null) { + args.add(lastArg); + } + + String[] argStrings = args.toArray(new String[] {}); + return new CommandLineRunner( + argStrings, + new PrintStream(outReader), + new PrintStream(errReader)); + } + + private Compiler compile(String[] original) { + CommandLineRunner runner = createCommandLineRunner(original); + assertTrue(new String(errReader.toByteArray()), runner.shouldRunCompiler()); + Supplier> inputsSupplier = null; + Supplier> modulesSupplier = null; + + if (useModules == ModulePattern.NONE) { + List inputs = Lists.newArrayList(); + for (int i = 0; i < original.length; i++) { + inputs.add(SourceFile.fromCode(getFilename(i), original[i])); + } + inputsSupplier = Suppliers.ofInstance(inputs); + } else if (useModules == ModulePattern.STAR) { + modulesSupplier = Suppliers.>ofInstance( + Lists.newArrayList( + CompilerTestCase.createModuleStar(original))); + } else if (useModules == ModulePattern.CHAIN) { + modulesSupplier = Suppliers.>ofInstance( + Lists.newArrayList( + CompilerTestCase.createModuleChain(original))); + } else { + throw new IllegalArgumentException("Unknown module type: " + useModules); + } + + runner.enableTestMode( + Suppliers.>ofInstance(externs), + inputsSupplier, + modulesSupplier, + new Function() { + @Override + public Boolean apply(Integer code) { + return exitCodes.add(code); + } + }); + runner.run(); + lastCompiler = runner.getCompiler(); + lastCommandLineRunner = runner; + return lastCompiler; + } + + private Node parse(String[] original) { + String[] argStrings = args.toArray(new String[] {}); + CommandLineRunner runner = new CommandLineRunner(argStrings); + Compiler compiler = runner.createCompiler(); + List inputs = Lists.newArrayList(); + for (int i = 0; i < original.length; i++) { + inputs.add(SourceFile.fromCode(getFilename(i), original[i])); + } + CompilerOptions options = new CompilerOptions(); + // ECMASCRIPT5 is the most forgiving. + options.setLanguageIn(LanguageMode.ECMASCRIPT5); + compiler.init(externs, inputs, options); + Node all = compiler.parseInputs(); + Preconditions.checkState(compiler.getErrorCount() == 0); + Preconditions.checkNotNull(all); + Node n = all.getLastChild(); + return n; + } + + private void setFilename(int i, String filename) { + this.filenames.put(i, filename); + } + + private String getFilename(int i) { + if (filenames.isEmpty()) { + return "input" + i; + } + return filenames.get(i); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CompilerOptionsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CompilerOptionsTest.java new file mode 100644 index 0000000..898234b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CompilerOptionsTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import junit.framework.TestCase; + +import java.util.Map; + +/** + * Tests for {@link CompilerOptions}. + * @author nicksantos@google.com (Nick Santos) + */ +public class CompilerOptionsTest extends TestCase { + + public void testDefines() throws Exception { + CompilerOptions options = new CompilerOptions(); + options.setDefineToBooleanLiteral("trueVar", true); + options.setDefineToBooleanLiteral("falseVar", false); + options.setDefineToNumberLiteral("threeVar", 3); + options.setDefineToStringLiteral("strVar", "str"); + + Map actual = options.getDefineReplacements(); + assertEquivalent(new Node(Token.TRUE), actual.get("trueVar")); + assertEquivalent(new Node(Token.FALSE), actual.get("falseVar")); + assertEquivalent(Node.newNumber(3), actual.get("threeVar")); + assertEquivalent(Node.newString("str"), actual.get("strVar")); + } + + public void assertEquivalent(Node a, Node b) { + assertTrue(a.isEquivalentTo(b)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CompilerTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CompilerTest.java new file mode 100644 index 0000000..86efa50 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CompilerTest.java @@ -0,0 +1,163 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.javascript.rhino.Node; + +import junit.framework.TestCase; + +import java.util.List; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class CompilerTest extends TestCase { + + public void testCodeBuilderColumnAfterResetDummy() { + Compiler compiler = new Compiler(); + Node n = compiler.parseTestCode(""); + Compiler.CodeBuilder cb = new Compiler.CodeBuilder(); + } + + // Verify the line and column information is maintained after a reset + public void testCodeBuilderColumnAfterReset() { + Compiler.CodeBuilder cb = new Compiler.CodeBuilder(); + String js = "foo();\ngoo();"; + cb.append(js); + assertEquals(js, cb.toString()); + assertEquals(1, cb.getLineIndex()); + assertEquals(6, cb.getColumnIndex()); + + cb.reset(); + + assertTrue(cb.toString().isEmpty()); + assertEquals(1, cb.getLineIndex()); + assertEquals(6, cb.getColumnIndex()); + } + + public void testCodeBuilderAppend() { + Compiler.CodeBuilder cb = new Compiler.CodeBuilder(); + cb.append("foo();"); + assertEquals(0, cb.getLineIndex()); + assertEquals(6, cb.getColumnIndex()); + + cb.append("goo();"); + + assertEquals(0, cb.getLineIndex()); + assertEquals(12, cb.getColumnIndex()); + + // newline reset the column index + cb.append("blah();\ngoo();"); + + assertEquals(1, cb.getLineIndex()); + assertEquals(6, cb.getColumnIndex()); + } + + public void testCyclicalDependencyInInputs() { + List inputs = Lists.newArrayList( + SourceFile.fromCode( + "gin", "goog.provide('gin'); goog.require('tonic'); var gin = {};"), + SourceFile.fromCode("tonic", + "goog.provide('tonic'); goog.require('gin'); var tonic = {};"), + SourceFile.fromCode( + "mix", "goog.require('gin'); goog.require('tonic');")); + CompilerOptions options = new CompilerOptions(); + options.ideMode = true; + options.setManageClosureDependencies(true); + Compiler compiler = new Compiler(); + compiler.init(ImmutableList.of(), inputs, options); + compiler.parseInputs(); + assertEquals(compiler.externAndJsRoot, compiler.jsRoot.getParent()); + assertEquals(compiler.externAndJsRoot, compiler.externsRoot.getParent()); + assertNotNull(compiler.externAndJsRoot); + + Node jsRoot = compiler.jsRoot; + assertEquals(3, jsRoot.getChildCount()); + } + + public void testLocalUndefined() throws Exception { + // Some JavaScript libraries like to create a local instance of "undefined", + // to ensure that other libraries don't try to overwrite it. + // + // Most of the time, this is OK, because normalization will rename + // that variable to undefined$$1. But this won't happen if they don't + // include the default externs. + // + // This test is just to make sure that the compiler doesn't crash. + CompilerOptions options = new CompilerOptions(); + CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel( + options); + Compiler compiler = new Compiler(); + SourceFile externs = SourceFile.fromCode("externs.js", ""); + SourceFile input = SourceFile.fromCode("input.js", + "(function (undefined) { alert(undefined); })();"); + compiler.compile(externs, input, options); + } + + public void testCommonJSProvidesAndRequire() throws Exception { + List inputs = Lists.newArrayList( + SourceFile.fromCode("gin.js", "require('tonic')"), + SourceFile.fromCode("tonic.js", ""), + SourceFile.fromCode("mix.js", "require('gin'); require('tonic');")); + List entryPoints = Lists.newArrayList("module$mix"); + + Compiler compiler = initCompilerForCommonJS(inputs, entryPoints); + JSModuleGraph graph = compiler.getModuleGraph(); + assertEquals(4, graph.getModuleCount()); + List result = graph.manageDependencies(entryPoints, + compiler.getInputsForTesting()); + assertEquals("[root]", result.get(0).getName()); + assertEquals("[module$tonic]", result.get(1).getName()); + assertEquals("[module$gin]", result.get(2).getName()); + assertEquals("tonic.js", result.get(3).getName()); + assertEquals("gin.js", result.get(4).getName()); + assertEquals("mix.js", result.get(5).getName()); + } + + public void testCommonJSMissingRequire() throws Exception { + List inputs = Lists.newArrayList( + SourceFile.fromCode("gin.js", "require('missing')")); + Compiler compiler = initCompilerForCommonJS( + inputs, ImmutableList.of("module$gin")); + compiler.processAMDAndCommonJSModules(); + + assertEquals(1, compiler.getErrorManager().getErrorCount()); + String error = compiler.getErrorManager().getErrors()[0].toString(); + assertTrue( + "Unexpected error: " + error, + error.contains( + "required entry point \"module$missing\" never provided")); + } + + private Compiler initCompilerForCommonJS( + List inputs, List entryPoints) + throws Exception { + CompilerOptions options = new CompilerOptions(); + options.ideMode = true; + options.setManageClosureDependencies(entryPoints); + options.closurePass = true; + options.processCommonJSModules = true; + Compiler compiler = new Compiler(); + compiler.init(Lists.newArrayList(), inputs, options); + compiler.parseInputs(); + return compiler; + } + + +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CompilerTestCase.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CompilerTestCase.java new file mode 100644 index 0000000..bced304 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CompilerTestCase.java @@ -0,0 +1,1094 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.CodeChangeHandler.RecentChange; +import com.google.javascript.jscomp.CompilerOptions.LanguageMode; +import com.google.javascript.jscomp.type.ReverseAbstractInterpreter; +import com.google.javascript.jscomp.type.SemanticReverseAbstractInterpreter; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.testing.BaseJSTypeTestCase; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.util.List; + +/** + *

      Base class for testing JS compiler classes that change + * the node tree of a compiled JS input.

      + * + *

      Pulls in shared functionality from different test cases. Also supports + * node tree comparison for input and output (instead of string comparison), + * which makes it easier to write tests b/c you don't have to get the syntax + * exactly correct to the spacing.

      + * + */ +public abstract class CompilerTestCase extends TestCase { + + /** Externs for the test */ + private final List externsInputs; + + /** Whether to compare input and output as trees instead of strings */ + private final boolean compareAsTree; + + /** Whether to parse type info from JSDoc comments */ + protected boolean parseTypeInfo; + + /** Whether we check warnings without source information. */ + private boolean allowSourcelessWarnings = false; + + /** True iff closure pass runs before pass being tested. */ + private boolean closurePassEnabled = false; + + /** True iff type checking pass runs before pass being tested. */ + private boolean typeCheckEnabled = false; + + /** Error level reported by type checker. */ + private CheckLevel typeCheckLevel; + + /** Whether the Normalize pass runs before pass being tested. */ + private boolean normalizeEnabled = false; + + /** Whether the expected JS strings should be normalized. */ + private boolean normalizeExpected = false; + + /** Whether to check that all line number information is preserved. */ + private boolean checkLineNumbers = true; + + /** + * An expected symbol table error. Only useful for testing the + * symbol table error-handling. + */ + private DiagnosticType expectedSymbolTableError = null; + + /** + * Whether the MarkNoSideEffectsCalls pass runs before the pass being tested + */ + private boolean markNoSideEffects = false; + + /** The most recently used Compiler instance. */ + private Compiler lastCompiler; + + /** + * Whether to acceptES5 source. + */ + private boolean acceptES5 = true; + + /** + * Whether externs changes should be allowed for this pass. + */ + private boolean allowExternsChanges = false; + + /** + * Whether the AST should be validated. + */ + private boolean astValidationEnabled = true; + + private String filename = "testcode"; + + /** + * Constructs a test. + * + * @param externs Externs JS as a string + * @param compareAsTree True to compare output & expected as a node tree. + * 99% of the time you want to compare as a tree. There are a few + * special cases where you don't, like if you want to test the code + * printing of "unnatural" syntax trees. For example, + * + *
      +   * IF
      +   *   IF
      +   *     STATEMENT
      +   * ELSE
      +   *   STATEMENT
      +   * 
      + */ + protected CompilerTestCase(String externs, boolean compareAsTree) { + this.externsInputs = ImmutableList.of( + SourceFile.fromCode("externs", externs)); + this.compareAsTree = compareAsTree; + this.parseTypeInfo = false; + } + + /** + * Constructs a test. Uses AST comparison. + * @param externs Externs JS as a string + */ + protected CompilerTestCase(String externs) { + this(externs, true); + } + + /** + * Constructs a test. Uses AST comparison and no externs. + */ + protected CompilerTestCase() { + this("", true); + } + + /** + * Gets the compiler pass instance to use for a test. + * + * @param compiler The compiler + * @return The pass to test + */ + protected abstract CompilerPass getProcessor(Compiler compiler); + + + /** + * Gets the compiler options to use for this test. Use getProcessor to + * determine what passes should be run. + */ + protected CompilerOptions getOptions() { + return getOptions(new CompilerOptions()); + } + + /** + * Gets the compiler options to use for this test. Use getProcessor to + * determine what passes should be run. + */ + protected CompilerOptions getOptions(CompilerOptions options) { + if (this.acceptES5) { + options.setLanguageIn(LanguageMode.ECMASCRIPT5); + } + + // This doesn't affect whether checkSymbols is run--it just affects + // whether variable warnings are filtered. + options.checkSymbols = true; + + options.setWarningLevel( + DiagnosticGroups.MISSING_PROPERTIES, CheckLevel.WARNING); + options.setWarningLevel( + DiagnosticGroups.CAST, CheckLevel.WARNING); + options.setCodingConvention(getCodingConvention()); + return options; + } + + protected CodingConvention getCodingConvention() { + return new GoogleCodingConvention(); + } + + public void setFilename(String filename) { + this.filename = filename; + } + + /** + * Returns the number of times the pass should be run before results are + * verified. + */ + protected int getNumRepetitions() { + // Since most compiler passes should be idempotent, we run each pass twice + // by default. + return 2; + } + + /** Expect warnings without source information. */ + void allowSourcelessWarnings() { + allowSourcelessWarnings = true; + } + + /** The most recently used JSComp instance. */ + Compiler getLastCompiler() { + return lastCompiler; + } + + /** + * Whether to allow ECMASCRIPT5 source parsing. + */ + protected void enableEcmaScript5(boolean acceptES5) { + this.acceptES5 = acceptES5; + } + + /** + * Whether to allow externs changes. + */ + protected void allowExternsChanges(boolean allowExternsChanges) { + this.allowExternsChanges = allowExternsChanges; + } + + /** + * Perform type checking before running the test pass. This will check + * for type errors and annotate nodes with type information. + * + * @param level the level of severity to report for type errors + * + * @see TypeCheck + */ + public void enableTypeCheck(CheckLevel level) { + typeCheckEnabled = true; + typeCheckLevel = level; + } + + /** + * Check to make sure that line numbers were preserved. + */ + public void enableLineNumberCheck(boolean newVal) { + checkLineNumbers = newVal; + } + + /** + * Do not run type checking before running the test pass. + * + * @see TypeCheck + */ + void disableTypeCheck() { + typeCheckEnabled = false; + } + + /** + * Process closure library primitives. + */ + // TODO(nicksantos): Fix other passes to use this when appropriate. + void enableClosurePass() { + closurePassEnabled = true; + } + + /** + * Perform AST normalization before running the test pass, and anti-normalize + * after running it. + * + * @see Normalize + */ + protected void enableNormalize() { + enableNormalize(true); + } + + /** + * Perform AST normalization before running the test pass, and anti-normalize + * after running it. + * + * @param normalizeExpected Whether to perform normalization on the + * expected JS result. + * @see Normalize + */ + protected void enableNormalize(boolean normalizeExpected) { + normalizeEnabled = true; + this.normalizeExpected = normalizeExpected; + } + + /** + * Don't perform AST normalization before running the test pass. + * @see Normalize + */ + protected void disableNormalize() { + normalizeEnabled = false; + } + + /** + * Run the MarkSideEffectCalls pass before running the test pass. + * + * @see MarkNoSideEffectCalls + */ + void enableMarkNoSideEffects() { + markNoSideEffects = true; + } + + /** + * Whether to allow Validate the AST after each run of the pass. + */ + protected void enableAstValidation(boolean validate) { + astValidationEnabled = validate; + } + + /** Returns a newly created TypeCheck. */ + private static TypeCheck createTypeCheck(Compiler compiler, + CheckLevel level) { + ReverseAbstractInterpreter rai = + new SemanticReverseAbstractInterpreter(compiler.getCodingConvention(), + compiler.getTypeRegistry()); + + return new TypeCheck(compiler, rai, compiler.getTypeRegistry(), + level, CheckLevel.OFF); + } + + /** + * Verifies that the compiler pass's JS output matches the expected output. + * + * @param js Input + * @param expected Expected JS output + */ + public void test(String js, String expected) { + test(js, expected, (DiagnosticType) null); + } + + /** + * Verifies that the compiler pass's JS output matches the expected output, + * or that an expected error is encountered. + * + * @param js Input + * @param expected Expected output, or null if an error is expected + * @param error Expected error, or null if no error is expected + */ + public void test(String js, String expected, DiagnosticType error) { + test(js, expected, error, null); + } + + + /** + * Verifies that the compiler pass's JS output matches the expected output, + * or that an expected error is encountered. + * + * @param js Input + * @param expected Expected output, or null if an error is expected + * @param error Expected error, or null if no error is expected + * @param warning Expected warning, or null if no warning is expected + * @param description The content of the error expected + */ + public void test(String js, String expected, DiagnosticType error, + DiagnosticType warning, String description) { + test(externsInputs, js, expected, error, warning, description); + } + + /** + * Verifies that the compiler pass's JS output matches the expected output + * and (optionally) that an expected warning is issued. Or, if an error is + * expected, this method just verifies that the error is encountered. + * + * @param js Input + * @param expected Expected output, or null if an error is expected + * @param error Expected error, or null if no error is expected + * @param warning Expected warning, or null if no warning is expected + */ + public void test(String js, String expected, + DiagnosticType error, DiagnosticType warning) { + test(externsInputs, js, expected, error, warning, null); + } + + /** + * Verifies that the compiler pass's JS output matches the expected output + * and (optionally) that an expected warning is issued. Or, if an error is + * expected, this method just verifies that the error is encountered. + * + * @param externs Externs input + * @param js Input + * @param expected Expected output, or null if an error is expected + * @param error Expected error, or null if no error is expected + * @param warning Expected warning, or null if no warning is expected + */ + public void test(String externs, String js, String expected, + DiagnosticType error, DiagnosticType warning) { + test(externs, js, expected, error, warning, null); + } + + /** + * Verifies that the compiler pass's JS output matches the expected output + * and (optionally) that an expected warning is issued. Or, if an error is + * expected, this method just verifies that the error is encountered. + * + * @param externs Externs input + * @param js Input + * @param expected Expected output, or null if an error is expected + * @param error Expected error, or null if no error is expected + * @param warning Expected warning, or null if no warning is expected + * @param description The description of the expected warning, + * or null if no warning is expected or if the warning's description + * should not be examined + */ + public void test(String externs, String js, String expected, + DiagnosticType error, DiagnosticType warning, + String description) { + List externsInputs = ImmutableList.of( + SourceFile.fromCode("externs", externs)); + test(externsInputs, js, expected, error, warning, description); + } + + /** + * Verifies that the compiler pass's JS output matches the expected output + * and (optionally) that an expected warning is issued. Or, if an error is + * expected, this method just verifies that the error is encountered. + * + * @param externs Externs inputs + * @param js Input + * @param expected Expected output, or null if an error is expected + * @param error Expected error, or null if no error is expected + * @param warning Expected warning, or null if no warning is expected + * @param description The description of the expected warning, + * or null if no warning is expected or if the warning's description + * should not be examined + */ + public void test(List externs, String js, String expected, + DiagnosticType error, + DiagnosticType warning, String description) { + Compiler compiler = createCompiler(); + lastCompiler = compiler; + + CompilerOptions options = getOptions(); + + if (this.acceptES5) { + options.setLanguageIn(LanguageMode.ECMASCRIPT5); + } + // Note that in this context, turning on the checkTypes option won't + // actually cause the type check to run. + options.checkTypes = parseTypeInfo; + compiler.init(externs, ImmutableList.of( + SourceFile.fromCode(filename, js)), options); + + BaseJSTypeTestCase.addNativeProperties(compiler.getTypeRegistry()); + + test(compiler, maybeCreateArray(expected), error, warning, description); + } + + private String[] maybeCreateArray(String expected) { + if (expected != null) { + return new String[] { expected }; + } + return null; + } + + /** + * Verifies that the compiler pass's JS output matches the expected output. + * + * @param js Inputs + * @param expected Expected JS output + */ + public void test(String[] js, String[] expected) { + test(js, expected, null); + } + + /** + * Verifies that the compiler pass's JS output matches the expected output, + * or that an expected error is encountered. + * + * @param js Inputs + * @param expected Expected JS output + * @param error Expected error, or null if no error is expected + */ + public void test(String[] js, String[] expected, DiagnosticType error) { + test(js, expected, error, null); + } + + /** + * Verifies that the compiler pass's JS output matches the expected output + * and (optionally) that an expected warning is issued. Or, if an error is + * expected, this method just verifies that the error is encountered. + * + * @param js Inputs + * @param expected Expected JS output + * @param error Expected error, or null if no error is expected + * @param warning Expected warning, or null if no warning is expected + */ + public void test(String[] js, String[] expected, DiagnosticType error, + DiagnosticType warning) { + test(js, expected, error, warning, null); + } + + /** + * Verifies that the compiler pass's JS output matches the expected output + * and (optionally) that an expected warning is issued. Or, if an error is + * expected, this method just verifies that the error is encountered. + * + * @param js Inputs + * @param expected Expected JS output + * @param error Expected error, or null if no error is expected + * @param warning Expected warning, or null if no warning is expected + * @param description The description of the expected warning, + * or null if no warning is expected or if the warning's description + * should not be examined + */ + public void test(String[] js, String[] expected, DiagnosticType error, + DiagnosticType warning, String description) { + Compiler compiler = createCompiler(); + lastCompiler = compiler; + + List inputs = Lists.newArrayList(); + for (int i = 0; i < js.length; i++) { + inputs.add(SourceFile.fromCode("input" + i, js[i])); + } + compiler.init(externsInputs, inputs, getOptions()); + test(compiler, expected, error, warning, description); + } + + /** + * Verifies that the compiler pass's JS output matches the expected output. + * + * @param modules Module inputs + * @param expected Expected JS outputs (one per module) + */ + public void test(JSModule[] modules, String[] expected) { + test(modules, expected, null); + } + + /** + * Verifies that the compiler pass's JS output matches the expected output, + * or that an expected error is encountered. + * + * @param modules Module inputs + * @param expected Expected JS outputs (one per module) + * @param error Expected error, or null if no error is expected + */ + public void test(JSModule[] modules, String[] expected, + DiagnosticType error) { + test(modules, expected, error, null); + } + + /** + * Verifies that the compiler pass's JS output matches the expected output + * and (optionally) that an expected warning is issued. Or, if an error is + * expected, this method just verifies that the error is encountered. + * + * @param modules Module inputs + * @param expected Expected JS outputs (one per module) + * @param error Expected error, or null if no error is expected + * @param warning Expected warning, or null if no warning is expected + */ + public void test(JSModule[] modules, String[] expected, + DiagnosticType error, DiagnosticType warning) { + Compiler compiler = createCompiler(); + lastCompiler = compiler; + + compiler.initModules( + externsInputs, Lists.newArrayList(modules), getOptions()); + test(compiler, expected, error, warning); + } + + /** + * Verifies that the compiler pass's JS output is the same as its input. + * + * @param js Input and output + */ + public void testSame(String js) { + test(js, js); + } + + /** + * Verifies that the compiler pass's JS output is the same as its input + * and (optionally) that an expected warning is issued. + * + * @param js Input and output + * @param warning Expected warning, or null if no warning is expected + */ + public void testSame(String js, DiagnosticType warning) { + test(js, js, null, warning); + } + + /** + * Verifies that the compiler pass's JS output is the same as its input + * and (optionally) that an expected warning is issued. + * + * @param js Input and output + * @param diag Expected error or warning, or null if none is expected + * @param error true if diag is an error, false if it is a warning + */ + public void testSame(String js, DiagnosticType diag, boolean error) { + if (error) { + test(js, js, diag); + } else { + test(js, js, null, diag); + } + } + + /** + * Verifies that the compiler pass's JS output is the same as its input + * and (optionally) that an expected warning is issued. + * + * @param externs Externs input + * @param js Input and output + * @param warning Expected warning, or null if no warning is expected + */ + public void testSame(String externs, String js, DiagnosticType warning) { + testSame(externs, js, warning, null); + } + + /** + * Verifies that the compiler pass's JS output is the same as its input + * and (optionally) that an expected warning is issued. + * + * @param externs Externs input + * @param js Input and output + * @param diag Expected error or warning, or null if none is expected + * @param error true if diag is an error, false if it is a warning + */ + public void testSame( + String externs, String js, DiagnosticType diag, boolean error) { + if (error) { + test(externs, js, js, diag, null); + } else { + test(externs, js, js, null, diag); + } + } + + /** + * Verifies that the compiler pass's JS output is the same as its input + * and (optionally) that an expected warning and description is issued. + * + * @param externs Externs input + * @param js Input and output + * @param warning Expected warning, or null if no warning is expected + * @param description The description of the expected warning, + * or null if no warning is expected or if the warning's description + * should not be examined + */ + public void testSame(String externs, String js, DiagnosticType warning, + String description) { + List externsInputs = ImmutableList.of( + SourceFile.fromCode("externs", externs)); + test(externsInputs, js, js, null, warning, description); + } + + /** + * Verifies that the compiler pass's JS output is the same as its input. + * + * @param js Inputs and outputs + */ + public void testSame(String[] js) { + test(js, js); + } + + /** + * Verifies that the compiler pass's JS output is the same as its input, + * and emits the given error. + * + * @param js Inputs and outputs + * @param error Expected error, or null if no error is expected + */ + public void testSame(String[] js, DiagnosticType error) { + test(js, js, error); + } + + /** + * Verifies that the compiler pass's JS output is the same as its input, + * and emits the given error and warning. + * + * @param js Inputs and outputs + * @param error Expected error, or null if no error is expected + * @param warning Expected warning, or null if no warning is expected + */ + public void testSame( + String[] js, DiagnosticType error, DiagnosticType warning) { + test(js, js, error, warning); + } + + /** + * Verifies that the compiler pass's JS output is the same as the input. + * + * @param modules Module inputs + */ + public void testSame(JSModule[] modules) { + testSame(modules, null); + } + + /** + * Verifies that the compiler pass's JS output is the same as the input. + * + * @param modules Module inputs + * @param warning A warning, or null for no expected warning. + */ + public void testSame(JSModule[] modules, DiagnosticType warning) { + try { + String[] expected = new String[modules.length]; + for (int i = 0; i < modules.length; i++) { + expected[i] = ""; + for (CompilerInput input : modules[i].getInputs()) { + expected[i] += input.getSourceFile().getCode(); + } + } + test(modules, expected, null, warning); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Verifies that the compiler pass's JS output matches the expected output + * and (optionally) that an expected warning is issued. Or, if an error is + * expected, this method just verifies that the error is encountered. + * + * @param compiler A compiler that has been initialized via + * {@link Compiler#init} + * @param expected Expected output, or null if an error is expected + * @param error Expected error, or null if no error is expected + * @param warning Expected warning, or null if no warning is expected + */ + protected void test(Compiler compiler, String[] expected, + DiagnosticType error, DiagnosticType warning) { + test(compiler, expected, error, warning, null); + } + + + /** + * Verifies that the compiler pass's JS output matches the expected output + * and (optionally) that an expected warning is issued. Or, if an error is + * expected, this method just verifies that the error is encountered. + * + * @param compiler A compiler that has been initialized via + * {@link Compiler#init} + * @param expected Expected output, or null if an error is expected + * @param error Expected error, or null if no error is expected + * @param warning Expected warning, or null if no warning is expected + * @param description The description of the expected warning, + * or null if no warning is expected or if the warning's description + * should not be examined + */ + private void test(Compiler compiler, String[] expected, + DiagnosticType error, DiagnosticType warning, + String description) { + RecentChange recentChange = new RecentChange(); + compiler.addChangeHandler(recentChange); + + Node root = compiler.parseInputs(); + assertTrue("Unexpected parse error(s): " + + Joiner.on("\n").join(compiler.getErrors()), root != null); + + if (astValidationEnabled) { + (new AstValidator()).validateRoot(root); + } + Node externsRoot = root.getFirstChild(); + Node mainRoot = root.getLastChild(); + + // Save the tree for later comparison. + Node rootClone = root.cloneTree(); + Node externsRootClone = rootClone.getFirstChild(); + Node mainRootClone = rootClone.getLastChild(); + + int numRepetitions = getNumRepetitions(); + ErrorManager[] errorManagers = new ErrorManager[numRepetitions]; + int aggregateWarningCount = 0; + List aggregateWarnings = Lists.newArrayList(); + boolean hasCodeChanged = false; + + assertFalse("Code should not change before processing", + recentChange.hasCodeChanged()); + + for (int i = 0; i < numRepetitions; ++i) { + if (compiler.getErrorCount() == 0) { + errorManagers[i] = new BlackHoleErrorManager(compiler); + + // Only run process closure primitives once, if asked. + if (closurePassEnabled && i == 0) { + recentChange.reset(); + new ProcessClosurePrimitives(compiler, null, CheckLevel.ERROR) + .process(null, mainRoot); + hasCodeChanged = hasCodeChanged || recentChange.hasCodeChanged(); + } + + // Only run the type checking pass once, if asked. + // Running it twice can cause unpredictable behavior because duplicate + // objects for the same type are created, and the type system + // uses reference equality to compare many types. + if (typeCheckEnabled && i == 0) { + TypeCheck check = createTypeCheck(compiler, typeCheckLevel); + check.processForTesting(externsRoot, mainRoot); + } + + // Only run the normalize pass once, if asked. + if (normalizeEnabled && i == 0) { + normalizeActualCode(compiler, externsRoot, mainRoot); + } + + if (markNoSideEffects && i == 0) { + MarkNoSideEffectCalls mark = new MarkNoSideEffectCalls(compiler); + mark.process(externsRoot, mainRoot); + } + + recentChange.reset(); + + getProcessor(compiler).process(externsRoot, mainRoot); + if (astValidationEnabled) { + (new AstValidator()).validateRoot(root); + } + if (checkLineNumbers) { + (new LineNumberCheck(compiler)).process(externsRoot, mainRoot); + } + + hasCodeChanged = hasCodeChanged || recentChange.hasCodeChanged(); + aggregateWarningCount += errorManagers[i].getWarningCount(); + aggregateWarnings.addAll(Lists.newArrayList(compiler.getWarnings())); + + if (normalizeEnabled) { + boolean verifyDeclaredConstants = true; + new Normalize.VerifyConstants(compiler, verifyDeclaredConstants) + .process(externsRoot, mainRoot); + } + } + } + + if (error == null) { + assertEquals( + "Unexpected error(s): " + Joiner.on("\n").join(compiler.getErrors()), + 0, compiler.getErrorCount()); + + // Verify the symbol table. + ErrorManager symbolTableErrorManager = + new BlackHoleErrorManager(compiler); + Node expectedRoot = null; + if (expected != null) { + expectedRoot = parseExpectedJs(expected); + expectedRoot.detachFromParent(); + } + + JSError[] stErrors = symbolTableErrorManager.getErrors(); + if (expectedSymbolTableError != null) { + assertEquals("There should be one error.", 1, stErrors.length); + assertEquals(expectedSymbolTableError, stErrors[0].getType()); + } else { + assertEquals("Unexpected symbol table error(s): " + + Joiner.on("\n").join(stErrors), + 0, stErrors.length); + } + + if (warning == null) { + assertEquals( + "Unexpected warning(s): " + Joiner.on("\n").join(aggregateWarnings), + 0, aggregateWarningCount); + } else { + assertEquals("There should be one warning, repeated " + numRepetitions + + " time(s).", numRepetitions, aggregateWarningCount); + for (int i = 0; i < numRepetitions; ++i) { + JSError[] warnings = errorManagers[i].getWarnings(); + JSError actual = warnings[0]; + assertEquals(warning, actual.getType()); + + // Make sure that source information is always provided. + if (!allowSourcelessWarnings) { + assertTrue("Missing source file name in warning", + actual.sourceName != null && !actual.sourceName.isEmpty()); + assertTrue("Missing line number in warning", + -1 != actual.lineNumber); + assertTrue("Missing char number in warning", + -1 != actual.getCharno()); + } + + if (description != null) { + assertEquals(description, actual.description); + } + } + } + + if (normalizeEnabled) { + normalizeActualCode(compiler, externsRootClone, mainRootClone); + } + + boolean codeChange = !mainRootClone.isEquivalentTo(mainRoot); + boolean externsChange = !externsRootClone.isEquivalentTo(externsRoot); + + // Generally, externs should not be change by the compiler passes. + if (externsChange && !allowExternsChanges) { + String explanation = externsRootClone.checkTreeEquals(externsRoot); + fail("Unexpected changes to externs" + + "\nExpected: " + compiler.toSource(externsRootClone) + + "\nResult: " + compiler.toSource(externsRoot) + + "\n" + explanation); + } + + if (!codeChange && !externsChange) { + assertFalse( + "compiler.reportCodeChange() was called " + + "even though nothing changed", + hasCodeChanged); + } else { + assertTrue("compiler.reportCodeChange() should have been called", + hasCodeChanged); + } + + if (expected != null) { + if (compareAsTree) { + String explanation = expectedRoot.checkTreeEquals(mainRoot); + assertNull("\nExpected: " + compiler.toSource(expectedRoot) + + "\nResult: " + compiler.toSource(mainRoot) + + "\n" + explanation, explanation); + } else if (expected != null) { + assertEquals( + Joiner.on("").join(expected), compiler.toSource(mainRoot)); + } + } + + // Verify normalization is not invalidated. + Node normalizeCheckRootClone = root.cloneTree(); + Node normalizeCheckExternsRootClone = root.getFirstChild(); + Node normalizeCheckMainRootClone = root.getLastChild(); + new PrepareAst(compiler).process( + normalizeCheckExternsRootClone, normalizeCheckMainRootClone); + String explanation = + normalizeCheckMainRootClone.checkTreeEquals(mainRoot); + assertNull("Node structure normalization invalidated.\nExpected: " + + compiler.toSource(normalizeCheckMainRootClone) + + "\nResult: " + compiler.toSource(mainRoot) + + "\n" + explanation, explanation); + + // TODO(johnlenz): enable this for most test cases. + // Currently, this invalidates test for while-loops, for-loop + // initializers, and other naming. However, a set of code + // (FoldConstants, etc) runs before the Normalize pass, so this can't be + // force on everywhere. + if (normalizeEnabled) { + new Normalize(compiler, true).process( + normalizeCheckExternsRootClone, normalizeCheckMainRootClone); + explanation = normalizeCheckMainRootClone.checkTreeEquals(mainRoot); + assertNull("Normalization invalidated.\nExpected: " + + compiler.toSource(normalizeCheckMainRootClone) + + "\nResult: " + compiler.toSource(mainRoot) + + "\n" + explanation, explanation); + } + } else { + String errors = ""; + for (JSError actualError : compiler.getErrors()) { + errors += actualError.description + "\n"; + } + assertEquals("There should be one error. " + errors, + 1, compiler.getErrorCount()); + assertEquals(errors, error, compiler.getErrors()[0].getType()); + + if (warning != null) { + String warnings = ""; + for (JSError actualError : compiler.getWarnings()) { + warnings += actualError.description + "\n"; + } + assertEquals("There should be one warning. " + warnings, + 1, compiler.getWarningCount()); + assertEquals(warnings, warning, compiler.getWarnings()[0].getType()); + } + } + } + + private void normalizeActualCode( + Compiler compiler, Node externsRoot, Node mainRoot) { + Normalize normalize = new Normalize(compiler, false); + normalize.process(externsRoot, mainRoot); + } + + /** + * Parses expected JS inputs and returns the root of the parse tree. + */ + protected Node parseExpectedJs(String[] expected) { + Compiler compiler = createCompiler(); + List inputs = Lists.newArrayList(); + for (int i = 0; i < expected.length; i++) { + inputs.add(SourceFile.fromCode("expected" + i, expected[i])); + } + compiler.init(externsInputs, inputs, getOptions()); + Node root = compiler.parseInputs(); + assertTrue("Unexpected parse error(s): " + + Joiner.on("\n").join(compiler.getErrors()), root != null); + Node externsRoot = root.getFirstChild(); + Node mainRoot = externsRoot.getNext(); + // Only run the normalize pass, if asked. + if (normalizeEnabled && normalizeExpected && !compiler.hasErrors()) { + Normalize normalize = new Normalize(compiler, false); + normalize.process(externsRoot, mainRoot); + } + return mainRoot; + } + + protected Node parseExpectedJs(String expected) { + return parseExpectedJs(new String[] {expected}); + } + + /** + * Generates a list of modules from a list of inputs, such that each module + * depends on the module before it. + */ + static JSModule[] createModuleChain(String... inputs) { + JSModule[] modules = createModules(inputs); + for (int i = 1; i < modules.length; i++) { + modules[i].addDependency(modules[i - 1]); + } + return modules; + } + + /** + * Generates a list of modules from a list of inputs, such that each module + * depends on the first module. + */ + static JSModule[] createModuleStar(String... inputs) { + JSModule[] modules = createModules(inputs); + for (int i = 1; i < modules.length; i++) { + modules[i].addDependency(modules[0]); + } + return modules; + } + + /** + * Generates a list of modules from a list of inputs, such that modules + * form a bush formation. In a bush formation, module 2 depends + * on module 1, and all other modules depend on module 2. + */ + static JSModule[] createModuleBush(String ... inputs) { + Preconditions.checkState(inputs.length > 2); + JSModule[] modules = createModules(inputs); + for (int i = 1; i < modules.length; i++) { + modules[i].addDependency(modules[i == 1 ? 0 : 1]); + } + return modules; + } + + /** + * Generates a list of modules from a list of inputs, such that modules + * form a tree formation. In a tree formation, module N depends on + * module `floor(N/2)`, So the modules form a balanced binary tree. + */ + static JSModule[] createModuleTree(String ... inputs) { + JSModule[] modules = createModules(inputs); + for (int i = 1; i < modules.length; i++) { + modules[i].addDependency(modules[(i - 1) / 2]); + } + return modules; + } + + /** + * Generates a list of modules from a list of inputs. Does not generate any + * dependencies between the modules. + */ + static JSModule[] createModules(String... inputs) { + JSModule[] modules = new JSModule[inputs.length]; + for (int i = 0; i < inputs.length; i++) { + JSModule module = modules[i] = new JSModule("m" + i); + module.add(SourceFile.fromCode("i" + i, inputs[i])); + } + return modules; + } + + private static class BlackHoleErrorManager extends BasicErrorManager { + private BlackHoleErrorManager(Compiler compiler) { + compiler.setErrorManager(this); + } + + @Override + public void println(CheckLevel level, JSError error) {} + + @Override + public void printSummary() {} + } + + Compiler createCompiler() { + Compiler compiler = new Compiler(); + return compiler; + } + + protected void setExpectedSymbolTableError(DiagnosticType type) { + this.expectedSymbolTableError = type; + } + + /** Finds the first matching qualified name node in post-traversal order. */ + protected final Node findQualifiedNameNode(final String name, Node root) { + final List matches = Lists.newArrayList(); + NodeUtil.visitPostOrder(root, + new NodeUtil.Visitor() { + @Override public void visit(Node n) { + if (name.equals(n.getQualifiedName())) { + matches.add(n); + } + } + }, + Predicates.alwaysTrue()); + return matches.get(0); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CompilerTypeTestCase.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CompilerTypeTestCase.java new file mode 100644 index 0000000..954fd54 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CompilerTypeTestCase.java @@ -0,0 +1,113 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.CompilerOptions.LanguageMode; +import com.google.javascript.rhino.testing.BaseJSTypeTestCase; + +abstract class CompilerTypeTestCase extends BaseJSTypeTestCase { + + static final String ACTIVE_X_OBJECT_DEF = + "/**\n" + + " * @param {string} progId\n" + + " * @param {string=} opt_location\n" + + " * @constructor\n" + + " * @see http://msdn.microsoft.com/en-us/library/7sw4ddf8.aspx\n" + + " */\n" + + "function ActiveXObject(progId, opt_location) {}\n"; + + static final String CLOSURE_DEFS = + "var goog = {};" + + "goog.inherits = function(x, y) {};" + + "/** @type {!Function} */ goog.abstractMethod = function() {};" + + "goog.isArray = function(x) {};" + + "goog.isDef = function(x) {};" + + "goog.isFunction = function(x) {};" + + "goog.isNull = function(x) {};" + + "goog.isString = function(x) {};" + + "goog.isObject = function(x) {};" + + "goog.isDefAndNotNull = function(x) {};" + + "goog.array = {};" + + // simplified ArrayLike definition + "/**\n" + + " * @typedef {Array|{length: number}}\n" + + " */\n" + + "goog.array.ArrayLike;" + + "/**\n" + + " * @param {Array.|{length:number}} arr\n" + + " * @param {function(this:S, T, number, goog.array.ArrayLike):boolean} f\n" + + " * @param {S=} opt_obj\n" + + " * @return {!Array.}\n" + + " * @template T,S\n" + + " */" + + "goog.array.filter = function(arr, f, opt_obj){};" + + "goog.asserts = {};" + + "/** @return {*} */ goog.asserts.assert = function(x) { return x; };"; + + /** A default set of externs for testing. */ + static final String DEFAULT_EXTERNS = + "/** @constructor \n * @param {*=} opt_value */ " + + "function Object(opt_value) {}" + + "/** @constructor \n * @param {*} var_args */ " + + "function Function(var_args) {}" + + "/** @type {!Function} */ Function.prototype.apply;" + + "/** @type {!Function} */ Function.prototype.bind;" + + "/** @type {!Function} */ Function.prototype.call;" + + "/** @constructor \n * @param {*=} arg \n @return {string} */" + + "function String(arg) {}" + + "/** @param {number} sliceArg */\n" + + "String.prototype.slice = function(sliceArg) {};" + + "/** @type {number} */ String.prototype.length;" + + "/** @constructor \n * @param {*} var_args \n @return {!Array} */" + + "function Array(var_args) {}\n" + + "/** @type {number} */ Array.prototype.length;\n" + + "/** @constructor */\n" + + "function Arguments() {}\n" + + "/** @type {number} */\n" + + "Arguments.prototype.length;\n" + + "/** @type {!Arguments} */\n" + + "var arguments;" + + "" + ACTIVE_X_OBJECT_DEF; + + protected Compiler compiler; + + protected CompilerOptions getOptions() { + CompilerOptions options = new CompilerOptions(); + options.setLanguageIn(LanguageMode.ECMASCRIPT5); + options.setWarningLevel( + DiagnosticGroups.MISSING_PROPERTIES, CheckLevel.WARNING); + options.setWarningLevel( + DiagnosticGroups.MISPLACED_TYPE_ANNOTATION, CheckLevel.WARNING); + options.setWarningLevel( + DiagnosticGroups.CAST, CheckLevel.WARNING); + options.setCodingConvention(getCodingConvention()); + return options; + } + + protected CodingConvention getCodingConvention() { + return new GoogleCodingConvention(); + } + + @Override + protected void setUp() throws Exception { + compiler = new Compiler(); + compiler.initOptions(getOptions()); + registry = compiler.getTypeRegistry(); + initTypes(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ConcreteTypeTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ConcreteTypeTest.java new file mode 100644 index 0000000..fe4cd60 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ConcreteTypeTest.java @@ -0,0 +1,386 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import static com.google.javascript.jscomp.ConcreteType.ALL; +import static com.google.javascript.jscomp.ConcreteType.NONE; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.ConcreteType.ConcreteFunctionType; +import com.google.javascript.jscomp.ConcreteType.ConcreteInstanceType; +import com.google.javascript.jscomp.ConcreteType.ConcreteUnionType; +import com.google.javascript.jscomp.ConcreteType.Factory; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.StaticReference; +import com.google.javascript.rhino.jstype.StaticScope; +import com.google.javascript.rhino.jstype.StaticSlot; +import com.google.javascript.rhino.testing.AbstractStaticScope; +import com.google.javascript.rhino.testing.TestErrorReporter; + +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Unit test for the the subclasses of ConcreteType. + * + */ +public class ConcreteTypeTest extends TestCase { + private JSTypeRegistry typeRegistry; + private JSType unknownType; + private Factory factory; + + @Override + public void setUp() { + typeRegistry = new JSTypeRegistry(new TestErrorReporter(null, null)); + unknownType = typeRegistry.getNativeType(JSTypeNative.UNKNOWN_TYPE); + factory = new FakeFactory(); + } + + private void checkEquality(List types) { + for (int i = 0; i < types.size(); ++i) { + for (int j = 0; j < types.size(); ++j) { + if (i == j) { + assertEquals(types.get(i), types.get(j)); + } else { + assertFalse(types.get(i).equals(types.get(j))); + } + } + } + } + + public void testEquals() { + ConcreteFunctionType fun1 = createFunction("fun1"); + ConcreteFunctionType fun2 = createFunction("fun2"); + ConcreteType obj1 = fun1.getInstanceType(); + ConcreteType obj2 = fun2.getInstanceType(); + ConcreteType union1 = new ConcreteUnionType(fun1, fun2); + ConcreteType union2 = new ConcreteUnionType(fun1, obj1); + ConcreteType union3 = new ConcreteUnionType(fun1, obj1); + + checkEquality(Lists.newArrayList(fun1, fun2, obj1, obj2, + union1, union2)); + + assertEquals(union2, union3); + } + + public void testUnionWith() { + ConcreteFunctionType fun = createFunction("fun"); + ConcreteType obj = fun.getInstanceType(); + ConcreteType both = new ConcreteUnionType(fun, obj); + + assertTrue(fun.isSingleton()); + assertTrue(obj.isSingleton()); + assertFalse(both.isSingleton()); + assertFalse(NONE.isSingleton()); + assertFalse(ALL.isSingleton()); + + checkUnionWith(fun, NONE, fun); + checkUnionWith(fun, ALL, ALL); + + checkUnionWith(fun, obj, both); + checkUnionWith(both, NONE, both); + checkUnionWith(both, ALL, ALL); + } + + private void checkUnionWith(ConcreteType a, ConcreteType b, ConcreteType c) { + assertEquals(a, a.unionWith(a)); + assertEquals(b, b.unionWith(b)); + assertEquals(c, a.unionWith(b)); + assertEquals(c, b.unionWith(a)); + } + + public void testIntersectionWith() { + ConcreteFunctionType fun = createFunction("fun"); + ConcreteFunctionType fun2 = createFunction("fun2"); + ConcreteType obj = fun.getInstanceType(); + ConcreteType both = new ConcreteUnionType(fun, obj); + + assertEquals(NONE, fun.intersectWith(obj)); + assertEquals(NONE, obj.intersectWith(fun)); + + assertEquals(fun, both.intersectWith(fun)); + assertEquals(fun, fun.intersectWith(both)); + + assertEquals(NONE, NONE.intersectWith(both)); + assertEquals(NONE, both.intersectWith(NONE)); + assertEquals(NONE, fun.intersectWith(NONE)); + assertEquals(NONE, NONE.intersectWith(fun)); + + assertEquals(NONE, both.intersectWith(fun2)); + + assertEquals(both, ALL.intersectWith(both)); + assertEquals(both, both.intersectWith(ALL)); + assertEquals(fun, ALL.intersectWith(fun)); + assertEquals(fun, fun.intersectWith(ALL)); + assertEquals(NONE, ALL.intersectWith(NONE)); + assertEquals(NONE, NONE.intersectWith(ALL)); + } + + public void testFunction() { + ConcreteFunctionType fun = createFunction("fun", "a", "b"); + assertTrue(fun.isFunction()); + assertNotNull(fun.getCallSlot()); + assertNotNull(fun.getReturnSlot()); + assertNotNull(fun.getParameterSlot(0)); + assertNotNull(fun.getParameterSlot(1)); + assertNull(fun.getParameterSlot(2)); + assertTrue(fun.getInstanceType().isInstance()); + } + + public void testInstance() { + ConcreteInstanceType obj = createInstance("MyObj", "a", "b"); + assertTrue(obj.isInstance()); + assertNotNull(obj.getPropertySlot("a")); + assertNotNull(obj.getPropertySlot("b")); + assertNull(obj.getPropertySlot("c")); + + // The prototype chain should be: MyObj -> MyObj.prototype -> Object -> + // Object.prototype -> null. + for (int i = 0; i < 3; ++i) { + assertNotNull(obj = obj.getImplicitPrototype()); + assertTrue(obj.isInstance()); + } + assertNull(obj.getImplicitPrototype()); + } + + public void testGetX() { + ConcreteFunctionType fun1 = createFunction("fun1"); + ConcreteFunctionType fun2 = createFunction("fun2"); + ConcreteInstanceType obj1 = fun1.getInstanceType(); + ConcreteInstanceType obj2 = fun2.getInstanceType(); + ConcreteType union1 = fun1.unionWith(obj1); + ConcreteType union2 = + union1.unionWith(fun2).unionWith(obj2); + + assertEqualSets(Lists.newArrayList(), NONE.getFunctions()); + assertEqualSets(Lists.newArrayList(), NONE.getInstances()); + assertEqualSets(Lists.newArrayList(fun1), fun1.getFunctions()); + assertEqualSets(Lists.newArrayList(), fun1.getInstances()); + assertEqualSets(Lists.newArrayList(), obj1.getFunctions()); + assertEqualSets(Lists.newArrayList(obj1), obj1.getInstances()); + + assertEqualSets(Lists.newArrayList(fun1), union1.getFunctions()); + assertEqualSets(Lists.newArrayList(obj1), union1.getInstances()); + + assertEqualSets(Lists.newArrayList(fun1, fun2), union2.getFunctions()); + assertEqualSets(Lists.newArrayList(obj1, obj2), union2.getInstances()); + } + + /** Checks that the two collections are equal as sets. */ + private void assertEqualSets(Collection first, Collection second) { + assertEquals(Sets.newHashSet(first), Sets.newHashSet(second)); + } + + /** Creates a fake function with the given description. */ + private ConcreteFunctionType createFunction( + String name, String... paramNames) { + Node args = new Node(Token.PARAM_LIST); + for (int i = 0; i < paramNames.length; ++i) { + args.addChildToBack(Node.newString(Token.NAME, paramNames[i])); + } + + Node decl = new Node(Token.FUNCTION, + Node.newString(Token.NAME, name), + args, + new Node(Token.BLOCK)); + + JSType[] paramTypes = new JSType[paramNames.length]; + Arrays.fill(paramTypes, unknownType); + decl.setJSType(typeRegistry.createConstructorType( + name, decl, args, unknownType, null)); + + return new ConcreteFunctionType(factory, decl, null); + } + + /** Creates a fake instance with the given description. */ + private ConcreteInstanceType createInstance( + String name, String... propNames) { + ObjectType objType = typeRegistry.createObjectType(name, null, + typeRegistry.createObjectType(name + ".prototype", null, null)); + for (int i = 0; i < propNames.length; ++i) { + objType.defineDeclaredProperty(propNames[i], unknownType, null); + } + return new ConcreteInstanceType(factory, objType); + } + + private class FakeFactory implements Factory { + private final Map functionByDeclaration = + Maps.newHashMap(); + private final Map functionByJSType = + Maps.newHashMap(); + private final Map instanceByJSType = + Maps.newHashMap(); + + private final JSTypeRegistry registry = new JSTypeRegistry( + new TestErrorReporter(null, null)); + + @Override + public JSTypeRegistry getTypeRegistry() { + return registry; + } + + /** {@inheritDoc} */ + @Override + public ConcreteFunctionType createConcreteFunction( + Node decl, StaticScope parent) { + ConcreteFunctionType funcType = functionByDeclaration.get(decl); + if (funcType == null) { + functionByDeclaration.put(decl, funcType = + new ConcreteFunctionType(this, decl, parent)); + if (decl.getJSType() != null) { + functionByJSType.put((FunctionType) decl.getJSType(), funcType); + } + } + return funcType; + } + + /** {@inheritDoc} */ + @Override + public ConcreteInstanceType createConcreteInstance( + ObjectType instanceType) { + ConcreteInstanceType instType = instanceByJSType.get(instanceType); + if (instType == null) { + instanceByJSType.put(instanceType, + instType = new ConcreteInstanceType(this, instanceType)); + } + return instType; + } + + /** {@inheritDoc} */ + @Override + public ConcreteFunctionType getConcreteFunction(FunctionType functionType) { + return functionByJSType.get(functionType); + } + + /** {@inheritDoc} */ + @Override + public ConcreteInstanceType getConcreteInstance(ObjectType instanceType) { + return instanceByJSType.get(instanceType); + } + + /** {@inheritDoc} */ + @Override + public StaticScope createFunctionScope( + Node decl, StaticScope parent) { + FakeScope scope = new FakeScope((FakeScope) parent); + scope.addSlot(ConcreteFunctionType.CALL_SLOT_NAME); + scope.addSlot(ConcreteFunctionType.THIS_SLOT_NAME); + scope.addSlot(ConcreteFunctionType.RETURN_SLOT_NAME); + for (Node n = decl.getFirstChild().getNext().getFirstChild(); + n != null; + n = n.getNext()) { + scope.addSlot(n.getString()); + } + return scope; + } + + /** {@inheritDoc} */ + @Override + public StaticScope createInstanceScope( + ObjectType instanceType) { + FakeScope parentScope = null; + if (instanceType.getImplicitPrototype() != null) { + ConcreteInstanceType prototype = + createConcreteInstance(instanceType.getImplicitPrototype()); + parentScope = (FakeScope) prototype.getScope(); + } + + FakeScope scope = new FakeScope(parentScope); + for (String propName : instanceType.getOwnPropertyNames()) { + scope.addSlot(propName); + } + return scope; + } + } + + // TODO(user): move to a common place if it can be used elsewhere + private class FakeScope extends AbstractStaticScope { + private final FakeScope parent; + private final Map slots = Maps.newHashMap(); + + FakeScope(FakeScope parent) { + this.parent = parent; + } + + /** {@inheritDoc} */ + @Override + public StaticScope getParentScope() { return parent; } + + /** {@inheritDoc} */ + @Override + public StaticSlot getOwnSlot(String name) { + return slots.get(name); + } + + /** {@inheritDoc} */ + @Override + public StaticSlot getSlot(String name) { + if (slots.containsKey(name)) { + return slots.get(name); + } else if (parent != null) { + return parent.getSlot(name); + } else { + return null; + } + } + + /** {@inheritDoc} */ + @Override + public ConcreteType getTypeOfThis() { return ConcreteType.ALL; } + + void addSlot(String name) { + slots.put(name, new FakeSlot(name)); + } + } + + // TODO(user): move to a common place if it can be used elsewhere + private class FakeSlot implements StaticSlot { + private final String name; + + FakeSlot(String name) { + this.name = name; + } + + @Override + public String getName() { return name; } + + @Override + public ConcreteType getType() { return ConcreteType.ALL; } + + @Override + public boolean isTypeInferred() { return true; } + + @Override + public StaticReference getDeclaration() { return null; } + + @Override + public JSDocInfo getJSDocInfo() { return null; } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ConstCheckTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ConstCheckTest.java new file mode 100644 index 0000000..2a222aa --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ConstCheckTest.java @@ -0,0 +1,159 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + + +/** + * Tests {@link ConstCheck}. + * + */ +public class ConstCheckTest extends CompilerTestCase { + + public ConstCheckTest() { + enableNormalize(); + } + + @Override + public CompilerPass getProcessor(Compiler compiler) { + return new ConstCheck(compiler); + } + + @Override + public int getNumRepetitions() { + return 1; + } + + public void testConstantDefinition1() { + testSame("var XYZ = 1;"); + } + + public void testConstantDefinition2() { + testSame("var a$b$XYZ = 1;"); + } + + public void testConstantInitializedInAnonymousNamespace1() { + testSame("var XYZ; (function(){ XYZ = 1; })();"); + } + + public void testConstantInitializedInAnonymousNamespace2() { + testSame("var a$b$XYZ; (function(){ a$b$XYZ = 1; })();"); + } + + public void testObjectModified() { + testSame("var IE = true, XYZ = {a:1,b:1}; if (IE) XYZ['c'] = 1;"); + } + + public void testObjectPropertyInitializedLate() { + testSame("var XYZ = {}; for (var i = 0; i < 10; i++) { XYZ[i] = i; }"); + } + + public void testObjectRedefined1() { + testError("var XYZ = {}; XYZ = 2;"); + } + + public void testConstantRedefined1() { + testError("var XYZ = 1; XYZ = 2;"); + } + + public void testConstantRedefined2() { + testError("var a$b$XYZ = 1; a$b$XYZ = 2;"); + } + + public void testConstantRedefinedInLocalScope1() { + testError("var XYZ = 1; (function(){ XYZ = 2; })();"); + } + + public void testConstantRedefinedInLocalScope2() { + testError("var a$b$XYZ = 1; (function(){ a$b$XYZ = 2; })();"); + } + + public void testConstantRedefinedInLocalScopeOutOfOrder() { + testError("function f() { XYZ = 2; } var XYZ = 1;"); + } + + public void testConstantPostIncremented1() { + testError("var XYZ = 1; XYZ++;"); + } + + public void testConstantPostIncremented2() { + testError("var a$b$XYZ = 1; a$b$XYZ++;"); + } + + public void testConstantPreIncremented1() { + testError("var XYZ = 1; XYZ++;"); + } + + public void testConstantPreIncremented2() { + testError("var a$b$XYZ = 1; a$b$XYZ++;"); + } + + public void testConstantPostDecremented1() { + testError("var XYZ = 1; XYZ--;"); + } + + public void testConstantPostDecremented2() { + testError("var a$b$XYZ = 1; a$b$XYZ--;"); + } + + public void testConstantPreDecremented1() { + testError("var XYZ = 1; XYZ--;"); + } + + public void testConstantPreDecremented2() { + testError("var a$b$XYZ = 1; a$b$XYZ--;"); + } + + public void testAbbreviatedArithmeticAssignment1() { + testError("var XYZ = 1; XYZ += 2;"); + } + + public void testAbbreviatedArithmeticAssignment2() { + testError("var a$b$XYZ = 1; a$b$XYZ %= 2;"); + } + + public void testAbbreviatedBitAssignment1() { + testError("var XYZ = 1; XYZ |= 2;"); + } + + public void testAbbreviatedBitAssignment2() { + testError("var a$b$XYZ = 1; a$b$XYZ &= 2;"); + } + + public void testAbbreviatedShiftAssignment1() { + testError("var XYZ = 1; XYZ >>= 2;"); + } + + public void testAbbreviatedShiftAssignment2() { + testError("var a$b$XYZ = 1; a$b$XYZ <<= 2;"); + } + + public void testConstAnnotation() { + testError("/** @const */ var xyz = 1; xyz = 3;"); + } + + public void testConstSuppression() { + testSame("/**\n" + + " * @fileoverview\n" + + " * @suppress {const}\n" + + " */\n" + + "/** @const */ var xyz = 1; xyz = 3;"); + } + + private void testError(String js) { + test(js, null, ConstCheck.CONST_REASSIGNED_VALUE_ERROR); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ControlFlowAnalysisTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ControlFlowAnalysisTest.java new file mode 100644 index 0000000..5560e2c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ControlFlowAnalysisTest.java @@ -0,0 +1,1441 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import junit.framework.TestCase; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + + +/** + * Tests {@link ControlFlowAnalysis}. + * + */ +public class ControlFlowAnalysisTest extends TestCase { + + /** + * Given an input in JavaScript, test if the control flow analysis + * creates the proper control flow graph by comparing the expected + * Dot file output. + * + * @param input Input JavaScript. + * @param expected Expected Graphviz Dot file. + */ + private void testCfg(String input, String expected) { + testCfg(input, expected, true); + } + + /** + * Gets all the edges of the graph. + */ + private static List> getAllEdges( + ControlFlowGraph cfg) { + List> edges = Lists.newArrayList(); + for (DiGraphNode n : cfg.getDirectedGraphNodes()) { + for (DiGraphEdge e : cfg.getOutEdges(n.getValue())) { + edges.add(e); + } + } + return edges; + } + + /** + * Gets all the control flow edges from some node with the first token to + * some node with the second token. + */ + private static List> getAllEdges( + ControlFlowGraph cfg, int startToken, int endToken) { + List> edges = getAllEdges(cfg); + Iterator> it = edges.iterator(); + while (it.hasNext()) { + DiGraphEdge edge = it.next(); + Node startNode = edge.getSource().getValue(); + Node endNode = edge.getDestination().getValue(); + if (startNode == null || endNode == null || + startNode.getType() != startToken || endNode.getType() != endToken) { + it.remove(); + } + } + return edges; + } + + /** + * Gets all the control flow edges of the given type from some node with the + * first token to some node with the second token. + */ + private static List> getAllEdges( + ControlFlowGraph cfg, int startToken, int endToken, Branch type) { + List> edges = + getAllEdges(cfg, startToken, endToken); + Iterator> it = edges.iterator(); + while (it.hasNext()) { + if (type != it.next().getValue()) { + it.remove(); + } + } + return edges; + } + + private static boolean isAncestor(Node n, Node maybeDescendent) { + for (Node current = n.getFirstChild(); current != null; + current = current.getNext()) { + if (current == maybeDescendent || + isAncestor(current, maybeDescendent)) { + return true; + } + } + + return false; + } + + /** + * Gets all the control flow edges of the given type from some node with + * the first token to some node with the second token. + * This edge must flow from a parent to one of its descendants. + */ + private static List> getAllDownEdges( + ControlFlowGraph cfg, int startToken, int endToken, Branch type) { + List> edges = + getAllEdges(cfg, startToken, endToken, type); + Iterator> it = edges.iterator(); + while (it.hasNext()) { + DiGraphEdge edge = it.next(); + Node source = edge.getSource().getValue(); + Node dest = edge.getDestination().getValue(); + if (!isAncestor(source, dest)) { + it.remove(); + } + } + + return edges; + } + + /** + * Assert that there exists a control flow edge of the given type + * from some node with the first token to some node with the second token. + */ + private static void assertNoEdge(ControlFlowGraph cfg, int startToken, + int endToken) { + assertEquals(0, getAllEdges(cfg, startToken, endToken).size()); + } + + /** + * Assert that there exists a control flow edge of the given type + * from some node with the first token to some node with the second token. + * This edge must flow from a parent to one of its descendants. + */ + private static void assertDownEdge(ControlFlowGraph cfg, + int startToken, int endToken, Branch type) { + assertTrue("No down edge found", + 0 != getAllDownEdges(cfg, startToken, endToken, type).size()); + } + + /** + * Assert that there exists a control flow edge of the given type + * from some node with the first token to some node with the second token. + * This edge must flow from a node to one of its ancestors. + */ + private static void assertUpEdge(ControlFlowGraph cfg, + int startToken, int endToken, Branch type) { + assertTrue("No up edge found", + 0 != getAllDownEdges(cfg, endToken, startToken, type).size()); + } + + /** + * Assert that there exists a control flow edge of the given type + * from some node with the first token to some node with the second token. + * This edge must flow between two nodes that are not in the same subtree. + */ + private static void assertCrossEdge(ControlFlowGraph cfg, + int startToken, int endToken, Branch type) { + int numDownEdges = getAllDownEdges(cfg, startToken, endToken, type).size(); + int numUpEdges = getAllDownEdges(cfg, endToken, startToken, type).size(); + int numEdges = getAllEdges(cfg, startToken, endToken, type).size(); + assertTrue("No cross edges found", numDownEdges + numUpEdges < numEdges); + } + + /** + * Assert that there exists a control flow edge of the given type + * from some node with the first token to the return node. + */ + private static void assertReturnEdge(ControlFlowGraph cfg, + int startToken) { + List> edges = getAllEdges(cfg); + for (DiGraphEdge edge : edges) { + Node source = edge.getSource().getValue(); + DiGraphNode dest = edge.getDestination(); + if (source.getType() == startToken && + cfg.isImplicitReturn(dest)) { + return; + } + } + + fail("No return edge found"); + } + + /** + * Assert that there exists no control flow edge of the given type + * from some node with the first token to the return node. + */ + private static void assertNoReturnEdge(ControlFlowGraph cfg, + int startToken) { + List> edges = getAllEdges(cfg); + for (DiGraphEdge edge : edges) { + Node source = edge.getSource().getValue(); + DiGraphNode dest = edge.getDestination(); + if (source.getType() == startToken) { + assertTrue("Token " + startToken + " should not have an out going" + + " edge to the implicit return", !cfg.isImplicitReturn(dest)); + return; + } + } + } + + /** + * Given an input in JavaScript, get a control flow graph for it. + * + * @param input Input JavaScript. + */ + private ControlFlowGraph createCfg(String input, + boolean runSynBlockPass) { + Compiler compiler = new Compiler(); + ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, true, true); + + Node root = compiler.parseSyntheticCode("cfgtest", input); + if (runSynBlockPass) { + CreateSyntheticBlocks pass = new CreateSyntheticBlocks( + compiler, "START", "END"); + pass.process(null, root); + } + cfa.process(null, root); + return cfa.getCfg(); + } + + private ControlFlowGraph createCfg(String input) { + return createCfg(input, false); + } + + /** + * Given an input in JavaScript, test if the control flow analysis + * creates the proper control flow graph by comparing the expected + * Dot file output. + * + * @param input Input JavaScript. + * @param expected Expected Graphviz Dot file. + * @param shouldTraverseFunctions Whether to traverse functions when + * constructing the CFG (true by default). Passed in to the + * constructor of {@link ControlFlowAnalysis}. + */ + private void testCfg(String input, String expected, + boolean shouldTraverseFunctions) { + Compiler compiler = new Compiler(); + ControlFlowAnalysis cfa = + new ControlFlowAnalysis(compiler, shouldTraverseFunctions, true); + + Node root = compiler.parseSyntheticCode("cfgtest", input); + cfa.process(null, root); + ControlFlowGraph cfg = cfa.getCfg(); + try { + assertEquals(expected, DotFormatter.toDot(root, cfg)); + } catch (java.io.IOException e) { + fail("Tests failed with IOExceptions"); + } + } + + public void testSimpleStatements() { + String src = "var a; a = a; a = a"; + ControlFlowGraph cfg = createCfg(src); + assertDownEdge(cfg, Token.SCRIPT, Token.VAR, Branch.UNCOND); + assertCrossEdge(cfg, Token.VAR, Token.EXPR_RESULT, Branch.UNCOND); + assertCrossEdge(cfg, Token.EXPR_RESULT, Token.EXPR_RESULT, Branch.UNCOND); + } + + // Test a simple IF control flow. + public void testSimpleIf() { + String src = "var x; if (x) { x() } else { x() };"; + ControlFlowGraph cfg = createCfg(src); + assertDownEdge(cfg, Token.SCRIPT, Token.VAR, Branch.UNCOND); + assertCrossEdge(cfg, Token.VAR, Token.IF, Branch.UNCOND); + assertDownEdge(cfg, Token.IF, Token.BLOCK, Branch.ON_TRUE); + assertDownEdge(cfg, Token.BLOCK, Token.EXPR_RESULT, Branch.UNCOND); + assertNoEdge(cfg, Token.EXPR_RESULT, Token.CALL); + assertDownEdge(cfg, Token.IF, Token.BLOCK, Branch.ON_FALSE); + assertReturnEdge(cfg, Token.EMPTY); + } + + public void testBreakingBlock() { + // BUG #1382217 + String src = "X: { while(1) { break } }"; + ControlFlowGraph cfg = createCfg(src); + assertUpEdge(cfg, Token.BREAK, Token.BLOCK, Branch.UNCOND); + } + + public void testBreakingTryBlock() { + String src = "a: try { break a; } finally {} if(x) {}"; + ControlFlowGraph cfg = createCfg(src); + assertCrossEdge(cfg, Token.BREAK, Token.IF, Branch.UNCOND); + + src = "a: try {} finally {break a;} if(x) {}"; + cfg = createCfg(src); + assertCrossEdge(cfg, Token.BREAK, Token.IF, Branch.UNCOND); + + src = "a: try {} catch(e) {break a;} if(x) {}"; + cfg = createCfg(src); + assertCrossEdge(cfg, Token.BREAK, Token.IF, Branch.UNCOND); + } + + public void testWithStatement() { + String src = "var x, y; with(x) { y() }"; + ControlFlowGraph cfg = createCfg(src); + assertDownEdge(cfg, Token.WITH, Token.BLOCK, Branch.UNCOND); + assertNoEdge(cfg, Token.WITH, Token.NAME); + assertNoEdge(cfg, Token.NAME, Token.BLOCK); + assertDownEdge(cfg, Token.BLOCK, Token.EXPR_RESULT, Branch.UNCOND); + assertReturnEdge(cfg, Token.EXPR_RESULT); + } + + // Test a simple WHILE control flow with BREAKs. + public void testSimpleWhile() { + String src = "var x; while (x) { x(); if (x) { break; } x() }"; + ControlFlowGraph cfg = createCfg(src); + assertDownEdge(cfg, Token.WHILE, Token.BLOCK, Branch.ON_TRUE); + assertDownEdge(cfg, Token.BLOCK, Token.EXPR_RESULT, Branch.UNCOND); + assertDownEdge(cfg, Token.IF, Token.BLOCK, Branch.ON_TRUE); + assertReturnEdge(cfg, Token.BREAK); + } + + public void testSimpleSwitch() { + String src = "var x; switch(x){ case(1): x(); case('x'): x(); break" + + "; default: x();}"; + ControlFlowGraph cfg = createCfg(src); + assertCrossEdge(cfg, Token.VAR, Token.SWITCH, Branch.UNCOND); + assertNoEdge(cfg, Token.SWITCH, Token.NAME); + // Transfer between cases and default. + assertDownEdge(cfg, Token.SWITCH, Token.CASE, Branch.UNCOND); + assertCrossEdge(cfg, Token.CASE, Token.CASE, Branch.ON_FALSE); + assertCrossEdge(cfg, Token.CASE, Token.DEFAULT_CASE, Branch.ON_FALSE); + // Within each case. + assertDownEdge(cfg, Token.CASE, Token.BLOCK, Branch.ON_TRUE); + assertDownEdge(cfg, Token.BLOCK, Token.EXPR_RESULT, Branch.UNCOND); + assertNoEdge(cfg, Token.EXPR_RESULT, Token.CALL); + assertNoEdge(cfg, Token.CALL, Token.NAME); + } + + public void testSimpleNoDefault() { + String src = "var x; switch(x){ case(1): break; } x();"; + ControlFlowGraph cfg = createCfg(src); + assertCrossEdge(cfg, Token.CASE, Token.EXPR_RESULT, Branch.ON_FALSE); + } + + public void testSwitchDefaultFirst() { + // DEFAULT appears first. But it is should evaluated last. + String src = "var x; switch(x){ default: break; case 1: break; }"; + ControlFlowGraph cfg = createCfg(src); + assertDownEdge(cfg, Token.SWITCH, Token.CASE, Branch.UNCOND); + assertCrossEdge(cfg, Token.CASE, Token.DEFAULT_CASE, Branch.ON_FALSE); + } + + public void testSwitchDefaultInMiddle() { + // DEFAULT appears in the middle. But it is should evaluated last. + String src = "var x; switch(x){ case 1: break; default: break; " + + "case 2: break; }"; + ControlFlowGraph cfg = createCfg(src); + assertDownEdge(cfg, Token.SWITCH, Token.CASE, Branch.UNCOND); + assertCrossEdge(cfg, Token.CASE, Token.CASE, Branch.ON_FALSE); + assertCrossEdge(cfg, Token.CASE, Token.DEFAULT_CASE, Branch.ON_FALSE); + } + + public void testSwitchEmpty() { + // DEFAULT appears first. But it is should evaluated last. + String src = "var x; switch(x){}; x()"; + ControlFlowGraph cfg = createCfg(src); + assertCrossEdge(cfg, Token.SWITCH, Token.EMPTY, Branch.UNCOND); + assertCrossEdge(cfg, Token.EMPTY, Token.EXPR_RESULT, Branch.UNCOND); + } + + public void testReturnThrowingException() { + String src = "function f() {try { return a(); } catch (e) {e()}}"; + ControlFlowGraph cfg = createCfg(src); + assertCrossEdge(cfg, Token.RETURN, Token.BLOCK, Branch.ON_EX); + assertDownEdge(cfg, Token.BLOCK, Token.CATCH, Branch.UNCOND); + } + + // Test a simple FOR loop. + public void testSimpleFor() { + String src = "var a; for (var x = 0; x < 100; x++) { a(); }"; + String expected = "digraph AST {\n" + + " node [color=lightblue2, style=filled];\n" + + " node0 [label=\"SCRIPT\"];\n" + + " node1 [label=\"VAR\"];\n" + + " node0 -> node1 [weight=1];\n" + + " node2 [label=\"NAME\"];\n" + + " node1 -> node2 [weight=1];\n" + + " node3 [label=\"VAR\"];\n" + + " node1 -> node3 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node4 [label=\"FOR\"];\n" + + " node0 -> node4 [weight=1];\n" + + " node4 -> node3 [weight=1];\n" + + " node5 [label=\"NAME\"];\n" + + " node3 -> node5 [weight=1];\n" + + " node6 [label=\"NUMBER\"];\n" + + " node5 -> node6 [weight=1];\n" + + " node3 -> node4 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node7 [label=\"LT\"];\n" + + " node4 -> node7 [weight=1];\n" + + " node8 [label=\"NAME\"];\n" + + " node7 -> node8 [weight=1];\n" + + " node9 [label=\"NUMBER\"];\n" + + " node7 -> node9 [weight=1];\n" + + " node10 [label=\"INC\"];\n" + + " node4 -> node10 [weight=1];\n" + + " node11 [label=\"NAME\"];\n" + + " node10 -> node11 [weight=1];\n" + + " node10 -> node4 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node12 [label=\"BLOCK\"];\n" + + " node4 -> node12 [weight=1];\n" + + " node13 [label=\"EXPR_RESULT\"];\n" + + " node12 -> node13 [weight=1];\n" + + " node14 [label=\"CALL\"];\n" + + " node13 -> node14 [weight=1];\n" + + " node15 [label=\"NAME\"];\n" + + " node14 -> node15 [weight=1];\n" + + " node13 -> node10 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node12 -> node13 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node4 -> RETURN " + + "[label=\"ON_FALSE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node4 -> node12 " + + "[label=\"ON_TRUE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> node1 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + "}\n"; + testCfg(src, expected); + } + + public void testSimpleForWithContinue() { + String src = "var a; for (var x = 0; x < 100; x++) {a();continue;a()}"; + String expected = "digraph AST {\n" + + " node [color=lightblue2, style=filled];\n" + + " node0 [label=\"SCRIPT\"];\n" + + " node1 [label=\"VAR\"];\n" + + " node0 -> node1 [weight=1];\n" + + " node2 [label=\"NAME\"];\n" + + " node1 -> node2 [weight=1];\n" + + " node3 [label=\"VAR\"];\n" + + " node1 -> node3 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node4 [label=\"FOR\"];\n" + + " node0 -> node4 [weight=1];\n" + + " node4 -> node3 [weight=1];\n" + + " node5 [label=\"NAME\"];\n" + + " node3 -> node5 [weight=1];\n" + + " node6 [label=\"NUMBER\"];\n" + + " node5 -> node6 [weight=1];\n" + + " node3 -> node4 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node7 [label=\"LT\"];\n" + + " node4 -> node7 [weight=1];\n" + + " node8 [label=\"NAME\"];\n" + + " node7 -> node8 [weight=1];\n" + + " node9 [label=\"NUMBER\"];\n" + + " node7 -> node9 [weight=1];\n" + + " node10 [label=\"INC\"];\n" + + " node4 -> node10 [weight=1];\n" + + " node11 [label=\"NAME\"];\n" + + " node10 -> node11 [weight=1];\n" + + " node10 -> node4 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node12 [label=\"BLOCK\"];\n" + + " node4 -> node12 [weight=1];\n" + + " node13 [label=\"EXPR_RESULT\"];\n" + + " node12 -> node13 [weight=1];\n" + + " node14 [label=\"CALL\"];\n" + + " node13 -> node14 [weight=1];\n" + + " node15 [label=\"NAME\"];\n" + + " node14 -> node15 [weight=1];\n" + + " node16 [label=\"CONTINUE\"];\n" + + " node13 -> node16 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node12 -> node16 [weight=1];\n" + + " node16 -> node10 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node17 [label=\"EXPR_RESULT\"];\n" + + " node12 -> node17 [weight=1];\n" + + " node18 [label=\"CALL\"];\n" + + " node17 -> node18 [weight=1];\n" + + " node19 [label=\"NAME\"];\n" + + " node18 -> node19 [weight=1];\n" + + " node17 -> node10 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node12 -> node13 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node4 -> RETURN " + + "[label=\"ON_FALSE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node4 -> node12 " + + "[label=\"ON_TRUE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> node1 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + "}\n"; + testCfg(src, expected); + } + + public void testNestedFor() { + // This is tricky as the inner FOR branches to "x++" ON_FALSE. + String src = "var a,b;a();for(var x=0;x<100;x++){for(var y=0;y<100;y++){" + + "continue;b();}}"; + String expected = "digraph AST {\n" + + " node [color=lightblue2, style=filled];\n" + + " node0 [label=\"SCRIPT\"];\n" + + " node1 [label=\"VAR\"];\n" + + " node0 -> node1 [weight=1];\n" + + " node2 [label=\"NAME\"];\n" + + " node1 -> node2 [weight=1];\n" + + " node3 [label=\"NAME\"];\n" + + " node1 -> node3 [weight=1];\n" + + " node4 [label=\"EXPR_RESULT\"];\n" + + " node1 -> node4 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> node4 [weight=1];\n" + + " node5 [label=\"CALL\"];\n" + + " node4 -> node5 [weight=1];\n" + + " node6 [label=\"NAME\"];\n" + + " node5 -> node6 [weight=1];\n" + + " node7 [label=\"VAR\"];\n" + + " node4 -> node7 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node8 [label=\"FOR\"];\n" + + " node0 -> node8 [weight=1];\n" + + " node8 -> node7 [weight=1];\n" + + " node9 [label=\"NAME\"];\n" + + " node7 -> node9 [weight=1];\n" + + " node10 [label=\"NUMBER\"];\n" + + " node9 -> node10 [weight=1];\n" + + " node7 -> node8 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node11 [label=\"LT\"];\n" + + " node8 -> node11 [weight=1];\n" + + " node12 [label=\"NAME\"];\n" + + " node11 -> node12 [weight=1];\n" + + " node13 [label=\"NUMBER\"];\n" + + " node11 -> node13 [weight=1];\n" + + " node14 [label=\"INC\"];\n" + + " node8 -> node14 [weight=1];\n" + + " node15 [label=\"NAME\"];\n" + + " node14 -> node15 [weight=1];\n" + + " node14 -> node8 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node16 [label=\"BLOCK\"];\n" + + " node8 -> node16 [weight=1];\n" + + " node17 [label=\"FOR\"];\n" + + " node16 -> node17 [weight=1];\n" + + " node18 [label=\"VAR\"];\n" + + " node17 -> node18 [weight=1];\n" + + " node19 [label=\"NAME\"];\n" + + " node18 -> node19 [weight=1];\n" + + " node20 [label=\"NUMBER\"];\n" + + " node19 -> node20 [weight=1];\n" + + " node18 -> node17 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node21 [label=\"LT\"];\n" + + " node17 -> node21 [weight=1];\n" + + " node22 [label=\"NAME\"];\n" + + " node21 -> node22 [weight=1];\n" + + " node23 [label=\"NUMBER\"];\n" + + " node21 -> node23 [weight=1];\n" + + " node24 [label=\"INC\"];\n" + + " node17 -> node24 [weight=1];\n" + + " node25 [label=\"NAME\"];\n" + + " node24 -> node25 [weight=1];\n" + + " node24 -> node17 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node26 [label=\"BLOCK\"];\n" + + " node17 -> node26 [weight=1];\n" + + " node27 [label=\"CONTINUE\"];\n" + + " node26 -> node27 [weight=1];\n" + + " node27 -> node24 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node28 [label=\"EXPR_RESULT\"];\n" + + " node26 -> node28 [weight=1];\n" + + " node29 [label=\"CALL\"];\n" + + " node28 -> node29 [weight=1];\n" + + " node30 [label=\"NAME\"];\n" + + " node29 -> node30 [weight=1];\n" + + " node28 -> node24 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node26 -> node27 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node17 -> node14 " + + "[label=\"ON_FALSE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node17 -> node26 " + + "[label=\"ON_TRUE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node16 -> node18 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node8 -> RETURN " + + "[label=\"ON_FALSE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node8 -> node16 " + + "[label=\"ON_TRUE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> node1 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + "}\n"; + testCfg(src, expected); + } + + public void testNestedDoWithBreak() { + // The BREAK branches to a() with UNCOND. + String src = "var a;do{do{break}while(a);do{a()}while(a)}while(a);"; + String expected = "digraph AST {\n" + + " node [color=lightblue2, style=filled];\n" + + " node0 [label=\"SCRIPT\"];\n" + + " node1 [label=\"VAR\"];\n" + + " node0 -> node1 [weight=1];\n" + + " node2 [label=\"NAME\"];\n" + + " node1 -> node2 [weight=1];\n" + + " node3 [label=\"BLOCK\"];\n" + + " node1 -> node3 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node4 [label=\"DO\"];\n" + + " node0 -> node4 [weight=1];\n" + + " node4 -> node3 [weight=1];\n" + + " node5 [label=\"DO\"];\n" + + " node3 -> node5 [weight=1];\n" + + " node6 [label=\"BLOCK\"];\n" + + " node5 -> node6 [weight=1];\n" + + " node7 [label=\"BREAK\"];\n" + + " node6 -> node7 [weight=1];\n" + + " node8 [label=\"BLOCK\"];\n" + + " node7 -> node8 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node6 -> node7 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node9 [label=\"NAME\"];\n" + + " node5 -> node9 [weight=1];\n" + + " node5 -> node6 " + + "[label=\"ON_TRUE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node5 -> node8 " + + "[label=\"ON_FALSE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node10 [label=\"DO\"];\n" + + " node3 -> node10 [weight=1];\n" + + " node10 -> node8 [weight=1];\n" + + " node11 [label=\"EXPR_RESULT\"];\n" + + " node8 -> node11 [weight=1];\n" + + " node12 [label=\"CALL\"];\n" + + " node11 -> node12 [weight=1];\n" + + " node13 [label=\"NAME\"];\n" + + " node12 -> node13 [weight=1];\n" + + " node11 -> node10 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node8 -> node11 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node14 [label=\"NAME\"];\n" + + " node10 -> node14 [weight=1];\n" + + " node10 -> node4 " + + "[label=\"ON_FALSE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node10 -> node8 " + + "[label=\"ON_TRUE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node3 -> node6 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node15 [label=\"NAME\"];\n" + + " node4 -> node15 [weight=1];\n" + + " node4 -> RETURN " + + "[label=\"ON_FALSE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node4 -> node3 " + + "[label=\"ON_TRUE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> node1 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + "}\n"; + testCfg(src, expected); + } + + public void testForIn() { + String src = "var a,b;for(a in b){a()};"; + String expected = "digraph AST {\n" + + " node [color=lightblue2, style=filled];\n" + + " node0 [label=\"SCRIPT\"];\n" + + " node1 [label=\"VAR\"];\n" + + " node0 -> node1 [weight=1];\n" + + " node2 [label=\"NAME\"];\n" + + " node1 -> node2 [weight=1];\n" + + " node3 [label=\"NAME\"];\n" + + " node1 -> node3 [weight=1];\n" + + " node4 [label=\"NAME\"];\n" + + " node1 -> node4 [label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node5 [label=\"FOR\"];\n" + + " node0 -> node5 [weight=1];\n" + + " node6 [label=\"NAME\"];\n" + + " node5 -> node6 [weight=1];\n" + + " node5 -> node4 [weight=1];\n" + + " node4 -> node5 [label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node7 [label=\"BLOCK\"];\n" + + " node5 -> node7 [weight=1];\n" + + " node8 [label=\"EXPR_RESULT\"];\n" + + " node7 -> node8 [weight=1];\n" + + " node9 [label=\"CALL\"];\n" + + " node8 -> node9 [weight=1];\n" + + " node10 [label=\"NAME\"];\n" + + " node9 -> node10 [weight=1];\n" + + " node8 -> node5 [label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node7 -> node8 [label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node11 [label=\"EMPTY\"];\n" + + " node5 -> node11 [label=\"ON_FALSE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node5 -> node7 [label=\"ON_TRUE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> node11 [weight=1];\n" + + " node11 -> RETURN [label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> node1 [label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + "}\n"; + testCfg(src, expected); + } + + public void testThrow() { + String src = "function f() { throw 1; f() }"; + String expected = "digraph AST {\n" + + " node [color=lightblue2, style=filled];\n" + + " node0 [label=\"SCRIPT\"];\n" + + " node1 [label=\"FUNCTION\"];\n" + + " node0 -> node1 [weight=1];\n" + + " node2 [label=\"NAME\"];\n" + + " node1 -> node2 [weight=1];\n" + + " node3 [label=\"PARAM_LIST\"];\n" + + " node1 -> node3 [weight=1];\n" + + " node4 [label=\"BLOCK\"];\n" + + " node1 -> node4 [weight=1];\n" + + " node5 [label=\"THROW\"];\n" + + " node4 -> node5 [weight=1];\n" + + " node6 [label=\"NUMBER\"];\n" + + " node5 -> node6 [weight=1];\n" + + " node7 [label=\"EXPR_RESULT\"];\n" + + " node4 -> node7 [weight=1];\n" + + " node8 [label=\"CALL\"];\n" + + " node7 -> node8 [weight=1];\n" + + " node9 [label=\"NAME\"];\n" + + " node8 -> node9 [weight=1];\n" + + " node7 -> RETURN " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node4 -> node5 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node1 -> node4 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> RETURN " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + "}\n"; + testCfg(src, expected); + } + + // Test a simple FUNCTION. + public void testSimpleFunction() { + String src = "function f() { f() } f()"; + String expected = "digraph AST {\n" + + " node [color=lightblue2, style=filled];\n" + + " node0 [label=\"SCRIPT\"];\n" + + " node1 [label=\"FUNCTION\"];\n" + + " node0 -> node1 [weight=1];\n" + + " node2 [label=\"NAME\"];\n" + + " node1 -> node2 [weight=1];\n" + + " node3 [label=\"PARAM_LIST\"];\n" + + " node1 -> node3 [weight=1];\n" + + " node4 [label=\"BLOCK\"];\n" + + " node1 -> node4 [weight=1];\n" + + " node5 [label=\"EXPR_RESULT\"];\n" + + " node4 -> node5 [weight=1];\n" + + " node6 [label=\"CALL\"];\n" + + " node5 -> node6 [weight=1];\n" + + " node7 [label=\"NAME\"];\n" + + " node6 -> node7 [weight=1];\n" + + " node5 -> RETURN " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node4 -> node5 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node1 -> node4 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node8 [label=\"EXPR_RESULT\"];\n" + + " node0 -> node8 [weight=1];\n" + + " node9 [label=\"CALL\"];\n" + + " node8 -> node9 [weight=1];\n" + + " node10 [label=\"NAME\"];\n" + + " node9 -> node10 [weight=1];\n" + + " node8 -> RETURN " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> node8 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + "}\n"; + testCfg(src, expected); + } + + public void testSimpleCatch() { + String src = "try{ throw x; x(); x['stuff']; x.x; x} catch (e) { e() }"; + String expected = "digraph AST {\n" + + " node [color=lightblue2, style=filled];\n" + + " node0 [label=\"SCRIPT\"];\n" + + " node1 [label=\"TRY\"];\n" + + " node0 -> node1 [weight=1];\n" + + " node2 [label=\"BLOCK\"];\n" + + " node1 -> node2 [weight=1];\n" + + " node3 [label=\"THROW\"];\n" + + " node2 -> node3 [weight=1];\n" + + " node4 [label=\"NAME\"];\n" + + " node3 -> node4 [weight=1];\n" + + " node5 [label=\"BLOCK\"];\n" + + " node3 -> node5 [label=\"ON_EX\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node6 [label=\"EXPR_RESULT\"];\n" + + " node2 -> node6 [weight=1];\n" + + " node7 [label=\"CALL\"];\n" + + " node6 -> node7 [weight=1];\n" + + " node8 [label=\"NAME\"];\n" + + " node7 -> node8 [weight=1];\n" + + " node9 [label=\"EXPR_RESULT\"];\n" + + " node6 -> node5 [label=\"ON_EX\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node6 -> node9 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node2 -> node9 [weight=1];\n" + + " node10 [label=\"GETELEM\"];\n" + + " node9 -> node10 [weight=1];\n" + + " node11 [label=\"NAME\"];\n" + + " node10 -> node11 [weight=1];\n" + + " node12 [label=\"STRING\"];\n" + + " node10 -> node12 [weight=1];\n" + + " node13 [label=\"EXPR_RESULT\"];\n" + + " node9 -> node13 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node9 -> node5 [label=\"ON_EX\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node2 -> node13 [weight=1];\n" + + " node14 [label=\"GETPROP\"];\n" + + " node13 -> node14 [weight=1];\n" + + " node15 [label=\"NAME\"];\n" + + " node14 -> node15 [weight=1];\n" + + " node16 [label=\"STRING\"];\n" + + " node14 -> node16 [weight=1];\n" + + " node17 [label=\"EXPR_RESULT\"];\n" + + " node13 -> node17 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node13 -> node5 [label=\"ON_EX\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node2 -> node17 [weight=1];\n" + + " node18 [label=\"NAME\"];\n" + + " node17 -> node18 [weight=1];\n" + + " node17 -> RETURN [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node2 -> node3 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node1 -> node5 [weight=1];\n" + + " node19 [label=\"CATCH\"];\n" + + " node5 -> node19 [weight=1];\n" + + " node20 [label=\"NAME\"];\n" + + " node19 -> node20 [weight=1];\n" + + " node21 [label=\"BLOCK\"];\n" + + " node19 -> node21 [weight=1];\n" + + " node22 [label=\"EXPR_RESULT\"];\n" + + " node21 -> node22 [weight=1];\n" + + " node23 [label=\"CALL\"];\n" + + " node22 -> node23 [weight=1];\n" + + " node24 [label=\"NAME\"];\n" + + " node23 -> node24 [weight=1];\n" + + " node22 -> RETURN [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node21 -> node22 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node19 -> node21 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node5 -> node19 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node1 -> node2 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> node1 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + "}\n"; + testCfg(src, expected); + } + + public void testFunctionWithinTry() { + // Make sure we don't search for the handler outside of the function. + String src = "try { function f() {throw 1;} } catch (e) { }"; + String expected = "digraph AST {\n" + + " node [color=lightblue2, style=filled];\n" + + " node0 [label=\"SCRIPT\"];\n" + + " node1 [label=\"TRY\"];\n" + + " node0 -> node1 [weight=1];\n" + + " node2 [label=\"BLOCK\"];\n" + + " node1 -> node2 [weight=1];\n" + + " node3 [label=\"FUNCTION\"];\n" + + " node2 -> node3 [weight=1];\n" + + " node4 [label=\"NAME\"];\n" + + " node3 -> node4 [weight=1];\n" + + " node5 [label=\"PARAM_LIST\"];\n" + + " node3 -> node5 [weight=1];\n" + + " node6 [label=\"BLOCK\"];\n" + + " node3 -> node6 [weight=1];\n" + + " node7 [label=\"THROW\"];\n" + + " node6 -> node7 [weight=1];\n" + + " node8 [label=\"NUMBER\"];\n" + + " node7 -> node8 [weight=1];\n" + + " node6 -> node7 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node3 -> node6 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node2 -> RETURN [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node9 [label=\"BLOCK\"];\n" + + " node1 -> node9 [weight=1];\n" + + " node10 [label=\"CATCH\"];\n" + + " node9 -> node10 [weight=1];\n" + + " node11 [label=\"NAME\"];\n" + + " node10 -> node11 [weight=1];\n" + + " node12 [label=\"BLOCK\"];\n" + + " node10 -> node12 [weight=1];\n" + + " node12 -> RETURN [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node10 -> node12 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node9 -> node10 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node1 -> node2 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> node1 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + "}\n"; + testCfg(src, expected); + } + + public void testNestedCatch() { + // Make sure we are going to the right handler. + String src = "try{try{throw 1;}catch(e){throw 2}}catch(f){}"; + String expected = "digraph AST {\n" + + " node [color=lightblue2, style=filled];\n" + + " node0 [label=\"SCRIPT\"];\n" + + " node1 [label=\"TRY\"];\n" + + " node0 -> node1 [weight=1];\n" + + " node2 [label=\"BLOCK\"];\n" + + " node1 -> node2 [weight=1];\n" + + " node3 [label=\"TRY\"];\n" + + " node2 -> node3 [weight=1];\n" + + " node4 [label=\"BLOCK\"];\n" + + " node3 -> node4 [weight=1];\n" + + " node5 [label=\"THROW\"];\n" + + " node4 -> node5 [weight=1];\n" + + " node6 [label=\"NUMBER\"];\n" + + " node5 -> node6 [weight=1];\n" + + " node7 [label=\"BLOCK\"];\n" + + " node5 -> node7 [label=\"ON_EX\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node4 -> node5 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node3 -> node7 [weight=1];\n" + + " node8 [label=\"CATCH\"];\n" + + " node7 -> node8 [weight=1];\n" + + " node9 [label=\"NAME\"];\n" + + " node8 -> node9 [weight=1];\n" + + " node10 [label=\"BLOCK\"];\n" + + " node8 -> node10 [weight=1];\n" + + " node11 [label=\"THROW\"];\n" + + " node10 -> node11 [weight=1];\n" + + " node12 [label=\"NUMBER\"];\n" + + " node11 -> node12 [weight=1];\n" + + " node13 [label=\"BLOCK\"];\n" + + " node11 -> node13 [label=\"ON_EX\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node10 -> node11 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node8 -> node10 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node7 -> node8 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node3 -> node4 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node2 -> node3 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node1 -> node13 [weight=1];\n" + + " node14 [label=\"CATCH\"];\n" + + " node13 -> node14 [weight=1];\n" + + " node15 [label=\"NAME\"];\n" + + " node14 -> node15 [weight=1];\n" + + " node16 [label=\"BLOCK\"];\n" + + " node14 -> node16 [weight=1];\n" + + " node16 -> RETURN [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node14 -> node16 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node13 -> node14 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node1 -> node2 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> node1 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + "}\n"; + testCfg(src, expected); + } + + public void testSimpleFinally() { + String src = "try{var x; foo()}finally{}"; + ControlFlowGraph cfg = createCfg(src); + assertDownEdge(cfg, Token.TRY, Token.BLOCK, Branch.UNCOND); + assertDownEdge(cfg, Token.BLOCK, Token.VAR, Branch.UNCOND); + // VAR to FINALLY. + assertCrossEdge(cfg, Token.EXPR_RESULT, Token.BLOCK, Branch.UNCOND); + // No CATCH to FINALLY. + assertNoEdge(cfg, Token.BLOCK, Token.BLOCK); + } + + public void testSimpleCatchFinally() { + // Make sure we are going to the right handler. + String src = "try{ if(a){throw 1}else{a} } catch(e){a}finally{a}"; + String expected = "digraph AST {\n" + + " node [color=lightblue2, style=filled];\n" + + " node0 [label=\"SCRIPT\"];\n" + + " node1 [label=\"TRY\"];\n" + + " node0 -> node1 [weight=1];\n" + + " node2 [label=\"BLOCK\"];\n" + + " node1 -> node2 [weight=1];\n" + + " node3 [label=\"IF\"];\n" + + " node2 -> node3 [weight=1];\n" + + " node4 [label=\"NAME\"];\n" + + " node3 -> node4 [weight=1];\n" + + " node5 [label=\"BLOCK\"];\n" + + " node3 -> node5 [weight=1];\n" + + " node6 [label=\"THROW\"];\n" + + " node5 -> node6 [weight=1];\n" + + " node7 [label=\"NUMBER\"];\n" + + " node6 -> node7 [weight=1];\n" + + " node8 [label=\"BLOCK\"];\n" + + " node6 -> node8 [label=\"ON_EX\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node5 -> node6 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node9 [label=\"BLOCK\"];\n" + + " node3 -> node9 [weight=1];\n" + + " node10 [label=\"EXPR_RESULT\"];\n" + + " node9 -> node10 [weight=1];\n" + + " node11 [label=\"NAME\"];\n" + + " node10 -> node11 [weight=1];\n" + + " node12 [label=\"BLOCK\"];\n" + + " node10 -> node12 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node9 -> node10 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node3 -> node5 [label=\"ON_TRUE\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node3 -> node9 [label=\"ON_FALSE\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node2 -> node3 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node1 -> node8 [weight=1];\n" + + " node13 [label=\"CATCH\"];\n" + + " node8 -> node13 [weight=1];\n" + + " node14 [label=\"NAME\"];\n" + + " node13 -> node14 [weight=1];\n" + + " node15 [label=\"BLOCK\"];\n" + + " node13 -> node15 [weight=1];\n" + + " node16 [label=\"EXPR_RESULT\"];\n" + + " node15 -> node16 [weight=1];\n" + + " node17 [label=\"NAME\"];\n" + + " node16 -> node17 [weight=1];\n" + + " node16 -> node12 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node15 -> node16 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node13 -> node15 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node8 -> node13 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node1 -> node12 [weight=1];\n" + + " node18 [label=\"EXPR_RESULT\"];\n" + + " node12 -> node18 [weight=1];\n" + + " node19 [label=\"NAME\"];\n" + + " node18 -> node19 [weight=1];\n" + + " node18 -> RETURN [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node12 -> node18 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node1 -> node2 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> node1 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + "}\n"; + testCfg(src, expected); + } + + public void testComplicatedFinally2() { + // Now the most nasty case..... + String src = "while(1){try{" + + "if(a){a;continue;}else if(b){b;break;} else if(c) throw 1; else a}" + + "catch(e){}finally{c()}bar}foo"; + + ControlFlowGraph cfg = createCfg(src); + // Focus only on the ON_EX edges. + assertCrossEdge(cfg, Token.CONTINUE, Token.BLOCK, Branch.UNCOND); + assertCrossEdge(cfg, Token.BREAK, Token.BLOCK, Branch.UNCOND); + assertCrossEdge(cfg, Token.THROW, Token.BLOCK, Branch.ON_EX); + } + + public void testDeepNestedBreakwithFinally() { + String src = "X:while(1){try{while(2){try{var a;break X;}" + + "finally{}}}finally{}}"; + ControlFlowGraph cfg = createCfg(src); + assertDownEdge(cfg, Token.WHILE, Token.BLOCK, Branch.ON_TRUE); + assertDownEdge(cfg, Token.BLOCK, Token.TRY, Branch.UNCOND); + assertDownEdge(cfg, Token.BLOCK, Token.VAR, Branch.UNCOND); + // BREAK to FINALLY. + assertCrossEdge(cfg, Token.BREAK, Token.BLOCK, Branch.UNCOND); + // FINALLY to FINALLY. + assertCrossEdge(cfg, Token.BLOCK, Token.BLOCK, Branch.ON_EX); + assertCrossEdge(cfg, Token.WHILE, Token.BLOCK, Branch.ON_FALSE); + assertReturnEdge(cfg, Token.BLOCK); + } + + public void testDeepNestedFinally() { + String src = "try{try{try{throw 1}" + + "finally{1;var a}}finally{2;if(a);}}finally{3;a()}"; + ControlFlowGraph cfg = createCfg(src); + assertCrossEdge(cfg, Token.THROW, Token.BLOCK, Branch.ON_EX); + assertCrossEdge(cfg, Token.VAR, Token.BLOCK, Branch.UNCOND); + assertCrossEdge(cfg, Token.IF, Token.BLOCK, Branch.ON_EX); + } + + public void testReturn() { + String src = "function f() { return; }"; + ControlFlowGraph cfg = createCfg(src); + assertReturnEdge(cfg, Token.RETURN); + } + + public void testReturnInFinally() { + String src = "function f(x){ try{} finally {return x;} }"; + ControlFlowGraph cfg = createCfg(src); + assertReturnEdge(cfg, Token.RETURN); + } + + public void testReturnInFinally2() { + String src = "function f(x){" + + " try{ try{}finally{var dummy; return x;} } finally {} }"; + ControlFlowGraph cfg = createCfg(src); + assertCrossEdge(cfg, Token.VAR, Token.RETURN, Branch.UNCOND); + assertCrossEdge(cfg, Token.RETURN, Token.BLOCK, Branch.UNCOND); + assertReturnEdge(cfg, Token.BLOCK); + assertNoReturnEdge(cfg, Token.RETURN); + } + + public void testReturnInTry() { + String src = "function f(x){ try{x; return x()} finally {} var y;}"; + ControlFlowGraph cfg = createCfg(src); + assertCrossEdge(cfg, Token.EXPR_RESULT, Token.RETURN, Branch.UNCOND); + assertCrossEdge(cfg, Token.RETURN, Token.BLOCK, Branch.UNCOND); + assertCrossEdge(cfg, Token.BLOCK, Token.VAR, Branch.UNCOND); + assertReturnEdge(cfg, Token.VAR); + assertReturnEdge(cfg, Token.BLOCK); + assertNoReturnEdge(cfg, Token.RETURN); + } + + public void testOptionNotToTraverseFunctions() { + String src = "var x = 1; function f() { x = null; }"; + String expectedWhenNotTraversingFunctions = "digraph AST {\n" + + " node [color=lightblue2, style=filled];\n" + + " node0 [label=\"SCRIPT\"];\n" + + " node1 [label=\"VAR\"];\n" + + " node0 -> node1 [weight=1];\n" + + " node2 [label=\"NAME\"];\n" + + " node1 -> node2 [weight=1];\n" + + " node3 [label=\"NUMBER\"];\n" + + " node2 -> node3 [weight=1];\n" + + " node1 -> RETURN " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node4 [label=\"FUNCTION\"];\n" + + " node0 -> node4 [weight=1];\n" + + " node5 [label=\"NAME\"];\n" + + " node4 -> node5 [weight=1];\n" + + " node6 [label=\"PARAM_LIST\"];\n" + + " node4 -> node6 [weight=1];\n" + + " node7 [label=\"BLOCK\"];\n" + + " node4 -> node7 [weight=1];\n" + + " node8 [label=\"EXPR_RESULT\"];\n" + + " node7 -> node8 [weight=1];\n" + + " node9 [label=\"ASSIGN\"];\n" + + " node8 -> node9 [weight=1];\n" + + " node10 [label=\"NAME\"];\n" + + " node9 -> node10 [weight=1];\n" + + " node11 [label=\"NULL\"];\n" + + " node9 -> node11 [weight=1];\n" + + " node0 -> node1 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + "}\n"; + String expected = "digraph AST {\n" + + " node [color=lightblue2, style=filled];\n" + + " node0 [label=\"SCRIPT\"];\n" + + " node1 [label=\"VAR\"];\n" + + " node0 -> node1 [weight=1];\n" + + " node2 [label=\"NAME\"];\n" + + " node1 -> node2 [weight=1];\n" + + " node3 [label=\"NUMBER\"];\n" + + " node2 -> node3 [weight=1];\n" + + " node1 -> RETURN " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node4 [label=\"FUNCTION\"];\n" + + " node0 -> node4 [weight=1];\n" + + " node5 [label=\"NAME\"];\n" + + " node4 -> node5 [weight=1];\n" + + " node6 [label=\"PARAM_LIST\"];\n" + + " node4 -> node6 [weight=1];\n" + + " node7 [label=\"BLOCK\"];\n" + + " node4 -> node7 [weight=1];\n" + + " node8 [label=\"EXPR_RESULT\"];\n" + + " node7 -> node8 [weight=1];\n" + + " node9 [label=\"ASSIGN\"];\n" + + " node8 -> node9 [weight=1];\n" + + " node10 [label=\"NAME\"];\n" + + " node9 -> node10 [weight=1];\n" + + " node11 [label=\"NULL\"];\n" + + " node9 -> node11 [weight=1];\n" + + " node8 -> RETURN " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node7 -> node8 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node4 -> node7 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> node1 " + + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + "}\n"; + testCfg(src, expected); + testCfg(src, expectedWhenNotTraversingFunctions, false); + } + + public void testInstanceOf() { + String src = "try { x instanceof 'x' } catch (e) { }"; + ControlFlowGraph cfg = createCfg(src, true); + assertCrossEdge(cfg, Token.EXPR_RESULT, Token.BLOCK, Branch.ON_EX); + } + + public void testSynBlock() { + String src = "START(); var x; END(); var y;"; + ControlFlowGraph cfg = createCfg(src, true); + assertCrossEdge(cfg, Token.BLOCK, Token.EXPR_RESULT, Branch.SYN_BLOCK); + } + + public void testPartialTraversalOfScope() { + Compiler compiler = new Compiler(); + ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, true, true); + + Node script1 = compiler.parseSyntheticCode("cfgtest", "var foo;"); + Node script2 = compiler.parseSyntheticCode("cfgtest2", "var bar;"); + Node root = new Node(Token.BLOCK, script1, script2); + + cfa.process(null, script1); + ControlFlowGraph cfg = cfa.getCfg(); + + assertNotNull(cfg.getNode(script1)); + assertNull(cfg.getNode(script2)); + } + + public void testForLoopOrder() { + assertNodeOrder( + createCfg("for (var i = 0; i < 5; i++) { var x = 3; } if (true) {}"), + Lists.newArrayList( + Token.SCRIPT, Token.VAR, Token.FOR, Token.BLOCK, Token.VAR, + Token.INC /* i++ */, + Token.IF, Token.BLOCK)); + } + + public void testLabelledForInLoopOrder() { + assertNodeOrder( + createCfg("var i = 0; var y = {}; " + + "label: for (var x in y) { " + + " if (x) { break label; } else { i++ } x(); }"), + Lists.newArrayList( + Token.SCRIPT, Token.VAR, Token.VAR, Token.NAME, + Token.FOR, Token.BLOCK, + Token.IF, Token.BLOCK, Token.BREAK, + Token.BLOCK, Token.EXPR_RESULT, Token.EXPR_RESULT)); + } + + public void testLocalFunctionOrder() { + ControlFlowGraph cfg = + createCfg("function f() { while (x) { x++; } } var x = 3;"); + assertNodeOrder( + cfg, + Lists.newArrayList( + Token.SCRIPT, Token.VAR, + + Token.FUNCTION, Token.BLOCK, + Token.WHILE, Token.BLOCK, Token.EXPR_RESULT)); + } + + public void testDoWhileOrder() { + assertNodeOrder( + createCfg("do { var x = 3; } while (true); void x;"), + Lists.newArrayList( + Token.SCRIPT, Token.BLOCK, Token.VAR, Token.DO, Token.EXPR_RESULT)); + } + + public void testBreakInFinally1() { + String src = + "f = function() {\n" + + " var action;\n" + + " a: {\n" + + " var proto = null;\n" + + " try {\n" + + " proto = new Proto\n" + + " } finally {\n" + + " action = proto;\n" + + " break a\n" + // Remove this... + " }\n" + + " }\n" + + " alert(action)\n" + // but not this. + "};"; + String expected = + "digraph AST {\n" + + " node [color=lightblue2, style=filled];\n" + + " node0 [label=\"SCRIPT\"];\n" + + " node1 [label=\"EXPR_RESULT\"];\n" + + " node0 -> node1 [weight=1];\n" + + " node2 [label=\"ASSIGN\"];\n" + + " node1 -> node2 [weight=1];\n" + + " node3 [label=\"NAME\"];\n" + + " node2 -> node3 [weight=1];\n" + + " node4 [label=\"FUNCTION\"];\n" + + " node2 -> node4 [weight=1];\n" + + " node5 [label=\"NAME\"];\n" + + " node4 -> node5 [weight=1];\n" + + " node6 [label=\"PARAM_LIST\"];\n" + + " node4 -> node6 [weight=1];\n" + + " node7 [label=\"BLOCK\"];\n" + + " node4 -> node7 [weight=1];\n" + + " node8 [label=\"VAR\"];\n" + + " node7 -> node8 [weight=1];\n" + + " node9 [label=\"NAME\"];\n" + + " node8 -> node9 [weight=1];\n" + + " node10 [label=\"LABEL\"];\n" + + " node7 -> node10 [weight=1];\n" + + " node11 [label=\"LABEL_NAME\"];\n" + + " node10 -> node11 [weight=1];\n" + + " node12 [label=\"BLOCK\"];\n" + + " node10 -> node12 [weight=1];\n" + + " node13 [label=\"VAR\"];\n" + + " node12 -> node13 [weight=1];\n" + + " node14 [label=\"NAME\"];\n" + + " node13 -> node14 [weight=1];\n" + + " node15 [label=\"NULL\"];\n" + + " node14 -> node15 [weight=1];\n" + + " node16 [label=\"TRY\"];\n" + + " node12 -> node16 [weight=1];\n" + + " node17 [label=\"BLOCK\"];\n" + + " node16 -> node17 [weight=1];\n" + + " node18 [label=\"EXPR_RESULT\"];\n" + + " node17 -> node18 [weight=1];\n" + + " node19 [label=\"ASSIGN\"];\n" + + " node18 -> node19 [weight=1];\n" + + " node20 [label=\"NAME\"];\n" + + " node19 -> node20 [weight=1];\n" + + " node21 [label=\"NEW\"];\n" + + " node19 -> node21 [weight=1];\n" + + " node22 [label=\"NAME\"];\n" + + " node21 -> node22 [weight=1];\n" + + " node23 [label=\"BLOCK\"];\n" + + " node16 -> node23 [weight=1];\n" + + " node24 [label=\"BLOCK\"];\n" + + " node16 -> node24 [weight=1];\n" + + " node25 [label=\"EXPR_RESULT\"];\n" + + " node24 -> node25 [weight=1];\n" + + " node26 [label=\"ASSIGN\"];\n" + + " node25 -> node26 [weight=1];\n" + + " node27 [label=\"NAME\"];\n" + + " node26 -> node27 [weight=1];\n" + + " node28 [label=\"NAME\"];\n" + + " node26 -> node28 [weight=1];\n" + + " node29 [label=\"BREAK\"];\n" + + " node24 -> node29 [weight=1];\n" + + " node30 [label=\"LABEL_NAME\"];\n" + + " node29 -> node30 [weight=1];\n" + + " node31 [label=\"EXPR_RESULT\"];\n" + + " node7 -> node31 [weight=1];\n" + + " node32 [label=\"CALL\"];\n" + + " node31 -> node32 [weight=1];\n" + + " node33 [label=\"NAME\"];\n" + + " node32 -> node33 [weight=1];\n" + + " node34 [label=\"NAME\"];\n" + + " node32 -> node34 [weight=1];\n" + + " node1 -> RETURN [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + " node0 -> node1 [label=\"UNCOND\", " + + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + + "}\n"; + testCfg(src, expected); + } + + public void testBreakInFinally2() { + String src = + "var action;\n" + + "a: {\n" + + " var proto = null;\n" + + " try {\n" + + " proto = new Proto\n" + + " } finally {\n" + + " action = proto;\n" + + " break a\n" + + " }\n" + + "}\n" + + "alert(action)\n"; + + ControlFlowGraph cfg = createCfg(src); + assertCrossEdge(cfg, Token.BREAK, Token.EXPR_RESULT, Branch.UNCOND); + assertNoEdge(cfg, Token.BREAK, Token.BLOCK); + } + + + /** + * Asserts the priority order of CFG nodes. + * + * Checks that the node type of the highest-priority node matches the + * first element of the list, the type of the second node matches the + * second element of the list, and so on. + * + * @param cfg The control flow graph. + * @param nodeTypes The expected node types, in order. + */ + private void assertNodeOrder(ControlFlowGraph cfg, + List nodeTypes) { + List> cfgNodes = + Lists.newArrayList(cfg.getDirectedGraphNodes()); + Collections.sort(cfgNodes, cfg.getOptionalNodeComparator(true)); + + // IMPLICIT RETURN must always be last. + Node implicitReturn = cfgNodes.remove(cfgNodes.size() - 1).getValue(); + assertNull(implicitReturn == null ? "null" : implicitReturn.toStringTree(), + implicitReturn); + + assertEquals("Wrong number of CFG nodes", + nodeTypes.size(), cfgNodes.size()); + for (int i = 0; i < cfgNodes.size(); i++) { + int expectedType = nodeTypes.get(i); + int actualType = cfgNodes.get(i).getValue().getType(); + assertEquals( + "node type mismatch at " + i + ".\n" + + "found : " + Token.name(actualType) + "\n" + + "required: " + Token.name(expectedType) + "\n", + expectedType, actualType); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ControlStructureCheckTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ControlStructureCheckTest.java new file mode 100644 index 0000000..f09e2cc --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ControlStructureCheckTest.java @@ -0,0 +1,147 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Test for the control structure verification. + * + */ +public class ControlStructureCheckTest extends CompilerTestCase { + // Rhino parse error message text + final String UNLABELED_BREAK = + "unlabelled break must be inside loop or switch"; + + final String UNEXPECTED_CONTINUE = "continue must be inside loop"; + + final String UNEXPECTED_LABLED_CONTINUE = + "continue can only use labeles of iteration statements"; + + final String UNDEFINED_LABEL = "undefined label"; + + @Override + public CompilerPass getProcessor(Compiler compiler) { + return new ControlStructureCheck(compiler); + } + + public void testWhile() { + assertNoError("while(1) { break; }"); + } + + public void testNextedWhile() { + assertNoError("while(1) { while(1) { break; } }"); + } + + public void testBreak() { + assertInvalidBreak("break;"); + } + + public void testContinue() { + assertInvalidContinue("continue;"); + } + + public void testBreakCrossFunction() { + assertInvalidBreak("while(1) { function f() { break; } }"); + } + + public void testBreakCrossFunctionInFor() { + assertInvalidBreak("while(1) {for(var f = function () { break; };;) {}}"); + } + + public void testContinueToSwitch() { + assertInvalidContinue("switch(1) {case(1): continue; }"); + } + + public void testContinueToSwitchWithNoCases() { + assertNoError("switch(1){}"); + } + + public void testContinueToSwitchWithTwoCases() { + assertInvalidContinue("switch(1){case(1):break;case(2):continue;}"); + } + + public void testContinueToSwitchWithDefault() { + assertInvalidContinue("switch(1){case(1):break;case(2):default:continue;}"); + } + + public void testContinueToLabelSwitch() { + assertInvalidLabeledContinue( + "while(1) {a: switch(1) {case(1): continue a; }}"); + } + + public void testContinueOutsideSwitch() { + assertNoError("b: while(1) { a: switch(1) { case(1): continue b; } }"); + } + + public void testContinueNotCrossFunction1() { + assertNoError("a:switch(1){case(1):function f(){a:while(1){continue a;}}}"); + } + + public void testContinueNotCrossFunction2() { + assertUndefinedLabel( + "a:switch(1){case(1):function f(){while(1){continue a;}}}"); + } + + public void testUseOfWith1() { + testSame("with(a){}", ControlStructureCheck.USE_OF_WITH); + } + + public void testUseOfWith2() { + testSame("/** @suppress {with} */" + + "with(a){}"); + } + + public void testUseOfWith3() { + testSame( + "function f(expr, context) {\n" + + " try {\n" + + " /** @suppress{with} */ with (context) {\n" + + " return eval('[' + expr + '][0]');\n" + + " }\n" + + " } catch (e) {\n" + + " return null;\n" + + " }\n" + + "};\n"); + } + + private void assertNoError(String js) { + testSame(js); + } + + private void assertInvalidBreak(String js) { + testParseError(js, UNLABELED_BREAK); + } + + private void assertInvalidContinue(String js) { + testParseError(js, UNEXPECTED_CONTINUE); + } + + private void assertInvalidLabeledContinue(String js) { + testParseError(js, UNEXPECTED_LABLED_CONTINUE); + } + + private void assertUndefinedLabel(String js) { + testParseError(js, UNDEFINED_LABEL); + } + + private void testParseError(String js, String errorText) { + Compiler compiler = new Compiler(); + compiler.parseTestCode(js); + assertTrue(compiler.getErrorCount() == 1); + String msg = compiler.getErrors()[0].toString(); + assertTrue(msg.contains(errorText)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ConvertToDottedPropertiesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ConvertToDottedPropertiesTest.java new file mode 100644 index 0000000..e3055d3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ConvertToDottedPropertiesTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Tests for {@link ConvertToDottedProperties}. + * + */ +public class ConvertToDottedPropertiesTest extends CompilerTestCase { + @Override public CompilerPass getProcessor(Compiler compiler) { + return new ConvertToDottedProperties(compiler); + } + + public void testConvert() { + test("a['p']", "a.p"); + test("a['_p_']", "a._p_"); + test("a['_']", "a._"); + test("a['$']", "a.$"); + test("a.b.c['p']", "a.b.c.p"); + test("a.b['c'].p", "a.b.c.p"); + test("a['p']();", "a.p();"); + test("a()['p']", "a().p"); + // ASCII in Unicode is safe. + test("a['\u0041A']", "a.AA"); + } + + public void testDoNotConvert() { + testSame("a[0]"); + testSame("a['']"); + testSame("a[' ']"); + testSame("a[',']"); + testSame("a[';']"); + testSame("a[':']"); + testSame("a['.']"); + testSame("a['0']"); + testSame("a['p ']"); + testSame("a['p' + '']"); + testSame("a[p]"); + testSame("a[P]"); + testSame("a[$]"); + testSame("a[p()]"); + testSame("a['default']"); + // upper case lower half of o from phonetic extensions set. + // valid in Safari, not in Firefox, IE. + test("a['\u1d17A']", "a['\u1d17A']"); + // Latin capital N with tilde - nice if we handled it, but for now let's + // only allow simple Latin (aka ASCII) to be converted. + test("a['\u00d1StuffAfter']", "a['\u00d1StuffAfter']"); + } + + + public void testQuotedProps() { + testSame("({'':0})"); + testSame("({'1.0':0})"); + testSame("({'\u1d17A':0})"); + } + + public void test5746867() { + testSame("var a = { '$\\\\' : 5 };"); + testSame("var a = { 'x\\\\u0041$\\\\' : 5 };"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CreateSyntheticBlocksTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CreateSyntheticBlocksTest.java new file mode 100644 index 0000000..640851e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CreateSyntheticBlocksTest.java @@ -0,0 +1,129 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + * Tests for {@link CreateSyntheticBlocks} + * + */ +public class CreateSyntheticBlocksTest extends CompilerTestCase { + private static final String START_MARKER = "startMarker"; + private static final String END_MARKER = "endMarker"; + + public CreateSyntheticBlocksTest() { + // Can't use compare as a tree because of the added synthetic blocks. + super("", false); + } + + @Override + public void setUp() { + super.enableLineNumberCheck(false); + } + + @Override + protected CompilerPass getProcessor(final Compiler compiler) { + return new CompilerPass() { + @Override + public void process(Node externs, Node js) { + new CreateSyntheticBlocks(compiler, START_MARKER, END_MARKER).process( + externs, js); + NodeTraversal.traverse(compiler, js, new MinimizeExitPoints(compiler)); + + new PeepholeOptimizationsPass(compiler, + new PeepholeRemoveDeadCode(), + new PeepholeSubstituteAlternateSyntax(true), + new PeepholeFoldConstants(true)) + .process(externs, js); + new MinimizeExitPoints(compiler).process(externs, js); + + new Denormalize(compiler).process(externs, js); + } + }; + } + + @Override + protected int getNumRepetitions() { + return 1; + } + + // TODO(johnlenz): Add tests to the IntegrationTest. + + public void testFold1() { + test("function f() { if (x) return; y(); }", + "function f(){x||y()}"); + } + + public void testFoldWithMarkers1() { + testSame("function f(){startMarker();if(x)return;endMarker();y()}"); + } + + public void testFoldWithMarkers1a() { + testSame("function f(){startMarker();if(x)return;endMarker()}"); + } + + public void testFold2() { + test("function f() { if (x) return; y(); if (a) return; b(); }", + "function f(){if(!x){y();a||b()}}"); + } + + public void testFoldWithMarkers2() { + testSame("function f(){startMarker(\"FOO\");startMarker(\"BAR\");" + + "if(x)return;endMarker(\"BAR\");y();if(a)return;" + + "endMarker(\"FOO\");b()}"); + } + + public void testUnmatchedStartMarker() { + testSame("startMarker()", CreateSyntheticBlocks.UNMATCHED_START_MARKER); + } + + public void testUnmatchedEndMarker1() { + testSame("endMarker()", CreateSyntheticBlocks.UNMATCHED_END_MARKER); + } + + public void testUnmatchedEndMarker2() { + test("if(y){startMarker();x()}endMarker()", + "if(y){startMarker();x()}endMarker()", null, + CreateSyntheticBlocks.UNMATCHED_END_MARKER); + } + + public void testInvalid1() { + test("startMarker() && true", + "startMarker()", null, + CreateSyntheticBlocks.INVALID_MARKER_USAGE); + } + + public void testInvalid2() { + test("false && endMarker()", + "", null, + CreateSyntheticBlocks.INVALID_MARKER_USAGE); + } + + + public void testDenormalize() { + testSame("startMarker();for(;;);endMarker()"); + } + + public void testNonMarkingUse() { + testSame("function foo(endMarker){}"); + testSame("function foo(){startMarker:foo()}"); + } + + public void testContainingBlockPreservation() { + testSame("if(y){startMarker();x();endMarker()}"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CrossModuleCodeMotionTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CrossModuleCodeMotionTest.java new file mode 100755 index 0000000..8732f82 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CrossModuleCodeMotionTest.java @@ -0,0 +1,780 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Tests for {@link CrossModuleCodeMotion}. + * + */ +public class CrossModuleCodeMotionTest extends CompilerTestCase { + + private static final String EXTERNS = "alert"; + + public CrossModuleCodeMotionTest() { + super(EXTERNS); + } + + @Override + public void setUp() { + super.enableLineNumberCheck(true); + } + + @Override + public CompilerPass getProcessor(Compiler compiler) { + return new CrossModuleCodeMotion(compiler, compiler.getModuleGraph()); + } + + public void testFunctionMovement1() { + // This tests lots of things: + // 1) f1 is declared in m1, and used in m2. Move it to m2 + // 2) f2 is declared in m1, and used in m3 twice. Move it to m3 + // 3) f3 is declared in m1, and used in m2+m3. It stays put + // 4) g declared in m1 and never used. It stays put + // 5) h declared in m2 and never used. It stays put + // 6) f4 declared in m1 and used in m2 as var. It moves to m2 + + JSModule[] modules = createModuleStar( + // m1 + "function f1(a) { alert(a); }" + + "function f2(a) { alert(a); }" + + "function f3(a) { alert(a); }" + + "function f4() { alert(1); }" + + "function g() { alert('ciao'); }", + // m2 + "f1('hi'); f3('bye'); var a = f4;" + + "function h(a) { alert('h:' + a); }", + // m3 + "f2('hi'); f2('hi'); f3('bye');"); + + test(modules, new String[] { + // m1 + "function f3(a) { alert(a); }" + + "function g() { alert('ciao'); }", + // m2 + "function f4() { alert(1); }" + + "function f1(a) { alert(a); }" + + "f1('hi'); f3('bye'); var a = f4;" + + "function h(a) { alert('h:' + a); }", + // m3 + "function f2(a) { alert(a); }" + + "f2('hi'); f2('hi'); f3('bye');", + }); + } + + public void testFunctionMovement2() { + // having f declared as a local variable should block the migration to m2 + JSModule[] modules = createModuleStar( + // m1 + "function f(a) { alert(a); }" + + "function g() {var f = 1; f++}", + // m2 + "f(1);"); + + test(modules, new String[] { + // m1 + "function g() {var f = 1; f++}", + // m2 + "function f(a) { alert(a); }" + + "f(1);", + }); + } + + public void testFunctionMovement3() { + // having f declared as a arg should block the migration to m2 + JSModule[] modules = createModuleStar( + // m1 + "function f(a) { alert(a); }" + + "function g(f) {f++}", + // m2 + "f(1);"); + + test(modules, new String[] { + // m1 + "function g(f) {f++}", + // m2 + "function f(a) { alert(a); }" + + "f(1);", + }); + } + + public void testFunctionMovement4() { + // Try out moving a function which returns a closure + JSModule[] modules = createModuleStar( + // m1 + "function f(){return function(a){}}", + // m2 + "var a = f();" + ); + + test(modules, new String[] { + // m1 + "", + // m2 + "function f(){return function(a){}}" + + "var a = f();", + }); + } + + public void testFunctionMovement5() { + // Try moving a recursive function [using factorials for kicks] + JSModule[] modules = createModuleStar( + // m1 + "function f(n){return (n<1)?1:f(n-1)}", + // m2 + "var a = f(4);" + ); + + test(modules, new String[] { + // m1 + "", + // m2 + "function f(n){return (n<1)?1:f(n-1)}" + + "var a = f(4);", + }); + } + + public void testFunctionMovement5b() { + // Try moving a recursive function declared differently. + JSModule[] modules = createModuleStar( + // m1 + "var f = function(n){return (n<1)?1:f(n-1)};", + // m2 + "var a = f(4);" + ); + + test(modules, new String[] { + // m1 + "", + // m2 + "var f = function(n){return (n<1)?1:f(n-1)};" + + "var a = f(4);", + }); + } + + public void testFunctionMovement6() { + // Try out moving to the common ancestor + JSModule[] modules = createModuleChain( + // m1 + "function f(){return 1}", + // m2 + "var a = f();", + // m3 + "var b = f();" + ); + + test(modules, new String[] { + // m1 + "", + // m2 + "function f(){return 1}" + + "var a = f();", + // m3 + "var b = f();", + }); + } + + public void testFunctionMovement7() { + // Try out moving to the common ancestor with deeper ancestry chain + JSModule[] modules = createModules( + // m1 + "function f(){return 1}", + // m2 + "", + // m3 + "var a = f();", + // m4 + "var b = f();", + // m5 + "var c = f();" + ); + + + modules[1].addDependency(modules[0]); + modules[2].addDependency(modules[1]); + modules[3].addDependency(modules[1]); + modules[4].addDependency(modules[1]); + + test(modules, new String[] { + // m1 + "", + // m2 + "function f(){return 1}", + // m3 + "var a = f();", + // m4 + "var b = f();", + // m5 + "var c = f();", + }); + } + + public void testFunctionMovement8() { + // Check what happens with named functions + JSModule[] modules = createModuleChain( + // m1 + "var v = function f(){return 1}", + // m2 + "v();" + ); + + test(modules, new String[] { + // m1 + "", + // m2 + "var v = function f(){return 1};" + + "v();", + }); + } + + public void testFunctionNonMovement1() { + // This tests lots of things: + // 1) we can't move it if it is a class with non-const attributes accessed + // 2) if it's in an if statement, we can't move it + // 3) if it's in an while statement, we can't move it [with some extra + // block elements] + testSame(createModuleStar( + // m1 + "function f(){};f.prototype.bar=new f;" + + "if(a)function f2(){}" + + "{{while(a)function f3(){}}}", + // m2 + "var a = new f();f2();f3();")); + } + + public void testFunctionNonMovement2() { + // A generic case where 2 modules depend on the first one. But it's the + // common ancestor, so we can't move. + testSame(createModuleStar( + // m1 + "function f(){return 1}", + // m2 + "var a = f();", + // m3 + "var b = f();")); + } + + public void testClassMovement1() { + test(createModuleStar( + // m1 + "function f(){} f.prototype.bar=function (){};", + // m2 + "var a = new f();"), + new String[] { + "", + "function f(){} f.prototype.bar=function (){};" + + "var a = new f();" + }); + } + + public void testClassMovement2() { + // NOTE: this is the result of two iterations + test(createModuleChain( + // m1 + "function f(){} f.prototype.bar=3; f.prototype.baz=5;", + // m2 + "f.prototype.baq = 7;", + // m3 + "f.prototype.baz = 9;", + // m4 + "var a = new f();"), + new String[] { + // m1 + "", + // m2 + "", + // m3 + "function f(){} f.prototype.bar=3; f.prototype.baz=5;" + + "f.prototype.baq = 7;" + + "f.prototype.baz = 9;", + // m4 + "var a = new f();" + }); + } + + public void testClassMovement3() { + // NOTE: this is the result of two iterations + test(createModuleChain( + // m1 + "var f = function() {}; f.prototype.bar=3; f.prototype.baz=5;", + // m2 + "f = 7;", + // m3 + "f = 9;", + // m4 + "f = 11;"), + new String[] { + // m1 + "", + // m2 + "", + // m3 + "var f = function() {}; f.prototype.bar=3; f.prototype.baz=5;" + + "f = 7;" + + "f = 9;", + // m4 + "f = 11;" + }); + } + + public void testClassMovement4() { + testSame(createModuleStar( + // m1 + "function f(){} f.prototype.bar=3; f.prototype.baz=5;", + // m2 + "f.prototype.baq = 7;", + // m3 + "var a = new f();")); + } + + public void testClassMovement5() { + JSModule[] modules = createModules( + // m1 + "function f(){} f.prototype.bar=3; f.prototype.baz=5;", + // m2 + "", + // m3 + "f.prototype.baq = 7;", + // m4 + "var a = new f();"); + + modules[1].addDependency(modules[0]); + modules[2].addDependency(modules[1]); + modules[3].addDependency(modules[1]); + + test(modules, + new String[] { + // m1 + "", + // m2 + "function f(){} f.prototype.bar=3; f.prototype.baz=5;", + // m3 + "f.prototype.baq = 7;", + // m4 + + "var a = new f();" + }); + } + + public void testClassMovement6() { + test(createModuleChain( + // m1 + "function Foo(){} function Bar(){} goog.inherits(Bar, Foo);" + + "new Foo();", + // m2 + "new Bar();"), + new String[] { + // m1 + "function Foo(){} new Foo();", + // m2 + "function Bar(){} goog.inherits(Bar, Foo); new Bar();" + }); + } + + public void testClassMovement7() { + testSame(createModuleChain( + // m1 + "function Foo(){} function Bar(){} goog.inherits(Bar, Foo);" + + "new Bar();", + // m2 + "new Foo();")); + } + + public void testStubMethodMovement1() { + test(createModuleChain( + // m1 + "function Foo(){} " + + "Foo.prototype.bar = JSCompiler_stubMethod(x);", + // m2 + "new Foo();"), + new String[] { + // m1 + "", + "function Foo(){} " + + "Foo.prototype.bar = JSCompiler_stubMethod(x);" + + "new Foo();" + }); + } + + public void testStubMethodMovement2() { + test(createModuleChain( + // m1 + "function Foo(){} " + + "Foo.prototype.bar = JSCompiler_unstubMethod(x);", + // m2 + "new Foo();"), + new String[] { + // m1 + "", + "function Foo(){} " + + "Foo.prototype.bar = JSCompiler_unstubMethod(x);" + + "new Foo();" + }); + } + + public void testNoMoveSideEffectProperty() { + testSame(createModuleChain( + // m1 + "function Foo(){} " + + "Foo.prototype.bar = createSomething();", + // m2 + "new Foo();")); + } + + public void testAssignMovement() { + test(createModuleChain( + // m1 + "var f = 3;" + + "f = 5;", + // m2 + "var h = f;"), + new String[] { + // m1 + "", + // m2 + "var f = 3;" + + "f = 5;" + + "var h = f;" + }); + + // don't move nested assigns + testSame(createModuleChain( + // m1 + "var f = 3;" + + "var g = f = 5;", + // m2 + "var h = f;")); + } + + public void testNoClassMovement2() { + test(createModuleChain( + // m1 + "var f = {};" + + "f.h = 5;", + // m2 + "var h = f;"), + new String[] { + // m1 + "", + // m2 + "var f = {};" + + "f.h = 5;" + + "var h = f;" + }); + + // don't move nested getprop assigns + testSame(createModuleChain( + // m1 + "var f = {};" + + "var g = f.h = 5;", + // m2 + "var h = f;")); + } + + public void testLiteralMovement1() { + test(createModuleChain( + // m1 + "var f = {'hi': 'mom', 'bye': function() {}};", + // m2 + "var h = f;"), + new String[] { + // m1 + "", + // m2 + "var f = {'hi': 'mom', 'bye': function() {}};" + + "var h = f;" + }); + } + + public void testLiteralMovement2() { + testSame(createModuleChain( + // m1 + "var f = {'hi': 'mom', 'bye': goog.nullFunction};", + // m2 + "var h = f;")); + } + + public void testLiteralMovement3() { + test(createModuleChain( + // m1 + "var f = ['hi', function() {}];", + // m2 + "var h = f;"), + new String[] { + // m1 + "", + // m2 + "var f = ['hi', function() {}];" + + "var h = f;" + }); + } + + public void testLiteralMovement4() { + testSame(createModuleChain( + // m1 + "var f = ['hi', goog.nullFunction];", + // m2 + "var h = f;")); + } + + public void testVarMovement1() { + // test moving a variable + JSModule[] modules = createModuleStar( + // m1 + "var a = 0;", + // m2 + "var x = a;" + ); + + test(modules, new String[] { + // m1 + "", + // m2 + "var a = 0;" + + "var x = a;", + }); + } + + public void testVarMovement2() { + // Test moving 1 variable out of the block + JSModule[] modules = createModuleStar( + // m1 + "var a = 0; var b = 1; var c = 2;", + // m2 + "var x = b;" + ); + + test(modules, new String[] { + // m1 + "var a = 0; var c = 2;", + // m2 + "var b = 1;" + + "var x = b;" + }); + } + + public void testVarMovement3() { + // Test moving all variables out of the block + JSModule[] modules = createModuleStar( + // m1 + "var a = 0; var b = 1;", + // m2 + "var x = a + b;" + ); + + test(modules, new String[] { + // m1 + "", + // m2 + "var b = 1;" + + "var a = 0;" + + "var x = a + b;" + }); + } + + + public void testVarMovement4() { + // Test moving a function + JSModule[] modules = createModuleStar( + // m1 + "var a = function(){alert(1)};", + // m2 + "var x = a;" + ); + + test(modules, new String[] { + // m1 + "", + // m2 + "var a = function(){alert(1)};" + + "var x = a;" + }); + } + + + public void testVarMovement5() { + // Don't move a function outside of scope + testSame(createModuleStar( + // m1 + "var a = alert;", + // m2 + "var x = a;")); + } + + public void testVarMovement6() { + // Test moving a var with no assigned value + JSModule[] modules = createModuleStar( + // m1 + "var a;", + // m2 + "var x = a;" + ); + + test(modules, new String[] { + // m1 + "", + // m2 + "var a;" + + "var x = a;" + }); + } + + public void testVarMovement7() { + // Don't move a variable higher in the dependency tree + testSame(createModuleStar( + // m1 + "function f() {g();}", + // m2 + "function g(){};")); + } + + public void testVarMovement8() { + JSModule[] modules = createModuleBush( + // m1 + "var a = 0;", + // m2 -> m1 + "", + // m3 -> m2 + "var x = a;", + // m4 -> m2 + "var y = a;" + ); + + test(modules, new String[] { + // m1 + "", + // m2 + "var a = 0;", + // m3 + "var x = a;", + // m4 + "var y = a;" + }); + } + + public void testVarMovement9() { + JSModule[] modules = createModuleTree( + // m1 + "var a = 0; var b = 1; var c = 3;", + // m2 -> m1 + "", + // m3 -> m1 + "", + // m4 -> m2 + "a;", + // m5 -> m2 + "a;c;", + // m6 -> m3 + "b;", + // m7 -> m4 + "b;c;" + ); + + test(modules, new String[] { + // m1 + "var c = 3;", + // m2 + "var a = 0;", + // m3 + "var b = 1;", + // m4 + "a;", + // m5 + "a;c;", + // m6 + "b;", + // m7 + "b;c;" + }); + } + + public void testClone1() { + test(createModuleChain( + // m1 + "function f(){} f.prototype.clone = function() { return new f };", + // m2 + "var a = (new f).clone();"), + new String[] { + // m1 + "", + "function f(){} f.prototype.clone = function() { return new f() };" + + // m2 + "var a = (new f).clone();" + }); + } + + public void testClone2() { + test(createModuleChain( + // m1 + "function f(){}" + + "f.prototype.cloneFun = function() {" + + " return function() {new f}" + + "};", + // m2 + "var a = (new f).cloneFun();"), + new String[] { + // m1 + "", + "function f(){}" + + "f.prototype.cloneFun = function() {" + + " return function() {new f}" + + "};" + + // m2 + "var a = (new f).cloneFun();" + }); + } + + public void testBug4118005() { + testSame(createModuleChain( + // m1 + "var m = 1;\n" + + "(function () {\n" + + " var x = 1;\n" + + " m = function() { return x };\n" + + "})();\n", + // m2 + "m();")); + } + + public void testEmptyModule() { + // When the dest module is empty, it might try to move the code to the + // one of the modules that the empty module depends on. In some cases + // this might ended up to be the same module as the definition of the code. + // When that happens, CrossModuleCodeMotion might report a code change + // while nothing is moved. This should not be a problem if we know all + // modules are non-empty. + JSModule m1 = new JSModule("m1"); + m1.add(SourceFile.fromCode("m1", "function x() {}")); + + JSModule empty = new JSModule("empty"); + empty.addDependency(m1); + + JSModule m2 = new JSModule("m2"); + m2.add(SourceFile.fromCode("m2", "x()")); + m2.addDependency(empty); + + JSModule m3 = new JSModule("m3"); + m3.add(SourceFile.fromCode("m3", "x()")); + m3.addDependency(empty); + + test(new JSModule[] {m1,empty,m2,m3}, + new String[] { + "", + "function x() {}", + "x()", + "x()" + }); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CrossModuleMethodMotionTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CrossModuleMethodMotionTest.java new file mode 100644 index 0000000..fba76a8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CrossModuleMethodMotionTest.java @@ -0,0 +1,565 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Tests for {@link CrossModuleMethodMotion}. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class CrossModuleMethodMotionTest extends CompilerTestCase { + private static final String EXTERNS = + "IFoo.prototype.bar; var mExtern; mExtern.bExtern; mExtern['cExtern'];"; + + private boolean canMoveExterns = false; + + private final String STUB_DECLARATIONS = + CrossModuleMethodMotion.STUB_DECLARATIONS; + + public CrossModuleMethodMotionTest() { + super(EXTERNS); + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new CrossModuleMethodMotion( + compiler, new CrossModuleMethodMotion.IdGenerator(), canMoveExterns); + } + + @Override + public void setUp() { + super.enableLineNumberCheck(true); + canMoveExterns = false; + } + + public void testMovePrototypeMethod1() { + testSame(createModuleChain( + "function Foo() {}" + + "Foo.prototype.bar = function() {};", + // Module 2 + "(new Foo).bar()")); + + canMoveExterns = true; + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.bar = function() {};", + // Module 2 + "(new Foo).bar()"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.bar = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.bar = JSCompiler_unstubMethod(0, function() {});" + + "(new Foo).bar()" + }); + } + + public void testMovePrototypeMethod2() { + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype = { method: function() {} };", + // Module 2 + "(new Foo).method()"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype = { method: JSCompiler_stubMethod(0) };", + // Module 2 + "Foo.prototype.method = " + + " JSCompiler_unstubMethod(0, function() {});" + + "(new Foo).method()" + }); + } + + public void testMovePrototypeMethod3() { + testSame(createModuleChain( + "function Foo() {}" + + "Foo.prototype = { get method() {} };", + // Module 2 + "(new Foo).method()")); + } + + public void testMovePrototypeRecursiveMethod() { + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.baz = function() { this.baz(); };", + // Module 2 + "(new Foo).baz()"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.baz = JSCompiler_unstubMethod(0, " + + " function() { this.baz(); });" + + "(new Foo).baz()" + }); + } + + public void testCantMovePrototypeProp() { + testSame(createModuleChain( + "function Foo() {}" + + "Foo.prototype.baz = goog.nullFunction;", + // Module 2 + "(new Foo).baz()")); + } + + public void testMoveMethodsInRightOrder() { + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.baz = function() { return 1; };" + + "Foo.prototype.baz = function() { return 2; };", + // Module 2 + "(new Foo).baz()"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(1);" + + "Foo.prototype.baz = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.baz = " + + "JSCompiler_unstubMethod(1, function() { return 1; });" + + "Foo.prototype.baz = " + + "JSCompiler_unstubMethod(0, function() { return 2; });" + + "(new Foo).baz()" + }); + } + + public void testMoveMethodsInRightOrder2() { + JSModule[] m = createModules( + "function Foo() {}" + + "Foo.prototype.baz = function() { return 1; };" + + "function Goo() {}" + + "Goo.prototype.baz = function() { return 2; };", + // Module 2, depends on 1 + "", + // Module 3, depends on 2 + "(new Foo).baz()", + // Module 4, depends on 3 + "", + // Module 5, depends on 3 + "(new Goo).baz()"); + + m[1].addDependency(m[0]); + m[2].addDependency(m[1]); + m[3].addDependency(m[2]); + m[4].addDependency(m[2]); + + test(m, + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(1);" + + "function Goo() {}" + + "Goo.prototype.baz = JSCompiler_stubMethod(0);", + // Module 2 + "", + // Module 3 + "Foo.prototype.baz = " + + "JSCompiler_unstubMethod(1, function() { return 1; });" + + "Goo.prototype.baz = " + + "JSCompiler_unstubMethod(0, function() { return 2; });" + + "(new Foo).baz()", + // Module 4 + "", + // Module 5 + "(new Goo).baz()" + }); + } + + public void testMoveMethodsUsedInTwoModules() { + testSame(createModuleStar( + "function Foo() {}" + + "Foo.prototype.baz = function() {};", + // Module 2 + "(new Foo).baz()", + // Module 3 + "(new Foo).baz()")); + } + + public void testMoveMethodsUsedInTwoModules2() { + JSModule[] modules = createModules( + "function Foo() {}" + + "Foo.prototype.baz = function() {};", + // Module 2 + "", // a blank module in the middle + // Module 3 + "(new Foo).baz() + 1", + // Module 4 + "(new Foo).baz() + 2"); + + modules[1].addDependency(modules[0]); + modules[2].addDependency(modules[1]); + modules[3].addDependency(modules[1]); + test(modules, + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.baz = JSCompiler_unstubMethod(0, function() {});", + // Module 3 + "(new Foo).baz() + 1", + // Module 4 + "(new Foo).baz() + 2" + }); + } + + public void testTwoMethods() {} +// Defects4J: flaky method +// public void testTwoMethods() { +// test(createModuleChain( +// "function Foo() {}" + +// "Foo.prototype.baz = function() {};", +// // Module 2 +// "Foo.prototype.callBaz = function() { this.baz(); }", +// // Module 3 +// "(new Foo).callBaz()"), +// new String[] { +// STUB_DECLARATIONS + +// "function Foo() {}" + +// "Foo.prototype.baz = JSCompiler_stubMethod(1);", +// // Module 2 +// "Foo.prototype.callBaz = JSCompiler_stubMethod(0);", +// // Module 3 +// "Foo.prototype.baz = JSCompiler_unstubMethod(1, function() {});" + +// "Foo.prototype.callBaz = " + +// " JSCompiler_unstubMethod(0, function() { this.baz(); });" + +// "(new Foo).callBaz()" +// }); +// } + + public void testTwoMethods2() { + // if the programmer screws up the module order, we don't try to correct + // the mistake. + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.baz = function() {};", + // Module 2 + "(new Foo).callBaz()", + // Module 3 + "Foo.prototype.callBaz = function() { this.baz(); }"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(0);", + // Module 2 + "(new Foo).callBaz()", + // Module 3 + "Foo.prototype.baz = JSCompiler_unstubMethod(0, function() {});" + + "Foo.prototype.callBaz = function() { this.baz(); };" + }); + } + + public void testGlobalFunctionsInGraph() { + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.baz = function() {};" + + "function x() { return (new Foo).baz(); }", + // Module 2 + "x();"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(0);" + + "function x() { return (new Foo).baz(); }", + // Module 2 + "Foo.prototype.baz = JSCompiler_unstubMethod(0, function() {});" + + "x();" + }); + } + + // Read of closure variable disables method motions. + public void testClosureVariableReads1() { + testSame(createModuleChain( + "function Foo() {}" + + "(function() {" + + "var x = 'x';" + + "Foo.prototype.baz = function() {x};" + + "})();", + // Module 2 + "var y = new Foo(); y.baz();")); + } + + // Read of global variable is fine. + public void testClosureVariableReads2() { + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.b1 = function() {" + + " var x = 1;" + + " Foo.prototype.b2 = function() {" + + " Foo.prototype.b3 = function() {" + + " x;" + + " }" + + " }" + + "};", + // Module 2 + "var y = new Foo(); y.b1();", + // Module 3 + "y = new Foo(); z.b2();", + // Module 4 + "y = new Foo(); z.b3();" + ), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.b1 = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.b1 = JSCompiler_unstubMethod(0, function() {" + + " var x = 1;" + + " Foo.prototype.b2 = function() {" + + " Foo.prototype.b3 = function() {" + + " x;" + + " }" + + " }" + + "});" + + "var y = new Foo(); y.b1();", + // Module 3 + "y = new Foo(); z.b2();", + // Module 4 + "y = new Foo(); z.b3();" + }); + } + + public void testClosureVariableReads3() {} +// Defects4J: flaky method +// public void testClosureVariableReads3() { +// test(createModuleChain( +// "function Foo() {}" + +// "Foo.prototype.b1 = function() {" + +// " Foo.prototype.b2 = function() {" + +// " var x = 1;" + +// " Foo.prototype.b3 = function() {" + +// " x;" + +// " }" + +// " }" + +// "};", +// // Module 2 +// "var y = new Foo(); y.b1();", +// // Module 3 +// "y = new Foo(); z.b2();", +// // Module 4 +// "y = new Foo(); z.b3();" +// ), +// new String[] { +// STUB_DECLARATIONS + +// "function Foo() {}" + +// "Foo.prototype.b1 = JSCompiler_stubMethod(0);", +// // Module 2 +// "Foo.prototype.b1 = JSCompiler_unstubMethod(0, function() {" + +// " Foo.prototype.b2 = JSCompiler_stubMethod(1);" + +// "});" + +// "var y = new Foo(); y.b1();", +// // Module 3 +// "Foo.prototype.b2 = JSCompiler_unstubMethod(1, function() {" + +// " var x = 1;" + +// " Foo.prototype.b3 = function() {" + +// " x;" + +// " }" + +// "});" + +// "y = new Foo(); z.b2();", +// // Module 4 +// "y = new Foo(); z.b3();" +// }); +// } + + // Read of global variable is fine. + public void testNoClosureVariableReads1() { + test(createModuleChain( + "function Foo() {}" + + "var x = 'x';" + + "Foo.prototype.baz = function(){x};", + // Module 2 + "var y = new Foo(); y.baz();"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "var x = 'x';" + + "Foo.prototype.baz = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.baz = JSCompiler_unstubMethod(0, function(){x});" + + "var y = new Foo(); y.baz();" + }); + } + + // Read of a local is fine. + public void testNoClosureVariableReads2() { + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.baz = function(){var x = 1;x};", + // Module 2 + "var y = new Foo(); y.baz();"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.baz = JSCompiler_unstubMethod(" + + " 0, function(){var x = 1; x});" + + "var y = new Foo(); y.baz();" + }); + } + + // An anonymous inner function reading a closure variable is fine. + public void testInnerFunctionClosureVariableReads() { + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.baz = function(){var x = 1;" + + " return function(){x}};", + // Module 2 + "var y = new Foo(); y.baz();"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.baz = JSCompiler_unstubMethod(" + + " 0, function(){var x = 1; return function(){x}});" + + "var y = new Foo(); y.baz();" + }); + } + + public void testIssue600() { + testSame( + createModuleChain( + "var jQuery1 = (function() {\n" + + " var jQuery2 = function() {};\n" + + " var theLoneliestNumber = 1;\n" + + " jQuery2.prototype = {\n" + + " size: function() {\n" + + " return theLoneliestNumber;\n" + + " }\n" + + " };\n" + + " return jQuery2;\n" + + "})();\n", + + "(function() {" + + " var div = jQuery1('div');" + + " div.size();" + + "})();")); + } + + public void testIssue600b() { + testSame( + createModuleChain( + "var jQuery1 = (function() {\n" + + " var jQuery2 = function() {};\n" + + " jQuery2.prototype = {\n" + + " size: function() {\n" + + " return 1;\n" + + " }\n" + + " };\n" + + " return jQuery2;\n" + + "})();\n", + + "(function() {" + + " var div = jQuery1('div');" + + " div.size();" + + "})();")); + } + + public void testIssue600c() { + test( + createModuleChain( + "var jQuery2 = function() {};\n" + + "jQuery2.prototype = {\n" + + " size: function() {\n" + + " return 1;\n" + + " }\n" + + "};\n", + + "(function() {" + + " var div = jQuery2('div');" + + " div.size();" + + "})();"), + new String[] { + STUB_DECLARATIONS + + "var jQuery2 = function() {};\n" + + "jQuery2.prototype = {\n" + + " size: JSCompiler_stubMethod(0)\n" + + "};\n", + "jQuery2.prototype.size=" + + " JSCompiler_unstubMethod(0,function(){return 1});" + + "(function() {" + + " var div = jQuery2('div');" + + " div.size();" + + "})();" + }); + } + + public void testIssue600d() { + test( + createModuleChain( + "var jQuery2 = function() {};\n" + + "(function() {" + + " jQuery2.prototype = {\n" + + " size: function() {\n" + + " return 1;\n" + + " }\n" + + " };\n" + + "})();", + + "(function() {" + + " var div = jQuery2('div');" + + " div.size();" + + "})();"), + new String[] { + STUB_DECLARATIONS + + "var jQuery2 = function() {};\n" + + "(function() {" + + " jQuery2.prototype = {\n" + + " size: JSCompiler_stubMethod(0)\n" + + " };\n" + + "})();", + "jQuery2.prototype.size=" + + " JSCompiler_unstubMethod(0,function(){return 1});" + + "(function() {" + + " var div = jQuery2('div');" + + " div.size();" + + "})();" + }); + } + + public void testIssue600e() { + testSame( + createModuleChain( + "var jQuery2 = function() {};\n" + + "(function() {" + + " var theLoneliestNumber = 1;\n" + + " jQuery2.prototype = {\n" + + " size: function() {\n" + + " return theLoneliestNumber;\n" + + " }\n" + + " };\n" + + "})();", + + "(function() {" + + " var div = jQuery2('div');" + + " div.size();" + + "})();")); + } + + public void testPrototypeOfThisAssign() { + testSame( + createModuleChain( + "/** @constructor */" + + "function F() {}" + + "this.prototype.foo = function() {};", + "(new F()).foo();")); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CrossModuleMethodMotionTest.java.bak b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CrossModuleMethodMotionTest.java.bak new file mode 100644 index 0000000..5e58a99 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/CrossModuleMethodMotionTest.java.bak @@ -0,0 +1,561 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Tests for {@link CrossModuleMethodMotion}. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class CrossModuleMethodMotionTest extends CompilerTestCase { + private static final String EXTERNS = + "IFoo.prototype.bar; var mExtern; mExtern.bExtern; mExtern['cExtern'];"; + + private boolean canMoveExterns = false; + + private final String STUB_DECLARATIONS = + CrossModuleMethodMotion.STUB_DECLARATIONS; + + public CrossModuleMethodMotionTest() { + super(EXTERNS); + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new CrossModuleMethodMotion( + compiler, new CrossModuleMethodMotion.IdGenerator(), canMoveExterns); + } + + @Override + public void setUp() { + super.enableLineNumberCheck(true); + canMoveExterns = false; + } + + public void testMovePrototypeMethod1() { + testSame(createModuleChain( + "function Foo() {}" + + "Foo.prototype.bar = function() {};", + // Module 2 + "(new Foo).bar()")); + + canMoveExterns = true; + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.bar = function() {};", + // Module 2 + "(new Foo).bar()"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.bar = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.bar = JSCompiler_unstubMethod(0, function() {});" + + "(new Foo).bar()" + }); + } + + public void testMovePrototypeMethod2() { + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype = { method: function() {} };", + // Module 2 + "(new Foo).method()"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype = { method: JSCompiler_stubMethod(0) };", + // Module 2 + "Foo.prototype.method = " + + " JSCompiler_unstubMethod(0, function() {});" + + "(new Foo).method()" + }); + } + + public void testMovePrototypeMethod3() { + testSame(createModuleChain( + "function Foo() {}" + + "Foo.prototype = { get method() {} };", + // Module 2 + "(new Foo).method()")); + } + + public void testMovePrototypeRecursiveMethod() { + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.baz = function() { this.baz(); };", + // Module 2 + "(new Foo).baz()"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.baz = JSCompiler_unstubMethod(0, " + + " function() { this.baz(); });" + + "(new Foo).baz()" + }); + } + + public void testCantMovePrototypeProp() { + testSame(createModuleChain( + "function Foo() {}" + + "Foo.prototype.baz = goog.nullFunction;", + // Module 2 + "(new Foo).baz()")); + } + + public void testMoveMethodsInRightOrder() { + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.baz = function() { return 1; };" + + "Foo.prototype.baz = function() { return 2; };", + // Module 2 + "(new Foo).baz()"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(1);" + + "Foo.prototype.baz = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.baz = " + + "JSCompiler_unstubMethod(1, function() { return 1; });" + + "Foo.prototype.baz = " + + "JSCompiler_unstubMethod(0, function() { return 2; });" + + "(new Foo).baz()" + }); + } + + public void testMoveMethodsInRightOrder2() { + JSModule[] m = createModules( + "function Foo() {}" + + "Foo.prototype.baz = function() { return 1; };" + + "function Goo() {}" + + "Goo.prototype.baz = function() { return 2; };", + // Module 2, depends on 1 + "", + // Module 3, depends on 2 + "(new Foo).baz()", + // Module 4, depends on 3 + "", + // Module 5, depends on 3 + "(new Goo).baz()"); + + m[1].addDependency(m[0]); + m[2].addDependency(m[1]); + m[3].addDependency(m[2]); + m[4].addDependency(m[2]); + + test(m, + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(1);" + + "function Goo() {}" + + "Goo.prototype.baz = JSCompiler_stubMethod(0);", + // Module 2 + "", + // Module 3 + "Foo.prototype.baz = " + + "JSCompiler_unstubMethod(1, function() { return 1; });" + + "Goo.prototype.baz = " + + "JSCompiler_unstubMethod(0, function() { return 2; });" + + "(new Foo).baz()", + // Module 4 + "", + // Module 5 + "(new Goo).baz()" + }); + } + + public void testMoveMethodsUsedInTwoModules() { + testSame(createModuleStar( + "function Foo() {}" + + "Foo.prototype.baz = function() {};", + // Module 2 + "(new Foo).baz()", + // Module 3 + "(new Foo).baz()")); + } + + public void testMoveMethodsUsedInTwoModules2() { + JSModule[] modules = createModules( + "function Foo() {}" + + "Foo.prototype.baz = function() {};", + // Module 2 + "", // a blank module in the middle + // Module 3 + "(new Foo).baz() + 1", + // Module 4 + "(new Foo).baz() + 2"); + + modules[1].addDependency(modules[0]); + modules[2].addDependency(modules[1]); + modules[3].addDependency(modules[1]); + test(modules, + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.baz = JSCompiler_unstubMethod(0, function() {});", + // Module 3 + "(new Foo).baz() + 1", + // Module 4 + "(new Foo).baz() + 2" + }); + } + + public void testTwoMethods() { + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.baz = function() {};", + // Module 2 + "Foo.prototype.callBaz = function() { this.baz(); }", + // Module 3 + "(new Foo).callBaz()"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(1);", + // Module 2 + "Foo.prototype.callBaz = JSCompiler_stubMethod(0);", + // Module 3 + "Foo.prototype.baz = JSCompiler_unstubMethod(1, function() {});" + + "Foo.prototype.callBaz = " + + " JSCompiler_unstubMethod(0, function() { this.baz(); });" + + "(new Foo).callBaz()" + }); + } + + public void testTwoMethods2() { + // if the programmer screws up the module order, we don't try to correct + // the mistake. + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.baz = function() {};", + // Module 2 + "(new Foo).callBaz()", + // Module 3 + "Foo.prototype.callBaz = function() { this.baz(); }"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(0);", + // Module 2 + "(new Foo).callBaz()", + // Module 3 + "Foo.prototype.baz = JSCompiler_unstubMethod(0, function() {});" + + "Foo.prototype.callBaz = function() { this.baz(); };" + }); + } + + public void testGlobalFunctionsInGraph() { + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.baz = function() {};" + + "function x() { return (new Foo).baz(); }", + // Module 2 + "x();"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(0);" + + "function x() { return (new Foo).baz(); }", + // Module 2 + "Foo.prototype.baz = JSCompiler_unstubMethod(0, function() {});" + + "x();" + }); + } + + // Read of closure variable disables method motions. + public void testClosureVariableReads1() { + testSame(createModuleChain( + "function Foo() {}" + + "(function() {" + + "var x = 'x';" + + "Foo.prototype.baz = function() {x};" + + "})();", + // Module 2 + "var y = new Foo(); y.baz();")); + } + + // Read of global variable is fine. + public void testClosureVariableReads2() { + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.b1 = function() {" + + " var x = 1;" + + " Foo.prototype.b2 = function() {" + + " Foo.prototype.b3 = function() {" + + " x;" + + " }" + + " }" + + "};", + // Module 2 + "var y = new Foo(); y.b1();", + // Module 3 + "y = new Foo(); z.b2();", + // Module 4 + "y = new Foo(); z.b3();" + ), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.b1 = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.b1 = JSCompiler_unstubMethod(0, function() {" + + " var x = 1;" + + " Foo.prototype.b2 = function() {" + + " Foo.prototype.b3 = function() {" + + " x;" + + " }" + + " }" + + "});" + + "var y = new Foo(); y.b1();", + // Module 3 + "y = new Foo(); z.b2();", + // Module 4 + "y = new Foo(); z.b3();" + }); + } + + public void testClosureVariableReads3() { + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.b1 = function() {" + + " Foo.prototype.b2 = function() {" + + " var x = 1;" + + " Foo.prototype.b3 = function() {" + + " x;" + + " }" + + " }" + + "};", + // Module 2 + "var y = new Foo(); y.b1();", + // Module 3 + "y = new Foo(); z.b2();", + // Module 4 + "y = new Foo(); z.b3();" + ), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.b1 = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.b1 = JSCompiler_unstubMethod(0, function() {" + + " Foo.prototype.b2 = JSCompiler_stubMethod(1);" + + "});" + + "var y = new Foo(); y.b1();", + // Module 3 + "Foo.prototype.b2 = JSCompiler_unstubMethod(1, function() {" + + " var x = 1;" + + " Foo.prototype.b3 = function() {" + + " x;" + + " }" + + "});" + + "y = new Foo(); z.b2();", + // Module 4 + "y = new Foo(); z.b3();" + }); + } + + // Read of global variable is fine. + public void testNoClosureVariableReads1() { + test(createModuleChain( + "function Foo() {}" + + "var x = 'x';" + + "Foo.prototype.baz = function(){x};", + // Module 2 + "var y = new Foo(); y.baz();"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "var x = 'x';" + + "Foo.prototype.baz = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.baz = JSCompiler_unstubMethod(0, function(){x});" + + "var y = new Foo(); y.baz();" + }); + } + + // Read of a local is fine. + public void testNoClosureVariableReads2() { + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.baz = function(){var x = 1;x};", + // Module 2 + "var y = new Foo(); y.baz();"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.baz = JSCompiler_unstubMethod(" + + " 0, function(){var x = 1; x});" + + "var y = new Foo(); y.baz();" + }); + } + + // An anonymous inner function reading a closure variable is fine. + public void testInnerFunctionClosureVariableReads() { + test(createModuleChain( + "function Foo() {}" + + "Foo.prototype.baz = function(){var x = 1;" + + " return function(){x}};", + // Module 2 + "var y = new Foo(); y.baz();"), + new String[] { + STUB_DECLARATIONS + + "function Foo() {}" + + "Foo.prototype.baz = JSCompiler_stubMethod(0);", + // Module 2 + "Foo.prototype.baz = JSCompiler_unstubMethod(" + + " 0, function(){var x = 1; return function(){x}});" + + "var y = new Foo(); y.baz();" + }); + } + + public void testIssue600() { + testSame( + createModuleChain( + "var jQuery1 = (function() {\n" + + " var jQuery2 = function() {};\n" + + " var theLoneliestNumber = 1;\n" + + " jQuery2.prototype = {\n" + + " size: function() {\n" + + " return theLoneliestNumber;\n" + + " }\n" + + " };\n" + + " return jQuery2;\n" + + "})();\n", + + "(function() {" + + " var div = jQuery1('div');" + + " div.size();" + + "})();")); + } + + public void testIssue600b() { + testSame( + createModuleChain( + "var jQuery1 = (function() {\n" + + " var jQuery2 = function() {};\n" + + " jQuery2.prototype = {\n" + + " size: function() {\n" + + " return 1;\n" + + " }\n" + + " };\n" + + " return jQuery2;\n" + + "})();\n", + + "(function() {" + + " var div = jQuery1('div');" + + " div.size();" + + "})();")); + } + + public void testIssue600c() { + test( + createModuleChain( + "var jQuery2 = function() {};\n" + + "jQuery2.prototype = {\n" + + " size: function() {\n" + + " return 1;\n" + + " }\n" + + "};\n", + + "(function() {" + + " var div = jQuery2('div');" + + " div.size();" + + "})();"), + new String[] { + STUB_DECLARATIONS + + "var jQuery2 = function() {};\n" + + "jQuery2.prototype = {\n" + + " size: JSCompiler_stubMethod(0)\n" + + "};\n", + "jQuery2.prototype.size=" + + " JSCompiler_unstubMethod(0,function(){return 1});" + + "(function() {" + + " var div = jQuery2('div');" + + " div.size();" + + "})();" + }); + } + + public void testIssue600d() { + test( + createModuleChain( + "var jQuery2 = function() {};\n" + + "(function() {" + + " jQuery2.prototype = {\n" + + " size: function() {\n" + + " return 1;\n" + + " }\n" + + " };\n" + + "})();", + + "(function() {" + + " var div = jQuery2('div');" + + " div.size();" + + "})();"), + new String[] { + STUB_DECLARATIONS + + "var jQuery2 = function() {};\n" + + "(function() {" + + " jQuery2.prototype = {\n" + + " size: JSCompiler_stubMethod(0)\n" + + " };\n" + + "})();", + "jQuery2.prototype.size=" + + " JSCompiler_unstubMethod(0,function(){return 1});" + + "(function() {" + + " var div = jQuery2('div');" + + " div.size();" + + "})();" + }); + } + + public void testIssue600e() { + testSame( + createModuleChain( + "var jQuery2 = function() {};\n" + + "(function() {" + + " var theLoneliestNumber = 1;\n" + + " jQuery2.prototype = {\n" + + " size: function() {\n" + + " return theLoneliestNumber;\n" + + " }\n" + + " };\n" + + "})();", + + "(function() {" + + " var div = jQuery2('div');" + + " div.size();" + + "})();")); + } + + public void testPrototypeOfThisAssign() { + testSame( + createModuleChain( + "/** @constructor */" + + "function F() {}" + + "this.prototype.foo = function() {};", + "(new F()).foo();")); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DataFlowAnalysisTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DataFlowAnalysisTest.java new file mode 100644 index 0000000..f63233f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DataFlowAnalysisTest.java @@ -0,0 +1,791 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.ControlFlowGraph.Branch; +import com.google.javascript.jscomp.DataFlowAnalysis.BranchedFlowState; +import com.google.javascript.jscomp.DataFlowAnalysis.BranchedForwardDataFlowAnalysis; +import com.google.javascript.jscomp.DataFlowAnalysis.FlowState; +import com.google.javascript.jscomp.DataFlowAnalysis.MaxIterationsExceededException; +import com.google.javascript.jscomp.JoinOp.BinaryJoinOp; +import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; +import com.google.javascript.jscomp.graph.GraphNode; +import com.google.javascript.jscomp.graph.LatticeElement; + +import junit.framework.TestCase; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +/** + * A test suite with a very small programming language that has two types of + * instructions: {@link BranchInstruction} and {@link ArithmeticInstruction}. + * Test cases must construct a small program with these instructions and + * manually put each instruction in a {@code ControlFlowGraph}. + * + */ +public class DataFlowAnalysisTest extends TestCase { + + /** + * Operations supported by ArithmeticInstruction. + */ + enum Operation { + ADD("+"), SUB("-"), DIV("/"), MUL("*"); + private final String stringRep; + + private Operation(String stringRep) { + this.stringRep = stringRep; + } + + @Override + public String toString() { + return stringRep; + } + } + + /** + * A simple value. + */ + abstract static class Value { + + boolean isNumber() { + return this instanceof Number; + } + + boolean isVariable() { + return this instanceof Variable; + } + } + + /** + * A variable. + */ + static class Variable extends Value { + private String name; + + /** + * Constructor. + * + * @param n Name of the variable. + */ + Variable(String n) { + name = n; + } + + String getName() { + return name; + } + + @Override + public boolean equals(Object other) { + // Use the String's .equals() + if (!(other instanceof Variable)) { + return false; + } + return ((Variable) other).name.equals(name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public String toString() { + return this.name; + } + } + + /** + * A number constant. + */ + static class Number extends Value { + private int value; + + /** + * Constructor + * + * @param v Value + */ + Number(int v) { + value = v; + } + + int getValue() { + return value; + } + + @Override + public String toString() { + return "" + value; + } + + @Override + public int hashCode() { + return value; + } + } + + /** + * An instruction of the dummy program. + */ + abstract static class Instruction { + + int order = 0; + + /** + * Check whether this is an arithmetic instruction. + * + * @return {@code true} if it is an arithmetic instruction. + */ + boolean isArithmetic() { + return this instanceof ArithmeticInstruction; + } + + /** + * Check whether this is a branch instruction. + * + * @return {@code true} if it is a branch instruction. + */ + boolean isBranch() { + return this instanceof BranchInstruction; + } + } + + /** + * Basic arithmetic instruction that only takes the form of: + * + *
      +   * Result = Operand1 operator Operand2
      +   * 
      + */ + static class ArithmeticInstruction extends Instruction { + private Operation operation; + private Value operand1; + private Value operand2; + private Variable result; + + /** + * Constructor + * + * @param res Result. + * @param op1 First Operand. + * @param o Operator. + * @param op2 Second Operand. + */ + ArithmeticInstruction(Variable res, int op1, Operation o, int op2) { + this(res, new Number(op1), o, new Number(op2)); + } + + /** + * Constructor + * + * @param res Result. + * @param op1 First Operand. + * @param o Operator. + * @param op2 Second Operand. + */ + ArithmeticInstruction(Variable res, Value op1, Operation o, int op2) { + this(res, op1, o, new Number(op2)); + } + + /** + * Constructor + * + * @param res Result. + * @param op1 First Operand. + * @param o Operator. + * @param op2 Second Operand. + */ + ArithmeticInstruction(Variable res, int op1, Operation o, Value op2) { + this(res, new Number(op1), o, op2); + } + + /** + * Constructor + * + * @param res Result. + * @param op1 First Operand. + * @param o Operator. + * @param op2 Second Operand. + */ + ArithmeticInstruction(Variable res, Value op1, Operation o, Value op2) { + result = res; + operand1 = op1; + operand2 = op2; + operation = o; + } + + Operation getOperator() { + return operation; + } + + void setOperator(Operation op) { + this.operation = op; + } + + Value getOperand1() { + return operand1; + } + + void setOperand1(Value operand1) { + this.operand1 = operand1; + } + + Value getOperand2() { + return operand2; + } + + void setOperand2(Value operand2) { + this.operand2 = operand2; + } + + Variable getResult() { + return result; + } + + void setResult(Variable result) { + this.result = result; + } + + @Override + public String toString() { + StringBuilder out = new StringBuilder(); + out.append(result); + out.append(" = "); + out.append(operand1); + out.append(operation); + out.append(operand2); + return out.toString(); + } + + @Override + public int hashCode() { + return toString().hashCode(); + } + } + + public static ArithmeticInstruction + newAssignNumberToVariableInstruction(Variable res, int num) { + return new ArithmeticInstruction(res, num, Operation.ADD, 0); + } + + public static ArithmeticInstruction + newAssignVariableToVariableInstruction(Variable lhs, Variable rhs) { + return new ArithmeticInstruction(lhs, rhs, Operation.ADD, 0); + } + + /** + * Branch instruction based on a {@link Value} as a condition. + */ + static class BranchInstruction extends Instruction { + private Value condition; + + BranchInstruction(Value cond) { + condition = cond; + } + + Value getCondition() { + return condition; + } + + void setCondition(Value condition) { + this.condition = condition; + } + } + + /** + * A lattice to represent constant states. Each variable of the program will + * have a lattice defined as: + * + *
      +   *        TOP
      +   *   / / |         \
      +   *  0  1 2 3 ..... MAX_VALUE
      +   *  \  \ |         /
      +   *       BOTTOM
      +   * 
      + * + * Where BOTTOM represents the variable is not a constant. + *

      + * This class will represent a product lattice of each variable's lattice. The + * whole lattice is store in a {@code HashMap}. If variable {@code x} is + * defined to be constant 10. The map will contain the value 10 with the + * variable {@code x} as key. Otherwise, {@code x} is not a constant. + */ + private static class ConstPropLatticeElement implements LatticeElement { + private final Map constMap; + private final boolean isTop; + + /** + * Constructor. + * + * @param isTop To define if the lattice is top. + */ + ConstPropLatticeElement(boolean isTop) { + this.isTop = isTop; + this.constMap = Maps.newHashMap(); + } + + /** + * Create a lattice where every variable is defined to be not constant. + */ + ConstPropLatticeElement() { + this(false); + } + + ConstPropLatticeElement(ConstPropLatticeElement other) { + this.isTop = other.isTop; + this.constMap = Maps.newHashMap(other.constMap); + } + + @Override + public String toString() { + if (isTop) { + return "TOP"; + } + StringBuilder out = new StringBuilder(); + + out.append("{"); + for (Variable var : constMap.keySet()) { + out.append(var); + out.append("="); + out.append(constMap.get(var)); + out.append(" "); + } + out.append("}"); + return out.toString(); + } + + @Override + public boolean equals(Object other) { + if (other instanceof ConstPropLatticeElement) { + ConstPropLatticeElement otherLattice = (ConstPropLatticeElement) other; + return (this.isTop == otherLattice.isTop) && + this.constMap.equals(otherLattice.constMap); + } + return false; + } + } + + private static class ConstPropJoinOp + extends BinaryJoinOp { + + @Override + public ConstPropLatticeElement apply(ConstPropLatticeElement a, + ConstPropLatticeElement b) { + ConstPropLatticeElement result = new ConstPropLatticeElement(); + // By the definition of TOP of the lattice. + if (a.isTop) { + return new ConstPropLatticeElement(a); + } + if (b.isTop) { + return new ConstPropLatticeElement(b); + } + // Do the join for each variable's lattice. + for (Variable var : a.constMap.keySet()) { + if (b.constMap.containsKey(var)) { + Integer number = b.constMap.get(var); + + // The result will contain that variable as a known constant + // if both lattice has that variable the same constant. + if (a.constMap.get(var).equals(number)) { + result.constMap.put(var, number); + } + } + } + return result; + } + } + + /** + * A simple forward constant propagation. + */ + static class DummyConstPropagation extends + DataFlowAnalysis { + + /** + * Constructor. + * + * @param targetCfg Control Flow Graph. + */ + DummyConstPropagation(ControlFlowGraph targetCfg) { + super(targetCfg, new ConstPropJoinOp()); + } + + @Override + boolean isForward() { + return true; + } + + @Override + ConstPropLatticeElement flowThrough(Instruction node, + ConstPropLatticeElement input) { + if (node.isBranch()) { + return new ConstPropLatticeElement(input); + } else { + return flowThroughArithmeticInstruction((ArithmeticInstruction) node, + input); + } + } + + @Override + ConstPropLatticeElement createEntryLattice() { + return new ConstPropLatticeElement(); + } + + @Override + ConstPropLatticeElement createInitialEstimateLattice() { + return new ConstPropLatticeElement(true); + } + } + + static ConstPropLatticeElement flowThroughArithmeticInstruction( + ArithmeticInstruction aInst, ConstPropLatticeElement input) { + + ConstPropLatticeElement out = new ConstPropLatticeElement(input); + // Try to see if left is a number. If it is a variable, it might already + // be a constant coming in. + Integer leftConst = null; + if (aInst.operand1.isNumber()) { + leftConst = ((Number) aInst.operand1).value; + } else { + if (input.constMap.containsKey(aInst.operand1)) { + leftConst = input.constMap.get(aInst.operand1); + } + } + + // Do the same thing to the right. + Integer rightConst = null; + if (aInst.operand2.isNumber()) { + rightConst = ((Number) aInst.operand2).value; + } else { + if (input.constMap.containsKey(aInst.operand2)) { + rightConst = input.constMap.get(aInst.operand2); + } + } + + // If both are known constant we can perform the operation. + if (leftConst != null && rightConst != null) { + Integer constResult = null; + if (aInst.operation == Operation.ADD) { + constResult = leftConst.intValue() + rightConst.intValue(); + } else if (aInst.operation == Operation.SUB) { + constResult = leftConst.intValue() - rightConst.intValue(); + } else if (aInst.operation == Operation.MUL) { + constResult = leftConst.intValue() * rightConst.intValue(); + } else if (aInst.operation == Operation.DIV) { + constResult = leftConst.intValue() / rightConst.intValue(); + } + // Put it in the map. (Possibly replacing the existing constant value) + out.constMap.put(aInst.result, constResult); + } else { + // If we cannot find a constant for it + out.constMap.remove(aInst.result); + } + return out; + } + + public void testSimpleIf() { + // if (a) { b = 1; } else { b = 1; } c = b; + Variable a = new Variable("a"); + Variable b = new Variable("b"); + Variable c = new Variable("c"); + Instruction inst1 = new BranchInstruction(a); + Instruction inst2 = newAssignNumberToVariableInstruction(b, 1); + Instruction inst3 = newAssignNumberToVariableInstruction(b, 1); + Instruction inst4 = newAssignVariableToVariableInstruction(c, b); + ControlFlowGraph cfg = + new ControlFlowGraph(inst1, true, true); + GraphNode n1 = cfg.createNode(inst1); + GraphNode n2 = cfg.createNode(inst2); + GraphNode n3 = cfg.createNode(inst3); + GraphNode n4 = cfg.createNode(inst4); + cfg.connect(inst1, ControlFlowGraph.Branch.ON_FALSE, inst2); + cfg.connect(inst1, ControlFlowGraph.Branch.ON_TRUE, inst3); + cfg.connect(inst2, ControlFlowGraph.Branch.UNCOND, inst4); + cfg.connect(inst3, ControlFlowGraph.Branch.UNCOND, inst4); + + DummyConstPropagation constProp = new DummyConstPropagation(cfg); + constProp.analyze(); + + // We cannot conclude anything from if (a). + verifyInHas(n1, a, null); + verifyInHas(n1, b, null); + verifyInHas(n1, c, null); + verifyOutHas(n1, a, null); + verifyOutHas(n1, b, null); + verifyOutHas(n1, c, null); + + // We can conclude b = 1 after the instruction. + verifyInHas(n2, a, null); + verifyInHas(n2, b, null); + verifyInHas(n2, c, null); + verifyOutHas(n2, a, null); + verifyOutHas(n2, b, 1); + verifyOutHas(n2, c, null); + + // Same as above. + verifyInHas(n3, a, null); + verifyInHas(n3, b, null); + verifyInHas(n3, c, null); + verifyOutHas(n3, a, null); + verifyOutHas(n3, b, 1); + verifyOutHas(n3, c, null); + + // After the merge we should still have b = 1. + verifyInHas(n4, a, null); + verifyInHas(n4, b, 1); + verifyInHas(n4, c, null); + verifyOutHas(n4, a, null); + // After the instruction both b and c are 1. + verifyOutHas(n4, b, 1); + verifyOutHas(n4, c, 1); + } + + public void testSimpleLoop() { + // a = 0; do { a = a + 1 } while (b); c = a; + Variable a = new Variable("a"); + Variable b = new Variable("b"); + Variable c = new Variable("c"); + Instruction inst1 = newAssignNumberToVariableInstruction(a, 0); + Instruction inst2 = new ArithmeticInstruction(a, a, Operation.ADD, 1); + Instruction inst3 = new BranchInstruction(b); + Instruction inst4 = newAssignVariableToVariableInstruction(c, a); + ControlFlowGraph cfg = + new ControlFlowGraph(inst1, true, true); + GraphNode n1 = cfg.createNode(inst1); + GraphNode n2 = cfg.createNode(inst2); + GraphNode n3 = cfg.createNode(inst3); + GraphNode n4 = cfg.createNode(inst4); + cfg.connect(inst1, ControlFlowGraph.Branch.UNCOND, inst2); + cfg.connect(inst2, ControlFlowGraph.Branch.UNCOND, inst3); + cfg.connect(inst3, ControlFlowGraph.Branch.ON_TRUE, inst2); + cfg.connect(inst3, ControlFlowGraph.Branch.ON_FALSE, inst4); + + DummyConstPropagation constProp = new DummyConstPropagation(cfg); + // This will also show that the framework terminates properly. + constProp.analyze(); + + // a = 0 is the only thing we know. + verifyInHas(n1, a, null); + verifyInHas(n1, b, null); + verifyInHas(n1, c, null); + verifyOutHas(n1, a, 0); + verifyOutHas(n1, b, null); + verifyOutHas(n1, c, null); + + // Nothing is provable in this program, so confirm that we haven't + // erroneously "proven" something. + verifyInHas(n2, a, null); + verifyInHas(n2, b, null); + verifyInHas(n2, c, null); + verifyOutHas(n2, a, null); + verifyOutHas(n2, b, null); + verifyOutHas(n2, c, null); + + verifyInHas(n3, a, null); + verifyInHas(n3, b, null); + verifyInHas(n3, c, null); + verifyOutHas(n3, a, null); + verifyOutHas(n3, b, null); + verifyOutHas(n3, c, null); + + verifyInHas(n4, a, null); + verifyInHas(n4, b, null); + verifyInHas(n4, c, null); + verifyOutHas(n4, a, null); + verifyOutHas(n4, b, null); + verifyOutHas(n4, c, null); + } + + public void testLatticeArrayMinimizationWhenMidpointIsEven() { + assertEquals(6, JoinOp.BinaryJoinOp.computeMidPoint(12)); + } + + public void testLatticeArrayMinimizationWhenMidpointRoundsDown() { + assertEquals(8, JoinOp.BinaryJoinOp.computeMidPoint(18)); + } + + public void testLatticeArrayMinimizationWithTwoElements() { + assertEquals(1, JoinOp.BinaryJoinOp.computeMidPoint(2)); + } + + + /** + * A simple forward constant propagation. + */ + static class BranchedDummyConstPropagation extends + BranchedForwardDataFlowAnalysis { + + BranchedDummyConstPropagation(ControlFlowGraph targetCfg) { + super(targetCfg, new ConstPropJoinOp()); + } + + @Override + ConstPropLatticeElement flowThrough(Instruction node, + ConstPropLatticeElement input) { + if (node.isArithmetic()) { + return flowThroughArithmeticInstruction( + (ArithmeticInstruction) node, input); + } else { + return new ConstPropLatticeElement(input); + } + } + + @Override + List branchedFlowThrough(Instruction node, + ConstPropLatticeElement input) { + List result = Lists.newArrayList(); + List> outEdges = + getCfg().getOutEdges(node); + if (node.isArithmetic()) { + assertTrue(outEdges.size() < 2); + ConstPropLatticeElement aResult = flowThroughArithmeticInstruction( + (ArithmeticInstruction) node, input); + for (DiGraphEdge _ : outEdges) { + result.add(aResult); + } + } else { + BranchInstruction branchInst = (BranchInstruction) node; + for (DiGraphEdge branch : outEdges) { + ConstPropLatticeElement edgeResult = + new ConstPropLatticeElement(input); + if (branch.getValue() == Branch.ON_FALSE && + branchInst.getCondition().isVariable()) { + edgeResult.constMap.put((Variable) branchInst.getCondition(), 0); + } + result.add(edgeResult); + } + } + return result; + } + + @Override + ConstPropLatticeElement createEntryLattice() { + return new ConstPropLatticeElement(); + } + + @Override + ConstPropLatticeElement createInitialEstimateLattice() { + return new ConstPropLatticeElement(true); + } + } + + public void testBranchedSimpleIf() { + // if (a) { a = 0; } else { b = 0; } c = b; + Variable a = new Variable("a"); + Variable b = new Variable("b"); + Variable c = new Variable("c"); + Instruction inst1 = new BranchInstruction(a); + Instruction inst2 = newAssignNumberToVariableInstruction(a, 0); + Instruction inst3 = newAssignNumberToVariableInstruction(b, 0); + Instruction inst4 = newAssignVariableToVariableInstruction(c, b); + ControlFlowGraph cfg = + new ControlFlowGraph(inst1, true, true); + GraphNode n1 = cfg.createNode(inst1); + GraphNode n2 = cfg.createNode(inst2); + GraphNode n3 = cfg.createNode(inst3); + GraphNode n4 = cfg.createNode(inst4); + cfg.connect(inst1, ControlFlowGraph.Branch.ON_TRUE, inst2); + cfg.connect(inst1, ControlFlowGraph.Branch.ON_FALSE, inst3); + cfg.connect(inst2, ControlFlowGraph.Branch.UNCOND, inst4); + cfg.connect(inst3, ControlFlowGraph.Branch.UNCOND, inst4); + + BranchedDummyConstPropagation constProp = + new BranchedDummyConstPropagation(cfg); + constProp.analyze(); + + // We cannot conclude anything from if (a). + verifyBranchedInHas(n1, a, null); + verifyBranchedInHas(n1, b, null); + verifyBranchedInHas(n1, c, null); + + // Nothing is known on the true branch. + verifyBranchedInHas(n2, a, null); + verifyBranchedInHas(n2, b, null); + verifyBranchedInHas(n2, c, null); + + // Verify that we have a = 0 on the false branch. + verifyBranchedInHas(n3, a, 0); + verifyBranchedInHas(n3, b, null); + verifyBranchedInHas(n3, c, null); + + // After the merge we should still have a = 0. + verifyBranchedInHas(n4, a, 0); + } + + public void testMaxIterationsExceededException() { + final int MAX_STEP = 10; + Variable a = new Variable("a"); + Instruction inst1 = new ArithmeticInstruction(a, a, Operation.ADD, a); + ControlFlowGraph cfg = + new ControlFlowGraph(inst1, true, true) { + @Override + public Comparator> + getOptionalNodeComparator(boolean isForward) { + return new Comparator>() { + @Override + public int compare(DiGraphNode o1, + DiGraphNode o2) { + return o1.getValue().order - o2.getValue().order; + } + }; + } + }; + cfg.createNode(inst1); + + // We have MAX_STEP + 1 nodes, it is impossible to finish the analysis with + // MAX_STEP number of steps. + for (int i = 0; i < MAX_STEP + 1; i++) { + Instruction inst2 = new ArithmeticInstruction(a, a, Operation.ADD, a); + cfg.createNode(inst2); + inst2.order = i + 1; + cfg.connect(inst1, ControlFlowGraph.Branch.UNCOND, inst2); + inst1 = inst2; + } + DummyConstPropagation constProp = new DummyConstPropagation(cfg); + try { + constProp.analyze(MAX_STEP); + fail("Expected MaxIterationsExceededException to be thrown."); + } catch (MaxIterationsExceededException e) { + assertEquals(e.getMessage(), "Analysis did not terminate after " + + MAX_STEP + " iterations"); + } + } + + static void verifyInHas(GraphNode node, Variable var, + Integer constant) { + FlowState fState = node.getAnnotation(); + assertEquals(constant, fState.getIn().constMap.get(var)); + } + + static void verifyOutHas(GraphNode node, Variable var, + Integer constant) { + FlowState fState = node.getAnnotation(); + assertEquals(constant, fState.getOut().constMap.get(var)); + } + + static void verifyBranchedInHas(GraphNode node, + Variable var, Integer constant) { + BranchedFlowState fState = node.getAnnotation(); + assertEquals(constant, fState.getIn().constMap.get(var)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DeadAssignmentsEliminationTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DeadAssignmentsEliminationTest.java new file mode 100644 index 0000000..c918296 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DeadAssignmentsEliminationTest.java @@ -0,0 +1,534 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + * Tests for {@link DeadAssignmentsElimination}. + * + */ +public class DeadAssignmentsEliminationTest extends CompilerTestCase { + + public DeadAssignmentsEliminationTest() { + super("var extern;"); + } + + @Override + public void setUp() { + super.enableLineNumberCheck(true); + } + + @Override + public CompilerPass getProcessor(final Compiler compiler) { + return new CompilerPass() { + @Override + public void process(Node externs, Node js) { + NodeTraversal.traverse( + compiler, js, new DeadAssignmentsElimination(compiler)); + } + }; + } + + @Override + protected int getNumRepetitions() { + return 1; + } + + public void testSimple() { + inFunction("var a; a=1", "var a; 1"); + inFunction("var a; a=1+1", "var a; 1+1"); + inFunction("var a; a=foo();", "var a; foo()"); + inFunction("a=1; var a; a=foo();", "1; var a; foo();"); + // This should be: "var a; (function f(){})", but we don't mess with + // functions with inner functions. + inFunction("var a; a=function f(){}"); + } + + public void testLoops() { + inFunction("for(var a=0; a<10; a++) {}"); + inFunction("var x; for(var a=0; a<10; a++) {x=a}; a(x)"); + inFunction("var x; for(var a=0; x=a<10; a++) {}", + "var x; for(var a=0; a<10; a++) {}"); + inFunction("var x; for(var a=0; a<10; x=a) {}", + "var x; for(var a=0; a<10; a) {}"); + inFunction("var x; for(var a=0; a<10; x=a,a++) {}", + "var x; for(var a=0; a<10; a,a++) {}"); + inFunction("var x; for(var a=0; a<10; a++,x=a) {}", + "var x; for(var a=0; a<10; a++,a) {}"); + inFunction("var x;for(var a=0; a<10; a++) {x=1}", + "var x;for(var a=0; a<10; a++) {1}"); + inFunction("var x; x=1; do{x=2}while(0); x", + "var x; 1; do{x=2}while(0); x"); + inFunction("var x; x=1; while(1){x=2}; x"); + } + + public void testMultiPaths() { + inFunction("var x,y; if(x)y=1;", "var x,y; if(x)1;"); + inFunction("var x,y; if(x)y=1; y=2; x(y)", "var x,y; if(x)1; y=2; x(y)"); + inFunction("var x; switch(x) { case(1): x=1; break; } x"); + inFunction("var x; switch(x) { case(1): x=1; break; }", + "var x; switch(x) { case(1): 1; break; }"); + } + + public void testUsedAsConditions() { + inFunction("var x; while(x=1){}", "var x; while(1){}"); + inFunction("var x; if(x=1){}", "var x; if(1){}"); + inFunction("var x; do{}while(x=1)", "var x; do{}while(1)"); + inFunction("var x; if(x=1==4&&1){}", "var x; if(1==4&&1) {}"); + inFunction("var x; if(0&&(x=1)){}", "var x; if(0&&1){}"); + inFunction("var x; if((x=2)&&(x=1)){}", "var x; if(2&&1){}"); + inFunction("var x; x=2; if(0&&(x=1)){}; x"); + + inFunction("var x,y; if( (x=1)+(y=2) > 3){}", + "var x,y; if( 1+2 > 3){}"); + } + + public void testUsedAsConditionsInSwitchStatements() { + inFunction("var x; switch(x=1){}","var x; switch(1){}"); + inFunction("var x; switch(x){case(x=1):break;}", + "var x; switch(x){case(1):break;}"); + + inFunction("var x,y; switch(y) { case (x += 1): break; case (x): break;}"); + + inFunction("var x,y; switch(y) { case (x = 1): break; case (2): break;}", + "var x,y; switch(y) { case (1): break; case (2): break;}"); + inFunction("var x,y; switch(y) { case (x+=1): break; case (x=2): break;}", + "var x,y; switch(y) { case (x+1): break; case (2): break;}"); + } + + public void testAssignmentInReturn() { + inFunction("var x; return x = 1;", "var x; return 1"); + inFunction("var x; return"); + } + + public void testAssignmentSamples() { + // We want this to be "var x" in these cases. + inFunction("var x = 2;"); + inFunction("var x = 2; x++;", "var x=2; void 0"); + inFunction("var x; x=x++;", "var x;x++"); + inFunction("var x; x+=1;", "var x;x+1"); + } + + public void testAssignmentInArgs() { + inFunction("var x; foo(x = 1);", "var x; foo(1);"); + inFunction("var x; return foo(x = 1);", "var x; return foo(1);"); + } + + /** + * BUG #1358904 + */ + public void testAssignAndReadInCondition() { + inFunction("var a, b; if ((a = 1) && (b = a)) {b}"); + inFunction("var a, b; if ((b = a) && (a = 1)) {b}", + "var a, b; if ((b = a) && (1)) {b}"); + } + + public void testParameters() { + inFunction("param1=1; param1=2; param2(param1)", + "1; param1=2; param2(param1)"); + inFunction("param1=param2()", "param2()"); + } + + public void testErrorHandling() { + inFunction("var x; try{ x=1 } catch(e){ x=2 }; x"); + inFunction("var x; try{ x=1 } catch(e){ x=2 }", + "var x;try{ 1 } catch(e) { 2 }"); + inFunction("var x; try{ x=1 } finally { x=2 }; x", + "var x;try{ 1 } finally{ x=2 }; x"); + inFunction("var x; while(1) { try{x=1;break}finally{x} }"); + inFunction("var x; try{throw 1} catch(e){x=2} finally{x}"); + inFunction("var x; try{x=1;throw 1;x} finally{x=2}; x", + "var x; try{1;throw 1;x} finally{x=2}; x"); + } + + public void testDeadVarDeclarations() { + // Dead assignments in VAR is _NOT_ supported yet. + inFunction("var x=1;"); + inFunction("var x=1; x=2; x"); + } + + public void testGlobal() { + // Doesn't do any work on global scope yet. + test("var x; x=1; x=2; x=3;", "var x; x=1; x=2; x=3;"); + } + + public void testInnerFunctions() { + inFunction("var x = function() { var x; x=1; }", + "var x = function() { var x; 1; }"); + } + + public void testInnerFunctions2() { + // Give up DCE if there is a inner function. + inFunction("var x = 0; print(x); x = 1; var y = function(){}; y()"); + } + + public void testSelfReAssignment() { + inFunction("var x; x = x;", "var x; x"); + } + + public void testSelfIncrement() { + inFunction("var x; x = x + 1;", "var x; x + 1"); + } + + public void testAssignmentOp() { + // We have remove constant expressions that cleans this one up. + inFunction("var x; x += foo()", "var x; x + foo()"); + } + + public void testAssignmentOpUsedAsLhs() { + inFunction("var x,y; y = x += foo(); print(y)", + "var x,y; y = x + foo(); print(y)"); + } + + public void testAssignmentOpUsedAsCondition() { + inFunction("var x; if(x += foo()) {}", + "var x; if(x + foo()) {}"); + + inFunction("var x; if((x += foo()) > 1) {}", + "var x; if((x + foo()) > 1) {}"); + + // Not in a while because this happens every loop. + inFunction("var x; while((x += foo()) > 1) {}"); + + inFunction("var x; for(;--x;){}"); + inFunction("var x; for(;x--;){}"); + inFunction("var x; for(;x -= 1;){}"); + inFunction("var x; for(;x = 0;){}", "var x; for(;0;){}"); + + inFunction("var x; for(;;--x){}"); + inFunction("var x; for(;;x--){}"); + inFunction("var x; for(;;x -= 1){}"); + inFunction("var x; for(;;x = 0){}", "var x; for(;;0){}"); + + inFunction("var x; for(--x;;){}", "var x; for(;;){}"); + inFunction("var x; for(x--;;){}", "var x; for(;;){}"); + inFunction("var x; for(x -= 1;;){}", "var x; for(x - 1;;){}"); + inFunction("var x; for(x = 0;;){}", "var x; for(0;;){}"); + } + + public void testDeadIncrement() { + // TODO(user): Optimize this. + inFunction("var x; x ++", "var x; void 0"); + inFunction("var x; x --", "var x; void 0"); + } + + public void testDeadButAlivePartiallyWithinTheExpression() { + inFunction("var x; x = 100, print(x), x = 101;", + "var x; x = 100, print(x), 101;"); + inFunction("var x; x = 100, print(x), print(x), x = 101;", + "var x; x = 100, print(x), print(x), 101;"); + inFunction("var x; x = 100, print(x), x = 0, print(x), x = 101;", + "var x; x = 100, print(x), x = 0, print(x), 101;"); + } + + public void testMutipleDeadAssignmentsButAlivePartiallyWithinTheExpression() { + inFunction("var x; x = 1, x = 2, x = 3, x = 4, x = 5," + + " print(x), x = 0, print(x), x = 101;", + + "var x; 1, 2, 3, 4, x = 5, print(x), x = 0, print(x), 101;"); + } + + + public void testDeadPartiallyWithinTheExpression() { + // Sadly, this is not covered. We don't suspect this would happen too + // often. + inFunction("var x; x = 100, x = 101; print(x);"); + } + + public void testAssignmentChain() { + inFunction("var a,b,c,d,e; a = b = c = d = e = 1", + "var a,b,c,d,e; 1"); + inFunction("var a,b,c,d,e; a = b = c = d = e = 1; print(c)", + "var a,b,c,d,e; c = 1 ; print(c)"); + inFunction("var a,b,c,d,e; a = b = c = d = e = 1; print(a + e)", + "var a,b,c,d,e; a = e = 1; print(a + e)"); + inFunction("var a,b,c,d,e; a = b = c = d = e = 1; print(b + d)", + "var a,b,c,d,e; b = d = 1; print(b + d)"); + inFunction("var a,b,c,d,e; a = b = c = d = e = 1; print(a + b + d + e)", + "var a,b,c,d,e; a = b = d = e = 1; print(a + b + d + e)"); + inFunction("var a,b,c,d,e; a = b = c = d = e = 1; print(a+b+c+d+e)"); + } + + public void testAssignmentOpChain() { + inFunction("var a,b,c,d,e; a = b = c += d = e = 1", + "var a,b,c,d,e; c + 1"); + inFunction("var a,b,c,d,e; a = b = c += d = e = 1; print(e)", + "var a,b,c,d,e; c + (e = 1); print(e)"); + inFunction("var a,b,c,d,e; a = b = c += d = e = 1; print(d)", + "var a,b,c,d,e; c + (d = 1) ; print(d)"); + inFunction("var a,b,c,d,e; a = b = c += d = e = 1; print(a)", + "var a,b,c,d,e; a = c + 1; print(a)"); + } + + public void testIncDecInSubExpressions() { + inFunction("var a; a = 1, a++; a"); + inFunction("var a; a = 1, ++a; a"); + inFunction("var a; a = 1, a--; a"); + inFunction("var a; a = 1, --a; a"); + + inFunction("var a; a = 1, a++, print(a)"); + inFunction("var a; a = 1, ++a, print(a)"); + inFunction("var a; a = 1, a--, print(a)"); + inFunction("var a; a = 1, --a, print(a)"); + + inFunction("var a; a = 1, print(a++)"); + inFunction("var a; a = 1, print(++a)"); + + inFunction("var a; a = 1, print(a++)"); + inFunction("var a; a = 1, print(++a)"); + + inFunction("var a; a = 1, print(a--)"); + inFunction("var a; a = 1, print(--a)"); + } + + public void testNestedReassignments() { + inFunction("var a; a = (a = 1)", "var a; 1"); + inFunction("var a; a = (a *= 2)", "var a; a*2"); + + // Note a = (a++) is not same as a++. Only if 'a' is dead. + inFunction("var a; a = (a++)", "var a; a++"); // Preferred: "var a" + inFunction("var a; a = (++a)", "var a; ++a"); // Preferred: "var a" + + inFunction("var a; a = (b = (a = 1))", "var a; b = 1"); + inFunction("var a; a = (b = (a *= 2))", "var a; b = a * 2"); + inFunction("var a; a = (b = (a++))", "var a; b=a++"); + inFunction("var a; a = (b = (++a))", "var a; b=++a"); + + // Include b as local. + inFunction("var a,b; a = (b = (a = 1))", "var a,b; 1"); + inFunction("var a,b; a = (b = (a *= 2))", "var a,b; a * 2"); + inFunction("var a,b; a = (b = (a++))", + "var a,b; a++"); // Preferred: "var a,b" + inFunction("var a,b; a = (b = (++a))", + "var a,b; ++a"); // Preferred: "var a,b" + + inFunction("var a; a += (a++)", "var a; a + a++"); + inFunction("var a; a += (++a)", "var a; a+ (++a)"); + + // Include b as local. + inFunction("var a,b; a += (b = (a = 1))", "var a,b; a + 1"); + inFunction("var a,b; a += (b = (a *= 2))", "var a,b; a + (a * 2)"); + inFunction("var a,b; a += (b = (a++))", "var a,b; a + a++"); + inFunction("var a,b; a += (b = (++a))", "var a,b; a+(++a)"); + } + + public void testIncrementalReassignmentInForLoops() { + inFunction("for(;x+=1;x+=1) {}"); + inFunction("for(;x;x+=1){}"); + inFunction("for(;x+=1;){foo(x)}"); + inFunction("for(;1;x+=1){foo(x)}"); + } + + public void testIdentityAssignments() { + inFunction("var x; x=x", "var x; x"); + } + + private void inFunction(String src) { + inFunction(src, src); + } + + private void inFunction(String src, String expected) { + test("function FUNC(param1, param2){" + src + "}", + "function FUNC(param1, param2){" + expected + "}"); + } + + public void testBug8730257() { + inFunction( + " try {" + + " var sortIndices = {};" + + " sortIndices = bar();" + + " for (var i = 0; i < 100; i++) {" + + " var sortIndex = sortIndices[i];" + + " bar(sortIndex);" + + " }" + + " } finally {" + + " bar();" + + " }" ); + } + + public void testAssignToExtern() { + inFunction("extern = true;"); + } + + public void testIssue297a() { + testSame("function f(p) {" + + " var x;" + + " return ((x=p.id) && (x=parseInt(x.substr(1))) && x>0);" + + "}; f('');"); + } + + public void testIssue297b() { + test("function f() {" + + " var x;" + + " return (x='') && (x = x.substr(1));" + + "};", + "function f() {" + + " var x;" + + " return (x='') && (x.substr(1));" + + "};"); + } + + public void testIssue297c() { + test("function f() {" + + " var x;" + + " return (x=1) && (x = f(x));" + + "};", + "function f() {" + + " var x;" + + " return (x=1) && f(x);" + + "};"); + } + + public void testIssue297d() { + test("function f(a) {" + + " return (a=1) && (a = f(a));" + + "};", + "function f(a) {" + + " return (a=1) && (f(a));" + + "};"); + } + + public void testIssue297e() { + test("function f(a) {" + + " return (a=1) - (a = g(a));" + + "};", + "function f(a) {" + + " return (a=1) - (g(a));" + + "};"); + } + + public void testIssue297f() { + test("function f(a) {" + + " h((a=1) - (a = g(a)));" + + "};", + "function f(a) {" + + " h((a=1) - (g(a)));" + + "};"); + } + + public void testIssue297g() { + test("function f(a) {" + + " var b = h((b=1) - (b = g(b)));" + + " return b;" + + "};", + // The last assignment in the initializer should be eliminated + "function f(a) {" + + " var b = h((b=1) - (b = g(b)));" + + " return b;" + + "};"); + } + + public void testIssue297h() { + test("function f(a) {" + + " var b = b=1;" + + " return b;" + + "};", + // The assignment in the initializer should be eliminated + "function f(a) {" + + " var b = b = 1;" + + " return b;" + + "};"); + } + + + public void testInExpression1() { + inFunction("var a; return a=(a=(a=3));", "var a; return 3;"); + inFunction("var a; return a=(a=(a=a));", "var a; return a;"); + inFunction("var a; return a=(a=(a=a+1)+1);", "var a; return a+1+1;"); + inFunction("var a; return a=(a=(a=f(a)+1)+1);", "var a; return f(a)+1+1;"); + inFunction("var a; return a=f(a=f(a=f(a)));", "var a; return f(f(f(a)));"); + } + + public void testInExpression2() { + // This can be improved. "a = 1" is dead but "a" is read in the following + // expression. + inFunction( + "var a; a = 1; if ((a = 2) || (a = 3) || (a)) {}", + "var a; a = 1; if (( 2) || (a = 3) || (a)) {}"); + + inFunction( + "var a; (a = 1) || (a = 2)", + "var a; 1 || 2"); + + inFunction("var a; (a = 1) || (a = 2); return a"); + + inFunction( + "var a; a = 1; a ? a = 2 : a;", + "var a; a = 1; a ? 2 : a;"); + + inFunction("var a; a = 1; a ? a = 2 : a; return a"); + + inFunction( + "var a; a = 1; a ? a : a = 2;", + "var a; a = 1; a ? a : 2;"); + + inFunction("var a; a = 1; a ? a : a =2; return a"); + + inFunction( + "var a; (a = 1) ? a = 2 : a = 3;", + "var a; 1 ? 2 : 3;"); + + // This can be improved. "a = 1" is dead but "a" is read in the following + // expression. + inFunction("var a; (a = 1) ? a = 2 : a = 3; return a"); + } + + public void testIssue384a() { + inFunction( + " var a, b;\n" + + " if (f(b = true) || f(b = false))\n" + + " a = b;\n" + + " else\n" + + " a = null;\n" + + " return a;"); + } + + public void testIssue384b() { + inFunction( + " var a, b;\n" + + " (f(b = true) || f(b = false)) ? (a = b) : (a = null);\n" + + " return a;"); + } + + public void testIssue384c() { + inFunction( + " var a, b;\n" + + " (a ? f(b = true) : f(b = false)) && (a = b);\n" + + " return a;"); + } + + public void testIssue384d() { + inFunction( + " var a, b;\n" + + " (f(b = true) || f(b = false)) && (a = b);\n" + + " return a;"); + } + + public void testForIn() { + inFunction("var x = {}; for (var y in x) { y() }"); + inFunction("var x, y, z; x = {}; z = {}; for (y in x = z) { y() }", + "var x, y, z; ({}); z = {}; for (y in z) { y() }"); + inFunction("var x, y, z; x = {}; z = {}; for (y[z=1] in z) { y() }", + "var x, y, z; ({}); z = {}; for (y[z=1] in z) { y() }"); + + // "x in z" doesn't overwrite x if z is empty. + // TODO(user): If you look outside of just liveness, x = {} is dead. + // That probably requires value numbering or SSA to detect that case. + inFunction("var x, y, z; x = {}; z = {}; for (x in z) { x() }"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DefaultCodingConventionTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DefaultCodingConventionTest.java new file mode 100644 index 0000000..3b7ee99 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DefaultCodingConventionTest.java @@ -0,0 +1,172 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import junit.framework.TestCase; + +/** + * Test class for the default {@link CodingConvention}. + */ +public class DefaultCodingConventionTest extends TestCase { + private CodingConvention conv = CodingConventions.getDefault(); + + public void testVarAndOptionalParams() { + Node args = new Node(Token.PARAM_LIST, + Node.newString(Token.NAME, "a"), + Node.newString(Token.NAME, "b")); + Node optArgs = new Node(Token.PARAM_LIST, + Node.newString(Token.NAME, "opt_a"), + Node.newString(Token.NAME, "opt_b")); + + assertFalse(conv.isVarArgsParameter(args.getFirstChild())); + assertTrue(conv.isVarArgsParameter(args.getLastChild())); + assertFalse(conv.isVarArgsParameter(optArgs.getFirstChild())); + assertTrue(conv.isVarArgsParameter(optArgs.getLastChild())); + + assertTrue(conv.isOptionalParameter(args.getFirstChild())); + assertFalse(conv.isOptionalParameter(args.getLastChild())); + assertTrue(conv.isOptionalParameter(optArgs.getFirstChild())); + assertFalse(conv.isOptionalParameter(optArgs.getLastChild())); + } + + public void testInlineName() { + assertFalse(conv.isConstant("a")); + assertFalse(conv.isConstant("XYZ123_")); + assertFalse(conv.isConstant("ABC")); + assertFalse(conv.isConstant("ABCdef")); + assertFalse(conv.isConstant("aBC")); + assertFalse(conv.isConstant("A")); + assertFalse(conv.isConstant("_XYZ123")); + assertFalse(conv.isConstant("a$b$XYZ123_")); + assertFalse(conv.isConstant("a$b$ABC_DEF")); + assertFalse(conv.isConstant("a$b$A")); + assertFalse(conv.isConstant("a$b$a")); + assertFalse(conv.isConstant("a$b$ABCdef")); + assertFalse(conv.isConstant("a$b$aBC")); + assertFalse(conv.isConstant("a$b$")); + assertFalse(conv.isConstant("$")); + } + + public void testExportedName() { + assertFalse(conv.isExported("_a")); + assertFalse(conv.isExported("_a_")); + assertFalse(conv.isExported("a")); + + assertFalse(conv.isExported("$super", false)); + assertTrue(conv.isExported("$super", true)); + assertTrue(conv.isExported("$super")); + } + + public void testPrivateName() { + assertFalse(conv.isPrivate("a_")); + assertFalse(conv.isPrivate("a")); + assertFalse(conv.isPrivate("_a_")); + } + + public void testEnumKey() { + assertTrue(conv.isValidEnumKey("A")); + assertTrue(conv.isValidEnumKey("123")); + assertTrue(conv.isValidEnumKey("FOO_BAR")); + + assertTrue(conv.isValidEnumKey("a")); + assertTrue(conv.isValidEnumKey("someKeyInCamelCase")); + assertTrue(conv.isValidEnumKey("_FOO_BAR")); + } + + public void testInheritanceDetection1() { + assertNotClassDefining("goog.foo(A, B);"); + } + + public void testInheritanceDetection2() { + assertNotClassDefining("goog.inherits(A, B);"); + } + + public void testInheritanceDetection3() { + assertNotClassDefining("A.inherits(B);"); + } + + public void testInheritanceDetection4() { + assertNotClassDefining("goog.inherits(goog.A, goog.B);"); + } + + public void testInheritanceDetection5() { + assertNotClassDefining("goog.A.inherits(goog.B);"); + } + + public void testInheritanceDetection6() { + assertNotClassDefining("A.inherits(this.B);"); + } + + public void testInheritanceDetection7() { + assertNotClassDefining("this.A.inherits(B);"); + } + + public void testInheritanceDetection8() { + assertNotClassDefining("goog.inherits(A, B, C);"); + } + + public void testInheritanceDetection9() { + assertNotClassDefining("A.mixin(B.prototype);"); + } + + public void testInheritanceDetection10() { + assertNotClassDefining("goog.mixin(A.prototype, B.prototype);"); + } + + public void testInheritanceDetectionPostCollapseProperties() { + assertNotClassDefining("goog$inherits(A, B);"); + assertNotClassDefining("goog$inherits(A);"); + } + + public void testFunctionBind() { + assertNotFunctionBind("goog.bind(f)"); + assertNotFunctionBind("goog$bind(f)"); + assertNotFunctionBind("goog.partial(f)"); + assertNotFunctionBind("goog$partial(f)"); + + assertFunctionBind("(function(){}).bind()"); + assertFunctionBind("(function(){}).bind(obj)"); + assertFunctionBind("(function(){}).bind(obj, p1)"); + + assertNotFunctionBind("Function.prototype.bind.call()"); + assertFunctionBind("Function.prototype.bind.call(obj)"); + assertFunctionBind("Function.prototype.bind.call(obj, p1)"); + } + + private void assertFunctionBind(String code) { + Node n = parseTestCode(code); + assertNotNull(conv.describeFunctionBind(n.getFirstChild())); + } + + private void assertNotFunctionBind(String code) { + Node n = parseTestCode(code); + assertNull(conv.describeFunctionBind(n.getFirstChild())); + } + + private void assertNotClassDefining(String code) { + Node n = parseTestCode(code); + assertNull(conv.getClassesDefinedByCall(n.getFirstChild())); + } + + private Node parseTestCode(String code) { + Compiler compiler = new Compiler(); + return compiler.parseTestCode(code).getFirstChild(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DefinitionsRemoverTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DefinitionsRemoverTest.java new file mode 100644 index 0000000..d6afab3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DefinitionsRemoverTest.java @@ -0,0 +1,92 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.DefinitionsRemover.Definition; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.Node; + +import java.util.List; + +/** + * Test for {@link DefinitionsRemover}. Basically test for the simple removal + * cases. More complicated cases will be tested by the clients of + * {@link DefinitionsRemover}. + * + */ +public class DefinitionsRemoverTest extends CompilerTestCase { + public void testRemoveFunction() { + testSame("{(function (){bar()})}"); + test("{function a(){bar()}}", "{}"); + test("foo(); function a(){} bar()", "foo(); bar();"); + test("foo(); function a(){} function b(){} bar()", "foo(); bar();"); + } + + public void testRemoveAssignment() { + test("x = 0;", "0"); + test("{x = 0}", "{0}"); + test("x = 0; y = 0;", "0; 0;"); + test("for (x = 0;x;x) {};", "for(0;x;x) {};"); + } + + public void testRemoveVarAssignment() { + test("var x = 0;", "0"); + test("{var x = 0}", "{0}"); + test("var x = 0; var y = 0;", "0;0"); + test("var x = 0; var y = 0;", "0;0"); + } + + public void testRemoveLiteral() { + test("foo({ 'one' : 1 })", "foo({ })"); + test("foo({ 'one' : 1 , 'two' : 2 })", "foo({ })"); + } + + public void testRemoveFunctionExpressionName() { + test("foo(function f(){})", "foo(function (){})"); + } + + @Override + protected CompilerPass getProcessor(final Compiler compiler) { + // Create a pass that removes all the definitions. + return new CompilerPass() { + @Override + public void process(Node externs, Node root) { + DefinitionsGatherer g = new DefinitionsGatherer(); + (new NodeTraversal(compiler, g)).traverse(root); + for (Definition def : g.definitions) { + def.remove(); + compiler.reportCodeChange(); + } + } + }; + } + + /** + * Gather all possible definition objects. + */ + private static class DefinitionsGatherer extends AbstractPostOrderCallback { + final List definitions = Lists.newArrayList(); + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + Definition def = DefinitionsRemover.getDefinition(n, false); + if (def != null) { + definitions.add(def); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DenormalizeTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DenormalizeTest.java new file mode 100644 index 0000000..7485331 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DenormalizeTest.java @@ -0,0 +1,128 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.Normalize.NormalizeStatements; +import com.google.javascript.rhino.Node; + +/** + * @author johnlenz@google.com (John Lenz) + * + */ +public class DenormalizeTest extends CompilerTestCase { + @Override + public CompilerPass getProcessor(final Compiler compiler) { + return new NormalizeAndDenormalizePass(compiler); + } + + @Override + protected int getNumRepetitions() { + // The normalize pass is only run once. + return 1; + } + + public void testFor() { + // Verify assignments are moved into the FOR init node. + test("a = 0; for(; a < 2 ; a++) foo()", + "for(a = 0; a < 2 ; a++) foo();"); + // Verify vars are are moved into the FOR init node. + test("var a = 0; for(; c < b ; c++) foo()", + "for(var a = 0; c < b ; c++) foo()"); + + // We don't handle labels yet. + testSame("var a = 0; a:for(; c < b ; c++) foo()"); + testSame("var a = 0; a:b:for(; c < b ; c++) foo()"); + + // Verify FOR inside IFs. + test("if(x){var a = 0; for(; c < b; c++) foo()}", + "if(x){for(var a = 0; c < b; c++) foo()}"); + + // Any other expression. + test("init(); for(; a < 2 ; a++) foo()", + "for(init(); a < 2 ; a++) foo();"); + + // Other statements are left as is. + test("function f(){ var a; for(; a < 2 ; a++) foo() }", + "function f(){ for(var a; a < 2 ; a++) foo() }"); + testSame("function f(){ return; for(; a < 2 ; a++) foo() }"); + } + + public void testForIn() { + test("var a; for(a in b) foo()", "for (var a in b) foo()"); + testSame("a = 0; for(a in b) foo()"); + testSame("var a = 0; for(a in b) foo()"); + + // We don't handle labels yet. + testSame("var a; a:for(a in b) foo()"); + testSame("var a; a:b:for(a in b) foo()"); + + // Verify FOR inside IFs. + test("if(x){var a; for(a in b) foo()}", + "if(x){for(var a in b) foo()}"); + + // Any other expression. + testSame("init(); for(a in b) foo()"); + + // Other statements are left as is. + testSame("function f(){ return; for(a in b) foo() }"); + } + + public void testInOperatorNotInsideFor() { + // in operators shouldn't be moved into for loops. + // Some JavaScript interpreters (such as the NetFront Access browser + // embedded in the PlayStation 3) will not parse an in operator in + // a for loop, even if it's protected by parentheses. + + // Make sure the in operator doesn't get moved into the for loop. + testSame("function f(){ var a; var i=\"length\" in a;" + + "for(; a < 2 ; a++) foo() }"); + // Same, but with parens around the operator. + testSame("function f(){ var a; var i=(\"length\" in a);" + + "for(; a < 2 ; a++) foo() }"); + // Make sure Normalize yanks the variable initializer out, and + // Denormalize doesn't put it back. + test("function f(){" + + "var b,a=0; for (var i=(\"length\" in b);a<2; a++) foo()}", + "function f(){var b; var a=0;var i=(\"length\" in b);" + + "for (;a<2;a++) foo()}"); + } + + /** + * Create a class to combine the Normalize and Denormalize passes. + * This is needed because the enableNormalize() call on CompilerTestCase + * causes normalization of the result *and* the expected string, and + * we really don't want the compiler twisting the expected code around. + */ + public class NormalizeAndDenormalizePass implements CompilerPass { + Denormalize denormalizePass; + NormalizeStatements normalizePass; + AbstractCompiler compiler; + + public NormalizeAndDenormalizePass(AbstractCompiler compiler) { + this.compiler = compiler; + denormalizePass = new Denormalize(compiler); + normalizePass = new NormalizeStatements(compiler, false); + } + + @Override + public void process(Node externs, Node root) { + NodeTraversal.traverse(compiler, root, normalizePass); + NodeTraversal.traverse(compiler, root, denormalizePass); + } + } + +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DevirtualizePrototypeMethodsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DevirtualizePrototypeMethodsTest.java new file mode 100644 index 0000000..80da746 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DevirtualizePrototypeMethodsTest.java @@ -0,0 +1,740 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.JSType; + +import java.util.List; + +/** + * Tests for {@link DevirtualizePrototypeMethods} + * + */ +public class DevirtualizePrototypeMethodsTest extends CompilerTestCase { + private static final String EXTERNAL_SYMBOLS = + "var extern;extern.externalMethod"; + private final List typeInformation; + + public DevirtualizePrototypeMethodsTest() { + super(EXTERNAL_SYMBOLS); + typeInformation = Lists.newArrayList(); + } + + @Override + protected int getNumRepetitions() { + // run pass once. + return 1; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + super.enableLineNumberCheck(true); + disableTypeCheck(); + } + + /** + * Combine source strings using '\n' as the separator. + */ + private static String newlineJoin(String ... parts) { + return Joiner.on("\n").join(parts); + } + + /** + * Combine source strings using ';' as the separator. + */ + private static String semicolonJoin(String ... parts) { + return Joiner.on(";").join(parts); + } + + /** + * Inputs for prototype method tests. + */ + private static class RewritePrototypeMethodTestInput { + static final String INPUT = newlineJoin( + "/** @constructor */", + "function a(){ this.x = 3; }", + "/** @return {number} */", + "a.prototype.foo = function() {return this.x};", + "/** @param {number} p\n@return {number} */", + "a.prototype.bar = function(p) {return this.x};", + "a.prototype.baz = function() {};", + "var o = new a;", + "o.foo();", + "o.bar(2);", + "o.baz()"); + + static final String EXPECTED = newlineJoin( + "function a(){ this.x = 3; }", + "var JSCompiler_StaticMethods_foo = ", + "function(JSCompiler_StaticMethods_foo$self) {", + " return JSCompiler_StaticMethods_foo$self.x", + "};", + "var JSCompiler_StaticMethods_bar = ", + "function(JSCompiler_StaticMethods_bar$self, p) {", + " return JSCompiler_StaticMethods_bar$self.x", + "};", + "var JSCompiler_StaticMethods_baz = ", + "function(JSCompiler_StaticMethods_baz$self) {", + "};", + "var o = new a;", + "JSCompiler_StaticMethods_foo(o);", + "JSCompiler_StaticMethods_bar(o, 2);", + "JSCompiler_StaticMethods_baz(o)"); + + static final List EXPECTED_TYPE_CHECKING_OFF = ImmutableList.of( + "FUNCTION a = null", + "NAME JSCompiler_StaticMethods_foo$self = null", + "FUNCTION JSCompiler_StaticMethods_foo = null", + "NAME JSCompiler_StaticMethods_bar$self = null", + "FUNCTION JSCompiler_StaticMethods_bar = null", + "FUNCTION JSCompiler_StaticMethods_baz = null", + "NEW a = null", + "CALL JSCompiler_StaticMethods_foo = null", + "CALL JSCompiler_StaticMethods_bar = null", + "CALL JSCompiler_StaticMethods_baz = null"); + + static final List EXPECTED_TYPE_CHECKING_ON = ImmutableList.of( + "FUNCTION a = function (new:a): undefined", + "NAME JSCompiler_StaticMethods_foo$self = a", + "FUNCTION JSCompiler_StaticMethods_foo = function (a): number", + "NAME JSCompiler_StaticMethods_bar$self = a", + "FUNCTION JSCompiler_StaticMethods_bar = function (a, number): number", + "FUNCTION JSCompiler_StaticMethods_baz = function (a): undefined", + "NEW a = a", + "CALL JSCompiler_StaticMethods_foo = number", + "CALL JSCompiler_StaticMethods_bar = number", + "CALL JSCompiler_StaticMethods_baz = undefined"); + + private RewritePrototypeMethodTestInput() {} + } + + public void testRewritePrototypeMethods1() throws Exception { + // type checking off + disableTypeCheck(); + checkTypes(RewritePrototypeMethodTestInput.INPUT, + RewritePrototypeMethodTestInput.EXPECTED, + RewritePrototypeMethodTestInput.EXPECTED_TYPE_CHECKING_OFF); + } + + public void testRewritePrototypeMethods2() throws Exception { + // type checking on + enableTypeCheck(CheckLevel.ERROR); + checkTypes(RewritePrototypeMethodTestInput.INPUT, + RewritePrototypeMethodTestInput.EXPECTED, + RewritePrototypeMethodTestInput.EXPECTED_TYPE_CHECKING_ON); + } + + public void testRewriteChained() throws Exception { + String source = newlineJoin( + "A.prototype.foo = function(){return this.b};", + "B.prototype.bar = function(){};", + "o.foo().bar()"); + + String expected = newlineJoin( + "var JSCompiler_StaticMethods_foo = ", + "function(JSCompiler_StaticMethods_foo$self) {", + " return JSCompiler_StaticMethods_foo$self.b", + "};", + "var JSCompiler_StaticMethods_bar = ", + "function(JSCompiler_StaticMethods_bar$self) {", + "};", + "JSCompiler_StaticMethods_bar(JSCompiler_StaticMethods_foo(o))"); + test(source, expected); + } + + /** + * Inputs for declaration used as an r-value tests. + */ + private static class NoRewriteDeclarationUsedAsRValue { + static final String DECL = "a.prototype.foo = function() {}"; + static final String CALL = "o.foo()"; + + private NoRewriteDeclarationUsedAsRValue() {} + } + + public void testRewriteDeclIsExpressionStatement() throws Exception { + test(semicolonJoin(NoRewriteDeclarationUsedAsRValue.DECL, + NoRewriteDeclarationUsedAsRValue.CALL), + "var JSCompiler_StaticMethods_foo =" + + "function(JSCompiler_StaticMethods_foo$self) {};" + + "JSCompiler_StaticMethods_foo(o)"); + } + + public void testNoRewriteDeclUsedAsAssignmentRhs() throws Exception { + testSame(semicolonJoin("var c = " + NoRewriteDeclarationUsedAsRValue.DECL, + NoRewriteDeclarationUsedAsRValue.CALL)); + } + + public void testNoRewriteDeclUsedAsCallArgument() throws Exception { + testSame(semicolonJoin("f(" + NoRewriteDeclarationUsedAsRValue.DECL + ")", + NoRewriteDeclarationUsedAsRValue.CALL)); + } + + /** + * Inputs for restrict-to-global-scope tests. + */ + private static class NoRewriteIfNotInGlobalScopeTestInput { + static final String INPUT = newlineJoin( + "function a(){}", + "a.prototype.foo = function() {return this.x};", + "var o = new a;", + "o.foo()"); + + private NoRewriteIfNotInGlobalScopeTestInput() {} + } + + public void testRewriteInGlobalScope() throws Exception { + String expected = newlineJoin( + "function a(){}", + "var JSCompiler_StaticMethods_foo = ", + "function(JSCompiler_StaticMethods_foo$self) {", + " return JSCompiler_StaticMethods_foo$self.x", + "};", + "var o = new a;", + "JSCompiler_StaticMethods_foo(o);"); + + test(NoRewriteIfNotInGlobalScopeTestInput.INPUT, expected); + } + + public void testNoRewriteIfNotInGlobalScope1() throws Exception { + testSame("if(true){" + NoRewriteIfNotInGlobalScopeTestInput.INPUT + "}"); + } + + public void testNoRewriteIfNotInGlobalScope2() throws Exception { + testSame("function enclosingFunction() {" + + NoRewriteIfNotInGlobalScopeTestInput.INPUT + + "}"); + } + + public void testNoRewriteNamespaceFunctions() throws Exception { + String source = newlineJoin( + "function a(){}", + "a.foo = function() {return this.x};", + "a.foo()"); + testSame(source); + } + + /** + * Inputs for multiple definition tests. + */ + private static class NoRewriteMultipleDefinitionTestInput { + static final String TEMPLATE = ".prototype.foo = function() {}"; + static final String SOURCE_A = "a" + TEMPLATE; + static final String SOURCE_B = "b" + TEMPLATE; + static final String CALL = "o.foo()"; + + static final String SINGLE_DEFINITION_EXPECTED = + "var JSCompiler_StaticMethods_foo = " + + " function(JSCompiler_StaticMethods_foo$self) {};" + + "JSCompiler_StaticMethods_foo(o)"; + + private NoRewriteMultipleDefinitionTestInput() {} + } + + public void testRewriteSingleDefinition1() throws Exception { + test(semicolonJoin(NoRewriteMultipleDefinitionTestInput.SOURCE_A, + NoRewriteMultipleDefinitionTestInput.CALL), + NoRewriteMultipleDefinitionTestInput.SINGLE_DEFINITION_EXPECTED); + } + + public void testRewriteSingleDefinition2() throws Exception { + test(semicolonJoin(NoRewriteMultipleDefinitionTestInput.SOURCE_B, + NoRewriteMultipleDefinitionTestInput.CALL), + NoRewriteMultipleDefinitionTestInput.SINGLE_DEFINITION_EXPECTED); + } + + public void testNoRewriteMultipleDefinition1() throws Exception { + testSame(semicolonJoin(NoRewriteMultipleDefinitionTestInput.SOURCE_A, + NoRewriteMultipleDefinitionTestInput.SOURCE_A, + NoRewriteMultipleDefinitionTestInput.CALL)); + } + + public void testNoRewriteMultipleDefinition2() throws Exception { + testSame(semicolonJoin(NoRewriteMultipleDefinitionTestInput.SOURCE_B, + NoRewriteMultipleDefinitionTestInput.SOURCE_B, + NoRewriteMultipleDefinitionTestInput.CALL)); + } + + public void testNoRewriteMultipleDefinition3() throws Exception { + testSame(semicolonJoin(NoRewriteMultipleDefinitionTestInput.SOURCE_A, + NoRewriteMultipleDefinitionTestInput.SOURCE_B, + NoRewriteMultipleDefinitionTestInput.CALL)); + } + + /** + * Inputs for object literal tests. + */ + private static class NoRewritePrototypeObjectLiteralsTestInput { + static final String REGULAR = "b.prototype.foo = function() {}"; + static final String OBJ_LIT = "a.prototype = {foo : function() {}}"; + static final String CALL = "o.foo()"; + + private NoRewritePrototypeObjectLiteralsTestInput() {} + } + + public void testRewritePrototypeNoObjectLiterals() throws Exception { + test(semicolonJoin(NoRewritePrototypeObjectLiteralsTestInput.REGULAR, + NoRewritePrototypeObjectLiteralsTestInput.CALL), + "var JSCompiler_StaticMethods_foo = " + + "function(JSCompiler_StaticMethods_foo$self) {};" + + "JSCompiler_StaticMethods_foo(o)"); + } + + public void testRewritePrototypeObjectLiterals1() throws Exception { + test(semicolonJoin(NoRewritePrototypeObjectLiteralsTestInput.OBJ_LIT, + NoRewritePrototypeObjectLiteralsTestInput.CALL), + "a.prototype={};" + + "var JSCompiler_StaticMethods_foo=" + + "function(JSCompiler_StaticMethods_foo$self){};" + + "JSCompiler_StaticMethods_foo(o)"); + } + + public void testNoRewritePrototypeObjectLiterals2() throws Exception { + testSame(semicolonJoin(NoRewritePrototypeObjectLiteralsTestInput.OBJ_LIT, + NoRewritePrototypeObjectLiteralsTestInput.REGULAR, + NoRewritePrototypeObjectLiteralsTestInput.CALL)); + } + + public void testNoRewriteExternalMethods1() throws Exception { + testSame("a.externalMethod()"); + } + + public void testNoRewriteExternalMethods2() throws Exception { + testSame("A.prototype.externalMethod = function(){}; o.externalMethod()"); + } + + public void testNoRewriteCodingConvention() throws Exception { + // no rename, leading _ indicates exported symbol + testSame("a.prototype._foo = function() {};"); + } + + public void testRewriteNoVarArgs() throws Exception { + String source = newlineJoin( + "function a(){}", + "a.prototype.foo = function(args) {return args};", + "var o = new a;", + "o.foo()"); + + String expected = newlineJoin( + "function a(){}", + "var JSCompiler_StaticMethods_foo = ", + " function(JSCompiler_StaticMethods_foo$self, args) {return args};", + "var o = new a;", + "JSCompiler_StaticMethods_foo(o)"); + + test(source, expected); + } + + public void testNoRewriteVarArgs() throws Exception { + String source = newlineJoin( + "function a(){}", + "a.prototype.foo = function(var_args) {return arguments};", + "var o = new a;", + "o.foo()"); + testSame(source); + } + + /** + * Inputs for invalidating reference tests. + */ + private static class NoRewriteNonCallReferenceTestInput { + static final String BASE = newlineJoin( + "function a(){}", + "a.prototype.foo = function() {return this.x};", + "var o = new a;"); + + private NoRewriteNonCallReferenceTestInput() {} + } + + public void testRewriteCallReference() throws Exception { + String expected = newlineJoin( + "function a(){}", + "var JSCompiler_StaticMethods_foo = ", + "function(JSCompiler_StaticMethods_foo$self) {", + " return JSCompiler_StaticMethods_foo$self.x", + "};", + "var o = new a;", + "JSCompiler_StaticMethods_foo(o);"); + + test(NoRewriteNonCallReferenceTestInput.BASE + "o.foo()", expected); + } + + public void testNoRewriteNoReferences() throws Exception { + testSame(NoRewriteNonCallReferenceTestInput.BASE); + } + + public void testNoRewriteNonCallReference() throws Exception { + testSame(NoRewriteNonCallReferenceTestInput.BASE + "o.foo && o.foo()"); + } + + /** + * Inputs for nested definition tests. + */ + private static class NoRewriteNestedFunctionTestInput { + static final String PREFIX = "a.prototype.foo = function() {"; + static final String SUFFIX = "o.foo()"; + static final String INNER = "a.prototype.bar = function() {}; o.bar()"; + static final String EXPECTED_PREFIX = + "var JSCompiler_StaticMethods_foo=" + + "function(JSCompiler_StaticMethods_foo$self){"; + static final String EXPECTED_SUFFIX = + "JSCompiler_StaticMethods_foo(o)"; + + private NoRewriteNestedFunctionTestInput() {} + } + + public void testRewriteNoNestedFunction() throws Exception { + test(semicolonJoin( + NoRewriteNestedFunctionTestInput.PREFIX + "}", + NoRewriteNestedFunctionTestInput.SUFFIX, + NoRewriteNestedFunctionTestInput.INNER), + semicolonJoin( + NoRewriteNestedFunctionTestInput.EXPECTED_PREFIX + "}", + NoRewriteNestedFunctionTestInput.EXPECTED_SUFFIX, + "var JSCompiler_StaticMethods_bar=" + + "function(JSCompiler_StaticMethods_bar$self){}", + "JSCompiler_StaticMethods_bar(o)")); + } + + public void testNoRewriteNestedFunction() throws Exception { + test(NoRewriteNestedFunctionTestInput.PREFIX + + NoRewriteNestedFunctionTestInput.INNER + "};" + + NoRewriteNestedFunctionTestInput.SUFFIX, + NoRewriteNestedFunctionTestInput.EXPECTED_PREFIX + + NoRewriteNestedFunctionTestInput.INNER + "};" + + NoRewriteNestedFunctionTestInput.EXPECTED_SUFFIX); + } + + public void testRewriteImplementedMethod() throws Exception { + String source = newlineJoin( + "function a(){}", + "a.prototype.foo = function(args) {return args};", + "var o = new a;", + "o.foo()"); + String expected = newlineJoin( + "function a(){}", + "var JSCompiler_StaticMethods_foo = ", + " function(JSCompiler_StaticMethods_foo$self, args) {return args};", + "var o = new a;", + "JSCompiler_StaticMethods_foo(o)"); + test(source, expected); + } + + public void testRewriteImplementedMethod2() throws Exception { + String source = newlineJoin( + "function a(){}", + "a.prototype['foo'] = function(args) {return args};", + "var o = new a;", + "o.foo()"); + testSame(source); + } + + public void testRewriteImplementedMethod3() throws Exception { + String source = newlineJoin( + "function a(){}", + "a.prototype.foo = function(args) {return args};", + "var o = new a;", + "o['foo']"); + testSame(source); + } + + public void testRewriteImplementedMethod4() throws Exception { + String source = newlineJoin( + "function a(){}", + "a.prototype['foo'] = function(args) {return args};", + "var o = new a;", + "o['foo']"); + testSame(source); + } + + public void testRewriteImplementedMethodInObj() throws Exception { + String source = newlineJoin( + "function a(){}", + "a.prototype = {foo: function(args) {return args}};", + "var o = new a;", + "o.foo()"); + test(source, + "function a(){}" + + "a.prototype={};" + + "var JSCompiler_StaticMethods_foo=" + + "function(JSCompiler_StaticMethods_foo$self,args){return args};" + + "var o=new a;" + + "JSCompiler_StaticMethods_foo(o)"); + } + + public void testNoRewriteGet1() throws Exception { + // Getters and setter require special handling. + String source = newlineJoin( + "function a(){}", + "a.prototype = {get foo(){return f}};", + "var o = new a;", + "o.foo()"); + testSame(source); + } + + public void testNoRewriteGet2() throws Exception { + // Getters and setter require special handling. + String source = newlineJoin( + "function a(){}", + "a.prototype = {get foo(){return 1}};", + "var o = new a;", + "o.foo"); + testSame(source); + } + + public void testNoRewriteSet1() throws Exception { + // Getters and setter require special handling. + String source = newlineJoin( + "function a(){}", + "a.prototype = {set foo(a){}};", + "var o = new a;", + "o.foo()"); + testSame(source); + } + + public void testNoRewriteSet2() throws Exception { + // Getters and setter require special handling. + String source = newlineJoin( + "function a(){}", + "a.prototype = {set foo(a){}};", + "var o = new a;", + "o.foo = 1"); + testSame(source); + } + + public void testNoRewriteNotImplementedMethod() throws Exception { + testSame(newlineJoin("function a(){}", + "var o = new a;", + "o.foo()")); + } + + public void testWrapper() { + testSame("(function() {})()"); + } + + private static class ModuleTestInput { + static final String DEFINITION = "a.prototype.foo = function() {}"; + static final String USE = "x.foo()"; + + static final String REWRITTEN_DEFINITION = + "var JSCompiler_StaticMethods_foo=" + + "function(JSCompiler_StaticMethods_foo$self){}"; + static final String REWRITTEN_USE = + "JSCompiler_StaticMethods_foo(x)"; + + private ModuleTestInput() {} + } + + public void testRewriteSameModule1() throws Exception { + JSModule[] modules = createModuleStar( + // m1 + semicolonJoin(ModuleTestInput.DEFINITION, + ModuleTestInput.USE), + // m2 + ""); + + test(modules, new String[] { + // m1 + semicolonJoin(ModuleTestInput.REWRITTEN_DEFINITION, + ModuleTestInput.REWRITTEN_USE), + // m2 + "", + }); + } + + public void testRewriteSameModule2() throws Exception { + JSModule[] modules = createModuleStar( + // m1 + "", + // m2 + semicolonJoin(ModuleTestInput.DEFINITION, + ModuleTestInput.USE)); + + test(modules, new String[] { + // m1 + "", + // m2 + semicolonJoin(ModuleTestInput.REWRITTEN_DEFINITION, + ModuleTestInput.REWRITTEN_USE) + }); + } + + public void testRewriteSameModule3() throws Exception { + JSModule[] modules = createModuleStar( + // m1 + semicolonJoin(ModuleTestInput.USE, + ModuleTestInput.DEFINITION), + // m2 + ""); + + test(modules, new String[] { + // m1 + semicolonJoin(ModuleTestInput.REWRITTEN_USE, + ModuleTestInput.REWRITTEN_DEFINITION), + // m2 + "" + }); + } + + public void testRewriteDefinitionBeforeUse() throws Exception { + JSModule[] modules = createModuleStar( + // m1 + ModuleTestInput.DEFINITION, + // m2 + ModuleTestInput.USE); + + test(modules, new String[] { + // m1 + ModuleTestInput.REWRITTEN_DEFINITION, + // m2 + ModuleTestInput.REWRITTEN_USE + }); + } + + public void testNoRewriteUseBeforeDefinition() throws Exception { + JSModule[] modules = createModuleStar( + // m1 + ModuleTestInput.USE, + // m2 + ModuleTestInput.DEFINITION); + + testSame(modules); + } + + /** + * Verifies that the compiler pass's output matches the expected + * output, and that nodes are annotated with the expected jstype + * information. + */ + private void checkTypes(String source, + String expected, + List expectedTypes) { + typeInformation.clear(); + test(source, expected); + assertEquals(expectedTypes, typeInformation); + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new TypeInformationGatherer( + compiler, new DevirtualizePrototypeMethods(compiler), typeInformation); + } + + /** + * Wrapper that gathers function, call, and self variable type strings after + * the pass under test runs. For use to test passes that modify JSType + * annotations. + */ + private static class TypeInformationGatherer + implements CompilerPass { + private final Compiler compiler; + private final CompilerPass passUnderTest; + private final List typeInformation; + + TypeInformationGatherer(Compiler compiler, + CompilerPass passUnderTest, + List typeInformation) { + this.compiler = compiler; + this.passUnderTest = passUnderTest; + this.typeInformation = typeInformation; + } + + @Override + public void process(Node externs, Node root) { + passUnderTest.process(externs, root); + NodeTraversal.traverse(compiler, externs, new GatherCallback()); + NodeTraversal.traverse(compiler, root, new GatherCallback()); + } + + public String getNameString(Node n) { + int type = n.getType(); + if (type == Token.NAME) { + return n.getString(); + } else if (type == Token.GETPROP) { + String left = getNameString(n.getFirstChild()); + if (left == null) { + return null; + } + return left + "." + n.getLastChild().getString(); + } else if (type == Token.GETELEM) { + String left = getNameString(n.getFirstChild()); + if (left == null) { + return null; + } + return left + "[" + n.getLastChild().getString() + "]"; + } else if (type == Token.THIS) { + return "this"; + } else if (type == Token.FUNCTION){ + return "{ANON FUNCTION}"; + } else { + // I wonder if we should just die on this. + return null; + } + } + + private class GatherCallback extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal traversal, Node node, Node parent) { + Node nameNode = null; + if (node.isFunction()) { + if (parent.isName()) { + nameNode = parent; + } else if (parent.isAssign()) { + nameNode = parent.getFirstChild(); + } else { + nameNode = node.getFirstChild(); + } + } else if (node.isCall() || node.isNew()) { + nameNode = node.getFirstChild(); + } + + if (nameNode != null) { + JSType type = node.getJSType(); + typeInformation.add( + Joiner.on("").join( + Token.name(node.getType()), + " ", + getNameString(nameNode), + " = ", + (type != null) ? type.toString() : "null")); + } + + if (node.isGetProp()) { + Node child = node.getFirstChild(); + if (child.isName() && child.getString().endsWith("$self")) { + JSType type = child.getJSType(); + typeInformation.add( + Joiner.on("").join( + Token.name(child.getType()), + " ", + child.getString(), + " = ", + (type != null) ? type.toString() : "null")); + } + } + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DiagnosticGroupTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DiagnosticGroupTest.java new file mode 100644 index 0000000..f160525 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DiagnosticGroupTest.java @@ -0,0 +1,32 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import junit.framework.TestCase; + +/** + * Tests for DiagnosticGroup. + * @author nicksantos@google.com (Nick Santos) + */ +public class DiagnosticGroupTest extends TestCase { + + public void testRegistration() throws Exception { + DiagnosticGroups dg = new DiagnosticGroups(); + assertEquals(DiagnosticGroups.DEPRECATED, + dg.forName("deprecated")); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DisambiguatePropertiesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DisambiguatePropertiesTest.java new file mode 100644 index 0000000..9446d2f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DisambiguatePropertiesTest.java @@ -0,0 +1,1382 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.testing.BaseJSTypeTestCase; +import com.google.javascript.rhino.testing.TestErrorReporter; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +/** + * Unit test for the Compiler DisambiguateProperties pass. + * + */ +public class DisambiguatePropertiesTest extends CompilerTestCase { + private DisambiguateProperties lastPass; + private boolean runTightenTypes; + + public DisambiguatePropertiesTest() { + parseTypeInfo = true; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + super.enableNormalize(true); + super.enableTypeCheck(CheckLevel.WARNING); + } + + @Override + public CompilerPass getProcessor(final Compiler compiler) { + + return new CompilerPass() { + @Override + public void process(Node externs, Node root) { + Map propertiesToErrorFor = + Maps.newHashMap(); + propertiesToErrorFor.put("foobar", CheckLevel.ERROR); + + if (runTightenTypes) { + TightenTypes tightener = new TightenTypes(compiler); + tightener.process(externs, root); + lastPass = DisambiguateProperties.forConcreteTypeSystem(compiler, + tightener, propertiesToErrorFor); + } else { + // This must be created after type checking is run as it depends on + // any mismatches found during checking. + lastPass = DisambiguateProperties.forJSTypeSystem( + compiler, propertiesToErrorFor); + } + + lastPass.process(externs, root); + } + }; + } + + @Override + protected int getNumRepetitions() { + return 1; + } + + public void testOneType1() { + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a = 0;\n" + + "/** @type Foo */\n" + + "var F = new Foo;\n" + + "F.a = 0;"; + testSets(false, js, js, "{a=[[Foo.prototype]]}"); + testSets(true, js, js, "{a=[[Foo.prototype]]}"); + } + + public void testOneType2() { + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype = {a: 0};\n" + + "/** @type Foo */\n" + + "var F = new Foo;\n" + + "F.a = 0;"; + String expected = "{a=[[Foo.prototype]]}"; + testSets(false, js, js, expected); + testSets(true, js, js, expected); + } + + public void testOneType3() { + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype = { get a() {return 0}," + + " set a(b) {} };\n" + + "/** @type Foo */\n" + + "var F = new Foo;\n" + + "F.a = 0;"; + String expected = "{a=[[Foo.prototype]]}"; + testSets(false, js, js, expected); + testSets(true, js, js, expected); + } + + public void testPrototypeAndInstance() { + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a = 0;\n" + + "/** @type Foo */\n" + + "var F = new Foo;\n" + + "F.a = 0;"; + testSets(false, js, js, "{a=[[Foo.prototype]]}"); + testSets(true, js, js, "{a=[[Foo.prototype]]}"); + } + + public void testPrototypeAndInstance2() { + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a = 0;\n" + + "new Foo().a = 0;"; + testSets(false, js, js, "{a=[[Foo.prototype]]}"); + testSets(true, js, js, "{a=[[Foo.prototype]]}"); + } + + public void testTwoTypes1() { + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a = 0;" + + "/** @type Foo */\n" + + "var F = new Foo;\n" + + "F.a = 0;" + + "/** @constructor */ function Bar() {}\n" + + "Bar.prototype.a = 0;" + + "/** @type Bar */\n" + + "var B = new Bar;\n" + + "B.a = 0;"; + String output = "" + + "function Foo(){}" + + "Foo.prototype.Foo_prototype$a=0;" + + "var F=new Foo;" + + "F.Foo_prototype$a=0;" + + "function Bar(){}" + + "Bar.prototype.Bar_prototype$a=0;" + + "var B=new Bar;" + + "B.Bar_prototype$a=0"; + testSets(false, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); + testSets(true, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); + } + + public void testTwoTypes2() { + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype = {a: 0};" + + "/** @type Foo */\n" + + "var F = new Foo;\n" + + "F.a = 0;" + + "/** @constructor */ function Bar() {}\n" + + "Bar.prototype = {a: 0};" + + "/** @type Bar */\n" + + "var B = new Bar;\n" + + "B.a = 0;"; + + String output = "" + + "function Foo(){}" + + "Foo.prototype = {Foo_prototype$a: 0};" + + "var F=new Foo;" + + "F.Foo_prototype$a=0;" + + "function Bar(){}" + + "Bar.prototype = {Bar_prototype$a: 0};" + + "var B=new Bar;" + + "B.Bar_prototype$a=0"; + + testSets(false, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); + testSets(true, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); + } + + public void testTwoTypes3() { + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype = { get a() {return 0}," + + " set a(b) {} };\n" + + "/** @type Foo */\n" + + "var F = new Foo;\n" + + "F.a = 0;" + + "/** @constructor */ function Bar() {}\n" + + "Bar.prototype = { get a() {return 0}," + + " set a(b) {} };\n" + + "/** @type Bar */\n" + + "var B = new Bar;\n" + + "B.a = 0;"; + + String output = "" + + "function Foo(){}" + + "Foo.prototype = { get Foo_prototype$a() {return 0}," + + " set Foo_prototype$a(b) {} };\n" + + "var F=new Foo;" + + "F.Foo_prototype$a=0;" + + "function Bar(){}" + + "Bar.prototype = { get Bar_prototype$a() {return 0}," + + " set Bar_prototype$a(b) {} };\n" + + "var B=new Bar;" + + "B.Bar_prototype$a=0"; + + testSets(false, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); + testSets(true, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); + } + + public void testTwoFields() { + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a = 0;" + + "Foo.prototype.b = 0;" + + "/** @type Foo */\n" + + "var F = new Foo;\n" + + "F.a = 0;" + + "F.b = 0;"; + String output = "function Foo(){}Foo.prototype.a=0;Foo.prototype.b=0;" + + "var F=new Foo;F.a=0;F.b=0"; + testSets(false, js, output, "{a=[[Foo.prototype]], b=[[Foo.prototype]]}"); + testSets(true, js, output, "{a=[[Foo.prototype]], b=[[Foo.prototype]]}"); + } + + public void testTwoSeparateFieldsTwoTypes() { + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a = 0;" + + "Foo.prototype.b = 0;" + + "/** @type Foo */\n" + + "var F = new Foo;\n" + + "F.a = 0;" + + "F.b = 0;" + + "/** @constructor */ function Bar() {}\n" + + "Bar.prototype.a = 0;" + + "Bar.prototype.b = 0;" + + "/** @type Bar */\n" + + "var B = new Bar;\n" + + "B.a = 0;" + + "B.b = 0;"; + String output = "" + + "function Foo(){}" + + "Foo.prototype.Foo_prototype$a=0;" + + "Foo.prototype.Foo_prototype$b=0;" + + "var F=new Foo;" + + "F.Foo_prototype$a=0;" + + "F.Foo_prototype$b=0;" + + "function Bar(){}" + + "Bar.prototype.Bar_prototype$a=0;" + + "Bar.prototype.Bar_prototype$b=0;" + + "var B=new Bar;" + + "B.Bar_prototype$a=0;" + + "B.Bar_prototype$b=0"; + testSets(false, js, output, "{a=[[Bar.prototype], [Foo.prototype]]," + + " b=[[Bar.prototype], [Foo.prototype]]}"); + testSets(true, js, output, "{a=[[Bar.prototype], [Foo.prototype]]," + + " b=[[Bar.prototype], [Foo.prototype]]}"); + } + + public void testUnionType() { + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a = 0;" + + "/** @constructor */ function Bar() {}\n" + + "Bar.prototype.a = 0;" + + "/** @type {Bar|Foo} */\n" + + "var B = new Bar;\n" + + "B.a = 0;\n" + + "B = new Foo;\n" + + "B.a = 0;\n" + + "/** @constructor */ function Baz() {}\n" + + "Baz.prototype.a = 0;\n"; + testSets(false, js, + "{a=[[Bar.prototype, Foo.prototype], [Baz.prototype]]}"); + testSets(true, js, "{a=[[Bar.prototype, Foo.prototype], [Baz.prototype]]}"); + } + + public void testIgnoreUnknownType() { + String js = "" + + "/** @constructor */\n" + + "function Foo() {}\n" + + "Foo.prototype.blah = 3;\n" + + "/** @type {Foo} */\n" + + "var F = new Foo;\n" + + "F.blah = 0;\n" + + "var U = function() { return {} };\n" + + "U().blah();"; + String expected = "" + + "function Foo(){}Foo.prototype.blah=3;var F = new Foo;F.blah=0;" + + "var U=function(){return{}};U().blah()"; + testSets(false, js, expected, "{}"); + testSets(true, BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES, + js, expected, "{}"); + } + + public void testIgnoreUnknownType1() { + String js = "" + + "/** @constructor */\n" + + "function Foo() {}\n" + + "Foo.prototype.blah = 3;\n" + + "/** @type {Foo} */\n" + + "var F = new Foo;\n" + + "F.blah = 0;\n" + + "/** @return {Object} */\n" + + "var U = function() { return {} };\n" + + "U().blah();"; + String expected = "" + + "function Foo(){}Foo.prototype.blah=3;var F = new Foo;F.blah=0;" + + "var U=function(){return{}};U().blah()"; + testSets(false, js, expected, "{blah=[[Foo.prototype]]}"); + testSets(true, BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES, + js, expected, "{}"); + } + + public void testIgnoreUnknownType2() { + String js = "" + + "/** @constructor */\n" + + "function Foo() {}\n" + + "Foo.prototype.blah = 3;\n" + + "/** @type {Foo} */\n" + + "var F = new Foo;\n" + + "F.blah = 0;\n" + + "/** @constructor */\n" + + "function Bar() {}\n" + + "Bar.prototype.blah = 3;\n" + + "/** @return {Object} */\n" + + "var U = function() { return {} };\n" + + "U().blah();"; + String expected = "" + + "function Foo(){}Foo.prototype.blah=3;var F = new Foo;F.blah=0;" + + "function Bar(){}Bar.prototype.blah=3;" + + "var U=function(){return{}};U().blah()"; + testSets(false, js, expected, "{}"); + testSets(true, BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES, + js, expected, "{}"); + } + + public void testUnionTypeTwoFields() { + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a = 0;\n" + + "Foo.prototype.b = 0;\n" + + "/** @constructor */ function Bar() {}\n" + + "Bar.prototype.a = 0;\n" + + "Bar.prototype.b = 0;\n" + + "/** @type {Foo|Bar} */\n" + + "var B = new Bar;\n" + + "B.a = 0;\n" + + "B.b = 0;\n" + + "B = new Foo;\n" + + "/** @constructor */ function Baz() {}\n" + + "Baz.prototype.a = 0;\n" + + "Baz.prototype.b = 0;\n"; + String output = "" + + "function Foo(){}" + + "Foo.prototype.Bar_prototype$a=0;" + + "Foo.prototype.Bar_prototype$b=0;" + + "function Bar(){}" + + "Bar.prototype.Bar_prototype$a=0;" + + "Bar.prototype.Bar_prototype$b=0;" + + "var B=new Bar;" + + "B.Bar_prototype$a=0;" + + "B.Bar_prototype$b=0;" + + "function Baz(){}" + + "Baz.prototype.a$Baz_prototype=0;" + + "Baz.prototype.b$Baz_prototype=0;"; + testSets(false, js, "{a=[[Bar.prototype, Foo.prototype], [Baz.prototype]]," + + " b=[[Bar.prototype, Foo.prototype], [Baz.prototype]]}"); + testSets(true, js, "{a=[[Bar.prototype, Foo.prototype], [Baz.prototype]]," + + " b=[[Bar.prototype, Foo.prototype], [Baz.prototype]]}"); + } + + public void testCast() { + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a = 0;" + + "/** @constructor */ function Bar() {}\n" + + "Bar.prototype.a = 0;" + + "/** @type {Foo|Bar} */\n" + + "var F = new Foo;\n" + + "(/** @type {Bar} */(F)).a = 0;"; + String output = "" + + "function Foo(){}Foo.prototype.Foo_prototype$a=0;" + + "function Bar(){}Bar.prototype.Bar_prototype$a=0;" + + "var F=new Foo;F.Bar_prototype$a=0;"; + String ttOutput = "" + + "function Foo(){}Foo.prototype.Foo_prototype$a=0;" + + "function Bar(){}Bar.prototype.Bar_prototype$a=0;" + + "var F=new Foo;F.Unique$1$a=0;"; + testSets(false, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); + testSets(true, js, ttOutput, + "{a=[[Bar.prototype], [Foo.prototype], [Unique$1]]}"); + } + + public void testConstructorFields() { + String js = "" + + "/** @constructor */\n" + + "var Foo = function() { this.a = 0; };\n" + + "/** @constructor */ function Bar() {}\n" + + "Bar.prototype.a = 0;" + + "new Foo"; + String output = "" + + "var Foo=function(){this.Foo$a=0};" + + "function Bar(){}" + + "Bar.prototype.Bar_prototype$a=0;" + + "new Foo"; + String ttOutput = "" + + "var Foo=function(){this.Foo_prototype$a=0};" + + "function Bar(){}" + + "Bar.prototype.Bar_prototype$a=0;" + + "new Foo"; + testSets(false, js, output, "{a=[[Bar.prototype], [Foo]]}"); + testSets(true, js, ttOutput, "{a=[[Bar.prototype], [Foo.prototype]]}"); + } + + public void testStaticProperty() { + String js = "" + + "/** @constructor */ function Foo() {} \n" + + "/** @constructor */ function Bar() {}\n" + + "Foo.a = 0;" + + "Bar.a = 0;"; + String output = "" + + "function Foo(){}" + + "function Bar(){}" + + "Foo.function__new_Foo___undefined$a = 0;" + + "Bar.function__new_Bar___undefined$a = 0;"; + + testSets(false, js, output, + "{a=[[function (new:Bar): undefined]," + + " [function (new:Foo): undefined]]}"); + } + + public void testSupertypeWithSameField() { + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a = 0;\n" + + "/** @constructor\n* @extends Foo */ function Bar() {}\n" + + "/** @override */\n" + + "Bar.prototype.a = 0;\n" + + "/** @type Bar */ var B = new Bar;\n" + + "B.a = 0;" + + "/** @constructor */ function Baz() {}\n" + + "Baz.prototype.a = function(){};\n"; + + String output = "" + + "function Foo(){}Foo.prototype.Foo_prototype$a=0;" + + "function Bar(){}Bar.prototype.Foo_prototype$a=0;" + + "var B = new Bar;B.Foo_prototype$a=0;" + + "function Baz(){}Baz.prototype.Baz_prototype$a=function(){};"; + String ttOutput = "" + + "function Foo(){}Foo.prototype.Foo_prototype$a=0;" + + "function Bar(){}Bar.prototype.Bar_prototype$a=0;" + + "var B = new Bar;B.Bar_prototype$a=0;" + + "function Baz(){}Baz.prototype.Baz_prototype$a=function(){};"; + testSets(false, js, output, "{a=[[Baz.prototype], [Foo.prototype]]}"); + testSets(true, js, ttOutput, + "{a=[[Bar.prototype], [Baz.prototype], [Foo.prototype]]}"); + } + + public void testScopedType() { + String js = "" + + "var g = {};\n" + + "/** @constructor */ g.Foo = function() {}\n" + + "g.Foo.prototype.a = 0;" + + "/** @constructor */ g.Bar = function() {}\n" + + "g.Bar.prototype.a = 0;"; + String output = "" + + "var g={};" + + "g.Foo=function(){};" + + "g.Foo.prototype.g_Foo_prototype$a=0;" + + "g.Bar=function(){};" + + "g.Bar.prototype.g_Bar_prototype$a=0;"; + testSets(false, js, output, "{a=[[g.Bar.prototype], [g.Foo.prototype]]}"); + testSets(true, js, output, "{a=[[g.Bar.prototype], [g.Foo.prototype]]}"); + } + + public void testUnresolvedType() { + // NOTE(nicksantos): This behavior seems very wrong to me. + String js = "" + + "var g = {};" + + "/** @constructor \n @extends {?} */ " + + "var Foo = function() {};\n" + + "Foo.prototype.a = 0;" + + "/** @constructor */ var Bar = function() {};\n" + + "Bar.prototype.a = 0;"; + String output = "" + + "var g={};" + + "var Foo=function(){};" + + "Foo.prototype.Foo_prototype$a=0;" + + "var Bar=function(){};" + + "Bar.prototype.Bar_prototype$a=0;"; + testSets(false, BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES, + js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); + testSets(true, BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES, + js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); + } + + public void testNamedType() { + String js = "" + + "var g = {};" + + "/** @constructor \n @extends g.Late */ var Foo = function() {}\n" + + "Foo.prototype.a = 0;" + + "/** @constructor */ var Bar = function() {}\n" + + "Bar.prototype.a = 0;" + + "/** @constructor */ g.Late = function() {}"; + String output = "" + + "var g={};" + + "var Foo=function(){};" + + "Foo.prototype.Foo_prototype$a=0;" + + "var Bar=function(){};" + + "Bar.prototype.Bar_prototype$a=0;" + + "g.Late = function(){}"; + testSets(false, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); + testSets(true, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); + } + + public void testUnknownType() { + String js = "" + + "/** @constructor */ var Foo = function() {};\n" + + "/** @constructor */ var Bar = function() {};\n" + + "/** @return {?} */ function fun() {}\n" + + "Foo.prototype.a = fun();\n" + + "fun().a;\n" + + "Bar.prototype.a = 0;"; + String ttOutput = "" + + "var Foo=function(){};\n" + + "var Bar=function(){};\n" + + "function fun(){}\n" + + "Foo.prototype.Foo_prototype$a=fun();\n" + + "fun().Unique$1$a;\n" + + "Bar.prototype.Bar_prototype$a=0;"; + testSets(false, js, js, "{}"); + testSets(true, BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES, js, ttOutput, + "{a=[[Bar.prototype], [Foo.prototype], [Unique$1]]}"); + } + + public void testEnum() { + String js = "" + + "/** @enum {string} */ var En = {\n" + + " A: 'first',\n" + + " B: 'second'\n" + + "};\n" + + "var EA = En.A;\n" + + "var EB = En.B;\n" + + "/** @constructor */ function Foo(){};\n" + + "Foo.prototype.A = 0;\n" + + "Foo.prototype.B = 0;\n"; + String output = "" + + "var En={A:'first',B:'second'};" + + "var EA=En.A;" + + "var EB=En.B;" + + "function Foo(){};" + + "Foo.prototype.Foo_prototype$A=0;" + + "Foo.prototype.Foo_prototype$B=0"; + String ttOutput = "" + + "var En={A:'first',B:'second'};" + + "var EA=En.A;" + + "var EB=En.B;" + + "function Foo(){};" + + "Foo.prototype.Foo_prototype$A=0;" + + "Foo.prototype.Foo_prototype$B=0"; + testSets(false, js, output, "{A=[[Foo.prototype]], B=[[Foo.prototype]]}"); + testSets(true, js, ttOutput, "{A=[[Foo.prototype]], B=[[Foo.prototype]]}"); + } + + public void testEnumOfObjects() { + String js = "" + + "/** @constructor */ function Formatter() {}" + + "Formatter.prototype.format = function() {};" + + "/** @constructor */ function Unrelated() {}" + + "Unrelated.prototype.format = function() {};" + + "/** @enum {!Formatter} */ var Enum = {\n" + + " A: new Formatter()\n" + + "};\n" + + "Enum.A.format();\n"; + String output = "" + + "/** @constructor */ function Formatter() {}" + + "Formatter.prototype.Formatter_prototype$format = function() {};" + + "/** @constructor */ function Unrelated() {}" + + "Unrelated.prototype.Unrelated_prototype$format = function() {};" + + "/** @enum {!Formatter} */ var Enum = {\n" + + " A: new Formatter()\n" + + "};\n" + + "Enum.A.Formatter_prototype$format();\n"; + testSets(false, js, output, + "{format=[[Formatter.prototype], [Unrelated.prototype]]}"); + + // TODO(nicksantos): Fix the type tightener to handle this case. + // It currently doesn't work, because getSubTypes is broken for enums. + } + + public void testEnumOfObjects2() { + String js = "" + + "/** @constructor */ function Formatter() {}" + + "Formatter.prototype.format = function() {};" + + "/** @constructor */ function Unrelated() {}" + + "Unrelated.prototype.format = function() {};" + + "/** @enum {?Formatter} */ var Enum = {\n" + + " A: new Formatter(),\n" + + " B: new Formatter()\n" + + "};\n" + + "function f() {\n" + + " var formatter = window.toString() ? Enum.A : Enum.B;\n" + + " formatter.format();\n" + + "}"; + String output = "" + + "/** @constructor */ function Formatter() {}" + + "Formatter.prototype.format = function() {};" + + "/** @constructor */ function Unrelated() {}" + + "Unrelated.prototype.format = function() {};" + + "/** @enum {?Formatter} */ var Enum = {\n" + + " A: new Formatter(),\n" + + " B: new Formatter()\n" + + "};\n" + + "function f() {\n" + + " var formatter = window.toString() ? Enum.A : Enum.B;\n" + + " formatter.format();\n" + + "}"; + testSets(false, js, output, "{}"); + } + + public void testEnumOfObjects3() { + String js = "" + + "/** @constructor */ function Formatter() {}" + + "Formatter.prototype.format = function() {};" + + "/** @constructor */ function Unrelated() {}" + + "Unrelated.prototype.format = function() {};" + + "/** @enum {!Formatter} */ var Enum = {\n" + + " A: new Formatter(),\n" + + " B: new Formatter()\n" + + "};\n" + + "/** @enum {!Enum} */ var SubEnum = {\n" + + " C: Enum.A\n" + + "};\n" + + "function f() {\n" + + " var formatter = SubEnum.C\n" + + " formatter.format();\n" + + "}"; + String output = "" + + "/** @constructor */ function Formatter() {}" + + "Formatter.prototype.Formatter_prototype$format = function() {};" + + "/** @constructor */ function Unrelated() {}" + + "Unrelated.prototype.Unrelated_prototype$format = function() {};" + + "/** @enum {!Formatter} */ var Enum = {\n" + + " A: new Formatter(),\n" + + " B: new Formatter()\n" + + "};\n" + + "/** @enum {!Enum} */ var SubEnum = {\n" + + " C: Enum.A\n" + + "};\n" + + "function f() {\n" + + " var formatter = SubEnum.C\n" + + " formatter.Formatter_prototype$format();\n" + + "}"; + testSets(false, js, output, + "{format=[[Formatter.prototype], [Unrelated.prototype]]}"); + } + + public void testUntypedExterns() { + String externs = + BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES + + "var window;" + + "window.alert = function() {x};"; + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a = 0;\n" + + "Foo.prototype.alert = 0;\n" + + "Foo.prototype.window = 0;\n" + + "/** @constructor */ function Bar() {}\n" + + "Bar.prototype.a = 0;\n" + + "Bar.prototype.alert = 0;\n" + + "Bar.prototype.window = 0;\n" + + "window.alert();"; + String output = "" + + "function Foo(){}" + + "Foo.prototype.Foo_prototype$a=0;" + + "Foo.prototype.alert=0;" + + "Foo.prototype.Foo_prototype$window=0;" + + "function Bar(){}" + + "Bar.prototype.Bar_prototype$a=0;" + + "Bar.prototype.alert=0;" + + "Bar.prototype.Bar_prototype$window=0;" + + "window.alert();"; + + testSets(false, externs, js, output, "{a=[[Bar.prototype], [Foo.prototype]]" + + ", window=[[Bar.prototype], [Foo.prototype]]}"); + testSets(true, externs, js, output, "{a=[[Bar.prototype], [Foo.prototype]]," + + " window=[[Bar.prototype], [Foo.prototype]]}"); + } + + public void testUnionTypeInvalidation() { + String externs = "" + + "/** @constructor */ function Baz() {}" + + "Baz.prototype.a"; + String js = "" + + "/** @constructor */ function Ind() {this.a=0}\n" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a = 0;\n" + + "/** @constructor */ function Bar() {}\n" + + "Bar.prototype.a = 0;\n" + + "/** @type {Foo|Bar} */\n" + + "var F = new Foo;\n" + + "F.a = 1\n;" + + "F = new Bar;\n" + + "/** @type {Baz} */\n" + + "var Z = new Baz;\n" + + "Z.a = 1\n;" + + "/** @type {Bar|Baz} */\n" + + "var B = new Baz;\n" + + "B.a = 1;\n" + + "B = new Bar;\n"; + // Only the constructor field a of Ind is renamed, as Foo is related to Baz + // through Bar in the unions Bar|Baz and Foo|Bar. + String output = "" + + "function Ind() { this.Ind$a = 0; }" + + "function Foo() {}" + + "Foo.prototype.a = 0;" + + "function Bar() {}" + + "Bar.prototype.a = 0;" + + "var F = new Foo;" + + "F.a = 1;" + + "F = new Bar;" + + "var Z = new Baz;" + + "Z.a = 1;" + + "var B = new Baz;" + + "B.a = 1;" + + "B = new Bar;"; + String ttOutput = "" + + "function Ind() { this.Unique$1$a = 0; }" + + "function Foo() {}" + + "Foo.prototype.a = 0;" + + "function Bar() {}" + + "Bar.prototype.a = 0;" + + "var F = new Foo;" + + "F.a = 1;" + + "F = new Bar;" + + "var Z = new Baz;" + + "Z.a = 1;" + + "var B = new Baz;" + + "B.a = 1;" + + "B = new Bar;"; + testSets(false, externs, js, output, "{a=[[Ind]]}"); + testSets(true, externs, js, ttOutput, "{a=[[Unique$1]]}"); + } + + public void testUnionAndExternTypes() { + String externs = "" + + "/** @constructor */ function Foo() { }" + + "Foo.prototype.a = 4;\n"; + String js = "" + + "/** @constructor */ function Bar() { this.a = 2; }\n" + + "/** @constructor */ function Baz() { this.a = 3; }\n" + + "/** @constructor */ function Buz() { this.a = 4; }\n" + + "/** @constructor */ function T1() { this.a = 3; }\n" + + "/** @constructor */ function T2() { this.a = 3; }\n" + + "/** @type {Bar|Baz} */ var b;\n" + + "/** @type {Baz|Buz} */ var c;\n" + + "/** @type {Buz|Foo} */ var d;\n" + + "b.a = 5; c.a = 6; d.a = 7;"; + String output = "" + + "/** @constructor */ function Bar() { this.a = 2; }\n" + + "/** @constructor */ function Baz() { this.a = 3; }\n" + + "/** @constructor */ function Buz() { this.a = 4; }\n" + + "/** @constructor */ function T1() { this.T1$a = 3; }\n" + + "/** @constructor */ function T2() { this.T2$a = 3; }\n" + + "/** @type {Bar|Baz} */ var b;\n" + + "/** @type {Baz|Buz} */ var c;\n" + + "/** @type {Buz|Foo} */ var d;\n" + + "b.a = 5; c.a = 6; d.a = 7;"; + + // We are testing the skipping of multiple types caused by unionizing with + // extern types. + testSets(false, externs, js, output, "{a=[[T1], [T2]]}"); + } + + public void testTypedExterns() { + String externs = "" + + "/** @constructor */ function Window() {};\n" + + "Window.prototype.alert;" + + "/** @type {Window} */" + + "var window;"; + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.alert = 0;\n" + + "window.alert('blarg');"; + String output = "" + + "function Foo(){}" + + "Foo.prototype.Foo_prototype$alert=0;" + + "window.alert('blarg');"; + testSets(false, externs, js, output, "{alert=[[Foo.prototype]]}"); + testSets(true, externs, js, output, "{alert=[[Foo.prototype]]}"); + } + + public void testSubtypesWithSameField() { + String js = "" + + "/** @constructor */ function Top() {}\n" + + "/** @constructor \n@extends Top*/ function Foo() {}\n" + + "Foo.prototype.a;\n" + + "/** @constructor \n@extends Top*/ function Bar() {}\n" + + "Bar.prototype.a;\n" + + "/** @param {Top} top */" + + "function foo(top) {\n" + + " var x = top.a;\n" + + "}\n" + + "foo(new Foo);\n" + + "foo(new Bar);\n"; + testSets(false, js, "{}"); + testSets(true, js, "{a=[[Bar.prototype, Foo.prototype]]}"); + } + + public void testSupertypeReferenceOfSubtypeProperty() { + String externs = "" + + "/** @constructor */ function Ext() {}" + + "Ext.prototype.a;"; + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "/** @constructor \n@extends Foo*/ function Bar() {}\n" + + "Bar.prototype.a;\n" + + "/** @param {Foo} foo */" + + "function foo(foo) {\n" + + " var x = foo.a;\n" + + "}\n"; + String result = "" + + "function Foo() {}\n" + + "function Bar() {}\n" + + "Bar.prototype.Bar_prototype$a;\n" + + "function foo(foo$$1) {\n" + + " var x = foo$$1.Bar_prototype$a;\n" + + "}\n"; + testSets(false, externs, js, result, "{a=[[Bar.prototype]]}"); + } + + public void testObjectLiteralNotRenamed() { + String js = "" + + "var F = {a:'a', b:'b'};" + + "F.a = 'z';"; + testSets(false, js, js, "{}"); + testSets(true, js, js, "{}"); + } + + public void testObjectLiteralReflected() { + String js = "" + + "var goog = {};" + + "goog.reflect = {};" + + "goog.reflect.object = function(x, y) { return y; };" + + "/** @constructor */ function F() {}" + + "/** @type {number} */ F.prototype.foo = 3;" + + "/** @constructor */ function G() {}" + + "/** @type {number} */ G.prototype.foo = 3;" + + "goog.reflect.object(F, {foo: 5});"; + String result = "" + + "var goog = {};" + + "goog.reflect = {};" + + "goog.reflect.object = function(x, y) { return y; };" + + "function F() {}" + + "F.prototype.F_prototype$foo = 3;" + + "function G() {}" + + "G.prototype.G_prototype$foo = 3;" + + "goog.reflect.object(F, {F_prototype$foo: 5});"; + testSets(false, js, result, "{foo=[[F.prototype], [G.prototype]]}"); + testSets(true, js, result, "{foo=[[F.prototype], [G.prototype]]}"); + } + + public void testObjectLiteralLends() { + String js = "" + + "var mixin = function(x) { return x; };" + + "/** @constructor */ function F() {}" + + "/** @type {number} */ F.prototype.foo = 3;" + + "/** @constructor */ function G() {}" + + "/** @type {number} */ G.prototype.foo = 3;" + + "mixin(/** @lends {F.prototype} */ ({foo: 5}));"; + String result = "" + + "var mixin = function(x) { return x; };" + + "function F() {}" + + "F.prototype.F_prototype$foo = 3;" + + "function G() {}" + + "G.prototype.G_prototype$foo = 3;" + + "mixin(/** @lends {F.prototype} */ ({F_prototype$foo: 5}));"; + testSets(false, js, result, "{foo=[[F.prototype], [G.prototype]]}"); + testSets(true, js, result, "{foo=[[F.prototype], [G.prototype]]}"); + } + + public void testClosureInherits() { + String js = "" + + "var goog = {};" + + "/* @param {Function} childCtor Child class.\n" + + " * @param {Function} parentCtor Parent class. */\n" + + "goog.inherits = function(childCtor, parentCtor) {\n" + + " /** @constructor */\n" + + " function tempCtor() {};\n" + + " tempCtor.prototype = parentCtor.prototype;\n" + + " childCtor.superClass_ = parentCtor.prototype;\n" + + " childCtor.prototype = new tempCtor();\n" + + " childCtor.prototype.constructor = childCtor;\n" + + "};" + + "/** @constructor */ function Top() {}\n" + + "Top.prototype.f = function() {};" + + "/** @constructor \n@extends Top*/ function Foo() {}\n" + + "goog.inherits(Foo, Top);\n" + + "/** @override */\n" + + "Foo.prototype.f = function() {" + + " Foo.superClass_.f();" + + "};\n" + + "/** @constructor \n* @extends Foo */ function Bar() {}\n" + + "goog.inherits(Bar, Foo);\n" + + "/** @override */\n" + + "Bar.prototype.f = function() {" + + " Bar.superClass_.f();" + + "};\n" + + "(new Bar).f();\n"; + testSets(false, js, "{f=[[Top.prototype]]}"); + testSets(true, js, "{constructor=[[Bar.prototype, Foo.prototype]], " + + "f=[[Bar.prototype], [Foo.prototype], [Top.prototype]]}"); + } + + public void testSkipNativeFunctionMethod() { + String externs = "" + + "/** @constructor \n @param {*} var_args */" + + "function Function(var_args) {}" + + "Function.prototype.call = function() {};"; + String js = "" + + "/** @constructor */ function Foo(){};" + + "/** @constructor\n @extends Foo */" + + "function Bar() { Foo.call(this); };"; // call should not be renamed + testSame(externs, js, null); + } + + public void testSkipNativeObjectMethod() { + String externs = "" + + "/** @constructor \n @param {*} opt_v */ function Object(opt_v) {}" + + "Object.prototype.hasOwnProperty;"; + String js = "" + + "/** @constructor */ function Foo(){};" + + "(new Foo).hasOwnProperty('x');"; + testSets(false, externs, js, js, "{}"); + testSets(true, externs, js, js, "{}"); + } + + public void testExtendNativeType() { + String externs = "" + + "/** @constructor \n @return {string} */" + + "function Date(opt_1, opt_2, opt_3, opt_4, opt_5, opt_6, opt_7) {}" + + "/** @override */ Date.prototype.toString = function() {}"; + String js = "" + + "/** @constructor\n @extends {Date} */ function SuperDate() {};\n" + + "(new SuperDate).toString();"; + testSets(true, externs, js, js, "{}"); + testSets(false, externs, js, js, "{}"); + } + + public void testStringFunction() { + // Extern functions are not renamed, but user functions on a native + // prototype object are. + String externs = "/**@constructor\n@param {*} opt_str \n @return {string}*/" + + "function String(opt_str) {};\n" + + "/** @override \n @return {string} */\n" + + "String.prototype.toString = function() { };\n"; + String js = "" + + "/** @constructor */ function Foo() {};\n" + + "Foo.prototype.foo = function() {};\n" + + "String.prototype.foo = function() {};\n" + + "var a = 'str'.toString().foo();\n"; + String output = "" + + "function Foo() {};\n" + + "Foo.prototype.Foo_prototype$foo = function() {};\n" + + "String.prototype.String_prototype$foo = function() {};\n" + + "var a = 'str'.toString().String_prototype$foo();\n"; + + testSets(false, externs, js, output, + "{foo=[[Foo.prototype], [String.prototype]]}"); + testSets(true, externs, js, output, + "{foo=[[Foo.prototype], [String.prototype]]}"); + } + + public void testUnusedTypeInExterns() { + String externs = "" + + "/** @constructor */ function Foo() {};\n" + + "Foo.prototype.a"; + String js = "" + + "/** @constructor */ function Bar() {};\n" + + "Bar.prototype.a;" + + "/** @constructor */ function Baz() {};\n" + + "Baz.prototype.a;"; + String output = "" + + "/** @constructor */ function Bar() {};\n" + + "Bar.prototype.Bar_prototype$a;" + + "/** @constructor */ function Baz() {};\n" + + "Baz.prototype.Baz_prototype$a"; + testSets(false, externs, js, output, + "{a=[[Bar.prototype], [Baz.prototype]]}"); + testSets(true, externs, js, output, + "{a=[[Bar.prototype], [Baz.prototype]]}"); + } + + public void testInterface() { + String js = "" + + "/** @interface */ function I() {};\n" + + "I.prototype.a;\n" + + "/** @constructor \n @implements I */ function Foo() {};\n" + + "Foo.prototype.a;\n" + + "/** @type I */\n" + + "var F = new Foo;" + + "var x = F.a;"; + testSets(false, js, "{a=[[Foo.prototype, I.prototype]]}"); + testSets(true, js, "{a=[[Foo.prototype], [I.prototype]]}"); + } + + public void testInterfaceOfSuperclass() { + String js = "" + + "/** @interface */ function I() {};\n" + + "I.prototype.a;\n" + + "/** @constructor \n @implements I */ function Foo() {};\n" + + "Foo.prototype.a;\n" + + "/** @constructor \n @extends Foo */ function Bar() {};\n" + + "Bar.prototype.a;\n" + + "/** @type Bar */\n" + + "var B = new Bar;" + + "B.a = 0"; + testSets(false, js, "{a=[[Foo.prototype, I.prototype]]}"); + testSets(true, js, + "{a=[[Bar.prototype], [Foo.prototype], [I.prototype]]}"); + } + + public void testTwoInterfacesWithSomeInheritance() { + String js = "" + + "/** @interface */ function I() {};\n" + + "I.prototype.a;\n" + + "/** @interface */ function I2() {};\n" + + "I2.prototype.a;\n" + + "/** @constructor \n @implements I */ function Foo() {};\n" + + "Foo.prototype.a;\n" + + "/** @constructor \n @extends Foo \n @implements I2*/\n" + + "function Bar() {};\n" + + "Bar.prototype.a;\n" + + "/** @type Bar */\n" + + "var B = new Bar;" + + "B.a = 0"; + testSets(false, js, "{a=[[Foo.prototype, I.prototype, I2.prototype]]}"); + testSets(true, js, "{a=[[Bar.prototype], [Foo.prototype], " + + "[I.prototype], [I2.prototype]]}"); + } + + public void testInvalidatingInterface() { + String js = "" + + "/** @interface */ function I2() {};\n" + + "I2.prototype.a;\n" + + "/** @constructor */ function Bar() {}\n" + + "/** @type I */\n" + + "var i = new Bar;\n" // Make I invalidating + + "/** @constructor \n @implements I \n @implements I2 */" + + "function Foo() {};\n" + + "/** @override */\n" + + "Foo.prototype.a = 0;\n" + + "(new Foo).a = 0;" + + "/** @interface */ function I() {};\n" + + "I.prototype.a;\n"; + testSets(false, js, "{}", TypeValidator.TYPE_MISMATCH_WARNING); + testSets(true, js, "{}", TypeValidator.TYPE_MISMATCH_WARNING); + } + + public void testMultipleInterfaces() { + String js = "" + + "/** @interface */ function I() {};\n" + + "/** @interface */ function I2() {};\n" + + "I2.prototype.a;\n" + + "/** @constructor \n @implements I \n @implements I2 */" + + "function Foo() {};\n" + + "/** @override */" + + "Foo.prototype.a = 0;\n" + + "(new Foo).a = 0"; + testSets(false, js, "{a=[[Foo.prototype, I2.prototype]]}"); + testSets(true, js, "{a=[[Foo.prototype], [I2.prototype]]}"); + } + + public void testInterfaceWithSupertypeImplementor() { + String js = "" + + "/** @interface */ function C() {}\n" + + "C.prototype.foo = function() {};\n" + + "/** @constructor */ function A (){}\n" + + "A.prototype.foo = function() {};\n" + + "/** @constructor \n @implements {C} \n @extends {A} */\n" + + "function B() {}\n" + + "/** @type {C} */ var b = new B();\n" + + "b.foo();\n"; + testSets(false, js, "{foo=[[A.prototype, C.prototype]]}"); + testSets(true, js, "{foo=[[A.prototype], [C.prototype]]}"); + } + + public void testSuperInterface() { + String js = "" + + "/** @interface */ function I() {};\n" + + "I.prototype.a;\n" + + "/** @interface \n @extends I */ function I2() {};\n" + + "/** @constructor \n @implements I2 */" + + "function Foo() {};\n" + + "/** @override */\n" + + "Foo.prototype.a = 0;\n" + + "(new Foo).a = 0"; + testSets(false, js, "{a=[[Foo.prototype, I.prototype]]}"); + testSets(true, js, "{a=[[Foo.prototype], [I.prototype]]}"); + } + + public void testInterfaceUnionWithCtor() { + String js = "" + + "/** @interface */ function I() {};\n" + + "/** @type {!Function} */ I.prototype.addEventListener;\n" + + "/** @constructor \n * @implements {I} */ function Impl() {};\n" + + "/** @type {!Function} */ Impl.prototype.addEventListener;" + + "/** @constructor */ function C() {};\n" + + "/** @type {!Function} */ C.prototype.addEventListener;" + + "/** @param {C|I} x */" + + "function f(x) { x.addEventListener(); };\n" + + "f(new C()); f(new Impl());"; + + testSets(false, js, js, + "{addEventListener=[[C.prototype, I.prototype, Impl.prototype]]}"); + + // In the tightened case, the disambiguator is smart enough to + // disambiguate Impl's method from the interface method. + String tightenedOutput = "" + + "function I() {};\n" + + "I.prototype.I_prototype$addEventListener;\n" + + "function Impl() {};\n" + + "Impl.prototype.C_prototype$addEventListener;" + + "function C() {};\n" + + "C.prototype.C_prototype$addEventListener;" + + "/** @param {C|I} x */" + + "function f(x) { x.C_prototype$addEventListener(); };\n" + + "f(new C()); f(new Impl());"; + + testSets(true, js, tightenedOutput, + "{addEventListener=[[C.prototype, Impl.prototype], [I.prototype]]}"); + } + + public void testExternInterfaceUnionWithCtor() { + String externs = "" + + "/** @interface */ function I() {};\n" + + "/** @type {!Function} */ I.prototype.addEventListener;\n" + + "/** @constructor \n * @implements {I} */ function Impl() {};\n" + + "/** @type {!Function} */ Impl.prototype.addEventListener;"; + + String js = "" + + "/** @constructor */ function C() {};\n" + + "/** @type {!Function} */ C.prototype.addEventListener;" + + "/** @param {C|I} x */" + + "function f(x) { x.addEventListener(); };\n" + + "f(new C()); f(new Impl());"; + + testSets(false, externs, js, js, "{}"); + testSets(true, externs, js, js, "{}"); + } + + /** + * Tests that the type based version skips renaming on types that have a + * mismatch, and the type tightened version continues to work as normal. + */ + public void testMismatchInvalidation() { + String js = "" + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a = 0;\n" + + "/** @constructor */ function Bar() {}\n" + + "Bar.prototype.a = 0;\n" + + "/** @type Foo */\n" + + "var F = new Bar;\n" + + "F.a = 0;"; + + testSets(false, "", js, js, "{}", TypeValidator.TYPE_MISMATCH_WARNING, + "initializing variable\n" + + "found : Bar\n" + + "required: (Foo|null)"); + testSets(true, "", js, js, "{}", TypeValidator.TYPE_MISMATCH_WARNING, + "initializing variable\n" + + "found : Bar\n" + + "required: (Foo|null)"); + } + + public void testBadCast() { + String js = "/** @constructor */ function Foo() {};\n" + + "Foo.prototype.a = 0;\n" + + "/** @constructor */ function Bar() {};\n" + + "Bar.prototype.a = 0;\n" + + "var a = /** @type {!Foo} */ (new Bar);\n" + + "a.a = 4;"; + testSets(false, "", js, js, "{}", + TypeValidator.INVALID_CAST, + "invalid cast - must be a subtype or supertype\n" + + "from: Bar\n" + + "to : Foo"); + } + + public void testDeterministicNaming() { + String js = + "/** @constructor */function A() {}\n" + + "/** @return {string} */A.prototype.f = function() {return 'a';};\n" + + "/** @constructor */function B() {}\n" + + "/** @return {string} */B.prototype.f = function() {return 'b';};\n" + + "/** @constructor */function C() {}\n" + + "/** @return {string} */C.prototype.f = function() {return 'c';};\n" + + "/** @type {A|B} */var ab = 1 ? new B : new A;\n" + + "/** @type {string} */var n = ab.f();\n"; + + String output = + "function A() {}\n" + + "A.prototype.A_prototype$f = function() { return'a'; };\n" + + "function B() {}\n" + + "B.prototype.A_prototype$f = function() { return'b'; };\n" + + "function C() {}\n" + + "C.prototype.C_prototype$f = function() { return'c'; };\n" + + "var ab = 1 ? new B : new A; var n = ab.A_prototype$f();\n"; + + for (int i = 0; i < 5; i++) { + testSets(false, js, output, + "{f=[[A.prototype, B.prototype], [C.prototype]]}"); + + testSets(true, js, output, + "{f=[[A.prototype, B.prototype], [C.prototype]]}"); + } + } + + public void testObjectLiteral() { + String js = "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a;\n" + + "/** @constructor */ function Bar() {}\n" + + "Bar.prototype.a;\n" + + "var F = /** @type {Foo} */({ a: 'a' });\n"; + + String output = "function Foo() {}\n" + + "Foo.prototype.Foo_prototype$a;\n" + + "function Bar() {}\n" + + "Bar.prototype.Bar_prototype$a;\n" + + "var F = { Foo_prototype$a: 'a' };\n"; + + testSets(false, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); + testSets(true, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); + } + + public void testCustomInherits() { + String js = "Object.prototype.inheritsFrom = function(shuper) {\n" + + " /** @constructor */\n" + + " function Inheriter() { }\n" + + " Inheriter.prototype = shuper.prototype;\n" + + " this.prototype = new Inheriter();\n" + + " this.superConstructor = shuper;\n" + + "};\n" + + "function Foo(var1, var2, strength) {\n" + + " Foo.superConstructor.call(this, strength);\n" + + "}" + + "Foo.inheritsFrom(Object);"; + + String externs = "" + + "function Function(var_args) {}" + + "/** @return {*} */Function.prototype.call = function(var_args) {};"; + + testSets(false, externs, js, js, "{}"); + } + + public void testSkipNativeFunctionStaticProperty() { + String js = "" + + "/** @param {!Function} ctor */\n" + + "function addSingletonGetter(ctor) { ctor.a; }\n" + + "/** @constructor */ function Foo() {}\n" + + "Foo.a = 0;" + + "/** @constructor */ function Bar() {}\n" + + "Bar.a = 0;"; + + String output = "" + + "function addSingletonGetter(ctor){ctor.a}" + + "function Foo(){}" + + "Foo.a=0;" + + "function Bar(){}" + + "Bar.a=0"; + + testSets(false, js, output, "{}"); + } + + public void testErrorOnProtectedProperty() { + test("function addSingletonGetter(foo) { foo.foobar = 'a'; };", null, + DisambiguateProperties.Warnings.INVALIDATION); + assertTrue(getLastCompiler().getErrors()[0].toString().contains("foobar")); + } + + public void testMismatchForbiddenInvalidation() { + test("/** @constructor */ function F() {}" + + "/** @type {number} */ F.prototype.foobar = 3;" + + "/** @return {number} */ function g() { return new F(); }", + null, + DisambiguateProperties.Warnings.INVALIDATION); + assertTrue(getLastCompiler().getErrors()[0].toString() + .contains("Consider fixing errors")); + } + + public void runFindHighestTypeInChain() { + // Check that this doesn't go into an infinite loop. + DisambiguateProperties.forJSTypeSystem(new Compiler(), + Maps.newHashMap()) + .getTypeWithProperty("no", + new JSTypeRegistry(new TestErrorReporter(null, null)) + .getNativeType(JSTypeNative.OBJECT_PROTOTYPE)); + } + + @SuppressWarnings("unchecked") + private void testSets(boolean runTightenTypes, String js, String expected, + String fieldTypes) { + this.runTightenTypes = runTightenTypes; + test(js, expected); + assertEquals( + fieldTypes, mapToString(lastPass.getRenamedTypesForTesting())); + } + + @SuppressWarnings("unchecked") + private void testSets(boolean runTightenTypes, String externs, String js, + String expected, String fieldTypes) { + testSets(runTightenTypes, externs, js, expected, fieldTypes, null, null); + } + + @SuppressWarnings("unchecked") + private void testSets(boolean runTightenTypes, String externs, String js, + String expected, String fieldTypes, DiagnosticType warning, + String description) { + this.runTightenTypes = runTightenTypes; + test(externs, js, expected, null, warning, description); + assertEquals( + fieldTypes, mapToString(lastPass.getRenamedTypesForTesting())); + } + + /** + * Compiles the code and checks that the set of types for each field matches + * the expected value. + * + *

      The format for the set of types for fields is: + * {field=[[Type1, Type2]]} + */ + private void testSets(boolean runTightenTypes, String js, String fieldTypes) { + this.runTightenTypes = runTightenTypes; + test(js, null, null, null); + assertEquals(fieldTypes, mapToString(lastPass.getRenamedTypesForTesting())); + } + + /** + * Compiles the code and checks that the set of types for each field matches + * the expected value. + * + *

      The format for the set of types for fields is: + * {field=[[Type1, Type2]]} + */ + private void testSets(boolean runTightenTypes, String js, String fieldTypes, + DiagnosticType warning) { + this.runTightenTypes = runTightenTypes; + test(js, null, null, warning); + assertEquals(fieldTypes, mapToString(lastPass.getRenamedTypesForTesting())); + } + + /** Sorts the map and converts to a string for comparison purposes. */ + private String mapToString(Multimap> map) { + TreeMap retMap = Maps.newTreeMap(); + for (String key : map.keySet()) { + TreeSet treeSet = Sets.newTreeSet(); + for (Collection collection : map.get(key)) { + Set subSet = Sets.newTreeSet(); + for (T type : collection) { + subSet.add(type.toString()); + } + treeSet.add(subSet.toString()); + } + retMap.put(key, treeSet.toString()); + } + return retMap.toString(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DotFormatterTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DotFormatterTest.java new file mode 100644 index 0000000..b5d72dc --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/DotFormatterTest.java @@ -0,0 +1,93 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import junit.framework.TestCase; + +public class DotFormatterTest extends TestCase { + /** + * Tests that keys are assigned sequentially. + */ + public void testKeyAssignementSequential() throws Exception { + DotFormatter dot = DotFormatter.newInstanceForTesting(); + assertEquals(0, dot.key(new Node(Token.BLOCK))); + assertEquals(1, dot.key(new Node(Token.BLOCK))); + assertEquals(2, dot.key(new Node(Token.BLOCK))); + assertEquals(3, dot.key(new Node(Token.BLOCK))); + assertEquals(4, dot.key(new Node(Token.BLOCK))); + } + + /** + * Tests that keys are assigned once per node. + */ + public void testKeyAssignementOncePerNode() throws Exception { + DotFormatter dot = DotFormatter.newInstanceForTesting(); + Node node0 = new Node(Token.BLOCK); + Node node1 = new Node(Token.BLOCK); + Node node2 = new Node(Token.BLOCK); + + assertEquals(0, dot.key(node0)); + assertEquals(1, dot.key(node1)); + assertEquals(2, dot.key(node2)); + assertEquals(0, dot.key(node0)); + assertEquals(1, dot.key(node1)); + assertEquals(2, dot.key(node2)); + } + + /** + * Tests the formatting (simple tree). + */ + public void testToDotSimple() throws Exception { + Node ast = new Node(Token.BITOR); + + String expected = "digraph AST {\n" + + " node [color=lightblue2, style=filled];\n" + + " node0 [label=\"BITOR\"];\n" + + "}\n"; + test(expected, ast); + } + + /** + * Tests the formatting (3 element tree). + */ + public void testToDot3Elements() throws Exception { + Node ast = new Node(Token.BLOCK); + ast.addChildToBack(new Node(Token.NAME)); + ast.addChildToBack(new Node(Token.STRING)); + + String expected = "digraph AST {\n" + + " node [color=lightblue2, style=filled];\n" + + " node0 [label=\"BLOCK\"];\n" + + " node1 [label=\"NAME\"];\n" + + " node0 -> node1 [weight=1];\n" + + " node2 [label=\"STRING\"];\n" + + " node0 -> node2 [weight=1];\n" + + "}\n"; + test(expected, ast); + } + + private void test(String expected, Node ast) { + try { + assertEquals(expected, DotFormatter.toDot(ast)); + } catch (java.io.IOException e) { + fail("Tests failed with IOExceptions"); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExpandJqueryAliasesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExpandJqueryAliasesTest.java new file mode 100644 index 0000000..b67be7c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExpandJqueryAliasesTest.java @@ -0,0 +1,170 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Tests for {@link ExpandJqueryAliases} + */ +public class ExpandJqueryAliasesTest extends CompilerTestCase { + private JqueryCodingConvention conv = new JqueryCodingConvention(); + + final DiagnosticType NAME_ERROR = + ExpandJqueryAliases.JQUERY_UNABLE_TO_EXPAND_INVALID_NAME_ERROR; + + final DiagnosticType INVALID_LIT_ERROR = + ExpandJqueryAliases.JQUERY_UNABLE_TO_EXPAND_INVALID_LIT_ERROR; + + final DiagnosticType USELESS_EACH_ERROR = + ExpandJqueryAliases.JQUERY_USELESS_EACH_EXPANSION; + + public ExpandJqueryAliasesTest() {} + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + compiler.options.setCodingConvention(conv); + return new ExpandJqueryAliases(compiler); + } + + public void testJqueryFnAliasExpansion() { + String setupCode = "var jQuery={};jQuery.fn=jQuery.prototype;"; + + testSame(setupCode); + + test(setupCode + "jQuery.fn.foo='bar';", + setupCode + "jQuery.prototype.foo='bar';"); + + test(setupCode + "jQuerySub.fn.foo='bar';", + setupCode + "jQuerySub.prototype.foo='bar';"); + } + + public void testJqueryExtendExpansion() { + String setupCode = "var jQuery={},obj2={};"; + + // test for extend call that should not be expanded - no arguments + testSame(setupCode + "jQuery.extend()"); + + // test for extend call that should not be expanded - empty argument + // this statement has no effect in actual code + testSame(setupCode + "jQuery.extend({})"); + + // test single argument call - should assign to the jQuery namespace + test(setupCode + "jQuery.extend({a:'test'})", + setupCode + "{jQuery.a = 'test';}"); + + // test expansion when extending the jQuery prototype + test(setupCode + "jQuery.fn=jQuery.prototype;" + + "jQuery.fn.extend({a:'test', b:'test2'});", + setupCode + "jQuery.fn=jQuery.prototype;" + + "{jQuery.prototype.a = 'test'; jQuery.prototype.b = 'test2';}"); + + // Expand the extension of obj2 + test(setupCode + "jQuery.extend(obj2, {a:'test', b:'test2'});", + setupCode + "{obj2=obj2||{}; obj2.a='test'; obj2.b='test2';}"); + + // Expand the jQuery namespace - 2 argument call + // Must ensure that the first argument is defined + test(setupCode + "jQuery.extend(jQuery,{a:'test', b:'test2'});", + setupCode + "{jQuery = jQuery || {}; jQuery.a = 'test';" + + "jQuery.b = 'test2';}"); + + // Test extend call where first argument includes a method call + testSame(setupCode+"obj2.meth=function() { return { a:{} }; };" + + "jQuery.extend(obj2.meth().a, {a: 'test'});"); + } + + public void testJqueryExpandedEachExpansion() { + String setupCode = "var jQuery={};" + + "jQuery.expandedEach=function(vals, callback){};"; + + testSame(setupCode); + + // Test expansion with object literal + test(setupCode + "jQuery.expandedEach({'a': 1, 'b': 2, 'c': 8}," + + "function(key, val) { var a = key; jQuery[key] = val; });", + setupCode + "(function(){ var a = 'a'; jQuery.a = 1 })();" + + "(function(){ var a = 'b'; jQuery.b = 2 })();" + + "(function(){ var a = 'c'; jQuery.c = 8 })();"); + + // Test expansion with array literal + // For array literals, the key parameter will be the element index number + // and the value parameter will be the string literal. In this case, the + // string literal value should become a property name. + test(setupCode + "jQuery.expandedEach(['a', 'b', 'c']," + + "function(key, val){ jQuery[val] = key; });", + setupCode + "(function(){ jQuery.a = 0; })();" + + "(function(){ jQuery.b = 1; })();" + + "(function(){ jQuery.c = 2 })();"); + + // Test expansion with object literal using 'this' keyword + test(setupCode + "jQuery.expandedEach({'a': 1, 'b': 2, 'c': 8}," + + "function(key, val) { var a = key; jQuery[key] = this; });", + setupCode + "(function(){ var a = 'a'; jQuery.a = 1 })();" + + "(function(){ var a = 'b'; jQuery.b = 2 })();" + + "(function(){ var a = 'c'; jQuery.c = 8 })();"); + + // Test expansion with array literal using 'this' keyword + test(setupCode + "jQuery.expandedEach(['a', 'b', 'c']," + + "function(key, val){ jQuery[this] = key; });", + setupCode + "(function(){ jQuery.a = 0; })();" + + "(function(){ jQuery.b = 1; })();" + + "(function(){ jQuery.c = 2 })();"); + + // test nested function using argument name to shadow callback name + test(setupCode + "jQuery.expandedEach(['a'], function(key,val) {" + + "jQuery[val] = key; (function(key) { jQuery[key] = 1;})('test'); })", + setupCode + "(function(){ jQuery.a = 0;" + + "(function(key){ jQuery[key] = 1})('test') })()"); + + // test nested function using var name to shadow callback name + test(setupCode + "jQuery.expandedEach(['a'], function(key,val) {" + + "jQuery[val] = key; (function(key) { var val = 2;" + + "jQuery[key] = val;})('test');})", + setupCode + "(function(){" + + "jQuery.a=0;" + + "(function(key){var val = 2; jQuery[key] = val;})('test')})()"); + + // test nested function using function name to shadow callback name + test(setupCode + "jQuery.expandedEach(['a'], function(key,val) {" + + "jQuery[val] = key; (function(key1) {" + + "function key() {}; key();" + + "})('test');})", + setupCode + "(function(){" + + "jQuery.a=0;(function(key1) {" + + "function key() {}; key(); })('test')})()"); + + // test using return val + test(setupCode + "alert(jQuery.expandedEach(['a']," + + "function(key,val) { jQuery[val] = key;})[0])", + setupCode + "alert((function(){" + + "(function(){ jQuery.a = 0;})(); return ['a']})()[0]);"); + + // Loop object is a variable. Test that warning is raised. + testSame(setupCode + "var a = ['a'];" + + "jQuery.expandedEach(a, function(key,val){ jQuery[key]=val; })", + INVALID_LIT_ERROR); + + // Invalid property name. Test that warning is raised. + testSame(setupCode + "var obj2={};" + + "jQuery.expandedEach(['foo','bar'], function(i, name) {" + + "obj2[ '[object ' + name + ']' ] = 'a';});", NAME_ERROR, true); + + // Useless expansion (key not used). Test that warning is raised. + testSame(setupCode + "var obj2={};" + + "jQuery.expandedEach(['foo','bar'], function(i, name) {" + + "obj2[i] = 1;});", USELESS_EACH_ERROR, false); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExploitAssignsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExploitAssignsTest.java new file mode 100644 index 0000000..318652d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExploitAssignsTest.java @@ -0,0 +1,164 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Unit tests for {@link ExploitAssigns} + * + * @author nicksantos@google.com (Nick Santos) + * @author acleung@google.com (Alan Leung) + */ +public class ExploitAssignsTest extends CompilerTestCase { + + public void testExprExploitationTypes() { + test("a = true; b = true", + "b = a = true"); + test("a = !0; b = !0", + "b = a = !0"); + test("a = !1; b = !1", + "b = a = !1"); + test("a = void 0; b = void 0", + "b = a = void 0"); + test("a = -Infinity; b = -Infinity", + "b = a = -Infinity"); + } + + public void testExprExploitationTypes2() { + test("a = !0; b = !0", + "b = a = !0"); + } + + public void testExprExploitation() { + test("a = null; b = null; var c = b", + "var c = b = a = null"); + test("a = null; b = null", + "b = a = null"); + test("a = undefined; b = undefined", + "b = a = undefined"); + test("a = 0; b = 0", "b=a=0"); + test("a = 'foo'; b = 'foo'", + "b = a = \"foo\""); + test("a = c; b = c", "b=a=c"); + + testSame("a = 0; b = 1"); + testSame("a = \"foo\"; b = \"foox\""); + + test("a = null; a && b;", "(a = null)&&b"); + test("a = null; a || b;", "(a = null)||b"); + + test("a = null; a ? b : c;", "(a = null) ? b : c"); + + test("a = null; this.foo = null;", + "this.foo = a = null"); + test("function f(){ a = null; return null; }", + "function f(){return a = null}"); + + test("a = true; if (a) { foo(); }", + "if (a = true) { foo() }"); + test("a = true; if (a && a) { foo(); }", + "if ((a = true) && a) { foo() }"); + test("a = false; if (a) { foo(); }", + "if (a = false) { foo() }"); + + test("a = !0; if (a) { foo(); }", + "if (a = !0) { foo() }"); + test("a = !0; if (a && a) { foo(); }", + "if ((a = !0) && a) { foo() }"); + test("a = !1; if (a) { foo(); }", + "if (a = !1) { foo() }"); + + testSame("a = this.foo; a();"); + test("a = b; b = a;", + "b = a = b"); + testSame("a = b; a.c = a"); + test("this.foo = null; this.bar = null;", + "this.bar = this.foo = null"); + test("this.foo = null; this.bar = null; this.baz = this.bar", + "this.baz = this.bar = this.foo = null"); + test("this.foo = null; a = null;", + "a = this.foo = null"); + test("this.foo = null; a = this.foo;", + "a = this.foo = null"); + test("a.b.c=null; a=null;", + "a = a.b.c = null"); + testSame("a = null; a.b.c = null"); + test("(a=b).c = null; this.b = null;", + "this.b = (a=b).c = null"); + testSame("if(x) a = null; else b = a"); + } + + public void testNestedExprExploitation() { + test("this.foo = null; this.bar = null; this.baz = null;", + "this.baz = this.bar = this.foo = null"); + + test("a = 3; this.foo = a; this.bar = a; this.baz = 3;", + "this.baz = this.bar = this.foo = a = 3"); + test("a = 3; this.foo = a; this.bar = this.foo; this.baz = a;", + "this.baz = this.bar = this.foo = a = 3"); + test("a = 3; this.foo = a; this.bar = 3; this.baz = this.foo;", + "this.baz = this.bar = this.foo = a = 3"); + test("a = 3; this.foo = a; a = 3; this.bar = 3; " + + "a = 3; this.baz = this.foo;", + "this.baz = a = this.bar = a = this.foo = a = 3"); + + test("a = 4; this.foo = a; a = 3; this.bar = 3; " + + "a = 3; this.baz = this.foo;", + "this.foo = a = 4; a = this.bar = a = 3; this.baz = this.foo"); + test("a = 3; this.foo = a; a = 4; this.bar = 3; " + + "a = 3; this.baz = this.foo;", + "this.foo = a = 3; a = 4; a = this.bar = 3; this.baz = this.foo"); + test("a = 3; this.foo = a; a = 3; this.bar = 3; " + + "a = 4; this.baz = this.foo;", + "this.bar = a = this.foo = a = 3; a = 4; this.baz = this.foo"); + } + + public void testBug1840071() { + // Some external properties are implemented as setters. Let's + // make sure that we don't collapse them inappropriately. + test("a.b = a.x; if (a.x) {}", "if (a.b = a.x) {}"); + testSame("a.b = a.x; if (a.b) {}"); + test("a.b = a.c = a.x; if (a.x) {}", "if (a.b = a.c = a.x) {}"); + testSame("a.b = a.c = a.x; if (a.c) {}"); + testSame("a.b = a.c = a.x; if (a.b) {}"); + } + + public void testBug2072343() { + testSame("a = a.x;a = a.x"); + testSame("a = a.x;b = a.x"); + test("b = a.x;a = a.x", "a = b = a.x"); + testSame("a.x = a;a = a.x"); + testSame("a.b = a.b.x;a.b = a.b.x"); + testSame("a.y = a.y.x;b = a.y;c = a.y.x"); + test("a = a.x;b = a;c = a.x", "b = a = a.x;c = a.x"); + test("b = a.x;a = b;c = a.x", "a = b = a.x;c = a.x"); + } + + public void testBadCollapseIntoCall() { + // Can't collapse this, because if we did, 'foo' would be called + // in the wrong 'this' context. + testSame("this.foo = function() {}; this.foo();"); + } + + public void testBadCollapse() { + testSame("this.$e$ = []; this.$b$ = null;"); + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new PeepholeOptimizationsPass(compiler,new ExploitAssigns()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExportTestFunctionsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExportTestFunctionsTest.java new file mode 100644 index 0000000..ce9e0d8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExportTestFunctionsTest.java @@ -0,0 +1,141 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +/** + * Tests for ExportTestFunctions. + * + */ +public class ExportTestFunctionsTest extends CompilerTestCase { + + private static final String EXTERNS = + "function google_exportSymbol(a, b) {}; " + + "function google_exportProperty(a, b, c) {};"; + + private static final String TEST_FUNCTIONS_WITH_NAMES = + "function Foo(arg) {}; " + + "function setUp(arg3) {}; " + + "function tearDown(arg, arg2) {}; " + + "function testBar(arg) {}"; + + public ExportTestFunctionsTest() { + super(EXTERNS); + } + + @Override + public void setUp() { + super.enableLineNumberCheck(false); + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new ExportTestFunctions(compiler, "google_exportSymbol", + "google_exportProperty"); + } + + @Override + protected int getNumRepetitions() { + // This pass only runs once. + return 1; + } + + public void testFunctionsAreExported() { + test(TEST_FUNCTIONS_WITH_NAMES, + "function Foo(arg){}; " + + "function setUp(arg3){} google_exportSymbol(\"setUp\",setUp);; " + + "function tearDown(arg,arg2) {} " + + "google_exportSymbol(\"tearDown\",tearDown);; " + + "function testBar(arg){} google_exportSymbol(\"testBar\",testBar)" + ); + } + + // Helper functions + public void testBasicTestFunctionsAreExported() { + test("function Foo() {function testA() {}}", + "function Foo() {function testA(){}}"); + test("function setUp() {}", + "function setUp(){} google_exportSymbol('setUp',setUp)"); + test("function setUpPage() {}", + "function setUpPage(){} google_exportSymbol('setUpPage',setUpPage)"); + test("function tearDown() {}", + "function tearDown(){} google_exportSymbol('tearDown',tearDown)"); + test("function tearDownPage() {}", + "function tearDownPage(){} google_exportSymbol('tearDownPage'," + + "tearDownPage)"); + test("function testBar() { function testB() {}}", + "function testBar(){function testB(){}}" + + "google_exportSymbol('testBar',testBar)"); + testSame("var testCase = {}; testCase.setUpPage = function() {}"); + } + + /** + * Make sure this works for global functions declared as function expressions: + *

      +   * var testFunctionName = function() {
      +   *   // Implementation
      +   * };
      +   * 
      + * This format should be supported in addition to function statements. + */ + public void testFunctionExpressionsAreExported() { + test("var Foo = function() {var testA = function() {}}", + "var Foo = function() {var testA = function() {}}"); + test("var setUp = function() {}", + "var setUp = function() {}; " + + "google_exportSymbol('setUp',setUp)"); + test("var setUpPage = function() {}", + "var setUpPage = function() {}; " + + "google_exportSymbol('setUpPage',setUpPage)"); + test("var tearDown = function() {}", + "var tearDown = function() {}; " + + "google_exportSymbol('tearDown',tearDown)"); + test("var tearDownPage = function() {}", + "var tearDownPage = function() {}; " + + "google_exportSymbol('tearDownPage', tearDownPage)"); + test("var testBar = function() { var testB = function() {}}", + "var testBar = function(){ var testB = function() {}}; " + + "google_exportSymbol('testBar',testBar)"); + } + + public void testFunctionAssignmentsAreExported() { + test("Foo = {}; Foo.prototype.bar = function() {};", + "Foo = {}; Foo.prototype.bar = function() {};"); + + test("Foo = {}; Foo.prototype.setUpPage = function() {};", + "Foo = {}; Foo.prototype.setUpPage = function() {};" + + "google_exportProperty(Foo.prototype, 'setUpPage', " + + "Foo.prototype.setUpPage);"); + + test("Foo = {}; Foo.prototype.testBar = function() {};", + "Foo = {}; Foo.prototype.testBar = function() {};" + + "google_exportProperty(Foo.prototype, 'testBar', " + + "Foo.prototype.testBar);"); + + test("Foo = {}; Foo.prototype.testBar = function() " + + "{ var testBaz = function() {}};", + "Foo = {}; Foo.prototype.testBar = function() " + + "{ var testBaz = function() {}};" + + "google_exportProperty(Foo.prototype, 'testBar', " + + "Foo.prototype.testBar);"); + + test("Foo = {}; Foo.baz.prototype.testBar = function() " + + "{ var testBaz = function() {}};", + "Foo = {}; Foo.baz.prototype.testBar = function() " + + "{ var testBaz = function() {}};" + + "google_exportProperty(Foo.baz.prototype, 'testBar', " + + "Foo.baz.prototype.testBar);"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExpressionDecomposerTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExpressionDecomposerTest.java new file mode 100644 index 0000000..e01404b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExpressionDecomposerTest.java @@ -0,0 +1,675 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.CompilerOptions.LanguageMode; +import com.google.javascript.jscomp.ExpressionDecomposer.DecompositionType; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import junit.framework.TestCase; + +import java.util.Set; + +import javax.annotation.Nullable; + +/** + * Unit tests for ExpressionDecomposer + * @author johnlenz@google.com (John Lenz) + */ +public class ExpressionDecomposerTest extends TestCase { + // Note: functions "foo" and "goo" are external functions + // in the helper. + + public void testCanExposeExpression1() { + // Can't move or decompose some classes of expressions. + helperCanExposeExpression( + DecompositionType.UNDECOMPOSABLE, "while(foo());", "foo"); + helperCanExposeExpression( + DecompositionType.UNDECOMPOSABLE, "while(x = goo()&&foo()){}", "foo"); + helperCanExposeExpression( + DecompositionType.UNDECOMPOSABLE, "while(x += goo()&&foo()){}", "foo"); + + helperCanExposeExpression( + DecompositionType.UNDECOMPOSABLE, "do{}while(foo());", "foo"); + helperCanExposeExpression( + DecompositionType.UNDECOMPOSABLE, "for(;foo(););", "foo"); + // This case could be supported for loops without conditional continues + // by moving the increment into the loop body. + helperCanExposeExpression( + DecompositionType.UNDECOMPOSABLE, "for(;;foo());", "foo"); + // FOR initializer could be supported but they never occur + // as they are normalized away. + + // This is potentially doable but a bit too complex currently. + helperCanExposeExpression( + DecompositionType.UNDECOMPOSABLE, "switch(1){case foo():;}", "foo"); + } + + public void testCanExposeExpression2() { + helperCanExposeExpression( + DecompositionType.MOVABLE, "foo()", "foo"); + helperCanExposeExpression( + DecompositionType.MOVABLE, "x = foo()", "foo"); + helperCanExposeExpression( + DecompositionType.MOVABLE, "var x = foo()", "foo"); + helperCanExposeExpression( + DecompositionType.MOVABLE, "if(foo()){}", "foo"); + helperCanExposeExpression( + DecompositionType.MOVABLE, "switch(foo()){}", "foo"); + helperCanExposeExpression( + DecompositionType.MOVABLE, "switch(foo()){}", "foo"); + helperCanExposeExpression( + DecompositionType.MOVABLE, "function f(){ return foo();}", "foo"); + + helperCanExposeExpression( + DecompositionType.MOVABLE, "x = foo() && 1", "foo"); + helperCanExposeExpression( + DecompositionType.MOVABLE, "x = foo() || 1", "foo"); + helperCanExposeExpression( + DecompositionType.MOVABLE, "x = foo() ? 0 : 1", "foo"); + helperCanExposeExpression( + DecompositionType.MOVABLE, "(function(a){b = a})(foo())", "foo"); + } + + public void testCanExposeExpression3() { + helperCanExposeExpression( + DecompositionType.DECOMPOSABLE, "x = 0 && foo()", "foo"); + helperCanExposeExpression( + DecompositionType.DECOMPOSABLE, "x = 1 || foo()", "foo"); + helperCanExposeExpression( + DecompositionType.DECOMPOSABLE, "var x = 1 ? foo() : 0", "foo"); + + helperCanExposeExpression( + DecompositionType.DECOMPOSABLE, "goo() && foo()", "foo"); + helperCanExposeExpression( + DecompositionType.DECOMPOSABLE, "x = goo() && foo()", "foo"); + helperCanExposeExpression( + DecompositionType.DECOMPOSABLE, "x += goo() && foo()", "foo"); + helperCanExposeExpression( + DecompositionType.DECOMPOSABLE, "var x = goo() && foo()", "foo"); + helperCanExposeExpression( + DecompositionType.DECOMPOSABLE, "if(goo() && foo()){}", "foo"); + helperCanExposeExpression( + DecompositionType.DECOMPOSABLE, "switch(goo() && foo()){}", "foo"); + helperCanExposeExpression( + DecompositionType.DECOMPOSABLE, "switch(goo() && foo()){}", "foo"); + helperCanExposeExpression( + DecompositionType.DECOMPOSABLE, "switch(x = goo() && foo()){}", "foo"); + helperCanExposeExpression( + DecompositionType.DECOMPOSABLE, + "function f(){ return goo() && foo();}", "foo"); + } + + public void testCanExposeExpression4() { + // 'this' must be preserved in call. + helperCanExposeExpression( + DecompositionType.UNDECOMPOSABLE, "if (goo.a(1, foo()));", "foo"); + } + + public void testCanExposeExpression5() { + // 'this' must be preserved in call. + helperCanExposeExpression( + DecompositionType.UNDECOMPOSABLE, "if (goo['a'](foo()));", "foo"); + } + + public void testCanExposeExpression6() { + // 'this' must be preserved in call. + helperCanExposeExpression( + DecompositionType.UNDECOMPOSABLE, "z:if (goo.a(1, foo()));", "foo"); + } + + public void testCanExposeExpression7() { + // Verify calls to function expressions are movable. + helperCanExposeFunctionExpression( + DecompositionType.MOVABLE, + "(function(map){descriptions_=map})(\n" + + "function(){\n" + + "var ret={};\n" + + "ret[INIT]='a';\n" + + "ret[MIGRATION_BANNER_DISMISS]='b';\n" + + "return ret\n" + + "}()\n" + + ");", 2); + } + + public void testCanExposeExpression8() { + // Can it be decompose? + helperCanExposeExpression( + DecompositionType.DECOMPOSABLE, + "HangoutStarter.prototype.launchHangout = function() {\n" + + " var self = a.b;\n" + + " var myUrl = new goog.Uri(getDomServices_(self).getDomHelper()." + + "getWindow().location.href);\n" + + "};", + "getDomServices_"); + + // Verify it is properly expose the target expression. + helperExposeExpression( + "HangoutStarter.prototype.launchHangout = function() {\n" + + " var self = a.b;\n" + + " var myUrl = new goog.Uri(getDomServices_(self).getDomHelper()." + + "getWindow().location.href);\n" + + "};", + "getDomServices_", + "HangoutStarter.prototype.launchHangout = function() {" + + " var self = a.b;" + + " var temp_const$$0 = goog.Uri;" + + " var myUrl = new temp_const$$0(getDomServices_(self)." + + " getDomHelper().getWindow().location.href)}"); + + // Verify the results can be properly moved. + helperMoveExpression( + "HangoutStarter.prototype.launchHangout = function() {" + + " var self = a.b;" + + " var temp_const$$0 = goog.Uri;" + + " var myUrl = new temp_const$$0(getDomServices_(self)." + + " getDomHelper().getWindow().location.href)}", + "getDomServices_", + "HangoutStarter.prototype.launchHangout = function() {" + + " var self=a.b;" + + " var temp_const$$0=goog.Uri;" + + " var result$$0=getDomServices_(self);" + + " var myUrl=new temp_const$$0(result$$0.getDomHelper()." + + " getWindow().location.href)}"); + } + + public void testMoveExpression1() { + // There isn't a reason to do this, but it works. + helperMoveExpression("foo()", "foo", "var result$$0 = foo(); result$$0;"); + } + + public void testMoveExpression2() { + helperMoveExpression( + "x = foo()", + "foo", + "var result$$0 = foo(); x = result$$0;"); + } + + public void testMoveExpression3() { + helperMoveExpression( + "var x = foo()", + "foo", + "var result$$0 = foo(); var x = result$$0;"); + } + + public void testMoveExpression4() { + helperMoveExpression( + "if(foo()){}", + "foo", + "var result$$0 = foo(); if (result$$0);"); + } + + public void testMoveExpression5() { + helperMoveExpression( + "switch(foo()){}", + "foo", + "var result$$0 = foo(); switch(result$$0){}"); + } + + public void testMoveExpression6() { + helperMoveExpression( + "switch(1 + foo()){}", + "foo", + "var result$$0 = foo(); switch(1 + result$$0){}"); + } + + public void testMoveExpression7() { + helperMoveExpression( + "function f(){ return foo();}", + "foo", + "function f(){ var result$$0 = foo(); return result$$0;}"); + } + + public void testMoveExpression8() { + helperMoveExpression( + "x = foo() && 1", + "foo", + "var result$$0 = foo(); x = result$$0 && 1"); + } + + public void testMoveExpression9() { + helperMoveExpression( + "x = foo() || 1", + "foo", + "var result$$0 = foo(); x = result$$0 || 1"); + } + + public void testMoveExpression10() { + helperMoveExpression( + "x = foo() ? 0 : 1", + "foo", + "var result$$0 = foo(); x = result$$0 ? 0 : 1"); + } + + /* Decomposition tests. */ + + public void testExposeExpression1() { + helperExposeExpression( + "x = 0 && foo()", + "foo", + "var temp$$0; if (temp$$0 = 0) temp$$0 = foo(); x = temp$$0;"); + } + + public void testExposeExpression2() { + helperExposeExpression( + "x = 1 || foo()", + "foo", + "var temp$$0; if (temp$$0 = 1); else temp$$0 = foo(); x = temp$$0;"); + } + + public void testExposeExpression3() { + helperExposeExpression( + "var x = 1 ? foo() : 0", + "foo", + "var temp$$0;" + + " if (1) temp$$0 = foo(); else temp$$0 = 0;var x = temp$$0;"); + } + + public void testExposeExpression4() { + helperExposeExpression( + "goo() && foo()", + "foo", + "if (goo()) foo();"); + } + + public void testExposeExpression5() { + helperExposeExpression( + "x = goo() && foo()", + "foo", + "var temp$$0; if (temp$$0 = goo()) temp$$0 = foo(); x = temp$$0;"); + } + + public void testExposeExpression6() { + helperExposeExpression( + "var x = 1 + (goo() && foo())", + "foo", + "var temp$$0; if (temp$$0 = goo()) temp$$0 = foo();" + + "var x = 1 + temp$$0;"); + } + + public void testExposeExpression7() { + helperExposeExpression( + "if(goo() && foo());", + "foo", + "var temp$$0;" + + "if (temp$$0 = goo()) temp$$0 = foo();" + + "if(temp$$0);"); + } + + public void testExposeExpression8() { + helperExposeExpression( + "switch(goo() && foo()){}", + "foo", + "var temp$$0;" + + "if (temp$$0 = goo()) temp$$0 = foo();" + + "switch(temp$$0){}"); + } + + public void testExposeExpression9() { + helperExposeExpression( + "switch(1 + goo() + foo()){}", + "foo", + "var temp_const$$0 = 1 + goo();" + + "switch(temp_const$$0 + foo()){}"); + } + + public void testExposeExpression10() { + helperExposeExpression( + "function f(){ return goo() && foo();}", + "foo", + "function f(){" + + "var temp$$0; if (temp$$0 = goo()) temp$$0 = foo();" + + "return temp$$0;" + + "}"); + } + + public void testExposeExpression11() { + // TODO(johnlenz): We really want a constant marking pass. + // The value "goo" should be constant, but it isn't known to be so. + helperExposeExpression( + "if (goo(1, goo(2), (1 ? foo() : 0)));", + "foo", + "var temp_const$$1 = goo;" + + "var temp_const$$0 = goo(2);" + + "var temp$$2;" + + "if (1) temp$$2 = foo(); else temp$$2 = 0;" + + "if (temp_const$$1(1, temp_const$$0, temp$$2));"); + } + + // Simple name on LHS of assignment-op. + public void testExposePlusEquals1() { + helperExposeExpression( + "var x = 0; x += foo() + 1", + "foo", + "var x = 0; var temp_const$$0 = x;" + + "x = temp_const$$0 + (foo() + 1);"); + + helperExposeExpression( + "var x = 0; y = (x += foo()) + x", + "foo", + "var x = 0; var temp_const$$0 = x;" + + "y = (x = temp_const$$0 + foo()) + x"); + } + + // Structure on LHS of assignment-op. + public void testExposePlusEquals2() { + helperExposeExpression( + "var x = {}; x.a += foo() + 1", + "foo", + "var x = {}; var temp_const$$0 = x;" + + "var temp_const$$1 = temp_const$$0.a;" + + "temp_const$$0.a = temp_const$$1 + (foo() + 1);"); + + helperExposeExpression( + "var x = {}; y = (x.a += foo()) + x.a", + "foo", + "var x = {}; var temp_const$$0 = x;" + + "var temp_const$$1 = temp_const$$0.a;" + + "y = (temp_const$$0.a = temp_const$$1 + foo()) + x.a"); + } + + // Constant object on LHS of assignment-op. + public void testExposePlusEquals3() { + helperExposeExpression( + "/** @const */ var XX = {};\n" + + "XX.a += foo() + 1", + "foo", + "var XX = {}; var temp_const$$0 = XX.a;" + + "XX.a = temp_const$$0 + (foo() + 1);"); + + helperExposeExpression( + "var XX = {}; y = (XX.a += foo()) + XX.a", + "foo", + "var XX = {}; var temp_const$$0 = XX.a;" + + "y = (XX.a = temp_const$$0 + foo()) + XX.a"); + } + + // Function all on LHS of assignment-op. + public void testExposePlusEquals4() { + helperExposeExpression( + "var x = {}; goo().a += foo() + 1", + "foo", + "var x = {};" + + "var temp_const$$0 = goo();" + + "var temp_const$$1 = temp_const$$0.a;" + + "temp_const$$0.a = temp_const$$1 + (foo() + 1);"); + + helperExposeExpression( + "var x = {}; y = (goo().a += foo()) + goo().a", + "foo", + "var x = {};" + + "var temp_const$$0 = goo();" + + "var temp_const$$1 = temp_const$$0.a;" + + "y = (temp_const$$0.a = temp_const$$1 + foo()) + goo().a"); + } + + // Test multiple levels + public void testExposePlusEquals5() { + helperExposeExpression( + "var x = {}; goo().a.b += foo() + 1", + "foo", + "var x = {};" + + "var temp_const$$0 = goo().a;" + + "var temp_const$$1 = temp_const$$0.b;" + + "temp_const$$0.b = temp_const$$1 + (foo() + 1);"); + + helperExposeExpression( + "var x = {}; y = (goo().a.b += foo()) + goo().a", + "foo", + "var x = {};" + + "var temp_const$$0 = goo().a;" + + "var temp_const$$1 = temp_const$$0.b;" + + "y = (temp_const$$0.b = temp_const$$1 + foo()) + goo().a"); + } + + public void testExposeObjectLit1() { + // Validate that getter and setters methods are see as side-effect + // free and that values can move past them. We don't need to be + // concerned with exposing the getter or setter here but the + // decomposer does not have a method of exposing properties only variables. + helperMoveExpression( + "var x = {get a() {}, b: foo()};", + "foo", + "var result$$0=foo();var x = {get a() {}, b: result$$0};"); + + helperMoveExpression( + "var x = {set a(p) {}, b: foo()};", + "foo", + "var result$$0=foo();var x = {set a(p) {}, b: result$$0};"); + } + + /** Test case helpers. */ + + private void helperCanExposeExpression( + DecompositionType expectedResult, + String code, + String fnName + ) { + helperCanExposeExpression(expectedResult, code, fnName, null); + } + + private void helperCanExposeFunctionExpression( + DecompositionType expectedResult, String code, int call) { + Compiler compiler = getCompiler(); + Set knownConstants = Sets.newHashSet(); + ExpressionDecomposer decomposer = new ExpressionDecomposer( + compiler, compiler.getUniqueNameIdSupplier(), knownConstants); + Node tree = parse(compiler, code); + assertNotNull(tree); + + Node externsRoot = parse(compiler, + "function goo() {}" + + "function foo() {}"); + assertNotNull(externsRoot); + Node mainRoot = tree; + + Node callSite = findCall(tree, null, 2); + assertNotNull("Call " + call + " was not found.", callSite); + + compiler.resetUniqueNameId(); + DecompositionType result = decomposer.canExposeExpression( + callSite); + assertEquals(expectedResult, result); + } + + private void helperCanExposeExpression( + DecompositionType expectedResult, + String code, + String fnName, + Set knownConstants + ) { + Compiler compiler = getCompiler(); + if (knownConstants == null) { + knownConstants = Sets.newHashSet(); + } + ExpressionDecomposer decomposer = new ExpressionDecomposer( + compiler, compiler.getUniqueNameIdSupplier(), knownConstants); + Node tree = parse(compiler, code); + assertNotNull(tree); + + Node externsRoot = parse(compiler, + "function goo() {}" + + "function foo() {}"); + assertNotNull(externsRoot); + Node mainRoot = tree; + + Node callSite = findCall(tree, fnName); + assertNotNull("Call to " + fnName + " was not found.", callSite); + + compiler.resetUniqueNameId(); + DecompositionType result = decomposer.canExposeExpression( + callSite); + assertEquals(expectedResult, result); + } + + private void helperExposeExpression( + String code, + String fnName, + String expectedResult + ) { + helperExposeExpression( + code, fnName, expectedResult, null); + } + + private void validateSourceInfo(Compiler compiler, Node subtree) { + (new LineNumberCheck(compiler)).setCheckSubTree(subtree); + // Source information problems are reported as compiler errors. + if (compiler.getErrorCount() != 0) { + String msg = "Error encountered: "; + for (JSError err : compiler.getErrors()) { + msg += err.toString() + "\n"; + } + assertTrue(msg, compiler.getErrorCount() == 0); + } + } + + private void helperExposeExpression( + String code, + String fnName, + String expectedResult, + Set knownConstants + ) { + Compiler compiler = getCompiler(); + if (knownConstants == null) { + knownConstants = Sets.newHashSet(); + } + ExpressionDecomposer decomposer = new ExpressionDecomposer( + compiler, compiler.getUniqueNameIdSupplier(), knownConstants); + decomposer.setTempNamePrefix("temp"); + decomposer.setResultNamePrefix("result"); + Node expectedRoot = parse(compiler, expectedResult); + Node tree = parse(compiler, code); + assertNotNull(tree); + + Node externsRoot = new Node(Token.EMPTY); + Node mainRoot = tree; + + Node callSite = findCall(tree, fnName); + assertNotNull("Call to " + fnName + " was not found.", callSite); + + DecompositionType result = decomposer.canExposeExpression(callSite); + assertTrue(result == DecompositionType.DECOMPOSABLE); + + compiler.resetUniqueNameId(); + decomposer.exposeExpression(callSite); + validateSourceInfo(compiler, tree); + String explanation = expectedRoot.checkTreeEquals(tree); + assertNull("\nExpected: " + compiler.toSource(expectedRoot) + + "\nResult: " + compiler.toSource(tree) + + "\n" + explanation, explanation); + } + + private void helperMoveExpression( + String code, + String fnName, + String expectedResult + ) { + helperMoveExpression( + code, fnName, expectedResult, null); + } + + private void helperMoveExpression( + String code, + String fnName, + String expectedResult, + Set knownConstants + ) { + Compiler compiler = getCompiler(); + if (knownConstants == null) { + knownConstants = Sets.newHashSet(); + } + + ExpressionDecomposer decomposer = new ExpressionDecomposer( + compiler, compiler.getUniqueNameIdSupplier(), knownConstants); + decomposer.setTempNamePrefix("temp"); + decomposer.setResultNamePrefix("result"); + Node expectedRoot = parse(compiler, expectedResult); + Node tree = parse(compiler, code); + assertNotNull(tree); + + Node externsRoot = new Node(Token.EMPTY); + Node mainRoot = tree; + + Node callSite = findCall(tree, fnName); + assertNotNull("Call to " + fnName + " was not found.", callSite); + + compiler.resetUniqueNameId(); + decomposer.moveExpression(callSite); + validateSourceInfo(compiler, tree); + String explanation = expectedRoot.checkTreeEquals(tree); + assertNull("\nExpected: " + compiler.toSource(expectedRoot) + + "\nResult: " + compiler.toSource(tree) + + "\n" + explanation, explanation); + } + + private static Compiler getCompiler() { + Compiler compiler = new Compiler(); + CompilerOptions options = new CompilerOptions(); + options.setLanguageIn(LanguageMode.ECMASCRIPT5); + options.setCodingConvention(new GoogleCodingConvention()); + compiler.initOptions(options); + return compiler; + } + + private static Node findCall(Node n, String name) { + return findCall(n, name, 1); + } + + /** + * @param name The name to look for. + * @param call The call to look for. + * @return The return the Nth CALL node to name found in a pre-order + * traversal. + */ + private static Node findCall( + Node root, @Nullable final String name, final int call) { + class Find { + int found = 0; + Node find(Node n) { + if (n.isCall()) { + Node callee = n.getFirstChild(); + if (name == null || (callee.isName() + && callee.getString().equals(name))) { + found++; + if (found == call) { + return n; + } + } + } + + for (Node c : n.children()) { + Node result = find(c); + if (result != null) { + return result; + } + } + + return null; + } + } + + return (new Find()).find(root); + } + + private static Node parse(Compiler compiler, String js) { + Node n = Normalize.parseAndNormalizeTestCode(compiler, js, ""); + assertEquals(0, compiler.getErrorCount()); + return n; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExternExportsPassTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExternExportsPassTest.java new file mode 100755 index 0000000..b7816dc --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExternExportsPassTest.java @@ -0,0 +1,488 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; + +import junit.framework.TestCase; + +import java.util.List; + +/** + * Tests for {@link ExternExportsPass}. + * + */ +public class ExternExportsPassTest extends TestCase { + + private boolean runCheckTypes = true; + + /** + * ExternExportsPass relies on type information to emit JSDoc annotations for + * exported externs. However, the user can disable type checking and still + * ask for externs to be exported. Set this flag to enable or disable checking + * of types during a test. + */ + private void setRunCheckTypes(boolean shouldRunCheckTypes) { + runCheckTypes = shouldRunCheckTypes; + } + + @Override + public void setUp() throws Exception { + super.setUp(); + + setRunCheckTypes(true); + } + + public void testExportSymbol() throws Exception { + compileAndCheck("var a = {}; a.b = {}; a.b.c = function(d, e, f) {};" + + "goog.exportSymbol('foobar', a.b.c)", + "/**\n" + + " * @param {*} d\n" + + " * @param {*} e\n" + + " * @param {*} f\n" + + " * @return {undefined}\n" + + " */\n" + + "var foobar = function(d, e, f) {\n};\n"); + } + + public void testExportSymbolDefinedInVar() throws Exception { + compileAndCheck("var a = function(d, e, f) {};" + + "goog.exportSymbol('foobar', a)", + "/**\n" + + " * @param {*} d\n" + + " * @param {*} e\n" + + " * @param {*} f\n" + + " * @return {undefined}\n" + + " */\n" + + "var foobar = function(d, e, f) {\n};\n"); + } + + public void testExportProperty() throws Exception { + compileAndCheck("var a = {}; a.b = {}; a.b.c = function(d, e, f) {};" + + "goog.exportProperty(a.b, 'cprop', a.b.c)", + "var a;\n" + + "a.b;\n" + + "/**\n" + + " * @param {*} d\n" + + " * @param {*} e\n" + + " * @param {*} f\n" + + " * @return {undefined}\n" + + " */\n" + + "a.b.cprop = function(d, e, f) {\n};\n"); + } + + public void testExportMultiple() throws Exception { + compileAndCheck("var a = {}; a.b = function(p1) {}; " + + "a.b.c = function(d, e, f) {};" + + "a.b.prototype.c = function(g, h, i) {};" + + "goog.exportSymbol('a.b', a.b);" + + "goog.exportProperty(a.b, 'c', a.b.c);" + + "goog.exportProperty(a.b.prototype, 'c', a.b.prototype.c);", + + "var a;\n" + + "/**\n" + + " * @param {*} p1\n" + + " * @return {undefined}\n" + + " */\n" + + "a.b = function(p1) {\n};\n" + + "/**\n" + + " * @param {*} d\n" + + " * @param {*} e\n" + + " * @param {*} f\n" + + " * @return {undefined}\n" + + " */\n" + + "a.b.c = function(d, e, f) {\n};\n" + + "/**\n" + + " * @param {*} g\n" + + " * @param {*} h\n" + + " * @param {*} i\n" + + " * @return {undefined}\n" + + " */\n" + + "a.b.prototype.c = function(g, h, i) {\n};\n"); + } + + public void testExportMultiple2() throws Exception { + compileAndCheck("var a = {}; a.b = function(p1) {}; " + + "a.b.c = function(d, e, f) {};" + + "a.b.prototype.c = function(g, h, i) {};" + + "goog.exportSymbol('hello', a);" + + "goog.exportProperty(a.b, 'c', a.b.c);" + + "goog.exportProperty(a.b.prototype, 'c', a.b.prototype.c);", + + "/** @type {{b: function (?): undefined}} */\n" + + "var hello = {};\n" + + "hello.b;\n" + + "/**\n" + + " * @param {*} d\n" + + " * @param {*} e\n" + + " * @param {*} f\n" + + " * @return {undefined}\n" + + " */\n" + + "hello.b.c = function(d, e, f) {\n};\n" + + "/**\n" + + " * @param {*} g\n" + + " * @param {*} h\n" + + " * @param {*} i\n" + + " * @return {undefined}\n" + + " */\n" + + "hello.b.prototype.c = function(g, h, i) {\n};\n"); + } + + public void testExportMultiple3() throws Exception { + compileAndCheck("var a = {}; a.b = function(p1) {}; " + + "a.b.c = function(d, e, f) {};" + + "a.b.prototype.c = function(g, h, i) {};" + + "goog.exportSymbol('prefix', a.b);" + + "goog.exportProperty(a.b, 'c', a.b.c);", + + "/**\n" + + " * @param {*} p1\n" + + " * @return {undefined}\n" + + " */\n" + + "var prefix = function(p1) {\n};\n" + + "/**\n" + + " * @param {*} d\n" + + " * @param {*} e\n" + + " * @param {*} f\n" + + " * @return {undefined}\n" + + " */\n" + + "prefix.c = function(d, e, f) {\n};\n"); + } + + public void testExportNonStaticSymbol() throws Exception { + compileAndCheck("var a = {}; a.b = {}; var d = {}; a.b.c = d;" + + "goog.exportSymbol('foobar', a.b.c)", + "var foobar;\n"); + } + + public void testExportNonStaticSymbol2() throws Exception { + compileAndCheck("var a = {}; a.b = {}; var d = null; a.b.c = d;" + + "goog.exportSymbol('foobar', a.b.c())", + "var foobar;\n"); + } + + public void testExportNonexistentProperty() throws Exception { + compileAndCheck("var a = {}; a.b = {}; a.b.c = function(d, e, f) {};" + + "goog.exportProperty(a.b, 'none', a.b.none)", + "var a;\n" + + "a.b;\n" + + "a.b.none;\n"); + } + + public void testExportSymbolWithTypeAnnotation() { + + compileAndCheck("var internalName;\n" + + "/**\n" + + " * @param {string} param1\n" + + " * @param {number} param2\n" + + " * @return {string}\n" + + " */\n" + + "internalName = function(param1, param2) {" + + "return param1 + param2;" + + "};" + + "goog.exportSymbol('externalName', internalName)", + "/**\n" + + " * @param {string} param1\n" + + " * @param {number} param2\n" + + " * @return {string}\n" + + " */\n" + + "var externalName = function(param1, param2) {\n};\n"); + } + + public void testExportSymbolWithoutTypeCheck() { + // ExternExportsPass should not emit annotations + // if there is no type information available. + setRunCheckTypes(false); + + compileAndCheck("var internalName;\n" + + "/**\n" + + " * @param {string} param1\n" + + " * @param {number} param2\n" + + " * @return {string}\n" + + " */\n" + + "internalName = function(param1, param2) {" + + "return param1 + param2;" + + "};" + + "goog.exportSymbol('externalName', internalName)", + "var externalName = function(param1, param2) {\n};\n"); + } + + public void testExportSymbolWithConstructor() { + compileAndCheck("var internalName;\n" + + "/**\n" + + " * @constructor\n" + + " */\n" + + "internalName = function() {" + + "};" + + "goog.exportSymbol('externalName', internalName)", + "/**\n" + + " * @return {undefined}\n" + + " * @constructor\n" + + " */\n" + + "var externalName = function() {\n};\n"); + } + + public void testExportSymbolWithConstructorWithoutTypeCheck() { + // For now, skipping type checking should prevent generating + // annotations of any kind, so, e.g., @constructor is not preserved. + // This is probably not ideal, but since JSDocInfo for functions is attached + // to JSTypes and not Nodes (and no JSTypes are created when checkTypes + // is false), we don't really have a choice. + + setRunCheckTypes(false); + + compileAndCheck("var internalName;\n" + + "/**\n" + + " * @constructor\n" + + " */\n" + + "internalName = function() {" + + "};" + + "goog.exportSymbol('externalName', internalName)", + "var externalName = function() {\n};\n"); + } + + public void testExportFunctionWithOptionalArguments() { + compileAndCheck("var internalName;\n" + + "/**\n" + + " * @param {number=} a\n" + + " */\n" + + "internalName = function(a) {" + + " return 6;\n" + + "};" + + "goog.exportSymbol('externalName', internalName)", + "/**\n" + + " * @param {number=} a\n" + + " */\n" + + "var externalName = function(a) {\n};\n"); + } + + public void testExportFunctionWithVariableArguments() { + compileAndCheck("var internalName;\n" + + "/**\n" + + " * @param {...number} a\n" + + " * @return {number}\n" + + " */\n" + + "internalName = function(a) {" + + " return 6;\n" + + "};" + + "goog.exportSymbol('externalName', internalName)", + "/**\n" + + " * @param {...number} a\n" + + " * @return {number}\n" + + " */\n" + + "var externalName = function(a) {\n};\n"); + } + + /** + * Enums are not currently handled. + */ + public void testExportEnum() { + // We don't care what the values of the object properties are. + // They're ignored by the type checker, and even if they weren't, it'd + // be incomputable to get them correct in all cases + // (think complex objects). + compileAndCheck( + "/** @enum {string}\n @export */ var E = {A:8, B:9};" + + "goog.exportSymbol('E', E);", + "/** @enum {string} */\n" + + "var E = {A:1, B:2};\n"); + } + + /** If we export a property with "prototype" as a path component, there + * is no need to emit the initializer for prototype because every namespace + * has one automatically. + */ + public void testExportDontEmitPrototypePathPrefix() { + compileAndCheck( + "/**\n" + + " * @constructor\n" + + " */\n" + + "var Foo = function() {};" + + "/**\n" + + " * @return {number}\n" + + " */\n" + + "Foo.prototype.m = function() {return 6;};\n" + + "goog.exportSymbol('Foo', Foo);\n" + + "goog.exportProperty(Foo.prototype, 'm', Foo.prototype.m);", + "/**\n" + + " * @return {undefined}\n" + + " * @constructor\n" + + " */\n" + + "var Foo = function() {\n};\n" + + "/**\n" + + " * @return {number}\n" + + " */\n" + + "Foo.prototype.m = function() {\n};\n" + ); + } + + /** + * Test the workflow of creating an externs file for a library + * via the export pass and then using that externs file in a client. + * + * There should be no warnings in the client if the library includes + * type information for the exported functions and the client uses them + * correctly. + */ + public void testUseExportsAsExterns() { + String librarySource = + "/**\n" + + " * @param {number} a\n" + + " * @constructor\n" + + " */\n" + + "var InternalName = function(a) {" + + "};" + + "goog.exportSymbol('ExternalName', InternalName)"; + + String clientSource = + "var a = new ExternalName(6);\n" + + "/**\n" + + " * @param {ExternalName} x\n" + + " */\n" + + "var b = function(x) {};"; + + Result libraryCompileResult = compileAndExportExterns(librarySource); + + assertEquals(0, libraryCompileResult.warnings.length); + assertEquals(0, libraryCompileResult.errors.length); + + String generatedExterns = libraryCompileResult.externExport; + + Result clientCompileResult = compileAndExportExterns(clientSource, + generatedExterns); + + assertEquals(0, clientCompileResult.warnings.length); + assertEquals(0, clientCompileResult.errors.length); + } + + public void testWarnOnExportFunctionWithUnknownReturnType() { + String librarySource = + "var InternalName = function() {" + + " return 6;" + + "};" + + "goog.exportSymbol('ExternalName', InternalName)"; + + Result libraryCompileResult = compileAndExportExterns(librarySource); + + assertEquals(1, libraryCompileResult.warnings.length); + assertEquals(0, libraryCompileResult.errors.length); + } + + public void testDontWarnOnExportConstructorWithUnknownReturnType() { + String librarySource = + "/**\n" + + " * @constructor\n" + + " */\n " + + "var InternalName = function() {" + + "};" + + "goog.exportSymbol('ExternalName', InternalName)"; + + Result libraryCompileResult = compileAndExportExterns(librarySource); + + assertEquals(0, libraryCompileResult.warnings.length); + assertEquals(0, libraryCompileResult.errors.length); + } + + public void testTypedef() { + compileAndCheck( + "/** @typedef {{x: number, y: number}} */ var Coord;\n" + + "/**\n" + + " * @param {Coord} a\n" + + " * @export\n" + + " */\n" + + "var fn = function(a) {};" + + "goog.exportSymbol('fn', fn);", + "/**\n" + + " * @param {{x: number, y: number}} a\n" + + " * @return {undefined}\n" + + " */\n" + + "var fn = function(a) {\n};\n"); + } + + private void compileAndCheck(String js, String expected) { + Result result = compileAndExportExterns(js); + + assertEquals(expected, result.externExport); + } + + public void testWarnOnExportFunctionWithUnknownParameterTypes() { + /* This source is missing types for the b and c parameters */ + String librarySource = + "/**\n" + + " * @param {number} a\n" + + " * @return {number}" + + " */\n " + + "var InternalName = function(a,b,c) {" + + " return 6;" + + "};" + + "goog.exportSymbol('ExternalName', InternalName)"; + + Result libraryCompileResult = compileAndExportExterns(librarySource); + + assertEquals(2, libraryCompileResult.warnings.length); + assertEquals(0, libraryCompileResult.errors.length); + } + + private Result compileAndExportExterns(String js) { + return compileAndExportExterns(js, ""); + } + + /** + * Compiles the passed in JavaScript with the passed in externs and returns + * the new externs exported by the this pass. + * + * @param js the source to be compiled + * @param externs the externs the {@code js} source needs + * @return the externs generated from {@code js} + */ + private Result compileAndExportExterns(String js, String externs) { + Compiler compiler = new Compiler(); + CompilerOptions options = new CompilerOptions(); + options.externExportsPath = "externs.js"; + + // Turn on IDE mode to get rid of optimizations. + options.ideMode = true; + + /* Check types so we can make sure our exported externs have + * type information. + */ + options.checkSymbols = true; + options.checkTypes = runCheckTypes; + + List inputs = Lists.newArrayList( + SourceFile.fromCode("testcode", + "var goog = {};" + + "goog.exportSymbol = function(a, b) {}; " + + "goog.exportProperty = function(a, b, c) {}; " + + js)); + + List externFiles = Lists.newArrayList( + SourceFile.fromCode("externs", externs)); + + Result result = compiler.compile(externFiles, inputs, options); + + if (!result.success) { + String msg = "Errors:"; + msg += Joiner.on("\n").join(result.errors); + assertTrue(msg, result.success); + } + + return result; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExtractPrototypeMemberDeclarationsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExtractPrototypeMemberDeclarationsTest.java new file mode 100644 index 0000000..9f08b80 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ExtractPrototypeMemberDeclarationsTest.java @@ -0,0 +1,287 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.ExtractPrototypeMemberDeclarations.Pattern; + +/** + * Tests for {@link ExtractPrototypeMemberDeclarations}. + * + */ +public class ExtractPrototypeMemberDeclarationsTest extends CompilerTestCase { + private static final String TMP = "JSCompiler_prototypeAlias"; + private Pattern pattern = Pattern.USE_GLOBAL_TEMP; + + @Override + protected void setUp() { + super.enableLineNumberCheck(true); + enableNormalize(); + pattern = Pattern.USE_GLOBAL_TEMP; + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new ExtractPrototypeMemberDeclarations(compiler, pattern); + } + + public void testNotEnoughPrototypeToExtract() { + // switch statement with stuff after "return" + for (int i = 0; i < 7; i++) { + testSame(generatePrototypeDeclarations("x", i)); + } + } + + public void testExtractingSingleClassPrototype() { + extract(generatePrototypeDeclarations("x", 7), + loadPrototype("x") + + generateExtractedDeclarations(7)); + } + + public void testExtractingTwoClassPrototype() { + extract( + generatePrototypeDeclarations("x", 6) + + generatePrototypeDeclarations("y", 6), + loadPrototype("x") + + generateExtractedDeclarations(6) + + loadPrototype("y") + + generateExtractedDeclarations(6)); + } + + public void testExtractingTwoClassPrototypeInDifferentBlocks() { + extract( + generatePrototypeDeclarations("x", 6) + + "if (foo()) {" + + generatePrototypeDeclarations("y", 6) + + "}", + loadPrototype("x") + + generateExtractedDeclarations(6) + + "if (foo()) {" + + loadPrototype("y") + + generateExtractedDeclarations(6) + + "}"); + } + + public void testNoMemberDeclarations() { + testSame( + "x.prototype = {}; x.prototype = {}; x.prototype = {};" + + "x.prototype = {}; x.prototype = {}; x.prototype = {};" + + "x.prototype = {}; x.prototype = {}; x.prototype = {};"); + } + + public void testExtractingPrototypeWithQName() { + extract( + generatePrototypeDeclarations("com.google.javascript.jscomp.x", 7), + loadPrototype("com.google.javascript.jscomp.x") + + generateExtractedDeclarations(7)); + } + + public void testInterweaved() { + testSame( + "x.prototype.a=1; y.prototype.a=1;" + + "x.prototype.b=1; y.prototype.b=1;" + + "x.prototype.c=1; y.prototype.c=1;" + + "x.prototype.d=1; y.prototype.d=1;" + + "x.prototype.e=1; y.prototype.e=1;" + + "x.prototype.f=1; y.prototype.f=1;"); + } + + public void testExtractingPrototypeWithNestedMembers() { + extract( + "x.prototype.y.a = 1;" + + "x.prototype.y.b = 1;" + + "x.prototype.y.c = 1;" + + "x.prototype.y.d = 1;" + + "x.prototype.y.e = 1;" + + "x.prototype.y.f = 1;" + + "x.prototype.y.g = 1;", + loadPrototype("x") + + TMP + ".y.a = 1;" + + TMP + ".y.b = 1;" + + TMP + ".y.c = 1;" + + TMP + ".y.d = 1;" + + TMP + ".y.e = 1;" + + TMP + ".y.f = 1;" + + TMP + ".y.g = 1;"); + } + + public void testWithDevirtualization() { + extract( + "x.prototype.a = 1;" + + "x.prototype.b = 1;" + + "function devirtualize1() { }" + + "x.prototype.c = 1;" + + "x.prototype.d = 1;" + + "x.prototype.e = 1;" + + "x.prototype.f = 1;" + + "x.prototype.g = 1;", + + loadPrototype("x") + + TMP + ".a = 1;" + + TMP + ".b = 1;" + + "function devirtualize1() { }" + + TMP + ".c = 1;" + + TMP + ".d = 1;" + + TMP + ".e = 1;" + + TMP + ".f = 1;" + + TMP + ".g = 1;"); + + extract( + "x.prototype.a = 1;" + + "x.prototype.b = 1;" + + "function devirtualize1() { }" + + "x.prototype.c = 1;" + + "x.prototype.d = 1;" + + "function devirtualize2() { }" + + "x.prototype.e = 1;" + + "x.prototype.f = 1;" + + "function devirtualize3() { }" + + "x.prototype.g = 1;", + + loadPrototype("x") + + TMP + ".a = 1;" + + TMP + ".b = 1;" + + "function devirtualize1() { }" + + TMP + ".c = 1;" + + TMP + ".d = 1;" + + "function devirtualize2() { }" + + TMP + ".e = 1;" + + TMP + ".f = 1;" + + "function devirtualize3() { }" + + TMP + ".g = 1;"); + } + + public void testAnonSimple() { + pattern = Pattern.USE_ANON_FUNCTION; + + extract( + generatePrototypeDeclarations("x", 3), + generateExtractedDeclarations(3) + + loadPrototype("x")); + + testSame(generatePrototypeDeclarations("x", 1)); + testSame(generatePrototypeDeclarations("x", 2)); + + extract( + generatePrototypeDeclarations("x", 7), + generateExtractedDeclarations(7) + + loadPrototype("x")); + + } + + public void testAnonWithDevirtualization() { + pattern = Pattern.USE_ANON_FUNCTION; + + extract( + "x.prototype.a = 1;" + + "x.prototype.b = 1;" + + "function devirtualize() { }" + + "x.prototype.c = 1;", + + "(function(" + TMP + "){" + + TMP + ".a = 1;" + + TMP + ".b = 1;" + + TMP + ".c = 1;" + + loadPrototype("x") + + "function devirtualize() { }"); + + extract( + "x.prototype.a = 1;" + + "function devirtualize1() { }" + + "x.prototype.b = 1;" + + "function devirtualize2() { }" + + "x.prototype.c = 1;" + + "function devirtualize3() { }", + + "(function(" + TMP + "){" + + TMP + ".a = 1;" + + TMP + ".b = 1;" + + TMP + ".c = 1;" + + loadPrototype("x") + + "function devirtualize1() { }" + + "function devirtualize2() { }" + + "function devirtualize3() { }"); + } + + public void testAnonWithSideFx() { + pattern = Pattern.USE_ANON_FUNCTION; + testSame( + "function foo() {};" + + "foo.prototype.a1 = 1;" + + "bar();;" + + "foo.prototype.a2 = 2;" + + "bar();;" + + "foo.prototype.a3 = 3;" + + "bar();;" + + "foo.prototype.a4 = 4;" + + "bar();;" + + "foo.prototype.a5 = 5;" + + "bar();;" + + "foo.prototype.a6 = 6;" + + "bar();;" + + "foo.prototype.a7 = 7;" + + "bar();"); + } + + public String loadPrototype(String qName) { + if (pattern == Pattern.USE_GLOBAL_TEMP) { + return TMP + " = " + qName + ".prototype;"; + } else { + return "})(" + qName + ".prototype);"; + } + } + + public void extract(String src, String expected) { + if (pattern == Pattern.USE_GLOBAL_TEMP) { + test(src, "var " + TMP + ";" + expected); + } else { + test(src, expected); + } + } + + public String generatePrototypeDeclarations(String className, int num) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < num; i++) { + char member = (char) ('a' + i); + builder.append(generatePrototypeDeclaration( + className, "" + member, "" + member)); + } + return builder.toString(); + } + + public String generatePrototypeDeclaration(String className, String member, + String value) { + return className + ".prototype." + member + " = " + value + ";"; + } + + public String generateExtractedDeclarations(int num) { + StringBuilder builder = new StringBuilder(); + + if (pattern == Pattern.USE_ANON_FUNCTION) { + builder.append("(function(" + TMP + "){"); + } + + for (int i = 0; i < num; i++) { + char member = (char) ('a' + i); + builder.append(generateExtractedDeclaration("" + member, "" + member)); + } + return builder.toString(); + } + + public String generateExtractedDeclaration(String member, String value) { + return TMP + "." + member + " = " + value + ";"; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FixedPointGraphTraversalTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FixedPointGraphTraversalTest.java new file mode 100644 index 0000000..3c79559 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FixedPointGraphTraversalTest.java @@ -0,0 +1,211 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.graph.FixedPointGraphTraversal; +import com.google.javascript.jscomp.graph.LinkedDirectedGraph; +import com.google.javascript.jscomp.graph.FixedPointGraphTraversal.EdgeCallback; +import com.google.javascript.jscomp.graph.DiGraph; + +import junit.framework.TestCase; + +/** + * Test for FixedPointGraphTraversal. + * @author nicksantos@google.com (Nick Santos) + */ +public class FixedPointGraphTraversalTest extends TestCase { + + // The maximum value of a counter that counts as a "change" + // to the state of the graph, for the purposes of fixed-point + // computation. + private int maxChange = 0; + + private class Counter { + int value = 0; + } + + private class CounterIncrementer implements EdgeCallback { + @Override + public boolean traverseEdge(Counter source, String e, Counter dest) { + dest.value++; + return dest.value <= maxChange; + } + } + + private DiGraph graph; + + private Counter A, B, C, D, E; + private CounterIncrementer callback = new CounterIncrementer(); + private FixedPointGraphTraversal traversal = + new FixedPointGraphTraversal(callback); + + // Create a new graph of the following form: + // + // A + // / \ + // | B + // / \ / + // C D + // \ / + // E + // + // with all edges pointing downwards, and an "up-edge" from E to D. + @Override + public void setUp() { + A = new Counter(); + B = new Counter(); + C = new Counter(); + D = new Counter(); + E = new Counter(); + + graph = LinkedDirectedGraph.create(); + graph.createDirectedGraphNode(A); + graph.createDirectedGraphNode(B); + graph.createDirectedGraphNode(C); + graph.createDirectedGraphNode(D); + graph.createDirectedGraphNode(E); + + graph.connect(A, "->", B); + graph.connect(A, "->", C); + graph.connect(A, "->", D); + graph.connect(B, "->", D); + graph.connect(C, "->", E); + graph.connect(D, "->", E); + graph.connect(E, "->", D); + } + + public void testGraph1() { + maxChange = 0; + traversal.computeFixedPoint(graph, A); + + assertEquals(0, A.value); + assertEquals(1, B.value); + assertEquals(1, C.value); + assertEquals(1, D.value); + assertEquals(0, E.value); + } + + public void testGraph2() { + maxChange = 0; + traversal.computeFixedPoint(graph, D); + + assertEquals(0, A.value); + assertEquals(0, B.value); + assertEquals(0, C.value); + assertEquals(0, D.value); + assertEquals(1, E.value); + } + + public void testGraph3() { + maxChange = 1; + traversal.computeFixedPoint(graph, A); + + assertEquals(0, A.value); + assertEquals(1, B.value); + assertEquals(1, C.value); + assertEquals(3, D.value); + assertEquals(2, E.value); + } + + public void testGraph4() { + maxChange = 1; + traversal.computeFixedPoint(graph, D); + + assertEquals(0, A.value); + assertEquals(0, B.value); + assertEquals(0, C.value); + assertEquals(1, D.value); + assertEquals(2, E.value); + } + + public void testGraph5() { + maxChange = 5; + traversal.computeFixedPoint(graph, A); + + assertEquals(0, A.value); + assertEquals(1, B.value); + assertEquals(1, C.value); + assertEquals(6, D.value); + assertEquals(5, E.value); + } + + public void testGraph6() { + maxChange = 5; + traversal.computeFixedPoint(graph, B); + + assertEquals(0, A.value); + assertEquals(0, B.value); + assertEquals(0, C.value); + assertEquals(6, D.value); + assertEquals(5, E.value); + } + + public void testGraph8() { + maxChange = 2; + traversal.computeFixedPoint(graph, A); + + try { + traversal = new FixedPointGraphTraversal( + new EdgeCallback() { + @Override + public boolean traverseEdge(Counter source, String e, Counter dest) { + return true; + } + }); + traversal.computeFixedPoint(graph, A); + fail("Expecting Error: " + + FixedPointGraphTraversal.NON_HALTING_ERROR_MSG); + } catch (IllegalStateException e) { + assertEquals(e.getMessage(), + FixedPointGraphTraversal.NON_HALTING_ERROR_MSG); + } + } + + public void testGraph9() { + maxChange = 0; + + // when the graph traversal is done for the whole graph, we're actually + // counting the number of "in" edges for each node. + traversal.computeFixedPoint(graph); + + assertEquals(0, A.value); + assertEquals(1, B.value); + assertEquals(1, C.value); + assertEquals(3, D.value); + assertEquals(2, E.value); + } + + public void testGraph10() { + // Test a graph with self-edges. + maxChange = 5; + + A = new Counter(); + B = new Counter(); + + graph = LinkedDirectedGraph.create(); + graph.createDirectedGraphNode(A); + graph.createDirectedGraphNode(B); + + graph.connect(A, "->", A); + graph.connect(A, "->", B); + + traversal.computeFixedPoint(graph); + + assertEquals(6, A.value); + assertEquals(6, B.value); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FlowSensitiveInlineVariablesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FlowSensitiveInlineVariablesTest.java new file mode 100644 index 0000000..3dbf8b6 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FlowSensitiveInlineVariablesTest.java @@ -0,0 +1,585 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + * Unit tests for {@link FlowSensitiveInlineVariables}. + * + */ +public class FlowSensitiveInlineVariablesTest extends CompilerTestCase { + + public static final String EXTERN_FUNCTIONS = "" + + "var print;\n" + + "/** @nosideeffects */ function noSFX() {} \n" + + " function hasSFX() {} \n"; + + public FlowSensitiveInlineVariablesTest() { + enableNormalize(true); + } + + @Override + public int getNumRepetitions() { + // Test repeatedly inline. + return 3; + } + + @Override + protected CompilerPass getProcessor(final Compiler compiler) { + //return new FlowSensitiveInlineVariables(compiler); + return new CompilerPass() { + @Override + public void process(Node externs, Node root) { + (new MarkNoSideEffectCalls(compiler)).process(externs, root); + (new FlowSensitiveInlineVariables(compiler)).process(externs, root); + } + }; + } + + public void testSimpleAssign() { + inline("var x; x = 1; print(x)", "var x; print(1)"); + inline("var x; x = 1; x", "var x; 1"); + inline("var x; x = 1; var a = x", "var x; var a = 1"); + inline("var x; x = 1; x = x + 1", "var x; x = 1 + 1"); + } + + public void testSimpleVar() { + inline("var x = 1; print(x)", "var x; print(1)"); + inline("var x = 1; x", "var x; 1"); + inline("var x = 1; var a = x", "var x; var a = 1"); + inline("var x = 1; x = x + 1", "var x; x = 1 + 1"); + } + + public void testSimpleForIn() { + inline("var a,b,x = a in b; x", + "var a,b,x; a in b"); + noInline("var a, b; var x = a in b; print(1); x"); + noInline("var a,b,x = a in b; delete a[b]; x"); + } + + public void testExported() { + noInline("var _x = 1; print(_x)"); + } + + public void testDoNotInlineIncrement() { + noInline("var x = 1; x++;"); + noInline("var x = 1; x--;"); + } + + public void testDoNotInlineAssignmentOp() { + noInline("var x = 1; x += 1;"); + noInline("var x = 1; x -= 1;"); + } + + public void testDoNotInlineIntoLhsOfAssign() { + noInline("var x = 1; x += 3;"); + } + + public void testMultiUse() { + noInline("var x; x = 1; print(x); print (x);"); + } + + public void testMultiUseInSameCfgNode() { + noInline("var x; x = 1; print(x) || print (x);"); + } + + public void testMultiUseInTwoDifferentPath() { + noInline("var x = 1; if (print) { print(x) } else { alert(x) }"); + } + + public void testAssignmentBeforeDefinition() { + inline("x = 1; var x = 0; print(x)","x = 1; var x; print(0)" ); + } + + public void testVarInConditionPath() { + noInline("if (foo) { var x = 0 } print(x)"); + } + + public void testMultiDefinitionsBeforeUse() { + inline("var x = 0; x = 1; print(x)", "var x = 0; print(1)"); + } + + public void testMultiDefinitionsInSameCfgNode() { + noInline("var x; (x = 1) || (x = 2); print(x)"); + noInline("var x; x = (1 || (x = 2)); print(x)"); + noInline("var x;(x = 1) && (x = 2); print(x)"); + noInline("var x;x = (1 && (x = 2)); print(x)"); + noInline("var x; x = 1 , x = 2; print(x)"); + } + + public void testNotReachingDefinitions() { + noInline("var x; if (foo) { x = 0 } print (x)"); + } + + public void testNoInlineLoopCarriedDefinition() { + // First print is undefined instead. + noInline("var x; while(true) { print(x); x = 1; }"); + + // Prints 0 1 1 1 1.... + noInline("var x = 0; while(true) { print(x); x = 1; }"); + } + + public void testDoNotExitLoop() { + noInline("while (z) { var x = 3; } var y = x;"); + } + + public void testDoNotInlineWithinLoop() { + noInline("var y = noSFX(); do { var z = y.foo(); } while (true);"); + } + + public void testDoNotInlineCatchExpression1() { + noInline( + "var a;\n" + + "try {\n" + + " throw Error(\"\");\n" + + "}catch(err) {" + + " a = err;\n" + + "}\n" + + "return a.stack\n"); + } + + public void testDoNotInlineCatchExpression1a() { + noInline( + "var a;\n" + + "try {\n" + + " throw Error(\"\");\n" + + "}catch(err) {" + + " a = err + 1;\n" + + "}\n" + + "return a.stack\n"); + } + + public void testDoNotInlineCatchExpression2() { + noInline( + "var a;\n" + + "try {\n" + + " if (x) {throw Error(\"\");}\n" + + "}catch(err) {" + + " a = err;\n" + + "}\n" + + "return a.stack\n"); + } + + public void testDoNotInlineCatchExpression3() { + noInline( + "var a;\n" + + "try {\n" + + " throw Error(\"\");\n" + + "} catch(err) {" + + " err = x;\n" + + " a = err;\n" + + "}\n" + + "return a.stack\n"); + } + + public void testDoNotInlineCatchExpression4() { + // Note: it is valid to inline "x" here but we currently don't. + noInline( + "try {\n" + + " stuff();\n" + + "} catch (e) {\n" + + " x = e;\n" + + " print(x);\n" + + "}"); + } + + public void testDefinitionAfterUse() { + inline("var x = 0; print(x); x = 1", "var x; print(0); x = 1"); + } + + public void testInlineSameVariableInStraightLine() { + inline("var x; x = 1; print(x); x = 2; print(x)", + "var x; print(1); print(2)"); + } + + public void testInlineInDifferentPaths() { + inline("var x; if (print) {x = 1; print(x)} else {x = 2; print(x)}", + "var x; if (print) {print(1)} else {print(2)}"); + } + + public void testNoInlineInMergedPath() { + noInline( + "var x,y;x = 1;while(y) { if(y){ print(x) } else { x = 1 } } print(x)"); + } + + public void testInlineIntoExpressions() { + inline("var x = 1; print(x + 1);", "var x; print(1 + 1)"); + } + + public void testInlineExpressions1() { + inline("var a, b; var x = a+b; print(x)", "var a, b; var x; print(a+b)"); + } + + public void testInlineExpressions2() { + // We can't inline because of the redefinition of "a". + noInline("var a, b; var x = a + b; a = 1; print(x)"); + } + + public void testInlineExpressions3() { + inline("var a,b,x; x=a+b; x=a-b ; print(x)", + "var a,b,x; x=a+b; print(a-b)"); + } + + public void testInlineExpressions4() { + // Precision is lost due to comma's. + noInline("var a,b,x; x=a+b, x=a-b; print(x)"); + } + + public void testInlineExpressions5() { + noInline("var a; var x = a = 1; print(x)"); + } + + public void testInlineExpressions6() { + noInline("var a, x; a = 1 + (x = 1); print(x)"); + } + + public void testInlineExpression7() { + // Possible side effects in foo() that might conflict with bar(); + noInline("var x = foo() + 1; bar(); print(x)"); + + // This is a possible case but we don't have analysis to prove this yet. + // TODO(user): It is possible to cover this case with the same algorithm + // as the missing return check. + noInline("var x = foo() + 1; print(x)"); + } + + public void testInlineExpression8() { + // The same variable inlined twice. + inline( + "var a,b;" + + "var x = a + b; print(x); x = a - b; print(x)", + "var a,b;" + + "var x; print(a + b); print(a - b)"); + } + + public void testInlineExpression9() { + // Check for actual control flow sensitivity. + inline( + "var a,b;" + + "var x; if (g) { x= a + b; print(x) } x = a - b; print(x)", + "var a,b;" + + "var x; if (g) { print(a + b)} print(a - b)"); + } + + public void testInlineExpression10() { + // The DFA is not fine grain enough for this. + noInline("var x, y; x = ((y = 1), print(y))"); + } + + public void testInlineExpressions11() { + inline("var x; x = x + 1; print(x)", "var x; print(x + 1)"); + noInline("var x; x = x + 1; print(x); print(x)"); + } + + public void testInlineExpressions12() { + // ++ is an assignment and considered to modify state so it will not be + // inlined. + noInline("var x = 10; x = c++; print(x)"); + } + + public void testInlineExpressions13() { + inline("var a = 1, b = 2;" + + "var x = a;" + + "var y = b;" + + "var z = x + y;" + + "var i = z;" + + "var j = z + y;" + + "var k = i;", + + "var a, b;" + + "var x;" + + "var y = 2;" + + "var z = 1 + y;" + + "var i;" + + "var j = z + y;" + + "var k = z;"); + } + + public void testNoInlineIfDefinitionMayNotReach() { + noInline("var x; if (x=1) {} x;"); + } + + public void testNoInlineEscapedToInnerFunction() { + noInline("var x = 1; function foo() { x = 2 }; print(x)"); + } + + public void testNoInlineLValue() { + noInline("var x; if (x = 1) { print(x) }"); + } + + public void testSwitchCase() { + inline("var x = 1; switch(x) { }", "var x; switch(1) { }"); + } + + public void testShadowedVariableInnerFunction() { + inline("var x = 1; print(x) || (function() { var x; x = 1; print(x)})()", + "var x; print(1) || (function() { var x; print(1)})()"); + } + + public void testCatch() { + noInline("var x = 0; try { } catch (x) { }"); + noInline("try { } catch (x) { print(x) }"); + } + + public void testNoInlineGetProp() { + // We don't know if j alias a.b + noInline("var x = a.b.c; j.c = 1; print(x);"); + } + + public void testNoInlineGetProp2() { + noInline("var x = 1 * a.b.c; j.c = 1; print(x);"); + } + + public void testNoInlineGetProp3() { + // Anything inside a function is fine. + inline("var x = function(){1 * a.b.c}; print(x);", + "var x; print(function(){1 * a.b.c});"); + } + + public void testNoInlineGetEle() { + // Again we don't know if i = j + noInline("var x = a[i]; a[j] = 2; print(x); "); + } + + // TODO(user): These should be inlinable. + public void testNoInlineConstructors() { + noInline("var x = new Iterator(); x.next();"); + } + + // TODO(user): These should be inlinable. + public void testNoInlineArrayLits() { + noInline("var x = []; print(x)"); + } + + // TODO(user): These should be inlinable. + public void testNoInlineObjectLits() { + noInline("var x = {}; print(x)"); + } + + // TODO(user): These should be inlinable after the REGEX checks. + public void testNoInlineRegExpLits() { + noInline("var x = /y/; print(x)"); + } + + public void testInlineConstructorCallsIntoLoop() { + // Don't inline construction into loops. + noInline("var x = new Iterator();" + + "for(i = 0; i < 10; i++) {j = x.next()}"); + } + + public void testRemoveWithLabels() { + inline("var x = 1; L: x = 2; print(x)", "var x = 1; L:{} print(2)"); + inline("var x = 1; L: M: x = 2; print(x)", "var x = 1; L:M:{} print(2)"); + inline("var x = 1; L: M: N: x = 2; print(x)", + "var x = 1; L:M:N:{} print(2)"); + } + + public void testInlineAcrossSideEffect1() { + // This can't be inlined because print() has side-effects and might change + // the definition of noSFX. + // + // noSFX must be both const and pure in order to inline it. + noInline("var y; var x = noSFX(y); print(x)"); + //inline("var y; var x = noSFX(y); print(x)", "var y;var x;print(noSFX(y))"); + } + + public void testInlineAcrossSideEffect2() { + // Think noSFX() as a function that reads y.foo and return it + // and SFX() write some new value of y.foo. If that's the case, + // inlining across hasSFX() is not valid. + + // This is a case where hasSFX is right of the source of the inlining. + noInline("var y; var x = noSFX(y), z = hasSFX(y); print(x)"); + noInline("var y; var x = noSFX(y), z = new hasSFX(y); print(x)"); + noInline("var y; var x = new noSFX(y), z = new hasSFX(y); print(x)"); + } + + public void testInlineAcrossSideEffect3() { + // This is a case where hasSFX is left of the destination of the inlining. + noInline("var y; var x = noSFX(y); hasSFX(y), print(x)"); + noInline("var y; var x = noSFX(y); new hasSFX(y), print(x)"); + noInline("var y; var x = new noSFX(y); new hasSFX(y), print(x)"); + } + + public void testInlineAcrossSideEffect4() { + // This is a case where hasSFX is some control flow path between the + // source and its destination. + noInline("var y; var x = noSFX(y); hasSFX(y); print(x)"); + noInline("var y; var x = noSFX(y); new hasSFX(y); print(x)"); + noInline("var y; var x = new noSFX(y); new hasSFX(y); print(x)"); + } + + public void testCanInlineAcrossNoSideEffect() { + // This can't be inlined because print() has side-effects and might change + // the definition of noSFX. We should be able to mark noSFX as const + // in some way. + noInline( + "var y; var x = noSFX(y), z = noSFX(); noSFX(); noSFX(), print(x)"); + //inline( + // "var y; var x = noSFX(y), z = noSFX(); noSFX(); noSFX(), print(x)", + // "var y; var x, z = noSFX(); noSFX(); noSFX(), print(noSFX(y))"); + } + + public void testDependOnOuterScopeVariables() { + noInline("var x; function foo() { var y = x; x = 0; print(y) }"); + noInline("var x; function foo() { var y = x; x++; print(y) }"); + + // Sadly, we don't understand the data flow of outer scoped variables as + // it can be modified by code outside of this scope. We can't inline + // at all if the definition has dependence on such variable. + noInline("var x; function foo() { var y = x; print(y) }"); + } + + public void testInlineIfNameIsLeftSideOfAssign() { + inline("var x = 1; x = print(x) + 1", "var x; x = print(1) + 1"); + inline("var x = 1; L: x = x + 2", "var x; L: x = 1 + 2"); + inline("var x = 1; x = (x = x + 1)", "var x; x = (x = 1 + 1)"); + + noInline("var x = 1; x = (x = (x = 10) + x)"); + noInline("var x = 1; x = (f(x) + (x = 10) + x);"); + noInline("var x = 1; x=-1,foo(x)"); + noInline("var x = 1; x-=1,foo(x)"); + } + + public void testInlineArguments() { + testSame("function _func(x) { print(x) }"); + testSame("function _func(x,y) { if(y) { x = 1 }; print(x) }"); + + test("function f(x, y) { x = 1; print(x) }", + "function f(x, y) { print(1) }"); + + test("function f(x, y) { if (y) { x = 1; print(x) }}", + "function f(x, y) { if (y) { print(1) }}"); + } + + public void testInvalidInlineArguments1() { + testSame("function f(x, y) { x = 1; arguments[0] = 2; print(x) }"); + testSame("function f(x, y) { x = 1; var z = arguments;" + + "z[0] = 2; z[1] = 3; print(x)}"); + testSame("function g(a){a[0]=2} function f(x){x=1;g(arguments);print(x)}"); + } + + public void testInvalidInlineArguments2() { + testSame("function f(c) {var f = c; arguments[0] = this;" + + "f.apply(this, arguments); return this;}"); + } + + public void testForIn() { + noInline("var x; var y = {}; for(x in y){}"); + noInline("var x; var y = {}; var z; for(x in z = y){print(z)}"); + noInline("var x; var y = {}; var z; for(x in y){print(z)}"); + + } + + public void testNotOkToSkipCheckPathBetweenNodes() { + noInline("var x; for(x = 1; foo(x);) {}"); + noInline("var x; for(; x = 1;foo(x)) {}"); + } + + public void testIssue698() { + // Most of the flow algorithms operate on Vars. We want to make + // sure the algorithm bails out appropriately if it sees + // a var that it doesn't know about. + inline( + "var x = ''; " + + "unknown.length < 2 && (unknown='0' + unknown);" + + "x = x + unknown; " + + "unknown.length < 3 && (unknown='0' + unknown);" + + "x = x + unknown; " + + "return x;", + "var x; " + + "unknown.length < 2 && (unknown='0' + unknown);" + + "x = '' + unknown; " + + "unknown.length < 3 && (unknown='0' + unknown);" + + "x = x + unknown; " + + "return x;"); + } + + public void testIssue777() { + test( + "function f(cmd, ta) {" + + " var temp = cmd;" + + " var temp2 = temp >> 2;" + + " cmd = STACKTOP;" + + " for (var src = temp2, dest = cmd >> 2, stop = src + 37;" + + " src < stop;" + + " src++, dest++) {" + + " HEAP32[dest] = HEAP32[src];" + + " }" + + " temp = ta;" + + " temp2 = temp >> 2;" + + " ta = STACKTOP;" + + " STACKTOP += 8;" + + " HEAP32[ta >> 2] = HEAP32[temp2];" + + " HEAP32[ta + 4 >> 2] = HEAP32[temp2 + 1];" + + "}", + "function f(cmd, ta){" + + " var temp;" + + " var temp2 = cmd >> 2;" + + " cmd = STACKTOP;" + + " var src = temp2;" + + " var dest = cmd >> 2;" + + " var stop = src + 37;" + + " for(;src> 2;" + + " ta = STACKTOP;" + + " STACKTOP += 8;" + + " HEAP32[ta>>2] = HEAP32[temp2];" + + " HEAP32[ta+4>>2] = HEAP32[temp2+1];" + + "}"); + } + + public void testTransitiveDependencies1() { + test( + "function f(x) { var a = x; var b = a; x = 3; return b; }", + "function f(x) { var a; var b = x; x = 3; return b; }"); + } + + public void testTransitiveDependencies2() { + test( + "function f(x) { var a = x; var b = a; var c = b; x = 3; return c; }", + "function f(x) { var a ; var b = x; var c ; x = 3; return b; }"); + } + + public void testIssue794a() { + noInline( + "var x = 1; " + + "try { x += someFunction(); } catch (e) {}" + + "x += 1;" + + "try { x += someFunction(); } catch (e) {}" + + "return x;"); + } + + public void testIssue794b() { + noInline( + "var x = 1; " + + "try { x = x + someFunction(); } catch (e) {}" + + "x = x + 1;" + + "try { x = x + someFunction(); } catch (e) {}" + + "return x;"); + } + + private void noInline(String input) { + inline(input, input); + } + + private void inline(String input, String expected) { + test(EXTERN_FUNCTIONS, "function _func() {" + input + "}", + "function _func() {" + expected + "}", null, null); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionArgumentInjectorTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionArgumentInjectorTest.java new file mode 100644 index 0000000..82c9245 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionArgumentInjectorTest.java @@ -0,0 +1,493 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.collect.Sets; +import com.google.javascript.rhino.Node; +import junit.framework.TestCase; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * Inline function tests. + * @author johnlenz@google.com (John Lenz) + */ +public class FunctionArgumentInjectorTest extends TestCase { + + // TODO(johnlenz): Add unit tests for: + // inject + // getFunctionCallParameterMap + + private static final Set EMPTY_STRING_SET = Collections.emptySet(); + + public void testFindModifiedParameters1() { + assertEquals(Sets.newHashSet(), + FunctionArgumentInjector.findModifiedParameters( + parseFunction("function f(a){ return a==0; }"))); + } + + public void testFindModifiedParameters2() { + assertEquals(Sets.newHashSet(), + FunctionArgumentInjector.findModifiedParameters( + parseFunction("function f(a){ b=a }"))); + } + + public void testFindModifiedParameters3() { + assertEquals(Sets.newHashSet("a"), + FunctionArgumentInjector.findModifiedParameters( + parseFunction("function f(a){ a=0 }"))); + } + + public void testFindModifiedParameters4() { + assertEquals(Sets.newHashSet("a", "b"), + FunctionArgumentInjector.findModifiedParameters( + parseFunction("function f(a,b){ a=0;b=0 }"))); + } + + public void testFindModifiedParameters5() { + assertEquals(Sets.newHashSet("b"), + FunctionArgumentInjector.findModifiedParameters( + parseFunction("function f(a,b){ a; if (a) b=0 }"))); + } + + public void testFindModifiedParameters6() { + assertEquals(Sets.newHashSet("a", "b"), + FunctionArgumentInjector.findModifiedParameters( + parseFunction("function f(a,b){ function f(){ a;b; } }"))); + } + + public void testFindModifiedParameters7() { + assertEquals(Sets.newHashSet("b"), + FunctionArgumentInjector.findModifiedParameters( + parseFunction("function f(a,b){ a; function f(){ b; } }"))); + } + + public void testFindModifiedParameters8() { + assertEquals(Sets.newHashSet("b"), + FunctionArgumentInjector.findModifiedParameters( + parseFunction( + "function f(a,b){ "+ + "a; function f(){ function g() { b; } } }"))); + } + + public void testFindModifiedParameters9() { + assertEquals(Sets.newHashSet("a", "b"), + FunctionArgumentInjector.findModifiedParameters( + parseFunction("function f(a,b){ (function(){ a;b; }) }"))); + } + + public void testFindModifiedParameters10() { + assertEquals(Sets.newHashSet("b"), + FunctionArgumentInjector.findModifiedParameters( + parseFunction("function f(a,b){ a; (function (){ b; }) }"))); + } + + public void testFindModifiedParameters11() { + assertEquals(Sets.newHashSet("b"), + FunctionArgumentInjector.findModifiedParameters( + parseFunction( + "function f(a,b){ "+ + "a; (function(){ (function () { b; }) }) }"))); + } + + public void testMaybeAddTempsForCallArguments1() { + // Parameters with side-effects must be executed + // even if they aren't referenced. + testNeededTemps( + "function foo(a,b){}; foo(goo(),goo());", + "foo", + Sets.newHashSet("a", "b")); + } + + public void testMaybeAddTempsForCallArguments2() { + // Unreferenced parameters without side-effects + // can be ignored. + testNeededTemps( + "function foo(a,b){}; foo(1,2);", + "foo", + EMPTY_STRING_SET); + } + + public void testMaybeAddTempsForCallArguments3() { + // Referenced parameters without side-effects + // don't need temps. + testNeededTemps( + "function foo(a,b){a;b;}; foo(x,y);", + "foo", + EMPTY_STRING_SET); + } + + public void testMaybeAddTempsForCallArguments4() { + // Parameters referenced after side-effect must + // be assigned to temps. + testNeededTemps( + "function foo(a,b){a;goo();b;}; foo(x,y);", + "foo", + Sets.newHashSet("b")); + } + + public void testMaybeAddTempsForCallArguments5() { + // Parameters referenced after out-of-scope side-effect must + // be assigned to temps. + testNeededTemps( + "function foo(a,b){x = b; y = a;}; foo(x,y);", + "foo", + Sets.newHashSet("a")); + } + + public void testMaybeAddTempsForCallArguments6() { + // Parameter referenced after a out-of-scope side-effect must + // be assigned to a temp. + testNeededTemps( + "function foo(a){x++;a;}; foo(x);", + "foo", + Sets.newHashSet("a")); + } + + public void testMaybeAddTempsForCallArguments7() { + // No temp needed after local side-effects. + testNeededTemps( + "function foo(a){var c; c=0; a;}; foo(x);", + "foo", + EMPTY_STRING_SET); + } + + public void testMaybeAddTempsForCallArguments8() { + // Temp needed for side-effects to object using local name. + testNeededTemps( + "function foo(a){var c = {}; c.goo=0; a;}; foo(x);", + "foo", + Sets.newHashSet("a")); + } + + public void testMaybeAddTempsForCallArguments9() { + // Parameters referenced in a loop with side-effects must + // be assigned to temps. + testNeededTemps( + "function foo(a,b){while(true){a;goo();b;}}; foo(x,y);", + "foo", + Sets.newHashSet("a", "b")); + } + + public void testMaybeAddTempsForCallArguments10() { + // No temps for parameters referenced in a loop with no side-effects. + testNeededTemps( + "function foo(a,b){while(true){a;true;b;}}; foo(x,y);", + "foo", + EMPTY_STRING_SET); + } + + public void testMaybeAddTempsForCallArguments11() { + // Parameters referenced in a loop with side-effects must + // be assigned to temps. + testNeededTemps( + "function foo(a,b){do{a;b;}while(goo());}; foo(x,y);", + "foo", + Sets.newHashSet("a", "b")); + } + + public void testMaybeAddTempsForCallArguments12() { + // Parameters referenced in a loop with side-effects must + // be assigned to temps. + testNeededTemps( + "function foo(a,b){for(;;){a;b;goo();}}; foo(x,y);", + "foo", + Sets.newHashSet("a", "b")); + } + + public void testMaybeAddTempsForCallArguments13() { + // Parameters referenced in a inner loop without side-effects must + // be assigned to temps if the outer loop has side-effects. + testNeededTemps( + "function foo(a,b){for(;;){for(;;){a;b;}goo();}}; foo(x,y);", + "foo", + Sets.newHashSet("a", "b")); + } + + public void testMaybeAddTempsForCallArguments14() { + // Parameters referenced in a loop must + // be assigned to temps. + testNeededTemps( + "function foo(a,b){goo();for(;;){a;b;}}; foo(x,y);", + "foo", + Sets.newHashSet("a", "b")); + } + + public void testMaybeAddTempsForCallArguments20() { + // A long string referenced more than once should have a temp. + testNeededTemps( + "function foo(a){a;a;}; foo(\"blah blah\");", + "foo", + Sets.newHashSet("a")); + } + + public void testMaybeAddTempsForCallArguments21() { + // A short string referenced once should not have a temp. + testNeededTemps( + "function foo(a){a;a;}; foo(\"\");", + "foo", + EMPTY_STRING_SET); + } + + public void testMaybeAddTempsForCallArguments22() { + // A object literal not referenced. + testNeededTemps( + "function foo(a){}; foo({x:1});", + "foo", + EMPTY_STRING_SET); + // A object literal referenced, should have a temp. + testNeededTemps( + "function foo(a){a;}; foo({x:1});", + "foo", + Sets.newHashSet("a")); + // A object literal, referenced more than once, should have a temp. + testNeededTemps( + "function foo(a){a;a;}; foo({x:1});", + "foo", + Sets.newHashSet("a")); + } + + public void testMaybeAddTempsForCallArguments23() { + // A array literal, not referenced. + testNeededTemps( + "function foo(a){}; foo([1,2]);", + "foo", + EMPTY_STRING_SET); + // A array literal, referenced once, should have a temp. + testNeededTemps( + "function foo(a){a;}; foo([1,2]);", + "foo", + Sets.newHashSet("a")); + // A array literal, referenced more than once, should have a temp. + testNeededTemps( + "function foo(a){a;a;}; foo([1,2]);", + "foo", + Sets.newHashSet("a")); + } + + public void testMaybeAddTempsForCallArguments24() { + // A regex literal, not referenced. + testNeededTemps( + "function foo(a){}; foo(/mac/);", + "foo", + EMPTY_STRING_SET); + // A regex literal, referenced once, should have a temp. + testNeededTemps( + "function foo(a){a;}; foo(/mac/);", + "foo", + Sets.newHashSet("a")); + // A regex literal, referenced more than once, should have a temp. + testNeededTemps( + "function foo(a){a;a;}; foo(/mac/);", + "foo", + Sets.newHashSet("a")); + } + + public void testMaybeAddTempsForCallArguments25() { + // A side-effect-less constructor, not referenced. + testNeededTemps( + "function foo(a){}; foo(new Date());", + "foo", + EMPTY_STRING_SET); + // A side-effect-less constructor, referenced once, should have a temp. + testNeededTemps( + "function foo(a){a;}; foo(new Date());", + "foo", + Sets.newHashSet("a")); + // A side-effect-less constructor, referenced more than once, should have + // a temp. + testNeededTemps( + "function foo(a){a;a;}; foo(new Date());", + "foo", + Sets.newHashSet("a")); + } + + public void testMaybeAddTempsForCallArguments26() { + // A constructor, not referenced. + testNeededTemps( + "function foo(a){}; foo(new Bar());", + "foo", + Sets.newHashSet("a")); + // A constructor, referenced once, should have a temp. + testNeededTemps( + "function foo(a){a;}; foo(new Bar());", + "foo", + Sets.newHashSet("a")); + // A constructor, referenced more than once, should have a temp. + testNeededTemps( + "function foo(a){a;a;}; foo(new Bar());", + "foo", + Sets.newHashSet("a")); + } + + public void testMaybeAddTempsForCallArguments27() { + // Ensure the correct parameter is given a temp, when there is + // a this value in the call. + testNeededTemps( + "function foo(a,b,c){}; foo.call(this,1,goo(),2);", + "foo", + Sets.newHashSet("b")); + } + + public void testMaybeAddTempsForCallArguments28() { + // true/false are don't need temps + testNeededTemps( + "function foo(a){a;a;}; foo(true);", + "foo", + EMPTY_STRING_SET); + } + + public void testMaybeAddTempsForCallArguments29() { + // true/false are don't need temps + testNeededTemps( + "function foo(a){a;a;}; foo(false);", + "foo", + EMPTY_STRING_SET); + } + + public void testMaybeAddTempsForCallArguments30() { + // true/false are don't need temps + testNeededTemps( + "function foo(a){a;a;}; foo(!0);", + "foo", + EMPTY_STRING_SET); + } + + public void testMaybeAddTempsForCallArguments31() { + // true/false are don't need temps + testNeededTemps( + "function foo(a){a;a;}; foo(!1);", + "foo", + EMPTY_STRING_SET); + } + + public void testMaybeAddTempsForCallArguments32() { + // void 0 doesn't need a temp + testNeededTemps( + "function foo(a){a;a;}; foo(void 0);", + "foo", + EMPTY_STRING_SET); + } + + public void testMaybeAddTempsForCallArgumentsInLoops() { + // A mutable parameter referenced in loop needs a + // temporary. + testNeededTemps( + "function foo(a){for(;;)a;}; foo(new Bar());", + "foo", + Sets.newHashSet("a")); + + testNeededTemps( + "function foo(a){while(true)a;}; foo(new Bar());", + "foo", + Sets.newHashSet("a")); + + testNeededTemps( + "function foo(a){do{a;}while(true)}; foo(new Bar());", + "foo", + Sets.newHashSet("a")); + } + + private void testNeededTemps( + String code, String fnName, Set expectedTemps) { + Node n = parse(code); + Node fn = findFunction(n, fnName); + assertNotNull(fn); + Node call = findCall(n, fnName); + assertNotNull(call); + Map args = + FunctionArgumentInjector.getFunctionCallParameterMap( + fn, call, getNameSupplier()); + + Set actualTemps = Sets.newHashSet(); + FunctionArgumentInjector.maybeAddTempsForCallArguments( + fn, args, actualTemps, new ClosureCodingConvention()); + + assertEquals(expectedTemps, actualTemps); + } + + private static Supplier getNameSupplier() { + return new Supplier() { + int i = 0; + @Override + public String get() { + return String.valueOf(i++); + } + }; + } + + private static Node findCall(Node n, String name) { + if (n.isCall()) { + Node callee; + if (NodeUtil.isGet(n.getFirstChild())) { + callee = n.getFirstChild().getFirstChild(); + Node prop = callee.getNext(); + // Only "call" is support at this point. + Preconditions.checkArgument(prop.isString() && + prop.getString().equals("call")); + } else { + callee = n.getFirstChild(); + } + + if (callee.isName() + && callee.getString().equals(name)) { + return n; + } + } + + for (Node c : n.children()) { + Node result = findCall(c, name); + if (result != null) { + return result; + } + } + + return null; + } + + private static Node findFunction(Node n, String name) { + if (n.isFunction()) { + if (n.getFirstChild().getString().equals(name)) { + return n; + } + } + + for (Node c : n.children()) { + Node result = findFunction(c, name); + if (result != null) { + return result; + } + } + + return null; + } + + private static Node parseFunction(String js) { + return parse(js).getFirstChild(); + } + + private static Node parse(String js) { + Compiler compiler = new Compiler(); + Node n = compiler.parseTestCode(js); + assertEquals(0, compiler.getErrorCount()); + return n; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionInjectorTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionInjectorTest.java new file mode 100644 index 0000000..7a79d93 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionInjectorTest.java @@ -0,0 +1,1598 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage; +import com.google.javascript.jscomp.FunctionInjector.CanInlineResult; +import com.google.javascript.jscomp.FunctionInjector.InliningMode; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import junit.framework.TestCase; + +import java.util.List; +import java.util.Set; + +/** + * Inline function tests. + * @author johnlenz@google.com (John Lenz) + */ + +public class FunctionInjectorTest extends TestCase { + static final InliningMode INLINE_DIRECT = InliningMode.DIRECT; + static final InliningMode INLINE_BLOCK = InliningMode.BLOCK; + private boolean assumeStrictThis = false; + private boolean assumeMinimumCapture = false; + + @Override + protected void setUp() throws Exception { + super.setUp(); + assumeStrictThis = false; + } + + private FunctionInjector getInjector() { + Compiler compiler = new Compiler(); + return new FunctionInjector( + compiler, compiler.getUniqueNameIdSupplier(), true, + assumeStrictThis, assumeMinimumCapture); + } + + public void testIsSimpleFunction1() { + assertTrue(getInjector().isDirectCallNodeReplacementPossible( + prep("function f(){}"))); + } + + public void testIsSimpleFunction2() { + assertTrue(getInjector().isDirectCallNodeReplacementPossible( + prep("function f(){return 0;}"))); + } + + public void testIsSimpleFunction3() { + assertTrue(getInjector().isDirectCallNodeReplacementPossible( + prep("function f(){return x ? 0 : 1}"))); + } + + public void testIsSimpleFunction4() { + assertFalse(getInjector().isDirectCallNodeReplacementPossible( + prep("function f(){return;}"))); + } + + public void testIsSimpleFunction5() { + assertFalse(getInjector().isDirectCallNodeReplacementPossible( + prep("function f(){return 0; return 0;}"))); + } + + public void testIsSimpleFunction6() { + assertFalse(getInjector().isDirectCallNodeReplacementPossible( + prep("function f(){var x=true;return x ? 0 : 1}"))); + } + + public void testIsSimpleFunction7() { + assertFalse(getInjector().isDirectCallNodeReplacementPossible( + prep("function f(){if (x) return 0; else return 1}"))); + } + + public void testCanInlineReferenceToFunction1() { + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){}; foo();", "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction2() { + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){}; foo();", "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction3() { + // NOTE: FoldConstants will convert this to a empty function, + // so there is no need to explicitly support it. + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(){return;}; foo();", "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction4() { + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){return;}; foo();", "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction5() { + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){return true;}; foo();", "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction6() { + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){return true;}; foo();", "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction7() { + // In var initialization. + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){return true;}; var x=foo();", "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction8() { + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){return true;}; var x=foo();", "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction9() { + // In assignment. + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){return true;}; var x; x=foo();", "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction10() { + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){return true;}; var x; x=foo();", "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction11() { + // In expression. + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){return true;}; var x; x=x+foo();", "foo", + INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction12() { + // "foo" is not known to be side-effect free, it might change the value + // of "x", so it can't be inlined. + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(){return true;}; var x; x=x+foo();", "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction12b() { + // "foo" is not known to be side-effect free, it might change the value + // of "x", so it can't be inlined. + helperCanInlineReferenceToFunction( + CanInlineResult.AFTER_PREPARATION, + "function foo(){return true;}; var x; x=x+foo();", + "foo", INLINE_BLOCK, true); + } + +// TODO(nicksantos): Re-enable with side-effect detection. +// public void testCanInlineReferenceToFunction13() { +// // ... if foo is side-effect free we can inline here. +// helperCanInlineReferenceToFunction(true, +// "/** @nosideeffects */ function foo(){return true;};" + +// "var x; x=x+foo();", "foo", INLINE_BLOCK); +// } + + public void testCanInlineReferenceToFunction14() { + // Simple call with parameters + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(a){return true;}; foo(x);", "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction15() { + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(a){return true;}; foo(x);", "foo", INLINE_BLOCK); + } + + // TODO(johnlenz): remove this constant once this has been proven in + // production code. + static final CanInlineResult NEW_VARS_IN_GLOBAL_SCOPE = + CanInlineResult.YES; + + public void testCanInlineReferenceToFunction16() { + // Function "foo" as it contains "var b" which + // must be brought into the global scope. + helperCanInlineReferenceToFunction(NEW_VARS_IN_GLOBAL_SCOPE, + "function foo(a){var b;return a;}; foo(goo());", "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction17() { + // This doesn't bring names into the global name space. + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(a){return a;}; " + + "function x() { foo(goo()); }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction18() { + // Parameter has side-effects. + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return a;} foo(x++);", "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction19() { + // Parameter has mutable parameter referenced more than once. + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return a+a} foo([]);", "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction20() { + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return a+a} foo({});", "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction21() { + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return a+a} foo(new Date);", "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction22() { + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return a+a} foo(true && new Date);", "foo", + INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction23() { + // variables to global scope. + helperCanInlineReferenceToFunction(NEW_VARS_IN_GLOBAL_SCOPE, + "function foo(a){return a;}; foo(x++);", "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction24() { + // ... this is OK, because it doesn't introduce a new global name. + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(a){return a;}; " + + "function x() { foo(x++); }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction25() { + // Parameter has side-effects. + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return a+a;}; foo(x++);", "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction26() { + helperCanInlineReferenceToFunction(NEW_VARS_IN_GLOBAL_SCOPE, + "function foo(a){return a+a;}; foo(x++);", "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction27() { + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(a){return a+a;}; " + + "function x() { foo(x++); }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction28() { + // Parameter has side-effects. + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return true;}; foo(goo());", "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction29() { + helperCanInlineReferenceToFunction(NEW_VARS_IN_GLOBAL_SCOPE, + "function foo(a){return true;}; foo(goo());", "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction30() { + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(a){return true;}; " + + "function x() { foo(goo()); }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction31() { + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(a) {return true;}; " + + "function x() {foo.call(this, 1);}", + "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction32() { + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return true;}; " + + "function x() { foo.apply(this, [1]); }", + "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction33() { + // No special handling is required for method calls passing this. + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(a){return true;}; " + + "function x() { foo.bar(this, 1); }", + "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction34() { + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(a){return true;}; " + + "function x() { foo.call(this, goo()); }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction35() { + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return true;}; " + + "function x() { foo.apply(this, goo()); }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction36() { + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(a){return true;}; " + + "function x() { foo.bar(this, goo()); }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction37() { + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return true;}; " + + "function x() { foo.call(null, 1); }", + "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction38() { + assumeStrictThis = false; + + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return true;}; " + + "function x() { foo.call(null, goo()); }", + "foo", INLINE_BLOCK); + + assumeStrictThis = true; + + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(a){return true;}; " + + "function x() { foo.call(null, goo()); }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction39() { + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return true;}; " + + "function x() { foo.call(bar, 1); }", + "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction40() { + assumeStrictThis = false; + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return true;}; " + + "function x() { foo.call(bar, goo()); }", + "foo", INLINE_BLOCK); + + assumeStrictThis = true; + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(a){return true;}; " + + "function x() { foo.call(bar, goo()); }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction41() { + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return true;}; " + + "function x() { foo.call(new bar(), 1); }", + "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction42() { + assumeStrictThis = false; + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return true;}; " + + "function x() { foo.call(new bar(), goo()); }", + "foo", INLINE_BLOCK); + + assumeStrictThis = true; + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(a){return true;}; " + + "function x() { foo.call(new bar(), goo()); }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction43() { + // Handle the case of a missing 'this' value in a call. + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(){return true;}; " + + "function x() { foo.call(); }", + "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction44() { + assumeStrictThis = false; + // Handle the case of a missing 'this' value in a call. + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(){return true;}; " + + "function x() { foo.call(); }", + "foo", INLINE_BLOCK); + + assumeStrictThis = true; + // Handle the case of a missing 'this' value in a call. + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){return true;}; " + + "function x() { foo.call(); }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction45() { + // Call with inner function expression. + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){return function() {return true;}}; foo();", + "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction46() { + // Call with inner function expression. + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){return function() {return true;}}; foo();", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction47() { + // Call with inner function expression and variable decl. + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(){var a; return function() {return true;}}; foo();", + "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction48() { + // Call with inner function expression and variable decl. + // TODO(johnlenz): should we validate no values in scope? + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){var a; return function() {return true;}}; foo();", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction49() { + // Call with inner function expression. + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){return function() {var a; return true;}}; foo();", + "foo", INLINE_DIRECT); + } + + public void testCanInlineReferenceToFunction50() { + // Call with inner function expression. + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){return function() {var a; return true;}}; foo();", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunction51() { + // Call with inner function statement. + helperCanInlineReferenceToFunction(CanInlineResult.YES, + "function foo(){function x() {var a; return true;} return x}; foo();", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunctionInExpression1() { + // Call in if condition + helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, + "function foo(a){return true;}; " + + "function x() { if (foo(1)) throw 'test'; }", + "foo", INLINE_BLOCK, true); + } + + public void testCanInlineReferenceToFunctionInExpression2() { + // Call in return expression + helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, + "function foo(a){return true;}; " + + "function x() { return foo(1); }", + "foo", INLINE_BLOCK, true); + } + + public void testCanInlineReferenceToFunctionInExpression3() { + // Call in switch expression + helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, + "function foo(a){return true;}; " + + "function x() { switch(foo(1)) { default:break; } }", + "foo", INLINE_BLOCK, true); + } + + public void testCanInlineReferenceToFunctionInExpression4() { + // Call in hook condition + helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, + "function foo(a){return true;}; " + + "function x() {foo(1)?0:1 }", + "foo", INLINE_BLOCK, true); + } + + public void testCanInlineReferenceToFunctionInExpression5() { + // Call in hook side-effect free condition + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return true;}; " + + "function x() {true?foo(1):1 }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunctionInExpression5a() { + // Call in hook side-effect free condition + helperCanInlineReferenceToFunction( + CanInlineResult.AFTER_PREPARATION, + "function foo(a){return true;}; " + + "function x() {true?foo(1):1 }", + "foo", INLINE_BLOCK, true); + } + + public void testCanInlineReferenceToFunctionInExpression6() { + // Call in expression statement "condition" + helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, + "function foo(a){return true;}; " + + "function x() {foo(1) && 1 }", + "foo", INLINE_BLOCK, true); + } + + public void testCanInlineReferenceToFunctionInExpression7() { + // Call in expression statement after side-effect free "condition" + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return true;}; " + + "function x() {1 && foo(1) }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunctionInExpression7a() { + // Call in expression statement after side-effect free "condition" + helperCanInlineReferenceToFunction( + CanInlineResult.AFTER_PREPARATION, + "function foo(a){return true;}; " + + "function x() {1 && foo(1) }", + "foo", INLINE_BLOCK, true); + } + + public void testCanInlineReferenceToFunctionInExpression8() { + // Call in expression statement after side-effect free operator + helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, + "function foo(a){return true;}; " + + "function x() {1 + foo(1) }", + "foo", INLINE_BLOCK, true); + } + + public void testCanInlineReferenceToFunctionInExpression9() { + // Call in VAR expression. + helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, + "function foo(a){return true;}; " + + "function x() {var b = 1 + foo(1)}", + "foo", INLINE_BLOCK, true); + } + + public void testCanInlineReferenceToFunctionInExpression10() { + // Call in assignment expression. + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(a){return true;}; " + + "function x() {var b; b += 1 + foo(1) }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunctionInExpression10a() { + // Call in assignment expression. + helperCanInlineReferenceToFunction( + CanInlineResult.AFTER_PREPARATION, + "function foo(a){return true;}; " + + "function x() {var b; b += 1 + foo(1) }", + "foo", INLINE_BLOCK, true); + } + +// TODO(nicksantos): Re-enable with side-effect detection. +// public void testCanInlineReferenceToFunctionInExpression11() { +// helperCanInlineReferenceToFunction(true, +// "/** @nosideeffects */ function foo(a){return true;}; " + +// "function x() {var b; b += 1 + foo(1) }", +// "foo", INLINE_BLOCK); +// } + + public void testCanInlineReferenceToFunctionInExpression12() { + helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, + "function foo(a){return true;}; " + + "function x() {var a,b,c; a = b = c = foo(1) }", + "foo", INLINE_BLOCK, true); + } + + public void testCanInlineReferenceToFunctionInExpression13() { + helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, + "function foo(a){return true;}; " + + "function x() {var a,b,c; a = b = c = 1 + foo(1) }", + "foo", INLINE_BLOCK, true); + } + + public void testCanInlineReferenceToFunctionInExpression14() { + // ... foo can not be inlined because of possible changes to "c". + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "var a = {}, b = {}, c;" + + "a.test = 'a';" + + "b.test = 'b';" + + "c = a;" + + "function foo(){c = b; return 'foo'};" + + "c.test=foo();", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunctionInExpression14a() { + // ... foo can be inlined despite possible changes to "c". + helperCanInlineReferenceToFunction( + CanInlineResult.AFTER_PREPARATION, + "var a = {}, b = {}, c;" + + "a.test = 'a';" + + "b.test = 'b';" + + "c = a;" + + "function foo(){c = b; return 'foo'};" + + "c.test=foo();", + "foo", INLINE_BLOCK, true); + } + +// TODO(nicksantos): Re-enable with side-effect detection. +// public void testCanInlineReferenceToFunctionInExpression15() { +// // ... foo can be inlined as it is side-effect free. +// helperCanInlineReferenceToFunction(true, +// "var a = {}, b = {}, c;" + +// "a.test = 'a';" + +// "b.test = 'b';" + +// "c = a;" + +// "/** @nosideeffects */ function foo(){return 'foo'};" + +// "c.test=foo();", +// "foo", INLINE_BLOCK); +// } + +// public void testCanInlineReferenceToFunctionInExpression16() { +// // ... foo can not be inlined because of possible side-effects of x() +// helperCanInlineReferenceToFunction(false, +// "var a = {}, b = {}, c;" + +// "a.test = 'a';" + +// "b.test = 'b';" + +// "c = a;" + +// "function x(){return c};" + +// "/** @nosideeffects */ function foo(){return 'foo'};" + +// "x().test=foo();", +// "foo", INLINE_BLOCK); +// } + +// public void testCanInlineReferenceToFunctionInExpression17() { +// // ... foo can be inlined because of x() is side-effect free. +// helperCanInlineReferenceToFunction(true, +// "var a = {}, b = {}, c;" + +// "a.test = 'a';" + +// "b.test = 'b';" + +// "c = a;" + +// "/** @nosideeffects */ function x(){return c};" + +// "/** @nosideeffects */ function foo(){return 'foo'};" + +// "x().test=foo();", +// "foo", INLINE_BLOCK); +// } + + public void testCanInlineReferenceToFunctionInExpression18() { + // Call in within a call + helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, + "function foo(){return _g();}; " + + "function x() {1 + foo()() }", + "foo", INLINE_BLOCK, true); + } + + public void testCanInlineReferenceToFunctionInExpression19() { + // ... unless foo is known to be side-effect free, it might actually + // change the value of "_g" which would unfortunately change the behavior, + // so we can't inline here. + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(){return a;}; " + + "function x() {1 + _g(foo()) }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunctionInExpression19a() { + // ... unless foo is known to be side-effect free, it might actually + // change the value of "_g" which would unfortunately change the behavior, + // so we can't inline here. + helperCanInlineReferenceToFunction( + CanInlineResult.AFTER_PREPARATION, + "function foo(){return a;}; " + + "function x() {1 + _g(foo()) }", + "foo", INLINE_BLOCK, true); + } + +// TODO(nicksantos): Re-enable with side-effect detection. +// public void testCanInlineReferenceToFunctionInExpression20() { +// helperCanInlineReferenceToFunction(true, +// "/** @nosideeffects */ function foo(){return a;}; " + +// "function x() {1 + _g(foo()) }", +// "foo", INLINE_BLOCK); +// } + + public void testCanInlineReferenceToFunctionInExpression21() { + // Assignments to object are problematic if the call has side-effects, + // as the object that is being referred to can change. + // Note: This could be changed be inlined if we in some way make "z" + // as not escaping from the local scope. + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "var z = {};" + + "function foo(a){z = {};return true;}; " + + "function x() { z.gack = foo(1) }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunctionInExpression21a() { + // Assignments to object are problematic if the call has side-effects, + // as the object that is being referred to can change. + // Note: This could be changed be inlined if we in some way make "z" + // as not escaping from the local scope. + helperCanInlineReferenceToFunction( + CanInlineResult.AFTER_PREPARATION, + "var z = {};" + + "function foo(a){z = {};return true;}; " + + "function x() { z.gack = foo(1) }", + "foo", INLINE_BLOCK, true); + } + + public void testCanInlineReferenceToFunctionInExpression22() { + // ... foo() is after a side-effect + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(){return a;}; " + + "function x() {1 + _g(_a(), foo()) }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunctionInExpression22a() { + // ... foo() is after a side-effect + helperCanInlineReferenceToFunction( + CanInlineResult.AFTER_PREPARATION, + "function foo(){return a;}; " + + "function x() {1 + _g(_a(), foo()) }", + "foo", INLINE_BLOCK, true); + } + + public void testCanInlineReferenceToFunctionInExpression23() { + // ... foo() is after a side-effect + helperCanInlineReferenceToFunction(CanInlineResult.NO, + "function foo(){return a;}; " + + "function x() {1 + _g(_a(), foo.call(this)) }", + "foo", INLINE_BLOCK); + } + + public void testCanInlineReferenceToFunctionInExpression23a() { + // ... foo() is after a side-effect + helperCanInlineReferenceToFunction( + CanInlineResult.AFTER_PREPARATION, + "function foo(){return a;}; " + + "function x() {1 + _g(_a(), foo.call(this)) }", + "foo", INLINE_BLOCK, true); + } + + public void testCanInlineReferenceToFunctionInLoop1() { + helperCanInlineReferenceToFunction( + CanInlineResult.YES, + "function foo(){return a;}; " + + "while(1) { foo(); }", + "foo", INLINE_BLOCK, true); + } + + public void testCanInlineReferenceToFunctionInLoop2() { + // If function contains function, don't inline it into a loop. + // TODO(johnlenz): this can be improved by looking to see + // if the inner function contains any references to values defined + // in the outer function. + helperCanInlineReferenceToFunction( + CanInlineResult.NO, + "function foo(){return function() {};}; " + + "while(1) { foo(); }", + "foo", INLINE_BLOCK, true); + } + + public void testInline1() { + helperInlineReferenceToFunction( + "function foo(){}; foo();", + "function foo(){}; void 0", + "foo", INLINE_DIRECT); + } + + public void testInline2() { + helperInlineReferenceToFunction( + "function foo(){}; foo();", + "function foo(){}; {}", + "foo", INLINE_BLOCK); + } + + public void testInline3() { + helperInlineReferenceToFunction( + "function foo(){return;}; foo();", + "function foo(){return;}; {}", + "foo", INLINE_BLOCK); + } + + public void testInline4() { + helperInlineReferenceToFunction( + "function foo(){return true;}; foo();", + "function foo(){return true;}; true;", + "foo", INLINE_DIRECT); + } + + public void testInline5() { + helperInlineReferenceToFunction( + "function foo(){return true;}; foo();", + "function foo(){return true;}; {true;}", + "foo", INLINE_BLOCK); + } + + public void testInline6() { + // In var initialization. + helperInlineReferenceToFunction( + "function foo(){return true;}; var x=foo();", + "function foo(){return true;}; var x=true;", + "foo", INLINE_DIRECT); + } + + public void testInline7() { + helperInlineReferenceToFunction( + "function foo(){return true;}; var x=foo();", + "function foo(){return true;}; var x;" + + "{x=true}", + "foo", INLINE_BLOCK); + } + + public void testInline8() { + // In assignment. + helperInlineReferenceToFunction( + "function foo(){return true;}; var x; x=foo();", + "function foo(){return true;}; var x; x=true;", + "foo", INLINE_DIRECT); + } + + public void testInline9() { + helperInlineReferenceToFunction( + "function foo(){return true;}; var x; x=foo();", + "function foo(){return true;}; var x;{x=true}", + "foo", INLINE_BLOCK); + } + + public void testInline10() { + // In expression. + helperInlineReferenceToFunction( + "function foo(){return true;}; var x; x=x+foo();", + "function foo(){return true;}; var x; x=x+true;", + "foo", INLINE_DIRECT); + } + + public void testInline11() { + // Simple call with parameters + helperInlineReferenceToFunction( + "function foo(a){return true;}; foo(x);", + "function foo(a){return true;}; true;", + "foo", INLINE_DIRECT); + } + + public void testInline12() { + helperInlineReferenceToFunction( + "function foo(a){return true;}; foo(x);", + "function foo(a){return true;}; {true}", + "foo", INLINE_BLOCK); + } + + public void testInline13() { + // Parameter has side-effects. + helperInlineReferenceToFunction( + "function foo(a){return a;}; " + + "function x() { foo(x++); }", + "function foo(a){return a;}; " + + "function x() {{var a$$inline_0=x++;" + + "a$$inline_0}}", + "foo", INLINE_BLOCK); + } + + public void testInline14() { + // Parameter has side-effects. + helperInlineReferenceToFunction( + "function foo(a){return a+a;}; foo(x++);", + "function foo(a){return a+a;}; " + + "{var a$$inline_0=x++;" + + " a$$inline_0+" + + "a$$inline_0;}", + "foo", INLINE_BLOCK); + } + + public void testInline15() { + // Parameter has mutable, references more than once. + helperInlineReferenceToFunction( + "function foo(a){return a+a;}; foo(new Date());", + "function foo(a){return a+a;}; " + + "{var a$$inline_0=new Date();" + + " a$$inline_0+" + + "a$$inline_0;}", + "foo", INLINE_BLOCK); + } + + public void testInline16() { + // Parameter is large, references more than once. + helperInlineReferenceToFunction( + "function foo(a){return a+a;}; foo(function(){});", + "function foo(a){return a+a;}; " + + "{var a$$inline_0=function(){};" + + " a$$inline_0+" + + "a$$inline_0;}", + "foo", INLINE_BLOCK); + } + + public void testInline17() { + // Parameter has side-effects. + helperInlineReferenceToFunction( + "function foo(a){return true;}; foo(goo());", + "function foo(a){return true;};" + + "{var a$$inline_0=goo();true}", + "foo", INLINE_BLOCK); + } + + public void testInline18() { + // This doesn't bring names into the global name space. + helperInlineReferenceToFunction( + "function foo(a){var b;return a;}; " + + "function x() { foo(goo()); }", + "function foo(a){var b;return a;}; " + + "function x() {{var a$$inline_0=goo();" + + "var b$$inline_1;a$$inline_0}}", + "foo", INLINE_BLOCK); + } + + public void testInline19() { + // Properly alias. + helperInlineReferenceToFunction( + "var x = 1; var y = 2;" + + "function foo(a,b){x = b; y = a;}; " + + "function bar() { foo(x,y); }", + "var x = 1; var y = 2;" + + "function foo(a,b){x = b; y = a;}; " + + "function bar() {" + + "{var a$$inline_0=x;" + + "x = y;" + + "y = a$$inline_0;}" + + "}", + "foo", INLINE_BLOCK); + } + + public void testInline19b() { + helperInlineReferenceToFunction( + "var x = 1; var y = 2;" + + "function foo(a,b){y = a; x = b;}; " + + "function bar() { foo(x,y); }", + "var x = 1; var y = 2;" + + "function foo(a,b){y = a; x = b;}; " + + "function bar() {" + + "{var b$$inline_1=y;" + + "y = x;" + + "x = b$$inline_1;}" + + "}", + "foo", INLINE_BLOCK); + } + + public void testInlineIntoLoop() { + helperInlineReferenceToFunction( + "function foo(a){var b;return a;}; " + + "for(;1;){ foo(1); }", + "function foo(a){var b;return a;}; " + + "for(;1;){ {" + + "var b$$inline_1=void 0;1}}", + "foo", INLINE_BLOCK); + + helperInlineReferenceToFunction( + "function foo(a){var b;return a;}; " + + "do{ foo(1); } while(1)", + "function foo(a){var b;return a;}; " + + "do{ {" + + "var b$$inline_1=void 0;1}}while(1)", + "foo", INLINE_BLOCK); + + helperInlineReferenceToFunction( + "function foo(a){for(var b in c)return a;}; " + + "for(;1;){ foo(1); }", + "function foo(a){var b;for(b in c)return a;}; " + + "for(;1;){ {JSCompiler_inline_label_foo_2:{" + + "var b$$inline_1=void 0;for(b$$inline_1 in c){" + + "1;break JSCompiler_inline_label_foo_2" + + "}}}}", + "foo", INLINE_BLOCK); + } + + public void testInlineFunctionWithInnerFunction1() { + // Call with inner function expression. + helperInlineReferenceToFunction( + "function foo(){return function() {return true;}}; foo();", + "function foo(){return function() {return true;}};" + + "(function() {return true;})", + "foo", INLINE_DIRECT); + } + + public void testInlineFunctionWithInnerFunction2() { + // Call with inner function expression. + helperInlineReferenceToFunction( + "function foo(){return function() {return true;}}; foo();", + "function foo(){return function() {return true;}};" + + "{(function() {return true;})}", + "foo", INLINE_BLOCK); + } + + public void testInlineFunctionWithInnerFunction3() { + // Call with inner function expression. + helperInlineReferenceToFunction( + "function foo(){return function() {var a; return true;}}; foo();", + "function foo(){return function() {var a; return true;}};" + + "(function() {var a; return true;});", + "foo", INLINE_DIRECT); + } + + public void testInlineFunctionWithInnerFunction4() { + // Call with inner function expression. + helperInlineReferenceToFunction( + "function foo(){return function() {var a; return true;}}; foo();", + "function foo(){return function() {var a; return true;}};" + + "{(function() {var a$$inline_0; return true;});}", + "foo", INLINE_BLOCK); + } + + public void testInlineFunctionWithInnerFunction5() { + // Call with inner function statement. + helperInlineReferenceToFunction( + "function foo(){function x() {var a; return true;} return x}; foo();", + "function foo(){function x(){var a;return true}return x};" + + "{var x$$inline_0 = function(){" + + "var a$$inline_1;return true};x$$inline_0}", + "foo", INLINE_BLOCK); + } + + public void testInlineReferenceInExpression1() { + // Call in if condition + helperInlineReferenceToFunction( + "function foo(a){return true;}; " + + "function x() { if (foo(1)) throw 'test'; }", + "function foo(a){return true;}; " + + "function x() { var JSCompiler_inline_result$$0; " + + "{JSCompiler_inline_result$$0=true;}" + + "if (JSCompiler_inline_result$$0) throw 'test'; }", + "foo", INLINE_BLOCK, true); + } + + public void testInlineReferenceInExpression2() { + // Call in return expression + helperInlineReferenceToFunction( + "function foo(a){return true;}; " + + "function x() { return foo(1); }", + "function foo(a){return true;}; " + + "function x() { var JSCompiler_inline_result$$0; " + + "{JSCompiler_inline_result$$0=true;}" + + "return JSCompiler_inline_result$$0; }", + "foo", INLINE_BLOCK, true); + } + + public void testInlineReferenceInExpression3() { + // Call in switch expression + helperInlineReferenceToFunction( + "function foo(a){return true;}; " + + "function x() { switch(foo(1)) { default:break; } }", + "function foo(a){return true;}; " + + "function x() { var JSCompiler_inline_result$$0; " + + "{JSCompiler_inline_result$$0=true;}" + + "switch(JSCompiler_inline_result$$0) { default:break; } }", + "foo", INLINE_BLOCK, true); + } + + public void testInlineReferenceInExpression4() { + // Call in hook condition + helperInlineReferenceToFunction( + "function foo(a){return true;}; " + + "function x() {foo(1)?0:1 }", + "function foo(a){return true;}; " + + "function x() { var JSCompiler_inline_result$$0; " + + "{JSCompiler_inline_result$$0=true;}" + + "JSCompiler_inline_result$$0?0:1 }", + "foo", INLINE_BLOCK, true); + } + + public void testInlineReferenceInExpression5() { + // Call in expression statement "condition" + helperInlineReferenceToFunction( + "function foo(a){return true;}; " + + "function x() {foo(1)&&1 }", + "function foo(a){return true;}; " + + "function x() { var JSCompiler_inline_result$$0; " + + "{JSCompiler_inline_result$$0=true;}" + + "JSCompiler_inline_result$$0&&1 }", + "foo", INLINE_BLOCK, true); + } + + public void testInlineReferenceInExpression6() { + // Call in expression statement after side-effect free "condition" + helperInlineReferenceToFunction( + "function foo(a){return true;}; " + + "function x() {1 + foo(1) }", + "function foo(a){return true;}; " + + "function x() { var JSCompiler_inline_result$$0; " + + "{JSCompiler_inline_result$$0=true;}" + + "1 + JSCompiler_inline_result$$0 }", + "foo", INLINE_BLOCK, true); + } + + public void testInlineReferenceInExpression7() { + // Call in expression statement "condition" + helperInlineReferenceToFunction( + "function foo(a){return true;}; " + + "function x() {foo(1) && 1 }", + "function foo(a){return true;}; " + + "function x() { var JSCompiler_inline_result$$0; " + + "{JSCompiler_inline_result$$0=true;}" + + "JSCompiler_inline_result$$0&&1 }", + "foo", INLINE_BLOCK, true); + } + + public void testInlineReferenceInExpression8() { + // Call in expression statement after side-effect free operator + helperInlineReferenceToFunction( + "function foo(a){return true;}; " + + "function x() {1 + foo(1) }", + "function foo(a){return true;}; " + + "function x() { var JSCompiler_inline_result$$0;" + + "{JSCompiler_inline_result$$0=true;}" + + "1 + JSCompiler_inline_result$$0 }", + "foo", INLINE_BLOCK, true); + } + + public void testInlineReferenceInExpression9() { + // Call in VAR expression. + helperInlineReferenceToFunction( + "function foo(a){return true;}; " + + "function x() {var b = 1 + foo(1)}", + "function foo(a){return true;}; " + + "function x() { " + + "var JSCompiler_inline_result$$0;" + + "{JSCompiler_inline_result$$0=true;}" + + "var b = 1 + JSCompiler_inline_result$$0 " + + "}", + "foo", INLINE_BLOCK, true); + } + +// TODO(nicksantos): Re-enable with side-effect detection. +// public void testInlineReferenceInExpression10() { +// // Call in assignment expression. +// helperInlineReferenceToFunction( +// "/** @nosideeffects */ function foo(a){return true;}; " + +// "function x() {var b; b += 1 + foo(1) }", +// "function foo(a){return true;}; " + +// "function x() {var b;" + +// "{var JSCompiler_inline_result$$0; " + +// "JSCompiler_inline_result$$0=true;}" + +// "b += 1 + JSCompiler_inline_result$$0 }", +// "foo", INLINE_BLOCK); +// } + + public void testInlineReferenceInExpression11() { + // Call under label + helperInlineReferenceToFunction( + "function foo(a){return true;}; " + + "function x() {a:foo(1)?0:1 }", + "function foo(a){return true;}; " + + "function x() {" + + " a:{" + + " var JSCompiler_inline_result$$0; " + + " {JSCompiler_inline_result$$0=true;}" + + " JSCompiler_inline_result$$0?0:1 " + + " }" + + "}", + "foo", INLINE_BLOCK, true); + } + + public void testInlineReferenceInExpression12() { + helperInlineReferenceToFunction( + "function foo(a){return true;}" + + "function x() { 1?foo(1):1; }", + "function foo(a){return true}" + + "function x() {" + + " if(1) {" + + " {true;}" + + " } else {" + + " 1;" + + " }" + + "}", + "foo", INLINE_BLOCK, true); + } + + public void testInlineReferenceInExpression13() { + helperInlineReferenceToFunction( + "function foo(a){return true;}; " + + "function x() { goo() + (1?foo(1):1) }", + "function foo(a){return true;}; " + + "function x() { var JSCompiler_temp_const$$0=goo();" + + "var JSCompiler_temp$$1;" + + "if(1) {" + + " {JSCompiler_temp$$1=true;} " + + "} else {" + + " JSCompiler_temp$$1=1;" + + "}" + + "JSCompiler_temp_const$$0 + JSCompiler_temp$$1" + + "}", + "foo", INLINE_BLOCK, true); + } + + public void testInlineReferenceInExpression14() { + helperInlineReferenceToFunction( + "var z = {};" + + "function foo(a){z = {};return true;}; " + + "function x() { z.gack = foo(1) }", + + "var z = {};" + + "function foo(a){z = {};return true;}; " + + "function x() {" + + "var JSCompiler_temp_const$$0=z;" + + "var JSCompiler_inline_result$$1;" + + "{" + + "z= {};" + + "JSCompiler_inline_result$$1 = true;" + + "}" + + "JSCompiler_temp_const$$0.gack = JSCompiler_inline_result$$1;" + + "}", + "foo", INLINE_BLOCK, true); + } + + public void testInlineReferenceInExpression15() { + helperInlineReferenceToFunction( + "var z = {};" + + "function foo(a){z = {};return true;}; " + + "function x() { z.gack = foo.call(this,1) }", + + "var z = {};" + + "function foo(a){z = {};return true;}; " + + "function x() {" + + "var JSCompiler_temp_const$$0=z;" + + "var JSCompiler_inline_result$$1;" + + "{" + + "z= {};" + + "JSCompiler_inline_result$$1 = true;" + + "}" + + "JSCompiler_temp_const$$0.gack = JSCompiler_inline_result$$1;" + + "}", + "foo", INLINE_BLOCK, true); + } + + public void testInlineReferenceInExpression16() { + helperInlineReferenceToFunction( + "var z = {};" + + "function foo(a){z = {};return true;}; " + + "function x() { z[bar()] = foo(1) }", + + "var z = {};" + + "function foo(a){z = {};return true;}; " + + "function x() {" + + "var JSCompiler_temp_const$$1=z;" + + "var JSCompiler_temp_const$$0=bar();" + + "var JSCompiler_inline_result$$2;" + + "{" + + "z= {};" + + "JSCompiler_inline_result$$2 = true;" + + "}" + + "JSCompiler_temp_const$$1[JSCompiler_temp_const$$0] = " + + "JSCompiler_inline_result$$2;" + + "}", + "foo", INLINE_BLOCK, true); + } + + public void testInlineReferenceInExpression17() { + helperInlineReferenceToFunction( + "var z = {};" + + "function foo(a){z = {};return true;}; " + + "function x() { z.y.x.gack = foo(1) }", + + "var z = {};" + + "function foo(a){z = {};return true;}; " + + "function x() {" + + "var JSCompiler_temp_const$$0=z.y.x;" + + "var JSCompiler_inline_result$$1;" + + "{" + + "z= {};" + + "JSCompiler_inline_result$$1 = true;" + + "}" + + "JSCompiler_temp_const$$0.gack = JSCompiler_inline_result$$1;" + + "}", + "foo", INLINE_BLOCK, true); + } + + + public void testInlineWithinCalls1() { + // Call in within a call + helperInlineReferenceToFunction( + "function foo(){return _g;}; " + + "function x() {1 + foo()() }", + "function foo(){return _g;}; " + + "function x() { var JSCompiler_inline_result$$0;" + + "{JSCompiler_inline_result$$0=_g;}" + + "1 + JSCompiler_inline_result$$0() }", + "foo", INLINE_BLOCK, true); + } + +// TODO(nicksantos): Re-enable with side-effect detection. +// public void testInlineWithinCalls2() { +// helperInlineReferenceToFunction( +// "/** @nosideeffects */ function foo(){return true;}; " + +// "function x() {1 + _g(foo()) }", +// "function foo(){return true;}; " + +// "function x() { {var JSCompiler_inline_result$$0; " + +// "JSCompiler_inline_result$$0=true;}" + +// "1 + _g(JSCompiler_inline_result$$0) }", +// "foo", INLINE_BLOCK, true); +// } + + public void testInlineAssignmentToConstant() { + // Call in within a call + helperInlineReferenceToFunction( + "function foo(){return _g;}; " + + "function x(){var CONSTANT_RESULT = foo(); }", + + "function foo(){return _g;}; " + + "function x() {" + + " var JSCompiler_inline_result$$0;" + + " {JSCompiler_inline_result$$0=_g;}" + + " var CONSTANT_RESULT = JSCompiler_inline_result$$0;" + + "}", + "foo", INLINE_BLOCK, true); + } + + public void testBug1897706() { + helperInlineReferenceToFunction( + "function foo(a){}; foo(x())", + "function foo(a){}; {var a$$inline_0=x()}", + "foo", INLINE_BLOCK); + + helperInlineReferenceToFunction( + "function foo(a){bar()}; foo(x())", + "function foo(a){bar()}; {var a$$inline_0=x();bar()}", + "foo", INLINE_BLOCK); + + helperInlineReferenceToFunction( + "function foo(a,b){bar()}; foo(x(),y())", + "function foo(a,b){bar()};" + + "{var a$$inline_0=x();var b$$inline_1=y();bar()}", + "foo", INLINE_BLOCK); + } + + /** + * Test case + * + * var a = {}, b = {} + * a.test = "a", b.test = "b" + * c = a; + * foo() { c=b; return "a" } + * c.teste + * + */ + + public void helperCanInlineReferenceToFunction( + final CanInlineResult expectedResult, + final String code, + final String fnName, + final InliningMode mode) { + helperCanInlineReferenceToFunction( + expectedResult, code, fnName, mode, false); + } + + public void helperCanInlineReferenceToFunction( + final CanInlineResult expectedResult, + final String code, + final String fnName, + final InliningMode mode, + boolean allowDecomposition) { + final Compiler compiler = new Compiler(); + final FunctionInjector injector = new FunctionInjector( + compiler, compiler.getUniqueNameIdSupplier(), allowDecomposition, + assumeStrictThis, + assumeMinimumCapture); + final Node tree = parse(compiler, code); + + Node externsRoot = new Node(Token.EMPTY); + Node mainRoot = tree; + + final Node fnNode = findFunction(tree, fnName); + final Set unsafe = + FunctionArgumentInjector.findModifiedParameters(fnNode); + + // can-inline tester + Method tester = new Method() { + @Override + public boolean call(NodeTraversal t, Node n, Node parent) { + CanInlineResult result = injector.canInlineReferenceToFunction( + t, n, fnNode, unsafe, mode, + NodeUtil.referencesThis(fnNode), + NodeUtil.containsFunction(NodeUtil.getFunctionBody(fnNode))); + assertEquals(expectedResult, result); + return true; + } + }; + + compiler.resetUniqueNameId(); + TestCallback test = new TestCallback(fnName, tester); + NodeTraversal.traverse(compiler, tree, test); + } + + public void helperInlineReferenceToFunction( + String code, final String expectedResult, + final String fnName, final InliningMode mode) { + helperInlineReferenceToFunction( + code, expectedResult, fnName, mode, false); + } + + private void validateSourceInfo(Compiler compiler, Node subtree) { + (new LineNumberCheck(compiler)).setCheckSubTree(subtree); + // Source information problems are reported as compiler errors. + if (compiler.getErrorCount() != 0) { + String msg = "Error encountered: "; + for (JSError err : compiler.getErrors()) { + msg += err.toString() + "\n"; + } + assertTrue(msg, compiler.getErrorCount() == 0); + } + } + + public void helperInlineReferenceToFunction( + String code, final String expectedResult, + final String fnName, final InliningMode mode, + final boolean decompose) { + final Compiler compiler = new Compiler(); + final FunctionInjector injector = new FunctionInjector( + compiler, compiler.getUniqueNameIdSupplier(), decompose, + assumeStrictThis, + assumeMinimumCapture); + + List externsInputs = Lists.newArrayList( + SourceFile.fromCode("externs", "")); + + CompilerOptions options = new CompilerOptions(); + options.setCodingConvention(new GoogleCodingConvention()); + compiler.init(externsInputs, Lists.newArrayList( + SourceFile.fromCode("code", code)), options); + Node parseRoot = compiler.parseInputs(); + Node externsRoot = parseRoot.getFirstChild(); + final Node tree = parseRoot.getLastChild(); + assertNotNull(tree); + assertTrue(tree != externsRoot); + + final Node expectedRoot = parseExpected(new Compiler(), expectedResult); + + Node mainRoot = tree; + MarkNoSideEffectCalls mark = new MarkNoSideEffectCalls(compiler); + mark.process(externsRoot, mainRoot); + + Normalize normalize = new Normalize(compiler, false); + normalize.process(externsRoot, mainRoot); + compiler.setLifeCycleStage(LifeCycleStage.NORMALIZED); + + final Node fnNode = findFunction(tree, fnName); + assertNotNull(fnNode); + final Set unsafe = + FunctionArgumentInjector.findModifiedParameters(fnNode); + assertNotNull(fnNode); + + // inline tester + Method tester = new Method() { + @Override + public boolean call(NodeTraversal t, Node n, Node parent) { + + CanInlineResult canInline = injector.canInlineReferenceToFunction( + t, n, fnNode, unsafe, mode, + NodeUtil.referencesThis(fnNode), + NodeUtil.containsFunction(NodeUtil.getFunctionBody(fnNode))); + assertTrue("canInlineReferenceToFunction should not be CAN_NOT_INLINE", + CanInlineResult.NO != canInline); + if (decompose) { + assertTrue("canInlineReferenceToFunction " + + "should be CAN_INLINE_AFTER_DECOMPOSITION", + CanInlineResult.AFTER_PREPARATION == canInline); + + Set knownConstants = Sets.newHashSet(); + ExpressionDecomposer decomposer = new ExpressionDecomposer( + compiler, compiler.getUniqueNameIdSupplier(), knownConstants); + injector.setKnownConstants(knownConstants); + injector.maybePrepareCall(n); + + assertTrue("canInlineReferenceToFunction " + + "should be CAN_INLINE", + CanInlineResult.YES != canInline); + } + + Node result = injector.inline( + t, n, fnName, fnNode, mode); + validateSourceInfo(compiler, result); + String explanation = expectedRoot.checkTreeEquals(tree.getFirstChild()); + assertNull("\nExpected: " + toSource(expectedRoot) + + "\nResult: " + toSource(tree.getFirstChild()) + + "\n" + explanation, explanation); + return true; + } + }; + + compiler.resetUniqueNameId(); + TestCallback test = new TestCallback(fnName, tester); + NodeTraversal.traverse(compiler, tree, test); + } + + interface Method { + boolean call(NodeTraversal t, Node n, Node parent); + } + + class TestCallback implements Callback { + + private final String callname; + private final Method method; + private boolean complete = false; + + TestCallback(String callname, Method method) { + this.callname = callname; + this.method = method; + } + + @Override + public boolean shouldTraverse( + NodeTraversal nodeTraversal, Node n, Node parent) { + return !complete; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isCall()) { + Node callee; + if (NodeUtil.isGet(n.getFirstChild())) { + callee = n.getFirstChild().getFirstChild(); + } else { + callee = n.getFirstChild(); + } + + if (callee.isName() && + callee.getString().equals(callname)) { + complete = method.call(t, n, parent); + } + } + + if (parent == null) { + assertTrue(complete); + } + } + } + + private static Node findFunction(Node n, String name) { + if (n.isFunction()) { + if (n.getFirstChild().getString().equals(name)) { + return n; + } + } + + for (Node c : n.children()) { + Node result = findFunction(c, name); + if (result != null) { + return result; + } + } + + return null; + } + + private static Node prep(String js) { + Compiler compiler = new Compiler(); + Node n = compiler.parseTestCode(js); + assertEquals(0, compiler.getErrorCount()); + return n.getFirstChild(); + } + + private static Node parse(Compiler compiler, String js) { + Node n = compiler.parseTestCode(js); + assertEquals(0, compiler.getErrorCount()); + return n; + } + + private static Node parseExpected(Compiler compiler, String js) { + Node n = compiler.parseTestCode(js); + String message = "Unexpected errors: "; + JSError[] errs = compiler.getErrors(); + for (int i = 0; i < errs.length; i++){ + message += "\n" + errs[i].toString(); + } + assertEquals(message, 0, compiler.getErrorCount()); + return n; + } + + private static String toSource(Node n) { + return new CodePrinter.Builder(n) + .setPrettyPrint(false) + .setLineBreak(false) + .setSourceMap(null) + .build(); + } + +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionNamesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionNamesTest.java new file mode 100644 index 0000000..a49c1dc --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionNamesTest.java @@ -0,0 +1,94 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import com.google.common.collect.Maps; +import com.google.javascript.rhino.Node; + +import java.util.Map; + +/** + * Tests for {@link FunctionNames} + * + */ +public class FunctionNamesTest extends CompilerTestCase { + private FunctionNames functionNames; + + public FunctionNamesTest() { + this.functionNames = null; + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + functionNames = new FunctionNames(compiler); + return functionNames; + } + + public void testFunctionsNamesAndIds() { + final String jsSource = + "goog.widget = function(str) {\n" + + " this.member_fn = function() {};\n" + + " local_fn = function() {};\n" + + " (function(a){})(1);\n" + + "}\n" + + "function foo() {\n" + + " function bar() {}\n" + + "}\n" + + "literal = {f1 : function(){}, f2 : function(){}};\n" + + "goog.array.map(arr, function named(){});\n" + + "goog.array.map(arr, function(){});\n" + + "named_twice = function quax(){};\n" + + "recliteral = {l1 : {l2 : function(){}}};\n" + + "namedliteral = {n1 : function litnamed(){}};\n" + + "namedrecliteral = {n1 : {n2 : function reclitnamed(){}}};\n" + + "numliteral = {1 : function(){}};\n" + + "recnumliteral = {1 : {a : function(){}}};\n"; + + testSame(jsSource); + + final Map idNameMap = Maps.newLinkedHashMap(); + int count = 0; + for (Node f : functionNames.getFunctionNodeList()) { + int id = functionNames.getFunctionId(f); + String name = functionNames.getFunctionName(f); + idNameMap.put(id, name); + count++; + } + + assertEquals("Unexpected number of functions", 16, count); + + final Map expectedMap = Maps.newLinkedHashMap(); + + expectedMap.put(0, "goog.widget.member_fn"); + expectedMap.put(1, "goog.widget::local_fn"); + expectedMap.put(2, "goog.widget::"); + expectedMap.put(3, "goog.widget"); + expectedMap.put(4, "foo::bar"); + expectedMap.put(5, "foo"); + expectedMap.put(6, "literal.f1"); + expectedMap.put(7, "literal.f2"); + expectedMap.put(8, "named"); + expectedMap.put(9, ""); + expectedMap.put(10, "quax"); + expectedMap.put(11, "recliteral.l1.l2"); + expectedMap.put(12, "litnamed"); + expectedMap.put(13, "reclitnamed"); + expectedMap.put(14, "numliteral.__2"); + expectedMap.put(15, "recnumliteral.__3.a"); + assertEquals("Function id/name mismatch", + expectedMap, idNameMap); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionRewriterTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionRewriterTest.java new file mode 100644 index 0000000..77a0b2a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionRewriterTest.java @@ -0,0 +1,205 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Tests for {@link FunctionRewriter} + * + */ +public class FunctionRewriterTest extends CompilerTestCase { + + private static final String RETURNARG_HELPER = + "function JSCompiler_returnArg(JSCompiler_returnArg_value){" + + " return function() { return JSCompiler_returnArg_value }" + + "}"; + private static final String GET_HELPER = + "function JSCompiler_get(JSCompiler_get_name){" + + " return function() { return this[JSCompiler_get_name] }" + + "}"; + private static final String SET_HELPER = + "function JSCompiler_set(JSCompiler_set_name) {" + + " return function(JSCompiler_set_value){" + + " this[JSCompiler_set_name]=JSCompiler_set_value" + + " }" + + "}"; + private static final String EMPTY_HELPER = + "function JSCompiler_emptyFn() {" + + " return function(){}" + + "}"; + private static final String IDENTITY_HELPER = + "function JSCompiler_identityFn() {" + + " return function(JSCompiler_identityFn_value) {" + + " return JSCompiler_identityFn_value" + + " }" + + "}"; + + @Override + protected void setUp() { + super.enableLineNumberCheck(false); + } + + @Override + protected FunctionRewriter getProcessor(Compiler compiler) { + return new FunctionRewriter(compiler); + } + + @Override + protected int getNumRepetitions() { + // Pass reaches steady state after just 1 iteration + return 1; + } + + public void testReplaceReturnConst1() { + String source = "a.prototype.foo = function() {return \"foobar\"}"; + checkCompilesToSame(source, 3); + checkCompilesTo(source, + RETURNARG_HELPER, + "a.prototype.foo = JSCompiler_returnArg(\"foobar\")", + 4); + } + + public void testReplaceReturnConst2() { + checkCompilesToSame("a.prototype.foo = function() {return foobar}", 10); + } + + public void testReplaceReturnConst3() { + String source = "a.prototype.foo = function() {return void 0;}"; + checkCompilesToSame(source, 3); + checkCompilesTo(source, + RETURNARG_HELPER, + "a.prototype.foo = JSCompiler_returnArg(void 0)", + 4); + } + + public void testReplaceGetter1() { + String source = "a.prototype.foo = function() {return this.foo_}"; + checkCompilesToSame(source, 3); + checkCompilesTo(source, + GET_HELPER, + "a.prototype.foo = JSCompiler_get(\"foo_\")", + 4); + } + + public void testReplaceGetter2() { + checkCompilesToSame("a.prototype.foo = function() {return}", 10); + } + + public void testReplaceSetter1() { + String source = "a.prototype.foo = function(v) {this.foo_ = v}"; + checkCompilesToSame(source, 4); + checkCompilesTo(source, + SET_HELPER, + "a.prototype.foo = JSCompiler_set(\"foo_\")", + 5); + } + + public void testReplaceSetter2() { + String source = "a.prototype.foo = function(v, v2) {this.foo_ = v}"; + checkCompilesToSame(source, 3); + checkCompilesTo(source, + SET_HELPER, + "a.prototype.foo = JSCompiler_set(\"foo_\")", + 4); + } + + public void testReplaceSetter3() { + checkCompilesToSame("a.prototype.foo = function() {this.foo_ = v}", 10); + } + + public void testReplaceSetter4() { + checkCompilesToSame( + "a.prototype.foo = function(v, v2) {this.foo_ = v2}", 10); + } + + public void testReplaceEmptyFunction1() { + String source = "a.prototype.foo = function() {}"; + checkCompilesToSame(source, 4); + checkCompilesTo(source, + EMPTY_HELPER, + "a.prototype.foo = JSCompiler_emptyFn()", + 5); + } + + public void testReplaceEmptyFunction2() { + checkCompilesToSame("function foo() {}", 10); + } + + public void testReplaceEmptyFunction3() { + String source = "var foo = function() {}"; + checkCompilesToSame(source, 4); + checkCompilesTo(source, + EMPTY_HELPER, + "var foo = JSCompiler_emptyFn()", + 5); + } + + + + public void testReplaceIdentityFunction1() { + String source = "a.prototype.foo = function(a) {return a}"; + checkCompilesToSame(source, 2); + checkCompilesTo(source, + IDENTITY_HELPER, + "a.prototype.foo = JSCompiler_identityFn()", + 3); + } + + public void testReplaceIdentityFunction2() { + checkCompilesToSame("a.prototype.foo = function(a) {return a + 1}", 10); + } + + public void testIssue538() { + checkCompilesToSame( "/** @constructor */\n" + + "WebInspector.Setting = function() {}\n" + + "WebInspector.Setting.prototype = {\n" + + " get name0(){return this._name;},\n" + + " get name1(){return this._name;},\n" + + " get name2(){return this._name;},\n" + + " get name3(){return this._name;},\n" + + " get name4(){return this._name;},\n" + + " get name5(){return this._name;},\n" + + " get name6(){return this._name;},\n" + + " get name7(){return this._name;},\n" + + " get name8(){return this._name;},\n" + + " get name9(){return this._name;},\n" + + "}", 1); + } + + private void checkCompilesTo(String src, + String expectedHdr, + String expectedBody, + int repetitions) { + StringBuilder srcBuffer = new StringBuilder(); + StringBuilder expectedBuffer = new StringBuilder(); + + expectedBuffer.append(expectedHdr); + + for (int idx = 0; idx < repetitions; idx++) { + if (idx != 0) { + srcBuffer.append(";"); + expectedBuffer.append(";"); + } + srcBuffer.append(src); + expectedBuffer.append(expectedBody); + } + test(srcBuffer.toString(), expectedBuffer.toString()); + } + + private void checkCompilesToSame(String src, int repetitions) { + checkCompilesTo(src, "", src, repetitions); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionToBlockMutatorTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionToBlockMutatorTest.java new file mode 100644 index 0000000..abc940f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionToBlockMutatorTest.java @@ -0,0 +1,311 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import junit.framework.TestCase; + +import java.util.Set; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class FunctionToBlockMutatorTest extends TestCase { + + public void testMutateNoReturnWithoutResultAssignment() { + helperMutate( + "function foo(){}; foo();", + "{}", + "foo"); + } + + public void testMutateNoReturnWithResultAssignment() { + helperMutate( + "function foo(){}; var result = foo();", + "{result = void 0}", + "foo", true, false); + } + + + public void testMutateNoValueReturnWithoutResultAssignment() { + helperMutate( + "function foo(){return;}; foo();", + "{}", + "foo", null); + } + + public void testMutateNoValueReturnWithResultAssignment() { + helperMutate( + "function foo(){return;}; var result = foo();", + "{result = void 0}", + "foo"); + } + + public void testMutateValueReturnWithoutResultAssignment() { + helperMutate( + "function foo(){return true;}; foo();", + "{true;}", + "foo", null); + } + + public void testMutateValueReturnWithResultAssignment() { + helperMutate( + "function foo(){return true;}; var x=foo();", + "{x=true}", + "foo", "x", true, false); + } + + public void testMutateWithMultipleReturns() { + helperMutate( + "function foo(){ if (0) {return 0} else {return 1} };" + + "var result=foo();", + "{" + + "JSCompiler_inline_label_foo_0:{" + + "if(0) {" + + "result=0; break JSCompiler_inline_label_foo_0" + + "} else {" + + "result=1; break JSCompiler_inline_label_foo_0" + + "} result=void 0" + + "}" + + "}", + "foo", true, false); + } + + public void testMutateWithParameters1() { + // Simple call with useless parameter + helperMutate( + "function foo(a){return true;}; foo(x);", + "{true}", + "foo", null); + } + + public void testMutateWithParameters2() { + // Simple call with parameter + helperMutate( + "function foo(a){return x;}; foo(x);", + "{x}", + "foo", null); + } + + public void testMutateWithParameters3() { + // Parameter has side-effects. + helperMutate( + "function foo(a){return a;}; " + + "function x() { foo(x++); }", + "{var a$$inline_0 = x++; a$$inline_0}", + "foo", null); + } + + public void testMutate8() { + // Parameter has side-effects. + helperMutate( + "function foo(a){return a+a;}; foo(x++);", + "{var a$$inline_0 = x++;" + + "a$$inline_0 + a$$inline_0;}", + "foo", null); + } + + public void testMutateInitializeUninitializedVars1() { + helperMutate( + "function foo(a){var b;return a;}; foo(1);", + "{var b$$inline_1=void 0;1}", + "foo", null, false, true); + } + + public void testMutateInitializeUninitializedVars2() { + helperMutate( + "function foo(a){for(var b in c)return a;}; foo(1);", + "{JSCompiler_inline_label_foo_2:" + + "{" + + "for(var b$$inline_1 in c){" + + "1;break JSCompiler_inline_label_foo_2" + + "}" + + "}" + + "}", + "foo", null); + } + + public void testMutateCallInLoopVars1() { + // baseline: outside a loop, the constant remains constant. + boolean callInLoop = false; + helperMutate( + "function foo(a){var B = bar(); a;}; foo(1);", + "{var B$$inline_1=bar(); 1;}", + "foo", null, false, callInLoop); + // ... in a loop, the constant-ness is removed. + // TODO(johnlenz): update this test to look for the const annotation. + callInLoop = true; + helperMutate( + "function foo(a){var B = bar(); a;}; foo(1);", + "{var B$$inline_1 = bar(); 1;}", + "foo", null, false, callInLoop); + } + + public void testMutateFunctionDefinition() { + // function declarations are rewritten as function + // expressions + helperMutate( + "function foo(a){function g(){}}; foo(1);", + "{var g$$inline_1=function(){};}", + "foo", null); + } + + public void helperMutate( + String code, final String expectedResult, final String fnName) { + helperMutate(code, expectedResult, fnName, false, false); + } + + public void helperMutate( + String code, final String expectedResult, final String fnName, + final boolean needsDefaultResult, + final boolean isCallInLoop) { + helperMutate(code, expectedResult, fnName, + "result", needsDefaultResult, isCallInLoop); + } + + public void helperMutate( + String code, final String expectedResult, final String fnName, + final String resultName) { + helperMutate(code, expectedResult, fnName, resultName, false, false); + } + + private void validateSourceInfo(Compiler compiler, Node subtree) { + (new LineNumberCheck(compiler)).setCheckSubTree(subtree); + // Source information problems are reported as compiler errors. + if (compiler.getErrorCount() != 0) { + String msg = "Error encountered: "; + for (JSError err : compiler.getErrors()) { + msg += err.toString() + "\n"; + } + assertTrue(msg, compiler.getErrorCount() == 0); + } + } + + public void helperMutate( + String code, final String expectedResult, final String fnName, + final String resultName, + final boolean needsDefaultResult, + final boolean isCallInLoop) { + final Compiler compiler = new Compiler(); + final FunctionToBlockMutator mutator = new FunctionToBlockMutator( + compiler, compiler.getUniqueNameIdSupplier()); + Node expectedRoot = parse(compiler, expectedResult); + Preconditions.checkState(compiler.getErrorCount() == 0); + final Node expected = expectedRoot.getFirstChild(); + final Node tree = parse(compiler, code); + Preconditions.checkState(compiler.getErrorCount() == 0); + + Node externsRoot = new Node(Token.EMPTY); + Node mainRoot = tree; + MarkNoSideEffectCalls mark = new MarkNoSideEffectCalls(compiler); + mark.process(externsRoot, mainRoot); + + final Node fnNode = findFunction(tree, fnName); + final Set unsafe = + FunctionArgumentInjector.findModifiedParameters(fnNode); + + // Fake precondition. + compiler.setLifeCycleStage(LifeCycleStage.NORMALIZED); + + // inline tester + Method tester = new Method() { + @Override + public boolean call(NodeTraversal t, Node n, Node parent) { + + Node result = mutator.mutate( + fnName, fnNode, n, resultName, + needsDefaultResult, isCallInLoop); + validateSourceInfo(compiler, result); + String explanation = expected.checkTreeEquals(result); + assertNull("\nExpected: " + compiler.toSource(expected) + + "\nResult: " + compiler.toSource(result) + + "\n" + explanation, explanation); + return true; + } + }; + + compiler.resetUniqueNameId(); + TestCallback test = new TestCallback(fnName, tester); + NodeTraversal.traverse(compiler, tree, test); + } + + interface Method { + boolean call(NodeTraversal t, Node n, Node parent); + } + + class TestCallback implements Callback { + + private final String callname; + private final Method method; + private boolean complete = false; + + TestCallback(String callname, Method method) { + this.callname = callname; + this.method = method; + } + + @Override + public boolean shouldTraverse( + NodeTraversal nodeTraversal, Node n, Node parent) { + return !complete; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isCall()) { + Node first = n.getFirstChild(); + if (first.isName() && + first.getString().equals(callname)) { + complete = method.call(t, n, parent); + } + } + + if (parent == null) { + assertTrue(complete); + } + } + } + + private static Node findFunction(Node n, String name) { + if (n.isFunction()) { + if (n.getFirstChild().getString().equals(name)) { + return n; + } + } + + for (Node c : n.children()) { + Node result = findFunction(c, name); + if (result != null) { + return result; + } + } + + return null; + } + + private static Node parse(Compiler compiler, String js) { + Node n = compiler.parseTestCode(js); + assertEquals(0, compiler.getErrorCount()); + return n; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionTypeBuilderTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionTypeBuilderTest.java new file mode 100644 index 0000000..fc02fe8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/FunctionTypeBuilderTest.java @@ -0,0 +1,131 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import static com.google.javascript.rhino.testing.BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES; + +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.ObjectType; + +import java.util.List; + +/** + * Unit tests for {@link FunctionTypeBuilder}. + * + */ +public class FunctionTypeBuilderTest extends CompilerTestCase { + + public FunctionTypeBuilderTest() { + parseTypeInfo = true; + enableTypeCheck(CheckLevel.WARNING); + } + + @Override + public CompilerPass getProcessor(Compiler compiler) { + // By turning on type checking, the FunctionTypeBuilder will be invoked. + return new CompilerPass() { + @Override + public void process(Node externs, Node js) {} + }; + } + + @Override + public int getNumRepetitions() { + return 1; + } + + public void testValidBuiltInTypeRedefinition() throws Exception { + testSame(ALL_NATIVE_EXTERN_TYPES, "", null); + } + + public void testBuiltInTypeDifferentReturnType() throws Exception { + testSame( + "/**\n" + + " * @constructor\n" + + " * @param {*} opt_str\n" + + " * @return {number}\n" + + " */\n" + + "function String(opt_str) {}\n", + "", FunctionTypeBuilder.TYPE_REDEFINITION, + "attempted re-definition of type String\n" + + "found : function (new:String, *=): number\n" + + "expected: function (new:String, *=): string"); + } + + public void testBuiltInTypeDifferentNumParams() throws Exception { + testSame( + "/**\n" + + " * @constructor\n" + + " * @return {string}\n" + + " */\n" + + "function String() {}\n", + "", FunctionTypeBuilder.TYPE_REDEFINITION, + "attempted re-definition of type String\n" + + "found : function (new:String): string\n" + + "expected: function (new:String, *=): string"); + } + + public void testBuiltInTypeDifferentNumParams2() throws Exception { + testSame( + "/**\n" + + " * @constructor\n" + + " * @return {string}\n" + + " */\n" + + "function String(opt_str, opt_nothing) {}\n", + "", FunctionTypeBuilder.TYPE_REDEFINITION, + "attempted re-definition of type String\n" + + "found : function (new:String, ?=, ?=): string\n" + + "expected: function (new:String, *=): string"); + } + + public void testBuiltInTypeDifferentParamType() throws Exception { + testSame( + "/**\n" + + " * @constructor\n" + + " * @return {string}\n" + + " */\n" + + "function String(opt_str) {}\n", + "", FunctionTypeBuilder.TYPE_REDEFINITION, + "attempted re-definition of type String\n" + + "found : function (new:String, ?=): string\n" + + "expected: function (new:String, *=): string"); + } + + public void testBadFunctionTypeDefinition() throws Exception { + testSame( + "/** @constructor */function Function(opt_str) {}\n", + "", FunctionTypeBuilder.TYPE_REDEFINITION, + "attempted re-definition of type Function\n" + + "found : function (new:Function, ?=): ?\n" + + "expected: function (new:Function, ...[*]): ?"); + } + + public void testExternSubTypes() throws Exception { + testSame(ALL_NATIVE_EXTERN_TYPES, "", null); + + List subtypes = ((ObjectType) getLastCompiler() + .getTypeRegistry().getType("Error")).getConstructor().getSubTypes(); + for (FunctionType type : subtypes) { + String typeName = type.getInstanceType().toString(); + FunctionType typeInRegistry = ((ObjectType) getLastCompiler() + .getTypeRegistry().getType(typeName)).getConstructor(); + assertTrue(typeInRegistry == type); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GatherRawExportsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GatherRawExportsTest.java new file mode 100644 index 0000000..14ccdec --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GatherRawExportsTest.java @@ -0,0 +1,123 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Sets; + +import java.util.Set; + +/** + * Tests for {@link GatherRawExports}. + * + * @author johnlenz@google.com (John Lenz) + */ +public class GatherRawExportsTest extends CompilerTestCase { + + private static final String EXTERNS = "var window;"; + private GatherRawExports last; + + public GatherRawExportsTest() { + super(EXTERNS); + super.enableNormalize(); + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + last = new GatherRawExports(compiler); + return last; + } + + public void testExportsFound1() { + assertExported("var a"); + } + + public void testExportsFound2() { + assertExported("window['a']", "a"); + } + + public void testExportsFound3() { + assertExported("window.a", "a"); + } + + public void testExportsFound4() { + assertExported("this['a']", "a"); + } + + public void testExportsFound5() { + assertExported("this.a", "a"); + } + + public void testExportsFound6() { + assertExported("function f() { this['a'] }"); + } + + public void testExportsFound7() { + assertExported("function f() { this.a }"); + } + + public void testExportsFound8() { + assertExported("window['foo']", "foo"); + } + + public void testExportsFound9() { + assertExported("window['a'] = 1;", "a"); + } + + public void testExportsFound10() { + assertExported("window['a']['b']['c'] = 1;", "a"); + } + + public void testExportsFound11() { + assertExported("if (window['a'] = 1) alert(x);", "a"); + } + + public void testExportsFound12() { + assertExported("function foo() { window['a'] = 1; }", "a"); + } + + public void testExportsFound13() { + assertExported("function foo() {var window; window['a'] = 1; }"); + } + + public void testExportsFound14() { + assertExported("var a={window:{}}; a.window['b']"); + } + + public void testExportsFound15() { + assertExported("window.window['b']", "window"); + } + + public void testExportsFound16() { + // It would be nice to handle this case, hopefully inlining will take care + // of it for us. + assertExported("var a = window; a['b']"); + } + + public void testExportOnTopFound1() { + assertExported("top['a']", "a"); + } + + public void testExportOntopFound2() { + assertExported("top.a", "a"); + } + + private void assertExported(String js, String ... names) { + Set setNames = Sets.newHashSet(names); + testSame(js); + assertTrue(last.getExportedVariableNames().equals(setNames)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GatherSideEffectSubexpressionsCallbackTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GatherSideEffectSubexpressionsCallbackTest.java new file mode 100644 index 0000000..003d62d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GatherSideEffectSubexpressionsCallbackTest.java @@ -0,0 +1,192 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.GatherSideEffectSubexpressionsCallback.GetReplacementSideEffectSubexpressions; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import junit.framework.TestCase; + +import java.util.List; + +/** + * Tests for {@link GatherSideEffectSubexpressionsCallback} + * + */ +public class GatherSideEffectSubexpressionsCallbackTest extends TestCase { + public void testAndOr() throws Exception { + Node andNode = getSideEffectsAndNode(); + checkKeepSimplifiedShortCircuitExpr(andNode, + ImmutableList.of("foo&&(bar=0)")); + } + + public void testIllegalArgumentIfNotAndOr() throws Exception { + Node nameNode = Node.newString(Token.NAME, "foo"); + try { + checkKeepSimplifiedShortCircuitExpr(nameNode, + ImmutableList.of()); + fail("Expected exception"); + } catch (IllegalArgumentException e) { + // ignore + } + } + + public void testIllegalArgumentIfNoSideEffectAndOr() throws Exception { + Node andNode = getNoSideEffectsAndNode(); + try { + checkKeepSimplifiedShortCircuitExpr(andNode, + ImmutableList.of()); + fail("Expected exception"); + } catch (IllegalArgumentException e) { + // ignore + } + } + + public void testHook() throws Exception { + Node hook = getSideEffectsHookNode(); + + checkKeepSimplifiedHookExpr(hook, + true, + true, + ImmutableList.of("foo?bar=0:baz=0")); + } + + public void testIllegalArgumentIfNotHook() throws Exception { + Node nameNode = Node.newString(Token.NAME, "foo"); + try { + checkKeepSimplifiedHookExpr(nameNode, + true, + true, + ImmutableList.of()); + fail("Expected exception"); + } catch (IllegalArgumentException e) { + // ignore + } + } + + public void testIllegalArgumentIfNoSideEffectHook() throws Exception { + Node hookNode = getNoSideEffectsHookNode(); + try { + checkKeepSimplifiedHookExpr(hookNode, + true, + true, + ImmutableList.of()); + fail("Expected exception"); + } catch (IllegalArgumentException e) { + // ignore + } + } + + public void testIllegalArgumentIfHookKeepNeitherBranch() throws Exception { + Node hookNode = getSideEffectsHookNode(); + try { + checkKeepSimplifiedHookExpr(hookNode, + false, + false, + ImmutableList.of()); + fail("Expected exception"); + } catch (IllegalArgumentException e) { + // ignore + } + } + + private Node getNoSideEffectsAndNode() { + Node andNode = new Node(Token.AND); + + andNode.addChildToBack(Node.newString(Token.NAME, "foo")); + andNode.addChildToBack(Node.newString(Token.NAME, "bar")); + + return andNode; + } + + private Node getSideEffectsAndNode() { + Node andNode = new Node(Token.AND); + + Node assign = new Node(Token.ASSIGN); + assign.addChildToBack(Node.newString(Token.NAME, "bar")); + assign.addChildToBack(Node.newNumber(0)); + + andNode.addChildToBack(Node.newString(Token.NAME, "foo")); + andNode.addChildToBack(assign); + + return andNode; + } + + private Node getNoSideEffectsHookNode() { + Node hookNode = new Node(Token.HOOK); + + hookNode.addChildToBack(Node.newString(Token.NAME, "foo")); + hookNode.addChildToBack(Node.newString(Token.NAME, "bar")); + hookNode.addChildToBack(Node.newString(Token.NAME, "baz")); + + return hookNode; + } + + private Node getSideEffectsHookNode() { + Node hookNode = new Node(Token.HOOK); + + Node assign1 = new Node(Token.ASSIGN); + assign1.addChildToBack(Node.newString(Token.NAME, "bar")); + assign1.addChildToBack(Node.newNumber(0)); + + Node assign2 = new Node(Token.ASSIGN); + assign2.addChildToBack(Node.newString(Token.NAME, "baz")); + assign2.addChildToBack(Node.newNumber(0)); + + hookNode.addChildToBack(Node.newString(Token.NAME, "foo")); + hookNode.addChildToBack(assign1); + hookNode.addChildToBack(assign2); + + return hookNode; + } + + private void checkKeepSimplifiedShortCircuitExpr(Node root, + List expected) { + Compiler compiler = new Compiler(); + List replacements = Lists.newArrayList(); + GetReplacementSideEffectSubexpressions accumulator = + new GetReplacementSideEffectSubexpressions(compiler, replacements); + accumulator.keepSimplifiedShortCircuitExpression(root); + + List actual = Lists.newArrayList(); + for (Node replacement : replacements) { + actual.add(compiler.toSource(replacement)); + } + assertEquals(expected, actual); + } + + private void checkKeepSimplifiedHookExpr(Node root, + boolean thenHasSideEffects, + boolean elseHasSideEffects, + List expected) { + Compiler compiler = new Compiler(); + List replacements = Lists.newArrayList(); + GetReplacementSideEffectSubexpressions accumulator = + new GetReplacementSideEffectSubexpressions(compiler, replacements); + accumulator.keepSimplifiedHookExpression(root, + thenHasSideEffects, + elseHasSideEffects); + List actual = Lists.newArrayList(); + for (Node replacement : replacements) { + actual.add(compiler.toSource(replacement)); + } + assertEquals(expected, actual); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GenerateExportsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GenerateExportsTest.java new file mode 100644 index 0000000..fbbce69 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GenerateExportsTest.java @@ -0,0 +1,125 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Generate exports unit test. + * + */ +public class GenerateExportsTest extends CompilerTestCase { + + private static final String EXTERNS = + "function google_exportSymbol(a, b) {}; " + + "goog.exportProperty = function(a, b, c) {}; "; + + public GenerateExportsTest() { + super(EXTERNS); + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new GenerateExports(compiler, + "google_exportSymbol", "goog.exportProperty"); + } + + @Override + protected int getNumRepetitions() { + // This pass only runs once. + return 1; + } + + @Override + public void setUp() throws Exception { + super.setUp(); + super.enableLineNumberCheck(false); + } + + public void testExportSymbol() { + test("/** @export */function foo() {}", + "function foo(){}google_exportSymbol(\"foo\",foo)"); + } + + public void testExportSymbolAndProperties() { + test("/** @export */function foo() {}" + + "/** @export */foo.prototype.bar = function() {}", + "function foo(){}" + + "google_exportSymbol(\"foo\",foo);" + + "foo.prototype.bar=function(){};" + + "goog.exportProperty(foo.prototype,\"bar\",foo.prototype.bar)"); + } + + public void testExportSymbolAndConstantProperties() { + test("/** @export */function foo() {}" + + "/** @export */foo.BAR = 5;", + "function foo(){}" + + "google_exportSymbol(\"foo\",foo);" + + "foo.BAR=5;" + + "goog.exportProperty(foo,\"BAR\",foo.BAR)"); + } + + public void testExportVars() { + test("/** @export */var FOO = 5", + "var FOO=5;" + + "google_exportSymbol(\"FOO\",FOO)"); + } + + public void testNoExport() { + test("var FOO = 5", "var FOO=5"); + } + + /** + * Nested assignments are ambiguous and therefore not supported. + * @see FindExportableNodes + */ + public void testNestedVarAssign() { + test("var BAR;\n/** @export */var FOO = BAR = 5", + null, FindExportableNodes.NON_GLOBAL_ERROR); + } + + /** + * Nested assignments are ambiguous and therefore not supported. + * @see FindExportableNodes + */ + public void testNestedAssign() { + test("var BAR;var FOO = {};\n/** @export */FOO.test = BAR = 5", + null, FindExportableNodes.NON_GLOBAL_ERROR); + } + + public void testNonGlobalScopeExport() { + test("(function() { /** @export */var FOO = 5 })()", + null, FindExportableNodes.NON_GLOBAL_ERROR); + } + + public void testExportClass() { + test("/** @export */ function G() {} foo();", + "function G() {} google_exportSymbol('G', G); foo();"); + } + + public void testExportSubclass() { + test("var goog = {}; function F() {}" + + "/** @export */ function G() {} goog.inherits(G, F);", + "var goog = {}; function F() {}" + + "function G() {} goog.inherits(G, F); google_exportSymbol('G', G);"); + } + + public void testExportEnum() { + // TODO(johnlenz): Issue 310, should the values also be externed? + test("/** @enum {string}\n @export */ var E = {A:1, B:2};", + "/** @enum {string}\n @export */ var E = {A:1, B:2};" + + "google_exportSymbol('E', E);"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GlobalNamespaceTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GlobalNamespaceTest.java new file mode 100644 index 0000000..26e4b96 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GlobalNamespaceTest.java @@ -0,0 +1,72 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.GlobalNamespace.Name; +import com.google.javascript.jscomp.GlobalNamespace.Ref; + +import junit.framework.TestCase; + +/** + * Tests for {@link GlobalNamespace}. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class GlobalNamespaceTest extends TestCase { + + public void testRemoveDeclaration1() { + Name n = new Name("a", null, false); + Ref set1 = createNodelessRef(Ref.Type.SET_FROM_GLOBAL); + Ref set2 = createNodelessRef(Ref.Type.SET_FROM_GLOBAL); + + n.addRef(set1); + n.addRef(set2); + + assertEquals(set1, n.getDeclaration()); + assertEquals(2, n.globalSets); + assertEquals(2, n.getRefs().size()); + + n.removeRef(set1); + + assertEquals(set2, n.getDeclaration()); + assertEquals(1, n.globalSets); + assertEquals(1, n.getRefs().size()); + } + + public void testRemoveDeclaration2() { + Name n = new Name("a", null, false); + Ref set1 = createNodelessRef(Ref.Type.SET_FROM_GLOBAL); + Ref set2 = createNodelessRef(Ref.Type.SET_FROM_LOCAL); + + n.addRef(set1); + n.addRef(set2); + + assertEquals(set1, n.getDeclaration()); + assertEquals(1, n.globalSets); + assertEquals(1, n.localSets); + assertEquals(2, n.getRefs().size()); + + n.removeRef(set1); + + assertEquals(null, n.getDeclaration()); + assertEquals(0, n.globalSets); + } + + private Ref createNodelessRef(Ref.Type type) { + return Ref.createRefForTesting(type); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GlobalVarReferenceMapTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GlobalVarReferenceMapTest.java new file mode 100644 index 0000000..f70ec7c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GlobalVarReferenceMapTest.java @@ -0,0 +1,197 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import static com.google.javascript.jscomp.ReferenceCollectingCallback.Reference.createRefForTest; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.javascript.jscomp.ReferenceCollectingCallback.Reference; +import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import junit.framework.TestCase; + +import java.util.Map; + +/** + * Unit-tests for the GlobalVarReferenceMap class. + * + * @author bashir@google.com (Bashir Sadjad) + */ +public class GlobalVarReferenceMapTest extends TestCase { + + private final CompilerInput INPUT1 = + new CompilerInput(SourceFile.fromCode("input1", ""), false); + private final CompilerInput INPUT2 = + new CompilerInput(SourceFile.fromCode("input2", ""), false); + private final CompilerInput INPUT3 = + new CompilerInput(SourceFile.fromCode("input3", ""), false); + private final CompilerInput EXTERN1 = + new CompilerInput(SourceFile.fromCode("extern1", ""), true); + + private final GlobalVarReferenceMap map = new GlobalVarReferenceMap( + Lists.newArrayList(INPUT1, INPUT2, INPUT3), Lists.newArrayList(EXTERN1)); + private final Map globalMap = Maps.newHashMap(); + private final Node root = new Node(Token.BLOCK); + private final Scope globalScope = Scope.createGlobalScope(root); + private Node scriptRoot = new Node(Token.SCRIPT); + + // In the initial setUp we have 3 references to var1 (one in each input) and + // 2 references to var2 (in first and third inputs), and 2 references to var3 + // (in second input and first extern) + private static final String VAR1 = "var1"; + private static final String VAR2 = "var2"; + private static final String VAR3 = "var3"; + private final ReferenceCollection var1Refs = new ReferenceCollection(); + private final ReferenceCollection var2Refs = new ReferenceCollection(); + private final ReferenceCollection var3Refs = new ReferenceCollection(); + private final Reference var1In1Ref = createRefForTest(INPUT1); + private final Reference var1In2Ref = createRefForTest(INPUT2); + private final Reference var1In3Ref = createRefForTest(INPUT3); + private final Reference var2In1Ref = createRefForTest(INPUT1); + private final Reference var2In3Ref = createRefForTest(INPUT3); + private final Reference var3In2Ref = createRefForTest(INPUT2); + private final Reference var3In1Ext = createRefForTest(EXTERN1); + + @Override + protected void setUp() throws Exception { + super.setUp(); + globalScope.declare(VAR1, new Node(Token.NAME), null, INPUT1); + var1Refs.references = Lists.newArrayList(var1In1Ref, + var1In2Ref, var1In3Ref); + globalScope.declare(VAR2, new Node(Token.NAME), null, INPUT1); + var2Refs.references = Lists.newArrayList(var2In1Ref, var2In3Ref); + globalScope.declare(VAR3, new Node(Token.NAME), null, EXTERN1); + var3Refs.references = Lists.newArrayList(var3In1Ext, var3In2Ref); + + // We recreate these two ReferenceCollection to keep var1Refs and + // var2Refs intact in update operations for comparison in the tests. + ReferenceCollection var1TempRefs = new ReferenceCollection(); + var1TempRefs.references = Lists.newArrayList(var1Refs.references); + ReferenceCollection var2TempRefs = new ReferenceCollection(); + var2TempRefs.references = Lists.newArrayList(var2Refs.references); + ReferenceCollection var3TempRefs = new ReferenceCollection(); + var3TempRefs.references = Lists.newArrayList(var3Refs.references); + globalMap.put(globalScope.getVar(VAR1), var1TempRefs); + globalMap.put(globalScope.getVar(VAR2), var2TempRefs); + globalMap.put(globalScope.getVar(VAR3), var3TempRefs); + map.updateGlobalVarReferences(globalMap, root); + scriptRoot.setInputId(INPUT2.getInputId()); + scriptRoot.setSourceFileForTesting(INPUT2.getName()); + } + + /** Tests whether the global variable references are set/reset properly. */ + public void testUpdateGlobalVarReferences_ResetReferences() { + // First we check the original setup then reset again. + for (int i = 0; i < 2; i++) { + assertEquals(var1Refs.references, + map.getReferences(globalScope.getVar(VAR1)).references); + assertEquals(var2Refs.references, + map.getReferences(globalScope.getVar(VAR2)).references); + assertEquals(var3Refs.references, + map.getReferences(globalScope.getVar(VAR3)).references); + map.updateGlobalVarReferences(globalMap, root); + } + } + + /** Removes all variable references in second script. */ + public void testUpdateGlobalVarReferences_UpdateScriptNoRef() { + Map scriptMap = Maps.newHashMap(); + map.updateGlobalVarReferences(scriptMap, scriptRoot); + ReferenceCollection refs = map.getReferences(globalScope.getVar(VAR2)); + assertEquals(var2Refs.references, refs.references); + refs = map.getReferences(globalScope.getVar(VAR1)); + assertEquals(2, refs.references.size()); + assertEquals(var1Refs.references.get(0), refs.references.get(0)); + assertEquals(var1Refs.references.get(2), refs.references.get(1)); + refs = map.getReferences(globalScope.getVar(VAR3)); + assertEquals(1, refs.references.size()); + assertEquals(var3Refs.references.get(0), refs.references.get(0)); + } + + /** Changes variable references in second script. */ + public void testUpdateGlobalVarReferences_UpdateScriptNewRefs() { + Map scriptMap = Maps.newHashMap(); + + ReferenceCollection newVar1Refs = new ReferenceCollection(); + Reference newVar1In2Ref = createRefForTest(INPUT2); + newVar1Refs.references = Lists.newArrayList(newVar1In2Ref); + + ReferenceCollection newVar2Refs = new ReferenceCollection(); + Reference newVar2In2Ref = createRefForTest(INPUT2); + newVar2Refs.references = Lists.newArrayList(newVar2In2Ref); + + ReferenceCollection newVar3Refs = new ReferenceCollection(); + Reference newVar3In2Ref = createRefForTest(INPUT2); + newVar3Refs.references = Lists.newArrayList(newVar3In2Ref); + + scriptMap.put(globalScope.getVar(VAR1), newVar1Refs); + scriptMap.put(globalScope.getVar(VAR2), newVar2Refs); + scriptMap.put(globalScope.getVar(VAR3), newVar3Refs); + map.updateGlobalVarReferences(scriptMap, scriptRoot); + ReferenceCollection refs = map.getReferences(globalScope.getVar(VAR1)); + assertEquals(3, refs.references.size()); + assertEquals(var1Refs.references.get(0), refs.references.get(0)); + assertEquals(newVar1In2Ref, refs.references.get(1)); + assertEquals(var1Refs.references.get(2), refs.references.get(2)); + refs = map.getReferences(globalScope.getVar(VAR2)); + assertEquals(3, refs.references.size()); + assertEquals(var2Refs.references.get(0), refs.references.get(0)); + assertEquals(newVar2In2Ref, refs.references.get(1)); + assertEquals(var2Refs.references.get(1), refs.references.get(2)); + refs = map.getReferences(globalScope.getVar(VAR3)); + assertEquals(2, refs.references.size()); + assertEquals(var3Refs.references.get(0), refs.references.get(0)); + assertEquals(newVar3In2Ref, refs.references.get(1)); + } + + /** Changes variable references in second script. */ + public void testUpdateGlobalVarReferences_UpdateScriptNewVar() { + Map scriptMap = Maps.newHashMap(); + final String var4 = "var4"; + globalScope.declare(var4, new Node(Token.NAME), null, INPUT2); + ReferenceCollection newVar3Refs = new ReferenceCollection(); + Reference newVar3In2Ref = createRefForTest(INPUT2); + newVar3Refs.references = Lists.newArrayList(newVar3In2Ref); + scriptMap.put(globalScope.getVar(var4), newVar3Refs); + map.updateGlobalVarReferences(scriptMap, scriptRoot); + ReferenceCollection refs = map.getReferences(globalScope.getVar(var4)); + assertEquals(1, refs.references.size()); + assertEquals(newVar3In2Ref, refs.references.get(0)); + } + + public void testUpdateReferencesWithGlobalScope() { + Scope newGlobalScope = Scope.createGlobalScope(root); + map.updateReferencesWithGlobalScope(newGlobalScope); + ReferenceCollection references = + map.getReferences(globalScope.getVar(VAR1)); + for (Reference ref : references) { + assertEquals(newGlobalScope, ref.getScope()); + } + references = map.getReferences(globalScope.getVar(VAR2)); + for (Reference ref : references) { + assertEquals(newGlobalScope, ref.getScope()); + } + references = map.getReferences(globalScope.getVar(VAR3)); + for (Reference ref : references) { + assertEquals(newGlobalScope, ref.getScope()); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GoogleCodingConventionTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GoogleCodingConventionTest.java new file mode 100644 index 0000000..4ff9e6c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GoogleCodingConventionTest.java @@ -0,0 +1,160 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.CodingConvention.SubclassRelationship; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import junit.framework.TestCase; + +/** + * Test class for {@link GoogleCodingConvention}. + */ +public class GoogleCodingConventionTest extends TestCase { + private GoogleCodingConvention conv = new GoogleCodingConvention(); + + public void testVarAndOptionalParams() { + Node args = new Node(Token.PARAM_LIST, + Node.newString(Token.NAME, "a"), + Node.newString(Token.NAME, "b")); + Node optArgs = new Node(Token.PARAM_LIST, + Node.newString(Token.NAME, "opt_a"), + Node.newString(Token.NAME, "opt_b")); + + assertFalse(conv.isVarArgsParameter(args.getFirstChild())); + assertFalse(conv.isVarArgsParameter(args.getLastChild())); + assertFalse(conv.isVarArgsParameter(optArgs.getFirstChild())); + assertFalse(conv.isVarArgsParameter(optArgs.getLastChild())); + + assertFalse(conv.isOptionalParameter(args.getFirstChild())); + assertFalse(conv.isOptionalParameter(args.getLastChild())); + assertTrue(conv.isOptionalParameter(optArgs.getFirstChild())); + assertTrue(conv.isOptionalParameter(optArgs.getLastChild())); + } + + public void testInlineName() { + assertFalse(conv.isConstant("a")); + assertTrue(conv.isConstant("XYZ123_")); + assertTrue(conv.isConstant("ABC")); + assertFalse(conv.isConstant("ABCdef")); + assertFalse(conv.isConstant("aBC")); + assertFalse(conv.isConstant("A")); + assertFalse(conv.isConstant("_XYZ123")); + assertTrue(conv.isConstant("a$b$XYZ123_")); + assertTrue(conv.isConstant("a$b$ABC_DEF")); + assertTrue(conv.isConstant("a$b$A")); + assertFalse(conv.isConstant("a$b$a")); + assertFalse(conv.isConstant("a$b$ABCdef")); + assertFalse(conv.isConstant("a$b$aBC")); + assertFalse(conv.isConstant("a$b$")); + assertFalse(conv.isConstant("$")); + } + + public void testExportedName() { + assertTrue(conv.isExported("_a")); + assertTrue(conv.isExported("_a_")); + assertFalse(conv.isExported("a")); + + assertFalse(conv.isExported("$super", false)); + assertTrue(conv.isExported("$super", true)); + assertTrue(conv.isExported("$super")); + } + + public void testPrivateName() { + assertTrue(conv.isPrivate("a_")); + assertFalse(conv.isPrivate("a")); + assertFalse(conv.isPrivate("_a_")); + } + + public void testEnumKey() { + assertTrue(conv.isValidEnumKey("A")); + assertTrue(conv.isValidEnumKey("123")); + assertTrue(conv.isValidEnumKey("FOO_BAR")); + + assertFalse(conv.isValidEnumKey("a")); + assertFalse(conv.isValidEnumKey("someKeyInCamelCase")); + assertFalse(conv.isValidEnumKey("_FOO_BAR")); + } + + public void testInheritanceDetection1() { + assertNotClassDefining("goog.foo(A, B);"); + } + + public void testInheritanceDetection2() { + assertDefinesClasses("goog.inherits(A, B);", "A", "B"); + } + + public void testInheritanceDetection3() { + assertDefinesClasses("A.inherits(B);", "A", "B"); + } + + public void testInheritanceDetection4() { + assertDefinesClasses("goog.inherits(goog.A, goog.B);", "goog.A", "goog.B"); + } + + public void testInheritanceDetection5() { + assertDefinesClasses("goog.A.inherits(goog.B);", "goog.A", "goog.B"); + } + + public void testInheritanceDetection6() { + assertNotClassDefining("A.inherits(this.B);"); + } + + public void testInheritanceDetection7() { + assertNotClassDefining("this.A.inherits(B);"); + } + + public void testInheritanceDetection8() { + assertNotClassDefining("goog.inherits(A, B, C);"); + } + + public void testInheritanceDetection9() { + assertDefinesClasses("A.mixin(B.prototype);", + "A", "B"); + } + + public void testInheritanceDetection10() { + assertDefinesClasses("goog.mixin(A.prototype, B.prototype);", + "A", "B"); + } + + public void testInheritanceDetectionPostCollapseProperties() { + assertDefinesClasses("goog$inherits(A, B);", "A", "B"); + assertNotClassDefining("goog$inherits(A);"); + } + + private void assertNotClassDefining(String code) { + Node n = parseTestCode(code); + assertNull(conv.getClassesDefinedByCall(n.getFirstChild())); + } + + private void assertDefinesClasses(String code, String subclassName, + String superclassName) { + Node n = parseTestCode(code); + SubclassRelationship classes = + conv.getClassesDefinedByCall(n.getFirstChild()); + assertNotNull(classes); + assertEquals(subclassName, classes.subclassName); + assertEquals(superclassName, classes.superclassName); + } + + private Node parseTestCode(String code) { + Compiler compiler = new Compiler(); + return compiler.parseTestCode(code).getFirstChild(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GroupVariableDeclarationsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GroupVariableDeclarationsTest.java new file mode 100644 index 0000000..fc3336f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/GroupVariableDeclarationsTest.java @@ -0,0 +1,209 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +public class GroupVariableDeclarationsTest extends CompilerTestCase { + + @Override + protected void setUp() { + super.enableLineNumberCheck(false); + } + + public void testGroupingUninitializedVarsInScope() { + // basic with just one fn call in between + test("var a = 1; f1(); var b;", "var a = 1, b; f1();"); + // > 1 line of code in between + test("var a = \"mangoes\"; f1(); alert(a); var b;", + "var a = \"mangoes\", b; f1(); alert(a);"); + // nested block in between that also contains a declaration + initialization + // of the variable that needs to be collapsed + test("var a = 1; {var c; alert(c);} var b;", + "var a = 1, c, b; {alert(c);}"); + // multiple variables + test("var a = 1; var b = 1; f1(); f2(); var c; var d;", + "var a = 1, b, c, d; b = 1; f1(); f2();"); + test("var a = 1; var b = 2; var c; f1(); f2(); var d, e;", + "var a = 1, b, c, d, e; b = 2; f1(); f2();"); + test("var a = 1, b = 2, c; f1(); f2(); var d; var e; " + + "f3(); f4(); var f = 10; var g; var h = a + b;", + "var a = 1, b = 2, c, d, e, f, g, h; f1(); f2(); f3(); f4(); " + + "f = 10; h = a + b;"); + } + + public void testGroupingInitializedVarsInScope() { + // basic with just one fn call in between + test("var a = 1; f1(); var b = 2;", "var a = 1, b; f1(); b = 2;"); + // > 1 line of code in between + test("var a = \"mangoes\"; f1(); alert(a); var b = 2;", + "var a = \"mangoes\", b; f1(); alert(a); b = 2;"); + // nested block in between that also contains a declaration + initialization + // of the variable that needs to be collapsed + test("var a = 1; {var c = 34; alert(c);} var b = 2;", + "var a = 1, c, b; {c = 34; alert(c);} b = 2;"); + // multiple variables + test("var a = 1; var b = 1; f1(); f2(); var c = 3; var d = 4;", + "var a = 1, b, c, d; b = 1; f1(); f2(); c = 3; d = 4;"); + test("var a = 1; var b = 2; var c; f1(); f2(); var d = 4, e;", + "var a = 1, b, c, d, e; b = 2; f1(); f2(); d = 4;"); + test("var a = 1, b = 2, c; f1(); f2(); var d; var e = 6; " + + "f3(); f4(); var f; var g; var h = a + b;", + "var a = 1, b = 2, c, d, e, f, g, h; f1(); f2(); e = 6; " + + "f3(); f4(); h = a + b;"); + } + + public void testGroupingVarsInForAndForInLoops() { + // test for loop + test("var a = 1; for (var x = 0; x < 10; ++x) {a++;} var y;", + "var a = 1, x, y; for (x = 0; x < 10; ++x) {a++;}"); + test("var a = 1, x; for (x = 0; x < 10; ++x) {a++;} var y;", + "var a = 1, x, y; for (x = 0; x < 10; ++x) {a++;}"); + test("var a = 1, x; for (x; x < 10; ++x) {a++;} var y;", + "var a = 1, x, y; for (x; x < 10; ++x) {a++;}"); + test("var a = 1; for (; a < 10; ++a) {alert(a);} var y;", + "var a = 1, y; for (; a < 10; ++a) {alert(a);}"); + test("var a = 1; for (var x; x < 10; ++x) {a += 2;} var y = 5;", + "var a = 1, x, y; for (; x < 10; ++x) {a += 2;} y = 5;"); + // multiple loop-control variables in for loop + test("var a = 1; " + + "for (var a1 = 0, a2 = 10; a1 < 10 && a2 > 0; ++a1, --a2) {}" + + "var x = 5;", + "var a = 1, x;" + + "for (var a1 = 0, a2 = 10; a1 < 10 && a2 > 0; ++a1, --a2) {} " + + "x = 5;"); + test("var a = 1; " + + "for (var a1 = 0, a2; a1 < 10 && a2 > 0; ++a1, --a2) {}" + + "var x = 5;", + "var a = 1, a1, a2, x;" + + "for (a1 = 0; a1 < 10 && a2 > 0; ++a1, --a2) {}" + + "x = 5;"); + test("var a = 1; " + + "for (var a1, a2; a1 < 10 && a2 > 0; ++a1, --a2) {}" + + "var x = 5;", + "var a = 1, a1, a2, x;" + + "for (; a1 < 10 && a2 > 0; ++a1, --a2) {}" + + "x = 5;"); + + // test for-in loop + test("var a = [1, 2, 3, 4]; for (var z in a) {alert(z);} var y;", + "var a = [1, 2, 3, 4], z, y; for (z in a) {alert(z);}"); + test("var a = [1, 2, 3, 4]; for (var z in a) {alert(z);} var y = 5;", + "var a = [1, 2, 3, 4], z, y; for (z in a) {alert(z);} y = 5;"); + test("var a; for (var z in a = [1, 2, 3, 4]) {alert(z);} var y, x = 5;", + "var a, z, y, x; for (z in a = [1, 2, 3, 4]) {alert(z);} x = 5;"); + test("var a; for (var z = 1 in a = [1, 2, 3, 4]) {alert(z);} var y, x = 5;", + "var a, y, x; for (var z = 1 in a = [1, 2, 3, 4]) {alert(z);} x = 5;"); + test("var a, z; for (z in a = [1, 2, 3, 4]) {alert(z);} var y, x = 5;", + "var a, z, y, x; for (z in a = [1, 2, 3, 4]) {alert(z);} x = 5;"); + } + + public void testGroupingVarsNestedFunction() { + test("function f(b) {var x; function g() {var x; a = x; var y;} var a;}", + "function f(b) {var x, a; function g() {var x, y; a = x;}}"); + } + + public void testGroupingVarsInnerFunction() { + test("function f(b) {var x; h = x * x; var myfn = function() " + + "{var x; a = x; var y;}; var a;}", + "function f(b) {var x, myfn, a; h = x * x; myfn = function() " + + "{var x, y; a = x;};}"); + } + + public void testGroupingVarsFirstStatementNotVar() { + test("f(); var a; g(); var b;", "f(); var a, b; g();"); + } + + public void testGroupingVarsInScopeRegtest() { + // regtest with lots of different scopes + test("var x = 0, y = 1, z;" + + "function f1(aa, bb) {" + + " if (y) {" + + " if (x === 0) {" + + " var h, r = 999;" + + " }" + + " } else {" + + " r = 1000;" + + " }" + + " var mylist = [1, 2, 3, 4];" + + " var k1 = 200, k2 = 400;" + + " for (var i1 = 0; i1 < 10; ++i1) {" + + " for (var i2 in mylist) {" + + " alert(i1);" + + " }" + + " }" + + " var jam, q = 100;" + + " var myfn = function() {" + + " var x = 1;" + + " f5();" + + " var z = 5;" + + " };" + + " function f5() {" + + " var aa = 5;" + + " if (y === 1) {" + + " var x = 100;" + + " }" + + " }" + + "}" + + "var h = x + y;" + + "function g() {" + + " y = 0;" + + " { var x = 200;}" + + " var h = y + x;" + + "}" + + "var ggg = 0;", // End of input + "var x = 0, y = 1, z, h, ggg;" + + "function f1(aa, bb) {" + + " if (y) {" + + " if (x === 0) {" + + " var h, r = 999, mylist, i1, i2, jam, q, myfn;" + + " }" + + " } else {" + + " r = 1000;" + + " }" + + " mylist = [1, 2, 3, 4];" + + " var k1 = 200, k2 = 400;" + + " for (i1 = 0; i1 < 10; ++i1) {" + + " for (i2 in mylist) {" + + " alert(i1);" + + " }" + + " }" + + " q = 100; " + + " myfn = function() {" + + " var x = 1, z;" + + " f5();" + + " z = 5;" + + " };" + + " function f5() {" + + " var aa = 5, x;" + + " if (y === 1) {" + + " x = 100;" + + " }" + + " }" + + "}" + + "h = x + y;" + + "function g() {" + + " y = 0;" + + " { var x = 200, h;}" + + " h = y + x;" + + "}" + + "ggg = 0;"); + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new GroupVariableDeclarations(compiler); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/IgnoreCajaPropertiesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/IgnoreCajaPropertiesTest.java new file mode 100644 index 0000000..3184b46 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/IgnoreCajaPropertiesTest.java @@ -0,0 +1,144 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + + +/** + * {@link IgnoreCajaProperties} tests. + * + */ +public class IgnoreCajaPropertiesTest extends CompilerTestCase { + + private static final String EXTERNS = + "var z = {}, " + + "f = function(y) { z[y] = z[y] ? (z[y]+1) : 1; }, " + + "x, i;"; + + public IgnoreCajaPropertiesTest() { + super(EXTERNS); + } + + @Override + public void setUp() { + super.enableLineNumberCheck(false); + } + + @Override + public int getNumRepetitions() { + return 1; + } + + public void testSimpleKey() { + // Test a one-statement body. + test("for (i in x) f(i);", + "for (var JSCompiler_IgnoreCajaProperties_0 in x)" + + " if (!JSCompiler_IgnoreCajaProperties_0.match(/___$/)) {" + + " i = JSCompiler_IgnoreCajaProperties_0;" + + " { f(i); }" + + " }"); + // Test a two-statement body. + test("for (i in x) { f(i); f(i); }", + "for (var JSCompiler_IgnoreCajaProperties_0 in x)" + + " if (!JSCompiler_IgnoreCajaProperties_0.match(/___$/)) {" + + " i = JSCompiler_IgnoreCajaProperties_0;" + + " { f(i); f(i); }" + + " }"); + // Check that the counter's incrementing properly and + // that nested loops work. + test("for (i in x) for (j in y) f(i,j);", + "for (var JSCompiler_IgnoreCajaProperties_1 in x)" + + " if (!JSCompiler_IgnoreCajaProperties_1.match(/___$/)) {" + + " i = JSCompiler_IgnoreCajaProperties_1;" + + " {" + + " for (var JSCompiler_IgnoreCajaProperties_0 in y)" + + " if (!JSCompiler_IgnoreCajaProperties_0.match(/___$/)) {" + + " j = JSCompiler_IgnoreCajaProperties_0;" + + " { f(i,j); }" + + " }" + + " }" + + " }"); + } + + public void testPropertyKey() { + test("for (z.i in x) { f(z.i); f(z.i); }", + "for (var JSCompiler_IgnoreCajaProperties_0 in x) {" + + " if (!JSCompiler_IgnoreCajaProperties_0.match(/___$/)) {" + + " z.i = JSCompiler_IgnoreCajaProperties_0;" + + " { f(z.i); f(z.i); }" + + " }" + + "}"); + } + + public void testFunctionPropertyKey() { + // Note that both in the original code and the + // rewritten code, z.j() is invoked on every + // iteration of the loop. + test("for (z.j().i in x) { f(z.j().i); f(z.j().i); }", + "for (var JSCompiler_IgnoreCajaProperties_0 in x) {" + + " if (!JSCompiler_IgnoreCajaProperties_0.match(/___$/)) {" + + " z.j().i = JSCompiler_IgnoreCajaProperties_0;" + + " { f(z.j().i); f(z.j().i); }" + + " }" + + "}"); + } + + public void testVarKey() { + // Test a one-statement body. + test("for (var j in x) { f(j); }", + "for (var JSCompiler_IgnoreCajaProperties_0 in x) {" + + " if (!JSCompiler_IgnoreCajaProperties_0.match(/___$/)) {" + + " var j;" + + " j = JSCompiler_IgnoreCajaProperties_0;" + + " { f(j); }" + + " }" + + "}"); + // Test a two-statement body. + test("for (var j in x) { f(j); f(j); }", + "for (var JSCompiler_IgnoreCajaProperties_0 in x) {" + + " if (!JSCompiler_IgnoreCajaProperties_0.match(/___$/)) {" + + " var j;" + + " j = JSCompiler_IgnoreCajaProperties_0;" + + " { f(j); f(j); }" + + " }" + + "}"); + // Test two loops. + test("for (var i in x) for (var j in y) f(i,j);", + "for (var JSCompiler_IgnoreCajaProperties_1 in x)" + + " if (!JSCompiler_IgnoreCajaProperties_1.match(/___$/)) {" + + " var i;" + + " i = JSCompiler_IgnoreCajaProperties_1;" + + " {" + + " for (var JSCompiler_IgnoreCajaProperties_0 in y)" + + " if (!JSCompiler_IgnoreCajaProperties_0.match(/___$/)) {" + + " var j;" + + " j = JSCompiler_IgnoreCajaProperties_0;" + + " { f(i,j); }" + + " }" + + " }" + + " }"); + } + + public void testFourChildFor() { + test("for (i = 0; i < 10; ++i) { f(i); }", + "for (i = 0; i < 10; ++i) { f(i); }"); + } + + @Override + public CompilerPass getProcessor(Compiler compiler) { + return new IgnoreCajaProperties(compiler); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InferJSDocInfoTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InferJSDocInfoTest.java new file mode 100644 index 0000000..22eb15b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InferJSDocInfoTest.java @@ -0,0 +1,217 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + + +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.jscomp.NodeTraversal.Callback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.ObjectType; + +import java.util.Deque; + + +/** + * Tests for {@link InferJSDocInfo}. + * @author nicksantos@google.com (Nick Santos) + */ +// TODO(nicksantos): A lot of this code is duplicated from +// TypedScopeCreatorTest. We should create a common test harness for +// assertions about type information. +public class InferJSDocInfoTest extends CompilerTestCase { + + private Scope globalScope; + + @Override + public int getNumRepetitions() { + return 1; + } + + @Override + protected CompilerOptions getOptions() { + CompilerOptions options = super.getOptions(); + options.ideMode = true; + return options; + } + + private final Callback callback = new AbstractPostOrderCallback() { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + Scope s = t.getScope(); + if (s.isGlobal()) { + globalScope = s; + } + } + }; + + @Override + public CompilerPass getProcessor(final Compiler compiler) { + return new CompilerPass() { + @Override + public void process(Node externs, Node root) { + MemoizedScopeCreator scopeCreator = + new MemoizedScopeCreator(new TypedScopeCreator(compiler)); + Scope topScope = scopeCreator.createScope(root.getParent(), null); + (new TypeInferencePass( + compiler, compiler.getReverseAbstractInterpreter(), + topScope, scopeCreator)).process(externs, root); + NodeTraversal t = new NodeTraversal( + compiler, callback, scopeCreator); + t.traverseRoots(Lists.newArrayList(externs, root)); + (new InferJSDocInfo(compiler)).process(externs, root); + } + }; + } + + public void testNativeCtor() { + testSame( + "/** Object. \n * @param {*=} x \n * @constructor */ " + + "function Object(x) {};", + "var x = new Object();" + + "/** Another object. */ var y = new Object();", null); + assertEquals( + "Object.", + findGlobalNameType("x").getJSDocInfo().getBlockDescription()); + assertEquals( + "Object.", + findGlobalNameType("y").getJSDocInfo().getBlockDescription()); + assertEquals( + "Object.", + globalScope.getVar("y").getType().getJSDocInfo().getBlockDescription()); + } + + public void testStructuralFunctions() { + testSame( + "/** Object. \n * @param {*=} x \n * @constructor */ " + + "function Object(x) {};", + "/** Function. \n * @param {*} x */ " + + "function fn(x) {};" + + "var goog = {};" + + "/** Another object. \n * @type {Object} */ goog.x = new Object();" + + "/** Another function. \n * @param {number} x */ goog.y = fn;", null); + assertEquals( + "(Object|null)", + globalScope.getVar("goog.x").getType().toString()); + assertEquals( + "Object.", + globalScope.getVar("goog.x").getType().restrictByNotNullOrUndefined() + .getJSDocInfo().getBlockDescription()); + assertEquals( + "Another function.", + globalScope.getVar("goog.y").getType() + .getJSDocInfo().getBlockDescription()); + } + + public void testInstanceObject() { + // Asserts that no property gets attached to the instance object. + testSame( + "/** @constructor */ function Foo() {}" + + "var f = new Foo();" + + "/** @type {number} */ f.bar = 4;"); + ObjectType type = (ObjectType) globalScope.getVar("f").getType(); + assertEquals("Foo", type.toString()); + assertFalse(type.hasProperty("bar")); + assertNull(type.getOwnPropertyJSDocInfo("bar")); + } + + public void testInterface() { + testSame( + "/** An interface. \n * @interface */ function Foo() {}" + + "var f = new Foo();" + + "/** @type {number} */ f.bar = 4;"); + ObjectType type = (ObjectType) globalScope.getVar("Foo").getType(); + assertEquals( + "An interface.", + type.getJSDocInfo().getBlockDescription()); + } + + public void testNamespacedCtor() { + testSame( + "var goog = {};" + + "/** Hello! \n * @constructor */ goog.Foo = function() {};" + + "goog.Foo.bar = goog.Foo;" + + "/** Bye! \n * @param {string=} opt_x */" + + "goog.Foo.prototype.baz = goog.Foo;" + + "/** Blargh */ var x = new goog.Foo();"); + assertEquals( + "Hello!", + findGlobalNameType("x").getJSDocInfo().getBlockDescription()); + assertEquals( + "Hello!", + findGlobalNameType("goog.Foo").getJSDocInfo().getBlockDescription()); + assertEquals( + "Hello!", + findGlobalNameType( + "goog.Foo.bar").getJSDocInfo().getBlockDescription()); + + assertEquals( + "Hello!", + findGlobalNameType( + "goog.Foo.prototype.baz").getJSDocInfo().getBlockDescription()); + + ObjectType proto = (ObjectType) findGlobalNameType("goog.Foo.prototype"); + assertEquals( + "Bye!", + proto.getPropertyType("baz").getJSDocInfo().getBlockDescription()); + } + + public void testAbstractMethod() { + testSame( + "/** Abstract method. \n * @type {!Function} */ var abstractMethod;" + + "/** @constructor */ function Foo() {}" + + "/** Block description. \n * @param {number} x */" + + "Foo.prototype.bar = abstractMethod;"); + FunctionType abstractMethod = + (FunctionType) findGlobalNameType("abstractMethod"); + assertNull(abstractMethod.getJSDocInfo()); + + FunctionType ctor = (FunctionType) findGlobalNameType("Foo"); + ObjectType proto = ctor.getInstanceType().getImplicitPrototype(); + FunctionType method = (FunctionType) proto.getPropertyType("bar"); + assertEquals( + "Block description.", + method.getJSDocInfo().getBlockDescription()); + assertEquals( + "Block description.", + proto.getOwnPropertyJSDocInfo("bar").getBlockDescription()); + } + + private JSType findGlobalNameType(String name) { + return findNameType(name, globalScope); + } + + private JSType findNameType(String name, Scope scope) { + Node root = scope.getRootNode(); + Deque queue = Lists.newLinkedList(); + queue.push(root); + while (!queue.isEmpty()) { + Node current = queue.pop(); + if (name.equals(current.getQualifiedName()) && + current.getJSType() != null) { + return current.getJSType(); + } + + for (Node child : current.children()) { + queue.push(child); + } + } + return null; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlineCostEstimatorTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlineCostEstimatorTest.java new file mode 100644 index 0000000..feb3f64 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlineCostEstimatorTest.java @@ -0,0 +1,74 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +import junit.framework.TestCase; + + +/** + * Unit test for {@link InlineCostEstimator}. + * @author johnlenz@google.com (John Lenz) + */ +public class InlineCostEstimatorTest extends TestCase { + + static Node parse(String js) { + Compiler compiler = new Compiler(); + Node n = compiler.parseTestCode(js); + assertEquals(0, compiler.getErrorCount()); + return n; + } + + static String minimize(String js) { + CompilerOptions options = new CompilerOptions(); + options.setLineLengthThreshold(Integer.MAX_VALUE); + return new CodePrinter.Builder(parse(js)). + setCompilerOptions(options). + build(); + } + + static long cost(String js) { + return InlineCostEstimator.getCost(parse(js)); + } + + public void testCost() { + checkCost("1", "1"); + checkCost("true", "1"); + checkCost("false", "1"); + checkCost("a", "xx"); + checkCost("a + b", "xx+xx"); + checkCost("foo()", "xx()"); + checkCost("foo(a,b)", "xx(xx,xx)"); + checkCost("10 + foo(a,b)", "0+xx(xx,xx)"); + checkCost("1 + foo(a,b)", "1+xx(xx,xx)"); + checkCost("a ? 1 : 0", "xx?1:0"); + checkCost("a.b", "xx.xx"); + checkCost("new Obj()", "new xx"); + checkCost("function a() {return \"monkey\"}", + "function xx(){return\"monkey\"}"); + } + + private void checkCost(String source, String example) { + + // The example string should have been minified already. + assertEquals(minimize(example), example); + + // cost estimate should be the same as the length of the example string. + assertEquals(example.length(), cost(source)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlineFunctionsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlineFunctionsTest.java new file mode 100644 index 0000000..221189d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlineFunctionsTest.java @@ -0,0 +1,2376 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + + +/** + * Inline function tests. + * @author johnlenz@google.com (john lenz) + */ +public class InlineFunctionsTest extends CompilerTestCase { + boolean allowGlobalFunctionInlining = true; + boolean allowBlockInlining = true; + final boolean allowExpressionDecomposition = true; + final boolean allowFunctionExpressionInlining = true; + final boolean allowLocalFunctionInlining = true; + boolean assumeStrictThis = false; + boolean assumeMinimumCapture = false; + + public InlineFunctionsTest() { + this.enableNormalize(); + this.enableMarkNoSideEffects(); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + super.enableLineNumberCheck(true); + allowGlobalFunctionInlining = true; + allowBlockInlining = true; + assumeStrictThis = false; + assumeMinimumCapture = false; + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + compiler.resetUniqueNameId(); + return new InlineFunctions( + compiler, + compiler.getUniqueNameIdSupplier(), + allowGlobalFunctionInlining, + allowLocalFunctionInlining, + allowBlockInlining, + assumeStrictThis, + assumeMinimumCapture); + } + + /** + * Returns the number of times the pass should be run before results are + * verified. + */ + @Override + protected int getNumRepetitions() { + // Some inlining can only be done in multiple passes. + return 3; + } + + public void testInlineEmptyFunction1() { + // Empty function, no params. + test("function foo(){}" + + "foo();", + "void 0;"); + } + + public void testInlineEmptyFunction2() { + // Empty function, params with no side-effects. + test("function foo(){}" + + "foo(1, new Date, function(){});", + "void 0;"); + } + + public void testInlineEmptyFunction3() { + // Empty function, multiple references. + test("function foo(){}" + + "foo();foo();foo();", + "void 0;void 0;void 0"); + } + + public void testInlineEmptyFunction4() { + // Empty function, params with side-effects forces block inlining. + test("function foo(){}" + + "foo(x());", + "{var JSCompiler_inline_anon_param_0=x();}"); + } + + public void testInlineEmptyFunction5() { + // Empty function, call params with side-effects in expression can not + // be inlined. + allowBlockInlining = false; + testSame("function foo(){}" + + "foo(x());"); + } + + public void testInlineFunctions1() { + // As simple a test as we can get. + test("function foo(){ return 4 }" + + "foo();", + "4"); + } + + public void testInlineFunctions2() { + // inline simple constants + // NOTE: CD is not inlined. + test("var t;var AB=function(){return 4};" + + "function BC(){return 6;}" + + "CD=function(x){return x + 5};x=CD(3);y=AB();z=BC();", + "var t;CD=function(x){return x+5};x=CD(3);y=4;z=6" + ); + } + + public void testInlineFunctions3() { + // inline simple constants + test("var t;var AB=function(){return 4};" + + "function BC(){return 6;}" + + "var CD=function(x){return x + 5};x=CD(3);y=AB();z=BC();", + "var t;x=3+5;y=4;z=6"); + } + + public void testInlineFunctions4() { + // don't inline if there are multiple definitions (need DFA for that). + test("var t; var AB = function() { return 4 }; " + + "function BC() { return 6; }" + + "CD = 0;" + + "CD = function(x) { return x + 5 }; x = CD(3); y = AB(); z = BC();", + + "var t;CD=0;CD=function(x){return x+5};x=CD(3);y=4;z=6"); + } + + public void testInlineFunctions5() { + // inline additions + test("var FOO_FN=function(x,y) { return \"de\" + x + \"nu\" + y };" + + "var a = FOO_FN(\"ez\", \"ts\")", + + "var a=\"de\"+\"ez\"+\"nu\"+\"ts\""); + } + + public void testInlineFunctions6() { + // more complex inlines + test("function BAR_FN(x, y, z) { return z(foo(x + y)) }" + + "alert(BAR_FN(1, 2, baz))", + + "alert(baz(foo(1+2)))"); + } + + public void testInlineFunctions7() { + // inlines appearing multiple times + test("function FN(x,y,z){return x+x+y}" + + "var b=FN(1,2,3)", + + "var b=1+1+2"); + } + + public void testInlineFunctions8() { + // check correct parenthesization + test("function MUL(x,y){return x*y}function ADD(x,y){return x+y}" + + "var a=1+MUL(2,3);var b=2*ADD(3,4)", + + "var a=1+2*3;var b=2*(3+4)"); + } + + public void testInlineFunctions9() { + // don't inline if the input parameter is modified. + test("function INC(x){return x++}" + + "var y=INC(i)", + "var y;{var x$$inline_0=i;" + + "y=x$$inline_0++}"); + } + + public void testInlineFunctions10() { + test("function INC(x){return x++}" + + "var y=INC(i);y=INC(i)", + "var y;" + + "{var x$$inline_0=i;" + + "y=x$$inline_0++}" + + "{var x$$inline_2=i;" + + "y=x$$inline_2++}"); + } + + public void testInlineFunctions11() { + test("function f(x){return x}" + + "var y=f(i)", + "var y=i"); + } + + public void testInlineFunctions12() { + // don't inline if the input parameter has side-effects. + allowBlockInlining = false; + test("function f(x){return x}" + + "var y=f(i)", + "var y=i"); + testSame("function f(x){return x}" + + "var y=f(i++)"); + } + + public void testInlineFunctions13() { + // inline as block if the input parameter has side-effects. + test("function f(x){return x}" + + "var y=f(i++)", + "var y;{var x$$inline_0=i++;y=x$$inline_0}"); + } + + public void testInlineFunctions14() { + // don't remove functions that are referenced on other ways + test("function FOO(x){return x}var BAR=function(y){return y}" + + ";b=FOO;a(BAR);x=FOO(1);y=BAR(2)", + + "function FOO(x){return x}var BAR=function(y){return y}" + + ";b=FOO;a(BAR);x=1;y=2"); + } + + public void testInlineFunctions15a() { + // closure factories: do inline into global scope. + test("function foo(){return function(a){return a+1}}" + + "var b=function(){return c};" + + "var d=b()+foo()", + + "var d=c+function(a){return a+1}"); + } + + public void testInlineFunctions15b() { + assumeMinimumCapture = false; + + // closure factories: don't inline closure with locals into global scope. + test("function foo(){var x;return function(a){return a+1}}" + + "var b=function(){return c};" + + "var d=b()+foo()", + + "function foo(){var x;return function(a){return a+1}}" + + "var d=c+foo()"); + + assumeMinimumCapture = true; + + test("function foo(){var x;return function(a){return a+1}}" + + "var b=function(){return c};" + + "var d=b()+foo()", + + "var JSCompiler_temp_const$$0 = c;\n" + + "var JSCompiler_inline_result$$1;\n" + + "{\n" + + "var x$$inline_2;\n" + + "JSCompiler_inline_result$$1 = " + + " function(a$$inline_3){ return a$$inline_3+1 };\n" + + "}" + + "var d=JSCompiler_temp_const$$0 + JSCompiler_inline_result$$1"); + } + + public void testInlineFunctions15c() { + assumeMinimumCapture = false; + + // closure factories: don't inline into non-global scope. + test("function foo(){return function(a){return a+1}}" + + "var b=function(){return c};" + + "function _x(){ var d=b()+foo() }", + + "function foo(){return function(a){return a+1}}" + + "function _x(){ var d=c+foo() }"); + + assumeMinimumCapture = true; + + // closure factories: don't inline into non-global scope. + test("function foo(){return function(a){return a+1}}" + + "var b=function(){return c};" + + "function _x(){ var d=b()+foo() }", + + "function _x(){var d=c+function(a){return a+1}}"); + + } + + public void testInlineFunctions15d() { + assumeMinimumCapture = false; + + // closure factories: don't inline functions with vars. + test("function foo(){var x; return function(a){return a+1}}" + + "var b=function(){return c};" + + "function _x(){ var d=b()+foo() }", + + "function foo(){var x; return function(a){return a+1}}" + + "function _x(){ var d=c+foo() }"); + + assumeMinimumCapture = true; + + // closure factories: don't inline functions with vars. + test("function foo(){var x; return function(a){return a+1}}" + + "var b=function(){return c};" + + "function _x(){ var d=b()+foo() }", + + "function _x() { \n" + + " var JSCompiler_temp_const$$0 = c;\n" + + " var JSCompiler_inline_result$$1;\n" + + " {\n" + + " var x$$inline_2;\n" + + " JSCompiler_inline_result$$1 = " + + " function(a$$inline_3) {return a$$inline_3+1};\n" + + " }\n" + + " var d = JSCompiler_temp_const$$0+JSCompiler_inline_result$$1\n" + + "}"); + } + + public void testInlineFunctions16a() { + assumeMinimumCapture = false; + + testSame("function foo(b){return window.bar(function(){c(b)})}" + + "var d=foo(e)"); + + assumeMinimumCapture = true; + + test( + "function foo(b){return window.bar(function(){c(b)})}" + + "var d=foo(e)", + "var d;{var b$$inline_0=e;" + + "d=window.bar(function(){c(b$$inline_0)})}"); + } + + public void testInlineFunctions16b() { + test("function foo(){return window.bar(function(){c()})}" + + "var d=foo(e)", + "var d=window.bar(function(){c()})"); + } + + public void testInlineFunctions17() { + // don't inline recursive functions + testSame("function foo(x){return x*x+foo(3)}var bar=foo(4)"); + } + + public void testInlineFunctions18() { + // TRICKY ... test nested inlines + allowBlockInlining = false; + test("function foo(a, b){return a+b}" + + "function bar(d){return c}" + + "var d=foo(bar(1),e)", + "var d=c+e"); + } + + public void testInlineFunctions19() { + // TRICKY ... test nested inlines + // with block inlining possible + test("function foo(a, b){return a+b}" + + "function bar(d){return c}" + + "var d=foo(bar(1),e)", + "var d;{d=c+e}"); + } + + public void testInlineFunctions20() { + // Make sure both orderings work + allowBlockInlining = false; + test("function foo(a, b){return a+b}" + + "function bar(d){return c}" + + "var d=bar(foo(1,e));", + "var d=c"); + } + + public void testInlineFunctions21() { + // with block inlining possible + test("function foo(a, b){return a+b}" + + "function bar(d){return c}" + + "var d=bar(foo(1,e))", + "var d;{d=c}"); + } + + public void testInlineFunctions22() { + // Another tricky case ... test nested compiler inlines + test("function plex(a){if(a) return 0;else return 1;}" + + "function foo(a, b){return bar(a+b)}" + + "function bar(d){return plex(d)}" + + "var d=foo(1,2)", + + "var d;{JSCompiler_inline_label_plex_1:{" + + "if(1+2){" + + "d=0;break JSCompiler_inline_label_plex_1}" + + "else{" + + "d=1;break JSCompiler_inline_label_plex_1}d=void 0}}"); + } + + public void testInlineFunctions23() { + // Test both orderings again + test("function complex(a){if(a) return 0;else return 1;}" + + "function bar(d){return complex(d)}" + + "function foo(a, b){return bar(a+b)}" + + "var d=foo(1,2)", + + "var d;{JSCompiler_inline_label_complex_1:{" + + "if(1+2){" + + "d=0;break JSCompiler_inline_label_complex_1" + + "}else{" + + "d=1;break JSCompiler_inline_label_complex_1" + + "}d=void 0}}"); + } + + public void testInlineFunctions24() { + // Don't inline functions with 'arguments' or 'this' + testSame("function foo(x){return this}foo(1)"); + } + + public void testInlineFunctions25() { + testSame("function foo(){return arguments[0]}foo()"); + } + + public void testInlineFunctions26() { + // Don't inline external functions + testSame("function _foo(x){return x}_foo(1)"); + } + + public void testInlineFunctions27() { + test("var window = {}; function foo(){window.bar++; return 3;}" + + "var x = {y: 1, z: foo(2)};", + "var window={};" + + "var JSCompiler_inline_result$$0;" + + "{" + + " window.bar++;" + + " JSCompiler_inline_result$$0 = 3;" + + "}" + + "var x = {y: 1, z: JSCompiler_inline_result$$0};"); + } + + public void testInlineFunctions28() { + test("var window = {}; function foo(){window.bar++; return 3;}" + + "var x = {y: alert(), z: foo(2)};", + "var window = {};" + + "var JSCompiler_temp_const$$0 = alert();" + + "var JSCompiler_inline_result$$1;" + + "{" + + " window.bar++;" + + " JSCompiler_inline_result$$1 = 3;}" + + "var x = {" + + " y: JSCompiler_temp_const$$0," + + " z: JSCompiler_inline_result$$1" + + "};"); + } + + public void testInlineFunctions29() { + test("var window = {}; function foo(){window.bar++; return 3;}" + + "var x = {a: alert(), b: alert2(), c: foo(2)};", + "var window = {};" + + "var JSCompiler_temp_const$$1 = alert();" + + "var JSCompiler_temp_const$$0 = alert2();" + + "var JSCompiler_inline_result$$2;" + + "{" + + " window.bar++;" + + " JSCompiler_inline_result$$2 = 3;}" + + "var x = {" + + " a: JSCompiler_temp_const$$1," + + " b: JSCompiler_temp_const$$0," + + " c: JSCompiler_inline_result$$2" + + "};"); + } + + public void testInlineFunctions30() { + // As simple a test as we can get. + testSame("function foo(){ return eval() }" + + "foo();"); + } + + public void testInlineFunctions31() { + // Don't introduce a duplicate label in the same scope + test("function foo(){ lab:{4;} }" + + "lab:{foo();}", + "lab:{{JSCompiler_inline_label_0:{4}}}"); + } + + public void testMixedModeInlining1() { + // Base line tests, direct inlining + test("function foo(){return 1}" + + "foo();", + "1;"); + } + + public void testMixedModeInlining2() { + // Base line tests, block inlining. Block inlining is needed by + // possible-side-effect parameter. + test("function foo(){return 1}" + + "foo(x());", + "{var JSCompiler_inline_anon_param_0=x();1}"); + } + + public void testMixedModeInlining3() { + // Inline using both modes. + test("function foo(){return 1}" + + "foo();foo(x());", + "1;{var JSCompiler_inline_anon_param_0=x();1}"); + } + + public void testMixedModeInlining4() { + // Inline using both modes. Alternating. Second call of each type has + // side-effect-less parameter, this is thrown away. + test("function foo(){return 1}" + + "foo();foo(x());" + + "foo(1);foo(1,x());", + "1;{var JSCompiler_inline_anon_param_0=x();1}" + + "1;{var JSCompiler_inline_anon_param_4=x();1}"); + } + + public void testMixedModeInliningCosting1() { + // Inline using both modes. Costing estimates. + + // Base line. + test( + "function foo(a,b){return a+b+a+b+4+5+6+7+8+9+1+2+3+4+5}" + + "foo(1,2);" + + "foo(2,3)", + + "1+2+1+2+4+5+6+7+8+9+1+2+3+4+5;" + + "2+3+2+3+4+5+6+7+8+9+1+2+3+4+5"); + } + + public void testMixedModeInliningCosting2() { + // Don't inline here because the function definition can not be eliminated. + // TODO(johnlenz): Should we add constant removing to the unit test? + testSame( + "function foo(a,b){return a+b+a+b+4+5+6+7+8+9+1+2+3+4+5}" + + "foo(1,2);" + + "foo(2,3,x())"); + } + + public void testMixedModeInliningCosting3() { + // Do inline here because the function definition can be eliminated. + test( + "function foo(a,b){return a+b+a+b+4+5+6+7+8+9+1+2+3+10}" + + "foo(1,2);" + + "foo(2,3,x())", + + "1+2+1+2+4+5+6+7+8+9+1+2+3+10;" + + "{var JSCompiler_inline_anon_param_2=x();" + + "2+3+2+3+4+5+6+7+8+9+1+2+3+10}"); + } + + public void testMixedModeInliningCosting4() { + // Threshold test. + testSame( + "function foo(a,b){return a+b+a+b+4+5+6+7+8+9+1+2+3+4+101}" + + "foo(1,2);" + + "foo(2,3,x())"); + } + + public void testNoInlineIfParametersModified1() { + // Assignment + test("function f(x){return x=1}f(undefined)", + "{var x$$inline_0=undefined;" + + "x$$inline_0=1}"); + } + + public void testNoInlineIfParametersModified2() { + test("function f(x){return (x)=1;}f(2)", + "{var x$$inline_0=2;" + + "x$$inline_0=1}"); + } + + public void testNoInlineIfParametersModified3() { + // Assignment variant. + test("function f(x){return x*=2}f(2)", + "{var x$$inline_0=2;" + + "x$$inline_0*=2}"); + } + + public void testNoInlineIfParametersModified4() { + // Assignment in if. + test("function f(x){return x?(x=2):0}f(2)", + "{var x$$inline_0=2;" + + "x$$inline_0?(" + + "x$$inline_0=2):0}"); + } + + public void testNoInlineIfParametersModified5() { + // Assignment in if, multiple params + test("function f(x,y){return x?(y=2):0}f(2,undefined)", + "{var y$$inline_1=undefined;2?(" + + "y$$inline_1=2):0}"); + } + + public void testNoInlineIfParametersModified6() { + test("function f(x,y){return x?(y=2):0}f(2)", + "{var y$$inline_1=void 0;2?(" + + "y$$inline_1=2):0}"); + } + + public void testNoInlineIfParametersModified7() { + // Increment + test("function f(a){return++a<++a}f(1)", + "{var a$$inline_0=1;" + + "++a$$inline_0<" + + "++a$$inline_0}"); + } + + public void testNoInlineIfParametersModified8() { + // OK, object parameter modified. + test("function f(a){return a.x=2}f(o)", "o.x=2"); + } + + public void testNoInlineIfParametersModified9() { + // OK, array parameter modified. + test("function f(a){return a[2]=2}f(o)", "o[2]=2"); + } + + public void testInlineNeverPartialSubtitution1() { + test("function f(z){return x.y.z;}f(1)", + "x.y.z"); + } + + public void testInlineNeverPartialSubtitution2() { + test("function f(z){return x.y[z];}f(a)", + "x.y[a]"); + } + + public void testInlineNeverMutateConstants() { + test("function f(x){return x=1}f(undefined)", + "{var x$$inline_0=undefined;" + + "x$$inline_0=1}"); + } + + public void testInlineNeverOverrideNewValues() { + test("function f(a){return++a<++a}f(1)", + "{var a$$inline_0=1;" + + "++a$$inline_0<++a$$inline_0}"); + } + + public void testInlineMutableArgsReferencedOnce() { + test("function foo(x){return x;}foo([])", "[]"); + } + + public void testNoInlineMutableArgs1() { + allowBlockInlining = false; + testSame("function foo(x){return x+x} foo([])"); + } + + public void testNoInlineMutableArgs2() { + allowBlockInlining = false; + testSame("function foo(x){return x+x} foo(new Date)"); + } + + public void testNoInlineMutableArgs3() { + allowBlockInlining = false; + testSame("function foo(x){return x+x} foo(true&&new Date)"); + } + + public void testNoInlineMutableArgs4() { + allowBlockInlining = false; + testSame("function foo(x){return x+x} foo({})"); + } + + public void testInlineBlockMutableArgs1() { + test("function foo(x){x+x}foo([])", + "{var x$$inline_0=[];" + + "x$$inline_0+x$$inline_0}"); + } + + public void testInlineBlockMutableArgs2() { + test("function foo(x){x+x}foo(new Date)", + "{var x$$inline_0=new Date;" + + "x$$inline_0+x$$inline_0}"); + } + + public void testInlineBlockMutableArgs3() { + test("function foo(x){x+x}foo(true&&new Date)", + "{var x$$inline_0=true&&new Date;" + + "x$$inline_0+x$$inline_0}"); + } + + public void testInlineBlockMutableArgs4() { + test("function foo(x){x+x}foo({})", + "{var x$$inline_0={};" + + "x$$inline_0+x$$inline_0}"); + } + + public void testShadowVariables1() { + // The Normalize pass now guarantees that that globals are never shadowed + // by locals. + + // "foo" is inlined here as its parameter "a" doesn't conflict. + // "bar" is assigned a new name. + test("var a=0;" + + "function foo(a){return 3+a}" + + "function bar(){var a=foo(4)}" + + "bar();", + + "var a=0;" + + "{var a$$inline_0=3+4}"); + } + + public void testShadowVariables2() { + // "foo" is inlined here as its parameter "a" doesn't conflict. + // "bar" is inlined as its uses global "a", and does introduce any new + // globals. + test("var a=0;" + + "function foo(a){return 3+a}" + + "function bar(){a=foo(4)}" + + "bar()", + + "var a=0;" + + "{a=3+4}"); + } + + public void testShadowVariables3() { + // "foo" is inlined into exported "_bar", aliasing foo's "a". + test("var a=0;" + + "function foo(){var a=2;return 3+a}" + + "function _bar(){a=foo()}", + + "var a=0;" + + "function _bar(){{var a$$inline_0=2;" + + "a=3+a$$inline_0}}"); + } + + public void testShadowVariables4() { + // "foo" is inlined. + // block access to global "a". + test("var a=0;" + + "function foo(){return 3+a}" + + "function _bar(a){a=foo(4)+a}", + + "var a=0;function _bar(a$$1){" + + "a$$1=" + + "3+a+a$$1}"); + } + + public void testShadowVariables5() { + // Can't yet inline multiple statements functions into expressions + // (though some are possible using the COMMA operator). + allowBlockInlining = false; + testSame("var a=0;" + + "function foo(){var a=4;return 3+a}" + + "function _bar(a){a=foo(4)+a}"); + } + + public void testShadowVariables6() { + test("var a=0;" + + "function foo(){var a=4;return 3+a}" + + "function _bar(a){a=foo(4)}", + + "var a=0;function _bar(a$$2){{" + + "var a$$inline_0=4;" + + "a$$2=3+a$$inline_0}}"); + } + + public void testShadowVariables7() { + assumeMinimumCapture = false; + test("var a=3;" + + "function foo(){return a}" + + "(function(){var a=5;(function(){foo()})()})()", + "var a=3;" + + "{var a$$inline_0=5;{a}}" + ); + + assumeMinimumCapture = true; + test("var a=3;" + + "function foo(){return a}" + + "(function(){var a=5;(function(){foo()})()})()", + "var a=3;" + + "{var a$$inline_1=5;{a}}" + ); + } + + public void testShadowVariables8() { + // this should be inlined + test("var a=0;" + + "function foo(){return 3}" + + "function _bar(){var a=foo()}", + + "var a=0;" + + "function _bar(){var a=3}"); + } + + public void testShadowVariables9() { + // this should be inlined too [even if the global is not declared] + test("function foo(){return 3}" + + "function _bar(){var a=foo()}", + + "function _bar(){var a=3}"); + } + + public void testShadowVariables10() { + // callee var must be renamed. + test("var a;function foo(){return a}" + + "function _bar(){var a=foo()}", + "var a;function _bar(){var a$$1=a}"); + } + + public void testShadowVariables11() { + // The call has a local variable + // which collides with the function being inlined + test("var a=0;var b=1;" + + "function foo(){return a+a}" + + "function _bar(){var a=foo();alert(a)}", + "var a=0;var b=1;" + + "function _bar(){var a$$1=a+a;" + + "alert(a$$1)}" + ); + } + + public void testShadowVariables12() { + // 2 globals colliding + test("var a=0;var b=1;" + + "function foo(){return a+b}" + + "function _bar(){var a=foo(),b;alert(a)}", + "var a=0;var b=1;" + + "function _bar(){var a$$1=a+b," + + "b$$1;" + + "alert(a$$1)}"); + } + + public void testShadowVariables13() { + // The only change is to remove the collision + test("var a=0;var b=1;" + + "function foo(){return a+a}" + + "function _bar(){var c=foo();alert(c)}", + + "var a=0;var b=1;" + + "function _bar(){var c=a+a;alert(c)}"); + } + + public void testShadowVariables14() { + // There is a collision even though it is not read. + test("var a=0;var b=1;" + + "function foo(){return a+b}" + + "function _bar(){var c=foo(),b;alert(c)}", + "var a=0;var b=1;" + + "function _bar(){var c=a+b," + + "b$$1;alert(c)}"); + } + + public void testShadowVariables15() { + // Both parent and child reference a global + test("var a=0;var b=1;" + + "function foo(){return a+a}" + + "function _bar(){var c=foo();alert(c+a)}", + + "var a=0;var b=1;" + + "function _bar(){var c=a+a;alert(c+a)}"); + } + + public void testShadowVariables16() { + assumeMinimumCapture = false; + // Inline functions defined as a child of the CALL node. + test("var a=3;" + + "function foo(){return a}" + + "(function(){var a=5;(function(){foo()})()})()", + "var a=3;" + + "{var a$$inline_0=5;{a}}" + ); + + assumeMinimumCapture = true; + // Inline functions defined as a child of the CALL node. + test("var a=3;" + + "function foo(){return a}" + + "(function(){var a=5;(function(){foo()})()})()", + "var a=3;" + + "{var a$$inline_1=5;{a}}" + ); + + } + + public void testShadowVariables17() { + test("var a=0;" + + "function bar(){return a+a}" + + "function foo(){return bar()}" + + "function _goo(){var a=2;var x=foo();}", + + "var a=0;" + + "function _goo(){var a$$1=2;var x=a+a}"); + } + + public void testShadowVariables18() { + test("var a=0;" + + "function bar(){return a+a}" + + "function foo(){var a=3;return bar()}" + + "function _goo(){var a=2;var x=foo();}", + + "var a=0;" + + "function _goo(){var a$$2=2;var x;" + + "{var a$$inline_0=3;x=a+a}}"); + } + + public void testCostBasedInlining1() { + testSame( + "function foo(a){return a}" + + "foo=new Function(\"return 1\");" + + "foo(1)"); + } + + public void testCostBasedInlining2() { + // Baseline complexity tests. + // Single call, function not removed. + test( + "function foo(a){return a}" + + "var b=foo;" + + "function _t1(){return foo(1)}", + + "function foo(a){return a}" + + "var b=foo;" + + "function _t1(){return 1}"); + } + + public void testCostBasedInlining3() { + // Two calls, function not removed. + test( + "function foo(a,b){return a+b}" + + "var b=foo;" + + "function _t1(){return foo(1,2)}" + + "function _t2(){return foo(2,3)}", + + "function foo(a,b){return a+b}" + + "var b=foo;" + + "function _t1(){return 1+2}" + + "function _t2(){return 2+3}"); + } + + public void testCostBasedInlining4() { + // Two calls, function not removed. + // Here there isn't enough savings to justify inlining. + testSame( + "function foo(a,b){return a+b+a+b}" + + "var b=foo;" + + "function _t1(){return foo(1,2)}" + + "function _t2(){return foo(2,3)}"); + } + + public void testCostBasedInlining5() { + // Here there is enough savings to justify inlining. + test( + "function foo(a,b){return a+b+a+b}" + + "function _t1(){return foo(1,2)}" + + "function _t2(){return foo(2,3)}", + + "function _t1(){return 1+2+1+2}" + + "function _t2(){return 2+3+2+3}"); + } + + public void testCostBasedInlining6() { + // Here we have a threshold test. + // Do inline here: + test( + "function foo(a,b){return a+b+a+b+a+b+a+b+4+5+6+7+8+9+1+2+3+4+5}" + + "function _t1(){return foo(1,2)}" + + "function _t2(){return foo(2,3)}", + + "function _t1(){return 1+2+1+2+1+2+1+2+4+5+6+7+8+9+1+2+3+4+5}" + + "function _t2(){return 2+3+2+3+2+3+2+3+4+5+6+7+8+9+1+2+3+4+5}"); + } + + public void testCostBasedInlining7() { + // Don't inline here (not enough savings): + testSame( + "function foo(a,b){" + + " return a+b+a+b+a+b+a+b+4+5+6+7+8+9+1+2+3+4+5+6}" + + "function _t1(){return foo(1,2)}" + + "function _t2(){return foo(2,3)}"); + } + + public void testCostBasedInlining8() { + // Verify multiple references in the same statement: + // Here "f" is not known to be removable, as it is a used as parameter + // and is not known to be side-effect free. The first call to f() can + // not be inlined on the first pass (as the call to f() as a parameter + // prevents this). However, the call to f() would be inlinable, if it + // is small enough to be inlined without removing the function declaration. + // but it is not in this first test. + allowBlockInlining = false; + testSame("function f(a){return 1 + a + a;}" + + "var a = f(f(1));"); + } + + public void testCostBasedInlining9() { + // Here both direct and block inlining is used. The call to f as a + // parameter is inlined directly, which the call to f with f as a parameter + // is inlined using block inlining. + test("function f(a){return 1 + a + a;}" + + "var a = f(f(1));", + "var a;" + + "{var a$$inline_0=1+1+1;" + + "a=1+a$$inline_0+a$$inline_0}"); + } + + public void testCostBasedInlining10() { + // But it is small enough here, and on the second iteration, the remaining + // call to f() is inlined, as there is no longer a possible side-effect-ing + // parameter. + allowBlockInlining = false; + test("function f(a){return a + a;}" + + "var a = f(f(1));", + "var a= 1+1+(1+1);"); + } + + public void testCostBasedInlining11() { + // With block inlining + test("function f(a){return a + a;}" + + "var a = f(f(1))", + "var a;" + + "{var a$$inline_0=1+1;" + + "a=a$$inline_0+a$$inline_0}"); + } + + public void testCostBasedInlining12() { + test("function f(a){return 1 + a + a;}" + + "var a = f(1) + f(2);", + + "var a=1+1+1+(1+2+2)"); + } + + public void testCostBasedInliningComplex1() { + testSame( + "function foo(a){a()}" + + "foo=new Function(\"return 1\");" + + "foo(1)"); + } + + public void testCostBasedInliningComplex2() { + // Baseline complexity tests. + // Single call, function not removed. + test( + "function foo(a){a()}" + + "var b=foo;" + + "function _t1(){foo(x)}", + + "function foo(a){a()}" + + "var b=foo;" + + "function _t1(){{x()}}"); + } + + public void testCostBasedInliningComplex3() { + // Two calls, function not removed. + test( + "function foo(a,b){a+b}" + + "var b=foo;" + + "function _t1(){foo(1,2)}" + + "function _t2(){foo(2,3)}", + + "function foo(a,b){a+b}" + + "var b=foo;" + + "function _t1(){{1+2}}" + + "function _t2(){{2+3}}"); + } + + public void testCostBasedInliningComplex4() { + // Two calls, function not removed. + // Here there isn't enough savings to justify inlining. + testSame( + "function foo(a,b){a+b+a+b}" + + "var b=foo;" + + "function _t1(){foo(1,2)}" + + "function _t2(){foo(2,3)}"); + } + + public void testCostBasedInliningComplex5() { + // Here there is enough savings to justify inlining. + test( + "function foo(a,b){a+b+a+b}" + + "function _t1(){foo(1,2)}" + + "function _t2(){foo(2,3)}", + + "function _t1(){{1+2+1+2}}" + + "function _t2(){{2+3+2+3}}"); + } + + public void testCostBasedInliningComplex6() { + // Here we have a threshold test. + // Do inline here: + test( + "function foo(a,b){a+b+a+b+a+b+a+b+4+5+6+7+8+9+1}" + + "function _t1(){foo(1,2)}" + + "function _t2(){foo(2,3)}", + + "function _t1(){{1+2+1+2+1+2+1+2+4+5+6+7+8+9+1}}" + + "function _t2(){{2+3+2+3+2+3+2+3+4+5+6+7+8+9+1}}"); + } + + public void testCostBasedInliningComplex7() { + // Don't inline here (not enough savings): + testSame( + "function foo(a,b){a+b+a+b+a+b+a+b+4+5+6+7+8+9+1+2}" + + "function _t1(){foo(1,2)}" + + "function _t2(){foo(2,3)}"); + } + + public void testCostBasedInliningComplex8() { + // Verify multiple references in the same statement. + testSame("function _f(a){1+a+a}" + + "a=_f(1)+_f(1)"); + } + + public void testCostBasedInliningComplex9() { + test("function f(a){1 + a + a;}" + + "f(1);f(2);", + "{1+1+1}{1+2+2}"); + } + + public void testDoubleInlining1() { + allowBlockInlining = false; + test("var foo = function(a) { return getWindow(a); };" + + "var bar = function(b) { return b; };" + + "foo(bar(x));", + "getWindow(x)"); + } + + public void testDoubleInlining2() { + test("var foo = function(a) { return getWindow(a); };" + + "var bar = function(b) { return b; };" + + "foo(bar(x));", + "{getWindow(x)}"); + } + + public void testNoInlineOfNonGlobalFunction1() { + test("var g;function _f(){function g(){return 0}}" + + "function _h(){return g()}", + "var g;function _f(){}" + + "function _h(){return g()}"); + } + + public void testNoInlineOfNonGlobalFunction2() { + test("var g;function _f(){var g=function(){return 0}}" + + "function _h(){return g()}", + "var g;function _f(){}" + + "function _h(){return g()}"); + } + + public void testNoInlineOfNonGlobalFunction3() { + test("var g;function _f(){var g=function(){return 0}}" + + "function _h(){return g()}", + "var g;function _f(){}" + + "function _h(){return g()}"); + } + + public void testNoInlineOfNonGlobalFunction4() { + test("var g;function _f(){function g(){return 0}}" + + "function _h(){return g()}", + "var g;function _f(){}" + + "function _h(){return g()}"); + + } + + public void testNoInlineMaskedFunction() { + // Normalization makes this test of marginal value. + // The unreferenced function is removed. + test("var g=function(){return 0};" + + "function _f(g){return g()}", + "function _f(g$$1){return g$$1()}"); + } + + public void testNoInlineNonFunction() { + testSame("var g=3;function _f(){return g()}"); + } + + public void testInlineCall() { + test("function f(g) { return g.h(); } f('x');", + "\"x\".h()"); + } + + public void testInlineFunctionWithArgsMismatch1() { + test("function f(g) { return g; } f();", + "void 0"); + } + + public void testInlineFunctionWithArgsMismatch2() { + test("function f() { return 0; } f(1);", + "0"); + } + + public void testInlineFunctionWithArgsMismatch3() { + test("function f(one, two, three) { return one + two + three; } f(1);", + "1+void 0+void 0"); + } + + public void testInlineFunctionWithArgsMismatch4() { + test("function f(one, two, three) { return one + two + three; }" + + "f(1,2,3,4,5);", + "1+2+3"); + } + + public void testArgumentsWithSideEffectsNeverInlined1() { + allowBlockInlining = false; + testSame("function f(){return 0} f(new goo());"); + } + + public void testArgumentsWithSideEffectsNeverInlined2() { + allowBlockInlining = false; + testSame("function f(g,h){return h+g}f(g(),h());"); + } + + public void testOneSideEffectCallDoesNotRuinOthers() { + allowBlockInlining = false; + test("function f(){return 0}f(new goo());f()", + "function f(){return 0}f(new goo());0"); + } + + public void testComplexInlineNoResultNoParamCall1() { + test("function f(){a()}f()", + "{a()}"); + } + + public void testComplexInlineNoResultNoParamCall2() { + test("function f(){if (true){return;}else;} f();", + "{JSCompiler_inline_label_f_0:{" + + "if(true)break JSCompiler_inline_label_f_0;else;}}"); + } + + public void testComplexInlineNoResultNoParamCall3() { + // We now allow vars in the global space. + // Don't inline into vars into global scope. + // testSame("function f(){a();b();var z=1+1}f()"); + + // But do inline into functions + test("function f(){a();b();var z=1+1}function _foo(){f()}", + "function _foo(){{a();b();var z$$inline_0=1+1}}"); + + } + + public void testComplexInlineNoResultWithParamCall1() { + test("function f(x){a(x)}f(1)", + "{a(1)}"); + } + + public void testComplexInlineNoResultWithParamCall2() { + test("function f(x,y){a(x)}var b=1;f(1,b)", + "var b=1;{a(1)}"); + } + + public void testComplexInlineNoResultWithParamCall3() { + test("function f(x,y){if (x) y(); return true;}var b=1;f(1,b)", + "var b=1;{if(1)b();true}"); + } + + public void testComplexInline1() { + test("function f(){if (true){return;}else;} z=f();", + "{JSCompiler_inline_label_f_0:" + + "{if(true){z=void 0;" + + "break JSCompiler_inline_label_f_0}else;z=void 0}}"); + } + + public void testComplexInline2() { + test("function f(){if (true){return;}else return;} z=f();", + "{JSCompiler_inline_label_f_0:{if(true){z=void 0;" + + "break JSCompiler_inline_label_f_0}else{z=void 0;" + + "break JSCompiler_inline_label_f_0}z=void 0}}"); + } + + public void testComplexInline3() { + test("function f(){if (true){return 1;}else return 0;} z=f();", + "{JSCompiler_inline_label_f_0:{if(true){z=1;" + + "break JSCompiler_inline_label_f_0}else{z=0;" + + "break JSCompiler_inline_label_f_0}z=void 0}}"); + } + + public void testComplexInline4() { + test("function f(x){a(x)} z = f(1)", + "{a(1);z=void 0}"); + } + + public void testComplexInline5() { + test("function f(x,y){a(x)}var b=1;z=f(1,b)", + "var b=1;{a(1);z=void 0}"); + } + + public void testComplexInline6() { + test("function f(x,y){if (x) y(); return true;}var b=1;z=f(1,b)", + "var b=1;{if(1)b();z=true}"); + } + + public void testComplexInline7() { + test("function f(x,y){if (x) return y(); else return true;}" + + "var b=1;z=f(1,b)", + "var b=1;{JSCompiler_inline_label_f_2:{if(1){z=b();" + + "break JSCompiler_inline_label_f_2}else{z=true;" + + "break JSCompiler_inline_label_f_2}z=void 0}}"); + } + + public void testComplexInline8() { + test("function f(x){a(x)}var z=f(1)", + "var z;{a(1);z=void 0}"); + } + + public void testComplexInlineVars1() { + test("function f(){if (true){return;}else;}var z=f();", + "var z;{JSCompiler_inline_label_f_0:{" + + "if(true){z=void 0;break JSCompiler_inline_label_f_0}else;z=void 0}}"); + } + + public void testComplexInlineVars2() { + test("function f(){if (true){return;}else return;}var z=f();", + "var z;{JSCompiler_inline_label_f_0:{" + + "if(true){z=void 0;break JSCompiler_inline_label_f_0" + + "}else{" + + "z=void 0;break JSCompiler_inline_label_f_0}z=void 0}}"); + } + + public void testComplexInlineVars3() { + test("function f(){if (true){return 1;}else return 0;}var z=f();", + "var z;{JSCompiler_inline_label_f_0:{if(true){" + + "z=1;break JSCompiler_inline_label_f_0" + + "}else{" + + "z=0;break JSCompiler_inline_label_f_0}z=void 0}}"); + } + + public void testComplexInlineVars4() { + test("function f(x){a(x)}var z = f(1)", + "var z;{a(1);z=void 0}"); + } + + public void testComplexInlineVars5() { + test("function f(x,y){a(x)}var b=1;var z=f(1,b)", + "var b=1;var z;{a(1);z=void 0}"); + } + + public void testComplexInlineVars6() { + test("function f(x,y){if (x) y(); return true;}var b=1;var z=f(1,b)", + "var b=1;var z;{if(1)b();z=true}"); + } + + public void testComplexInlineVars7() { + test("function f(x,y){if (x) return y(); else return true;}" + + "var b=1;var z=f(1,b)", + "var b=1;var z;" + + "{JSCompiler_inline_label_f_2:{if(1){z=b();" + + "break JSCompiler_inline_label_f_2" + + "}else{" + + "z=true;break JSCompiler_inline_label_f_2}z=void 0}}"); + } + + public void testComplexInlineVars8() { + test("function f(x){a(x)}var x;var z=f(1)", + "var x;var z;{a(1);z=void 0}"); + } + + public void testComplexInlineVars9() { + test("function f(x){a(x)}var x;var z=f(1);var y", + "var x;var z;{a(1);z=void 0}var y"); + } + + public void testComplexInlineVars10() { + test("function f(x){a(x)}var x=blah();var z=f(1);var y=blah();", + "var x=blah();var z;{a(1);z=void 0}var y=blah()"); + } + + public void testComplexInlineVars11() { + test("function f(x){a(x)}var x=blah();var z=f(1);var y;", + "var x=blah();var z;{a(1);z=void 0}var y"); + } + + public void testComplexInlineVars12() { + test("function f(x){a(x)}var x;var z=f(1);var y=blah();", + "var x;var z;{a(1);z=void 0}var y=blah()"); + } + + public void testComplexInlineInExpresssions1() { + test("function f(){a()}var z=f()", + "var z;{a();z=void 0}"); + } + + public void testComplexInlineInExpresssions2() { + test("function f(){a()}c=z=f()", + "var JSCompiler_inline_result$$0;" + + "{a();JSCompiler_inline_result$$0=void 0;}" + + "c=z=JSCompiler_inline_result$$0"); + } + + public void testComplexInlineInExpresssions3() { + test("function f(){a()}c=z=f()", + "var JSCompiler_inline_result$$0;" + + "{a();JSCompiler_inline_result$$0=void 0;}" + + "c=z=JSCompiler_inline_result$$0"); + } + + public void testComplexInlineInExpresssions4() { + test("function f(){a()}if(z=f());", + "var JSCompiler_inline_result$$0;" + + "{a();JSCompiler_inline_result$$0=void 0;}" + + "if(z=JSCompiler_inline_result$$0);"); + } + + public void testComplexInlineInExpresssions5() { + test("function f(){a()}if(z.y=f());", + "var JSCompiler_temp_const$$0=z;" + + "var JSCompiler_inline_result$$1;" + + "{a();JSCompiler_inline_result$$1=void 0;}" + + "if(JSCompiler_temp_const$$0.y=JSCompiler_inline_result$$1);"); + } + + public void testComplexNoInline1() { + testSame("function f(){a()}while(z=f())continue"); + } + + public void testComplexNoInline2() { + testSame("function f(){a()}do;while(z=f())"); + } + + public void testComplexSample() { + String result = "" + + "{{" + + "var styleSheet$$inline_2=null;" + + "if(goog$userAgent$IE)" + + "styleSheet$$inline_2=0;" + + "else " + + "var head$$inline_3=0;" + + "{" + + "var element$$inline_4=" + + "styleSheet$$inline_2;" + + "var stylesString$$inline_5=a;" + + "if(goog$userAgent$IE)" + + "element$$inline_4.cssText=" + + "stylesString$$inline_5;" + + "else " + + "{" + + "var propToSet$$inline_6=" + + "\"innerText\";" + + "element$$inline_4[" + + "propToSet$$inline_6]=" + + "stylesString$$inline_5" + + "}" + + "}" + + "styleSheet$$inline_2" + + "}}"; + + test("var foo = function(stylesString, opt_element) { " + + "var styleSheet = null;" + + "if (goog$userAgent$IE)" + + "styleSheet = 0;" + + "else " + + "var head = 0;" + + "" + + "goo$zoo(styleSheet, stylesString);" + + "return styleSheet;" + + " };\n " + + + "var goo$zoo = function(element, stylesString) {" + + "if (goog$userAgent$IE)" + + "element.cssText = stylesString;" + + "else {" + + "var propToSet = 'innerText';" + + "element[propToSet] = stylesString;" + + "}" + + "};" + + "(function(){foo(a,b);})();", + result); + } + + public void testComplexSampleNoInline() { + // This is the result we would expect if we could handle "foo = function" + String result = + "foo=function(stylesString,opt_element){" + + "var styleSheet=null;" + + "if(goog$userAgent$IE){" + + "styleSheet=0" + + "}else{" + + "var head=0" + + "}" + + "{var JSCompiler_inline_element_0=styleSheet;" + + "var JSCompiler_inline_stylesString_1=stylesString;" + + "if(goog$userAgent$IE){" + + "JSCompiler_inline_element_0.cssText=" + + "JSCompiler_inline_stylesString_1" + + "}else{" + + "var propToSet=goog$userAgent$WEBKIT?\"innerText\":\"innerHTML\";" + + "JSCompiler_inline_element_0[propToSet]=" + + "JSCompiler_inline_stylesString_1" + + "}}" + + "return styleSheet" + + "}"; + + testSame( + "foo=function(stylesString,opt_element){" + + "var styleSheet=null;" + + "if(goog$userAgent$IE)" + + "styleSheet=0;" + + "else " + + "var head=0;" + + "" + + "goo$zoo(styleSheet,stylesString);" + + "return styleSheet" + + "};" + + "goo$zoo=function(element,stylesString){" + + "if(goog$userAgent$IE)" + + "element.cssText=stylesString;" + + "else{" + + "var propToSet=goog$userAgent$WEBKIT?\"innerText\":\"innerHTML\";" + + "element[propToSet]=stylesString" + + "}" + + "}"); + } + + // Test redefinition of parameter name. + public void testComplexNoVarSub() { + test( + "function foo(x){" + + "var x;" + + "y=x" + + "}" + + "foo(1)", + + "{y=1}" + ); + } + + public void testComplexFunctionWithFunctionDefinition1() { + test("function f(){call(function(){return})}f()", + "{call(function(){return})}"); + } + + public void testComplexFunctionWithFunctionDefinition2() { + assumeMinimumCapture = false; + + // Don't inline if local names might be captured. + testSame("function f(a){call(function(){return})}f()"); + + assumeMinimumCapture = true; + + test("(function(){" + + "var f = function(a){call(function(){return a})};f()})()", + "{{var a$$inline_0=void 0;call(function(){return a$$inline_0})}}"); + } + + public void testComplexFunctionWithFunctionDefinition2a() { + assumeMinimumCapture = false; + + // Don't inline if local names might be captured. + testSame("(function(){" + + "var f = function(a){call(function(){return a})};f()})()"); + + assumeMinimumCapture = true; + + test("(function(){" + + "var f = function(a){call(function(){return a})};f()})()", + "{{var a$$inline_0=void 0;call(function(){return a$$inline_0})}}"); + } + + public void testComplexFunctionWithFunctionDefinition3() { + assumeMinimumCapture = false; + + // Don't inline if local names might need to be captured. + testSame("function f(){var a; call(function(){return a})}f()"); + + assumeMinimumCapture = true; + + test("function f(){var a; call(function(){return a})}f()", + "{var a$$inline_0;call(function(){return a$$inline_0})}"); + + } + + public void testDecomposePlusEquals() { + test("function f(){a=1;return 1} var x = 1; x += f()", + "var x = 1;" + + "var JSCompiler_temp_const$$0 = x;" + + "var JSCompiler_inline_result$$1;" + + "{a=1;" + + " JSCompiler_inline_result$$1=1}" + + "x = JSCompiler_temp_const$$0 + JSCompiler_inline_result$$1;"); + } + + public void testDecomposeFunctionExpressionInCall() { + test( + "(function(map){descriptions_=map})(\n" + + "function(){\n" + + "var ret={};\n" + + "ret[ONE]='a';\n" + + "ret[TWO]='b';\n" + + "return ret\n" + + "}()\n" + + ");", + "var JSCompiler_inline_result$$0;" + + "{" + + "var ret$$inline_1={};\n" + + "ret$$inline_1[ONE]='a';\n" + + "ret$$inline_1[TWO]='b';\n" + + "JSCompiler_inline_result$$0 = ret$$inline_1;\n" + + "}" + + "{" + + "descriptions_=JSCompiler_inline_result$$0;" + + "}" + ); + } + + public void testInlineConstructor1() { + test("function f() {} function _g() {f.call(this)}", + "function _g() {void 0}"); + } + + public void testInlineConstructor2() { + test("function f() {} f.prototype.a = 0; function _g() {f.call(this)}", + "function f() {} f.prototype.a = 0; function _g() {void 0}"); + } + + public void testInlineConstructor3() { + test("function f() {x.call(this)} f.prototype.a = 0;" + + "function _g() {f.call(this)}", + "function f() {x.call(this)} f.prototype.a = 0;" + + "function _g() {{x.call(this)}}"); + } + + public void testInlineConstructor4() { + test("function f() {x.call(this)} f.prototype.a = 0;" + + "function _g() {var t = f.call(this)}", + "function f() {x.call(this)} f.prototype.a = 0;" + + "function _g() {var t; {x.call(this); t = void 0}}"); + } + + public void testFunctionExpressionInlining1() { + test("(function(){})()", + "void 0"); + } + + public void testFunctionExpressionInlining2() { + test("(function(){foo()})()", + "{foo()}"); + } + + public void testFunctionExpressionInlining3() { + test("var a = (function(){return foo()})()", + "var a = foo()"); + } + + public void testFunctionExpressionInlining4() { + test("var a; a = 1 + (function(){return foo()})()", + "var a; a = 1 + foo()"); + } + + public void testFunctionExpressionCallInlining1() { + test("(function(){}).call(this)", + "void 0"); + } + + public void testFunctionExpressionCallInlining2() { + test("(function(){foo(this)}).call(this)", + "{foo(this)}"); + } + + public void testFunctionExpressionCallInlining3() { + test("var a = (function(){return foo(this)}).call(this)", + "var a = foo(this)"); + } + + public void testFunctionExpressionCallInlining4() { + test("var a; a = 1 + (function(){return foo(this)}).call(this)", + "var a; a = 1 + foo(this)"); + } + + public void testFunctionExpressionCallInlining5() { + test("a:(function(){return foo()})()", + "a:foo()"); + } + + public void testFunctionExpressionCallInlining6() { + test("a:(function(){return foo()}).call(this)", + "a:foo()"); + } + + public void testFunctionExpressionCallInlining7() { + test("a:(function(){})()", + "a:void 0"); + } + + public void testFunctionExpressionCallInlining8() { + test("a:(function(){}).call(this)", + "a:void 0"); + } + + public void testFunctionExpressionCallInlining9() { + // ... with unused recursive name. + test("(function foo(){})()", + "void 0"); + } + + public void testFunctionExpressionCallInlining10() { + // ... with unused recursive name. + test("(function foo(){}).call(this)", + "void 0"); + } + + public void testFunctionExpressionCallInlining11a() { + // Inline functions that return inner functions. + test("((function(){return function(){foo()}})())();", "{foo()}"); + } + + public void testFunctionExpressionCallInlining11b() { + assumeMinimumCapture = false; + // Can't inline functions that return inner functions and have local names. + testSame("((function(){var a; return function(){foo()}})())();"); + + assumeMinimumCapture = true; + test( + "((function(){var a; return function(){foo()}})())();", + + "var JSCompiler_inline_result$$0;" + + "{var a$$inline_1;" + + "JSCompiler_inline_result$$0=function(){foo()};}" + + "JSCompiler_inline_result$$0()"); + + } + + public void testFunctionExpressionCallInlining11c() { + // TODO(johnlenz): Can inline, not temps needed. + assumeMinimumCapture = false; + testSame("function _x() {" + + " ((function(){return function(){foo()}})())();" + + "}"); + + assumeMinimumCapture = true; + test( + "function _x() {" + + " ((function(){return function(){foo()}})())();" + + "}", + "function _x() {" + + " {foo()}" + + "}"); + } + + public void testFunctionExpressionCallInlining11d() { + // TODO(johnlenz): Can inline into a function containing eval, if + // no names are introduced. + assumeMinimumCapture = false; + testSame("function _x() {" + + " eval();" + + " ((function(){return function(){foo()}})())();" + + "}"); + + assumeMinimumCapture = true; + test( + "function _x() {" + + " eval();" + + " ((function(){return function(){foo()}})())();" + + "}", + "function _x() {" + + " eval();" + + " {foo()}" + + "}"); + + } + + public void testFunctionExpressionCallInlining11e() { + // No, don't inline into a function containing eval, + // if temps are introduced. + assumeMinimumCapture = false; + testSame("function _x() {" + + " eval();" + + " ((function(a){return function(){foo()}})())();" + + "}"); + + assumeMinimumCapture = true; + test("function _x() {" + + " eval();" + + " ((function(a){return function(){foo()}})())();" + + "}", + "function _x() {" + + " eval();" + + " {foo();}" + + "}"); + } + + public void testFunctionExpressionCallInlining12() { + // Can't inline functions that recurse. + testSame("(function foo(){foo()})()"); + } + + public void testFunctionExpressionOmega() { + // ... with unused recursive name. + test("(function (f){f(f)})(function(f){f(f)})", + "{var f$$inline_0=function(f$$1){f$$1(f$$1)};" + + "{{f$$inline_0(f$$inline_0)}}}"); + } + + public void testLocalFunctionInlining1() { + test("function _f(){ function g() {} g() }", + "function _f(){ void 0 }"); + } + + public void testLocalFunctionInlining2() { + test("function _f(){ function g() {foo(); bar();} g() }", + "function _f(){ {foo(); bar();} }"); + } + + public void testLocalFunctionInlining3() { + test("function _f(){ function g() {foo(); bar();} g() }", + "function _f(){ {foo(); bar();} }"); + } + + public void testLocalFunctionInlining4() { + test("function _f(){ function g() {return 1} return g() }", + "function _f(){ return 1 }"); + } + + public void testLocalFunctionInlining5() { + testSame("function _f(){ function g() {this;} g() }"); + } + + public void testLocalFunctionInlining6() { + testSame("function _f(){ function g() {this;} return g; }"); + } + + public void testLocalFunctionInliningOnly1() { + this.allowGlobalFunctionInlining = true; + test("function f(){} f()", "void 0;"); + this.allowGlobalFunctionInlining = false; + testSame("function f(){} f()"); + } + + public void testLocalFunctionInliningOnly2() { + this.allowGlobalFunctionInlining = false; + testSame("function f(){} f()"); + + test("function f(){ function g() {return 1} return g() }; f();", + "function f(){ return 1 }; f();"); + } + + public void testLocalFunctionInliningOnly3() { + this.allowGlobalFunctionInlining = false; + testSame("function f(){} f()"); + + test("(function(){ function g() {return 1} return g() })();", + "(function(){ return 1 })();"); + } + + public void testLocalFunctionInliningOnly4() { + this.allowGlobalFunctionInlining = false; + testSame("function f(){} f()"); + + test("(function(){ return (function() {return 1})() })();", + "(function(){ return 1 })();"); + } + + public void testInlineWithThis1() { + assumeStrictThis = false; + // If no "this" is provided it might need to be coerced to the global + // "this". + testSame("function f(){} f.call();"); + testSame("function f(){this} f.call();"); + + assumeStrictThis = true; + // In strict mode, "this" is never coerced so we can use the provided value. + test("function f(){} f.call();", "{}"); + test("function f(){this} f.call();", + "{void 0;}"); + } + + public void testInlineWithThis2() { + // "this" can always be replaced with "this" + assumeStrictThis = false; + test("function f(){} f.call(this);", "void 0"); + + assumeStrictThis = true; + test("function f(){} f.call(this);", "void 0"); + } + + public void testInlineWithThis3() { + assumeStrictThis = false; + // If no "this" is provided it might need to be coerced to the global + // "this". + testSame("function f(){} f.call([]);"); + + assumeStrictThis = true; + // In strict mode, "this" is never coerced so we can use the provided value. + test("function f(){} f.call([]);", "{}"); + } + + public void testInlineWithThis4() { + assumeStrictThis = false; + // If no "this" is provided it might need to be coerced to the global + // "this". + testSame("function f(){} f.call(new g);"); + + assumeStrictThis = true; + // In strict mode, "this" is never coerced so we can use the provided value. + test("function f(){} f.call(new g);", + "{var JSCompiler_inline_this_0=new g}"); + } + + public void testInlineWithThis5() { + assumeStrictThis = false; + // If no "this" is provided it might need to be coerced to the global + // "this". + testSame("function f(){} f.call(g());"); + + assumeStrictThis = true; + // In strict mode, "this" is never coerced so we can use the provided value. + test("function f(){} f.call(g());", + "{var JSCompiler_inline_this_0=g()}"); + } + + public void testInlineWithThis6() { + assumeStrictThis = false; + // If no "this" is provided it might need to be coerced to the global + // "this". + testSame("function f(){this} f.call(new g);"); + + assumeStrictThis = true; + // In strict mode, "this" is never coerced so we can use the provided value. + test("function f(){this} f.call(new g);", + "{var JSCompiler_inline_this_0=new g;JSCompiler_inline_this_0}"); + } + + public void testInlineWithThis7() { + assumeStrictThis = true; + // In strict mode, "this" is never coerced so we can use the provided value. + test("function f(a){a=1;this} f.call();", + "{var a$$inline_0=void 0; a$$inline_0=1; void 0;}"); + test("function f(a){a=1;this} f.call(x, x);", + "{var a$$inline_0=x; a$$inline_0=1; x;}"); + } + + // http://en.wikipedia.org/wiki/Fixed_point_combinator#Y_combinator + public void testFunctionExpressionYCombinator() { + assumeMinimumCapture = false; + testSame( + "var factorial = ((function(M) {\n" + + " return ((function(f) {\n" + + " return M(function(arg) {\n" + + " return (f(f))(arg);\n" + + " })\n" + + " })\n" + + " (function(f) {\n" + + " return M(function(arg) {\n" + + " return (f(f))(arg);\n" + + " })\n" + + " }));\n" + + " })\n" + + " (function(f) {\n" + + " return function(n) {\n" + + " if (n === 0)\n" + + " return 1;\n" + + " else\n" + + " return n * f(n - 1);\n" + + " };\n" + + " }));\n" + + "\n" + + "factorial(5)\n"); + + assumeMinimumCapture = true; + test( + "var factorial = ((function(M) {\n" + + " return ((function(f) {\n" + + " return M(function(arg) {\n" + + " return (f(f))(arg);\n" + + " })\n" + + " })\n" + + " (function(f) {\n" + + " return M(function(arg) {\n" + + " return (f(f))(arg);\n" + + " })\n" + + " }));\n" + + " })\n" + + " (function(f) {\n" + + " return function(n) {\n" + + " if (n === 0)\n" + + " return 1;\n" + + " else\n" + + " return n * f(n - 1);\n" + + " };\n" + + " }));\n" + + "\n" + + "factorial(5)\n", + "var factorial;\n" + + "{\n" + + "var M$$inline_4 = function(f$$2) {\n" + + " return function(n){if(n===0)return 1;else return n*f$$2(n-1)}\n" + + "};\n" + + "{\n" + + "var f$$inline_0=function(f$$inline_7){\n" + + " return M$$inline_4(\n" + + " function(arg$$inline_8){\n" + + " return f$$inline_7(f$$inline_7)(arg$$inline_8)\n" + + " })\n" + + "};\n" + + "factorial=M$$inline_4(\n" + + " function(arg$$inline_1){\n" + + " return f$$inline_0(f$$inline_0)(arg$$inline_1)\n" + + "});\n" + + "}\n" + + "}" + + "factorial(5)"); + } + + public void testRenamePropertyFunction() { + testSame("function JSCompiler_renameProperty(x) {return x} " + + "JSCompiler_renameProperty('foo')"); + } + + public void testReplacePropertyFunction() { + // baseline: an alias doesn't prevents declaration removal, but not + // inlining. + test("function f(x) {return x} " + + "foo(window, f); f(1)", + "function f(x) {return x} " + + "foo(window, f); 1"); + // a reference passed to JSCompiler_ObjectPropertyString prevents inlining + // as well. + testSame("function f(x) {return x} " + + "new JSCompiler_ObjectPropertyString(window, f); f(1)"); + } + + public void testInlineWithClosureContainingThis() { + test("(function (){return f(function(){return this})})();", + "f(function(){return this})"); + } + + public void testIssue5159924a() { + test("function f() { if (x()) return y() }\n" + + "while(1){ var m = f() || z() }", + "for(;1;) {" + + " var JSCompiler_inline_result$$0;" + + " {" + + " JSCompiler_inline_label_f_1: {" + + " if(x()) {" + + " JSCompiler_inline_result$$0 = y();" + + " break JSCompiler_inline_label_f_1" + + " }" + + " JSCompiler_inline_result$$0 = void 0;" + + " }" + + " }" + + " var m=JSCompiler_inline_result$$0 || z()" + + "}"); + } + + public void testIssue5159924b() { + test("function f() { if (x()) return y() }\n" + + "while(1){ var m = f() }", + "for(;1;){" + + " var m;" + + " {" + + " JSCompiler_inline_label_f_0: { " + + " if(x()) {" + + " m = y();" + + " break JSCompiler_inline_label_f_0" + + " }" + + " m = void 0" + + " }" + + " }" + + "}"); + } + + public void testInlineObject() { + new StringCompare().testInlineObject(); + } + + private static class StringCompare extends CompilerTestCase { + private boolean allowGlobalFunctionInlining = true; + + StringCompare() { + super("", false); + this.enableNormalize(); + this.enableMarkNoSideEffects(); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + super.enableLineNumberCheck(true); + allowGlobalFunctionInlining = true; + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + compiler.resetUniqueNameId(); + return new InlineFunctions( + compiler, + compiler.getUniqueNameIdSupplier(), + allowGlobalFunctionInlining, + true, // allowLocalFunctionInlining + true, // allowBlockInlining + true, // assumeStrictThis + true); // assumeMinimumCapture + } + + public void testInlineObject() { + allowGlobalFunctionInlining = false; + // TODO(johnlenz): normalize the AST so an AST comparison can be done. + // As is, the expected AST does not match the actual correct result: + // The AST matches "g.a()" with a FREE_CALL annotation, but this as + // expected string would fail as it won't be mark as a free call. + // "(0,g.a)()" matches the output, but not the resulting AST. + test("function inner(){function f(){return g.a}(f())()}", + "function inner(){(0,g.a)()}"); + } + } + + public void testBug4944818() { + test( + "var getDomServices_ = function(self) {\n" + + " if (!self.domServices_) {\n" + + " self.domServices_ = goog$component$DomServices.get(" + + " self.appContext_);\n" + + " }\n" + + "\n" + + " return self.domServices_;\n" + + "};\n" + + "\n" + + "var getOwnerWin_ = function(self) {\n" + + " return getDomServices_(self).getDomHelper().getWindow();\n" + + "};\n" + + "\n" + + "HangoutStarter.prototype.launchHangout = function() {\n" + + " var self = a.b;\n" + + " var myUrl = new goog.Uri(getOwnerWin_(self).location.href);\n" + + "};", + "HangoutStarter.prototype.launchHangout = function() { " + + " var self$$2 = a.b;" + + " var JSCompiler_temp_const$$0 = goog.Uri;" + + " var JSCompiler_inline_result$$1;" + + " {" + + " var self$$inline_2 = self$$2;" + + " if (!self$$inline_2.domServices_) {" + + " self$$inline_2.domServices_ = goog$component$DomServices.get(" + + " self$$inline_2.appContext_);" + + " }" + + " JSCompiler_inline_result$$1=self$$inline_2.domServices_;" + + " }" + + " var myUrl = new JSCompiler_temp_const$$0(" + + " JSCompiler_inline_result$$1.getDomHelper()." + + " getWindow().location.href)" + + "}"); + } + + public void testIssue423() { + assumeMinimumCapture = false; + test( + "(function($) {\n" + + " $.fn.multicheck = function(options) {\n" + + " initialize.call(this, options);\n" + + " };\n" + + "\n" + + " function initialize(options) {\n" + + " options.checkboxes = $(this).siblings(':checkbox');\n" + + " preload_check_all.call(this);\n" + + " }\n" + + "\n" + + " function preload_check_all() {\n" + + " $(this).data('checkboxes');\n" + + " }\n" + + "})(jQuery)", + "(function($){" + + " $.fn.multicheck=function(options$$1){" + + " {" + + " options$$1.checkboxes=$(this).siblings(\":checkbox\");" + + " {" + + " $(this).data(\"checkboxes\")" + + " }" + + " }" + + " }" + + "})(jQuery)"); + + assumeMinimumCapture = true; + test( + "(function($) {\n" + + " $.fn.multicheck = function(options) {\n" + + " initialize.call(this, options);\n" + + " };\n" + + "\n" + + " function initialize(options) {\n" + + " options.checkboxes = $(this).siblings(':checkbox');\n" + + " preload_check_all.call(this);\n" + + " }\n" + + "\n" + + " function preload_check_all() {\n" + + " $(this).data('checkboxes');\n" + + " }\n" + + "})(jQuery)", + "{var $$$inline_0=jQuery;\n" + + "$$$inline_0.fn.multicheck=function(options$$inline_4){\n" + + " {options$$inline_4.checkboxes=" + + "$$$inline_0(this).siblings(\":checkbox\");\n" + + " {$$$inline_0(this).data(\"checkboxes\")}" + + " }\n" + + "}\n" + + "}"); + } + + public void testIssue728() { + String f = "var f = function() { return false; };"; + StringBuilder calls = new StringBuilder(); + StringBuilder folded = new StringBuilder(); + for (int i = 0; i < 30; i++) { + calls.append("if (!f()) alert('x');"); + folded.append("if (!false) alert('x');"); + } + + test(f + calls, folded.toString()); + } + + public void testAnonymous1() { + assumeMinimumCapture = false; + test("(function(){var a=10;(function(){var b=a;a++;alert(b)})()})();", + "{var a$$inline_0=10;" + + "{var b$$inline_1=a$$inline_0;" + + "a$$inline_0++;alert(b$$inline_1)}}"); + + assumeMinimumCapture = true; + test("(function(){var a=10;(function(){var b=a;a++;alert(b)})()})();", + "{var a$$inline_2=10;" + + "{var b$$inline_0=a$$inline_2;" + + "a$$inline_2++;alert(b$$inline_0)}}"); + } + + public void testAnonymous2() { + testSame("(function(){eval();(function(){var b=a;a++;alert(b)})()})();"); + } + + public void testAnonymous3() { + // Introducing a new value into is tricky + assumeMinimumCapture = false; + testSame("(function(){var a=10;(function(){arguments;})()})();"); + + assumeMinimumCapture = true; + test("(function(){var a=10;(function(){arguments;})()})();", + "{var a$$inline_0=10;(function(){arguments;})();}"); + + test("(function(){(function(){arguments;})()})();", + "{(function(){arguments;})()}"); + } + + + public void testLoopWithFunctionWithFunction() { + assumeMinimumCapture = true; + test("function _testLocalVariableInLoop_() {\n" + + " var result = 0;\n" + + " function foo() {\n" + + " var arr = [1, 2, 3, 4, 5];\n" + + " for (var i = 0, l = arr.length; i < l; i++) {\n" + + " var j = arr[i];\n" + + // don't inline this function, because the correct behavior depends + // captured values. + " (function() {\n" + + " var k = j;\n" + + " setTimeout(function() { result += k; }, 5 * i);\n" + + " })();\n" + + " }\n" + + " }\n" + + " foo();\n" + + "}", + "function _testLocalVariableInLoop_(){\n" + + " var result=0;\n" + + " {" + + " var arr$$inline_0=[1,2,3,4,5];\n" + + " var i$$inline_1=0;\n" + + " var l$$inline_2=arr$$inline_0.length;\n" + + " for(;i$$inline_1 variable inlining + + public void testObject0() { + // Don't mess with global variables, that is the job of CollapseProperties. + testSame("var a = {x:1}; f(a.x);"); + } + + public void testObject1() { + testLocal("var a = {x:x(), y:y()}; f(a.x, a.y);", + "var JSCompiler_object_inline_x_0=x();" + + "var JSCompiler_object_inline_y_1=y();" + + "f(JSCompiler_object_inline_x_0, JSCompiler_object_inline_y_1);"); + } + + public void testObject1a() { + testLocal("var a; a = {x:x, y:y}; f(a.x, a.y);", + "var JSCompiler_object_inline_x_0;" + + "var JSCompiler_object_inline_y_1;" + + "(JSCompiler_object_inline_x_0=x," + + "JSCompiler_object_inline_y_1=y, true);" + + "f(JSCompiler_object_inline_x_0, JSCompiler_object_inline_y_1);"); + } + + public void testObject2() { + testLocal("var a = {y:y}; a.x = z; f(a.x, a.y);", + "var JSCompiler_object_inline_y_0 = y;" + + "var JSCompiler_object_inline_x_1;" + + "JSCompiler_object_inline_x_1=z;" + + "f(JSCompiler_object_inline_x_1, JSCompiler_object_inline_y_0);"); + } + + public void testObject3() { + // Inlining the 'y' would cause the 'this' to be different in the + // target function. + testSameLocal("var a = {y:y,x:x}; a.y(); f(a.x);"); + testSameLocal("var a; a = {y:y,x:x}; a.y(); f(a.x);"); + } + + public void testObject4() { + // Object literal is escaped. + testSameLocal("var a = {y:y}; a.x = z; f(a.x, a.y); g(a);"); + testSameLocal("var a; a = {y:y}; a.x = z; f(a.x, a.y); g(a);"); + } + + public void testObject5() { + testLocal("var a = {x:x, y:y}; var b = {a:a}; f(b.a.x, b.a.y);", + "var a = {x:x, y:y};" + + "var JSCompiler_object_inline_a_0=a;" + + "f(JSCompiler_object_inline_a_0.x, JSCompiler_object_inline_a_0.y);"); + } + + public void testObject6() { + testLocal("for (var i = 0; i < 5; i++) { var a = {i:i,x:x}; f(a.i, a.x); }", + "for (var i = 0; i < 5; i++) {" + + " var JSCompiler_object_inline_i_0=i;" + + " var JSCompiler_object_inline_x_1=x;" + + " f(JSCompiler_object_inline_i_0,JSCompiler_object_inline_x_1)" + + "}"); + testLocal("if (c) { var a = {i:i,x:x}; f(a.i, a.x); }", + "if (c) {" + + " var JSCompiler_object_inline_i_0=i;" + + " var JSCompiler_object_inline_x_1=x;" + + " f(JSCompiler_object_inline_i_0,JSCompiler_object_inline_x_1)" + + "}"); + } + + public void testObject7() { + testLocal("var a = {x:x, y:f()}; g(a.x);", + "var JSCompiler_object_inline_x_0=x;" + + "var JSCompiler_object_inline_y_1=f();" + + "g(JSCompiler_object_inline_x_0)"); + } + + public void testObject8() { + testSameLocal("var a = {x:x,y:y}; var b = {x:y}; f((c?a:b).x);"); + + testLocal("var a; if(c) { a={x:x, y:y}; } else { a={x:y}; } f(a.x);", + "var JSCompiler_object_inline_x_0;" + + "var JSCompiler_object_inline_y_1;" + + "if(c) JSCompiler_object_inline_x_0=x," + + " JSCompiler_object_inline_y_1=y," + + " true;" + + "else JSCompiler_object_inline_x_0=y," + + " JSCompiler_object_inline_y_1=void 0," + + " true;" + + "f(JSCompiler_object_inline_x_0)"); + testLocal("var a = {x:x,y:y}; var b = {x:y}; c ? f(a.x) : f(b.x);", + "var JSCompiler_object_inline_x_0 = x; " + + "var JSCompiler_object_inline_y_1 = y; " + + "var JSCompiler_object_inline_x_2 = y; " + + "c ? f(JSCompiler_object_inline_x_0):f(JSCompiler_object_inline_x_2)"); + } + + public void testObject9() { + // There is a call, so no inlining + testSameLocal("function f(a,b) {" + + " var x = {a:a,b:b}; x.a(); return x.b;" + + "}"); + + testLocal("function f(a,b) {" + + " var x = {a:a,b:b}; g(x.a); x = {a:a,b:2}; return x.b;" + + "}", + "function f(a,b) {" + + " var JSCompiler_object_inline_a_0 = a;" + + " var JSCompiler_object_inline_b_1 = b;" + + " g(JSCompiler_object_inline_a_0);" + + " JSCompiler_object_inline_a_0 = a," + + " JSCompiler_object_inline_b_1=2," + + " true;" + + " return JSCompiler_object_inline_b_1" + + "}"); + + testLocal("function f(a,b) { " + + " var x = {a:a,b:b}; g(x.a); x.b = x.c = 2; return x.b; " + + "}", + "function f(a,b) { " + + " var JSCompiler_object_inline_a_0=a;" + + " var JSCompiler_object_inline_b_1=b; " + + " var JSCompiler_object_inline_c_2;" + + " g(JSCompiler_object_inline_a_0);" + + " JSCompiler_object_inline_b_1=JSCompiler_object_inline_c_2=2;" + + " return JSCompiler_object_inline_b_1" + + "}"); + } + + public void testObject10() { + testLocal("var x; var b = f(); x = {a:a, b:b}; if(x.a) g(x.b);", + "var JSCompiler_object_inline_a_0;" + + "var JSCompiler_object_inline_b_1;" + + "var b = f();" + + "JSCompiler_object_inline_a_0=a,JSCompiler_object_inline_b_1=b,true;" + + "if(JSCompiler_object_inline_a_0) g(JSCompiler_object_inline_b_1)"); + testLocal("var x = {}; var b = f(); x = {a:a, b:b}; if(x.a) g(x.b) + x.c", + "var x = {}; var b = f(); x = {a:a, b:b}; if(x.a) g(x.b) + x.c"); + testLocal("var x; var b = f(); x = {a:a, b:b}; x.c = c; if(x.a) g(x.b) + x.c", + "var JSCompiler_object_inline_a_0;" + + "var JSCompiler_object_inline_b_1;" + + "var JSCompiler_object_inline_c_2;" + + "var b = f();" + + "JSCompiler_object_inline_a_0 = a,JSCompiler_object_inline_b_1 = b, " + + " JSCompiler_object_inline_c_2=void 0,true;" + + "JSCompiler_object_inline_c_2 = c;" + + "if (JSCompiler_object_inline_a_0)" + + " g(JSCompiler_object_inline_b_1) + JSCompiler_object_inline_c_2;"); + testLocal("var x = {a:a}; if (b) x={b:b}; f(x.a||x.b);", + "var JSCompiler_object_inline_a_0 = a;" + + "var JSCompiler_object_inline_b_1;" + + "if(b) JSCompiler_object_inline_b_1 = b," + + " JSCompiler_object_inline_a_0 = void 0," + + " true;" + + "f(JSCompiler_object_inline_a_0 || JSCompiler_object_inline_b_1)"); + testLocal("var x; var y = 5; x = {a:a, b:b, c:c}; if (b) x={b:b}; f(x.a||x.b);", + "var JSCompiler_object_inline_a_0;" + + "var JSCompiler_object_inline_b_1;" + + "var JSCompiler_object_inline_c_2;" + + "var y=5;" + + "JSCompiler_object_inline_a_0=a," + + "JSCompiler_object_inline_b_1=b," + + "JSCompiler_object_inline_c_2=c," + + "true;" + + "if (b) JSCompiler_object_inline_b_1=b," + + " JSCompiler_object_inline_a_0=void 0," + + " JSCompiler_object_inline_c_2=void 0," + + " true;" + + "f(JSCompiler_object_inline_a_0||JSCompiler_object_inline_b_1)"); + } + + public void testObject11() { + testSameLocal("var x = {a:b}; (x = {a:a}).c = 5; f(x.a);"); + testSameLocal("var x = {a:a}; f(x[a]); g(x[a]);"); + } + + public void testObject12() { + testLocal("var a; a = {x:1, y:2}; f(a.x, a.y2);", + "var a; a = {x:1, y:2}; f(a.x, a.y2);"); + } + + public void testObject13() { + testSameLocal("var x = {a:1, b:2}; x = {a:3, b:x.a};"); + } + + public void testObject14() { + testSameLocal("var x = {a:1}; if ('a' in x) { f(); }"); + testSameLocal("var x = {a:1}; for (var y in x) { f(y); }"); + } + + public void testObject15() { + testSameLocal("x = x || {}; f(x.a);"); + } + + public void testObject16() { + testLocal("function f(e) { bar(); x = {a: foo()}; var x; print(x.a); }", + "function f(e) { " + + " var JSCompiler_object_inline_a_0;" + + " bar();" + + " JSCompiler_object_inline_a_0 = foo(), true;" + + " print(JSCompiler_object_inline_a_0);" + + "}"); + } + + public void testObject17() { + // Note: Some day, with careful analysis, these two uses could be + // disambiguated, and the second assignment could be inlined. + testSameLocal( + "var a = {a: function(){}};" + + "a.a();" + + "a = {a1: 100};" + + "print(a.a1);"); + } + + public void testObject18() { + testSameLocal("var a,b; b=a={x:x, y:y}; f(b.x);"); + } + + public void testObject19() { + testSameLocal("var a,b; if(c) { b=a={x:x, y:y}; } else { b=a={x:y}; } f(b.x);"); + } + + public void testObject20() { + testSameLocal("var a,b; if(c) { b=a={x:x, y:y}; } else { b=a={x:y}; } f(a.x);"); + } + + public void testObject21() { + testSameLocal("var a,b; b=a={x:x, y:y};"); + testSameLocal("var a,b; if(c) { b=a={x:x, y:y}; }" + + "else { b=a={x:y}; } f(a.x); f(b.x)"); + testSameLocal("var a, b; if(c) { if (a={x:x, y:y}) f(); } " + + "else { b=a={x:y}; } f(a.x);"); + testSameLocal("var a,b; b = (a = {x:x, y:x});"); + testSameLocal("var a,b; a = {x:x, y:x}; b = a"); + testSameLocal("var a,b; a = {x:x, y:x}; b = x || a"); + testSameLocal("var a,b; a = {x:x, y:x}; b = y && a"); + testSameLocal("var a,b; a = {x:x, y:x}; b = y ? a : a"); + testSameLocal("var a,b; a = {x:x, y:x}; b = y , a"); + testSameLocal("b = x || (a = {x:1, y:2});"); + } + + public void testObject22() { + testLocal("while(1) { var a = {y:1}; if (b) a.x = 2; f(a.y, a.x);}", + "for(;1;){" + + " var JSCompiler_object_inline_y_0=1;" + + " var JSCompiler_object_inline_x_1;" + + " if(b) JSCompiler_object_inline_x_1=2;" + + " f(JSCompiler_object_inline_y_0,JSCompiler_object_inline_x_1)" + + "}"); + + testLocal("var a; while (1) { f(a.x, a.y); a = {x:1, y:1};}", + "var a; while (1) { f(a.x, a.y); a = {x:1, y:1};}"); + } + + public void testObject23() { + testLocal("function f() {\n" + + " var templateData = {\n" + + " linkIds: {\n" + + " CHROME: 'cl',\n" + + " DISMISS: 'd'\n" + + " }\n" + + " };\n" + + " var html = templateData.linkIds.CHROME \n" + + " + \":\" + templateData.linkIds.DISMISS;\n" + + "}", + "function f(){" + + "var JSCompiler_object_inline_CHROME_1='cl';" + + "var JSCompiler_object_inline_DISMISS_2='d';" + + "var html=JSCompiler_object_inline_CHROME_1 +" + + " ':' +JSCompiler_object_inline_DISMISS_2}"); + } + + public void testObject24() { + testLocal("function f() {\n" + + " var linkIds = {\n" + + " CHROME: 1,\n" + + " };\n" + + " var g = function () {var o = {a: linkIds};}\n" + + "}", + "function f(){var linkIds={CHROME:1};" + + "var g=function(){var JSCompiler_object_inline_a_0=linkIds}}"); + } + + public void testObject25() { + testLocal("var a = {x:f(), y:g()}; a = {y:g(), x:f()}; f(a.x, a.y);", + "var JSCompiler_object_inline_x_0=f();" + + "var JSCompiler_object_inline_y_1=g();" + + "JSCompiler_object_inline_y_1=g()," + + " JSCompiler_object_inline_x_0=f()," + + " true;" + + "f(JSCompiler_object_inline_x_0,JSCompiler_object_inline_y_1)"); + } + + public void testObject26() { + testLocal("var a = {}; a.b = function() {}; new a.b.c", + "var JSCompiler_object_inline_b_0;" + + "JSCompiler_object_inline_b_0=function(){};" + + "new JSCompiler_object_inline_b_0.c"); + } + + public void testBug545() { + testLocal("var a = {}", ""); + testLocal("var a; a = {}", "true"); + } + + public void testIssue724() { + testSameLocal( + "var getType; getType = {};" + + "return functionToCheck && " + + " getType.toString.apply(functionToCheck) === " + + " '[object Function]';"); + } + + public void testNoInlineDeletedProperties() { + testSameLocal( + "var foo = {bar:1};" + + "delete foo.bar;" + + "return foo.bar;"); + } + + private final String LOCAL_PREFIX = "function local(){"; + private final String LOCAL_POSTFIX = "}"; + + private void testLocal(String code, String result) { + test(LOCAL_PREFIX + code + LOCAL_POSTFIX, + LOCAL_PREFIX + result + LOCAL_POSTFIX); + } + + private void testSameLocal(String code) { + testLocal(code, code); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlinePropertiesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlinePropertiesTest.java new file mode 100644 index 0000000..6d1a0ce --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlinePropertiesTest.java @@ -0,0 +1,200 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class InlinePropertiesTest extends CompilerTestCase { + + private static final String EXTERNS = + "Function.prototype.call=function(){};" + + "Function.prototype.inherits=function(){};" + + "prop.toString;" + + "var google = { gears: { factory: {}, workerPool: {} } };"; + + public InlinePropertiesTest() { + super(EXTERNS); + enableNormalize(); + enableTypeCheck(CheckLevel.WARNING); + enableClosurePass(); + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new InlineProperties(compiler); + } + + public void testConstInstanceProp1() { + // Replace a reference to known constant property. + test( + "/** @constructor */\n" + + "function C() {\n" + + " this.foo = 1;\n" + + "}\n" + + "new C().foo;", + "function C() {\n" + + " this.foo = 1;\n" + + "}\n" + + "new C(), 1;"); + } + + public void testConstInstanceProp2() { + // Replace a constant reference + test( + "/** @constructor */\n" + + "function C() {\n" + + " this.foo = 1;\n" + + "}\n" + + "var x = new C();\n" + + "x.foo;", + "function C() {\n" + + " this.foo = 1\n" + + "}\n" + + "var x = new C();\n" + + "1;\n"); + } + + + public void testConstInstanceProp3() { + // Replace a constant reference + test( + "/** @constructor */\n" + + "function C() {\n" + + " this.foo = 1;\n" + + "}\n" + + "/** @type {C} */\n" + + "var x = new C();\n" + + "x.foo;", + "function C() {\n" + + " this.foo = 1\n" + + "}\n" + + "var x = new C();\n" + + "1;\n"); + } + + public void testConstInstanceProp4() { + // This pass replies on DisambiguateProperties to distinguish like named + // properties so it doesn't handle this case. + testSame( + "/** @constructor */\n" + + "function C() {\n" + + " this.foo = 1;\n" + + "}\n" + + "/** @constructor */\n" + + "function B() {\n" + + " this.foo = 1;\n" + + "}\n" + + "new C().foo;\n"); + } + + + public void testConstClassProps1() { + // For now, don't inline constant class properties, + // CollapseProperties should handle this in most cases. + testSame( + "/** @constructor */\n" + + "function C() {\n" + + "}\n" + + "C.foo = 1;\n" + + "C.foo;"); + } + + public void testConstClassProps2() { + // Don't confuse, class properties with instance properties + testSame( + "/** @constructor */\n" + + "function C() {\n" + + " this.foo = 1;\n" + + "}\n" + + "C.foo;"); + } + + public void testConstClassProps3() { + // Don't confuse, class properties with prototype properties + testSame( + "/** @constructor */\n" + + "function C() {}\n" + + "C.prototype.foo = 1;\n" + + "c.foo;\n"); + } + + public void testNonConstClassProp1() { + testSame( + "/** @constructor */\n" + + "function C() {\n" + + " this.foo = 1;\n" + + "}\n" + + "var x = new C();\n" + + "alert(x.foo);\n" + + "delete x.foo;"); + } + + public void testNonConstClassProp2() { + testSame( + "/** @constructor */\n" + + "function C() {\n" + + " this.foo = 1;\n" + + "}\n" + + "var x = new C();\n" + + "alert(x.foo);\n" + + "x.foo = 2;"); + } + + public void testNonConstructorClassProp1() { + testSame( + "function C() {\n" + + " this.foo = 1;\n" + + " return this;\n" + + "}\n" + + "C().foo;"); + } + + public void testConditionalClassProp1() { + testSame( + "/** @constructor */\n" + + "function C() {\n" + + " if (false) this.foo = 1;\n" + + "}\n" + + "new C().foo;"); + } + + public void testConstPrototypeProp1() { + test( + "/** @constructor */\n" + + "function C() {}\n" + + "C.prototype.foo = 1;\n" + + "new C().foo;\n", + "function C() {}\n" + + "C.prototype.foo = 1;\n" + + "new C(), 1;\n"); + } + + public void testConstPrototypeProp2() { + test( + "/** @constructor */\n" + + "function C() {}\n" + + "C.prototype.foo = 1;\n" + + "var x = new C();\n" + + "x.foo;\n", + "function C() {}\n" + + "C.prototype.foo = 1;\n" + + "var x = new C();\n" + + "1;\n"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlineSimpleMethodsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlineSimpleMethodsTest.java new file mode 100644 index 0000000..98ae1ba --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlineSimpleMethodsTest.java @@ -0,0 +1,304 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +public class InlineSimpleMethodsTest extends CompilerTestCase { + + public InlineSimpleMethodsTest() { + super("", false); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + super.enableLineNumberCheck(true); + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new InlineSimpleMethods(compiler); + } + + /** + * Helper for tests that expects definitions to remain unchanged, such + * that {@code definitions+js} is converted to {@code definitions+expected}. + */ + private void testWithPrefix(String definitions, String js, String expected) { + test(definitions + js, definitions + expected); + } + + public void testSimpleInline1() { + testWithPrefix("function Foo(){}" + + "Foo.prototype.bar=function(){return this.baz};", + "var x=(new Foo).bar();var y=(new Foo).bar();", + "var x=(new Foo).baz;var y=(new Foo).baz"); + } + + public void testSimpleInline2() { + testWithPrefix("function Foo(){}" + + "Foo.prototype={bar:function(){return this.baz}};", + "var x=(new Foo).bar();var y=(new Foo).bar();", + "var x=(new Foo).baz;var y=(new Foo).baz"); + } + + public void testSimpleGetterInline1() { + // TODO(johnlenz): Support this case. + testSame("function Foo(){}" + + "Foo.prototype={get bar(){return this.baz}};" + + "var x=(new Foo).bar;var y=(new Foo).bar"); + // Verify we are not confusing calling the result of an ES5 getter + // with call the getter. + testSame("function Foo(){}" + + "Foo.prototype={get bar(){return this.baz}};" + + "var x=(new Foo).bar();var y=(new Foo).bar()"); + } + + public void testSimpleSetterInline1() { + // Verify 'get' and 'set' are not confused. + testSame("function Foo(){}" + + "Foo.prototype={set bar(a){return this.baz}};" + + "var x=(new Foo).bar;var y=(new Foo).bar"); + testSame("function Foo(){}" + + "Foo.prototype={set bar(a){return this.baz}};" + + "var x=(new Foo).bar();var y=(new Foo).bar()"); + } + + public void testSelfInline() { + testWithPrefix("function Foo(){}" + + "Foo.prototype.bar=function(){return this.baz};", + "Foo.prototype.meth=function(){this.bar();}", + "Foo.prototype.meth=function(){this.baz}"); + } + + public void testCallWithArgs() { + testWithPrefix("function Foo(){}" + + "Foo.prototype.bar=function(){return this.baz};", + "var x=(new Foo).bar(3,new Foo)", + "var x=(new Foo).bar(3,new Foo)"); + } + + public void testCallWithConstArgs() { + testWithPrefix("function Foo(){}" + + "Foo.prototype.bar=function(a){return this.baz};", + "var x=(new Foo).bar(3, 4)", + "var x=(new Foo).baz"); + } + + public void testNestedProperties() { + testWithPrefix("function Foo(){}" + + "Foo.prototype.bar=function(){return this.baz.ooka};", + "(new Foo).bar()", + "(new Foo).baz.ooka"); + } + + public void testSkipComplexMethods() { + testWithPrefix("function Foo(){}" + + "Foo.prototype.bar=function(){return this.baz};" + + "Foo.prototype.condy=function(){return this.baz?this.baz:1};", + "var x=(new Foo).argy()", + "var x=(new Foo).argy()"); + } + + public void testSkipConflictingMethods() { + testWithPrefix("function Foo(){}" + + "Foo.prototype.bar=function(){return this.baz};" + + "Foo.prototype.bar=function(){return this.bazz};", + "var x=(new Foo).bar()", + "var x=(new Foo).bar()"); + } + + public void testSameNamesDifferentDefinitions() { + testWithPrefix("function A(){}" + + "A.prototype.g=function(){return this.a};" + + "function B(){}" + + "B.prototype.g=function(){return this.b};", + "var x=(new A).g();" + + "var y=(new B).g();" + + "var a=new A;" + + "var ag=a.g();", + "var x=(new A).g();" + + "var y=(new B).g();" + + "var a=new A;" + + "var ag=a.g()"); + } + + public void testSameNamesSameDefinitions() { + testWithPrefix("function A(){}" + + "A.prototype.g=function(){return this.a};" + + "function B(){}" + + "B.prototype.g=function(){return this.a};", + "var x=(new A).g();" + + "var y=(new B).g();" + + "var a=new A;" + + "var ag=a.g();", + "var x=(new A).a;" + + "var y=(new B).a;" + + "var a=new A;" + + "var ag=a.a"); + } + + public void testConfusingNames() { + testWithPrefix("function Foo(){}" + + "Foo.prototype.bar=function(){return this.baz};", + "function bar(){var bar=function(){};bar()}", + "function bar(){var bar=function(){};bar()}"); + } + + public void testConstantInline() { + testWithPrefix("function Foo(){}" + + "Foo.prototype.bar=function(){return 3};", + "var f=new Foo;var x=f.bar()", + "var f=new Foo;var x=3"); + } + + public void testConstantArrayInline() { + testWithPrefix("function Foo(){}" + + "Foo.prototype.bar=function(){return[3,4]};", + "var f=new Foo;var x=f.bar()", + "var f=new Foo;var x=[3,4]"); + } + + public void testConstantInlineWithSideEffects() { + testWithPrefix("function Foo(){}" + + "Foo.prototype.bar=function(){return 3};", + "var x=(new Foo).bar()", + "var x=(new Foo).bar()"); + } + + public void testEmptyMethodInline() { + testWithPrefix("function Foo(){}" + + "Foo.prototype.bar=function(a){};", + "var x=new Foo; x.bar();", + "var x=new Foo"); + } + + public void testEmptyMethodInlineWithSideEffects() { + testWithPrefix("function Foo(){}" + + "Foo.prototype.bar=function(){};", + "(new Foo).bar();var y=new Foo;y.bar(new Foo)", + "(new Foo).bar();var y=new Foo;y.bar(new Foo)"); + } + + public void testEmptyMethodInlineInAssign1() { + testWithPrefix("function Foo(){}" + + "Foo.prototype.bar=function(){};", + "var x=new Foo;var y=x.bar()", + "var x=new Foo;var y=void 0"); + } + + public void testEmptyMethodInlineInAssign2() { + testWithPrefix("function Foo(){}" + + "Foo.prototype.bar=function(){};", + "var x=new Foo;var y=x.bar().toString()", + "var x=new Foo;var y=(void 0).toString()"); + } + + public void testNormalMethod() { + testWithPrefix("function Foo(){}" + + "Foo.prototype.bar=function(){var x=1};", + "var x=new Foo;x.bar()", + "var x=new Foo;x.bar()"); + } + + public void testNoInlineOfExternMethods1() { + testSame("var external={};external.charAt;", + "external.charAt()", (DiagnosticType) null); + } + + public void testNoInlineOfExternMethods2() { + testSame("var external={};external.charAt=function(){};", + "external.charAt()", (DiagnosticType) null); + } + + public void testNoInlineOfExternMethods3() { + testSame("var external={};external.bar=function(){};", + "function Foo(){}Foo.prototype.bar=function(){};(new Foo).bar()", + (DiagnosticType) null); + } + + public void testNoInlineOfDangerousProperty() { + testSame("function Foo(){this.bar=3}" + + "Foo.prototype.bar=function(){};" + + "var x=new Foo;var y=x.bar()"); + } + + // Don't warn about argument naming conventions (this is done in another pass) + // opt_ parameters must not be followed by non-optional parameters. + // var_args must be last + public void testNoWarn() { + testSame("function Foo(){}" + + "Foo.prototype.bar=function(opt_a,b){var x=1};" + + "var x=new Foo;x.bar()"); + + testSame("function Foo(){}" + + "Foo.prototype.bar=function(var_args,b){var x=1};" + + "var x=new Foo;x.bar()"); + } + + public void testObjectLit() { + testSame("Foo.prototype.bar=function(){return this.baz_};" + + "var blah={bar:function(){}};" + + "(new Foo).bar()"); + } + + public void testObjectLit2() { + testSame("var blah={bar:function(){}};" + + "(new Foo).bar()"); + } + + public void testObjectLitExtern() { + String externs = "window.bridge={_sip:function(){}};"; + testSame(externs, "window.bridge._sip()", null); + } + + public void testExternFunction() { + String externs = "function emptyFunction() {}"; + testSame(externs, + "function Foo(){this.empty=emptyFunction}" + + "(new Foo).empty()", null); + } + + public void testIssue2508576_1() { + // Method defined by an extern should be left alone. + String externs = "function alert(a) {}"; + testSame(externs, "({a:alert,b:alert}).a(\"a\")", null); + } + + public void testIssue2508576_2() { + // Anonymous object definition with a side-effect should be left alone. + testSame("({a:function(){},b:x()}).a(\"a\")"); + } + + public void testIssue2508576_3() { + // Anonymous object definition without side-effect should be removed. + test("({a:function(){},b:alert}).a(\"a\")", ""); + } + + public void testAnonymousGet() { + // Anonymous object definition without side-effect should be removed. + testSame("({get a(){return function(){}},b:alert}).a(\"a\")"); + testSame("({get a(){},b:alert}).a(\"a\")"); + testSame("({get a(){},b:alert}).a"); + } + + public void testAnonymousSet() { + // Anonymous object definition without side-effect should be removed. + testSame("({set a(b){return function(){}},b:alert}).a(\"a\")"); + testSame("({set a(b){},b:alert}).a(\"a\")"); + testSame("({set a(b){},b:alert}).a"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlineVariablesConstantsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlineVariablesConstantsTest.java new file mode 100644 index 0000000..3810384 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlineVariablesConstantsTest.java @@ -0,0 +1,147 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + + +/** + * Ensures that the InlineVariables pass in constants-only mode + * is functionally equivalent to the old InlineVariablesConstants pass. + */ +public class InlineVariablesConstantsTest extends CompilerTestCase { + + private boolean inlineAllStrings = false; + + public InlineVariablesConstantsTest() { + enableNormalize(); + } + + @Override + protected CompilerPass getProcessor(final Compiler compiler) { + return new InlineVariables( + compiler, InlineVariables.Mode.CONSTANTS_ONLY, inlineAllStrings); + } + + @Override + public void tearDown() { + inlineAllStrings = false; + } + + public void testInlineVariablesConstants() { + test("var ABC=2; var x = ABC;", "var x=2"); + test("var AA = 'aa'; AA;", "'aa'"); + test("var A_A=10; A_A + A_A;", "10+10"); + test("var AA=1", ""); + test("var AA; AA=1", "1"); + test("var AA; if (false) AA=1; AA;", "if (false) 1; 1;"); + testSame("var AA; if (false) AA=1; else AA=2; AA;"); + + test("var AA;(function () {AA=1})()", + "(function () {1})()"); + + // Make sure that nothing explodes if there are undeclared variables. + testSame("var x = AA;"); + + // Don't inline if it will make the output larger. + testSame("var AA = '1234567890'; foo(AA); foo(AA); foo(AA);"); + + test("var AA = '123456789012345';AA;", + "'123456789012345'"); + } + + public void testNoInlineArraysOrRegexps() { + testSame("var AA = [10,20]; AA[0]"); + testSame("var AA = [10,20]; AA.push(1); AA[0]"); + testSame("var AA = /x/; AA.test('1')"); + testSame("/** @const */ var aa = /x/; aa.test('1')"); + } + + public void testInlineVariablesConstantsJsDocStyle() { + test("/** @const */var abc=2; var x = abc;", "var x=2"); + test("/** @const */var aa = 'aa'; aa;", "'aa'"); + test("/** @const */var a_a=10; a_a + a_a;", "10+10"); + test("/** @const */var aa=1;", ""); + test("/** @const */var aa; aa=1;", "1"); + test("/** @const */var aa;(function () {aa=1})()", "(function () {1})()"); + test("/** @const */var aa;(function () {aa=1})(); var z=aa", + "(function () {1})(); var z=1"); + testSame("/** @const */var aa;(function () {var y; aa=y})(); var z=aa"); + + // Don't inline if it will make the output larger. + testSame("/** @const */var aa = '1234567890'; foo(aa); foo(aa); foo(aa);"); + + test("/** @const */var aa = '123456789012345';aa;", + "'123456789012345'"); + } + + public void testInlineConditionallyDefinedConstant1() { + // Note that inlining conditionally defined constants can change the + // run-time behavior of code (e.g. when y is true and x is false in the + // example below). We inline them anyway because if the code author didn't + // want one inlined, he/she could define it as a non-const variable instead. + test("if (x) var ABC = 2; if (y) f(ABC);", + "if (x); if (y) f(2);"); + } + + public void testInlineConditionallyDefinedConstant2() { + test("if (x); else var ABC = 2; if (y) f(ABC);", + "if (x); else; if (y) f(2);"); + } + + public void testInlineConditionallyDefinedConstant3() { + test("if (x) { var ABC = 2; } if (y) { f(ABC); }", + "if (x) {} if (y) { f(2); }"); + } + + public void testInlineDefinedConstant() { + test( + "/**\n" + + " * @define {string}\n" + + " */\n" + + "var aa = '1234567890';\n" + + "foo(aa); foo(aa); foo(aa);", + "foo('1234567890');foo('1234567890');foo('1234567890')"); + + test( + "/**\n" + + " * @define {string}\n" + + " */\n" + + "var ABC = '1234567890';\n" + + "foo(ABC); foo(ABC); foo(ABC);", + "foo('1234567890');foo('1234567890');foo('1234567890')"); + } + + public void testInlineVariablesConstantsWithInlineAllStringsOn() { + inlineAllStrings = true; + test("var AA = '1234567890'; foo(AA); foo(AA); foo(AA);", + "foo('1234567890'); foo('1234567890'); foo('1234567890')"); + } + + public void testNoInlineWithoutConstDeclaration() { + testSame("var abc = 2; var x = abc;"); + } + +// TODO(nicksantos): enable this again once we allow constant aliasing. +// public void testInlineConstantAlias() { +// test("var XXX = new Foo(); var YYY = XXX; bar(YYY)", +// "var XXX = new Foo(); bar(XXX)"); +// } + + public void testNoInlineAliases() { + testSame("var XXX = new Foo(); var yyy = XXX; bar(yyy)"); + testSame("var xxx = new Foo(); var YYY = xxx; bar(YYY)"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlineVariablesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlineVariablesTest.java new file mode 100644 index 0000000..f44f22b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InlineVariablesTest.java @@ -0,0 +1,1067 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + + +/** + * Verifies that valid candidates for inlining are inlined, but + * that no dangerous inlining occurs. + * + * @author kushal@google.com (Kushal Dave) + */ +public class InlineVariablesTest extends CompilerTestCase { + + private boolean inlineAllStrings = false; + private boolean inlineLocalsOnly = false; + + public InlineVariablesTest() { + enableNormalize(); + } + + @Override + public void setUp() { + super.enableLineNumberCheck(true); + } + + @Override + protected CompilerPass getProcessor(final Compiler compiler) { + return new InlineVariables( + compiler, + (inlineLocalsOnly) + ? InlineVariables.Mode.LOCALS_ONLY + : InlineVariables.Mode.ALL, + inlineAllStrings); + } + + @Override + public void tearDown() { + inlineAllStrings = false; + inlineLocalsOnly = false; + } + + // Test respect for scopes and blocks + + public void testInlineGlobal() { + test("var x = 1; var z = x;", "var z = 1;"); + } + + public void testNoInlineExportedName() { + testSame("var _x = 1; var z = _x;"); + } + + public void testNoInlineExportedName2() { + testSame("var f = function() {}; var _x = f;" + + "var y = function() { _x(); }; var _y = f;"); + } + + public void testDoNotInlineIncrement() { + testSame("var x = 1; x++;"); + } + + public void testDoNotInlineDecrement() { + testSame("var x = 1; x--;"); + } + + public void testDoNotInlineIntoLhsOfAssign() { + testSame("var x = 1; x += 3;"); + } + + public void testInlineIntoRhsOfAssign() { + test("var x = 1; var y = x;", "var y = 1;"); + } + + public void testInlineInFunction() { + test("function baz() { var x = 1; var z = x; }", + "function baz() { var z = 1; }"); + } + + public void testInlineInFunction2() { + test("function baz() { " + + "var a = new obj();"+ + "result = a;" + + "}", + "function baz() { " + + "result = new obj()" + + "}"); + } + + public void testInlineInFunction3() { + testSame( + "function baz() { " + + "var a = new obj();" + + "(function(){a;})();" + + "result = a;" + + "}"); + } + + public void testInlineInFunction4() { + testSame( + "function baz() { " + + "var a = new obj();" + + "foo.result = a;" + + "}"); + } + + public void testInlineInFunction5() { + testSame( + "function baz() { " + + "var a = (foo = new obj());" + + "foo.x();" + + "result = a;" + + "}"); + } + + public void testInlineAcrossModules() { + // TODO(kushal): Make decision about overlap with CrossModuleCodeMotion + test(createModules("var a = 2;", "var b = a;"), + new String[] { "", "var b = 2;" }); + } + + public void testDoNotExitConditional1() { + testSame("if (true) { var x = 1; } var z = x;"); + } + + public void testDoNotExitConditional2() { + testSame("if (true) var x = 1; var z = x;"); + } + + + public void testDoNotExitConditional3() { + testSame("var x; if (true) x=1; var z = x;"); + } + + public void testDoNotExitLoop() { + testSame("while (z) { var x = 3; } var y = x;"); + } + + public void testDoNotExitForLoop() { + test("for (var i = 1; false; false) var z = i;", + "for (;false;false) var z = 1;"); + testSame("for (; false; false) var i = 1; var z = i;"); + testSame("for (var i in {}); var z = i;"); + } + + public void testDoNotEnterSubscope() { + testSame( + "var x = function() {" + + " var self = this; " + + " return function() { var y = self; };" + + "}"); + testSame( + "var x = function() {" + + " var y = [1]; " + + " return function() { var z = y; };" + + "}"); + } + + public void testDoNotExitTry() { + testSame("try { var x = y; } catch (e) {} var z = y; "); + testSame("try { throw e; var x = 1; } catch (e) {} var z = x; "); + } + + public void testDoNotEnterCatch() { + testSame("try { } catch (e) { var z = e; } "); + } + + public void testDoNotEnterFinally() { + testSame("try { throw e; var x = 1; } catch (e) {} " + + "finally { var z = x; } "); + } + + public void testInsideIfConditional() { + test("var a = foo(); if (a) { alert(3); }", "if (foo()) { alert(3); }"); + test("var a; a = foo(); if (a) { alert(3); }", "if (foo()) { alert(3); }"); + } + + public void testOnlyReadAtInitialization() { + test("var a; a = foo();", "foo();"); + test("var a; if (a = foo()) { alert(3); }", "if (foo()) { alert(3); }"); + test("var a; switch (a = foo()) {}", "switch(foo()) {}"); + test("var a; function f(){ return a = foo(); }", + "function f(){ return foo(); }"); + test("function f(){ var a; return a = foo(); }", + "function f(){ return foo(); }"); + test("var a; with (a = foo()) { alert(3); }", "with (foo()) { alert(3); }"); + + test("var a; b = (a = foo());", "b = foo();"); + test("var a; while(a = foo()) { alert(3); }", + "while(foo()) { alert(3); }"); + test("var a; for(;a = foo();) { alert(3); }", + "for(;foo();) { alert(3); }"); + test("var a; do {} while(a = foo()) { alert(3); }", + "do {} while(foo()) { alert(3); }"); + } + + public void testImmutableWithSingleReferenceAfterInitialzation() { + test("var a; a = 1;", "1;"); + test("var a; if (a = 1) { alert(3); }", "if (1) { alert(3); }"); + test("var a; switch (a = 1) {}", "switch(1) {}"); + test("var a; function f(){ return a = 1; }", + "function f(){ return 1; }"); + test("function f(){ var a; return a = 1; }", + "function f(){ return 1; }"); + test("var a; with (a = 1) { alert(3); }", "with (1) { alert(3); }"); + + test("var a; b = (a = 1);", "b = 1;"); + test("var a; while(a = 1) { alert(3); }", + "while(1) { alert(3); }"); + test("var a; for(;a = 1;) { alert(3); }", + "for(;1;) { alert(3); }"); + test("var a; do {} while(a = 1) { alert(3); }", + "do {} while(1) { alert(3); }"); + } + + public void testSingleReferenceAfterInitialzation() { + test("var a; a = foo();a;", "foo();"); + testSame("var a; if (a = foo()) { alert(3); } a;"); + testSame("var a; switch (a = foo()) {} a;"); + testSame("var a; function f(){ return a = foo(); } a;"); + testSame("function f(){ var a; return a = foo(); a;}"); + testSame("var a; with (a = foo()) { alert(3); } a;"); + testSame("var a; b = (a = foo()); a;"); + testSame("var a; while(a = foo()) { alert(3); } a;"); + testSame("var a; for(;a = foo();) { alert(3); } a;"); + testSame("var a; do {} while(a = foo()) { alert(3); } a;"); + } + + public void testInsideIfBranch() { + testSame("var a = foo(); if (1) { alert(a); }"); + } + + public void testInsideAndConditional() { + test("var a = foo(); a && alert(3);", "foo() && alert(3);"); + } + + public void testInsideAndBranch() { + testSame("var a = foo(); 1 && alert(a);"); + } + + public void testInsideOrBranch() { + testSame("var a = foo(); 1 || alert(a);"); + } + + public void testInsideHookBranch() { + testSame("var a = foo(); 1 ? alert(a) : alert(3)"); + } + + public void testInsideHookConditional() { + test("var a = foo(); a ? alert(1) : alert(3)", + "foo() ? alert(1) : alert(3)"); + } + + public void testInsideOrBranchInsideIfConditional() { + testSame("var a = foo(); if (x || a) {}"); + } + + public void testInsideOrBranchInsideIfConditionalWithConstant() { + // We don't inline non-immutable constants into branches. + testSame("var a = [false]; if (x || a) {}"); + } + + public void testCrossFunctionsAsLeftLeaves() { + // Ensures getNext() understands how to walk past a function leaf + test( + new String[] { "var x = function() {};", "", + "function cow() {} var z = x;"}, + new String[] { "", "", "function cow() {} var z = function() {};" }); + test( + new String[] { "var x = function() {};", "", + "var cow = function() {}; var z = x;"}, + new String[] { "", "", + "var cow = function() {}; var z = function() {};" }); + testSame( + new String[] { "var x = a;", "", + "(function() { a++; })(); var z = x;"}); + test( + new String[] { "var x = a;", "", + "function cow() { a++; }; cow(); var z = x;"}, + new String[] { "var x = a;", "", + ";(function cow(){ a++; })(); var z = x;"}); + testSame( + new String[] { "var x = a;", "", + "cow(); var z = x; function cow() { a++; };"}); + } + + // Test movement of constant values + + public void testDoCrossFunction() { + // We know foo() does not affect x because we require that x is only + // referenced twice. + test("var x = 1; foo(); var z = x;", "foo(); var z = 1;"); + } + + public void testDoNotCrossReferencingFunction() { + testSame( + "var f = function() { var z = x; };" + + "var x = 1;" + + "f();" + + "var z = x;" + + "f();"); + } + + + // Test tricky declarations and references + + public void testChainedAssignment() { + test("var a = 2, b = 2; var c = b;", "var a = 2; var c = 2;"); + test("var a = 2, b = 2; var c = a;", "var b = 2; var c = 2;"); + test("var a = b = 2; var f = 3; var c = a;", "var f = 3; var c = b = 2;"); + testSame("var a = b = 2; var c = b;"); + } + + public void testForIn() { + testSame("for (var i in j) { var c = i; }"); + testSame("var i = 0; for (i in j) ;"); + testSame("var i = 0; for (i in j) { var c = i; }"); + testSame("i = 0; for (var i in j) { var c = i; }"); + testSame("var j = {'key':'value'}; for (var i in j) {print(i)};"); + } + + // Test movement of values that have (may) side effects + + public void testDoCrossNewVariables() { + test("var x = foo(); var z = x;", "var z = foo();"); + } + + public void testDoNotCrossFunctionCalls() { + testSame("var x = foo(); bar(); var z = x;"); + } + + + // Test movement of values that are complex but lack side effects + + public void testDoNotCrossAssignment() { + testSame("var x = {}; var y = x.a; x.a = 1; var z = y;"); + testSame("var a = this.id; foo(this.id = 3, a);"); + } + + public void testDoNotCrossDelete() { + testSame("var x = {}; var y = x.a; delete x.a; var z = y;"); + } + + public void testDoNotCrossAssignmentPlus() { + testSame("var a = b; b += 2; var c = a;"); + } + + public void testDoNotCrossIncrement() { + testSame("var a = b.c; b.c++; var d = a;"); + } + + public void testDoNotCrossConstructor() { + testSame("var a = b; new Foo(); var c = a;"); + } + + public void testDoCrossVar() { + // Assumes we do not rely on undefined variables (not technically correct!) + test("var a = b; var b = 3; alert(a)", "alert(3);"); + } + + public void testOverlappingInlines() { + String source = + "a = function(el, x, opt_y) { " + + " var cur = bar(el); " + + " opt_y = x.y; " + + " x = x.x; " + + " var dx = x - cur.x; " + + " var dy = opt_y - cur.y;" + + " foo(el, el.offsetLeft + dx, el.offsetTop + dy); " + + "};"; + String expected = + "a = function(el, x, opt_y) { " + + " var cur = bar(el); " + + " opt_y = x.y; " + + " x = x.x; " + + " foo(el, el.offsetLeft + (x - cur.x)," + + " el.offsetTop + (opt_y - cur.y)); " + + "};"; + + test(source, expected); + } + + public void testOverlappingInlineFunctions() { + String source = + "a = function() { " + + " var b = function(args) {var n;}; " + + " var c = function(args) {}; " + + " d(b,c); " + + "};"; + String expected = + "a = function() { " + + " d(function(args){var n;}, function(args){}); " + + "};"; + + test(source, expected); + } + + public void testInlineIntoLoops() { + test("var x = true; while (true) alert(x);", + "while (true) alert(true);"); + test("var x = true; while (true) for (var i in {}) alert(x);", + "while (true) for (var i in {}) alert(true);"); + testSame("var x = [true]; while (true) alert(x);"); + } + + public void testInlineIntoFunction() { + test("var x = false; var f = function() { alert(x); };", + "var f = function() { alert(false); };"); + testSame("var x = [false]; var f = function() { alert(x); };"); + } + + public void testNoInlineIntoNamedFunction() { + testSame("f(); var x = false; function f() { alert(x); };"); + } + + public void testInlineIntoNestedNonHoistedNamedFunctions() { + test("f(); var x = false; if (false) function f() { alert(x); };", + "f(); if (false) function f() { alert(false); };"); + } + + public void testNoInlineIntoNestedNamedFunctions() { + testSame("f(); var x = false; function f() { if (false) { alert(x); } };"); + } + + public void testNoInlineMutatedVariable() { + testSame("var x = false; if (true) { var y = x; x = true; }"); + } + + public void testInlineImmutableMultipleTimes() { + test("var x = null; var y = x, z = x;", + "var y = null, z = null;"); + test("var x = 3; var y = x, z = x;", + "var y = 3, z = 3;"); + } + + public void testNoInlineStringMultipleTimesIfNotWorthwhile() { + testSame("var x = 'abcdefghijklmnopqrstuvwxyz'; var y = x, z = x;"); + } + + public void testInlineStringMultipleTimesWhenAliasingAllStrings() { + inlineAllStrings = true; + test("var x = 'abcdefghijklmnopqrstuvwxyz'; var y = x, z = x;", + "var y = 'abcdefghijklmnopqrstuvwxyz', " + + " z = 'abcdefghijklmnopqrstuvwxyz';"); + } + + public void testNoInlineBackwards() { + testSame("var y = x; var x = null;"); + } + + public void testNoInlineOutOfBranch() { + testSame("if (true) var x = null; var y = x;"); + } + + public void testInterferingInlines() { + test("var a = 3; var f = function() { var x = a; alert(x); };", + "var f = function() { alert(3); };"); + } + + public void testInlineIntoTryCatch() { + test("var a = true; " + + "try { var b = a; } " + + "catch (e) { var c = a + b; var d = true; } " + + "finally { var f = a + b + c + d; }", + "try { var b = true; } " + + "catch (e) { var c = true + b; var d = true; } " + + "finally { var f = true + b + c + d; }"); + } + + // Make sure that we still inline constants that are not provably + // written before they're read. + public void testInlineConstants() { + test("function foo() { return XXX; } var XXX = true;", + "function foo() { return true; }"); + } + + public void testInlineStringWhenWorthwhile() { + test("var x = 'a'; foo(x, x, x);", "foo('a', 'a', 'a');"); + } + + public void testInlineConstantAlias() { + test("var XXX = new Foo(); q(XXX); var YYY = XXX; bar(YYY)", + "var XXX = new Foo(); q(XXX); bar(XXX)"); + } + + public void testInlineConstantAliasWithAnnotation() { + test("/** @const */ var xxx = new Foo(); q(xxx); var YYY = xxx; bar(YYY)", + "/** @const */ var xxx = new Foo(); q(xxx); bar(xxx)"); + } + + public void testInlineConstantAliasWithNonConstant() { + test("var XXX = new Foo(); q(XXX); var y = XXX; bar(y); baz(y)", + "var XXX = new Foo(); q(XXX); bar(XXX); baz(XXX)"); + } + + public void testCascadingInlines() { + test("var XXX = 4; " + + "function f() { var YYY = XXX; bar(YYY); baz(YYY); }", + "function f() { bar(4); baz(4); }"); + } + + public void testNoInlineGetpropIntoCall() { + test("var a = b; a();", "b();"); + test("var a = b.c; f(a);", "f(b.c);"); + testSame("var a = b.c; a();"); + } + + public void testInlineFunctionDeclaration() { + test("var f = function () {}; var a = f;", + "var a = function () {};"); + test("var f = function () {}; foo(); var a = f;", + "foo(); var a = function () {};"); + test("var f = function () {}; foo(f);", + "foo(function () {});"); + + testSame("var f = function () {}; function g() {var a = f;}"); + testSame("var f = function () {}; function g() {h(f);}"); + } + + public void test2388531() { + testSame("var f = function () {};" + + "var g = function () {};" + + "goog.inherits(f, g);"); + testSame("var f = function () {};" + + "var g = function () {};" + + "goog$inherits(f, g);"); + } + + public void testRecursiveFunction1() { + testSame("var x = 0; (function x() { return x ? x() : 3; })();"); + } + + public void testRecursiveFunction2() { + testSame("function y() { return y(); }"); + } + + public void testUnreferencedBleedingFunction() { + testSame("var x = function y() {}"); + } + + public void testReferencedBleedingFunction() { + testSame("var x = function y() { return y(); }"); + } + + public void testInlineAliases1() { + test("var x = this.foo(); this.bar(); var y = x; this.baz(y);", + "var x = this.foo(); this.bar(); this.baz(x);"); + } + + public void testInlineAliases1b() { + test("var x = this.foo(); this.bar(); var y; y = x; this.baz(y);", + "var x = this.foo(); this.bar(); x; this.baz(x);"); + } + + public void testInlineAliases1c() { + test("var x; x = this.foo(); this.bar(); var y = x; this.baz(y);", + "var x; x = this.foo(); this.bar(); this.baz(x);"); + } + + public void testInlineAliases1d() { + test("var x; x = this.foo(); this.bar(); var y; y = x; this.baz(y);", + "var x; x = this.foo(); this.bar(); x; this.baz(x);"); + } + + public void testInlineAliases2() { + test("var x = this.foo(); this.bar(); " + + "function f() { var y = x; this.baz(y); }", + "var x = this.foo(); this.bar(); function f() { this.baz(x); }"); + } + + public void testInlineAliases2b() { + test("var x = this.foo(); this.bar(); " + + "function f() { var y; y = x; this.baz(y); }", + "var x = this.foo(); this.bar(); function f() { this.baz(x); }"); + } + + public void testInlineAliases2c() { + test("var x; x = this.foo(); this.bar(); " + + "function f() { var y = x; this.baz(y); }", + "var x; x = this.foo(); this.bar(); function f() { this.baz(x); }"); + } + + public void testInlineAliases2d() { + test("var x; x = this.foo(); this.bar(); " + + "function f() { var y; y = x; this.baz(y); }", + "var x; x = this.foo(); this.bar(); function f() { this.baz(x); }"); + } + + public void testInlineAliasesInLoop() { + test( + "function f() { " + + " var x = extern();" + + " for (var i = 0; i < 5; i++) {" + + " (function() {" + + " var y = x; window.setTimeout(function() { extern(y); }, 0);" + + " })();" + + " }" + + "}", + "function f() { " + + " var x = extern();" + + " for (var i = 0; i < 5; i++) {" + + " (function() {" + + " window.setTimeout(function() { extern(x); }, 0);" + + " })();" + + " }" + + "}"); + } + + public void testNoInlineAliasesInLoop() { + testSame( + "function f() { " + + " for (var i = 0; i < 5; i++) {" + + " var x = extern();" + + " (function() {" + + " var y = x; window.setTimeout(function() { extern(y); }, 0);" + + " })();" + + " }" + + "}"); + } + + public void testNoInlineAliases1() { + testSame( + "var x = this.foo(); this.bar(); var y = x; x = 3; this.baz(y);"); + } + + public void testNoInlineAliases1b() { + testSame( + "var x = this.foo(); this.bar(); var y; y = x; x = 3; this.baz(y);"); + } + + public void testNoInlineAliases2() { + testSame( + "var x = this.foo(); this.bar(); var y = x; y = 3; this.baz(y); "); + } + + public void testNoInlineAliases2b() { + testSame( + "var x = this.foo(); this.bar(); var y; y = x; y = 3; this.baz(y); "); + } + + public void testNoInlineAliases3() { + testSame( + "var x = this.foo(); this.bar(); " + + "function f() { var y = x; g(); this.baz(y); } " + + "function g() { x = 3; }"); + } + + public void testNoInlineAliases3b() { + testSame( + "var x = this.foo(); this.bar(); " + + "function f() { var y; y = x; g(); this.baz(y); } " + + "function g() { x = 3; }"); + } + + public void testNoInlineAliases4() { + testSame( + "var x = this.foo(); this.bar(); " + + "function f() { var y = x; y = 3; this.baz(y); }"); + } + + public void testNoInlineAliases4b() { + testSame( + "var x = this.foo(); this.bar(); " + + "function f() { var y; y = x; y = 3; this.baz(y); }"); + } + + public void testNoInlineAliases5() { + testSame( + "var x = this.foo(); this.bar(); var y = x; this.bing();" + + "this.baz(y); x = 3;"); + } + + public void testNoInlineAliases5b() { + testSame( + "var x = this.foo(); this.bar(); var y; y = x; this.bing();" + + "this.baz(y); x = 3;"); + } + + public void testNoInlineAliases6() { + testSame( + "var x = this.foo(); this.bar(); var y = x; this.bing();" + + "this.baz(y); y = 3;"); + } + + public void testNoInlineAliases6b() { + testSame( + "var x = this.foo(); this.bar(); var y; y = x; this.bing();" + + "this.baz(y); y = 3;"); + } + + public void testNoInlineAliases7() { + testSame( + "var x = this.foo(); this.bar(); " + + "function f() { var y = x; this.bing(); this.baz(y); x = 3; }"); + } + + public void testNoInlineAliases7b() { + testSame( + "var x = this.foo(); this.bar(); " + + "function f() { var y; y = x; this.bing(); this.baz(y); x = 3; }"); + } + + public void testNoInlineAliases8() { + testSame( + "var x = this.foo(); this.bar(); " + + "function f() { var y = x; this.baz(y); y = 3; }"); + } + + public void testNoInlineAliases8b() { + testSame( + "var x = this.foo(); this.bar(); " + + "function f() { var y; y = x; this.baz(y); y = 3; }"); + } + + public void testSideEffectOrder() { + // z can not be changed by the call to y, so x can be inlined. + String EXTERNS = "var z; function f(){}"; + test(EXTERNS, + "var x = f(y.a, y); z = x;", + "z = f(y.a, y);", null, null); + // z.b can be changed by the call to y, so x can not be inlined. + testSame(EXTERNS, "var x = f(y.a, y); z.b = x;", null, null); + } + + public void testInlineParameterAlias1() { + test( + "function f(x) {" + + " var y = x;" + + " g();" + + " y;y;" + + "}", + "function f(x) {" + + " g();" + + " x;x;" + + "}" + ); + } + + public void testInlineParameterAlias2() { + test( + "function f(x) {" + + " var y; y = x;" + + " g();" + + " y;y;" + + "}", + "function f(x) {" + + " x;" + + " g();" + + " x;x;" + + "}" + ); + } + + public void testInlineFunctionAlias1a() { + test( + "function f(x) {}" + + "var y = f;" + + "g();" + + "y();y();", + "var y = function f(x) {};" + + "g();" + + "y();y();" + ); + } + + public void testInlineFunctionAlias1b() { + test( + "function f(x) {};" + + "f;var y = f;" + + "g();" + + "y();y();", + "function f(x) {};" + + "f;g();" + + "f();f();" + ); + } + + public void testInlineFunctionAlias2a() { + test( + "function f(x) {}" + + "var y; y = f;" + + "g();" + + "y();y();", + "var y; y = function f(x) {};" + + "g();" + + "y();y();" + ); + } + + public void testInlineFunctionAlias2b() { + test( + "function f(x) {};" + + "f; var y; y = f;" + + "g();" + + "y();y();", + "function f(x) {};" + + "f; f;" + + "g();" + + "f();f();" + ); + } + + public void testInlineCatchAlias1() { + test( + "try {" + + "} catch (e) {" + + " var y = e;" + + " g();" + + " y;y;" + + "}", + "try {" + + "} catch (e) {" + + " g();" + + " e;e;" + + "}" + ); + } + + public void testInlineCatchAlias2() { + test( + "try {" + + "} catch (e) {" + + " var y; y = e;" + + " g();" + + " y;y;" + + "}", + "try {" + + "} catch (e) {" + + " e;" + + " g();" + + " e;e;" + + "}" + ); + } + + public void testLocalsOnly1() { + inlineLocalsOnly = true; + test( + "var x=1; x; function f() {var x = 1; x;}", + "var x=1; x; function f() {1;}"); + } + + public void testLocalsOnly2() { + inlineLocalsOnly = true; + test( + "/** @const */\n" + + "var X=1; X;\n" + + "function f() {\n" + + " /** @const */\n" + + " var X = 1; X;\n" + + "}", + "var X=1; X; function f() {1;}"); + } + + public void testInlineUndefined1() { + test("var x; x;", + "void 0;"); + } + + public void testInlineUndefined2() { + testSame("var x; x++;"); + } + + public void testInlineUndefined3() { + testSame("var x; var x;"); + } + + public void testInlineUndefined4() { + test("var x; x; x;", + "void 0; void 0;"); + } + + public void testInlineUndefined5() { + test("var x; for(x in a) {}", + "var x; for(x in a) {}"); + } + + public void testIssue90() { + test("var x; x && alert(1)", + "void 0 && alert(1)"); + } + + public void testRenamePropertyFunction() { + testSame("var JSCompiler_renameProperty; " + + "JSCompiler_renameProperty('foo')"); + } + + public void testThisAlias() { + test("function f() { var a = this; a.y(); a.z(); }", + "function f() { this.y(); this.z(); }"); + } + + public void testThisEscapedAlias() { + testSame( + "function f() { var a = this; var g = function() { a.y(); }; a.z(); }"); + } + + public void testInlineNamedFunction() { + test("function f() {} f();", "(function f(){})()"); + } + + public void testIssue378ModifiedArguments1() { + testSame( + "function g(callback) {\n" + + " var f = callback;\n" + + " arguments[0] = this;\n" + + " f.apply(this, arguments);\n" + + "}"); + } + + public void testIssue378ModifiedArguments2() { + testSame( + "function g(callback) {\n" + + " /** @const */\n" + + " var f = callback;\n" + + " arguments[0] = this;\n" + + " f.apply(this, arguments);\n" + + "}"); + } + + public void testIssue378EscapedArguments1() { + testSame( + "function g(callback) {\n" + + " var f = callback;\n" + + " h(arguments,this);\n" + + " f.apply(this, arguments);\n" + + "}\n" + + "function h(a,b) {\n" + + " a[0] = b;" + + "}"); + } + + public void testIssue378EscapedArguments2() { + testSame( + "function g(callback) {\n" + + " /** @const */\n" + + " var f = callback;\n" + + " h(arguments,this);\n" + + " f.apply(this);\n" + + "}\n" + + "function h(a,b) {\n" + + " a[0] = b;" + + "}"); + } + + public void testIssue378EscapedArguments3() { + test( + "function g(callback) {\n" + + " var f = callback;\n" + + " f.apply(this, arguments);\n" + + "}\n", + "function g(callback) {\n" + + " callback.apply(this, arguments);\n" + + "}\n"); + } + + public void testIssue378EscapedArguments4() { + testSame( + "function g(callback) {\n" + + " var f = callback;\n" + + " h(arguments[0],this);\n" + + " f.apply(this, arguments);\n" + + "}\n" + + "function h(a,b) {\n" + + " a[0] = b;" + + "}"); + } + + public void testIssue378ArgumentsRead1() { + test( + "function g(callback) {\n" + + " var f = callback;\n" + + " var g = arguments[0];\n" + + " f.apply(this, arguments);\n" + + "}", + "function g(callback) {\n" + + " var g = arguments[0];\n" + + " callback.apply(this, arguments);\n" + + "}"); + } + + public void testIssue378ArgumentsRead2() { + test( + "function g(callback) {\n" + + " var f = callback;\n" + + " h(arguments[0],this);\n" + + " f.apply(this, arguments[0]);\n" + + "}\n" + + "function h(a,b) {\n" + + " a[0] = b;" + + "}", + "function g(callback) {\n" + + " h(arguments[0],this);\n" + + " callback.apply(this, arguments[0]);\n" + + "}\n" + + "function h(a,b) {\n" + + " a[0] = b;" + + "}"); + } + + public void testArgumentsModifiedInOuterFunction() { + test( + "function g(callback) {\n" + + " var f = callback;\n" + + " arguments[0] = this;\n" + + " f.apply(this, arguments);\n" + + " function inner(callback) {" + + " var x = callback;\n" + + " x.apply(this);\n" + + " }" + + "}", + "function g(callback) {\n" + + " var f = callback;\n" + + " arguments[0] = this;\n" + + " f.apply(this, arguments);\n" + + " function inner(callback) {" + + " callback.apply(this);\n" + + " }" + + "}"); + } + + public void testArgumentsModifiedInInnerFunction() { + test( + "function g(callback) {\n" + + " var f = callback;\n" + + " f.apply(this, arguments);\n" + + " function inner(callback) {" + + " var x = callback;\n" + + " arguments[0] = this;\n" + + " x.apply(this);\n" + + " }" + + "}", + "function g(callback) {\n" + + " callback.apply(this, arguments);\n" + + " function inner(callback) {" + + " var x = callback;\n" + + " arguments[0] = this;\n" + + " x.apply(this);\n" + + " }" + + "}"); + } + + public void testNoInlineRedeclaredExterns() { + String externs = "var test = 1;"; + String code = "/** @suppress {duplicate} */ var test = 2;alert(test);"; + test(externs, code, code, null, null); + } + + public void testBug6598844() { + testSame( + "function F() { this.a = 0; }" + + "F.prototype.inc = function() { this.a++; return 10; };" + + "F.prototype.bar = function() { var val = inc(); this.a += val; };"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InstrumentFunctionsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InstrumentFunctionsTest.java new file mode 100644 index 0000000..3653a36 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/InstrumentFunctionsTest.java @@ -0,0 +1,263 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableList; +import com.google.javascript.rhino.Node; + +import java.io.StringReader; +import java.util.List; + +/** + * Tests for {@link InstrumentFunctions} + * + */ +public class InstrumentFunctionsTest extends CompilerTestCase { + private String instrumentationPb; + + public InstrumentFunctionsTest() { + this.instrumentationPb = null; + } + + @Override + protected void setUp() { + super.enableLineNumberCheck(false); + this.instrumentationPb = null; + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new NameAndInstrumentFunctions(compiler); + } + + @Override + protected int getNumRepetitions() { + // This pass is not idempotent. + return 1; + } + + public void testInstrument() { + final String kPreamble = + "var $$toRemoveDefinition1, $$notToRemove;\n" + + "var $$toRemoveDefinition2, $$toRemoveDefinition3;\n"; + + // build instrumentation template and init code strings for use in + // tests below. + List initCodeList = ImmutableList.of( + "var $$Table = [];", + "function $$TestDefine(id) {", + " $$Table[id] = 0;", + "};", + "function $$TestInstrument(id) {", + " $$Table[id]++;", + "};"); + StringBuilder initCodeBuilder = new StringBuilder(); + StringBuilder pbBuilder = new StringBuilder(); + for (String line : initCodeList) { + initCodeBuilder.append(line).append("\n"); + pbBuilder.append("init: \"").append(line).append("\"\n"); + } + + pbBuilder.append("report_call: \"$$testInstrument\"") + .append("report_defined: \"$$testDefine\"") + .append("declaration_to_remove: \"$$toRemoveDefinition1\"") + .append("declaration_to_remove: \"$$toRemoveDefinition2\"") + .append("declaration_to_remove: \"$$toRemoveDefinition3\""); + + final String initCode = initCodeBuilder.toString(); + this.instrumentationPb = pbBuilder.toString(); + + // Test basic instrumentation + test("function a(){b}", + initCode + "$$testDefine(0);" + + "function a(){$$testInstrument(0);b}"); + + // Test declaration_to_remove + test(kPreamble + "function a(){b}", + initCode + + "$$testDefine(0);" + + "var $$notToRemove;" + + "function a(){$$testInstrument(0);b}"); + + // Test object literal declarations + test(kPreamble + "var a = { b: function(){c} }", + initCode + + "var $$notToRemove;" + + "$$testDefine(0);" + + "var a = { b: function(){$$testInstrument(0);c} }"); + + // Test multiple object literal declarations + test(kPreamble + + "var a = { b: function(){c}, d: function(){e} }", + initCode + + "var $$notToRemove;" + + "$$testDefine(0);" + + "$$testDefine(1);" + + "var a={b:function(){$$testInstrument(0);c}," + + "d:function(){$$testInstrument(1);e}}"); + + // Test recursive object literal declarations + test(kPreamble + + "var a = { b: { f: function(){c} }, d: function(){e} }", + initCode + + "var $$notToRemove;" + + "$$testDefine(0);" + + "$$testDefine(1);" + + "var a={b:{f:function(){$$testInstrument(0);c}}," + + "d:function(){$$testInstrument(1);e}}"); + } + + public void testEmpty() { + this.instrumentationPb = ""; + test("function a(){b}", "function a(){b}"); + } + + public void testAppNameSetter() { + this.instrumentationPb = "app_name_setter: \"setAppName\""; + test("function a(){b}", "setAppName(\"testfile.js\");function a(){b}"); + } + + public void testInit() { + this.instrumentationPb = "init: \"var foo = 0;\"\n" + + "init: \"function f(){g();}\"\n"; + test("function a(){b}", + "var foo = 0;function f(){g()}function a(){b}"); + } + + public void testDeclare() { + this.instrumentationPb = "report_defined: \"$$testDefine\""; + test("function a(){b}", "$$testDefine(0);function a(){b}"); + } + + public void testCall() { + this.instrumentationPb = "report_call: \"$$testCall\""; + test("function a(){b}", "function a(){$$testCall(0);b}"); + } + + public void testNested() { + this.instrumentationPb = "report_call: \"$$testCall\"\n" + + "report_defined: \"$$testDefine\""; + test("function a(){ function b(){}}", + "$$testDefine(1);$$testDefine(0);" + + "function a(){$$testCall(1);function b(){$$testCall(0)}}"); + } + + public void testExitPaths() { + this.instrumentationPb = "report_exit: \"$$testExit\""; + test("function a(){return}", + "function a(){return $$testExit(0)}"); + + test("function b(){return 5}", + "function b(){return $$testExit(0, 5)}"); + + test("function a(){if(2 != 3){return}else{return 5}}", + "function a(){if(2!=3){return $$testExit(0)}" + + "else{return $$testExit(0,5)}}"); + + test("function a(){if(2 != 3){return}else{return 5}}b()", + "function a(){if(2!=3){return $$testExit(0)}" + + "else{return $$testExit(0,5)}}b()"); + + test("function a(){if(2 != 3){return}else{return 5}}", + "function a(){if(2!=3){return $$testExit(0)}" + + "else{return $$testExit(0,5)}}"); + } + + public void testExitNoReturn() { + this.instrumentationPb = "report_exit: \"$$testExit\""; + test("function a(){}", + "function a(){$$testExit(0);}"); + + test("function a(){b()}", + "function a(){b();$$testExit(0);}"); + } + + public void testPartialExitPaths() { + this.instrumentationPb = "report_exit: \"$$testExit\""; + test("function a(){if (2 != 3) {return}}", + "function a(){if (2 != 3){return $$testExit(0)}$$testExit(0)}"); + } + + public void testExitTry() { + this.instrumentationPb = "report_exit: \"$$testExit\""; + test("function a(){try{return}catch(err){}}", + "function a(){try{return $$testExit(0)}catch(err){}$$testExit(0)}"); + + test("function a(){try{}catch(err){return}}", + "function a(){try{}catch(err){return $$testExit(0)}$$testExit(0)}"); + + test("function a(){try{return}finally{}}", + "function a(){try{return $$testExit(0)}finally{}$$testExit(0)}"); + + test("function a(){try{return}catch(err){}finally{}}", + "function a(){try{return $$testExit(0)}catch(err){}finally{}" + + "$$testExit(0)}"); + + test("function a(){try{return 1}catch(err){return 2}}", + "function a(){try{return $$testExit(0, 1)}" + + "catch(err){return $$testExit(0,2)}}"); + + test("function a(){try{return 1}catch(err){return 2}finally{}}", + "function a(){try{return $$testExit(0, 1)}" + + "catch(err){return $$testExit(0,2)}" + + "finally{}$$testExit(0)}"); + + test("function a(){try{return 1}catch(err){return 2}finally{return}}", + "function a(){try{return $$testExit(0, 1)}" + + "catch(err){return $$testExit(0,2)}finally{return $$testExit(0)}}"); + + test("function a(){try{}catch(err){}finally{return}}", + "function a(){try{}catch(err){}finally{return $$testExit(0)}}"); + } + + public void testNestedExit() { + this.instrumentationPb = "report_exit: \"$$testExit\"\n" + + "report_defined: \"$$testDefine\""; + test("function a(){ return function(){ return c;}}", + "$$testDefine(1);function a(){$$testDefine(0);" + + "return $$testExit(1, function(){return $$testExit(0, c);});}"); + } + + public void testProtobuffParseFail() { + this.instrumentationPb = "not an ascii pb\n"; + test("function a(){b}", "", RhinoErrorReporter.PARSE_ERROR); + } + + public void testInitJsParseFail() { + this.instrumentationPb = "init: \"= assignWithNoLhs();\""; + test("function a(){b}", "", RhinoErrorReporter.PARSE_ERROR); + } + + private class NameAndInstrumentFunctions implements CompilerPass { + private final Compiler compiler; + NameAndInstrumentFunctions(Compiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + FunctionNames functionNames = new FunctionNames(compiler); + functionNames.process(externs, root); + + InstrumentFunctions instrumentation = + new InstrumentFunctions(compiler, functionNames, + "test init code", "testfile.js", + new StringReader(instrumentationPb)); + instrumentation.process(externs, root); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/IntegrationTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/IntegrationTest.java new file mode 100644 index 0000000..e6bd4cd --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/IntegrationTest.java @@ -0,0 +1,2399 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.CompilerOptions.LanguageMode; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * Tests for {@link PassFactory}. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class IntegrationTest extends IntegrationTestCase { + + private static final String CLOSURE_BOILERPLATE = + "/** @define {boolean} */ var COMPILED = false; var goog = {};" + + "goog.exportSymbol = function() {};"; + + private static final String CLOSURE_COMPILED = + "var COMPILED = true; var goog$exportSymbol = function() {};"; + + public void testConstructorCycle() { + CompilerOptions options = createCompilerOptions(); + options.checkTypes = true; + test(options, + "/** @return {function()} */ var AsyncTestCase = function() {};\n" + + "/**\n" + + " * @constructor\n" + + " */ Foo = /** @type {function(new:Foo)} */ (AyncTestCase());", + RhinoErrorReporter.PARSE_ERROR); + } + + public void testBug1949424() { + CompilerOptions options = createCompilerOptions(); + options.collapseProperties = true; + options.closurePass = true; + test(options, CLOSURE_BOILERPLATE + "goog.provide('FOO'); FOO.bar = 3;", + CLOSURE_COMPILED + "var FOO$bar = 3;"); + } + + public void testBug1949424_v2() { + CompilerOptions options = createCompilerOptions(); + options.collapseProperties = true; + options.closurePass = true; + test(options, CLOSURE_BOILERPLATE + "goog.provide('FOO.BAR'); FOO.BAR = 3;", + CLOSURE_COMPILED + "var FOO$BAR = 3;"); + } + + public void testBug1956277() { + CompilerOptions options = createCompilerOptions(); + options.collapseProperties = true; + options.inlineVariables = true; + test(options, "var CONST = {}; CONST.bar = null;" + + "function f(url) { CONST.bar = url; }", + "var CONST$bar = null; function f(url) { CONST$bar = url; }"); + } + + public void testBug1962380() { + CompilerOptions options = createCompilerOptions(); + options.collapseProperties = true; + options.inlineVariables = true; + options.generateExports = true; + test(options, + CLOSURE_BOILERPLATE + "/** @export */ goog.CONSTANT = 1;" + + "var x = goog.CONSTANT;", + "(function() {})('goog.CONSTANT', 1);" + + "var x = 1;"); + } + + public void testBug2410122() { + CompilerOptions options = createCompilerOptions(); + options.generateExports = true; + options.closurePass = true; + test(options, + "var goog = {};" + + "function F() {}" + + "/** @export */ function G() { goog.base(this); } " + + "goog.inherits(G, F);", + "var goog = {};" + + "function F() {}" + + "function G() { F.call(this); } " + + "goog.inherits(G, F); goog.exportSymbol('G', G);"); + } + + public void testIssue90() { + CompilerOptions options = createCompilerOptions(); + options.foldConstants = true; + options.inlineVariables = true; + options.removeDeadCode = true; + test(options, + "var x; x && alert(1);", + ""); + } + + public void testClosurePassOff() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = false; + testSame( + options, + "var goog = {}; goog.require = function(x) {}; goog.require('foo');"); + testSame( + options, + "var goog = {}; goog.getCssName = function(x) {};" + + "goog.getCssName('foo');"); + } + + public void testClosurePassOn() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + test( + options, + "var goog = {}; goog.require = function(x) {}; goog.require('foo');", + ProcessClosurePrimitives.MISSING_PROVIDE_ERROR); + test( + options, + "/** @define {boolean} */ var COMPILED = false;" + + "var goog = {}; goog.getCssName = function(x) {};" + + "goog.getCssName('foo');", + "var COMPILED = true;" + + "var goog = {}; goog.getCssName = function(x) {};" + + "'foo';"); + } + + public void testCssNameCheck() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.checkMissingGetCssNameLevel = CheckLevel.ERROR; + options.checkMissingGetCssNameBlacklist = "foo"; + test(options, "var x = 'foo';", + CheckMissingGetCssName.MISSING_GETCSSNAME); + } + + public void testBug2592659() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.checkTypes = true; + options.checkMissingGetCssNameLevel = CheckLevel.WARNING; + options.checkMissingGetCssNameBlacklist = "foo"; + test(options, + "var goog = {};\n" + + "/**\n" + + " * @param {string} className\n" + + " * @param {string=} opt_modifier\n" + + " * @return {string}\n" + + "*/\n" + + "goog.getCssName = function(className, opt_modifier) {}\n" + + "var x = goog.getCssName(123, 'a');", + TypeValidator.TYPE_MISMATCH_WARNING); + } + + public void testTypedefBeforeOwner1() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + test(options, + "goog.provide('foo.Bar.Type');\n" + + "goog.provide('foo.Bar');\n" + + "/** @typedef {number} */ foo.Bar.Type;\n" + + "foo.Bar = function() {};", + "var foo = {}; foo.Bar.Type; foo.Bar = function() {};"); + } + + public void testTypedefBeforeOwner2() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.collapseProperties = true; + test(options, + "goog.provide('foo.Bar.Type');\n" + + "goog.provide('foo.Bar');\n" + + "/** @typedef {number} */ foo.Bar.Type;\n" + + "foo.Bar = function() {};", + "var foo$Bar$Type; var foo$Bar = function() {};"); + } + + public void testExportedNames() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.variableRenaming = VariableRenamingPolicy.ALL; + test(options, + "/** @define {boolean} */ var COMPILED = false;" + + "var goog = {}; goog.exportSymbol('b', goog);", + "var a = true; var c = {}; c.exportSymbol('b', c);"); + test(options, + "/** @define {boolean} */ var COMPILED = false;" + + "var goog = {}; goog.exportSymbol('a', goog);", + "var b = true; var c = {}; c.exportSymbol('a', c);"); + } + + public void testCheckGlobalThisOn() { + CompilerOptions options = createCompilerOptions(); + options.checkSuspiciousCode = true; + options.checkGlobalThisLevel = CheckLevel.ERROR; + test(options, "function f() { this.y = 3; }", CheckGlobalThis.GLOBAL_THIS); + } + + public void testSusiciousCodeOff() { + CompilerOptions options = createCompilerOptions(); + options.checkSuspiciousCode = false; + options.checkGlobalThisLevel = CheckLevel.ERROR; + test(options, "function f() { this.y = 3; }", CheckGlobalThis.GLOBAL_THIS); + } + + public void testCheckGlobalThisOff() { + CompilerOptions options = createCompilerOptions(); + options.checkSuspiciousCode = true; + options.checkGlobalThisLevel = CheckLevel.OFF; + testSame(options, "function f() { this.y = 3; }"); + } + + public void testCheckRequiresAndCheckProvidesOff() { + testSame(createCompilerOptions(), new String[] { + "/** @constructor */ function Foo() {}", + "new Foo();" + }); + } + + public void testCheckRequiresOn() { + CompilerOptions options = createCompilerOptions(); + options.checkRequires = CheckLevel.ERROR; + test(options, new String[] { + "/** @constructor */ function Foo() {}", + "new Foo();" + }, CheckRequiresForConstructors.MISSING_REQUIRE_WARNING); + } + + public void testCheckProvidesOn() { + CompilerOptions options = createCompilerOptions(); + options.checkProvides = CheckLevel.ERROR; + test(options, new String[] { + "/** @constructor */ function Foo() {}", + "new Foo();" + }, CheckProvides.MISSING_PROVIDE_WARNING); + } + + public void testGenerateExportsOff() { + testSame(createCompilerOptions(), "/** @export */ function f() {}"); + } + + public void testGenerateExportsOn() { + CompilerOptions options = createCompilerOptions(); + options.generateExports = true; + test(options, "/** @export */ function f() {}", + "/** @export */ function f() {} goog.exportSymbol('f', f);"); + } + + public void testExportTestFunctionsOff() { + testSame(createCompilerOptions(), "function testFoo() {}"); + } + + public void testExportTestFunctionsOn() { + CompilerOptions options = createCompilerOptions(); + options.exportTestFunctions = true; + test(options, "function testFoo() {}", + "/** @export */ function testFoo() {}" + + "goog.exportSymbol('testFoo', testFoo);"); + } + + public void testExpose() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.ADVANCED_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + test(options, + "var x = {eeny: 1, /** @expose */ meeny: 2};" + + "/** @constructor */ var Foo = function() {};" + + "/** @expose */ Foo.prototype.miny = 3;" + + "Foo.prototype.moe = 4;" + + "function moe(a, b) { return a.meeny + b.miny; }" + + "window['x'] = x;" + + "window['Foo'] = Foo;" + + "window['moe'] = moe;", + "function a(){}" + + "a.prototype.miny=3;" + + "window.x={a:1,meeny:2};" + + "window.Foo=a;" + + "window.moe=function(b,c){" + + " return b.meeny+c.miny" + + "}"); + } + + public void testCheckSymbolsOff() { + CompilerOptions options = createCompilerOptions(); + testSame(options, "x = 3;"); + } + + public void testCheckSymbolsOn() { + CompilerOptions options = createCompilerOptions(); + options.checkSymbols = true; + test(options, "x = 3;", VarCheck.UNDEFINED_VAR_ERROR); + } + + public void testCheckReferencesOff() { + CompilerOptions options = createCompilerOptions(); + testSame(options, "x = 3; var x = 5;"); + } + + public void testCheckReferencesOn() { + CompilerOptions options = createCompilerOptions(); + options.aggressiveVarCheck = CheckLevel.ERROR; + test(options, "x = 3; var x = 5;", + VariableReferenceCheck.UNDECLARED_REFERENCE); + } + + public void testInferTypes() { + CompilerOptions options = createCompilerOptions(); + options.inferTypes = true; + options.checkTypes = false; + options.closurePass = true; + + test(options, + CLOSURE_BOILERPLATE + + "goog.provide('Foo'); /** @enum */ Foo = {a: 3};", + TypeCheck.ENUM_NOT_CONSTANT); + assertTrue(lastCompiler.getErrorManager().getTypedPercent() == 0); + + // This does not generate a warning. + test(options, "/** @type {number} */ var n = window.name;", + "var n = window.name;"); + assertTrue(lastCompiler.getErrorManager().getTypedPercent() == 0); + } + + public void testTypeCheckAndInference() { + CompilerOptions options = createCompilerOptions(); + options.checkTypes = true; + test(options, "/** @type {number} */ var n = window.name;", + TypeValidator.TYPE_MISMATCH_WARNING); + assertTrue(lastCompiler.getErrorManager().getTypedPercent() > 0); + } + + public void testTypeNameParser() { + CompilerOptions options = createCompilerOptions(); + options.checkTypes = true; + test(options, "/** @type {n} */ var n = window.name;", + RhinoErrorReporter.TYPE_PARSE_ERROR); + } + + // This tests that the TypedScopeCreator is memoized so that it only creates a + // Scope object once for each scope. If, when type inference requests a scope, + // it creates a new one, then multiple JSType objects end up getting created + // for the same local type, and ambiguate will rename the last statement to + // o.a(o.a, o.a), which is bad. + public void testMemoizedTypedScopeCreator() { + CompilerOptions options = createCompilerOptions(); + options.checkTypes = true; + options.ambiguateProperties = true; + options.propertyRenaming = PropertyRenamingPolicy.ALL_UNQUOTED; + test(options, "function someTest() {\n" + + " /** @constructor */\n" + + " function Foo() { this.instProp = 3; }\n" + + " Foo.prototype.protoProp = function(a, b) {};\n" + + " /** @constructor\n @extends Foo */\n" + + " function Bar() {}\n" + + " goog.inherits(Bar, Foo);\n" + + " var o = new Bar();\n" + + " o.protoProp(o.protoProp, o.instProp);\n" + + "}", + "function someTest() {\n" + + " function Foo() { this.b = 3; }\n" + + " function Bar() {}\n" + + " Foo.prototype.a = function(a, b) {};\n" + + " goog.c(Bar, Foo);\n" + + " var o = new Bar();\n" + + " o.a(o.a, o.b);\n" + + "}"); + } + + public void testCheckTypes() { + CompilerOptions options = createCompilerOptions(); + options.checkTypes = true; + test(options, "var x = x || {}; x.f = function() {}; x.f(3);", + TypeCheck.WRONG_ARGUMENT_COUNT); + } + + public void testReplaceCssNames() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.gatherCssNames = true; + test(options, "/** @define {boolean} */\n" + + "var COMPILED = false;\n" + + "goog.setCssNameMapping({'foo':'bar'});\n" + + "function getCss() {\n" + + " return goog.getCssName('foo');\n" + + "}", + "var COMPILED = true;\n" + + "function getCss() {\n" + + " return \"bar\";" + + "}"); + assertEquals( + ImmutableMap.of("foo", new Integer(1)), + lastCompiler.getPassConfig().getIntermediateState().cssNames); + } + + public void testRemoveClosureAsserts() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + testSame(options, + "var goog = {};" + + "goog.asserts.assert(goog);"); + options.removeClosureAsserts = true; + test(options, + "var goog = {};" + + "goog.asserts.assert(goog);", + "var goog = {};"); + } + + public void testDeprecation() { + String code = "/** @deprecated */ function f() { } function g() { f(); }"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.setWarningLevel(DiagnosticGroups.DEPRECATED, CheckLevel.ERROR); + testSame(options, code); + + options.checkTypes = true; + test(options, code, CheckAccessControls.DEPRECATED_NAME); + } + + public void testVisibility() { + String[] code = { + "/** @private */ function f() { }", + "function g() { f(); }" + }; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.setWarningLevel(DiagnosticGroups.VISIBILITY, CheckLevel.ERROR); + testSame(options, code); + + options.checkTypes = true; + test(options, code, CheckAccessControls.BAD_PRIVATE_GLOBAL_ACCESS); + } + + public void testUnreachableCode() { + String code = "function f() { return \n 3; }"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.checkUnreachableCode = CheckLevel.ERROR; + test(options, code, CheckUnreachableCode.UNREACHABLE_CODE); + } + + public void testMissingReturn() { + String code = + "/** @return {number} */ function f() { if (f) { return 3; } }"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.checkMissingReturn = CheckLevel.ERROR; + testSame(options, code); + + options.checkTypes = true; + test(options, code, CheckMissingReturn.MISSING_RETURN_STATEMENT); + } + + public void testIdGenerators() { + String code = "function f() {} f('id');"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.idGenerators = Sets.newHashSet("f"); + test(options, code, "function f() {} 'a';"); + } + + public void testOptimizeArgumentsArray() { + String code = "function f() { return arguments[0]; }"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.optimizeArgumentsArray = true; + String argName = "JSCompiler_OptimizeArgumentsArray_p0"; + test(options, code, + "function f(" + argName + ") { return " + argName + "; }"); + } + + public void testOptimizeParameters() { + String code = "function f(a) { return a; } f(true);"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.optimizeParameters = true; + test(options, code, "function f() { var a = true; return a;} f();"); + } + + public void testOptimizeReturns() { + String code = "function f(a) { return a; } f(true);"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.optimizeReturns = true; + test(options, code, "function f(a) {return;} f(true);"); + } + + public void testRemoveAbstractMethods() { + String code = CLOSURE_BOILERPLATE + + "var x = {}; x.foo = goog.abstractMethod; x.bar = 3;"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.closurePass = true; + options.collapseProperties = true; + test(options, code, CLOSURE_COMPILED + " var x$bar = 3;"); + } + + public void testCollapseProperties1() { + String code = + "var x = {}; x.FOO = 5; x.bar = 3;"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.collapseProperties = true; + test(options, code, "var x$FOO = 5; var x$bar = 3;"); + } + + public void testCollapseProperties2() { + String code = + "var x = {}; x.FOO = 5; x.bar = 3;"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.collapseProperties = true; + options.collapseObjectLiterals = true; + test(options, code, "var x$FOO = 5; var x$bar = 3;"); + } + + public void testCollapseObjectLiteral1() { + // Verify collapseObjectLiterals does nothing in global scope + String code = "var x = {}; x.FOO = 5; x.bar = 3;"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.collapseObjectLiterals = true; + testSame(options, code); + } + + public void testCollapseObjectLiteral2() { + String code = + "function f() {var x = {}; x.FOO = 5; x.bar = 3;}"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.collapseObjectLiterals = true; + test(options, code, + "function f(){" + + "var JSCompiler_object_inline_FOO_0;" + + "var JSCompiler_object_inline_bar_1;" + + "JSCompiler_object_inline_FOO_0=5;" + + "JSCompiler_object_inline_bar_1=3}"); + } + + public void testTightenTypesWithoutTypeCheck() { + CompilerOptions options = createCompilerOptions(); + options.tightenTypes = true; + test(options, "", DefaultPassConfig.TIGHTEN_TYPES_WITHOUT_TYPE_CHECK); + } + + public void testDisambiguateProperties() { + String code = + "/** @constructor */ function Foo(){} Foo.prototype.bar = 3;" + + "/** @constructor */ function Baz(){} Baz.prototype.bar = 3;"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.disambiguateProperties = true; + options.checkTypes = true; + test(options, code, + "function Foo(){} Foo.prototype.Foo_prototype$bar = 3;" + + "function Baz(){} Baz.prototype.Baz_prototype$bar = 3;"); + } + + public void testMarkPureCalls() { + String testCode = "function foo() {} foo();"; + CompilerOptions options = createCompilerOptions(); + options.removeDeadCode = true; + + testSame(options, testCode); + + options.computeFunctionSideEffects = true; + test(options, testCode, "function foo() {}"); + } + + public void testMarkNoSideEffects() { + String testCode = "noSideEffects();"; + CompilerOptions options = createCompilerOptions(); + options.removeDeadCode = true; + + testSame(options, testCode); + + options.markNoSideEffectCalls = true; + test(options, testCode, ""); + } + + public void testChainedCalls() { + CompilerOptions options = createCompilerOptions(); + options.chainCalls = true; + test( + options, + "/** @constructor */ function Foo() {} " + + "Foo.prototype.bar = function() { return this; }; " + + "var f = new Foo();" + + "f.bar(); " + + "f.bar(); ", + "function Foo() {} " + + "Foo.prototype.bar = function() { return this; }; " + + "var f = new Foo();" + + "f.bar().bar();"); + } + + public void testExtraAnnotationNames() { + CompilerOptions options = createCompilerOptions(); + options.setExtraAnnotationNames(Sets.newHashSet("TagA", "TagB")); + test( + options, + "/** @TagA */ var f = new Foo(); /** @TagB */ f.bar();", + "var f = new Foo(); f.bar();"); + } + + public void testDevirtualizePrototypeMethods() { + CompilerOptions options = createCompilerOptions(); + options.devirtualizePrototypeMethods = true; + test( + options, + "/** @constructor */ var Foo = function() {}; " + + "Foo.prototype.bar = function() {};" + + "(new Foo()).bar();", + "var Foo = function() {};" + + "var JSCompiler_StaticMethods_bar = " + + " function(JSCompiler_StaticMethods_bar$self) {};" + + "JSCompiler_StaticMethods_bar(new Foo());"); + } + + public void testCheckConsts() { + CompilerOptions options = createCompilerOptions(); + options.inlineConstantVars = true; + test(options, "var FOO = true; FOO = false", + ConstCheck.CONST_REASSIGNED_VALUE_ERROR); + } + + public void testAllChecksOn() { + CompilerOptions options = createCompilerOptions(); + options.checkSuspiciousCode = true; + options.checkControlStructures = true; + options.checkRequires = CheckLevel.ERROR; + options.checkProvides = CheckLevel.ERROR; + options.generateExports = true; + options.exportTestFunctions = true; + options.closurePass = true; + options.checkMissingGetCssNameLevel = CheckLevel.ERROR; + options.checkMissingGetCssNameBlacklist = "goog"; + options.syntheticBlockStartMarker = "synStart"; + options.syntheticBlockEndMarker = "synEnd"; + options.checkSymbols = true; + options.aggressiveVarCheck = CheckLevel.ERROR; + options.processObjectPropertyString = true; + options.collapseProperties = true; + test(options, CLOSURE_BOILERPLATE, CLOSURE_COMPILED); + } + + public void testTypeCheckingWithSyntheticBlocks() { + CompilerOptions options = createCompilerOptions(); + options.syntheticBlockStartMarker = "synStart"; + options.syntheticBlockEndMarker = "synEnd"; + options.checkTypes = true; + + // We used to have a bug where the CFG drew an + // edge straight from synStart to f(progress). + // If that happens, then progress will get type {number|undefined}. + testSame( + options, + "/** @param {number} x */ function f(x) {}" + + "function g() {" + + " synStart('foo');" + + " var progress = 1;" + + " f(progress);" + + " synEnd('foo');" + + "}"); + } + + public void testCompilerDoesNotBlowUpIfUndefinedSymbols() { + CompilerOptions options = createCompilerOptions(); + options.checkSymbols = true; + + // Disable the undefined variable check. + options.setWarningLevel( + DiagnosticGroup.forType(VarCheck.UNDEFINED_VAR_ERROR), + CheckLevel.OFF); + + // The compiler used to throw an IllegalStateException on this. + testSame(options, "var x = {foo: y};"); + } + + // Make sure that if we change variables which are constant to have + // $$constant appended to their names, we remove that tag before + // we finish. + public void testConstantTagsMustAlwaysBeRemoved() { + CompilerOptions options = createCompilerOptions(); + + options.variableRenaming = VariableRenamingPolicy.LOCAL; + String originalText = "var G_GEO_UNKNOWN_ADDRESS=1;\n" + + "function foo() {" + + " var localVar = 2;\n" + + " if (G_GEO_UNKNOWN_ADDRESS == localVar) {\n" + + " alert(\"A\"); }}"; + String expectedText = "var G_GEO_UNKNOWN_ADDRESS=1;" + + "function foo(){var a=2;if(G_GEO_UNKNOWN_ADDRESS==a){alert(\"A\")}}"; + + test(options, originalText, expectedText); + } + + public void testClosurePassPreservesJsDoc() { + CompilerOptions options = createCompilerOptions(); + options.checkTypes = true; + options.closurePass = true; + + test(options, + CLOSURE_BOILERPLATE + + "goog.provide('Foo'); /** @constructor */ Foo = function() {};" + + "var x = new Foo();", + "var COMPILED=true;var goog={};goog.exportSymbol=function(){};" + + "var Foo=function(){};var x=new Foo"); + test(options, + CLOSURE_BOILERPLATE + + "goog.provide('Foo'); /** @enum */ Foo = {a: 3};", + TypeCheck.ENUM_NOT_CONSTANT); + } + + public void testProvidedNamespaceIsConst() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.inlineConstantVars = true; + options.collapseProperties = true; + test(options, + "var goog = {}; goog.provide('foo'); " + + "function f() { foo = {};}", + "var foo = {}; function f() { foo = {}; }", + ConstCheck.CONST_REASSIGNED_VALUE_ERROR); + } + + public void testProvidedNamespaceIsConst2() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.inlineConstantVars = true; + options.collapseProperties = true; + test(options, + "var goog = {}; goog.provide('foo.bar'); " + + "function f() { foo.bar = {};}", + "var foo$bar = {};" + + "function f() { foo$bar = {}; }", + ConstCheck.CONST_REASSIGNED_VALUE_ERROR); + } + + public void testProvidedNamespaceIsConst3() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.inlineConstantVars = true; + options.collapseProperties = true; + test(options, + "var goog = {}; " + + "goog.provide('foo.bar'); goog.provide('foo.bar.baz'); " + + "/** @constructor */ foo.bar = function() {};" + + "/** @constructor */ foo.bar.baz = function() {};", + "var foo$bar = function(){};" + + "var foo$bar$baz = function(){};"); + } + + public void testProvidedNamespaceIsConst4() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.inlineConstantVars = true; + options.collapseProperties = true; + test(options, + "var goog = {}; goog.provide('foo.Bar'); " + + "var foo = {}; foo.Bar = {};", + "var foo = {}; foo = {}; foo.Bar = {};"); + } + + public void testProvidedNamespaceIsConst5() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.inlineConstantVars = true; + options.collapseProperties = true; + test(options, + "var goog = {}; goog.provide('foo.Bar'); " + + "foo = {}; foo.Bar = {};", + "var foo = {}; foo = {}; foo.Bar = {};"); + } + + public void testProcessDefinesAlwaysOn() { + test(createCompilerOptions(), + "/** @define {boolean} */ var HI = true; HI = false;", + "var HI = false;false;"); + } + + public void testProcessDefinesAdditionalReplacements() { + CompilerOptions options = createCompilerOptions(); + options.setDefineToBooleanLiteral("HI", false); + test(options, + "/** @define {boolean} */ var HI = true;", + "var HI = false;"); + } + + public void testReplaceMessages() { + CompilerOptions options = createCompilerOptions(); + String prefix = "var goog = {}; goog.getMsg = function() {};"; + testSame(options, prefix + "var MSG_HI = goog.getMsg('hi');"); + + options.messageBundle = new EmptyMessageBundle(); + test(options, + prefix + "/** @desc xyz */ var MSG_HI = goog.getMsg('hi');", + prefix + "var MSG_HI = 'hi';"); + } + + public void testCheckGlobalNames() { + CompilerOptions options = createCompilerOptions(); + options.checkGlobalNamesLevel = CheckLevel.ERROR; + test(options, "var x = {}; var y = x.z;", + CheckGlobalNames.UNDEFINED_NAME_WARNING); + } + + public void testInlineGetters() { + CompilerOptions options = createCompilerOptions(); + String code = + "function Foo() {} Foo.prototype.bar = function() { return 3; };" + + "var x = new Foo(); x.bar();"; + + testSame(options, code); + options.inlineGetters = true; + + test(options, code, + "function Foo() {} Foo.prototype.bar = function() { return 3 };" + + "var x = new Foo(); 3;"); + } + + public void testInlineGettersWithAmbiguate() { + CompilerOptions options = createCompilerOptions(); + + String code = + "/** @constructor */" + + "function Foo() {}" + + "/** @type {number} */ Foo.prototype.field;" + + "Foo.prototype.getField = function() { return this.field; };" + + "/** @constructor */" + + "function Bar() {}" + + "/** @type {string} */ Bar.prototype.field;" + + "Bar.prototype.getField = function() { return this.field; };" + + "new Foo().getField();" + + "new Bar().getField();"; + + testSame(options, code); + + options.inlineGetters = true; + + test(options, code, + "function Foo() {}" + + "Foo.prototype.field;" + + "Foo.prototype.getField = function() { return this.field; };" + + "function Bar() {}" + + "Bar.prototype.field;" + + "Bar.prototype.getField = function() { return this.field; };" + + "new Foo().field;" + + "new Bar().field;"); + + options.checkTypes = true; + options.ambiguateProperties = true; + + // Propagating the wrong type information may cause ambiguate properties + // to generate bad code. + testSame(options, code); + } + + public void testInlineVariables() { + CompilerOptions options = createCompilerOptions(); + String code = "function foo() {} var x = 3; foo(x);"; + testSame(options, code); + + options.inlineVariables = true; + test(options, code, "(function foo() {})(3);"); + + options.propertyRenaming = PropertyRenamingPolicy.HEURISTIC; + test(options, code, DefaultPassConfig.CANNOT_USE_PROTOTYPE_AND_VAR); + } + + public void testInlineConstants() { + CompilerOptions options = createCompilerOptions(); + String code = "function foo() {} var x = 3; foo(x); var YYY = 4; foo(YYY);"; + testSame(options, code); + + options.inlineConstantVars = true; + test(options, code, "function foo() {} var x = 3; foo(x); foo(4);"); + } + + public void testMinimizeExits() { + CompilerOptions options = createCompilerOptions(); + String code = + "function f() {" + + " if (window.foo) return; window.h(); " + + "}"; + testSame(options, code); + + options.foldConstants = true; + test( + options, code, + "function f() {" + + " window.foo || window.h(); " + + "}"); + } + + public void testFoldConstants() { + CompilerOptions options = createCompilerOptions(); + String code = "if (true) { window.foo(); }"; + testSame(options, code); + + options.foldConstants = true; + test(options, code, "window.foo();"); + } + + public void testRemoveUnreachableCode() { + CompilerOptions options = createCompilerOptions(); + String code = "function f() { return; f(); }"; + testSame(options, code); + + options.removeDeadCode = true; + test(options, code, "function f() {}"); + } + + public void testRemoveUnusedPrototypeProperties1() { + CompilerOptions options = createCompilerOptions(); + String code = "function Foo() {} " + + "Foo.prototype.bar = function() { return new Foo(); };"; + testSame(options, code); + + options.removeUnusedPrototypeProperties = true; + test(options, code, "function Foo() {}"); + } + + public void testRemoveUnusedPrototypeProperties2() { + CompilerOptions options = createCompilerOptions(); + String code = "function Foo() {} " + + "Foo.prototype.bar = function() { return new Foo(); };" + + "function f(x) { x.bar(); }"; + testSame(options, code); + + options.removeUnusedPrototypeProperties = true; + testSame(options, code); + + options.removeUnusedVars = true; + test(options, code, ""); + } + + public void testSmartNamePass() { + CompilerOptions options = createCompilerOptions(); + String code = "function Foo() { this.bar(); } " + + "Foo.prototype.bar = function() { return Foo(); };"; + testSame(options, code); + + options.smartNameRemoval = true; + test(options, code, ""); + } + + public void testDeadAssignmentsElimination() { + CompilerOptions options = createCompilerOptions(); + String code = "function f() { var x = 3; 4; x = 5; return x; } f(); "; + testSame(options, code); + + options.deadAssignmentElimination = true; + testSame(options, code); + + options.removeUnusedVars = true; + test(options, code, "function f() { var x = 3; 4; x = 5; return x; } f();"); + } + + public void testInlineFunctions() { + CompilerOptions options = createCompilerOptions(); + String code = "function f() { return 3; } f(); "; + testSame(options, code); + + options.inlineFunctions = true; + test(options, code, "3;"); + } + + public void testRemoveUnusedVars1() { + CompilerOptions options = createCompilerOptions(); + String code = "function f(x) {} f();"; + testSame(options, code); + + options.removeUnusedVars = true; + test(options, code, "function f() {} f();"); + } + + public void testRemoveUnusedVars2() { + CompilerOptions options = createCompilerOptions(); + String code = "(function f(x) {})();var g = function() {}; g();"; + testSame(options, code); + + options.removeUnusedVars = true; + test(options, code, "(function() {})();var g = function() {}; g();"); + + options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.UNMAPPED; + test(options, code, "(function f() {})();var g = function $g$() {}; g();"); + } + + public void testCrossModuleCodeMotion() { + CompilerOptions options = createCompilerOptions(); + String[] code = new String[] { + "var x = 1;", + "x;", + }; + testSame(options, code); + + options.crossModuleCodeMotion = true; + test(options, code, new String[] { + "", + "var x = 1; x;", + }); + } + + public void testCrossModuleMethodMotion() { + CompilerOptions options = createCompilerOptions(); + String[] code = new String[] { + "var Foo = function() {}; Foo.prototype.bar = function() {};" + + "var x = new Foo();", + "x.bar();", + }; + testSame(options, code); + + options.crossModuleMethodMotion = true; + test(options, code, new String[] { + CrossModuleMethodMotion.STUB_DECLARATIONS + + "var Foo = function() {};" + + "Foo.prototype.bar=JSCompiler_stubMethod(0); var x=new Foo;", + "Foo.prototype.bar=JSCompiler_unstubMethod(0,function(){}); x.bar()", + }); + } + + public void testFlowSensitiveInlineVariables1() { + CompilerOptions options = createCompilerOptions(); + String code = "function f() { var x = 3; x = 5; return x; }"; + testSame(options, code); + + options.flowSensitiveInlineVariables = true; + test(options, code, "function f() { var x = 3; return 5; }"); + + String unusedVar = "function f() { var x; x = 5; return x; } f()"; + test(options, unusedVar, "function f() { var x; return 5; } f()"); + + options.removeUnusedVars = true; + test(options, unusedVar, "function f() { return 5; } f()"); + } + + public void testFlowSensitiveInlineVariables2() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.SIMPLE_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + test(options, + "function f () {\n" + + " var ab = 0;\n" + + " ab += '-';\n" + + " alert(ab);\n" + + "}", + "function f () {\n" + + " alert('0-');\n" + + "}"); + } + + public void testCollapseAnonymousFunctions() { + CompilerOptions options = createCompilerOptions(); + String code = "var f = function() {};"; + testSame(options, code); + + options.collapseAnonymousFunctions = true; + test(options, code, "function f() {}"); + } + + public void testMoveFunctionDeclarations() { + CompilerOptions options = createCompilerOptions(); + String code = "var x = f(); function f() { return 3; }"; + testSame(options, code); + + options.moveFunctionDeclarations = true; + test(options, code, "function f() { return 3; } var x = f();"); + } + + public void testNameAnonymousFunctions() { + CompilerOptions options = createCompilerOptions(); + String code = "var f = function() {};"; + testSame(options, code); + + options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.MAPPED; + test(options, code, "var f = function $() {}"); + assertNotNull(lastCompiler.getResult().namedAnonFunctionMap); + + options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.UNMAPPED; + test(options, code, "var f = function $f$() {}"); + assertNull(lastCompiler.getResult().namedAnonFunctionMap); + } + + public void testNameAnonymousFunctionsWithVarRemoval() { + CompilerOptions options = createCompilerOptions(); + options.setRemoveUnusedVariables(CompilerOptions.Reach.LOCAL_ONLY); + options.setInlineVariables(true); + String code = "var f = function longName() {}; var g = function() {};" + + "function longerName() {} var i = longerName;"; + test(options, code, + "var f = function() {}; var g = function() {}; " + + "var i = function() {};"); + + options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.MAPPED; + test(options, code, + "var f = function longName() {}; var g = function $() {};" + + "var i = function longerName(){};"); + assertNotNull(lastCompiler.getResult().namedAnonFunctionMap); + + options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.UNMAPPED; + test(options, code, + "var f = function longName() {}; var g = function $g$() {};" + + "var i = function longerName(){};"); + assertNull(lastCompiler.getResult().namedAnonFunctionMap); + } + + public void testExtractPrototypeMemberDeclarations() { + CompilerOptions options = createCompilerOptions(); + String code = "var f = function() {};"; + String expected = "var a; var b = function() {}; a = b.prototype;"; + for (int i = 0; i < 10; i++) { + code += "f.prototype.a = " + i + ";"; + expected += "a.a = " + i + ";"; + } + testSame(options, code); + + options.extractPrototypeMemberDeclarations = true; + options.variableRenaming = VariableRenamingPolicy.ALL; + test(options, code, expected); + + options.propertyRenaming = PropertyRenamingPolicy.HEURISTIC; + options.variableRenaming = VariableRenamingPolicy.OFF; + testSame(options, code); + } + + public void testDevirtualizationAndExtractPrototypeMemberDeclarations() { + CompilerOptions options = createCompilerOptions(); + options.devirtualizePrototypeMethods = true; + options.collapseAnonymousFunctions = true; + options.extractPrototypeMemberDeclarations = true; + options.variableRenaming = VariableRenamingPolicy.ALL; + String code = "var f = function() {};"; + String expected = "var a; function b() {} a = b.prototype;"; + for (int i = 0; i < 10; i++) { + code += "f.prototype.argz = function() {arguments};"; + code += "f.prototype.devir" + i + " = function() {};"; + + char letter = (char) ('d' + i); + expected += "a.argz = function() {arguments};"; + expected += "function " + letter + "(c){}"; + } + + code += "var F = new f(); F.argz();"; + expected += "var n = new b(); n.argz();"; + + for (int i = 0; i < 10; i++) { + code += "F.devir" + i + "();"; + + char letter = (char) ('d' + i); + expected += letter + "(n);"; + } + test(options, code, expected); + } + + public void testCoalesceVariableNames() { + CompilerOptions options = createCompilerOptions(); + String code = "function f() {var x = 3; var y = x; var z = y; return z;}"; + testSame(options, code); + + options.coalesceVariableNames = true; + test(options, code, + "function f() {var x = 3; x = x; x = x; return x;}"); + } + + public void testPropertyRenaming() { + CompilerOptions options = createCompilerOptions(); + options.propertyAffinity = true; + String code = + "function f() { return this.foo + this['bar'] + this.Baz; }" + + "f.prototype.bar = 3; f.prototype.Baz = 3;"; + String heuristic = + "function f() { return this.foo + this['bar'] + this.a; }" + + "f.prototype.bar = 3; f.prototype.a = 3;"; + String aggHeuristic = + "function f() { return this.foo + this['b'] + this.a; } " + + "f.prototype.b = 3; f.prototype.a = 3;"; + String all = + "function f() { return this.b + this['bar'] + this.a; }" + + "f.prototype.c = 3; f.prototype.a = 3;"; + testSame(options, code); + + options.propertyRenaming = PropertyRenamingPolicy.HEURISTIC; + test(options, code, heuristic); + + options.propertyRenaming = PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC; + test(options, code, aggHeuristic); + + options.propertyRenaming = PropertyRenamingPolicy.ALL_UNQUOTED; + test(options, code, all); + } + + public void testConvertToDottedProperties() { + CompilerOptions options = createCompilerOptions(); + String code = + "function f() { return this['bar']; } f.prototype.bar = 3;"; + String expected = + "function f() { return this.bar; } f.prototype.a = 3;"; + testSame(options, code); + + options.convertToDottedProperties = true; + options.propertyRenaming = PropertyRenamingPolicy.ALL_UNQUOTED; + test(options, code, expected); + } + + public void testRewriteFunctionExpressions() { + CompilerOptions options = createCompilerOptions(); + String code = "var a = function() {};"; + String expected = "function JSCompiler_emptyFn(){return function(){}} " + + "var a = JSCompiler_emptyFn();"; + for (int i = 0; i < 10; i++) { + code += "a = function() {};"; + expected += "a = JSCompiler_emptyFn();"; + } + testSame(options, code); + + options.rewriteFunctionExpressions = true; + test(options, code, expected); + } + + public void testAliasAllStrings() { + CompilerOptions options = createCompilerOptions(); + String code = "function f() { return 'a'; }"; + String expected = "var $$S_a = 'a'; function f() { return $$S_a; }"; + testSame(options, code); + + options.aliasAllStrings = true; + test(options, code, expected); + } + + public void testAliasExterns() { + CompilerOptions options = createCompilerOptions(); + String code = "function f() { return window + window + window + window; }"; + String expected = "var GLOBAL_window = window;" + + "function f() { return GLOBAL_window + GLOBAL_window + " + + " GLOBAL_window + GLOBAL_window; }"; + testSame(options, code); + + options.aliasExternals = true; + test(options, code, expected); + } + + public void testAliasKeywords() { + CompilerOptions options = createCompilerOptions(); + String code = + "function f() { return true + true + true + true + true + true; }"; + String expected = "var JSCompiler_alias_TRUE = true;" + + "function f() { return JSCompiler_alias_TRUE + " + + " JSCompiler_alias_TRUE + JSCompiler_alias_TRUE + " + + " JSCompiler_alias_TRUE + JSCompiler_alias_TRUE + " + + " JSCompiler_alias_TRUE; }"; + testSame(options, code); + + options.aliasKeywords = true; + test(options, code, expected); + } + + public void testRenameVars1() { + CompilerOptions options = createCompilerOptions(); + String code = + "var abc = 3; function f() { var xyz = 5; return abc + xyz; }"; + String local = "var abc = 3; function f() { var a = 5; return abc + a; }"; + String all = "var a = 3; function c() { var b = 5; return a + b; }"; + testSame(options, code); + + options.variableRenaming = VariableRenamingPolicy.LOCAL; + test(options, code, local); + + options.variableRenaming = VariableRenamingPolicy.ALL; + test(options, code, all); + + options.reserveRawExports = true; + } + + public void testRenameVars2() { + CompilerOptions options = createCompilerOptions(); + options.variableRenaming = VariableRenamingPolicy.ALL; + + String code = "var abc = 3; function f() { window['a'] = 5; }"; + String noexport = "var a = 3; function b() { window['a'] = 5; }"; + String export = "var b = 3; function c() { window['a'] = 5; }"; + + options.reserveRawExports = false; + test(options, code, noexport); + + options.reserveRawExports = true; + test(options, code, export); + } + + public void testShadowVaribles() { + CompilerOptions options = createCompilerOptions(); + options.variableRenaming = VariableRenamingPolicy.LOCAL; + options.shadowVariables = true; + String code = "var f = function(x) { return function(y) {}}"; + String expected = "var f = function(a) { return function(a) {}}"; + test(options, code, expected); + } + + public void testRenameLabels() { + CompilerOptions options = createCompilerOptions(); + String code = "longLabel: for(;true;) { break longLabel; }"; + String expected = "a: for(;true;) { break a; }"; + testSame(options, code); + + options.labelRenaming = true; + test(options, code, expected); + } + + public void testBadBreakStatementInIdeMode() { + // Ensure that type-checking doesn't crash, even if the CFG is malformed. + // This can happen in IDE mode. + CompilerOptions options = createCompilerOptions(); + options.ideMode = true; + options.checkTypes = true; + test(options, + "function f() { try { } catch(e) { break; } }", + RhinoErrorReporter.PARSE_ERROR); + } + + public void testIssue63SourceMap() { + CompilerOptions options = createCompilerOptions(); + String code = "var a;"; + + options.skipAllPasses = true; + options.sourceMapOutputPath = "./src.map"; + + Compiler compiler = compile(options, code); + compiler.toSource(); + } + + public void testRegExp1() { + CompilerOptions options = createCompilerOptions(); + options.foldConstants = true; + + String code = "/(a)/.test(\"a\");"; + + testSame(options, code); + + options.computeFunctionSideEffects = true; + + String expected = ""; + + test(options, code, expected); + } + + public void testRegExp2() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = true; + + String code = "/(a)/.test(\"a\");var a = RegExp.$1"; + + testSame(options, code); + + options.computeFunctionSideEffects = true; + + test(options, code, CheckRegExp.REGEXP_REFERENCE); + + options.setWarningLevel(DiagnosticGroups.CHECK_REGEXP, CheckLevel.OFF); + + testSame(options, code); + } + + public void testFoldLocals1() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = true; + + // An external object, whose constructor has no side-effects, + // and whose method "go" only modifies the object. + String code = "new Widget().go();"; + + testSame(options, code); + + options.computeFunctionSideEffects = true; + + test(options, code, ""); + } + + public void testFoldLocals2() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = true; + options.checkTypes = true; + + // An external function that returns a local object that the + // method "go" that only modifies the object. + String code = "widgetToken().go();"; + + testSame(options, code); + + options.computeFunctionSideEffects = true; + + test(options, code, "widgetToken()"); + } + + + public void testFoldLocals3() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = true; + + // A function "f" who returns a known local object, and a method that + // modifies only modifies that. + String definition = "function f(){return new Widget()}"; + String call = "f().go();"; + String code = definition + call; + + testSame(options, code); + + options.computeFunctionSideEffects = true; + + // BROKEN + //test(options, code, definition); + testSame(options, code); + } + + public void testFoldLocals4() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = true; + + String code = "/** @constructor */\n" + + "function InternalWidget(){this.x = 1;}" + + "InternalWidget.prototype.internalGo = function (){this.x = 2};" + + "new InternalWidget().internalGo();"; + + testSame(options, code); + + options.computeFunctionSideEffects = true; + + String optimized = "" + + "function InternalWidget(){this.x = 1;}" + + "InternalWidget.prototype.internalGo = function (){this.x = 2};"; + + test(options, code, optimized); + } + + public void testFoldLocals5() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = true; + + String code = "" + + "function fn(){var a={};a.x={};return a}" + + "fn().x.y = 1;"; + + // "fn" returns a unescaped local object, we should be able to fold it, + // but we don't currently. + String result = "" + + "function fn(){var a={x:{}};return a}" + + "fn().x.y = 1;"; + + test(options, code, result); + + options.computeFunctionSideEffects = true; + + test(options, code, result); + } + + public void testFoldLocals6() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = true; + + String code = "" + + "function fn(){return {}}" + + "fn().x.y = 1;"; + + testSame(options, code); + + options.computeFunctionSideEffects = true; + + testSame(options, code); + } + + public void testFoldLocals7() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = true; + + String code = "" + + "function InternalWidget(){return [];}" + + "Array.prototype.internalGo = function (){this.x = 2};" + + "InternalWidget().internalGo();"; + + testSame(options, code); + + options.computeFunctionSideEffects = true; + + String optimized = "" + + "function InternalWidget(){return [];}" + + "Array.prototype.internalGo = function (){this.x = 2};"; + + test(options, code, optimized); + } + + public void testVarDeclarationsIntoFor() { + CompilerOptions options = createCompilerOptions(); + + options.collapseVariableDeclarations = false; + + String code = "var a = 1; for (var b = 2; ;) {}"; + + testSame(options, code); + + options.collapseVariableDeclarations = true; + + test(options, code, "for (var a = 1, b = 2; ;) {}"); + } + + public void testExploitAssigns() { + CompilerOptions options = createCompilerOptions(); + + options.collapseVariableDeclarations = false; + + String code = "a = 1; b = a; c = b"; + + testSame(options, code); + + options.collapseVariableDeclarations = true; + + test(options, code, "c=b=a=1"); + } + + public void testRecoverOnBadExterns() throws Exception { + // This test is for a bug in a very narrow set of circumstances: + // 1) externs validation has to be off. + // 2) aliasExternals has to be on. + // 3) The user has to reference a "normal" variable in externs. + // This case is handled at checking time by injecting a + // synthetic extern variable, and adding a "@suppress {duplicate}" to + // the normal code at compile time. But optimizations may remove that + // annotation, so we need to make sure that the variable declarations + // are de-duped before that happens. + CompilerOptions options = createCompilerOptions(); + + options.aliasExternals = true; + externs = ImmutableList.of( + SourceFile.fromCode("externs", "extern.foo")); + + test(options, + "var extern; " + + "function f() { return extern + extern + extern + extern; }", + "var extern; " + + "function f() { return extern + extern + extern + extern; }", + VarCheck.UNDEFINED_EXTERN_VAR_ERROR); + } + + public void testDuplicateVariablesInExterns() { + CompilerOptions options = createCompilerOptions(); + options.checkSymbols = true; + externs = ImmutableList.of( + SourceFile.fromCode("externs", + "var externs = {}; /** @suppress {duplicate} */ var externs = {};")); + testSame(options, ""); + } + + public void testLanguageMode() { + CompilerOptions options = createCompilerOptions(); + options.setLanguageIn(LanguageMode.ECMASCRIPT3); + + String code = "var a = {get f(){}}"; + + Compiler compiler = compile(options, code); + checkUnexpectedErrorsOrWarnings(compiler, 1); + assertEquals( + "JSC_PARSE_ERROR. Parse error. " + + "getters are not supported in older versions of JS. " + + "If you are targeting newer versions of JS, " + + "set the appropriate language_in option. " + + "at i0 line 1 : 0", + compiler.getErrors()[0].toString()); + + options.setLanguageIn(LanguageMode.ECMASCRIPT5); + + testSame(options, code); + + options.setLanguageIn(LanguageMode.ECMASCRIPT5_STRICT); + + testSame(options, code); + } + + public void testLanguageMode2() { + CompilerOptions options = createCompilerOptions(); + options.setLanguageIn(LanguageMode.ECMASCRIPT3); + options.setWarningLevel(DiagnosticGroups.ES5_STRICT, CheckLevel.OFF); + + String code = "var a = 2; delete a;"; + + testSame(options, code); + + options.setLanguageIn(LanguageMode.ECMASCRIPT5); + + testSame(options, code); + + options.setLanguageIn(LanguageMode.ECMASCRIPT5_STRICT); + + test(options, + code, + code, + StrictModeCheck.DELETE_VARIABLE); + } + + public void testIssue598() { + CompilerOptions options = createCompilerOptions(); + options.setLanguageIn(LanguageMode.ECMASCRIPT5_STRICT); + WarningLevel.VERBOSE.setOptionsForWarningLevel(options); + + options.setLanguageIn(LanguageMode.ECMASCRIPT5); + + String code = + "'use strict';\n" + + "function App() {}\n" + + "App.prototype = {\n" + + " get appData() { return this.appData_; },\n" + + " set appData(data) { this.appData_ = data; }\n" + + "};"; + + Compiler compiler = compile(options, code); + testSame(options, code); + } + + public void testIssue701() { + // Check ASCII art in license comments. + String ascii = "/**\n" + + " * @preserve\n" + + " This\n" + + " is\n" + + " ASCII ART\n" + + "*/"; + String result = "/*\n\n" + + " This\n" + + " is\n" + + " ASCII ART\n" + + "*/\n"; + testSame(createCompilerOptions(), ascii); + assertEquals(result, lastCompiler.toSource()); + } + + public void testIssue724() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.ADVANCED_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + String code = + "isFunction = function(functionToCheck) {" + + " var getType = {};" + + " return functionToCheck && " + + " getType.toString.apply(functionToCheck) === " + + " '[object Function]';" + + "};"; + String result = + "isFunction=function(a){var b={};" + + "return a&&\"[object Function]\"===b.b.a(a)}"; + + test(options, code, result); + } + + public void testIssue730() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.ADVANCED_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + + String code = + "/** @constructor */function A() {this.foo = 0; Object.seal(this);}\n" + + "/** @constructor */function B() {this.a = new A();}\n" + + "B.prototype.dostuff = function() {this.a.foo++;alert('hi');}\n" + + "new B().dostuff();\n"; + + test(options, + code, + "function a(){this.b=0;Object.seal(this)}" + + "(new function(){this.a=new a}).a.b++;" + + "alert(\"hi\")"); + + options.removeUnusedClassProperties = true; + + // This is still a problem when removeUnusedClassProperties are enabled. + test(options, + code, + "function a(){Object.seal(this)}" + + "(new function(){this.a=new a}).a.b++;" + + "alert(\"hi\")"); + } + + public void testCoaleseVariables() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = false; + options.coalesceVariableNames = true; + + String code = + "function f(a) {" + + " if (a) {" + + " return a;" + + " } else {" + + " var b = a;" + + " return b;" + + " }" + + " return a;" + + "}"; + String expected = + "function f(a) {" + + " if (a) {" + + " return a;" + + " } else {" + + " a = a;" + + " return a;" + + " }" + + " return a;" + + "}"; + + test(options, code, expected); + + options.foldConstants = true; + options.coalesceVariableNames = false; + + code = + "function f(a) {" + + " if (a) {" + + " return a;" + + " } else {" + + " var b = a;" + + " return b;" + + " }" + + " return a;" + + "}"; + expected = + "function f(a) {" + + " if (!a) {" + + " var b = a;" + + " return b;" + + " }" + + " return a;" + + "}"; + + test(options, code, expected); + + options.foldConstants = true; + options.coalesceVariableNames = true; + + expected = + "function f(a) {" + + " return a;" + + "}"; + + test(options, code, expected); + } + + public void testLateStatementFusion() { + CompilerOptions options = createCompilerOptions(); + options.foldConstants = true; + test(options, + "while(a){a();if(b){b();b()}}", + "for(;a;)a(),b&&(b(),b())"); + } + + public void testLateConstantReordering() { + CompilerOptions options = createCompilerOptions(); + options.foldConstants = true; + test(options, + "if (x < 1 || x > 1 || 1 < x || 1 > x) { alert(x) }", + " (1 > x || 1 < x || 1 < x || 1 > x) && alert(x) "); + } + + public void testsyntheticBlockOnDeadAssignments() { + CompilerOptions options = createCompilerOptions(); + options.deadAssignmentElimination = true; + options.removeUnusedVars = true; + options.syntheticBlockStartMarker = "START"; + options.syntheticBlockEndMarker = "END"; + test(options, "var x; x = 1; START(); x = 1;END();x()", + "var x; x = 1;{START();{x = 1}END()}x()"); + } + + public void testBug4152835() { + CompilerOptions options = createCompilerOptions(); + options.foldConstants = true; + options.syntheticBlockStartMarker = "START"; + options.syntheticBlockEndMarker = "END"; + test(options, "START();END()", "{START();{}END()}"); + } + + public void testBug5786871() { + CompilerOptions options = createCompilerOptions(); + options.ideMode = true; + test(options, "function () {}", RhinoErrorReporter.PARSE_ERROR); + } + + public void testIssue378() { + CompilerOptions options = createCompilerOptions(); + options.inlineVariables = true; + options.flowSensitiveInlineVariables = true; + testSame(options, "function f(c) {var f = c; arguments[0] = this;" + + " f.apply(this, arguments); return this;}"); + } + + public void testIssue550() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.SIMPLE_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + options.foldConstants = true; + options.inlineVariables = true; + options.flowSensitiveInlineVariables = true; + test(options, + "function f(h) {\n" + + " var a = h;\n" + + " a = a + 'x';\n" + + " a = a + 'y';\n" + + " return a;\n" + + "}", + // This should eventually get inlined completely. + "function f(a) { a += 'x'; return a += 'y'; }"); + } + + public void testIssue284() { + CompilerOptions options = createCompilerOptions(); + options.smartNameRemoval = true; + test(options, + "var goog = {};" + + "goog.inherits = function(x, y) {};" + + "var ns = {};" + + "/** @constructor */" + + "ns.PageSelectionModel = function() {};" + + "/** @constructor */" + + "ns.PageSelectionModel.FooEvent = function() {};" + + "/** @constructor */" + + "ns.PageSelectionModel.SelectEvent = function() {};" + + "goog.inherits(ns.PageSelectionModel.ChangeEvent," + + " ns.PageSelectionModel.FooEvent);", + ""); + } + + public void testIssue772() throws Exception { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.checkTypes = true; + test( + options, + "/** @const */ var a = {};" + + "/** @const */ a.b = {};" + + "/** @const */ a.b.c = {};" + + "goog.scope(function() {" + + " var b = a.b;" + + " var c = b.c;" + + " /** @typedef {string} */" + + " c.MyType;" + + " /** @param {c.MyType} x The variable. */" + + " c.myFunc = function(x) {};" + + "});", + "/** @const */ var a = {};" + + "/** @const */ a.b = {};" + + "/** @const */ a.b.c = {};" + + "a.b.c.MyType;" + + "a.b.c.myFunc = function(x) {};"); + } + + public void testCodingConvention() { + Compiler compiler = new Compiler(); + compiler.initOptions(new CompilerOptions()); + assertEquals( + compiler.getCodingConvention().getClass().toString(), + ClosureCodingConvention.class.toString()); + } + + public void testJQueryStringSplitLoops() { + CompilerOptions options = createCompilerOptions(); + options.foldConstants = true; + test(options, + "var x=['1','2','3','4','5','6','7']", + "var x='1234567'.split('')"); + + options = createCompilerOptions(); + options.foldConstants = true; + options.computeFunctionSideEffects = false; + options.removeUnusedVars = true; + + // If we do splits too early, it would add a side-effect to x. + test(options, + "var x=['1','2','3','4','5','6','7']", + ""); + + } + + public void testAlwaysRunSafetyCheck() { + CompilerOptions options = createCompilerOptions(); + options.checkSymbols = false; + options.customPasses = ArrayListMultimap.create(); + options.customPasses.put( + CustomPassExecutionTime.BEFORE_OPTIMIZATIONS, + new CompilerPass() { + @Override public void process(Node externs, Node root) { + Node var = root.getLastChild().getFirstChild(); + assertEquals(Token.VAR, var.getType()); + var.detachFromParent(); + } + }); + try { + test(options, + "var x = 3; function f() { return x + z; }", + "function f() { return x + z; }"); + fail("Expected run-time exception"); + } catch (RuntimeException e) { + assertTrue(e.getMessage().indexOf("Unexpected variable x") != -1); + } + } + + public void testSuppressEs5StrictWarning() { + CompilerOptions options = createCompilerOptions(); + options.setWarningLevel(DiagnosticGroups.ES5_STRICT, CheckLevel.WARNING); + test(options, + "/** @suppress{es5Strict} */\n" + + "function f() { var arguments; }", + "function f() {}"); + } + + public void testCheckProvidesWarning() { + CompilerOptions options = createCompilerOptions(); + options.setWarningLevel(DiagnosticGroups.CHECK_PROVIDES, CheckLevel.WARNING); + options.setCheckProvides(CheckLevel.WARNING); + test(options, + "/** @constructor */\n" + + "function f() { var arguments; }", + DiagnosticType.warning("JSC_MISSING_PROVIDE", "missing goog.provide(''{0}'')")); + } + + public void testSuppressCheckProvidesWarning() { + CompilerOptions options = createCompilerOptions(); + options.setWarningLevel(DiagnosticGroups.CHECK_PROVIDES, CheckLevel.WARNING); + options.setCheckProvides(CheckLevel.WARNING); + testSame(options, + "/** @constructor\n" + + " * @suppress{checkProvides} */\n" + + "function f() {}"); + } + + public void testSuppressCastWarning() { + CompilerOptions options = createCompilerOptions(); + options.setWarningLevel(DiagnosticGroups.CHECK_TYPES, CheckLevel.WARNING); + + normalizeResults = true; + + test(options, + "function f() { var xyz = /** @type {string} */ (0); }", + DiagnosticType.warning( + "JSC_INVALID_CAST", "invalid cast")); + + testSame(options, + "/** @suppress{cast} */\n" + + "function f() { var xyz = /** @type {string} */ (0); }"); + } + + public void testRenamePrefix() { + String code = "var x = {}; function f(y) {}"; + CompilerOptions options = createCompilerOptions(); + options.renamePrefix = "G_"; + options.variableRenaming = VariableRenamingPolicy.ALL; + test(options, code, "var G_={}; function G_a(a) {}"); + } + + public void testRenamePrefixNamespace() { + String code = + "var x = {}; x.FOO = 5; x.bar = 3;"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.collapseProperties = true; + options.renamePrefixNamespace = "_"; + test(options, code, "_.x$FOO = 5; _.x$bar = 3;"); + } + + public void testRenamePrefixNamespaceProtectSideEffects() { + String code = "var x = null; try { +x.FOO; } catch (e) {}"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel( + options); + options.renamePrefixNamespace = "_"; + test(options, code, "_.x = null; try { +_.x.FOO; } catch (e) {}"); + } + + public void testRenamePrefixNamespaceActivatesMoveFunctionDeclarations() { + CompilerOptions options = createCompilerOptions(); + String code = "var x = f; function f() { return 3; }"; + testSame(options, code); + assertFalse(options.moveFunctionDeclarations); + options.renamePrefixNamespace = "_"; + test(options, code, "_.f = function() { return 3; }; _.x = _.f;"); + } + + public void testBrokenNameSpace() { + CompilerOptions options = createCompilerOptions(); + String code = "var goog; goog.provide('i.am.on.a.Horse');" + + "i.am.on.a.Horse = function() {};" + + "i.am.on.a.Horse.prototype.x = function() {};" + + "i.am.on.a.Boat.prototype.y = function() {}"; + options.closurePass = true; + options.collapseProperties = true; + options.smartNameRemoval = true; + test(options, code, ""); + } + + public void testNamelessParameter() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.ADVANCED_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + String code = + "var impl_0;" + + "$load($init());" + + "function $load(){" + + " window['f'] = impl_0;" + + "}" + + "function $init() {" + + " impl_0 = {};" + + "}"; + String result = + "window.f = {};"; + test(options, code, result); + } + + public void testHiddenSideEffect() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.ADVANCED_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + options.setAliasExternals(true); + String code = + "window.offsetWidth;"; + String result = + "window.offsetWidth;"; + test(options, code, result); + } + + public void testNegativeZero() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.ADVANCED_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + test(options, + "function bar(x) { return x; }\n" + + "function foo(x) { print(x / bar(0));\n" + + " print(x / bar(-0)); }\n" + + "foo(3);", + "print(3/0);print(3/-0);"); + } + + public void testSingletonGetter1() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.ADVANCED_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + options.setCodingConvention(new ClosureCodingConvention()); + test(options, + "/** @const */\n" + + "var goog = goog || {};\n" + + "goog.addSingletonGetter = function(ctor) {\n" + + " ctor.getInstance = function() {\n" + + " return ctor.instance_ || (ctor.instance_ = new ctor());\n" + + " };\n" + + "};" + + "function Foo() {}\n" + + "goog.addSingletonGetter(Foo);" + + "Foo.prototype.bar = 1;" + + "function Bar() {}\n" + + "goog.addSingletonGetter(Bar);" + + "Bar.prototype.bar = 1;", + ""); + } + + public void testIncompleteFunction1() { + CompilerOptions options = createCompilerOptions(); + options.ideMode = true; + DiagnosticType[] warnings = new DiagnosticType[]{ + RhinoErrorReporter.PARSE_ERROR, + RhinoErrorReporter.PARSE_ERROR}; + test(options, + new String[] { "var foo = {bar: function(e) }" }, + new String[] { "var foo = {bar: function(e){}};" }, + warnings + ); + } + + public void testIncompleteFunction2() { + CompilerOptions options = createCompilerOptions(); + options.ideMode = true; + DiagnosticType[] warnings = new DiagnosticType[]{ + RhinoErrorReporter.PARSE_ERROR, + RhinoErrorReporter.PARSE_ERROR, + RhinoErrorReporter.PARSE_ERROR, + RhinoErrorReporter.PARSE_ERROR, + RhinoErrorReporter.PARSE_ERROR, + RhinoErrorReporter.PARSE_ERROR}; + test(options, + new String[] { "function hi" }, + new String[] { "function hi() {}" }, + warnings + ); + } + + public void testSortingOff() { + CompilerOptions options = new CompilerOptions(); + options.closurePass = true; + options.setCodingConvention(new ClosureCodingConvention()); + test(options, + new String[] { + "goog.require('goog.beer');", + "goog.provide('goog.beer');" + }, + ProcessClosurePrimitives.LATE_PROVIDE_ERROR); + } + + public void testUnboundedArrayLiteralInfiniteLoop() { + CompilerOptions options = createCompilerOptions(); + options.ideMode = true; + test(options, + "var x = [1, 2", + "var x = [1, 2]", + RhinoErrorReporter.PARSE_ERROR); + } + + public void testProvideRequireSameFile() throws Exception { + CompilerOptions options = createCompilerOptions(); + options.setDependencyOptions( + new DependencyOptions() + .setDependencySorting(true)); + options.closurePass = true; + test( + options, + "goog.provide('x');\ngoog.require('x');", + "var x = {};"); + } + + public void testDependencySorting() throws Exception { + CompilerOptions options = createCompilerOptions(); + options.setDependencyOptions( + new DependencyOptions() + .setDependencySorting(true)); + test( + options, + new String[] { + "goog.require('x');", + "goog.provide('x');", + }, + new String[] { + "goog.provide('x');", + "goog.require('x');", + + // For complicated reasons involving modules, + // the compiler creates a synthetic source file. + "", + }); + } + + public void testStrictWarningsGuard() throws Exception { + CompilerOptions options = createCompilerOptions(); + options.checkTypes = true; + options.addWarningsGuard(new StrictWarningsGuard()); + + Compiler compiler = compile(options, + "/** @return {number} */ function f() { return true; }"); + assertEquals(1, compiler.getErrors().length); + assertEquals(0, compiler.getWarnings().length); + } + + public void testStrictWarningsGuardEmergencyMode() throws Exception { + CompilerOptions options = createCompilerOptions(); + options.checkTypes = true; + options.addWarningsGuard(new StrictWarningsGuard()); + options.useEmergencyFailSafe(); + + Compiler compiler = compile(options, + "/** @return {number} */ function f() { return true; }"); + assertEquals(0, compiler.getErrors().length); + assertEquals(1, compiler.getWarnings().length); + } + + public void testInlineProperties() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS; + level.setOptionsForCompilationLevel(options); + level.setTypeBasedOptimizationOptions(options); + + String code = "" + + "var ns = {};\n" + + "/** @constructor */\n" + + "ns.C = function () {this.someProperty = 1}\n" + + "alert(new ns.C().someProperty + new ns.C().someProperty);\n"; + assertTrue(options.inlineProperties); + assertTrue(options.collapseProperties); + // CollapseProperties used to prevent inlining this property. + test(options, code, "alert(2);"); + } + + public void testGoogDefineClass1() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS; + level.setOptionsForCompilationLevel(options); + level.setTypeBasedOptimizationOptions(options); + + String code = "" + + "var ns = {};\n" + + "ns.C = goog.defineClass(null, {\n" + + " /** @constructor */\n" + + " constructor: function () {this.someProperty = 1}\n" + + "});\n" + + "alert(new ns.C().someProperty + new ns.C().someProperty);\n"; + assertTrue(options.inlineProperties); + assertTrue(options.collapseProperties); + // CollapseProperties used to prevent inlining this property. + test(options, code, "alert(2);"); + } + + public void testGoogDefineClass2() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS; + level.setOptionsForCompilationLevel(options); + level.setTypeBasedOptimizationOptions(options); + + String code = "" + + "var C = goog.defineClass(null, {\n" + + " /** @constructor */\n" + + " constructor: function () {this.someProperty = 1}\n" + + "});\n" + + "alert(new C().someProperty + new C().someProperty);\n"; + assertTrue(options.inlineProperties); + assertTrue(options.collapseProperties); + // CollapseProperties used to prevent inlining this property. + test(options, code, "alert(2);"); + } + + public void testGoogDefineClass3() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS; + level.setOptionsForCompilationLevel(options); + level.setTypeBasedOptimizationOptions(options); + WarningLevel warnings = WarningLevel.VERBOSE; + warnings.setOptionsForWarningLevel(options); + + String code = "" + + "var C = goog.defineClass(null, {\n" + + " /** @constructor */\n" + + " constructor: function () {\n" + + " /** @type {number} */\n" + + " this.someProperty = 1},\n" + + " /** @param {string} a */\n" + + " someMethod: function (a) {}\n" + + "});" + + "var x = new C();\n" + + "x.someMethod(x.someProperty);\n"; + assertTrue(options.inlineProperties); + assertTrue(options.collapseProperties); + // CollapseProperties used to prevent inlining this property. + test(options, code, TypeValidator.TYPE_MISMATCH_WARNING); + } + + public void testCheckConstants1() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS; + level.setOptionsForCompilationLevel(options); + WarningLevel warnings = WarningLevel.QUIET; + warnings.setOptionsForWarningLevel(options); + + String code = "" + + "var foo; foo();\n" + + "/** @const */\n" + + "var x = 1; foo(); x = 2;\n"; + test(options, code, code); + } + + public void testCheckConstants2() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS; + level.setOptionsForCompilationLevel(options); + WarningLevel warnings = WarningLevel.DEFAULT; + warnings.setOptionsForWarningLevel(options); + + String code = "" + + "var foo;\n" + + "/** @const */\n" + + "var x = 1; foo(); x = 2;\n"; + test(options, code, ConstCheck.CONST_REASSIGNED_VALUE_ERROR); + } + + public void testIssue787() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS; + level.setOptionsForCompilationLevel(options); + WarningLevel warnings = WarningLevel.DEFAULT; + warnings.setOptionsForWarningLevel(options); + + String code = "" + + "function some_function() {\n" + + " var fn1;\n" + + " var fn2;\n" + + "\n" + + " if (any_expression) {\n" + + " fn2 = external_ref;\n" + + " fn1 = function (content) {\n" + + " return fn2();\n" + + " }\n" + + " }\n" + + "\n" + + " return {\n" + + " method1: function () {\n" + + " if (fn1) fn1();\n" + + " return true;\n" + + " },\n" + + " method2: function () {\n" + + " return false;\n" + + " }\n" + + " }\n" + + "}"; + + String result = "" + + "function some_function() {\n" + + " var a, b;\n" + + " any_expression && (b = external_ref, a = function(a) {\n" + + " return b()\n" + + " });\n" + + " return{method1:function() {\n" + + " a && a();\n" + + " return !0\n" + + " }, method2:function() {\n" + + " return !1\n" + + " }}\n" + + "}\n" + + ""; + + test(options, code, result); + } + + public void testManyAdds() {} +// Defects4J: flaky method +// public void testManyAdds() { +// CompilerOptions options = createCompilerOptions(); +// CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS; +// level.setOptionsForCompilationLevel(options); +// WarningLevel warnings = WarningLevel.VERBOSE; +// warnings.setOptionsForWarningLevel(options); +// +// int numAdds = 4750; +// StringBuilder original = new StringBuilder("var x = 0"); +// for (int i = 0; i < numAdds; i++) { +// original.append(" + 1"); +// } +// original.append(";"); +// test(options, original.toString(), "var x = " + numAdds + ";"); +// } + + /** Creates a CompilerOptions object with google coding conventions. */ + @Override + protected CompilerOptions createCompilerOptions() { + CompilerOptions options = new CompilerOptions(); + options.setCodingConvention(new GoogleCodingConvention()); + return options; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/IntegrationTest.java.bak b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/IntegrationTest.java.bak new file mode 100644 index 0000000..47604bc --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/IntegrationTest.java.bak @@ -0,0 +1,2397 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.CompilerOptions.LanguageMode; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * Tests for {@link PassFactory}. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class IntegrationTest extends IntegrationTestCase { + + private static final String CLOSURE_BOILERPLATE = + "/** @define {boolean} */ var COMPILED = false; var goog = {};" + + "goog.exportSymbol = function() {};"; + + private static final String CLOSURE_COMPILED = + "var COMPILED = true; var goog$exportSymbol = function() {};"; + + public void testConstructorCycle() { + CompilerOptions options = createCompilerOptions(); + options.checkTypes = true; + test(options, + "/** @return {function()} */ var AsyncTestCase = function() {};\n" + + "/**\n" + + " * @constructor\n" + + " */ Foo = /** @type {function(new:Foo)} */ (AyncTestCase());", + RhinoErrorReporter.PARSE_ERROR); + } + + public void testBug1949424() { + CompilerOptions options = createCompilerOptions(); + options.collapseProperties = true; + options.closurePass = true; + test(options, CLOSURE_BOILERPLATE + "goog.provide('FOO'); FOO.bar = 3;", + CLOSURE_COMPILED + "var FOO$bar = 3;"); + } + + public void testBug1949424_v2() { + CompilerOptions options = createCompilerOptions(); + options.collapseProperties = true; + options.closurePass = true; + test(options, CLOSURE_BOILERPLATE + "goog.provide('FOO.BAR'); FOO.BAR = 3;", + CLOSURE_COMPILED + "var FOO$BAR = 3;"); + } + + public void testBug1956277() { + CompilerOptions options = createCompilerOptions(); + options.collapseProperties = true; + options.inlineVariables = true; + test(options, "var CONST = {}; CONST.bar = null;" + + "function f(url) { CONST.bar = url; }", + "var CONST$bar = null; function f(url) { CONST$bar = url; }"); + } + + public void testBug1962380() { + CompilerOptions options = createCompilerOptions(); + options.collapseProperties = true; + options.inlineVariables = true; + options.generateExports = true; + test(options, + CLOSURE_BOILERPLATE + "/** @export */ goog.CONSTANT = 1;" + + "var x = goog.CONSTANT;", + "(function() {})('goog.CONSTANT', 1);" + + "var x = 1;"); + } + + public void testBug2410122() { + CompilerOptions options = createCompilerOptions(); + options.generateExports = true; + options.closurePass = true; + test(options, + "var goog = {};" + + "function F() {}" + + "/** @export */ function G() { goog.base(this); } " + + "goog.inherits(G, F);", + "var goog = {};" + + "function F() {}" + + "function G() { F.call(this); } " + + "goog.inherits(G, F); goog.exportSymbol('G', G);"); + } + + public void testIssue90() { + CompilerOptions options = createCompilerOptions(); + options.foldConstants = true; + options.inlineVariables = true; + options.removeDeadCode = true; + test(options, + "var x; x && alert(1);", + ""); + } + + public void testClosurePassOff() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = false; + testSame( + options, + "var goog = {}; goog.require = function(x) {}; goog.require('foo');"); + testSame( + options, + "var goog = {}; goog.getCssName = function(x) {};" + + "goog.getCssName('foo');"); + } + + public void testClosurePassOn() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + test( + options, + "var goog = {}; goog.require = function(x) {}; goog.require('foo');", + ProcessClosurePrimitives.MISSING_PROVIDE_ERROR); + test( + options, + "/** @define {boolean} */ var COMPILED = false;" + + "var goog = {}; goog.getCssName = function(x) {};" + + "goog.getCssName('foo');", + "var COMPILED = true;" + + "var goog = {}; goog.getCssName = function(x) {};" + + "'foo';"); + } + + public void testCssNameCheck() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.checkMissingGetCssNameLevel = CheckLevel.ERROR; + options.checkMissingGetCssNameBlacklist = "foo"; + test(options, "var x = 'foo';", + CheckMissingGetCssName.MISSING_GETCSSNAME); + } + + public void testBug2592659() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.checkTypes = true; + options.checkMissingGetCssNameLevel = CheckLevel.WARNING; + options.checkMissingGetCssNameBlacklist = "foo"; + test(options, + "var goog = {};\n" + + "/**\n" + + " * @param {string} className\n" + + " * @param {string=} opt_modifier\n" + + " * @return {string}\n" + + "*/\n" + + "goog.getCssName = function(className, opt_modifier) {}\n" + + "var x = goog.getCssName(123, 'a');", + TypeValidator.TYPE_MISMATCH_WARNING); + } + + public void testTypedefBeforeOwner1() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + test(options, + "goog.provide('foo.Bar.Type');\n" + + "goog.provide('foo.Bar');\n" + + "/** @typedef {number} */ foo.Bar.Type;\n" + + "foo.Bar = function() {};", + "var foo = {}; foo.Bar.Type; foo.Bar = function() {};"); + } + + public void testTypedefBeforeOwner2() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.collapseProperties = true; + test(options, + "goog.provide('foo.Bar.Type');\n" + + "goog.provide('foo.Bar');\n" + + "/** @typedef {number} */ foo.Bar.Type;\n" + + "foo.Bar = function() {};", + "var foo$Bar$Type; var foo$Bar = function() {};"); + } + + public void testExportedNames() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.variableRenaming = VariableRenamingPolicy.ALL; + test(options, + "/** @define {boolean} */ var COMPILED = false;" + + "var goog = {}; goog.exportSymbol('b', goog);", + "var a = true; var c = {}; c.exportSymbol('b', c);"); + test(options, + "/** @define {boolean} */ var COMPILED = false;" + + "var goog = {}; goog.exportSymbol('a', goog);", + "var b = true; var c = {}; c.exportSymbol('a', c);"); + } + + public void testCheckGlobalThisOn() { + CompilerOptions options = createCompilerOptions(); + options.checkSuspiciousCode = true; + options.checkGlobalThisLevel = CheckLevel.ERROR; + test(options, "function f() { this.y = 3; }", CheckGlobalThis.GLOBAL_THIS); + } + + public void testSusiciousCodeOff() { + CompilerOptions options = createCompilerOptions(); + options.checkSuspiciousCode = false; + options.checkGlobalThisLevel = CheckLevel.ERROR; + test(options, "function f() { this.y = 3; }", CheckGlobalThis.GLOBAL_THIS); + } + + public void testCheckGlobalThisOff() { + CompilerOptions options = createCompilerOptions(); + options.checkSuspiciousCode = true; + options.checkGlobalThisLevel = CheckLevel.OFF; + testSame(options, "function f() { this.y = 3; }"); + } + + public void testCheckRequiresAndCheckProvidesOff() { + testSame(createCompilerOptions(), new String[] { + "/** @constructor */ function Foo() {}", + "new Foo();" + }); + } + + public void testCheckRequiresOn() { + CompilerOptions options = createCompilerOptions(); + options.checkRequires = CheckLevel.ERROR; + test(options, new String[] { + "/** @constructor */ function Foo() {}", + "new Foo();" + }, CheckRequiresForConstructors.MISSING_REQUIRE_WARNING); + } + + public void testCheckProvidesOn() { + CompilerOptions options = createCompilerOptions(); + options.checkProvides = CheckLevel.ERROR; + test(options, new String[] { + "/** @constructor */ function Foo() {}", + "new Foo();" + }, CheckProvides.MISSING_PROVIDE_WARNING); + } + + public void testGenerateExportsOff() { + testSame(createCompilerOptions(), "/** @export */ function f() {}"); + } + + public void testGenerateExportsOn() { + CompilerOptions options = createCompilerOptions(); + options.generateExports = true; + test(options, "/** @export */ function f() {}", + "/** @export */ function f() {} goog.exportSymbol('f', f);"); + } + + public void testExportTestFunctionsOff() { + testSame(createCompilerOptions(), "function testFoo() {}"); + } + + public void testExportTestFunctionsOn() { + CompilerOptions options = createCompilerOptions(); + options.exportTestFunctions = true; + test(options, "function testFoo() {}", + "/** @export */ function testFoo() {}" + + "goog.exportSymbol('testFoo', testFoo);"); + } + + public void testExpose() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.ADVANCED_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + test(options, + "var x = {eeny: 1, /** @expose */ meeny: 2};" + + "/** @constructor */ var Foo = function() {};" + + "/** @expose */ Foo.prototype.miny = 3;" + + "Foo.prototype.moe = 4;" + + "function moe(a, b) { return a.meeny + b.miny; }" + + "window['x'] = x;" + + "window['Foo'] = Foo;" + + "window['moe'] = moe;", + "function a(){}" + + "a.prototype.miny=3;" + + "window.x={a:1,meeny:2};" + + "window.Foo=a;" + + "window.moe=function(b,c){" + + " return b.meeny+c.miny" + + "}"); + } + + public void testCheckSymbolsOff() { + CompilerOptions options = createCompilerOptions(); + testSame(options, "x = 3;"); + } + + public void testCheckSymbolsOn() { + CompilerOptions options = createCompilerOptions(); + options.checkSymbols = true; + test(options, "x = 3;", VarCheck.UNDEFINED_VAR_ERROR); + } + + public void testCheckReferencesOff() { + CompilerOptions options = createCompilerOptions(); + testSame(options, "x = 3; var x = 5;"); + } + + public void testCheckReferencesOn() { + CompilerOptions options = createCompilerOptions(); + options.aggressiveVarCheck = CheckLevel.ERROR; + test(options, "x = 3; var x = 5;", + VariableReferenceCheck.UNDECLARED_REFERENCE); + } + + public void testInferTypes() { + CompilerOptions options = createCompilerOptions(); + options.inferTypes = true; + options.checkTypes = false; + options.closurePass = true; + + test(options, + CLOSURE_BOILERPLATE + + "goog.provide('Foo'); /** @enum */ Foo = {a: 3};", + TypeCheck.ENUM_NOT_CONSTANT); + assertTrue(lastCompiler.getErrorManager().getTypedPercent() == 0); + + // This does not generate a warning. + test(options, "/** @type {number} */ var n = window.name;", + "var n = window.name;"); + assertTrue(lastCompiler.getErrorManager().getTypedPercent() == 0); + } + + public void testTypeCheckAndInference() { + CompilerOptions options = createCompilerOptions(); + options.checkTypes = true; + test(options, "/** @type {number} */ var n = window.name;", + TypeValidator.TYPE_MISMATCH_WARNING); + assertTrue(lastCompiler.getErrorManager().getTypedPercent() > 0); + } + + public void testTypeNameParser() { + CompilerOptions options = createCompilerOptions(); + options.checkTypes = true; + test(options, "/** @type {n} */ var n = window.name;", + RhinoErrorReporter.TYPE_PARSE_ERROR); + } + + // This tests that the TypedScopeCreator is memoized so that it only creates a + // Scope object once for each scope. If, when type inference requests a scope, + // it creates a new one, then multiple JSType objects end up getting created + // for the same local type, and ambiguate will rename the last statement to + // o.a(o.a, o.a), which is bad. + public void testMemoizedTypedScopeCreator() { + CompilerOptions options = createCompilerOptions(); + options.checkTypes = true; + options.ambiguateProperties = true; + options.propertyRenaming = PropertyRenamingPolicy.ALL_UNQUOTED; + test(options, "function someTest() {\n" + + " /** @constructor */\n" + + " function Foo() { this.instProp = 3; }\n" + + " Foo.prototype.protoProp = function(a, b) {};\n" + + " /** @constructor\n @extends Foo */\n" + + " function Bar() {}\n" + + " goog.inherits(Bar, Foo);\n" + + " var o = new Bar();\n" + + " o.protoProp(o.protoProp, o.instProp);\n" + + "}", + "function someTest() {\n" + + " function Foo() { this.b = 3; }\n" + + " function Bar() {}\n" + + " Foo.prototype.a = function(a, b) {};\n" + + " goog.c(Bar, Foo);\n" + + " var o = new Bar();\n" + + " o.a(o.a, o.b);\n" + + "}"); + } + + public void testCheckTypes() { + CompilerOptions options = createCompilerOptions(); + options.checkTypes = true; + test(options, "var x = x || {}; x.f = function() {}; x.f(3);", + TypeCheck.WRONG_ARGUMENT_COUNT); + } + + public void testReplaceCssNames() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.gatherCssNames = true; + test(options, "/** @define {boolean} */\n" + + "var COMPILED = false;\n" + + "goog.setCssNameMapping({'foo':'bar'});\n" + + "function getCss() {\n" + + " return goog.getCssName('foo');\n" + + "}", + "var COMPILED = true;\n" + + "function getCss() {\n" + + " return \"bar\";" + + "}"); + assertEquals( + ImmutableMap.of("foo", new Integer(1)), + lastCompiler.getPassConfig().getIntermediateState().cssNames); + } + + public void testRemoveClosureAsserts() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + testSame(options, + "var goog = {};" + + "goog.asserts.assert(goog);"); + options.removeClosureAsserts = true; + test(options, + "var goog = {};" + + "goog.asserts.assert(goog);", + "var goog = {};"); + } + + public void testDeprecation() { + String code = "/** @deprecated */ function f() { } function g() { f(); }"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.setWarningLevel(DiagnosticGroups.DEPRECATED, CheckLevel.ERROR); + testSame(options, code); + + options.checkTypes = true; + test(options, code, CheckAccessControls.DEPRECATED_NAME); + } + + public void testVisibility() { + String[] code = { + "/** @private */ function f() { }", + "function g() { f(); }" + }; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.setWarningLevel(DiagnosticGroups.VISIBILITY, CheckLevel.ERROR); + testSame(options, code); + + options.checkTypes = true; + test(options, code, CheckAccessControls.BAD_PRIVATE_GLOBAL_ACCESS); + } + + public void testUnreachableCode() { + String code = "function f() { return \n 3; }"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.checkUnreachableCode = CheckLevel.ERROR; + test(options, code, CheckUnreachableCode.UNREACHABLE_CODE); + } + + public void testMissingReturn() { + String code = + "/** @return {number} */ function f() { if (f) { return 3; } }"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.checkMissingReturn = CheckLevel.ERROR; + testSame(options, code); + + options.checkTypes = true; + test(options, code, CheckMissingReturn.MISSING_RETURN_STATEMENT); + } + + public void testIdGenerators() { + String code = "function f() {} f('id');"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.idGenerators = Sets.newHashSet("f"); + test(options, code, "function f() {} 'a';"); + } + + public void testOptimizeArgumentsArray() { + String code = "function f() { return arguments[0]; }"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.optimizeArgumentsArray = true; + String argName = "JSCompiler_OptimizeArgumentsArray_p0"; + test(options, code, + "function f(" + argName + ") { return " + argName + "; }"); + } + + public void testOptimizeParameters() { + String code = "function f(a) { return a; } f(true);"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.optimizeParameters = true; + test(options, code, "function f() { var a = true; return a;} f();"); + } + + public void testOptimizeReturns() { + String code = "function f(a) { return a; } f(true);"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.optimizeReturns = true; + test(options, code, "function f(a) {return;} f(true);"); + } + + public void testRemoveAbstractMethods() { + String code = CLOSURE_BOILERPLATE + + "var x = {}; x.foo = goog.abstractMethod; x.bar = 3;"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.closurePass = true; + options.collapseProperties = true; + test(options, code, CLOSURE_COMPILED + " var x$bar = 3;"); + } + + public void testCollapseProperties1() { + String code = + "var x = {}; x.FOO = 5; x.bar = 3;"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.collapseProperties = true; + test(options, code, "var x$FOO = 5; var x$bar = 3;"); + } + + public void testCollapseProperties2() { + String code = + "var x = {}; x.FOO = 5; x.bar = 3;"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.collapseProperties = true; + options.collapseObjectLiterals = true; + test(options, code, "var x$FOO = 5; var x$bar = 3;"); + } + + public void testCollapseObjectLiteral1() { + // Verify collapseObjectLiterals does nothing in global scope + String code = "var x = {}; x.FOO = 5; x.bar = 3;"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.collapseObjectLiterals = true; + testSame(options, code); + } + + public void testCollapseObjectLiteral2() { + String code = + "function f() {var x = {}; x.FOO = 5; x.bar = 3;}"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.collapseObjectLiterals = true; + test(options, code, + "function f(){" + + "var JSCompiler_object_inline_FOO_0;" + + "var JSCompiler_object_inline_bar_1;" + + "JSCompiler_object_inline_FOO_0=5;" + + "JSCompiler_object_inline_bar_1=3}"); + } + + public void testTightenTypesWithoutTypeCheck() { + CompilerOptions options = createCompilerOptions(); + options.tightenTypes = true; + test(options, "", DefaultPassConfig.TIGHTEN_TYPES_WITHOUT_TYPE_CHECK); + } + + public void testDisambiguateProperties() { + String code = + "/** @constructor */ function Foo(){} Foo.prototype.bar = 3;" + + "/** @constructor */ function Baz(){} Baz.prototype.bar = 3;"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.disambiguateProperties = true; + options.checkTypes = true; + test(options, code, + "function Foo(){} Foo.prototype.Foo_prototype$bar = 3;" + + "function Baz(){} Baz.prototype.Baz_prototype$bar = 3;"); + } + + public void testMarkPureCalls() { + String testCode = "function foo() {} foo();"; + CompilerOptions options = createCompilerOptions(); + options.removeDeadCode = true; + + testSame(options, testCode); + + options.computeFunctionSideEffects = true; + test(options, testCode, "function foo() {}"); + } + + public void testMarkNoSideEffects() { + String testCode = "noSideEffects();"; + CompilerOptions options = createCompilerOptions(); + options.removeDeadCode = true; + + testSame(options, testCode); + + options.markNoSideEffectCalls = true; + test(options, testCode, ""); + } + + public void testChainedCalls() { + CompilerOptions options = createCompilerOptions(); + options.chainCalls = true; + test( + options, + "/** @constructor */ function Foo() {} " + + "Foo.prototype.bar = function() { return this; }; " + + "var f = new Foo();" + + "f.bar(); " + + "f.bar(); ", + "function Foo() {} " + + "Foo.prototype.bar = function() { return this; }; " + + "var f = new Foo();" + + "f.bar().bar();"); + } + + public void testExtraAnnotationNames() { + CompilerOptions options = createCompilerOptions(); + options.setExtraAnnotationNames(Sets.newHashSet("TagA", "TagB")); + test( + options, + "/** @TagA */ var f = new Foo(); /** @TagB */ f.bar();", + "var f = new Foo(); f.bar();"); + } + + public void testDevirtualizePrototypeMethods() { + CompilerOptions options = createCompilerOptions(); + options.devirtualizePrototypeMethods = true; + test( + options, + "/** @constructor */ var Foo = function() {}; " + + "Foo.prototype.bar = function() {};" + + "(new Foo()).bar();", + "var Foo = function() {};" + + "var JSCompiler_StaticMethods_bar = " + + " function(JSCompiler_StaticMethods_bar$self) {};" + + "JSCompiler_StaticMethods_bar(new Foo());"); + } + + public void testCheckConsts() { + CompilerOptions options = createCompilerOptions(); + options.inlineConstantVars = true; + test(options, "var FOO = true; FOO = false", + ConstCheck.CONST_REASSIGNED_VALUE_ERROR); + } + + public void testAllChecksOn() { + CompilerOptions options = createCompilerOptions(); + options.checkSuspiciousCode = true; + options.checkControlStructures = true; + options.checkRequires = CheckLevel.ERROR; + options.checkProvides = CheckLevel.ERROR; + options.generateExports = true; + options.exportTestFunctions = true; + options.closurePass = true; + options.checkMissingGetCssNameLevel = CheckLevel.ERROR; + options.checkMissingGetCssNameBlacklist = "goog"; + options.syntheticBlockStartMarker = "synStart"; + options.syntheticBlockEndMarker = "synEnd"; + options.checkSymbols = true; + options.aggressiveVarCheck = CheckLevel.ERROR; + options.processObjectPropertyString = true; + options.collapseProperties = true; + test(options, CLOSURE_BOILERPLATE, CLOSURE_COMPILED); + } + + public void testTypeCheckingWithSyntheticBlocks() { + CompilerOptions options = createCompilerOptions(); + options.syntheticBlockStartMarker = "synStart"; + options.syntheticBlockEndMarker = "synEnd"; + options.checkTypes = true; + + // We used to have a bug where the CFG drew an + // edge straight from synStart to f(progress). + // If that happens, then progress will get type {number|undefined}. + testSame( + options, + "/** @param {number} x */ function f(x) {}" + + "function g() {" + + " synStart('foo');" + + " var progress = 1;" + + " f(progress);" + + " synEnd('foo');" + + "}"); + } + + public void testCompilerDoesNotBlowUpIfUndefinedSymbols() { + CompilerOptions options = createCompilerOptions(); + options.checkSymbols = true; + + // Disable the undefined variable check. + options.setWarningLevel( + DiagnosticGroup.forType(VarCheck.UNDEFINED_VAR_ERROR), + CheckLevel.OFF); + + // The compiler used to throw an IllegalStateException on this. + testSame(options, "var x = {foo: y};"); + } + + // Make sure that if we change variables which are constant to have + // $$constant appended to their names, we remove that tag before + // we finish. + public void testConstantTagsMustAlwaysBeRemoved() { + CompilerOptions options = createCompilerOptions(); + + options.variableRenaming = VariableRenamingPolicy.LOCAL; + String originalText = "var G_GEO_UNKNOWN_ADDRESS=1;\n" + + "function foo() {" + + " var localVar = 2;\n" + + " if (G_GEO_UNKNOWN_ADDRESS == localVar) {\n" + + " alert(\"A\"); }}"; + String expectedText = "var G_GEO_UNKNOWN_ADDRESS=1;" + + "function foo(){var a=2;if(G_GEO_UNKNOWN_ADDRESS==a){alert(\"A\")}}"; + + test(options, originalText, expectedText); + } + + public void testClosurePassPreservesJsDoc() { + CompilerOptions options = createCompilerOptions(); + options.checkTypes = true; + options.closurePass = true; + + test(options, + CLOSURE_BOILERPLATE + + "goog.provide('Foo'); /** @constructor */ Foo = function() {};" + + "var x = new Foo();", + "var COMPILED=true;var goog={};goog.exportSymbol=function(){};" + + "var Foo=function(){};var x=new Foo"); + test(options, + CLOSURE_BOILERPLATE + + "goog.provide('Foo'); /** @enum */ Foo = {a: 3};", + TypeCheck.ENUM_NOT_CONSTANT); + } + + public void testProvidedNamespaceIsConst() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.inlineConstantVars = true; + options.collapseProperties = true; + test(options, + "var goog = {}; goog.provide('foo'); " + + "function f() { foo = {};}", + "var foo = {}; function f() { foo = {}; }", + ConstCheck.CONST_REASSIGNED_VALUE_ERROR); + } + + public void testProvidedNamespaceIsConst2() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.inlineConstantVars = true; + options.collapseProperties = true; + test(options, + "var goog = {}; goog.provide('foo.bar'); " + + "function f() { foo.bar = {};}", + "var foo$bar = {};" + + "function f() { foo$bar = {}; }", + ConstCheck.CONST_REASSIGNED_VALUE_ERROR); + } + + public void testProvidedNamespaceIsConst3() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.inlineConstantVars = true; + options.collapseProperties = true; + test(options, + "var goog = {}; " + + "goog.provide('foo.bar'); goog.provide('foo.bar.baz'); " + + "/** @constructor */ foo.bar = function() {};" + + "/** @constructor */ foo.bar.baz = function() {};", + "var foo$bar = function(){};" + + "var foo$bar$baz = function(){};"); + } + + public void testProvidedNamespaceIsConst4() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.inlineConstantVars = true; + options.collapseProperties = true; + test(options, + "var goog = {}; goog.provide('foo.Bar'); " + + "var foo = {}; foo.Bar = {};", + "var foo = {}; foo = {}; foo.Bar = {};"); + } + + public void testProvidedNamespaceIsConst5() { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.inlineConstantVars = true; + options.collapseProperties = true; + test(options, + "var goog = {}; goog.provide('foo.Bar'); " + + "foo = {}; foo.Bar = {};", + "var foo = {}; foo = {}; foo.Bar = {};"); + } + + public void testProcessDefinesAlwaysOn() { + test(createCompilerOptions(), + "/** @define {boolean} */ var HI = true; HI = false;", + "var HI = false;false;"); + } + + public void testProcessDefinesAdditionalReplacements() { + CompilerOptions options = createCompilerOptions(); + options.setDefineToBooleanLiteral("HI", false); + test(options, + "/** @define {boolean} */ var HI = true;", + "var HI = false;"); + } + + public void testReplaceMessages() { + CompilerOptions options = createCompilerOptions(); + String prefix = "var goog = {}; goog.getMsg = function() {};"; + testSame(options, prefix + "var MSG_HI = goog.getMsg('hi');"); + + options.messageBundle = new EmptyMessageBundle(); + test(options, + prefix + "/** @desc xyz */ var MSG_HI = goog.getMsg('hi');", + prefix + "var MSG_HI = 'hi';"); + } + + public void testCheckGlobalNames() { + CompilerOptions options = createCompilerOptions(); + options.checkGlobalNamesLevel = CheckLevel.ERROR; + test(options, "var x = {}; var y = x.z;", + CheckGlobalNames.UNDEFINED_NAME_WARNING); + } + + public void testInlineGetters() { + CompilerOptions options = createCompilerOptions(); + String code = + "function Foo() {} Foo.prototype.bar = function() { return 3; };" + + "var x = new Foo(); x.bar();"; + + testSame(options, code); + options.inlineGetters = true; + + test(options, code, + "function Foo() {} Foo.prototype.bar = function() { return 3 };" + + "var x = new Foo(); 3;"); + } + + public void testInlineGettersWithAmbiguate() { + CompilerOptions options = createCompilerOptions(); + + String code = + "/** @constructor */" + + "function Foo() {}" + + "/** @type {number} */ Foo.prototype.field;" + + "Foo.prototype.getField = function() { return this.field; };" + + "/** @constructor */" + + "function Bar() {}" + + "/** @type {string} */ Bar.prototype.field;" + + "Bar.prototype.getField = function() { return this.field; };" + + "new Foo().getField();" + + "new Bar().getField();"; + + testSame(options, code); + + options.inlineGetters = true; + + test(options, code, + "function Foo() {}" + + "Foo.prototype.field;" + + "Foo.prototype.getField = function() { return this.field; };" + + "function Bar() {}" + + "Bar.prototype.field;" + + "Bar.prototype.getField = function() { return this.field; };" + + "new Foo().field;" + + "new Bar().field;"); + + options.checkTypes = true; + options.ambiguateProperties = true; + + // Propagating the wrong type information may cause ambiguate properties + // to generate bad code. + testSame(options, code); + } + + public void testInlineVariables() { + CompilerOptions options = createCompilerOptions(); + String code = "function foo() {} var x = 3; foo(x);"; + testSame(options, code); + + options.inlineVariables = true; + test(options, code, "(function foo() {})(3);"); + + options.propertyRenaming = PropertyRenamingPolicy.HEURISTIC; + test(options, code, DefaultPassConfig.CANNOT_USE_PROTOTYPE_AND_VAR); + } + + public void testInlineConstants() { + CompilerOptions options = createCompilerOptions(); + String code = "function foo() {} var x = 3; foo(x); var YYY = 4; foo(YYY);"; + testSame(options, code); + + options.inlineConstantVars = true; + test(options, code, "function foo() {} var x = 3; foo(x); foo(4);"); + } + + public void testMinimizeExits() { + CompilerOptions options = createCompilerOptions(); + String code = + "function f() {" + + " if (window.foo) return; window.h(); " + + "}"; + testSame(options, code); + + options.foldConstants = true; + test( + options, code, + "function f() {" + + " window.foo || window.h(); " + + "}"); + } + + public void testFoldConstants() { + CompilerOptions options = createCompilerOptions(); + String code = "if (true) { window.foo(); }"; + testSame(options, code); + + options.foldConstants = true; + test(options, code, "window.foo();"); + } + + public void testRemoveUnreachableCode() { + CompilerOptions options = createCompilerOptions(); + String code = "function f() { return; f(); }"; + testSame(options, code); + + options.removeDeadCode = true; + test(options, code, "function f() {}"); + } + + public void testRemoveUnusedPrototypeProperties1() { + CompilerOptions options = createCompilerOptions(); + String code = "function Foo() {} " + + "Foo.prototype.bar = function() { return new Foo(); };"; + testSame(options, code); + + options.removeUnusedPrototypeProperties = true; + test(options, code, "function Foo() {}"); + } + + public void testRemoveUnusedPrototypeProperties2() { + CompilerOptions options = createCompilerOptions(); + String code = "function Foo() {} " + + "Foo.prototype.bar = function() { return new Foo(); };" + + "function f(x) { x.bar(); }"; + testSame(options, code); + + options.removeUnusedPrototypeProperties = true; + testSame(options, code); + + options.removeUnusedVars = true; + test(options, code, ""); + } + + public void testSmartNamePass() { + CompilerOptions options = createCompilerOptions(); + String code = "function Foo() { this.bar(); } " + + "Foo.prototype.bar = function() { return Foo(); };"; + testSame(options, code); + + options.smartNameRemoval = true; + test(options, code, ""); + } + + public void testDeadAssignmentsElimination() { + CompilerOptions options = createCompilerOptions(); + String code = "function f() { var x = 3; 4; x = 5; return x; } f(); "; + testSame(options, code); + + options.deadAssignmentElimination = true; + testSame(options, code); + + options.removeUnusedVars = true; + test(options, code, "function f() { var x = 3; 4; x = 5; return x; } f();"); + } + + public void testInlineFunctions() { + CompilerOptions options = createCompilerOptions(); + String code = "function f() { return 3; } f(); "; + testSame(options, code); + + options.inlineFunctions = true; + test(options, code, "3;"); + } + + public void testRemoveUnusedVars1() { + CompilerOptions options = createCompilerOptions(); + String code = "function f(x) {} f();"; + testSame(options, code); + + options.removeUnusedVars = true; + test(options, code, "function f() {} f();"); + } + + public void testRemoveUnusedVars2() { + CompilerOptions options = createCompilerOptions(); + String code = "(function f(x) {})();var g = function() {}; g();"; + testSame(options, code); + + options.removeUnusedVars = true; + test(options, code, "(function() {})();var g = function() {}; g();"); + + options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.UNMAPPED; + test(options, code, "(function f() {})();var g = function $g$() {}; g();"); + } + + public void testCrossModuleCodeMotion() { + CompilerOptions options = createCompilerOptions(); + String[] code = new String[] { + "var x = 1;", + "x;", + }; + testSame(options, code); + + options.crossModuleCodeMotion = true; + test(options, code, new String[] { + "", + "var x = 1; x;", + }); + } + + public void testCrossModuleMethodMotion() { + CompilerOptions options = createCompilerOptions(); + String[] code = new String[] { + "var Foo = function() {}; Foo.prototype.bar = function() {};" + + "var x = new Foo();", + "x.bar();", + }; + testSame(options, code); + + options.crossModuleMethodMotion = true; + test(options, code, new String[] { + CrossModuleMethodMotion.STUB_DECLARATIONS + + "var Foo = function() {};" + + "Foo.prototype.bar=JSCompiler_stubMethod(0); var x=new Foo;", + "Foo.prototype.bar=JSCompiler_unstubMethod(0,function(){}); x.bar()", + }); + } + + public void testFlowSensitiveInlineVariables1() { + CompilerOptions options = createCompilerOptions(); + String code = "function f() { var x = 3; x = 5; return x; }"; + testSame(options, code); + + options.flowSensitiveInlineVariables = true; + test(options, code, "function f() { var x = 3; return 5; }"); + + String unusedVar = "function f() { var x; x = 5; return x; } f()"; + test(options, unusedVar, "function f() { var x; return 5; } f()"); + + options.removeUnusedVars = true; + test(options, unusedVar, "function f() { return 5; } f()"); + } + + public void testFlowSensitiveInlineVariables2() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.SIMPLE_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + test(options, + "function f () {\n" + + " var ab = 0;\n" + + " ab += '-';\n" + + " alert(ab);\n" + + "}", + "function f () {\n" + + " alert('0-');\n" + + "}"); + } + + public void testCollapseAnonymousFunctions() { + CompilerOptions options = createCompilerOptions(); + String code = "var f = function() {};"; + testSame(options, code); + + options.collapseAnonymousFunctions = true; + test(options, code, "function f() {}"); + } + + public void testMoveFunctionDeclarations() { + CompilerOptions options = createCompilerOptions(); + String code = "var x = f(); function f() { return 3; }"; + testSame(options, code); + + options.moveFunctionDeclarations = true; + test(options, code, "function f() { return 3; } var x = f();"); + } + + public void testNameAnonymousFunctions() { + CompilerOptions options = createCompilerOptions(); + String code = "var f = function() {};"; + testSame(options, code); + + options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.MAPPED; + test(options, code, "var f = function $() {}"); + assertNotNull(lastCompiler.getResult().namedAnonFunctionMap); + + options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.UNMAPPED; + test(options, code, "var f = function $f$() {}"); + assertNull(lastCompiler.getResult().namedAnonFunctionMap); + } + + public void testNameAnonymousFunctionsWithVarRemoval() { + CompilerOptions options = createCompilerOptions(); + options.setRemoveUnusedVariables(CompilerOptions.Reach.LOCAL_ONLY); + options.setInlineVariables(true); + String code = "var f = function longName() {}; var g = function() {};" + + "function longerName() {} var i = longerName;"; + test(options, code, + "var f = function() {}; var g = function() {}; " + + "var i = function() {};"); + + options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.MAPPED; + test(options, code, + "var f = function longName() {}; var g = function $() {};" + + "var i = function longerName(){};"); + assertNotNull(lastCompiler.getResult().namedAnonFunctionMap); + + options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.UNMAPPED; + test(options, code, + "var f = function longName() {}; var g = function $g$() {};" + + "var i = function longerName(){};"); + assertNull(lastCompiler.getResult().namedAnonFunctionMap); + } + + public void testExtractPrototypeMemberDeclarations() { + CompilerOptions options = createCompilerOptions(); + String code = "var f = function() {};"; + String expected = "var a; var b = function() {}; a = b.prototype;"; + for (int i = 0; i < 10; i++) { + code += "f.prototype.a = " + i + ";"; + expected += "a.a = " + i + ";"; + } + testSame(options, code); + + options.extractPrototypeMemberDeclarations = true; + options.variableRenaming = VariableRenamingPolicy.ALL; + test(options, code, expected); + + options.propertyRenaming = PropertyRenamingPolicy.HEURISTIC; + options.variableRenaming = VariableRenamingPolicy.OFF; + testSame(options, code); + } + + public void testDevirtualizationAndExtractPrototypeMemberDeclarations() { + CompilerOptions options = createCompilerOptions(); + options.devirtualizePrototypeMethods = true; + options.collapseAnonymousFunctions = true; + options.extractPrototypeMemberDeclarations = true; + options.variableRenaming = VariableRenamingPolicy.ALL; + String code = "var f = function() {};"; + String expected = "var a; function b() {} a = b.prototype;"; + for (int i = 0; i < 10; i++) { + code += "f.prototype.argz = function() {arguments};"; + code += "f.prototype.devir" + i + " = function() {};"; + + char letter = (char) ('d' + i); + expected += "a.argz = function() {arguments};"; + expected += "function " + letter + "(c){}"; + } + + code += "var F = new f(); F.argz();"; + expected += "var n = new b(); n.argz();"; + + for (int i = 0; i < 10; i++) { + code += "F.devir" + i + "();"; + + char letter = (char) ('d' + i); + expected += letter + "(n);"; + } + test(options, code, expected); + } + + public void testCoalesceVariableNames() { + CompilerOptions options = createCompilerOptions(); + String code = "function f() {var x = 3; var y = x; var z = y; return z;}"; + testSame(options, code); + + options.coalesceVariableNames = true; + test(options, code, + "function f() {var x = 3; x = x; x = x; return x;}"); + } + + public void testPropertyRenaming() { + CompilerOptions options = createCompilerOptions(); + options.propertyAffinity = true; + String code = + "function f() { return this.foo + this['bar'] + this.Baz; }" + + "f.prototype.bar = 3; f.prototype.Baz = 3;"; + String heuristic = + "function f() { return this.foo + this['bar'] + this.a; }" + + "f.prototype.bar = 3; f.prototype.a = 3;"; + String aggHeuristic = + "function f() { return this.foo + this['b'] + this.a; } " + + "f.prototype.b = 3; f.prototype.a = 3;"; + String all = + "function f() { return this.b + this['bar'] + this.a; }" + + "f.prototype.c = 3; f.prototype.a = 3;"; + testSame(options, code); + + options.propertyRenaming = PropertyRenamingPolicy.HEURISTIC; + test(options, code, heuristic); + + options.propertyRenaming = PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC; + test(options, code, aggHeuristic); + + options.propertyRenaming = PropertyRenamingPolicy.ALL_UNQUOTED; + test(options, code, all); + } + + public void testConvertToDottedProperties() { + CompilerOptions options = createCompilerOptions(); + String code = + "function f() { return this['bar']; } f.prototype.bar = 3;"; + String expected = + "function f() { return this.bar; } f.prototype.a = 3;"; + testSame(options, code); + + options.convertToDottedProperties = true; + options.propertyRenaming = PropertyRenamingPolicy.ALL_UNQUOTED; + test(options, code, expected); + } + + public void testRewriteFunctionExpressions() { + CompilerOptions options = createCompilerOptions(); + String code = "var a = function() {};"; + String expected = "function JSCompiler_emptyFn(){return function(){}} " + + "var a = JSCompiler_emptyFn();"; + for (int i = 0; i < 10; i++) { + code += "a = function() {};"; + expected += "a = JSCompiler_emptyFn();"; + } + testSame(options, code); + + options.rewriteFunctionExpressions = true; + test(options, code, expected); + } + + public void testAliasAllStrings() { + CompilerOptions options = createCompilerOptions(); + String code = "function f() { return 'a'; }"; + String expected = "var $$S_a = 'a'; function f() { return $$S_a; }"; + testSame(options, code); + + options.aliasAllStrings = true; + test(options, code, expected); + } + + public void testAliasExterns() { + CompilerOptions options = createCompilerOptions(); + String code = "function f() { return window + window + window + window; }"; + String expected = "var GLOBAL_window = window;" + + "function f() { return GLOBAL_window + GLOBAL_window + " + + " GLOBAL_window + GLOBAL_window; }"; + testSame(options, code); + + options.aliasExternals = true; + test(options, code, expected); + } + + public void testAliasKeywords() { + CompilerOptions options = createCompilerOptions(); + String code = + "function f() { return true + true + true + true + true + true; }"; + String expected = "var JSCompiler_alias_TRUE = true;" + + "function f() { return JSCompiler_alias_TRUE + " + + " JSCompiler_alias_TRUE + JSCompiler_alias_TRUE + " + + " JSCompiler_alias_TRUE + JSCompiler_alias_TRUE + " + + " JSCompiler_alias_TRUE; }"; + testSame(options, code); + + options.aliasKeywords = true; + test(options, code, expected); + } + + public void testRenameVars1() { + CompilerOptions options = createCompilerOptions(); + String code = + "var abc = 3; function f() { var xyz = 5; return abc + xyz; }"; + String local = "var abc = 3; function f() { var a = 5; return abc + a; }"; + String all = "var a = 3; function c() { var b = 5; return a + b; }"; + testSame(options, code); + + options.variableRenaming = VariableRenamingPolicy.LOCAL; + test(options, code, local); + + options.variableRenaming = VariableRenamingPolicy.ALL; + test(options, code, all); + + options.reserveRawExports = true; + } + + public void testRenameVars2() { + CompilerOptions options = createCompilerOptions(); + options.variableRenaming = VariableRenamingPolicy.ALL; + + String code = "var abc = 3; function f() { window['a'] = 5; }"; + String noexport = "var a = 3; function b() { window['a'] = 5; }"; + String export = "var b = 3; function c() { window['a'] = 5; }"; + + options.reserveRawExports = false; + test(options, code, noexport); + + options.reserveRawExports = true; + test(options, code, export); + } + + public void testShadowVaribles() { + CompilerOptions options = createCompilerOptions(); + options.variableRenaming = VariableRenamingPolicy.LOCAL; + options.shadowVariables = true; + String code = "var f = function(x) { return function(y) {}}"; + String expected = "var f = function(a) { return function(a) {}}"; + test(options, code, expected); + } + + public void testRenameLabels() { + CompilerOptions options = createCompilerOptions(); + String code = "longLabel: for(;true;) { break longLabel; }"; + String expected = "a: for(;true;) { break a; }"; + testSame(options, code); + + options.labelRenaming = true; + test(options, code, expected); + } + + public void testBadBreakStatementInIdeMode() { + // Ensure that type-checking doesn't crash, even if the CFG is malformed. + // This can happen in IDE mode. + CompilerOptions options = createCompilerOptions(); + options.ideMode = true; + options.checkTypes = true; + test(options, + "function f() { try { } catch(e) { break; } }", + RhinoErrorReporter.PARSE_ERROR); + } + + public void testIssue63SourceMap() { + CompilerOptions options = createCompilerOptions(); + String code = "var a;"; + + options.skipAllPasses = true; + options.sourceMapOutputPath = "./src.map"; + + Compiler compiler = compile(options, code); + compiler.toSource(); + } + + public void testRegExp1() { + CompilerOptions options = createCompilerOptions(); + options.foldConstants = true; + + String code = "/(a)/.test(\"a\");"; + + testSame(options, code); + + options.computeFunctionSideEffects = true; + + String expected = ""; + + test(options, code, expected); + } + + public void testRegExp2() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = true; + + String code = "/(a)/.test(\"a\");var a = RegExp.$1"; + + testSame(options, code); + + options.computeFunctionSideEffects = true; + + test(options, code, CheckRegExp.REGEXP_REFERENCE); + + options.setWarningLevel(DiagnosticGroups.CHECK_REGEXP, CheckLevel.OFF); + + testSame(options, code); + } + + public void testFoldLocals1() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = true; + + // An external object, whose constructor has no side-effects, + // and whose method "go" only modifies the object. + String code = "new Widget().go();"; + + testSame(options, code); + + options.computeFunctionSideEffects = true; + + test(options, code, ""); + } + + public void testFoldLocals2() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = true; + options.checkTypes = true; + + // An external function that returns a local object that the + // method "go" that only modifies the object. + String code = "widgetToken().go();"; + + testSame(options, code); + + options.computeFunctionSideEffects = true; + + test(options, code, "widgetToken()"); + } + + + public void testFoldLocals3() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = true; + + // A function "f" who returns a known local object, and a method that + // modifies only modifies that. + String definition = "function f(){return new Widget()}"; + String call = "f().go();"; + String code = definition + call; + + testSame(options, code); + + options.computeFunctionSideEffects = true; + + // BROKEN + //test(options, code, definition); + testSame(options, code); + } + + public void testFoldLocals4() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = true; + + String code = "/** @constructor */\n" + + "function InternalWidget(){this.x = 1;}" + + "InternalWidget.prototype.internalGo = function (){this.x = 2};" + + "new InternalWidget().internalGo();"; + + testSame(options, code); + + options.computeFunctionSideEffects = true; + + String optimized = "" + + "function InternalWidget(){this.x = 1;}" + + "InternalWidget.prototype.internalGo = function (){this.x = 2};"; + + test(options, code, optimized); + } + + public void testFoldLocals5() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = true; + + String code = "" + + "function fn(){var a={};a.x={};return a}" + + "fn().x.y = 1;"; + + // "fn" returns a unescaped local object, we should be able to fold it, + // but we don't currently. + String result = "" + + "function fn(){var a={x:{}};return a}" + + "fn().x.y = 1;"; + + test(options, code, result); + + options.computeFunctionSideEffects = true; + + test(options, code, result); + } + + public void testFoldLocals6() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = true; + + String code = "" + + "function fn(){return {}}" + + "fn().x.y = 1;"; + + testSame(options, code); + + options.computeFunctionSideEffects = true; + + testSame(options, code); + } + + public void testFoldLocals7() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = true; + + String code = "" + + "function InternalWidget(){return [];}" + + "Array.prototype.internalGo = function (){this.x = 2};" + + "InternalWidget().internalGo();"; + + testSame(options, code); + + options.computeFunctionSideEffects = true; + + String optimized = "" + + "function InternalWidget(){return [];}" + + "Array.prototype.internalGo = function (){this.x = 2};"; + + test(options, code, optimized); + } + + public void testVarDeclarationsIntoFor() { + CompilerOptions options = createCompilerOptions(); + + options.collapseVariableDeclarations = false; + + String code = "var a = 1; for (var b = 2; ;) {}"; + + testSame(options, code); + + options.collapseVariableDeclarations = true; + + test(options, code, "for (var a = 1, b = 2; ;) {}"); + } + + public void testExploitAssigns() { + CompilerOptions options = createCompilerOptions(); + + options.collapseVariableDeclarations = false; + + String code = "a = 1; b = a; c = b"; + + testSame(options, code); + + options.collapseVariableDeclarations = true; + + test(options, code, "c=b=a=1"); + } + + public void testRecoverOnBadExterns() throws Exception { + // This test is for a bug in a very narrow set of circumstances: + // 1) externs validation has to be off. + // 2) aliasExternals has to be on. + // 3) The user has to reference a "normal" variable in externs. + // This case is handled at checking time by injecting a + // synthetic extern variable, and adding a "@suppress {duplicate}" to + // the normal code at compile time. But optimizations may remove that + // annotation, so we need to make sure that the variable declarations + // are de-duped before that happens. + CompilerOptions options = createCompilerOptions(); + + options.aliasExternals = true; + externs = ImmutableList.of( + SourceFile.fromCode("externs", "extern.foo")); + + test(options, + "var extern; " + + "function f() { return extern + extern + extern + extern; }", + "var extern; " + + "function f() { return extern + extern + extern + extern; }", + VarCheck.UNDEFINED_EXTERN_VAR_ERROR); + } + + public void testDuplicateVariablesInExterns() { + CompilerOptions options = createCompilerOptions(); + options.checkSymbols = true; + externs = ImmutableList.of( + SourceFile.fromCode("externs", + "var externs = {}; /** @suppress {duplicate} */ var externs = {};")); + testSame(options, ""); + } + + public void testLanguageMode() { + CompilerOptions options = createCompilerOptions(); + options.setLanguageIn(LanguageMode.ECMASCRIPT3); + + String code = "var a = {get f(){}}"; + + Compiler compiler = compile(options, code); + checkUnexpectedErrorsOrWarnings(compiler, 1); + assertEquals( + "JSC_PARSE_ERROR. Parse error. " + + "getters are not supported in older versions of JS. " + + "If you are targeting newer versions of JS, " + + "set the appropriate language_in option. " + + "at i0 line 1 : 0", + compiler.getErrors()[0].toString()); + + options.setLanguageIn(LanguageMode.ECMASCRIPT5); + + testSame(options, code); + + options.setLanguageIn(LanguageMode.ECMASCRIPT5_STRICT); + + testSame(options, code); + } + + public void testLanguageMode2() { + CompilerOptions options = createCompilerOptions(); + options.setLanguageIn(LanguageMode.ECMASCRIPT3); + options.setWarningLevel(DiagnosticGroups.ES5_STRICT, CheckLevel.OFF); + + String code = "var a = 2; delete a;"; + + testSame(options, code); + + options.setLanguageIn(LanguageMode.ECMASCRIPT5); + + testSame(options, code); + + options.setLanguageIn(LanguageMode.ECMASCRIPT5_STRICT); + + test(options, + code, + code, + StrictModeCheck.DELETE_VARIABLE); + } + + public void testIssue598() { + CompilerOptions options = createCompilerOptions(); + options.setLanguageIn(LanguageMode.ECMASCRIPT5_STRICT); + WarningLevel.VERBOSE.setOptionsForWarningLevel(options); + + options.setLanguageIn(LanguageMode.ECMASCRIPT5); + + String code = + "'use strict';\n" + + "function App() {}\n" + + "App.prototype = {\n" + + " get appData() { return this.appData_; },\n" + + " set appData(data) { this.appData_ = data; }\n" + + "};"; + + Compiler compiler = compile(options, code); + testSame(options, code); + } + + public void testIssue701() { + // Check ASCII art in license comments. + String ascii = "/**\n" + + " * @preserve\n" + + " This\n" + + " is\n" + + " ASCII ART\n" + + "*/"; + String result = "/*\n\n" + + " This\n" + + " is\n" + + " ASCII ART\n" + + "*/\n"; + testSame(createCompilerOptions(), ascii); + assertEquals(result, lastCompiler.toSource()); + } + + public void testIssue724() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.ADVANCED_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + String code = + "isFunction = function(functionToCheck) {" + + " var getType = {};" + + " return functionToCheck && " + + " getType.toString.apply(functionToCheck) === " + + " '[object Function]';" + + "};"; + String result = + "isFunction=function(a){var b={};" + + "return a&&\"[object Function]\"===b.b.a(a)}"; + + test(options, code, result); + } + + public void testIssue730() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.ADVANCED_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + + String code = + "/** @constructor */function A() {this.foo = 0; Object.seal(this);}\n" + + "/** @constructor */function B() {this.a = new A();}\n" + + "B.prototype.dostuff = function() {this.a.foo++;alert('hi');}\n" + + "new B().dostuff();\n"; + + test(options, + code, + "function a(){this.b=0;Object.seal(this)}" + + "(new function(){this.a=new a}).a.b++;" + + "alert(\"hi\")"); + + options.removeUnusedClassProperties = true; + + // This is still a problem when removeUnusedClassProperties are enabled. + test(options, + code, + "function a(){Object.seal(this)}" + + "(new function(){this.a=new a}).a.b++;" + + "alert(\"hi\")"); + } + + public void testCoaleseVariables() { + CompilerOptions options = createCompilerOptions(); + + options.foldConstants = false; + options.coalesceVariableNames = true; + + String code = + "function f(a) {" + + " if (a) {" + + " return a;" + + " } else {" + + " var b = a;" + + " return b;" + + " }" + + " return a;" + + "}"; + String expected = + "function f(a) {" + + " if (a) {" + + " return a;" + + " } else {" + + " a = a;" + + " return a;" + + " }" + + " return a;" + + "}"; + + test(options, code, expected); + + options.foldConstants = true; + options.coalesceVariableNames = false; + + code = + "function f(a) {" + + " if (a) {" + + " return a;" + + " } else {" + + " var b = a;" + + " return b;" + + " }" + + " return a;" + + "}"; + expected = + "function f(a) {" + + " if (!a) {" + + " var b = a;" + + " return b;" + + " }" + + " return a;" + + "}"; + + test(options, code, expected); + + options.foldConstants = true; + options.coalesceVariableNames = true; + + expected = + "function f(a) {" + + " return a;" + + "}"; + + test(options, code, expected); + } + + public void testLateStatementFusion() { + CompilerOptions options = createCompilerOptions(); + options.foldConstants = true; + test(options, + "while(a){a();if(b){b();b()}}", + "for(;a;)a(),b&&(b(),b())"); + } + + public void testLateConstantReordering() { + CompilerOptions options = createCompilerOptions(); + options.foldConstants = true; + test(options, + "if (x < 1 || x > 1 || 1 < x || 1 > x) { alert(x) }", + " (1 > x || 1 < x || 1 < x || 1 > x) && alert(x) "); + } + + public void testsyntheticBlockOnDeadAssignments() { + CompilerOptions options = createCompilerOptions(); + options.deadAssignmentElimination = true; + options.removeUnusedVars = true; + options.syntheticBlockStartMarker = "START"; + options.syntheticBlockEndMarker = "END"; + test(options, "var x; x = 1; START(); x = 1;END();x()", + "var x; x = 1;{START();{x = 1}END()}x()"); + } + + public void testBug4152835() { + CompilerOptions options = createCompilerOptions(); + options.foldConstants = true; + options.syntheticBlockStartMarker = "START"; + options.syntheticBlockEndMarker = "END"; + test(options, "START();END()", "{START();{}END()}"); + } + + public void testBug5786871() { + CompilerOptions options = createCompilerOptions(); + options.ideMode = true; + test(options, "function () {}", RhinoErrorReporter.PARSE_ERROR); + } + + public void testIssue378() { + CompilerOptions options = createCompilerOptions(); + options.inlineVariables = true; + options.flowSensitiveInlineVariables = true; + testSame(options, "function f(c) {var f = c; arguments[0] = this;" + + " f.apply(this, arguments); return this;}"); + } + + public void testIssue550() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.SIMPLE_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + options.foldConstants = true; + options.inlineVariables = true; + options.flowSensitiveInlineVariables = true; + test(options, + "function f(h) {\n" + + " var a = h;\n" + + " a = a + 'x';\n" + + " a = a + 'y';\n" + + " return a;\n" + + "}", + // This should eventually get inlined completely. + "function f(a) { a += 'x'; return a += 'y'; }"); + } + + public void testIssue284() { + CompilerOptions options = createCompilerOptions(); + options.smartNameRemoval = true; + test(options, + "var goog = {};" + + "goog.inherits = function(x, y) {};" + + "var ns = {};" + + "/** @constructor */" + + "ns.PageSelectionModel = function() {};" + + "/** @constructor */" + + "ns.PageSelectionModel.FooEvent = function() {};" + + "/** @constructor */" + + "ns.PageSelectionModel.SelectEvent = function() {};" + + "goog.inherits(ns.PageSelectionModel.ChangeEvent," + + " ns.PageSelectionModel.FooEvent);", + ""); + } + + public void testIssue772() throws Exception { + CompilerOptions options = createCompilerOptions(); + options.closurePass = true; + options.checkTypes = true; + test( + options, + "/** @const */ var a = {};" + + "/** @const */ a.b = {};" + + "/** @const */ a.b.c = {};" + + "goog.scope(function() {" + + " var b = a.b;" + + " var c = b.c;" + + " /** @typedef {string} */" + + " c.MyType;" + + " /** @param {c.MyType} x The variable. */" + + " c.myFunc = function(x) {};" + + "});", + "/** @const */ var a = {};" + + "/** @const */ a.b = {};" + + "/** @const */ a.b.c = {};" + + "a.b.c.MyType;" + + "a.b.c.myFunc = function(x) {};"); + } + + public void testCodingConvention() { + Compiler compiler = new Compiler(); + compiler.initOptions(new CompilerOptions()); + assertEquals( + compiler.getCodingConvention().getClass().toString(), + ClosureCodingConvention.class.toString()); + } + + public void testJQueryStringSplitLoops() { + CompilerOptions options = createCompilerOptions(); + options.foldConstants = true; + test(options, + "var x=['1','2','3','4','5','6','7']", + "var x='1234567'.split('')"); + + options = createCompilerOptions(); + options.foldConstants = true; + options.computeFunctionSideEffects = false; + options.removeUnusedVars = true; + + // If we do splits too early, it would add a side-effect to x. + test(options, + "var x=['1','2','3','4','5','6','7']", + ""); + + } + + public void testAlwaysRunSafetyCheck() { + CompilerOptions options = createCompilerOptions(); + options.checkSymbols = false; + options.customPasses = ArrayListMultimap.create(); + options.customPasses.put( + CustomPassExecutionTime.BEFORE_OPTIMIZATIONS, + new CompilerPass() { + @Override public void process(Node externs, Node root) { + Node var = root.getLastChild().getFirstChild(); + assertEquals(Token.VAR, var.getType()); + var.detachFromParent(); + } + }); + try { + test(options, + "var x = 3; function f() { return x + z; }", + "function f() { return x + z; }"); + fail("Expected run-time exception"); + } catch (RuntimeException e) { + assertTrue(e.getMessage().indexOf("Unexpected variable x") != -1); + } + } + + public void testSuppressEs5StrictWarning() { + CompilerOptions options = createCompilerOptions(); + options.setWarningLevel(DiagnosticGroups.ES5_STRICT, CheckLevel.WARNING); + test(options, + "/** @suppress{es5Strict} */\n" + + "function f() { var arguments; }", + "function f() {}"); + } + + public void testCheckProvidesWarning() { + CompilerOptions options = createCompilerOptions(); + options.setWarningLevel(DiagnosticGroups.CHECK_PROVIDES, CheckLevel.WARNING); + options.setCheckProvides(CheckLevel.WARNING); + test(options, + "/** @constructor */\n" + + "function f() { var arguments; }", + DiagnosticType.warning("JSC_MISSING_PROVIDE", "missing goog.provide(''{0}'')")); + } + + public void testSuppressCheckProvidesWarning() { + CompilerOptions options = createCompilerOptions(); + options.setWarningLevel(DiagnosticGroups.CHECK_PROVIDES, CheckLevel.WARNING); + options.setCheckProvides(CheckLevel.WARNING); + testSame(options, + "/** @constructor\n" + + " * @suppress{checkProvides} */\n" + + "function f() {}"); + } + + public void testSuppressCastWarning() { + CompilerOptions options = createCompilerOptions(); + options.setWarningLevel(DiagnosticGroups.CHECK_TYPES, CheckLevel.WARNING); + + normalizeResults = true; + + test(options, + "function f() { var xyz = /** @type {string} */ (0); }", + DiagnosticType.warning( + "JSC_INVALID_CAST", "invalid cast")); + + testSame(options, + "/** @suppress{cast} */\n" + + "function f() { var xyz = /** @type {string} */ (0); }"); + } + + public void testRenamePrefix() { + String code = "var x = {}; function f(y) {}"; + CompilerOptions options = createCompilerOptions(); + options.renamePrefix = "G_"; + options.variableRenaming = VariableRenamingPolicy.ALL; + test(options, code, "var G_={}; function G_a(a) {}"); + } + + public void testRenamePrefixNamespace() { + String code = + "var x = {}; x.FOO = 5; x.bar = 3;"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + options.collapseProperties = true; + options.renamePrefixNamespace = "_"; + test(options, code, "_.x$FOO = 5; _.x$bar = 3;"); + } + + public void testRenamePrefixNamespaceProtectSideEffects() { + String code = "var x = null; try { +x.FOO; } catch (e) {}"; + + CompilerOptions options = createCompilerOptions(); + testSame(options, code); + + CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel( + options); + options.renamePrefixNamespace = "_"; + test(options, code, "_.x = null; try { +_.x.FOO; } catch (e) {}"); + } + + public void testRenamePrefixNamespaceActivatesMoveFunctionDeclarations() { + CompilerOptions options = createCompilerOptions(); + String code = "var x = f; function f() { return 3; }"; + testSame(options, code); + assertFalse(options.moveFunctionDeclarations); + options.renamePrefixNamespace = "_"; + test(options, code, "_.f = function() { return 3; }; _.x = _.f;"); + } + + public void testBrokenNameSpace() { + CompilerOptions options = createCompilerOptions(); + String code = "var goog; goog.provide('i.am.on.a.Horse');" + + "i.am.on.a.Horse = function() {};" + + "i.am.on.a.Horse.prototype.x = function() {};" + + "i.am.on.a.Boat.prototype.y = function() {}"; + options.closurePass = true; + options.collapseProperties = true; + options.smartNameRemoval = true; + test(options, code, ""); + } + + public void testNamelessParameter() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.ADVANCED_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + String code = + "var impl_0;" + + "$load($init());" + + "function $load(){" + + " window['f'] = impl_0;" + + "}" + + "function $init() {" + + " impl_0 = {};" + + "}"; + String result = + "window.f = {};"; + test(options, code, result); + } + + public void testHiddenSideEffect() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.ADVANCED_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + options.setAliasExternals(true); + String code = + "window.offsetWidth;"; + String result = + "window.offsetWidth;"; + test(options, code, result); + } + + public void testNegativeZero() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.ADVANCED_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + test(options, + "function bar(x) { return x; }\n" + + "function foo(x) { print(x / bar(0));\n" + + " print(x / bar(-0)); }\n" + + "foo(3);", + "print(3/0);print(3/-0);"); + } + + public void testSingletonGetter1() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel.ADVANCED_OPTIMIZATIONS + .setOptionsForCompilationLevel(options); + options.setCodingConvention(new ClosureCodingConvention()); + test(options, + "/** @const */\n" + + "var goog = goog || {};\n" + + "goog.addSingletonGetter = function(ctor) {\n" + + " ctor.getInstance = function() {\n" + + " return ctor.instance_ || (ctor.instance_ = new ctor());\n" + + " };\n" + + "};" + + "function Foo() {}\n" + + "goog.addSingletonGetter(Foo);" + + "Foo.prototype.bar = 1;" + + "function Bar() {}\n" + + "goog.addSingletonGetter(Bar);" + + "Bar.prototype.bar = 1;", + ""); + } + + public void testIncompleteFunction1() { + CompilerOptions options = createCompilerOptions(); + options.ideMode = true; + DiagnosticType[] warnings = new DiagnosticType[]{ + RhinoErrorReporter.PARSE_ERROR, + RhinoErrorReporter.PARSE_ERROR}; + test(options, + new String[] { "var foo = {bar: function(e) }" }, + new String[] { "var foo = {bar: function(e){}};" }, + warnings + ); + } + + public void testIncompleteFunction2() { + CompilerOptions options = createCompilerOptions(); + options.ideMode = true; + DiagnosticType[] warnings = new DiagnosticType[]{ + RhinoErrorReporter.PARSE_ERROR, + RhinoErrorReporter.PARSE_ERROR, + RhinoErrorReporter.PARSE_ERROR, + RhinoErrorReporter.PARSE_ERROR, + RhinoErrorReporter.PARSE_ERROR, + RhinoErrorReporter.PARSE_ERROR}; + test(options, + new String[] { "function hi" }, + new String[] { "function hi() {}" }, + warnings + ); + } + + public void testSortingOff() { + CompilerOptions options = new CompilerOptions(); + options.closurePass = true; + options.setCodingConvention(new ClosureCodingConvention()); + test(options, + new String[] { + "goog.require('goog.beer');", + "goog.provide('goog.beer');" + }, + ProcessClosurePrimitives.LATE_PROVIDE_ERROR); + } + + public void testUnboundedArrayLiteralInfiniteLoop() { + CompilerOptions options = createCompilerOptions(); + options.ideMode = true; + test(options, + "var x = [1, 2", + "var x = [1, 2]", + RhinoErrorReporter.PARSE_ERROR); + } + + public void testProvideRequireSameFile() throws Exception { + CompilerOptions options = createCompilerOptions(); + options.setDependencyOptions( + new DependencyOptions() + .setDependencySorting(true)); + options.closurePass = true; + test( + options, + "goog.provide('x');\ngoog.require('x');", + "var x = {};"); + } + + public void testDependencySorting() throws Exception { + CompilerOptions options = createCompilerOptions(); + options.setDependencyOptions( + new DependencyOptions() + .setDependencySorting(true)); + test( + options, + new String[] { + "goog.require('x');", + "goog.provide('x');", + }, + new String[] { + "goog.provide('x');", + "goog.require('x');", + + // For complicated reasons involving modules, + // the compiler creates a synthetic source file. + "", + }); + } + + public void testStrictWarningsGuard() throws Exception { + CompilerOptions options = createCompilerOptions(); + options.checkTypes = true; + options.addWarningsGuard(new StrictWarningsGuard()); + + Compiler compiler = compile(options, + "/** @return {number} */ function f() { return true; }"); + assertEquals(1, compiler.getErrors().length); + assertEquals(0, compiler.getWarnings().length); + } + + public void testStrictWarningsGuardEmergencyMode() throws Exception { + CompilerOptions options = createCompilerOptions(); + options.checkTypes = true; + options.addWarningsGuard(new StrictWarningsGuard()); + options.useEmergencyFailSafe(); + + Compiler compiler = compile(options, + "/** @return {number} */ function f() { return true; }"); + assertEquals(0, compiler.getErrors().length); + assertEquals(1, compiler.getWarnings().length); + } + + public void testInlineProperties() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS; + level.setOptionsForCompilationLevel(options); + level.setTypeBasedOptimizationOptions(options); + + String code = "" + + "var ns = {};\n" + + "/** @constructor */\n" + + "ns.C = function () {this.someProperty = 1}\n" + + "alert(new ns.C().someProperty + new ns.C().someProperty);\n"; + assertTrue(options.inlineProperties); + assertTrue(options.collapseProperties); + // CollapseProperties used to prevent inlining this property. + test(options, code, "alert(2);"); + } + + public void testGoogDefineClass1() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS; + level.setOptionsForCompilationLevel(options); + level.setTypeBasedOptimizationOptions(options); + + String code = "" + + "var ns = {};\n" + + "ns.C = goog.defineClass(null, {\n" + + " /** @constructor */\n" + + " constructor: function () {this.someProperty = 1}\n" + + "});\n" + + "alert(new ns.C().someProperty + new ns.C().someProperty);\n"; + assertTrue(options.inlineProperties); + assertTrue(options.collapseProperties); + // CollapseProperties used to prevent inlining this property. + test(options, code, "alert(2);"); + } + + public void testGoogDefineClass2() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS; + level.setOptionsForCompilationLevel(options); + level.setTypeBasedOptimizationOptions(options); + + String code = "" + + "var C = goog.defineClass(null, {\n" + + " /** @constructor */\n" + + " constructor: function () {this.someProperty = 1}\n" + + "});\n" + + "alert(new C().someProperty + new C().someProperty);\n"; + assertTrue(options.inlineProperties); + assertTrue(options.collapseProperties); + // CollapseProperties used to prevent inlining this property. + test(options, code, "alert(2);"); + } + + public void testGoogDefineClass3() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS; + level.setOptionsForCompilationLevel(options); + level.setTypeBasedOptimizationOptions(options); + WarningLevel warnings = WarningLevel.VERBOSE; + warnings.setOptionsForWarningLevel(options); + + String code = "" + + "var C = goog.defineClass(null, {\n" + + " /** @constructor */\n" + + " constructor: function () {\n" + + " /** @type {number} */\n" + + " this.someProperty = 1},\n" + + " /** @param {string} a */\n" + + " someMethod: function (a) {}\n" + + "});" + + "var x = new C();\n" + + "x.someMethod(x.someProperty);\n"; + assertTrue(options.inlineProperties); + assertTrue(options.collapseProperties); + // CollapseProperties used to prevent inlining this property. + test(options, code, TypeValidator.TYPE_MISMATCH_WARNING); + } + + public void testCheckConstants1() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS; + level.setOptionsForCompilationLevel(options); + WarningLevel warnings = WarningLevel.QUIET; + warnings.setOptionsForWarningLevel(options); + + String code = "" + + "var foo; foo();\n" + + "/** @const */\n" + + "var x = 1; foo(); x = 2;\n"; + test(options, code, code); + } + + public void testCheckConstants2() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS; + level.setOptionsForCompilationLevel(options); + WarningLevel warnings = WarningLevel.DEFAULT; + warnings.setOptionsForWarningLevel(options); + + String code = "" + + "var foo;\n" + + "/** @const */\n" + + "var x = 1; foo(); x = 2;\n"; + test(options, code, ConstCheck.CONST_REASSIGNED_VALUE_ERROR); + } + + public void testIssue787() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS; + level.setOptionsForCompilationLevel(options); + WarningLevel warnings = WarningLevel.DEFAULT; + warnings.setOptionsForWarningLevel(options); + + String code = "" + + "function some_function() {\n" + + " var fn1;\n" + + " var fn2;\n" + + "\n" + + " if (any_expression) {\n" + + " fn2 = external_ref;\n" + + " fn1 = function (content) {\n" + + " return fn2();\n" + + " }\n" + + " }\n" + + "\n" + + " return {\n" + + " method1: function () {\n" + + " if (fn1) fn1();\n" + + " return true;\n" + + " },\n" + + " method2: function () {\n" + + " return false;\n" + + " }\n" + + " }\n" + + "}"; + + String result = "" + + "function some_function() {\n" + + " var a, b;\n" + + " any_expression && (b = external_ref, a = function(a) {\n" + + " return b()\n" + + " });\n" + + " return{method1:function() {\n" + + " a && a();\n" + + " return !0\n" + + " }, method2:function() {\n" + + " return !1\n" + + " }}\n" + + "}\n" + + ""; + + test(options, code, result); + } + + public void testManyAdds() { + CompilerOptions options = createCompilerOptions(); + CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS; + level.setOptionsForCompilationLevel(options); + WarningLevel warnings = WarningLevel.VERBOSE; + warnings.setOptionsForWarningLevel(options); + + int numAdds = 4750; + StringBuilder original = new StringBuilder("var x = 0"); + for (int i = 0; i < numAdds; i++) { + original.append(" + 1"); + } + original.append(";"); + test(options, original.toString(), "var x = " + numAdds + ";"); + } + + /** Creates a CompilerOptions object with google coding conventions. */ + @Override + protected CompilerOptions createCompilerOptions() { + CompilerOptions options = new CompilerOptions(); + options.setCodingConvention(new GoogleCodingConvention()); + return options; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/IntegrationTestCase.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/IntegrationTestCase.java new file mode 100644 index 0000000..955eaad --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/IntegrationTestCase.java @@ -0,0 +1,225 @@ +/* + * Copyright 2012 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.javascript.rhino.Node; + +import junit.framework.TestCase; + +import java.util.List; + +/** + * Framework for end-to-end test cases. + * + * @author nicksantos@google.com (Nick Santos) + */ +abstract class IntegrationTestCase extends TestCase { + + /** Externs for the test */ + protected final List DEFAULT_EXTERNS = ImmutableList.of( + SourceFile.fromCode("externs", + "var arguments;\n" + + "/** @constructor */ function Window() {}\n" + + "/** @type {string} */ Window.prototype.name;\n" + + "/** @type {string} */ Window.prototype.offsetWidth;\n" + + "/** @type {Window} */ var window;\n" + + "/** @nosideeffects */ function noSideEffects() {}\n" + + "/** @constructor\n * @nosideeffects */ function Widget() {}\n" + + "/** @modifies {this} */ Widget.prototype.go = function() {};\n" + + "/** @return {string} */ var widgetToken = function() {};\n" + + "function alert(message) {}" + + "function Object() {}" + + "Object.seal;")); + + protected List externs = DEFAULT_EXTERNS; + + // The most recently used compiler. + protected Compiler lastCompiler; + + protected boolean normalizeResults = false; + + @Override + public void setUp() { + externs = DEFAULT_EXTERNS; + lastCompiler = null; + normalizeResults = false; + } + + protected void testSame(CompilerOptions options, String original) { + testSame(options, new String[] { original }); + } + + protected void testSame(CompilerOptions options, String[] original) { + test(options, original, original); + } + + /** + * Asserts that when compiling with the given compiler options, + * {@code original} is transformed into {@code compiled}. + */ + protected void test(CompilerOptions options, + String original, String compiled) { + test(options, new String[] { original }, new String[] { compiled }); + } + + /** + * Asserts that when compiling with the given compiler options, + * {@code original} is transformed into {@code compiled}. + */ + protected void test(CompilerOptions options, + String[] original, String[] compiled) { + Compiler compiler = compile(options, original); + assertEquals("Expected no warnings or errors\n" + + "Errors: \n" + Joiner.on("\n").join(compiler.getErrors()) + + "Warnings: \n" + Joiner.on("\n").join(compiler.getWarnings()), + 0, compiler.getErrors().length + compiler.getWarnings().length); + + Node root = compiler.getRoot().getLastChild(); + Node expectedRoot = parse(compiled, options, normalizeResults); + String explanation = expectedRoot.checkTreeEquals(root); + assertNull("\nExpected: " + compiler.toSource(expectedRoot) + + "\nResult: " + compiler.toSource(root) + + "\n" + explanation, explanation); + } + + /** + * Asserts that when compiling with the given compiler options, + * there is an error or warning. + */ + protected void test(CompilerOptions options, + String original, DiagnosticType warning) { + test(options, new String[] { original }, warning); + } + + protected void test(CompilerOptions options, + String original, String compiled, DiagnosticType warning) { + test(options, new String[] { original }, new String[] { compiled }, + warning); + } + + protected void test(CompilerOptions options, + String[] original, DiagnosticType warning) { + test(options, original, null, warning); + } + + /** + * Asserts that when compiling with the given compiler options, + * there is an error or warning. + */ + protected void test(CompilerOptions options, + String[] original, String[] compiled, DiagnosticType warning) { + Compiler compiler = compile(options, original); + checkUnexpectedErrorsOrWarnings(compiler, 1); + assertEquals("Expected exactly one warning or error", + 1, compiler.getErrors().length + compiler.getWarnings().length); + if (compiler.getErrors().length > 0) { + assertEquals(warning, compiler.getErrors()[0].getType()); + } else { + assertEquals(warning, compiler.getWarnings()[0].getType()); + } + + if (compiled != null) { + Node root = compiler.getRoot().getLastChild(); + Node expectedRoot = parse(compiled, options, normalizeResults); + String explanation = expectedRoot.checkTreeEquals(root); + assertNull("\nExpected: " + compiler.toSource(expectedRoot) + + "\nResult: " + compiler.toSource(root) + + "\n" + explanation, explanation); + } + } + + /** + * Asserts that when compiling with the given compiler options, + * there is an error or warning. + */ + protected void test(CompilerOptions options, + String[] original, String[] compiled, DiagnosticType[] warnings) { + Compiler compiler = compile(options, original); + checkUnexpectedErrorsOrWarnings(compiler, warnings.length); + + if (compiled != null) { + Node root = compiler.getRoot().getLastChild(); + Node expectedRoot = parse(compiled, options, normalizeResults); + String explanation = expectedRoot.checkTreeEquals(root); + assertNull("\nExpected: " + compiler.toSource(expectedRoot) + + "\nResult: " + compiler.toSource(root) + + "\n" + explanation, explanation); + } + } + + protected void checkUnexpectedErrorsOrWarnings( + Compiler compiler, int expected) { + int actual = compiler.getErrors().length + compiler.getWarnings().length; + if (actual != expected) { + String msg = ""; + for (JSError err : compiler.getErrors()) { + msg += "Error:" + err.toString() + "\n"; + } + for (JSError err : compiler.getWarnings()) { + msg += "Warning:" + err.toString() + "\n"; + } + assertEquals("Unexpected warnings or errors.\n " + msg, + expected, actual); + } + } + + protected Compiler compile(CompilerOptions options, String original) { + return compile(options, new String[] { original }); + } + + protected Compiler compile(CompilerOptions options, String[] original) { + Compiler compiler = lastCompiler = new Compiler(); + List inputs = Lists.newArrayList(); + for (int i = 0; i < original.length; i++) { + inputs.add(SourceFile.fromCode("input" + i, original[i])); + } + compiler.compileModules( + externs, Lists.newArrayList(CompilerTestCase.createModuleChain(original)), + options); + return compiler; + } + + protected Node parse( + String[] original, CompilerOptions options, boolean normalize) { + Compiler compiler = new Compiler(); + List inputs = Lists.newArrayList(); + for (int i = 0; i < original.length; i++) { + inputs.add(SourceFile.fromCode("input" + i, original[i])); + } + compiler.init(externs, inputs, options); + checkUnexpectedErrorsOrWarnings(compiler, 0); + Node all = compiler.parseInputs(); + checkUnexpectedErrorsOrWarnings(compiler, 0); + Node n = all.getLastChild(); + Node externs = all.getFirstChild(); + + (new CreateSyntheticBlocks( + compiler, "synStart", "synEnd")).process(externs, n); + + if (normalize) { + compiler.normalize(); + } + + return n; + } + + /** Creates a CompilerOptions object with google coding conventions. */ + abstract CompilerOptions createCompilerOptions(); +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JSCompilerSourceExcerptProviderTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JSCompilerSourceExcerptProviderTest.java new file mode 100644 index 0000000..baec301 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JSCompilerSourceExcerptProviderTest.java @@ -0,0 +1,121 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + + +import com.google.common.collect.ImmutableList; + +import junit.framework.TestCase; + +/** + */ +public class JSCompilerSourceExcerptProviderTest extends TestCase { + private SourceExcerptProvider provider; + + @Override + protected void setUp() throws Exception { + SourceFile foo = SourceFile.fromCode("foo", + "foo:first line\nfoo:second line\nfoo:third line\n"); + SourceFile bar = SourceFile.fromCode("bar", + "bar:first line\nbar:second line\nbar:third line\nbar:fourth line\n"); + SourceFile foo2 = SourceFile.fromCode("foo2", + "foo2:first line\nfoo2:second line\nfoo2:third line"); + Compiler compiler = new Compiler(); + CompilerOptions options = new CompilerOptions(); + compiler.init( + ImmutableList.of(), + ImmutableList.of(foo, bar, foo2), + options); + this.provider = compiler; + } + + public void testExcerptOneLine() throws Exception { + assertEquals("foo:first line", provider.getSourceLine("foo", 1)); + assertEquals("foo:second line", provider.getSourceLine("foo", 2)); + assertEquals("foo:third line", provider.getSourceLine("foo", 3)); + assertEquals("bar:first line", provider.getSourceLine("bar", 1)); + assertEquals("bar:second line", provider.getSourceLine("bar", 2)); + assertEquals("bar:third line", provider.getSourceLine("bar", 3)); + assertEquals("bar:fourth line", provider.getSourceLine("bar", 4)); + } + + public void testExcerptLineFromInexistantSource() throws Exception { + assertEquals(null, provider.getSourceLine("inexistant", 1)); + assertEquals(null, provider.getSourceLine("inexistant", 7)); + assertEquals(null, provider.getSourceLine("inexistant", 90)); + } + + public void testExcerptInexistantLine() throws Exception { + assertEquals(null, provider.getSourceLine("foo", 0)); + assertEquals(null, provider.getSourceLine("foo", 4)); + assertEquals(null, provider.getSourceLine("bar", 0)); + assertEquals(null, provider.getSourceLine("bar", 5)); + } + + public void testExceptNoNewLine() throws Exception { + assertEquals("foo2:first line", provider.getSourceLine("foo2", 1)); + assertEquals("foo2:second line", provider.getSourceLine("foo2", 2)); + assertEquals("foo2:third line", provider.getSourceLine("foo2", 3)); + assertEquals(null, provider.getSourceLine("foo2", 4)); + } + + public void testExcerptRegion() throws Exception { + assertRegionWellFormed("foo", 1); + assertRegionWellFormed("foo", 2); + assertRegionWellFormed("foo", 3); + assertRegionWellFormed("bar", 1); + assertRegionWellFormed("bar", 2); + assertRegionWellFormed("bar", 3); + assertRegionWellFormed("bar", 4); + } + + public void testExcerptRegionFromInexistantSource() throws Exception { + assertEquals(null, provider.getSourceRegion("inexistant", 0)); + assertEquals(null, provider.getSourceRegion("inexistant", 6)); + assertEquals(null, provider.getSourceRegion("inexistant", 90)); + } + + public void testExcerptInexistantRegion() throws Exception { + assertEquals(null, provider.getSourceRegion("foo", 0)); + assertEquals(null, provider.getSourceRegion("foo", 4)); + assertEquals(null, provider.getSourceRegion("bar", 0)); + assertEquals(null, provider.getSourceRegion("bar", 5)); + } + + /** + * Asserts that a region is 'well formed': it must not be an empty and + * cannot start or finish by a carriage return. In addition, it must + * contain the line whose region we are taking. + */ + private void assertRegionWellFormed(String sourceName, int lineNumber) { + Region region = provider.getSourceRegion(sourceName, lineNumber); + assertNotNull(region); + String sourceRegion = region.getSourceExcerpt(); + assertNotNull(sourceRegion); + if (lineNumber == 1) { + assertEquals(1, region.getBeginningLineNumber()); + } else { + assertTrue(region.getBeginningLineNumber() <= lineNumber); + } + assertTrue(lineNumber <= region.getEndingLineNumber()); + assertNotSame(sourceRegion, 0, sourceRegion.length()); + assertNotSame(sourceRegion, '\n', sourceRegion.charAt(0)); + assertNotSame(sourceRegion, + '\n', sourceRegion.charAt(sourceRegion.length() - 1)); + String line = provider.getSourceLine(sourceName, lineNumber); + assertTrue(sourceRegion, sourceRegion.contains(line)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JSModuleGraphTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JSModuleGraphTest.java new file mode 100644 index 0000000..144c7fa --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JSModuleGraphTest.java @@ -0,0 +1,354 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import junit.framework.*; + +import java.util.*; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Tests for {@link JSModuleGraph} + * + */ +public class JSModuleGraphTest extends TestCase { + + private final JSModule A = new JSModule("A"); + private final JSModule B = new JSModule("B"); + private final JSModule C = new JSModule("C"); + private final JSModule D = new JSModule("D"); + private final JSModule E = new JSModule("E"); + private final JSModule F = new JSModule("F"); + private JSModuleGraph graph = null; + + // For resolving dependencies only. + private Compiler compiler; + + @Override + public void setUp() throws Exception { + super.setUp(); + B.addDependency(A); // __A__ + C.addDependency(A); // / | \ + D.addDependency(B); // B C | + E.addDependency(B); // / \ /| | + E.addDependency(C); // D E | / + F.addDependency(A); // \|/ + F.addDependency(C); // F + F.addDependency(E); + graph = new JSModuleGraph(new JSModule[] {A, B, C, D, E, F}); + compiler = new Compiler(); + } + + public void testModuleDepth() { + assertEquals("A should have depth 0", 0, A.getDepth()); + assertEquals("B should have depth 1", 1, B.getDepth()); + assertEquals("C should have depth 1", 1, C.getDepth()); + assertEquals("D should have depth 2", 2, D.getDepth()); + assertEquals("E should have depth 2", 2, E.getDepth()); + assertEquals("F should have depth 3", 3, F.getDepth()); + } + + public void testDeepestCommonDep() { + assertDeepestCommonDep(null, A, A); + assertDeepestCommonDep(null, A, B); + assertDeepestCommonDep(null, A, C); + assertDeepestCommonDep(null, A, D); + assertDeepestCommonDep(null, A, E); + assertDeepestCommonDep(null, A, F); + assertDeepestCommonDep(A, B, B); + assertDeepestCommonDep(A, B, C); + assertDeepestCommonDep(A, B, D); + assertDeepestCommonDep(A, B, E); + assertDeepestCommonDep(A, B, F); + assertDeepestCommonDep(A, C, C); + assertDeepestCommonDep(A, C, D); + assertDeepestCommonDep(A, C, E); + assertDeepestCommonDep(A, C, F); + assertDeepestCommonDep(B, D, D); + assertDeepestCommonDep(B, D, E); + assertDeepestCommonDep(B, D, F); + assertDeepestCommonDep(C, E, E); + assertDeepestCommonDep(C, E, F); + assertDeepestCommonDep(E, F, F); + } + + public void testDeepestCommonDepInclusive() { + assertDeepestCommonDepInclusive(A, A, A); + assertDeepestCommonDepInclusive(A, A, B); + assertDeepestCommonDepInclusive(A, A, C); + assertDeepestCommonDepInclusive(A, A, D); + assertDeepestCommonDepInclusive(A, A, E); + assertDeepestCommonDepInclusive(A, A, F); + assertDeepestCommonDepInclusive(B, B, B); + assertDeepestCommonDepInclusive(A, B, C); + assertDeepestCommonDepInclusive(B, B, D); + assertDeepestCommonDepInclusive(B, B, E); + assertDeepestCommonDepInclusive(B, B, F); + assertDeepestCommonDepInclusive(C, C, C); + assertDeepestCommonDepInclusive(A, C, D); + assertDeepestCommonDepInclusive(C, C, E); + assertDeepestCommonDepInclusive(C, C, F); + assertDeepestCommonDepInclusive(D, D, D); + assertDeepestCommonDepInclusive(B, D, E); + assertDeepestCommonDepInclusive(B, D, F); + assertDeepestCommonDepInclusive(E, E, E); + assertDeepestCommonDepInclusive(E, E, F); + assertDeepestCommonDepInclusive(F, F, F); + } + + public void testGetTransitiveDepsDeepestFirst() { + assertTransitiveDepsDeepestFirst(A); + assertTransitiveDepsDeepestFirst(B, A); + assertTransitiveDepsDeepestFirst(C, A); + assertTransitiveDepsDeepestFirst(D, B, A); + assertTransitiveDepsDeepestFirst(E, C, B, A); + assertTransitiveDepsDeepestFirst(F, E, C, B, A); + } + + public void testCoalesceDuplicateFiles() { + A.add(SourceFile.fromCode("a.js", "")); + + B.add(SourceFile.fromCode("a.js", "")); + B.add(SourceFile.fromCode("b.js", "")); + + C.add(SourceFile.fromCode("b.js", "")); + C.add(SourceFile.fromCode("c.js", "")); + + E.add(SourceFile.fromCode("c.js", "")); + E.add(SourceFile.fromCode("d.js", "")); + + graph.coalesceDuplicateFiles(); + + assertEquals(2, A.getInputs().size()); + assertEquals("a.js", A.getInputs().get(0).getName()); + assertEquals("b.js", A.getInputs().get(1).getName()); + assertEquals(0, B.getInputs().size()); + assertEquals(1, C.getInputs().size()); + assertEquals("c.js", C.getInputs().get(0).getName()); + assertEquals(1, E.getInputs().size()); + assertEquals("d.js", E.getInputs().get(0).getName()); + } + + public void testManageDependencies1() throws Exception { + List inputs = setUpManageDependenciesTest(); + List results = graph.manageDependencies( + ImmutableList.of(), inputs); + + assertInputs(A, "a1", "a3"); + assertInputs(B, "a2", "b2"); + assertInputs(C); // no inputs + assertInputs(E, "c1", "e1", "e2"); + + assertEquals( + Lists.newArrayList("a1", "a3", "a2", "b2", "c1", "e1", "e2"), + sourceNames(results)); + } + + public void testManageDependencies2() throws Exception { + List inputs = setUpManageDependenciesTest(); + List results = graph.manageDependencies( + ImmutableList.of("c2"), inputs); + + assertInputs(A, "a1", "a3"); + assertInputs(B, "a2", "b2"); + assertInputs(C, "c1", "c2"); + assertInputs(E, "e1", "e2"); + + assertEquals( + Lists.newArrayList("a1", "a3", "a2", "b2", "c1", "c2", "e1", "e2"), + sourceNames(results)); + } + + public void testManageDependencies3() throws Exception { + List inputs = setUpManageDependenciesTest(); + DependencyOptions depOptions = new DependencyOptions(); + depOptions.setDependencySorting(true); + depOptions.setDependencyPruning(true); + depOptions.setMoocherDropping(true); + depOptions.setEntryPoints(ImmutableList.of("c2")); + List results = graph.manageDependencies( + depOptions, inputs); + + // Everything gets pushed up into module c, because that's + // the only one that has entry points. + assertInputs(A); + assertInputs(B); + assertInputs(C, "a1", "c1", "c2"); + assertInputs(E); + + assertEquals( + Lists.newArrayList("a1", "c1", "c2"), + sourceNames(results)); + } + + public void testManageDependencies4() throws Exception { + setUpManageDependenciesTest(); + DependencyOptions depOptions = new DependencyOptions(); + depOptions.setDependencySorting(true); + + List inputs = Lists.newArrayList(); + + // Add the inputs in a random order. + inputs.addAll(E.getInputs()); + inputs.addAll(B.getInputs()); + inputs.addAll(A.getInputs()); + inputs.addAll(C.getInputs()); + + List results = graph.manageDependencies( + depOptions, inputs); + + assertInputs(A, "a1", "a2", "a3"); + assertInputs(B, "b1", "b2"); + assertInputs(C, "c1", "c2"); + assertInputs(E, "e1", "e2"); + + assertEquals( + Lists.newArrayList( + "a1", "a2", "a3", "b1", "b2", "c1", "c2", "e1", "e2"), + sourceNames(results)); + } + + public void testNoFiles() throws Exception { + DependencyOptions depOptions = new DependencyOptions(); + depOptions.setDependencySorting(true); + + List inputs = Lists.newArrayList(); + List results = graph.manageDependencies( + depOptions, inputs); + assertTrue(results.isEmpty()); + } + + public void testToJson() throws JSONException { + JSONArray modules = graph.toJson(); + assertEquals(6, modules.length()); + for (int i = 0; i < modules.length(); i++) { + JSONObject m = modules.getJSONObject(i); + assertNotNull(m.getString("name")); + assertNotNull(m.getJSONArray("dependencies")); + assertNotNull(m.getJSONArray("transitive-dependencies")); + assertNotNull(m.getJSONArray("inputs")); + } + JSONObject m = modules.getJSONObject(3); + assertEquals("D", m.getString("name")); + assertEquals("[\"B\"]", m.getJSONArray("dependencies").toString()); + assertEquals(2, + m.getJSONArray("transitive-dependencies").length()); + assertEquals("[]", m.getJSONArray("inputs").toString()); + } + + private List setUpManageDependenciesTest() { + List inputs = Lists.newArrayList(); + + A.add(code("a1", provides("a1"), requires())); + A.add(code("a2", provides("a2"), requires("a1"))); + A.add(code("a3", provides(), requires("a1"))); + + B.add(code("b1", provides("b1"), requires("a2"))); + B.add(code("b2", provides(), requires("a1", "a2"))); + + C.add(code("c1", provides("c1"), requires("a1"))); + C.add(code("c2", provides("c2"), requires("c1"))); + + E.add(code("e1", provides(), requires("c1"))); + E.add(code("e2", provides(), requires("c1"))); + + inputs.addAll(A.getInputs()); + inputs.addAll(B.getInputs()); + inputs.addAll(C.getInputs()); + inputs.addAll(E.getInputs()); + + for (CompilerInput input : inputs) { + input.setCompiler(compiler); + } + return inputs; + } + + private void assertInputs(JSModule module, String ... sourceNames) { + List actualInputs = module.getInputs(); + + assertEquals( + Lists.newArrayList(sourceNames), + sourceNames(module.getInputs())); + } + + private List sourceNames(List inputs) { + List inputNames = Lists.newArrayList(); + for (CompilerInput input : inputs) { + inputNames.add(input.getName()); + } + return inputNames; + } + + private SourceFile code( + String sourceName, List provides, List requires) { + String text = ""; + for (String p : provides) { + text += "goog.provide('" + p + "');\n"; + } + for (String r : requires) { + text += "goog.require('" + r + "');\n"; + } + return SourceFile.fromCode(sourceName, text); + } + + private List provides(String ... strings) { + return Lists.newArrayList(strings); + } + + private List requires(String ... strings) { + return Lists.newArrayList(strings); + } + + private void assertDeepestCommonDepInclusive( + JSModule expected, JSModule m1, JSModule m2) { + assertDeepestCommonDepOneWay(expected, m1, m2, true); + assertDeepestCommonDepOneWay(expected, m2, m1, true); + } + + private void assertDeepestCommonDep( + JSModule expected, JSModule m1, JSModule m2) { + assertDeepestCommonDepOneWay(expected, m1, m2, false); + assertDeepestCommonDepOneWay(expected, m2, m1, false); + } + + private void assertDeepestCommonDepOneWay( + JSModule expected, JSModule m1, JSModule m2, boolean inclusive) { + JSModule actual = inclusive ? + graph.getDeepestCommonDependencyInclusive(m1, m2) : + graph.getDeepestCommonDependency(m1, m2); + if (actual != expected) { + fail(String.format( + "Deepest common dep of %s and %s should be %s but was %s", + m1.getName(), m2.getName(), + expected == null ? "null" : expected.getName(), + actual == null ? "null" : actual.getName())); + } + } + + private void assertTransitiveDepsDeepestFirst(JSModule m, JSModule... deps) { + Iterable actual = graph.getTransitiveDepsDeepestFirst(m); + assertEquals(Arrays.toString(deps), + Arrays.toString(Iterables.toArray(actual, JSModule.class))); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JSModuleTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JSModuleTest.java new file mode 100644 index 0000000..1963664 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JSModuleTest.java @@ -0,0 +1,149 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + +import junit.framework.TestCase; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; + +/** + * Tests for {@link JSModule} + * + */ +public class JSModuleTest extends TestCase { + private JSModule mod1; + private JSModule mod2; // depends on mod1 + private JSModule mod3; // depends on mod1 + private JSModule mod4; // depends on mod2, mod3 + private JSModule mod5; // depends on mod1 + + @Override + protected void setUp() { + List modulesInDepOrder = new ArrayList(); + + mod1 = new JSModule("mod1"); + modulesInDepOrder.add(mod1); + + mod2 = new JSModule("mod2"); + mod2.addDependency(mod1); + modulesInDepOrder.add(mod2); + + mod3 = new JSModule("mod3"); + mod3.addDependency(mod1); + modulesInDepOrder.add(mod3); + + mod4 = new JSModule("mod4"); + mod4.addDependency(mod2); + mod4.addDependency(mod3); + modulesInDepOrder.add(mod4); + + mod5 = new JSModule("mod5"); + mod5.addDependency(mod1); + modulesInDepOrder.add(mod5); + } + + public void testDependencies() { + assertEquals(ImmutableSet.of(), mod1.getAllDependencies()); + assertEquals(ImmutableSet.of(mod1), mod2.getAllDependencies()); + assertEquals(ImmutableSet.of(mod1), mod3.getAllDependencies()); + assertEquals(ImmutableSet.of(mod1, mod2, mod3), mod4.getAllDependencies()); + + assertEquals(ImmutableSet.of(mod1), mod1.getThisAndAllDependencies()); + assertEquals(ImmutableSet.of(mod1, mod2), mod2.getThisAndAllDependencies()); + assertEquals(ImmutableSet.of(mod1, mod3), mod3.getThisAndAllDependencies()); + assertEquals(ImmutableSet.of(mod1, mod2, mod3, mod4), + mod4.getThisAndAllDependencies()); + } + + public void testSortInputs() throws Exception { + CompilerInput a = new CompilerInput( + SourceFile.fromCode("a.js", + "goog.require('b');goog.require('c')")); + CompilerInput b = new CompilerInput( + SourceFile.fromCode("b.js", + "goog.provide('b');goog.require('d')")); + CompilerInput c = new CompilerInput( + SourceFile.fromCode("c.js", + "goog.provide('c');goog.require('d')")); + CompilerInput d = new CompilerInput( + SourceFile.fromCode("d.js", + "goog.provide('d')")); + + // Independent modules. + CompilerInput e = new CompilerInput( + SourceFile.fromCode("e.js", + "goog.provide('e')")); + CompilerInput f = new CompilerInput( + SourceFile.fromCode("f.js", + "goog.provide('f')")); + + assertSortedInputs( + ImmutableList.of(d, b, c, a), + ImmutableList.of(a, b, c, d)); + assertSortedInputs( + ImmutableList.of(d, b, c, a), + ImmutableList.of(d, b, c, a)); + assertSortedInputs( + ImmutableList.of(d, c, b, a), + ImmutableList.of(d, c, b, a)); + assertSortedInputs( + ImmutableList.of(d, b, c, a), + ImmutableList.of(d, a, b, c)); + } + + private void assertSortedInputs( + List expected, List shuffled) + throws Exception { + JSModule mod = new JSModule("mod"); + for (CompilerInput input : shuffled) { + input.setModule(null); + mod.add(input); + } + Compiler compiler = new Compiler(System.err); + compiler.initCompilerOptionsIfTesting(); + mod.sortInputsByDeps(compiler); + + assertEquals(expected, mod.getInputs()); + } + + public void testSortJsModules() throws Exception { + // already in order: + assertEquals(ImmutableList.of(mod1, mod2, mod3, mod4), + Arrays.asList(JSModule.sortJsModules( + ImmutableList.of(mod1, mod2, mod3, mod4)))); + assertEquals(ImmutableList.of(mod1, mod3, mod2, mod4), + Arrays.asList(JSModule.sortJsModules( + ImmutableList.of(mod1, mod3, mod2, mod4)))); + + // one out of order: + assertEquals(ImmutableList.of(mod1, mod3, mod2, mod4), + Arrays.asList(JSModule.sortJsModules( + ImmutableList.of(mod4, mod3, mod2, mod1)))); + assertEquals(ImmutableList.of(mod1, mod3, mod2, mod4), + Arrays.asList(JSModule.sortJsModules( + ImmutableList.of(mod3, mod1, mod2, mod4)))); + + // more out of order: + assertEquals(ImmutableList.of(mod1, mod3, mod2, mod4), + Arrays.asList(JSModule.sortJsModules( + ImmutableList.of(mod4, mod3, mod1, mod2)))); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JsMessageExtractorTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JsMessageExtractorTest.java new file mode 100644 index 0000000..588b2c6 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JsMessageExtractorTest.java @@ -0,0 +1,234 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import static com.google.javascript.jscomp.JsMessage.Style.RELAX; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** + * Unit test for {@link JsMessageExtractor}. + * + */ +public class JsMessageExtractorTest extends TestCase { + + private Collection extractMessages(String... js) { + try { + String sourceCode = Joiner.on("\n").join(js); + return new JsMessageExtractor(null, RELAX) + .extractMessages(SourceFile.fromCode("testcode", sourceCode)); + } catch (IOException e) { + fail(e.getMessage()); + return null; + } + } + + private JsMessage extractMessage(String... js) { + Collection messages = extractMessages(js); + assertEquals(1, messages.size()); + return messages.iterator().next(); + } + + public void testSyntaxError1() { + try { + extractMessage("if (true) {}}"); + fail("Expected exception"); + } catch (RuntimeException e) { + assertTrue(e.getMessage().contains("JSCompiler errors\n")); + assertTrue(e.getMessage().contains( + "testcode:1: ERROR - Parse error. syntax error\n")); + assertTrue(e.getMessage().contains("if (true) {}}\n")); + } + } + + public void testSyntaxError2() { + try { + extractMessage("", "if (true) {}}"); + fail("Expected exception"); + } catch (RuntimeException e) { + assertTrue( + e.getMessage(), + e.getMessage().contains("JSCompiler errors\n")); + assertTrue( + e.getMessage(), + e.getMessage().contains( + "testcode:2: ERROR - Parse error. syntax error\n")); + assertTrue( + e.getMessage(), + e.getMessage().contains("if (true) {}}\n")); + } + } + + public void testExtractNewStyleMessage1() { + // A simple message with no description. + assertEquals( + new JsMessage.Builder("MSG_SILLY") + .appendStringPart("silly test message") + .build(), + extractMessage("var MSG_SILLY = goog.getMsg('silly test message');")); + } + + public void testExtractNewStyleMessage2() { + // A message with placeholders and meta data. + assertEquals( + new JsMessage.Builder("MSG_WELCOME") + .appendStringPart("Hi ") + .appendPlaceholderReference("userName") + .appendStringPart("! Welcome to ") + .appendPlaceholderReference("product") + .appendStringPart(".") + .setDesc("The welcome message.") + .setIsHidden(true) + .build(), + extractMessage( + "/**", + " * @desc The welcome", + " * message.", + " *", + " * @hidden", + " */", + "var MSG_WELCOME = goog.getMsg(", + " 'Hi {$userName}! Welcome to {$product}.',", + " {userName: someUserName, product: getProductName()});")); + } + + public void testExtractOldStyleMessage1() { + // Description before the message. + assertEquals( + new JsMessage.Builder("MSG_SILLY") + .appendStringPart("silly test message") + .setDesc("Description.") + .build(), + extractMessage( + "var MSG_SILLY_HELP = 'Description.';", + "var MSG_SILLY = 'silly test message';")); + } + + public void testExtractOldStyleMessage2() { + // Description after the message, broken into parts. + assertEquals( + new JsMessage.Builder("MSG_SILLY") + .appendStringPart("silly test message") + .setDesc("Description.") + .build(), + extractMessage( + "var MSG_SILLY = 'silly test message';", + "var MSG_SILLY_HELP = 'Descrip' + 'tion.';")); + } + + public void testExtractOldStyleMessage3() { + // Function-style message with two placeholders and no description. + assertEquals( + new JsMessage.Builder("MSG_SILLY") + .appendPlaceholderReference("one") + .appendStringPart(", ") + .appendPlaceholderReference("two") + .appendStringPart(", buckle my shoe") + .build(), + extractMessage( + "var MSG_SILLY = function(one, two) {", + " return one + ', ' + two + ', buckle my shoe';", + "};")); + } + + public void testExtractMixedMessages() { + // Several mixed-style messages in succession, one containing newlines. + Iterator msgs = extractMessages( + "var MSG_MONEY = function(amount) {", + " return 'You owe $' + amount +", + " ' to the credit card company.';", + "};", + "var MSG_TIME = goog.getMsg('You need to finish your work in ' +", + " '{$duration} hours.', {'duration': d});", + "var MSG_NAG = 'Clean your room.\\n\\nWash your clothes.';", + "var MSG_NAG_HELP = 'Just some ' +", + " 'nags.';").iterator(); + + assertEquals( + new JsMessage.Builder("MSG_MONEY") + .appendStringPart("You owe $") + .appendPlaceholderReference("amount") + .appendStringPart(" to the credit card company.") + .build(), + msgs.next()); + assertEquals( + new JsMessage.Builder("MSG_TIME") + .appendStringPart("You need to finish your work in ") + .appendPlaceholderReference("duration") + .appendStringPart(" hours.") + .build(), + msgs.next()); + assertEquals( + new JsMessage.Builder("MSG_NAG") + .appendStringPart("Clean your room.\n\nWash your clothes.") + .setDesc("Just some nags.") + .build(), + msgs.next()); + } + + public void testDuplicateUnnamedVariables() { + // Make sure that duplicate unnamed variables don't get swallowed when using + // a Google-specific ID generator. + Collection msgs = extractMessages( + "function a() {", + " var MSG_UNNAMED_2 = goog.getMsg('foo');", + "}", + "function b() {", + " var MSG_UNNAMED_2 = goog.getMsg('bar');", + "}"); + + assertEquals(2, msgs.size()); + final Iterator iter = msgs.iterator(); + assertEquals("foo", iter.next().toString()); + assertEquals("bar", iter.next().toString()); + } + + public void testMeaningAnnotation() { + List msgs = Lists.newArrayList( + extractMessages( + "var MSG_UNNAMED_1 = goog.getMsg('foo');", + "var MSG_UNNAMED_2 = goog.getMsg('foo');")); + assertEquals(2, msgs.size()); + assertTrue(msgs.get(0).getId().equals(msgs.get(1).getId())); + assertEquals(msgs.get(0), msgs.get(1)); + + msgs = Lists.newArrayList( + extractMessages( + "var MSG_UNNAMED_1 = goog.getMsg('foo');", + "/** @meaning bar */ var MSG_UNNAMED_2 = goog.getMsg('foo');")); + assertEquals(2, msgs.size()); + assertFalse(msgs.get(0).getId().equals(msgs.get(1).getId())); + } + + private void assertEquals(JsMessage expected, JsMessage actual) { + assertEquals(expected.getId(), actual.getId()); + assertEquals(expected.getKey(), actual.getKey()); + assertEquals(expected.parts(), actual.parts()); + assertEquals(expected.placeholders(), actual.placeholders()); + assertEquals(expected.getDesc(), actual.getDesc()); + assertEquals(expected.isHidden(), actual.isHidden()); + assertEquals(expected.getMeaning(), actual.getMeaning()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JsMessageTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JsMessageTest.java new file mode 100644 index 0000000..3065e33 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JsMessageTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import junit.framework.*; + +/** + * @author anatol@google.com (Anatol Pomazau) + */ +public class JsMessageTest extends TestCase { + + public void testIsEmpty() { + assertTrue(new JsMessage.Builder().build().isEmpty()); + assertTrue(new JsMessage.Builder().appendStringPart("").build().isEmpty()); + assertTrue(new JsMessage.Builder().appendStringPart("") + .appendStringPart("").build().isEmpty()); + assertFalse(new JsMessage.Builder().appendStringPart("s") + .appendStringPart("").build().isEmpty()); + assertFalse(new JsMessage.Builder().appendPlaceholderReference("3") + .build().isEmpty()); + } + + public void testMeaningChangesId() { + String id1 = new JsMessage.Builder() + .appendStringPart("foo").build().getId(); + String id2 = new JsMessage.Builder() + .appendStringPart("foo").setMeaning("bar").build().getId(); + assertFalse(id1.equals(id2)); + } + + public void testHashValues() { + final String EMPTY = ""; + final String VAL = "Hello, world"; + final long ANSWER_STRING_64 = 0x43ec5d9731515874L; + final long ANSWER_EMPTY_64 = 0x468d9ea2c42361aaL; + + assertEquals(ANSWER_STRING_64, JsMessage.Hash.hash64(VAL)); + assertEquals(ANSWER_EMPTY_64, JsMessage.Hash.hash64(EMPTY)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JsMessageVisitorTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JsMessageVisitorTest.java new file mode 100644 index 0000000..7b42bf8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/JsMessageVisitorTest.java @@ -0,0 +1,586 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import static com.google.javascript.jscomp.JsMessage.Style; +import static com.google.javascript.jscomp.JsMessage.Style.CLOSURE; +import static com.google.javascript.jscomp.JsMessage.Style.LEGACY; +import static com.google.javascript.jscomp.JsMessage.Style.RELAX; +import static com.google.javascript.jscomp.JsMessageVisitor.isLowerCamelCaseWithNumericSuffixes; +import static com.google.javascript.jscomp.JsMessageVisitor.toLowerCamelCaseWithNumericSuffixes; +import com.google.javascript.rhino.Node; + +import junit.framework.TestCase; + +import java.util.List; + +/** + * Test for {@link JsMessageVisitor}. + * + * @author anatol@google.com (Anatol Pomazau) + */ +public class JsMessageVisitorTest extends TestCase { + + private Compiler compiler; + private List messages; + private boolean allowLegacyMessages; + + @Override + protected void setUp() throws Exception { + messages = Lists.newLinkedList(); + allowLegacyMessages = true; + } + + public void testJsMessageOnVar() { + extractMessagesSafely( + "/** @desc Hello */ var MSG_HELLO = goog.getMsg('a')"); + assertEquals(0, compiler.getWarningCount()); + assertEquals(1, messages.size()); + + JsMessage msg = messages.get(0); + assertEquals("MSG_HELLO", msg.getKey()); + assertEquals("Hello", msg.getDesc()); + } + + public void testJsMessageOnProperty() { + extractMessagesSafely("/** @desc a */ " + + "pint.sub.MSG_MENU_MARK_AS_UNREAD = goog.getMsg('a')"); + assertEquals(0, compiler.getWarningCount()); + assertEquals(1, messages.size()); + + JsMessage msg = messages.get(0); + assertEquals("MSG_MENU_MARK_AS_UNREAD", msg.getKey()); + assertEquals("a", msg.getDesc()); + } + + public void testOrphanedJsMessage() { + extractMessagesSafely("goog.getMsg('a')"); + assertEquals(1, compiler.getWarningCount()); + assertEquals(0, messages.size()); + + JSError warn = compiler.getWarnings()[0]; + assertEquals(JsMessageVisitor.MESSAGE_NODE_IS_ORPHANED, warn.getType()); + } + + public void testMessageWithoutDescription() { + extractMessagesSafely("var MSG_HELLO = goog.getMsg('a')"); + assertEquals(1, compiler.getWarningCount()); + assertEquals(1, messages.size()); + + JsMessage msg = messages.get(0); + assertEquals("MSG_HELLO", msg.getKey()); + + assertEquals(JsMessageVisitor.MESSAGE_HAS_NO_DESCRIPTION, + compiler.getWarnings()[0].getType()); + } + + public void testIncorrectMessageReporting() { + extractMessages("var MSG_HELLO = goog.getMsg('a' + + 'b')"); + assertEquals(1, compiler.getErrorCount()); + assertEquals(0, compiler.getWarningCount()); + assertEquals(0, messages.size()); + + JSError mailformedTreeError = compiler.getErrors()[0]; + assertEquals(JsMessageVisitor.MESSAGE_TREE_MALFORMED, + mailformedTreeError.getType()); + assertEquals("Message parse tree malformed. " + + "STRING or ADD node expected; found: POS", + mailformedTreeError.description); + } + + public void testEmptyMessage() { + // This is an edge case. Empty messages are useless, but shouldn't fail + extractMessagesSafely("var MSG_EMPTY = '';"); + + assertEquals(1, messages.size()); + JsMessage msg = messages.get(0); + assertEquals("MSG_EMPTY", msg.getKey()); + assertEquals("", msg.toString()); + } + + public void testConcatOfStrings() { + extractMessagesSafely("var MSG_NOTEMPTY = 'aa' + 'bbb' \n + ' ccc';"); + + assertEquals(1, messages.size()); + JsMessage msg = messages.get(0); + assertEquals("MSG_NOTEMPTY", msg.getKey()); + assertEquals("aabbb ccc", msg.toString()); + } + + public void testLegacyFormatDescription() { + extractMessagesSafely("var MSG_SILLY = 'silly test message';\n" + + "var MSG_SILLY_HELP = 'help text';"); + + assertEquals(1, messages.size()); + JsMessage msg = messages.get(0); + assertEquals("MSG_SILLY", msg.getKey()); + assertEquals("help text", msg.getDesc()); + assertEquals("silly test message", msg.toString()); + } + + public void testLegacyFormatParametizedFunction() { + extractMessagesSafely("var MSG_SILLY = function(one, two) {" + + " return one + ', ' + two + ', buckle my shoe';" + + "};"); + + assertEquals(1, messages.size()); + JsMessage msg = messages.get(0); + assertEquals("MSG_SILLY", msg.getKey()); + assertEquals(null, msg.getDesc()); + assertEquals("{$one}, {$two}, buckle my shoe", msg.toString()); + } + + public void testLegacyMessageWithDescAnnotation() { + // Well, is was better do not allow legacy messages with @desc annotations, + // but people love to mix styles so we need to check @desc also. + extractMessagesSafely( + "/** @desc The description */ var MSG_A = 'The Message';"); + + assertEquals(1, messages.size()); + assertEquals(1, compiler.getWarningCount()); + JsMessage msg = messages.get(0); + assertEquals("MSG_A", msg.getKey()); + assertEquals("The Message", msg.toString()); + assertEquals("The description", msg.getDesc()); + } + + public void testLegacyMessageWithDescAnnotationAndHelpVar() { + // Well, is was better do not allow legacy messages with @desc annotations, + // but people love to mix styles so we need to check @desc also. + extractMessagesSafely( + "var MSG_A_HELP = 'This is a help var';\n" + + "/** @desc The description in @desc*/ var MSG_A = 'The Message';"); + + assertEquals(1, messages.size()); + assertEquals(1, compiler.getWarningCount()); + JsMessage msg = messages.get(0); + assertEquals("MSG_A", msg.getKey()); + assertEquals("The Message", msg.toString()); + assertEquals("The description in @desc", msg.getDesc()); + } + + public void testClosureMessageWithHelpPostfix() { + extractMessagesSafely("/** @desc help text */\n" + + "var MSG_FOO_HELP = goog.getMsg('Help!');"); + + assertEquals(1, messages.size()); + JsMessage msg = messages.get(0); + assertEquals("MSG_FOO_HELP", msg.getKey()); + assertEquals("help text", msg.getDesc()); + assertEquals("Help!", msg.toString()); + } + + public void testClosureMessageWithoutGoogGetmsg() { + allowLegacyMessages = false; + + extractMessages("var MSG_FOO_HELP = 'I am a bad message';"); + + assertEquals(1, messages.size()); + assertEquals(1, compiler.getErrors().length); + JSError error = compiler.getErrors()[0]; + assertEquals(JsMessageVisitor.MESSAGE_NOT_INITIALIZED_USING_NEW_SYNTAX, + error.getType()); + } + + public void testClosureFormatParametizedFunction() { + extractMessagesSafely("/** @desc help text */" + + "var MSG_SILLY = goog.getMsg('{$adjective} ' + 'message', " + + "{'adjective': 'silly'});"); + + assertEquals(1, messages.size()); + JsMessage msg = messages.get(0); + assertEquals("MSG_SILLY", msg.getKey()); + assertEquals("help text", msg.getDesc()); + assertEquals("{$adjective} message", msg.toString()); + } + + public void testHugeMessage() { + extractMessagesSafely("/**" + + " * @desc A message with lots of stuff.\n" + + " * @hidden\n" + + " */" + + "var MSG_HUGE = goog.getMsg(" + + " '{$startLink_1}Google{$endLink}' +" + + " '{$startLink_2}blah{$endLink}{$boo}{$foo_001}{$boo}' +" + + " '{$foo_002}{$xxx_001}{$image}{$image_001}{$xxx_002}'," + + " {'startLink_1': ''," + + " 'endLink': ''," + + " 'startLink_2': ''," + + " 'boo': opt_data.boo," + + " 'foo_001': opt_data.foo," + + " 'foo_002': opt_data.boo.foo," + + " 'xxx_001': opt_data.boo + opt_data.foo," + + " 'image': htmlTag7," + + " 'image_001': opt_data.image," + + " 'xxx_002': foo.callWithOnlyTopLevelKeys(" + + " bogusFn, opt_data, null, 'bogusKey1'," + + " opt_data.moo, 'bogusKey2', param10)});"); + + assertEquals(1, messages.size()); + JsMessage msg = messages.get(0); + assertEquals("MSG_HUGE", msg.getKey()); + assertEquals("A message with lots of stuff.", msg.getDesc()); + assertTrue(msg.isHidden()); + assertEquals("{$startLink_1}Google{$endLink}{$startLink_2}blah{$endLink}" + + "{$boo}{$foo_001}{$boo}{$foo_002}{$xxx_001}{$image}" + + "{$image_001}{$xxx_002}", msg.toString()); + } + + public void testUnnamedGoogleMessage() { + extractMessagesSafely("var MSG_UNNAMED_2 = goog.getMsg('Hullo');"); + + assertEquals(1, messages.size()); + JsMessage msg = messages.get(0); + assertEquals(null, msg.getDesc()); + assertEquals("MSG_16LJMYKCXT84X", msg.getKey()); + assertEquals("MSG_16LJMYKCXT84X", msg.getId()); + } + + public void testEmptyTextMessage() { + extractMessagesSafely("/** @desc text */ var MSG_FOO = goog.getMsg('');"); + + assertEquals(1, messages.size()); + assertEquals(1, compiler.getWarningCount()); + assertEquals("Message value of MSG_FOO is just an empty string. " + + "Empty messages are forbidden.", + compiler.getWarnings()[0].description); + } + + public void testEmptyTextComplexMessage() { + extractMessagesSafely("/** @desc text */ var MSG_BAR = goog.getMsg(" + + "'' + '' + '' + ''\n+'');"); + + assertEquals(1, messages.size()); + assertEquals(1, compiler.getWarningCount()); + assertEquals("Message value of MSG_BAR is just an empty string. " + + "Empty messages are forbidden.", + compiler.getWarnings()[0].description); + } + + public void testMessageIsNoUnnamed() { + extractMessagesSafely("var MSG_UNNAMED_ITEM = goog.getMsg('Hullo');"); + + assertEquals(1, messages.size()); + JsMessage msg = messages.get(0); + assertEquals("MSG_UNNAMED_ITEM", msg.getKey()); + assertFalse(msg.isHidden()); + } + + public void testMsgVarWithoutAssignment() { + extractMessages("var MSG_SILLY;"); + + assertEquals(1, compiler.getErrors().length); + JSError error = compiler.getErrors()[0]; + assertEquals(JsMessageVisitor.MESSAGE_HAS_NO_VALUE, error.getType()); + } + + public void testRegularVarWithoutAssignment() { + extractMessagesSafely("var SILLY;"); + + assertTrue(messages.isEmpty()); + } + + public void itIsNotImplementedYet_testMsgPropertyWithoutAssignment() { + extractMessages("goog.message.MSG_SILLY_PROP;"); + + assertEquals(1, compiler.getErrors().length); + JSError error = compiler.getErrors()[0]; + assertEquals("Message MSG_SILLY_PROP has no value", error.description); + } + + public void testMsgVarWithIncorrectRightSide() { + extractMessages("var MSG_SILLY = 0;"); + + assertEquals(1, compiler.getErrors().length); + JSError error = compiler.getErrors()[0]; + assertEquals("Message parse tree malformed. Cannot parse value of " + + "message MSG_SILLY", error.description); + } + + public void testIncorrectMessage() { + extractMessages("DP_DatePicker.MSG_DATE_SELECTION = {};"); + + assertEquals(0, messages.size()); + assertEquals(1, compiler.getErrors().length); + JSError error = compiler.getErrors()[0]; + assertEquals("Message parse tree malformed. "+ + "Message must be initialized using goog.getMsg function.", + error.description); + } + + public void testUnrecognizedFunction() { + allowLegacyMessages = false; + extractMessages("DP_DatePicker.MSG_DATE_SELECTION = somefunc('a')"); + + assertEquals(0, messages.size()); + assertEquals(1, compiler.getErrors().length); + JSError error = compiler.getErrors()[0]; + assertEquals("Message parse tree malformed. "+ + "Message initialized using unrecognized function. " + + "Please use goog.getMsg() instead.", + error.description); + } + + public void testExtractPropertyMessage() { + extractMessagesSafely("/**" + + " * @desc A message that demonstrates placeholders\n" + + " * @hidden\n" + + " */" + + "a.b.MSG_SILLY = goog.getMsg(\n" + + " '{$adjective} ' + '{$someNoun}',\n" + + " {'adjective': adj, 'someNoun': noun});"); + + assertEquals(1, messages.size()); + JsMessage msg = messages.get(0); + assertEquals("MSG_SILLY", msg.getKey()); + assertEquals("{$adjective} {$someNoun}", msg.toString()); + assertEquals("A message that demonstrates placeholders", msg.getDesc()); + assertTrue(msg.isHidden()); + } + + public void testAlmostButNotExternalMessage() { + extractMessagesSafely( + "/** @desc External */ var MSG_EXTERNAL = goog.getMsg('External');"); + assertEquals(0, compiler.getWarningCount()); + assertEquals(1, messages.size()); + assertFalse(messages.get(0).isExternal()); + assertEquals("MSG_EXTERNAL", messages.get(0).getKey()); + } + + public void testExternalMessage() { + extractMessagesSafely("var MSG_EXTERNAL_111 = goog.getMsg('Hello World');"); + assertEquals(0, compiler.getWarningCount()); + assertEquals(1, messages.size()); + assertTrue(messages.get(0).isExternal()); + assertEquals("111", messages.get(0).getId()); + } + + public void testIsValidMessageNameStrict() { + JsMessageVisitor visitor = new DummyJsVisitor(CLOSURE); + + assertTrue(visitor.isMessageName("MSG_HELLO", true)); + assertTrue(visitor.isMessageName("MSG_", true)); + assertTrue(visitor.isMessageName("MSG_HELP", true)); + assertTrue(visitor.isMessageName("MSG_FOO_HELP", true)); + + assertFalse(visitor.isMessageName("_FOO_HELP", true)); + assertFalse(visitor.isMessageName("MSGFOOP", true)); + } + + public void testIsValidMessageNameRelax() { + JsMessageVisitor visitor = new DummyJsVisitor(RELAX); + + assertFalse(visitor.isMessageName("MSG_HELP", false)); + assertFalse(visitor.isMessageName("MSG_FOO_HELP", false)); + } + + public void testIsValidMessageNameLegacy() { + theseAreLegacyMessageNames(new DummyJsVisitor(RELAX)); + theseAreLegacyMessageNames(new DummyJsVisitor(LEGACY)); + } + + private void theseAreLegacyMessageNames(JsMessageVisitor visitor) { + assertTrue(visitor.isMessageName("MSG_HELLO", false)); + assertTrue(visitor.isMessageName("MSG_", false)); + + assertFalse(visitor.isMessageName("MSG_HELP", false)); + assertFalse(visitor.isMessageName("MSG_FOO_HELP", false)); + assertFalse(visitor.isMessageName("_FOO_HELP", false)); + assertFalse(visitor.isMessageName("MSGFOOP", false)); + } + + public void testUnexistedPlaceholders() { + extractMessages("var MSG_FOO = goog.getMsg('{$foo}:', {});"); + + assertEquals(0, messages.size()); + JSError[] errors = compiler.getErrors(); + assertEquals(1, errors.length); + JSError error = errors[0]; + assertEquals(JsMessageVisitor.MESSAGE_TREE_MALFORMED, error.getType()); + assertEquals("Message parse tree malformed. Unrecognized message " + + "placeholder referenced: foo", error.description); + } + + public void testUnusedReferenesAreNotOK() { + extractMessages("/** @desc AA */ " + + "var MSG_FOO = goog.getMsg('lalala:', {foo:1});"); + assertEquals(0, messages.size()); + JSError[] errors = compiler.getErrors(); + assertEquals(1, errors.length); + JSError error = errors[0]; + assertEquals(JsMessageVisitor.MESSAGE_TREE_MALFORMED, error.getType()); + assertEquals("Message parse tree malformed. Unused message placeholder: " + + "foo", error.description); + } + + public void testDuplicatePlaceHoldersAreBad() { + extractMessages("var MSG_FOO = goog.getMsg(" + + "'{$foo}:', {'foo': 1, 'foo' : 2});"); + + assertEquals(0, messages.size()); + JSError[] errors = compiler.getErrors(); + assertEquals(1, errors.length); + JSError error = errors[0]; + assertEquals(JsMessageVisitor.MESSAGE_TREE_MALFORMED, error.getType()); + assertEquals("Message parse tree malformed. Duplicate placeholder " + + "name: foo", error.description); + } + + public void testDuplicatePlaceholderReferencesAreOk() { + extractMessagesSafely("var MSG_FOO = goog.getMsg(" + + "'{$foo}:, {$foo}', {'foo': 1});"); + + assertEquals(1, messages.size()); + JsMessage msg = messages.get(0); + assertEquals("{$foo}:, {$foo}", msg.toString()); + } + + public void testCamelcasePlaceholderNamesAreOk() { + extractMessagesSafely("var MSG_WITH_CAMELCASE = goog.getMsg(" + + "'Slide {$slideNumber}:', {'slideNumber': opt_index + 1});"); + + assertEquals(1, messages.size()); + JsMessage msg = messages.get(0); + assertEquals("MSG_WITH_CAMELCASE", msg.getKey()); + assertEquals("Slide {$slideNumber}:", msg.toString()); + List parts = msg.parts(); + assertEquals(3, parts.size()); + assertEquals("slideNumber", + ((JsMessage.PlaceholderReference)parts.get(1)).getName()); + } + + public void testWithNonCamelcasePlaceholderNamesAreNotOk() { + extractMessages("var MSG_WITH_CAMELCASE = goog.getMsg(" + + "'Slide {$slide_number}:', {'slide_number': opt_index + 1});"); + + assertEquals(0, messages.size()); + JSError[] errors = compiler.getErrors(); + assertEquals(1, errors.length); + JSError error = errors[0]; + assertEquals(JsMessageVisitor.MESSAGE_TREE_MALFORMED, error.getType()); + assertEquals("Message parse tree malformed. Placeholder name not in " + + "lowerCamelCase: slide_number", error.description); + } + + public void testUnquotedPlaceholdersAreOk() { + extractMessagesSafely("/** @desc Hello */ " + + "var MSG_FOO = goog.getMsg('foo {$unquoted}:', {unquoted: 12});"); + + assertEquals(1, messages.size()); + assertEquals(0, compiler.getWarningCount()); + } + + public void testIsLowerCamelCaseWithNumericSuffixes() { + assertTrue(isLowerCamelCaseWithNumericSuffixes("name")); + assertFalse(isLowerCamelCaseWithNumericSuffixes("NAME")); + assertFalse(isLowerCamelCaseWithNumericSuffixes("Name")); + + assertTrue(isLowerCamelCaseWithNumericSuffixes("a4Letter")); + assertFalse(isLowerCamelCaseWithNumericSuffixes("A4_LETTER")); + + assertTrue(isLowerCamelCaseWithNumericSuffixes("startSpan_1_23")); + assertFalse(isLowerCamelCaseWithNumericSuffixes("startSpan_1_23b")); + assertFalse(isLowerCamelCaseWithNumericSuffixes("START_SPAN_1_23")); + + assertFalse(isLowerCamelCaseWithNumericSuffixes("")); + } + + public void testToLowerCamelCaseWithNumericSuffixes() { + assertEquals("name", toLowerCamelCaseWithNumericSuffixes("NAME")); + assertEquals("a4Letter", toLowerCamelCaseWithNumericSuffixes("A4_LETTER")); + assertEquals("startSpan_1_23", + toLowerCamelCaseWithNumericSuffixes("START_SPAN_1_23")); + } + + public void testDuplicateMessageError() { + extractMessages( + "(function () {/** @desc Hello */ var MSG_HELLO = goog.getMsg('a')})" + + "(function () {/** @desc Hello2 */ var MSG_HELLO = goog.getMsg('a')})"); + + assertEquals(0, compiler.getWarningCount()); + assertOneError(JsMessageVisitor.MESSAGE_DUPLICATE_KEY); + } + + public void testNoDuplicateErrorOnExternMessage() { + extractMessagesSafely( + "(function () {/** @desc Hello */ " + + "var MSG_EXTERNAL_2 = goog.getMsg('a')})" + + "(function () {/** @desc Hello2 */ " + + "var MSG_EXTERNAL_2 = goog.getMsg('a')})"); + } + + public void testErrorWhenUsingMsgPrefixWithFallback() { + extractMessages( + "/** @desc Hello */ var MSG_HELLO_1 = goog.getMsg('hello');\n" + + "/** @desc Hello */ var MSG_HELLO_2 = goog.getMsg('hello');\n" + + "/** @desc Hello */ " + + "var MSG_HELLO_3 = goog.getMsgWithFallback(MSG_HELLO_1, MSG_HELLO_2);"); + assertOneError(JsMessageVisitor.MESSAGE_TREE_MALFORMED); + } + + private void assertOneError(DiagnosticType type) { + String errors = Joiner.on("\n").join(compiler.getErrors()); + assertEquals("There should be one error. " + errors, + 1, compiler.getErrorCount()); + JSError error = compiler.getErrors()[0]; + assertEquals(type, error.getType()); + } + + private void extractMessagesSafely(String input) { + extractMessages(input); + JSError[] errors = compiler.getErrors(); + assertEquals( + "Unexpected error(s): " + Joiner.on("\n").join(compiler.getErrors()), + 0, compiler.getErrorCount()); + } + + private void extractMessages(String input) { + compiler = new Compiler(); + Node root = compiler.parseTestCode(input); + JsMessageVisitor visitor = new CollectMessages(compiler); + visitor.process(null, root); + } + + private class CollectMessages extends JsMessageVisitor { + + private CollectMessages(Compiler compiler) { + super(compiler, true, Style.getFromParams(true, allowLegacyMessages), + null); + } + + @Override + protected void processJsMessage(JsMessage message, + JsMessageDefinition definition) { + messages.add(message); + } + } + + private class DummyJsVisitor extends JsMessageVisitor { + + private DummyJsVisitor(Style style) { + super(null, true, style, null); + } + + @Override + protected void processJsMessage(JsMessage message, + JsMessageDefinition definition) { + // no-op + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/LightweightMessageFormatterTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/LightweightMessageFormatterTest.java new file mode 100644 index 0000000..51e20ab --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/LightweightMessageFormatterTest.java @@ -0,0 +1,130 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import static com.google.javascript.jscomp.LightweightMessageFormatter.LineNumberingFormatter; + +import com.google.javascript.rhino.Node; + +import junit.framework.TestCase; + +public class LightweightMessageFormatterTest extends TestCase { + private static final DiagnosticType FOO_TYPE = + DiagnosticType.error("TEST_FOO", "error description here"); + + public void testNull() throws Exception { + assertNull(format(null)); + } + + public void testOneLineRegion() throws Exception { + assertEquals(" 5| hello world", format(region(5, 5, "hello world"))); + } + + public void testTwoLineRegion() throws Exception { + assertEquals(" 5| hello world\n" + + " 6| foo bar", format(region(5, 6, "hello world\nfoo bar"))); + } + + public void testThreeLineRegionAcrossNumberRange() throws Exception { + String region = format(region(9, 11, "hello world\nfoo bar\nanother one")); + assertEquals(" 9| hello world\n" + + " 10| foo bar\n" + + " 11| another one", region); + } + + public void testThreeLineRegionEmptyLine() throws Exception { + String region = format(region(7, 9, "hello world\n\nanother one")); + assertEquals(" 7| hello world\n" + + " 8| \n" + + " 9| another one", region); + } + + public void testOnlyOneEmptyLine() throws Exception { + assertNull(format(region(7, 7, ""))); + } + + public void testTwoEmptyLines() throws Exception { + assertEquals(" 7| ", format(region(7, 8, "\n"))); + } + + public void testThreeLineRemoveLastEmptyLine() throws Exception { + String region = format(region(7, 9, "hello world\nfoobar\n")); + assertEquals(" 7| hello world\n" + + " 8| foobar", region); + } + + public void testFormatErrorSpaces() throws Exception { + JSError error = JSError.make("javascript/complex.js", + Node.newString("foobar", 5, 8), FOO_TYPE); + LightweightMessageFormatter formatter = formatter(" if (foobar) {"); + assertEquals("javascript/complex.js:5: ERROR - error description here\n" + + " if (foobar) {\n" + + " ^\n", formatter.formatError(error)); + } + + public void testFormatErrorTabs() throws Exception { + JSError error = JSError.make("javascript/complex.js", + Node.newString("foobar", 5, 6), FOO_TYPE); + LightweightMessageFormatter formatter = formatter("\t\tif (foobar) {"); + assertEquals("javascript/complex.js:5: ERROR - error description here\n" + + "\t\tif (foobar) {\n" + + "\t\t ^\n", formatter.formatError(error)); + } + + public void testFormatErrorSpaceEndOfLine1() throws Exception { + JSError error = JSError.make("javascript/complex.js", + 1, 10, FOO_TYPE); + LightweightMessageFormatter formatter = formatter("assert (1;"); + assertEquals("javascript/complex.js:1: ERROR - error description here\n" + + "assert (1;\n" + + " ^\n", formatter.formatError(error)); + } + + public void testFormatErrorSpaceEndOfLine2() throws Exception { + JSError error = JSError.make("javascript/complex.js", + 6, 7, FOO_TYPE); + LightweightMessageFormatter formatter = formatter("if (foo"); + assertEquals("javascript/complex.js:6: ERROR - error description here\n" + + "if (foo\n" + + " ^\n", formatter.formatError(error)); + } + + private LightweightMessageFormatter formatter(String string) { + return new LightweightMessageFormatter(source(string)); + } + + private SourceExcerptProvider source(final String source) { + return new SourceExcerptProvider() { + @Override + public String getSourceLine(String sourceName, int lineNumber) { + return source; + } + @Override + public Region getSourceRegion(String sourceName, int lineNumber) { + throw new UnsupportedOperationException(); + } + }; + } + + private String format(Region region) { + return new LineNumberingFormatter().formatRegion(region); + } + + private Region region(final int startLine, final int endLine, + final String source) { + return new SimpleRegion(startLine, endLine, source); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/LinkedFlowScopeTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/LinkedFlowScopeTest.java new file mode 100644 index 0000000..07a903a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/LinkedFlowScopeTest.java @@ -0,0 +1,324 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.type.FlowScope; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.JSType; + +/** + * Tests for LinkedFlowScope. + * @author nicksantos@google.com (Nick Santos) + */ +public class LinkedFlowScopeTest extends CompilerTypeTestCase { + + private final Node blockNode = new Node(Token.BLOCK); + private final Node functionNode = new Node(Token.FUNCTION); + private final int LONG_CHAIN_LENGTH = 1050; + + private Scope globalScope; + private Scope localScope; + @SuppressWarnings("unused") + private FlowScope globalEntry; + private FlowScope localEntry; + + @Override + public void setUp() throws Exception { + super.setUp(); + + globalScope = Scope.createGlobalScope(blockNode); + globalScope.declare("globalA", null, null, null); + globalScope.declare("globalB", null, null, null); + + localScope = new Scope(globalScope, functionNode); + localScope.declare("localA", null, null, null); + localScope.declare("localB", null, null, null); + + globalEntry = LinkedFlowScope.createEntryLattice(globalScope); + localEntry = LinkedFlowScope.createEntryLattice(localScope); + } + + public void testOptimize() { + assertEquals(localEntry, localEntry.optimize()); + + FlowScope child = localEntry.createChildFlowScope(); + assertEquals(localEntry, child.optimize()); + + child.inferSlotType("localB", NUMBER_TYPE); + assertEquals(child, child.optimize()); + } + + public void testJoin1() { + FlowScope childA = localEntry.createChildFlowScope(); + childA.inferSlotType("localB", NUMBER_TYPE); + + FlowScope childAB = childA.createChildFlowScope(); + childAB.inferSlotType("localB", STRING_TYPE); + + FlowScope childB = localEntry.createChildFlowScope(); + childB.inferSlotType("localB", BOOLEAN_TYPE); + + assertTypeEquals(STRING_TYPE, childAB.getSlot("localB").getType()); + assertTypeEquals(BOOLEAN_TYPE, childB.getSlot("localB").getType()); + assertNull(childB.getSlot("localA").getType()); + + FlowScope joined = join(childB, childAB); + assertTypeEquals(createUnionType(STRING_TYPE, BOOLEAN_TYPE), + joined.getSlot("localB").getType()); + assertNull(joined.getSlot("localA").getType()); + + joined = join(childAB, childB); + assertTypeEquals(createUnionType(STRING_TYPE, BOOLEAN_TYPE), + joined.getSlot("localB").getType()); + assertNull(joined.getSlot("localA").getType()); + + assertEquals("Join should be symmetric", + join(childB, childAB), join(childAB, childB)); + } + + public void testJoin2() { + FlowScope childA = localEntry.createChildFlowScope(); + childA.inferSlotType("localA", STRING_TYPE); + + FlowScope childB = localEntry.createChildFlowScope(); + childB.inferSlotType("globalB", BOOLEAN_TYPE); + + assertTypeEquals(STRING_TYPE, childA.getSlot("localA").getType()); + assertTypeEquals(BOOLEAN_TYPE, childB.getSlot("globalB").getType()); + assertNull(childB.getSlot("localB").getType()); + + FlowScope joined = join(childB, childA); + assertTypeEquals(STRING_TYPE, joined.getSlot("localA").getType()); + assertTypeEquals(BOOLEAN_TYPE, joined.getSlot("globalB").getType()); + + joined = join(childA, childB); + assertTypeEquals(STRING_TYPE, joined.getSlot("localA").getType()); + assertTypeEquals(BOOLEAN_TYPE, joined.getSlot("globalB").getType()); + + assertEquals("Join should be symmetric", + join(childB, childA), join(childA, childB)); + } + + public void testJoin3() { + localScope.declare("localC", null, STRING_TYPE, null); + localScope.declare("localD", null, STRING_TYPE, null); + + FlowScope childA = localEntry.createChildFlowScope(); + childA.inferSlotType("localC", NUMBER_TYPE); + + FlowScope childB = localEntry.createChildFlowScope(); + childA.inferSlotType("localD", BOOLEAN_TYPE); + + FlowScope joined = join(childB, childA); + assertTypeEquals(createUnionType(STRING_TYPE, NUMBER_TYPE), + joined.getSlot("localC").getType()); + assertTypeEquals(createUnionType(STRING_TYPE, BOOLEAN_TYPE), + joined.getSlot("localD").getType()); + + joined = join(childA, childB); + assertTypeEquals(createUnionType(STRING_TYPE, NUMBER_TYPE), + joined.getSlot("localC").getType()); + assertTypeEquals(createUnionType(STRING_TYPE, BOOLEAN_TYPE), + joined.getSlot("localD").getType()); + + assertEquals("Join should be symmetric", + join(childB, childA), join(childA, childB)); + } + + /** + * Create a long chain of flow scopes where each link in the chain + * contains one slot. + */ + public void testLongChain1() { + FlowScope chainA = localEntry.createChildFlowScope(); + FlowScope chainB = localEntry.createChildFlowScope(); + for (int i = 0; i < LONG_CHAIN_LENGTH; i++) { + localScope.declare("local" + i, null, null, null); + chainA.inferSlotType("local" + i, + i % 2 == 0 ? NUMBER_TYPE : BOOLEAN_TYPE); + chainB.inferSlotType("local" + i, + i % 3 == 0 ? STRING_TYPE : BOOLEAN_TYPE); + + chainA = chainA.createChildFlowScope(); + chainB = chainB.createChildFlowScope(); + } + + verifyLongChains(chainA, chainB); + } + + /** + * Create a long chain of flow scopes where each link in the chain + * contains 7 slots. + */ + public void testLongChain2() { + FlowScope chainA = localEntry.createChildFlowScope(); + FlowScope chainB = localEntry.createChildFlowScope(); + for (int i = 0; i < LONG_CHAIN_LENGTH * 7; i++) { + localScope.declare("local" + i, null, null, null); + chainA.inferSlotType("local" + i, + i % 2 == 0 ? NUMBER_TYPE : BOOLEAN_TYPE); + chainB.inferSlotType("local" + i, + i % 3 == 0 ? STRING_TYPE : BOOLEAN_TYPE); + + if (i % 7 == 0) { + chainA = chainA.createChildFlowScope(); + chainB = chainB.createChildFlowScope(); + } + } + + verifyLongChains(chainA, chainB); + } + + /** + * Create a long chain of flow scopes where every 4 links in the chain + * contain a slot. + */ + public void testLongChain3() { + FlowScope chainA = localEntry.createChildFlowScope(); + FlowScope chainB = localEntry.createChildFlowScope(); + for (int i = 0; i < LONG_CHAIN_LENGTH * 7; i++) { + if (i % 7 == 0) { + int j = i / 7; + localScope.declare("local" + j, null, null, null); + chainA.inferSlotType("local" + j, + j % 2 == 0 ? NUMBER_TYPE : BOOLEAN_TYPE); + chainB.inferSlotType("local" + j, + j % 3 == 0 ? STRING_TYPE : BOOLEAN_TYPE); + } + + chainA = chainA.createChildFlowScope(); + chainB = chainB.createChildFlowScope(); + } + + verifyLongChains(chainA, chainB); + } + + // Common chain verification for testLongChainN for all N. + private void verifyLongChains(FlowScope chainA, FlowScope chainB) { + FlowScope joined = join(chainA, chainB); + for (int i = 0; i < LONG_CHAIN_LENGTH; i++) { + assertTypeEquals( + i % 2 == 0 ? NUMBER_TYPE : BOOLEAN_TYPE, + chainA.getSlot("local" + i).getType()); + assertTypeEquals( + i % 3 == 0 ? STRING_TYPE : BOOLEAN_TYPE, + chainB.getSlot("local" + i).getType()); + + JSType joinedSlotType = joined.getSlot("local" + i).getType(); + if (i % 6 == 0) { + assertTypeEquals(createUnionType(STRING_TYPE, NUMBER_TYPE), joinedSlotType); + } else if (i % 2 == 0) { + assertTypeEquals(createUnionType(NUMBER_TYPE, BOOLEAN_TYPE), + joinedSlotType); + } else if (i % 3 == 0) { + assertTypeEquals(createUnionType(STRING_TYPE, BOOLEAN_TYPE), + joinedSlotType); + } else { + assertTypeEquals(BOOLEAN_TYPE, joinedSlotType); + } + } + + assertScopesDiffer(chainA, chainB); + assertScopesDiffer(chainA, joined); + assertScopesDiffer(chainB, joined); + } + + public void testFindUniqueSlot() { + FlowScope childA = localEntry.createChildFlowScope(); + childA.inferSlotType("localB", NUMBER_TYPE); + + FlowScope childAB = childA.createChildFlowScope(); + childAB.inferSlotType("localB", STRING_TYPE); + + FlowScope childABC = childAB.createChildFlowScope(); + childABC.inferSlotType("localA", BOOLEAN_TYPE); + + assertNull(childABC.findUniqueRefinedSlot(childABC)); + assertTypeEquals(BOOLEAN_TYPE, + childABC.findUniqueRefinedSlot(childAB).getType()); + assertNull(childABC.findUniqueRefinedSlot(childA)); + assertNull(childABC.findUniqueRefinedSlot(localEntry)); + + assertTypeEquals(STRING_TYPE, + childAB.findUniqueRefinedSlot(childA).getType()); + assertTypeEquals(STRING_TYPE, + childAB.findUniqueRefinedSlot(localEntry).getType()); + + assertTypeEquals(NUMBER_TYPE, + childA.findUniqueRefinedSlot(localEntry).getType()); + } + + public void testDiffer1() { + FlowScope childA = localEntry.createChildFlowScope(); + childA.inferSlotType("localB", NUMBER_TYPE); + + FlowScope childAB = childA.createChildFlowScope(); + childAB.inferSlotType("localB", STRING_TYPE); + + FlowScope childABC = childAB.createChildFlowScope(); + childABC.inferSlotType("localA", BOOLEAN_TYPE); + + FlowScope childB = childAB.createChildFlowScope(); + childB.inferSlotType("localB", STRING_TYPE); + + FlowScope childBC = childB.createChildFlowScope(); + childBC.inferSlotType("localA", NO_TYPE); + + assertScopesSame(childAB, childB); + assertScopesDiffer(childABC, childBC); + + assertScopesDiffer(childABC, childB); + assertScopesDiffer(childAB, childBC); + + assertScopesDiffer(childA, childAB); + assertScopesDiffer(childA, childABC); + assertScopesDiffer(childA, childB); + assertScopesDiffer(childA, childBC); + } + + public void testDiffer2() { + FlowScope childA = localEntry.createChildFlowScope(); + childA.inferSlotType("localA", NUMBER_TYPE); + + FlowScope childB = localEntry.createChildFlowScope(); + childB.inferSlotType("localA", NO_TYPE); + + assertScopesDiffer(childA, childB); + } + + private void assertScopesDiffer(FlowScope a, FlowScope b) { + assertFalse(a.equals(b)); + assertFalse(b.equals(a)); + assertEquals(a, a); + assertEquals(b, b); + } + + private void assertScopesSame(FlowScope a, FlowScope b) { + assertEquals(a, b); + assertEquals(b, a); + assertEquals(a, a); + assertEquals(b, b); + } + + @SuppressWarnings("unchecked") + private FlowScope join(FlowScope a, FlowScope b) { + return (new LinkedFlowScope.FlowScopeJoinOp()).apply( + Lists.newArrayList(a, b)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/LiveVariableAnalysisTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/LiveVariableAnalysisTest.java new file mode 100644 index 0000000..035fab7 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/LiveVariableAnalysisTest.java @@ -0,0 +1,377 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.jscomp.DataFlowAnalysis.FlowState; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import junit.framework.TestCase; + +/** + * Tests for {@link LiveVariablesAnalysis}. Test cases are snippets of a + * function and assertions are made at the instruction labeled with {@code X}. + * + */ +public class LiveVariableAnalysisTest extends TestCase { + + private LiveVariablesAnalysis liveness = null; + + public void testStraightLine() { + // A sample of simple straight line of code with different liveness changes. + assertNotLiveBeforeX("X:var a;", "a"); + assertNotLiveAfterX("X:var a;", "a"); + assertNotLiveAfterX("X:var a=1;", "a"); + assertLiveAfterX("X:var a=1; a()", "a"); + assertNotLiveBeforeX("X:var a=1; a()", "a"); + assertLiveBeforeX("var a;X:a;", "a"); + assertLiveBeforeX("var a;X:a=a+1;", "a"); + assertLiveBeforeX("var a;X:a+=1;", "a"); + assertLiveBeforeX("var a;X:a++;", "a"); + assertNotLiveAfterX("var a,b;X:b();", "a"); + assertNotLiveBeforeX("var a,b;X:b();", "a"); + assertLiveBeforeX("var a,b;X:b(a);", "a"); + assertLiveBeforeX("var a,b;X:b(1,2,3,b(a + 1));", "a"); + assertNotLiveBeforeX("var a,b;X:a=1;b(a)", "a"); + assertNotLiveAfterX("var a,b;X:b(a);b()", "a"); + assertLiveBeforeX("var a,b;X:b();b=1;a()", "b"); + assertLiveAfterX("X:a();var a;a()", "a"); + assertNotLiveAfterX("X:a();var a=1;a()", "a"); + assertLiveBeforeX("var a,b;X:a,b=1", "a"); + } + + public void testProperties() { + // Reading property of a local variable makes that variable live. + assertLiveBeforeX("var a,b;X:a.P;", "a"); + + // Assigning to a property doesn't kill "a". It makes it live instead. + assertLiveBeforeX("var a,b;X:a.P=1;b()", "a"); + assertLiveBeforeX("var a,b;X:a.P.Q=1;b()", "a"); + + // An "a" in a different context. + assertNotLiveAfterX("var a,b;X:b.P.Q.a=1;", "a"); + + assertLiveBeforeX("var a,b;X:b.P.Q=a;", "a"); + } + + public void testConditions() { + // Reading the condition makes the variable live. + assertLiveBeforeX("var a,b;X:if(a){}", "a"); + assertLiveBeforeX("var a,b;X:if(a||b) {}", "a"); + assertLiveBeforeX("var a,b;X:if(b||a) {}", "a"); + assertLiveBeforeX("var a,b;X:if(b||b(a)) {}", "a"); + assertNotLiveAfterX("var a,b;X:b();if(a) {}", "b"); + + // We can kill within a condition as well. + assertNotLiveAfterX("var a,b;X:a();if(a=b){}a()", "a"); + assertNotLiveAfterX("var a,b;X:a();while(a=b){}a()", "a"); + + // The kill can be "conditional" due to short circuit. + assertNotLiveAfterX("var a,b;X:a();if((a=b)&&b){}a()", "a"); + assertNotLiveAfterX("var a,b;X:a();while((a=b)&&b){}a()", "a"); + assertLiveBeforeX("var a,b;a();X:if(b&&(a=b)){}a()", "a"); // Assumed live. + assertLiveBeforeX("var a,b;a();X:if(a&&(a=b)){}a()", "a"); + assertLiveBeforeX("var a,b;a();X:while(b&&(a=b)){}a()", "a"); + assertLiveBeforeX("var a,b;a();X:while(a&&(a=b)){}a()", "a"); + } + + public void testArrays() { + assertLiveBeforeX("var a;X:a[1]", "a"); + assertLiveBeforeX("var a,b;X:b[a]", "a"); + assertLiveBeforeX("var a,b;X:b[1,2,3,4,b(a)]", "a"); + assertLiveBeforeX("var a,b;X:b=[a,'a']", "a"); + assertNotLiveBeforeX("var a,b;X:a=[];b(a)", "a"); + + // Element assignment doesn't kill the array. + assertLiveBeforeX("var a;X:a[1]=1", "a"); + } + + public void testTwoPaths() { + // Both Paths. + assertLiveBeforeX("var a,b;X:if(b){b(a)}else{b(a)};", "a"); + + // Only one path. + assertLiveBeforeX("var a,b;X:if(b){b(b)}else{b(a)};", "a"); + assertLiveBeforeX("var a,b;X:if(b){b(a)}else{b(b)};", "a"); + + // None of the paths. + assertNotLiveAfterX("var a,b;X:if(b){b(b)}else{b(b)};", "a"); + + // At the very end. + assertLiveBeforeX("var a,b;X:if(b){b(b)}else{b(b)}a();", "a"); + + // The loop might or might not be executed. + assertLiveBeforeX("var a;X:while(param1){a()};", "a"); + assertLiveBeforeX("var a;X:while(param1){a=1};a()", "a"); + + // Same idea with if. + assertLiveBeforeX("var a;X:if(param1){a()};", "a"); + assertLiveBeforeX("var a;X:if(param1){a=1};a()", "a"); + + // This is different in DO. We know for sure at least one iteration is + // executed. + assertNotLiveAfterX("X:var a;do{a=1}while(param1);a()", "a"); + } + + public void testThreePaths() { + assertLiveBeforeX("var a;X:if(1){}else if(2){}else{a()};", "a"); + assertLiveBeforeX("var a;X:if(1){}else if(2){a()}else{};", "a"); + assertLiveBeforeX("var a;X:if(1){a()}else if(2){}else{};", "a"); + assertLiveBeforeX("var a;X:if(1){}else if(2){}else{};a()", "a"); + } + + public void testHooks() { + assertLiveBeforeX("var a;X:1?a=1:1;a()", "a"); + + // Unfortunately, we cannot prove the following because we assume there is + // no control flow within a hook (i.e. no joins / set unions). + // assertNotLiveAfterX("var a;X:1?a=1:a=2;a", "a"); + assertLiveBeforeX("var a,b;X:b=1?a:2", "a"); + } + + public void testForLoops() { + // Induction variable should not be live after the loop. + assertNotLiveBeforeX("var a,b;for(a=0;a<9;a++){b(a)};X:b", "a"); + assertNotLiveBeforeX("var a,b;for(a in b){a()};X:b", "a"); + assertNotLiveBeforeX("var a,b;for(a in b){a()};X:a", "b"); + assertLiveBeforeX("var b;for(var a in b){X:a()};", "a"); + + // It should be live within the loop even if it is not used. + assertLiveBeforeX("var a,b;for(a=0;a<9;a++){X:1}", "a"); + assertLiveAfterX("var a,b;for(a in b){X:b};", "a"); + // For-In should serve as a gen as well. + assertLiveBeforeX("var a,b; X:for(a in b){ }", "a"); + + // "a in b" should kill "a" before it. + // Can't prove this unless we have branched backward DFA. + //assertNotLiveAfterX("var a,b;X:b;for(a in b){a()};", "a"); + + // Unless it is used before. + assertLiveBeforeX("var a,b;X:a();b();for(a in b){a()};", "a"); + + // Initializer + assertLiveBeforeX("var a,b;X:b;for(b=a;;){};", "a"); + assertNotLiveBeforeX("var a,b;X:a;for(b=a;;){b()};b();", "b"); + } + + public void testNestedLoops() { + assertLiveBeforeX("var a;X:while(1){while(1){a()}}", "a"); + assertLiveBeforeX("var a;X:while(1){while(1){while(1){a()}}}", "a"); + assertLiveBeforeX("var a;X:while(1){while(1){a()};a=1}", "a"); + assertLiveAfterX("var a;while(1){while(1){a()};X:a=1;}", "a"); + assertLiveAfterX("var a;while(1){X:a=1;while(1){a()}}", "a"); + assertNotLiveBeforeX( + "var a;X:1;do{do{do{a=1;}while(1)}while(1)}while(1);a()", "a"); + } + + public void testSwitches() { + assertLiveBeforeX("var a,b;X:switch(a){}", "a"); + assertLiveBeforeX("var a,b;X:switch(b){case(a):break;}", "a"); + assertLiveBeforeX("var a,b;X:switch(b){case(b):case(a):break;}", "a"); + assertNotLiveBeforeX( + "var a,b;X:switch(b){case 1:a=1;break;default:a=2;break};a()", "a"); + + assertLiveBeforeX("var a,b;X:switch(b){default:a();break;}", "a"); + } + + public void testAssignAndReadInCondition() { + // BUG #1358904 + // Technically, this isn't exactly true....but we haven't model control flow + // within an instruction. + assertLiveBeforeX("var a, b; X: if ((a = this) && (b = a)) {}", "a"); + assertNotLiveBeforeX("var a, b; X: a = 1, b = 1;", "a"); + assertNotLiveBeforeX("var a; X: a = 1, a = 1;", "a"); + } + + public void testParam() { + // Unused parameter should not be live. + assertNotLiveAfterX("var a;X:a()", "param1"); + assertLiveBeforeX("var a;X:a(param1)", "param1"); + assertNotLiveAfterX("var a;X:a();a(param2)", "param1"); + } + + public void testExpressionInForIn() { + assertLiveBeforeX("var a = [0]; X:for (a[1] in foo) { }", "a"); + } + + public void testArgumentsArray() { + // Check that use of arguments forces the parameters into the + // escaped set. + assertEscaped("arguments[0]", "param1"); + assertEscaped("arguments[0]", "param2"); + assertEscaped("var args = arguments", "param1"); + assertEscaped("var args = arguments", "param2"); + assertNotEscaped("arguments = []", "param1"); + assertNotEscaped("arguments = []", "param2"); + assertEscaped("arguments[0] = 1", "param1"); + assertEscaped("arguments[0] = 1", "param2"); + assertEscaped("arguments[arguments[0]] = 1", "param1"); + assertEscaped("arguments[arguments[0]] = 1", "param2"); + } + + public void testTryCatchFinally() { + assertLiveAfterX("var a; try {X:a=1} finally {a}", "a"); + assertLiveAfterX("var a; try {a()} catch(e) {X:a=1} finally {a}", "a"); + // Because the outer catch doesn't catch any exceptions at all, the read of + // "a" within the catch block should not make "a" live. + assertNotLiveAfterX("var a = 1; try {" + + "try {a()} catch(e) {X:1} } catch(E) {a}", "a"); + assertLiveAfterX("var a; while(1) { try {X:a=1;break} finally {a}}", "a"); + } + + public void testForInAssignment() { + assertLiveBeforeX("var a,b; for (var y in a = b) { X:a[y] }", "a"); + // No one refers to b after the first iteration. + assertNotLiveBeforeX("var a,b; for (var y in a = b) { X:a[y] }", "b"); + assertLiveBeforeX("var a,b; for (var y in a = b) { X:a[y] }", "y"); + assertLiveAfterX("var a,b; for (var y in a = b) { a[y]; X: y();}", "a"); + } + + public void testExceptionThrowingAssignments() { + assertLiveBeforeX("try{var a; X:a=foo();a} catch(e) {e()}", "a"); + assertLiveBeforeX("try{X:var a=foo();a} catch(e) {e()}", "a"); + assertLiveBeforeX("try{X:var a=foo()} catch(e) {e(a)}", "a"); + } + + public void testInnerFunctions() { + assertLiveBeforeX("function a() {}; X: a()", "a"); + assertNotLiveBeforeX("X: function a() {}", "a"); + assertLiveBeforeX("a = function(){}; function a() {}; X: a()", "a"); + // NOTE: function a() {} has no CFG node representation since it is not + // part of the control execution. + assertLiveAfterX("X: a = function(){}; function a() {}; a()", "a"); + assertNotLiveBeforeX("X: a = function(){}; function a() {}; a()", "a"); + } + + public void testEscaped() { + assertEscaped("var a;function b(){a()}", "a"); + assertEscaped("var a;function b(){param1()}", "param1"); + assertEscaped("var a;function b(){function c(){a()}}", "a"); + assertEscaped("var a;function b(){param1.x = function() {a()}}", "a"); + assertEscaped("try{} catch(e){}", "e"); + assertNotEscaped("var a;function b(){var c; c()}", "c"); + assertNotEscaped("var a;function f(){function b(){var c;c()}}", "c"); + assertNotEscaped("var a;function b(){};a()", "a"); + assertNotEscaped("var a;function f(){function b(){}}a()", "a"); + assertNotEscaped("var a;function b(){var a;a()};a()", "a"); + + // Escaped by exporting. + assertEscaped("var _x", "_x"); + } + + public void testEscapedLiveness() { + assertNotLiveBeforeX("var a;X:a();function b(){a()}", "a"); + } + + public void testBug1449316() { + assertLiveBeforeX("try {var x=[]; X:var y=x[0]} finally {foo()}", "x"); + } + + private void assertLiveBeforeX(String src, String var) { + FlowState state = + getFlowStateAtX(src); + assertNotNull(src + " should contain a label 'X:'", state); + assertTrue("Variable" + var + " should be live before X", state.getIn() + .isLive(liveness.getVarIndex(var))); + } + + private void assertLiveAfterX(String src, String var) { + FlowState state = + getFlowStateAtX(src); + assertTrue("Label X should be in the input program.", state != null); + assertTrue("Variable" + var + " should be live after X", state.getOut() + .isLive(liveness.getVarIndex(var))); + } + + private void assertNotLiveAfterX(String src, String var) { + FlowState state = + getFlowStateAtX(src); + assertTrue("Label X should be in the input program.", state != null); + assertTrue("Variable" + var + " should not be live after X", !state + .getOut().isLive(liveness.getVarIndex(var))); + } + + private void assertNotLiveBeforeX(String src, String var) { + FlowState state = + getFlowStateAtX(src); + assertTrue("Label X should be in the input program.", state != null); + assertTrue("Variable" + var + " should not be live before X", !state + .getIn().isLive(liveness.getVarIndex(var))); + } + + private FlowState getFlowStateAtX( + String src) { + liveness = computeLiveness(src); + return getFlowStateAtX(liveness.getCfg().getEntry().getValue(), liveness + .getCfg()); + } + + private FlowState getFlowStateAtX( + Node node, ControlFlowGraph cfg) { + if (node.isLabel()) { + if (node.getFirstChild().getString().equals("X")) { + return cfg.getNode(node.getLastChild()).getAnnotation(); + } + } + for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { + FlowState state = + getFlowStateAtX(c, cfg); + if (state != null) { + return state; + } + } + return null; + } + + private static void assertEscaped(String src, String name) { + for (Var var : computeLiveness(src).getEscapedLocals()) { + if (var.name.equals(name)) { + return; + } + } + fail("Variable " + name + " should be in the escaped local list."); + } + + private static void assertNotEscaped(String src, String name) { + for (Var var : computeLiveness(src).getEscapedLocals()) { + assertFalse(var.name.equals(name)); + } + } + + private static LiveVariablesAnalysis computeLiveness(String src) { + Compiler compiler = new Compiler(); + CompilerOptions options = new CompilerOptions(); + options.setCodingConvention(new GoogleCodingConvention()); + compiler.initOptions(options); + src = "function _FUNCTION(param1, param2){" + src + "}"; + Node n = compiler.parseTestCode(src).removeFirstChild(); + Node script = new Node(Token.SCRIPT, n); + script.setInputId(new InputId("test")); + assertEquals(0, compiler.getErrorCount()); + Scope scope = new SyntacticScopeCreator(compiler).createScope( + n, Scope.createGlobalScope(script)); + ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); + cfa.process(null, n); + ControlFlowGraph cfg = cfa.getCfg(); + LiveVariablesAnalysis analysis = + new LiveVariablesAnalysis(cfg, scope, compiler); + analysis.analyze(); + return analysis; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/LooseTypeCheckTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/LooseTypeCheckTest.java new file mode 100644 index 0000000..94ee207 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/LooseTypeCheckTest.java @@ -0,0 +1,7075 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.Scope.Var; +import com.google.javascript.jscomp.type.ClosureReverseAbstractInterpreter; +import com.google.javascript.jscomp.type.SemanticReverseAbstractInterpreter; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.FunctionType; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.testing.Asserts; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +/** + * Tests {@link TypeCheck}. + * + * This is a temporary fork of the TypeCheckTest for the experimental + * "looseTypes" option. These tests should be be folded into TypeCheckTest + * or removed along with the looseTypes option. + * + */ +public class LooseTypeCheckTest extends CompilerTypeTestCase { + + @Override + public CompilerOptions getOptions() { + CompilerOptions options = super.getOptions(); + options.looseTypes = true; + return options; + } + + public void testInitialTypingScope() { + Scope s = new TypedScopeCreator(compiler, + CodingConventions.getDefault()).createInitialScope( + new Node(Token.BLOCK)); + + assertTypeEquals(ARRAY_FUNCTION_TYPE, s.getVar("Array").getType()); + assertTypeEquals(BOOLEAN_OBJECT_FUNCTION_TYPE, + s.getVar("Boolean").getType()); + assertTypeEquals(DATE_FUNCTION_TYPE, s.getVar("Date").getType()); + assertTypeEquals(ERROR_FUNCTION_TYPE, s.getVar("Error").getType()); + assertTypeEquals(EVAL_ERROR_FUNCTION_TYPE, + s.getVar("EvalError").getType()); + assertTypeEquals(NUMBER_OBJECT_FUNCTION_TYPE, + s.getVar("Number").getType()); + assertTypeEquals(OBJECT_FUNCTION_TYPE, s.getVar("Object").getType()); + assertTypeEquals(RANGE_ERROR_FUNCTION_TYPE, + s.getVar("RangeError").getType()); + assertTypeEquals(REFERENCE_ERROR_FUNCTION_TYPE, + s.getVar("ReferenceError").getType()); + assertTypeEquals(REGEXP_FUNCTION_TYPE, s.getVar("RegExp").getType()); + assertTypeEquals(STRING_OBJECT_FUNCTION_TYPE, + s.getVar("String").getType()); + assertTypeEquals(SYNTAX_ERROR_FUNCTION_TYPE, + s.getVar("SyntaxError").getType()); + assertTypeEquals(TYPE_ERROR_FUNCTION_TYPE, + s.getVar("TypeError").getType()); + assertTypeEquals(URI_ERROR_FUNCTION_TYPE, + s.getVar("URIError").getType()); + } + + public void testTypeCheck1() throws Exception { + testTypes("/**@return {void}*/function foo(){ if (foo()) return; }"); + } + + public void testTypeCheck2() throws Exception { + testTypes("/**@return {void}*/function foo(){ var x=foo(); x--; }", + "increment/decrement\n" + + "found : undefined\n" + + "required: number"); + } + + public void testTypeCheck4() throws Exception { + testTypes("/**@return {void}*/function foo(){ !foo(); }"); + } + + public void testTypeCheck5() throws Exception { + testTypes("/**@return {void}*/function foo(){ var a = +foo(); }", + "sign operator\n" + + "found : undefined\n" + + "required: number"); + } + + public void testTypeCheck6() throws Exception { + testTypes( + "/**@return {void}*/function foo(){" + + "/** @type {undefined|number} */var a;if (a == foo())return;}"); + } + + public void testTypeCheck8() throws Exception { + testTypes("/**@return {void}*/function foo(){do {} while (foo());}"); + } + + public void testTypeCheck9() throws Exception { + testTypes("/**@return {void}*/function foo(){while (foo());}"); + } + + public void testTypeCheck10() throws Exception { + testTypes("/**@return {void}*/function foo(){for (;foo(););}"); + } + + public void testTypeCheck11() throws Exception { + testTypes("/**@type !Number */var a;" + + "/**@type !String */var b;" + + "a = b;", + "assignment\n" + + "found : String\n" + + "required: Number"); + } + + public void testTypeCheck12() throws Exception { + testTypes("/**@return {!Object}*/function foo(){var a = 3^foo();}", + "bad right operand to bitwise operator\n" + + "found : Object\n" + + "required: (boolean|null|number|string|undefined)"); + } + + public void testTypeCheck13() throws Exception { + testTypes("/**@type {!Number|!String}*/var i; i=/xx/;", + "assignment\n" + + "found : RegExp\n" + + "required: (Number|String)"); + } + + public void testTypeCheck14() throws Exception { + testTypes("/**@param opt_a*/function foo(opt_a){}"); + } + + + public void testTypeCheck15() throws Exception { + testTypes("/**@type {Number} */var x;x=null;x=10;", + "assignment\n" + + "found : number\n" + + "required: (Number|null|undefined)"); + } + + public void testTypeCheck16a() throws Exception { + testTypes("/**@type {Number|null} */var x='';", + "initializing variable\n" + + "found : string\n" + + "required: (Number|null|undefined)"); + } + + public void testTypeCheck16b() throws Exception { + testTypes("/**@type {!Number|null} */var x='';", + "initializing variable\n" + + "found : string\n" + + "required: (Number|null)"); + } + + public void testTypeCheck17() throws Exception { + testTypes("/**@return {Number}\n@param {Number} opt_foo */\n" + + "function a(opt_foo){\nreturn /**@type {Number}*/(opt_foo);\n}"); + } + + + public void testTypeCheck18() throws Exception { + testTypes("/**@return {RegExp}\n*/\n function a(){return new RegExp();}"); + } + + public void testTypeCheck19() throws Exception { + testTypes("/**@return {Array}\n*/\n function a(){return new Array();}"); + } + + public void testTypeCheck20() throws Exception { + testTypes("/**@return {Date}\n*/\n function a(){return new Date();}"); + } + + public void testTypeCheckBasicDowncast() throws Exception { + testTypes("/** @constructor */function foo() {}\n" + + "/** @type {Object} */ var bar = new foo();\n"); + } + + public void testTypeCheckNoDowncastToNumber() throws Exception { + testTypes("/** @constructor */function foo() {}\n" + + "/** @type {!Number} */ var bar = new foo();\n", + "initializing variable\n" + + "found : foo\n" + + "required: Number"); + } + + public void testTypeCheck21() throws Exception { + testTypes("/** @type Array. */var foo;"); + } + + public void testTypeCheck22() throws Exception { + testTypes("/** @param {Element|Object} p */\nfunction foo(p){}\n" + + "/** @constructor */function Element(){}\n" + + "/** @type {Element|Object} */var v;\n" + + "foo(v);\n"); + } + + public void testTypeCheck23() throws Exception { + testTypes("/** @type {(Object,Null)} */var foo; foo = null;"); + } + + public void testTypeCheck24() throws Exception { + testTypes("/** @constructor */function MyType(){}\n" + + "/** @type {(MyType,Null)} */var foo; foo = null;"); + } + + public void testTypeCheckDefaultExterns() throws Exception { + testTypes("/** @param {string} x */ function f(x) {}" + + "f([].length);" , + "actual parameter 1 of f does not match formal parameter\n" + + "found : number\n" + + "required: string"); + } + + public void testTypeCheckCustomExterns() throws Exception { + testTypes( + DEFAULT_EXTERNS + "/** @type {boolean} */ Array.prototype.oogabooga;", + "/** @param {string} x */ function f(x) {}" + + "f([].oogabooga);" , + "actual parameter 1 of f does not match formal parameter\n" + + "found : boolean\n" + + "required: string", false); + } + + public void testParameterizedArray1() throws Exception { + testTypes("/** @param {!Array.} a\n" + + "* @return {string}\n" + + "*/ var f = function(a) { return a[0]; };", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testParameterizedArray2() throws Exception { + testTypes("/** @param {!Array.>} a\n" + + "* @return {number}\n" + + "*/ var f = function(a) { return a[0]; };", + "inconsistent return type\n" + + "found : Array.\n" + + "required: number"); + } + + public void testParameterizedArray3() throws Exception { + testTypes("/** @param {!Array.} a\n" + + "* @return {number}\n" + + "*/ var f = function(a) { a[1] = 0; return a[0]; };"); + } + + public void testParameterizedArray4() throws Exception { + testTypes("/** @param {!Array.} a\n" + + "*/ var f = function(a) { a[0] = 'a'; };", + "assignment\n" + + "found : string\n" + + "required: number"); + } + + public void testParameterizedArray5() throws Exception { + testTypes("/** @param {!Array.<*>} a\n" + + "*/ var f = function(a) { a[0] = 'a'; };"); + } + + public void testParameterizedArray6() throws Exception { + testTypes("/** @param {!Array.<*>} a\n" + + "* @return {string}\n" + + "*/ var f = function(a) { return a[0]; };", + "inconsistent return type\n" + + "found : *\n" + + "required: string"); + } + + public void testParameterizedArray7() throws Exception { + testTypes("/** @param {?Array.} a\n" + + "* @return {string}\n" + + "*/ var f = function(a) { return a[0]; };", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testParameterizedObject1() throws Exception { + testTypes("/** @param {!Object.} a\n" + + "* @return {string}\n" + + "*/ var f = function(a) { return a[0]; };", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testParameterizedObject2() throws Exception { + testTypes("/** @param {!Object.} a\n" + + "* @return {string}\n" + + "*/ var f = function(a) { return a['x']; };", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testParameterizedObject3() throws Exception { + testTypes("/** @param {!Object.} a\n" + + "* @return {string}\n" + + "*/ var f = function(a) { return a['x']; };", + "restricted index type\n" + + "found : string\n" + + "required: number"); + } + + public void testParameterizedObject4() throws Exception { + testTypes("/** @enum {string} */ var E = {A: 'a', B: 'b'};\n" + + "/** @param {!Object.} a\n" + + "* @return {string}\n" + + "*/ var f = function(a) { return a['x']; };", + "restricted index type\n" + + "found : string\n" + + "required: E."); + } + + public void testUnionOfFunctionAndType() throws Exception { + testTypes("/** @type {null|(function(Number):void)} */ var a;" + + "/** @type {(function(Number):void)|null} */ var b = null; a = b;"); + } + + public void testOptionalParameterComparedToUndefined() throws Exception { + testTypes("/**@param opt_a {Number}*/function foo(opt_a)" + + "{if (opt_a==undefined) var b = 3;}"); + } + + public void testOptionalAllType() throws Exception { + testTypes("/** @param {*} opt_x */function f(opt_x) { return opt_x }\n" + + "/** @type {*} */var y;\n" + + "f(y);"); + } + + public void testOptionalUnknownNamedType() throws Exception { + testTypes("/** @param {!T} opt_x\n@return {undefined} */\n" + + "function f(opt_x) { return opt_x; }\n" + + "/** @constructor */var T = function() {};", + "inconsistent return type\n" + + "found : (T|undefined)\n" + + "required: undefined"); + } + + public void testOptionalArgFunctionParam() throws Exception { + testTypes("/** @param {function(number=)} a */" + + "function f(a) {a()};"); + } + + public void testOptionalArgFunctionParam2() throws Exception { + testTypes("/** @param {function(number=)} a */" + + "function f(a) {a(3)};"); + } + + public void testOptionalArgFunctionParam3() throws Exception { + testTypes("/** @param {function(number=)} a */" + + "function f(a) {a(undefined)};"); + } + + public void testOptionalArgFunctionParam4() throws Exception { + String expectedWarning = "Function a: called with 2 argument(s). " + + "Function requires at least 0 argument(s) and no more than 1 " + + "argument(s)."; + + testTypes("/** @param {function(number=)} a */function f(a) {a(3,4)};", + expectedWarning, false); + } + + public void testOptionalArgFunctionParamError() throws Exception { + String expectedWarning = + "Bad type annotation. variable length argument must be last"; + testTypes("/** @param {function(...[number], number=)} a */" + + "function f(a) {};", expectedWarning, false); + } + + public void testOptionalNullableArgFunctionParam() throws Exception { + testTypes("/** @param {function(?number=)} a */" + + "function f(a) {a()};"); + } + + public void testOptionalNullableArgFunctionParam2() throws Exception { + testTypes("/** @param {function(?number=)} a */" + + "function f(a) {a(null)};"); + } + + public void testOptionalNullableArgFunctionParam3() throws Exception { + testTypes("/** @param {function(?number=)} a */" + + "function f(a) {a(3)};"); + } + + public void testOptionalArgFunctionReturn() throws Exception { + testTypes("/** @return {function(number=)} */" + + "function f() { return function(opt_x) { }; };" + + "f()()"); + } + + public void testOptionalArgFunctionReturn2() throws Exception { + testTypes("/** @return {function(Object=)} */" + + "function f() { return function(opt_x) { }; };" + + "f()({})"); + } + + public void testBooleanType() throws Exception { + testTypes("/**@type {boolean} */var x = 1 < 2;"); + } + + public void testBooleanReduction1() throws Exception { + testTypes("/**@type {string} */var x; x = null || \"a\";"); + } + + public void testBooleanReduction2() throws Exception { + // It's important for the type system to recognize that in no case + // can the boolean expression evaluate to a boolean value. + testTypes("/** @param {string} s\n @return {string} */" + + "(function(s) { return ((s == 'a') && s) || 'b'; })"); + } + + public void testBooleanReduction3() throws Exception { + testTypes("/** @param {string} s\n @return {string?} */" + + "(function(s) { return s && null && 3; })"); + } + + public void testBooleanReduction4() throws Exception { + testTypes("/** @param {Object} x\n @return {Object} */" + + "(function(x) { return null || x || null ; })"); + } + + public void testBooleanReduction5() throws Exception { + testTypes("/**\n" + + "* @param {Array|string} x\n" + + "* @return {string?}\n" + + "*/\n" + + "var f = function(x) {\n" + + "if (!x || typeof x == 'string') {\n" + + "return x;\n" + + "}\n" + + "return null;\n" + + "};"); + } + + public void testBooleanReduction6() throws Exception { + testTypes("/**\n" + + "* @param {Array|string|null} x\n" + + "* @return {string?}\n" + + "*/\n" + + "var f = function(x) {\n" + + "if (!(x && typeof x != 'string')) {\n" + + "return x;\n" + + "}\n" + + "return null;\n" + + "};"); + } + + public void testBooleanReduction7() throws Exception { + testTypes("/** @constructor */var T = function() {};\n" + + "/**\n" + + "* @param {Array|T} x\n" + + "* @return {null|undefined}\n" + + "*/\n" + + "var f = function(x) {\n" + + "if (!x) {\n" + + "return x;\n" + + "}\n" + + "return null;\n" + + "};"); + } + + public void testNullAnd() throws Exception { + testTypes("/** @type null */var x;\n" + + "/** @type number */var r = x && x;", + "initializing variable\n" + + "found : null\n" + + "required: number"); + } + + public void testNullOr() throws Exception { + testTypes("/** @type null */var x;\n" + + "/** @type number */var r = x || x;", + "initializing variable\n" + + "found : null\n" + + "required: number"); + } + + public void testBooleanPreservation1() throws Exception { + testTypes("/**@type {string} */var x = \"a\";" + + "x = ((x == \"a\") && x) || x == \"b\";", + "assignment\n" + + "found : (boolean|string)\n" + + "required: string"); + } + + public void testBooleanPreservation2() throws Exception { + testTypes("/**@type {string} */var x = \"a\"; x = (x == \"a\") || x;", + "assignment\n" + + "found : (boolean|string)\n" + + "required: string"); + } + + public void testBooleanPreservation3() throws Exception { + testTypes("/** @param {Function?} x\n @return {boolean?} */" + + "function f(x) { return x && x == \"a\"; }", + "condition always evaluates to false\n" + + "left : Function\n" + + "right: string"); + } + + public void testBooleanPreservation4() throws Exception { + testTypes("/** @param {Function?|boolean} x\n @return {boolean} */" + + "function f(x) { return x && x == \"a\"; }", + "inconsistent return type\n" + + "found : (boolean|null|undefined)\n" + + "required: boolean"); + } + + public void testTypeOfReduction1() throws Exception { + testTypes("/** @param {string|number} x\n @return {string} */ " + + "function f(x) { return typeof x == 'number' ? String(x) : x; }"); + } + + public void testTypeOfReduction2() throws Exception { + testTypes("/** @param {string|number} x\n @return {string} */ " + + "function f(x) { return typeof x != 'string' ? String(x) : x; }"); + } + + public void testTypeOfReduction3() throws Exception { + testTypes("/** @param {number|null} x\n @return {number} */ " + + "function f(x) { return typeof x == 'object' ? 1 : x; }"); + } + + public void testTypeOfReduction4() throws Exception { + testTypes("/** @param {Object|undefined} x\n @return {Object} */ " + + "function f(x) { return typeof x == 'undefined' ? {} : x; }"); + } + + public void testTypeOfReduction5() throws Exception { + testTypes("/** @enum {string} */ var E = {A: 'a', B: 'b'};\n" + + "/** @param {!E|number} x\n @return {string} */ " + + "function f(x) { return typeof x != 'number' ? x : 'a'; }"); + } + + public void testTypeOfReduction6() throws Exception { + testTypes("/** @param {number|string} x\n@return {string} */\n" + + "function f(x) {\n" + + "return typeof x == 'string' && x.length == 3 ? x : 'a';\n" + + "}"); + } + + public void testTypeOfReduction7() throws Exception { + testTypes("/** @return {string} */var f = function(x) { " + + "return typeof x == 'number' ? x : 'a'; }", + "inconsistent return type\n" + + "found : (number|string)\n" + + "required: string"); + } + + public void testTypeOfReduction8() throws Exception { + testClosureTypes( + CLOSURE_DEFS + + "/** @param {number|string} x\n@return {string} */\n" + + "function f(x) {\n" + + "return goog.isString(x) && x.length == 3 ? x : 'a';\n" + + "}", null); + } + + public void testTypeOfReduction9() throws Exception { + testClosureTypes( + CLOSURE_DEFS + + "/** @param {!Array|string} x\n@return {string} */\n" + + "function f(x) {\n" + + "return goog.isArray(x) ? 'a' : x;\n" + + "}", null); + } + + public void testTypeOfReduction10() throws Exception { + testClosureTypes( + CLOSURE_DEFS + + "/** @param {Array|string} x\n@return {Array} */\n" + + "function f(x) {\n" + + "return goog.isArray(x) ? x : [];\n" + + "}", null); + } + + public void testTypeOfReduction11() throws Exception { + testClosureTypes( + CLOSURE_DEFS + + "/** @param {Array|string} x\n@return {Array} */\n" + + "function f(x) {\n" + + "return goog.isObject(x) ? x : [];\n" + + "}", null); + } + + public void testTypeOfReduction12() throws Exception { + testTypes("/** @enum {string} */ var E = {A: 'a', B: 'b'};\n" + + "/** @param {E|Array} x\n @return {Array} */ " + + "function f(x) { return typeof x == 'object' ? x : []; }"); + } + + public void testTypeOfReduction13() throws Exception { + testClosureTypes( + CLOSURE_DEFS + + "/** @enum {string} */ var E = {A: 'a', B: 'b'};\n" + + "/** @param {E|Array} x\n@return {Array} */ " + + "function f(x) { return goog.isObject(x) ? x : []; }", null); + } + + public void testTypeOfReduction14() throws Exception { + // Don't do type inference on GETELEMs. + testClosureTypes( + CLOSURE_DEFS + + "function f(x) { " + + " return goog.isString(arguments[0]) ? arguments[0] : 0;" + + "}", null); + } + + public void testTypeOfReduction15() throws Exception { + // Don't do type inference on GETELEMs. + testClosureTypes( + CLOSURE_DEFS + + "function f(x) { " + + " return typeof arguments[0] == 'string' ? arguments[0] : 0;" + + "}", null); + } + + public void testQualifiedNameReduction1() throws Exception { + testTypes("var x = {}; /** @type {string?} */ x.a = 'a';\n" + + "/** @return {string} */ var f = function() {\n" + + "return x.a ? x.a : 'a'; }"); + } + + public void testQualifiedNameReduction2() throws Exception { + testTypes("/** @param {string?} a\n@constructor */ var T = " + + "function(a) {this.a = a};\n" + + "/** @return {string} */ T.prototype.f = function() {\n" + + "return this.a ? this.a : 'a'; }"); + } + + public void testQualifiedNameReduction3() throws Exception { + testTypes("/** @param {string|Array} a\n@constructor */ var T = " + + "function(a) {this.a = a};\n" + + "/** @return {string} */ T.prototype.f = function() {\n" + + "return typeof this.a == 'string' ? this.a : 'a'; }"); + } + + public void testQualifiedNameReduction4() throws Exception { + testClosureTypes( + CLOSURE_DEFS + + "/** @param {string|Array} a\n@constructor */ var T = " + + "function(a) {this.a = a};\n" + + "/** @return {string} */ T.prototype.f = function() {\n" + + "return goog.isString(this.a) ? this.a : 'a'; }", null); + } + + public void testInstanceOfReduction1() throws Exception { + testTypes("/** @constructor */ var T = function() {};\n" + + "/** @param {T|string} x\n@return {T} */\n" + + "var f = function(x) {\n" + + "if (x instanceof T) { return x; } else { return new T(); }\n" + + "};"); + } + + public void testInstanceOfReduction2() throws Exception { + testTypes("/** @constructor */ var T = function() {};\n" + + "/** @param {!T|string} x\n@return {string} */\n" + + "var f = function(x) {\n" + + "if (x instanceof T) { return ''; } else { return x; }\n" + + "};"); + } + + public void testPropertyInferredPropagation() throws Exception { + testTypes("/** @return {Object} */function f() { return {}; }\n" + + "function g() { var x = f(); if (x.p) x.a = 'a'; else x.a = 'b'; }\n" + + "function h() { var x = f(); x.a = false; }"); + } + + public void testPropertyInference1() throws Exception { + testTypes( + "/** @constructor */ function F() { this.x_ = true; }" + + "/** @return {string} */" + + "F.prototype.bar = function() { if (this.x_) return this.x_; };", + "inconsistent return type\n" + + "found : boolean\n" + + "required: string"); + } + + public void testPropertyInference2() throws Exception { + testTypes( + "/** @constructor */ function F() { this.x_ = true; }" + + "F.prototype.baz = function() { this.x_ = null; };" + + "/** @return {string} */" + + "F.prototype.bar = function() { if (this.x_) return this.x_; };", + "inconsistent return type\n" + + "found : boolean\n" + + "required: string"); + } + + public void testPropertyInference3() throws Exception { + testTypes( + "/** @constructor */ function F() { this.x_ = true; }" + + "F.prototype.baz = function() { this.x_ = 3; };" + + "/** @return {string} */" + + "F.prototype.bar = function() { if (this.x_) return this.x_; };", + "inconsistent return type\n" + + "found : (boolean|number)\n" + + "required: string"); + } + + public void testPropertyInference4() throws Exception { + testTypes( + "/** @constructor */ function F() { }" + + "F.prototype.x_ = 3;" + + "/** @return {string} */" + + "F.prototype.bar = function() { if (this.x_) return this.x_; };", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testPropertyInference5() throws Exception { + testTypes( + "/** @constructor */ function F() { }" + + "F.prototype.baz = function() { this.x_ = 3; };" + + "/** @return {string} */" + + "F.prototype.bar = function() { if (this.x_) return this.x_; };"); + } + + public void testPropertyInference6() throws Exception { + testTypes( + "/** @constructor */ function F() { }" + + "(new F).x_ = 3;" + + "/** @return {string} */" + + "F.prototype.bar = function() { return this.x_; };"); + } + + public void testPropertyInference7() throws Exception { + testTypes( + "/** @constructor */ function F() { this.x_ = true; }" + + "(new F).x_ = 3;" + + "/** @return {string} */" + + "F.prototype.bar = function() { return this.x_; };", + "inconsistent return type\n" + + "found : boolean\n" + + "required: string"); + } + + public void testPropertyInference8() throws Exception { + testTypes( + "/** @constructor */ function F() { " + + " /** @type {string} */ this.x_ = 'x';" + + "}" + + "(new F).x_ = 3;" + + "/** @return {string} */" + + "F.prototype.bar = function() { return this.x_; };", + "assignment to property x_ of F\n" + + "found : number\n" + + "required: string"); + } + + public void testNoPersistentTypeInferenceForObjectProperties() + throws Exception { + testTypes("/** @param {Object} o\n@param {string} x */\n" + + "function s1(o,x) { o.x = x; }\n" + + "/** @param {Object} o\n@return {string} */\n" + + "function g1(o) { return typeof o.x == 'undefined' ? '' : o.x; }\n" + + "/** @param {Object} o\n@param {number} x */\n" + + "function s2(o,x) { o.x = x; }\n" + + "/** @param {Object} o\n@return {number} */\n" + + "function g2(o) { return typeof o.x == 'undefined' ? 0 : o.x; }"); + } + + public void testNoPersistentTypeInferenceForFunctionProperties() + throws Exception { + testTypes("/** @param {Function} o\n@param {string} x */\n" + + "function s1(o,x) { o.x = x; }\n" + + "/** @param {Function} o\n@return {string} */\n" + + "function g1(o) { return typeof o.x == 'undefined' ? '' : o.x; }\n" + + "/** @param {Function} o\n@param {number} x */\n" + + "function s2(o,x) { o.x = x; }\n" + + "/** @param {Function} o\n@return {number} */\n" + + "function g2(o) { return typeof o.x == 'undefined' ? 0 : o.x; }"); + } + + public void testObjectPropertyTypeInferredInLocalScope1() throws Exception { + testTypes("/** @param {!Object} o\n@return {string} */\n" + + "function f(o) { o.x = 1; return o.x; }", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testObjectPropertyTypeInferredInLocalScope2() throws Exception { + testTypes("/**@param {!Object} o\n@param {number?} x\n@return {string}*/" + + "function f(o, x) { o.x = 'a';\nif (x) {o.x = x;}\nreturn o.x; }", + "inconsistent return type\n" + + "found : (number|string)\n" + + "required: string"); + } + + public void testObjectPropertyTypeInferredInLocalScope3() throws Exception { + testTypes("/**@param {!Object} o\n@param {number?} x\n@return {string}*/" + + "function f(o, x) { if (x) {o.x = x;} else {o.x = 'a';}\nreturn o.x; }", + "inconsistent return type\n" + + "found : (number|string)\n" + + "required: string"); + } + + public void testMismatchingOverridingInferredPropertyBeforeDeclaredProperty1() + throws Exception { + testTypes("/** @constructor */var T = function() { this.x = ''; };\n" + + "/** @type {number} */ T.prototype.x = 0;", + "assignment to property x of T\n" + + "found : string\n" + + "required: number"); + } + + public void testMismatchingOverridingInferredPropertyBeforeDeclaredProperty2() + throws Exception { + testTypes("/** @constructor */var T = function() { this.x = ''; };\n" + + "/** @type {number} */ T.prototype.x;", + "assignment to property x of T\n" + + "found : string\n" + + "required: number"); + } + + public void testMismatchingOverridingInferredPropertyBeforeDeclaredProperty3() + throws Exception { + testTypes("/** @type {Object} */ var n = {};\n" + + "/** @constructor */ n.T = function() { this.x = ''; };\n" + + "/** @type {number} */ n.T.prototype.x = 0;", + "assignment to property x of n.T\n" + + "found : string\n" + + "required: number"); + } + + public void testMismatchingOverridingInferredPropertyBeforeDeclaredProperty4() + throws Exception { + testTypes("var n = {};\n" + + "/** @constructor */ n.T = function() { this.x = ''; };\n" + + "/** @type {number} */ n.T.prototype.x = 0;", + "assignment to property x of n.T\n" + + "found : string\n" + + "required: number"); + } + + public void testPropertyUsedBeforeDefinition1() throws Exception { + testTypes("/** @constructor */ var T = function() {};\n" + + "/** @return {string} */" + + "T.prototype.f = function() { return this.g(); };\n" + + "/** @return {number} */ T.prototype.g = function() { return 1; };\n", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testPropertyUsedBeforeDefinition2() throws Exception { + testTypes("var n = {};\n" + + "/** @constructor */ n.T = function() {};\n" + + "/** @return {string} */" + + "n.T.prototype.f = function() { return this.g(); };\n" + + "/** @return {number} */ n.T.prototype.g = function() { return 1; };\n", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testAdd1() throws Exception { + testTypes("/**@return {void}*/function foo(){var a = 'abc'+foo();}"); + } + + public void testAdd2() throws Exception { + testTypes("/**@return {void}*/function foo(){var a = foo()+4;}"); + } + + public void testAdd3() throws Exception { + testTypes("/** @type {string} */ var a = 'a';" + + "/** @type {string} */ var b = 'b';" + + "/** @type {string} */ var c = a + b;"); + } + + public void testAdd4() throws Exception { + testTypes("/** @type {number} */ var a = 5;" + + "/** @type {string} */ var b = 'b';" + + "/** @type {string} */ var c = a + b;"); + } + + public void testAdd5() throws Exception { + testTypes("/** @type {string} */ var a = 'a';" + + "/** @type {number} */ var b = 5;" + + "/** @type {string} */ var c = a + b;"); + } + + public void testAdd6() throws Exception { + testTypes("/** @type {number} */ var a = 5;" + + "/** @type {number} */ var b = 5;" + + "/** @type {number} */ var c = a + b;"); + } + + public void testAdd7() throws Exception { + testTypes("/** @type {number} */ var a = 5;" + + "/** @type {string} */ var b = 'b';" + + "/** @type {number} */ var c = a + b;", + "initializing variable\n" + + "found : string\n" + + "required: number"); + } + + public void testAdd8() throws Exception { + testTypes("/** @type {string} */ var a = 'a';" + + "/** @type {number} */ var b = 5;" + + "/** @type {number} */ var c = a + b;", + "initializing variable\n" + + "found : string\n" + + "required: number"); + } + + public void testAdd9() throws Exception { + testTypes("/** @type {number} */ var a = 5;" + + "/** @type {number} */ var b = 5;" + + "/** @type {string} */ var c = a + b;", + "initializing variable\n" + + "found : number\n" + + "required: string"); + } + + public void testAdd10() throws Exception { + // d.e.f will have unknown type. + testTypes( + suppressMissingProperty("e", "f") + + "/** @type {number} */ var a = 5;" + + "/** @type {string} */ var c = a + d.e.f;"); + } + + public void testAdd11() throws Exception { + // d.e.f will have unknown type. + testTypes( + suppressMissingProperty("e", "f") + + "/** @type {number} */ var a = 5;" + + "/** @type {number} */ var c = a + d.e.f;"); + } + + public void testAdd12() throws Exception { + testTypes("/** @return {(number,string)} */ function a() { return 5; }" + + "/** @type {number} */ var b = 5;" + + "/** @type {boolean} */ var c = a() + b;", + "initializing variable\n" + + "found : (number|string)\n" + + "required: boolean"); + } + + public void testAdd13() throws Exception { + testTypes("/** @type {number} */ var a = 5;" + + "/** @return {(number,string)} */ function b() { return 5; }" + + "/** @type {boolean} */ var c = a + b();", + "initializing variable\n" + + "found : (number|string)\n" + + "required: boolean"); + } + + public void testAdd14() throws Exception { + testTypes("/** @type {(null,string)} */ var a = null;" + + "/** @type {number} */ var b = 5;" + + "/** @type {boolean} */ var c = a + b;", + "initializing variable\n" + + "found : (number|string)\n" + + "required: boolean"); + } + + public void testAdd15() throws Exception { + testTypes("/** @type {number} */ var a = 5;" + + "/** @return {(number,string)} */ function b() { return 5; }" + + "/** @type {boolean} */ var c = a + b();", + "initializing variable\n" + + "found : (number|string)\n" + + "required: boolean"); + } + + public void testAdd16() throws Exception { + testTypes("/** @type {(undefined,string)} */ var a = undefined;" + + "/** @type {number} */ var b = 5;" + + "/** @type {boolean} */ var c = a + b;", + "initializing variable\n" + + "found : (number|string)\n" + + "required: boolean"); + } + + public void testAdd17() throws Exception { + testTypes("/** @type {number} */ var a = 5;" + + "/** @type {(undefined,string)} */ var b = undefined;" + + "/** @type {boolean} */ var c = a + b;", + "initializing variable\n" + + "found : (number|string)\n" + + "required: boolean"); + } + + public void testAdd18() throws Exception { + testTypes("function f() {};" + + "/** @type {string} */ var a = 'a';" + + "/** @type {number} */ var c = a + f();", + "initializing variable\n" + + "found : string\n" + + "required: number"); + } + + public void testAdd19() throws Exception { + testTypes("/** @param {number} opt_x\n@param {number} opt_y\n" + + "@return {number} */ function f(opt_x, opt_y) {" + + "return opt_x + opt_y;}"); + } + + public void testAdd20() throws Exception { + testTypes("/** @param {!Number} opt_x\n@param {!Number} opt_y\n" + + "@return {number} */ function f(opt_x, opt_y) {" + + "return opt_x + opt_y;}"); + } + + public void testAdd21() throws Exception { + testTypes("/** @param {Number|Boolean} opt_x\n" + + "@param {number|boolean} opt_y\n" + + "@return {number} */ function f(opt_x, opt_y) {" + + "return opt_x + opt_y;}"); + } + + public void testNumericComparison1() throws Exception { + testTypes("/**@param {number} a*/ function f(a) {return a < 3;}"); + } + + public void testNumericComparison2() throws Exception { + testTypes("/**@param {!Object} a*/ function f(a) {return a < 3;}", + "left side of numeric comparison\n" + + "found : Object\n" + + "required: number"); + } + + public void testNumericComparison3() throws Exception { + testTypes("/**@param {string} a*/ function f(a) {return a < 3;}"); + } + + public void testNumericComparison4() throws Exception { + testTypes("/**@param {(number,undefined)} a*/ " + + "function f(a) {return a < 3;}"); + } + + public void testNumericComparison5() throws Exception { + testTypes("/**@param {*} a*/ function f(a) {return a < 3;}", + "left side of numeric comparison\n" + + "found : *\n" + + "required: number"); + } + + public void testNumericComparison6() throws Exception { + testTypes("/**@return {void}*/ function foo() { if (3 >= foo()) return; }", + "right side of numeric comparison\n" + + "found : undefined\n" + + "required: number"); + } + + public void testStringComparison1() throws Exception { + testTypes("/**@param {string} a*/ function f(a) {return a < 'x';}"); + } + + public void testStringComparison2() throws Exception { + testTypes("/**@param {Object} a*/ function f(a) {return a < 'x';}"); + } + + public void testStringComparison3() throws Exception { + testTypes("/**@param {number} a*/ function f(a) {return a < 'x';}"); + } + + public void testStringComparison4() throws Exception { + testTypes("/**@param {string|undefined} a*/ " + + "function f(a) {return a < 'x';}"); + } + + public void testStringComparison5() throws Exception { + testTypes("/**@param {*} a*/ " + + "function f(a) {return a < 'x';}"); + } + + public void testStringComparison6() throws Exception { + testTypes("/**@return {void} */ " + + "function foo() { if ('a' >= foo()) return; }", + "right side of comparison\n" + + "found : undefined\n" + + "required: string"); + } + + public void testValueOfComparison1() throws Exception { + testTypes("/** @constructor */function O() {};" + + "/**@override*/O.prototype.valueOf = function() { return 1; };" + + "/**@param {!O} a\n@param {!O} b*/ function f(a,b) { return a < b; }"); + } + + public void testValueOfComparison2() throws Exception { + testTypes("/** @constructor */function O() {};" + + "/**@override*/O.prototype.valueOf = function() { return 1; };" + + "/**@param {!O} a\n@param {number} b*/" + + "function f(a,b) { return a < b; }"); + } + + public void testValueOfComparison3() throws Exception { + testTypes("/** @constructor */function O() {};" + + "/**@override*/O.prototype.toString = function() { return 'o'; };" + + "/**@param {!O} a\n@param {string} b*/" + + "function f(a,b) { return a < b; }"); + } + + public void testGenericRelationalExpression() throws Exception { + testTypes("/**@param {*} a\n@param {*} b*/ " + + "function f(a,b) {return a < b;}"); + } + + public void testInstanceof1() throws Exception { + testTypes("function foo(){" + + "if (bar instanceof 3)return;}", + "instanceof requires an object\n" + + "found : number\n" + + "required: Object"); + } + + public void testInstanceof2() throws Exception { + testTypes("/**@return {void}*/function foo(){" + + "if (foo() instanceof Object)return;}", + "deterministic instanceof yields false\n" + + "found : undefined\n" + + "required: NoObject"); + } + + public void testInstanceof3() throws Exception { + testTypes("/**@return {*} */function foo(){" + + "if (foo() instanceof Object)return;}"); + } + + public void testInstanceof4() throws Exception { + testTypes("/**@return {(Object|number)} */function foo(){" + + "if (foo() instanceof Object)return 3;}"); + } + + public void testInstanceof5() throws Exception { + // No warning for unknown types. + testTypes("/** @return {?} */ function foo(){" + + "if (foo() instanceof Object)return;}"); + } + + public void testInstanceof6() throws Exception { + testTypes("/**@return {(Array|number)} */function foo(){" + + "if (foo() instanceof Object)return 3;}"); + } + + public void testInstanceOfReduction3() throws Exception { + testTypes( + "/** \n" + + " * @param {Object} x \n" + + " * @param {Function} y \n" + + " * @return {boolean} \n" + + " */\n" + + "var f = function(x, y) {\n" + + " return x instanceof y;\n" + + "};"); + } + + public void testScoping1() throws Exception { + testTypes( + "/**@param {string} a*/function foo(a){" + + " /**@param {Array|string} a*/function bar(a){" + + " if (a instanceof Array)return;" + + " }" + + "}"); + } + + public void testScoping2() throws Exception { + testTypes( + "/** @type number */ var a;" + + "function Foo() {" + + " /** @type string */ var a;" + + "}"); + } + + public void testScoping3() throws Exception { + testTypes("\n\n/** @type{Number}*/var b;\n/** @type{!String} */var b;", + "variable b redefined with type String, original " + + "definition at [testcode]:3 with type (Number|null|undefined)"); + } + + public void testScoping4() throws Exception { + testTypes("/** @type{Number}*/var b; if (true) /** @type{!String} */var b;", + "variable b redefined with type String, original " + + "definition at [testcode]:1 with type (Number|null|undefined)"); + } + + public void testScoping5() throws Exception { + // multiple definitions are not checked by the type checker but by a + // subsequent pass + testTypes("if (true) var b; var b;"); + } + + public void testScoping6() throws Exception { + // multiple definitions are not checked by the type checker but by a + // subsequent pass + testTypes("if (true) var b; if (true) var b;"); + } + + public void testScoping7() throws Exception { + testTypes("/** @constructor */function A() {" + + " /** @type !A */this.a = null;" + + "}", + "assignment to property a of A\n" + + "found : null\n" + + "required: A"); + } + + public void testScoping8() throws Exception { + testTypes("/** @constructor */function A() {}" + + "/** @constructor */function B() {" + + " /** @type !A */this.a = null;" + + "}", + "assignment to property a of B\n" + + "found : null\n" + + "required: A"); + } + + public void testScoping9() throws Exception { + testTypes("/** @constructor */function B() {" + + " /** @type !A */this.a = null;" + + "}" + + "/** @constructor */function A() {}", + "assignment to property a of B\n" + + "found : null\n" + + "required: A"); + } + + public void testScoping10() throws Exception { + TypeCheckResult p = parseAndTypeCheckWithScope("var a = function b(){};"); + + // a declared, b is not + assertTrue(p.scope.isDeclared("a", false)); + assertFalse(p.scope.isDeclared("b", false)); + + // checking that a has the correct assigned type + assertEquals("function (): undefined", + p.scope.getVar("a").getType().toString()); + } + + public void testScoping11() throws Exception { + // named anonymous functions create a binding in their body only + // the return is wrong but the assignment is OK since the type of b is ? + testTypes( + "/** @return {number} */var a = function b(){ return b };", + "inconsistent return type\n" + + "found : function (): number\n" + + "required: number"); + } + + public void testFunctionArguments1() throws Exception { + testFunctionType( + "/** @param {number} a\n@return {string} */" + + "function f(a) {}", + "function (number): string"); + } + + public void testFunctionArguments2() throws Exception { + testFunctionType( + "/** @param {number} opt_a\n@return {string} */" + + "function f(opt_a) {}", + "function (number=): string"); + } + + public void testFunctionArguments3() throws Exception { + testFunctionType( + "/** @param {number} b\n@return {string} */" + + "function f(a,b) {}", + "function (?, number): string"); + } + + public void testFunctionArguments4() throws Exception { + testFunctionType( + "/** @param {number} opt_a\n@return {string} */" + + "function f(a,opt_a) {}", + "function (?, number=): string"); + } + + public void testFunctionArguments5() throws Exception { + testTypes( + "function a(opt_a,a) {}", + "optional arguments must be at the end"); + } + + public void testFunctionArguments6() throws Exception { + testTypes( + "function a(var_args,a) {}", + "variable length argument must be last"); + } + + public void testFunctionArguments7() throws Exception { + testTypes( + "/** @param {number} opt_a\n@return {string} */" + + "function a(a,opt_a,var_args) {}"); + } + + public void testFunctionArguments8() throws Exception { + testTypes( + "function a(a,opt_a,var_args,b) {}", + "variable length argument must be last"); + } + + public void testFunctionArguments9() throws Exception { + // testing that only one error is reported + testTypes( + "function a(a,opt_a,var_args,b,c) {}", + "variable length argument must be last"); + } + + public void testFunctionArguments10() throws Exception { + // testing that only one error is reported + testTypes( + "function a(a,opt_a,b,c) {}", + "optional arguments must be at the end"); + } + + public void testFunctionArguments11() throws Exception { + testTypes( + "function a(a,opt_a,b,c,var_args,d) {}", + "optional arguments must be at the end"); + } + + public void testFunctionArguments12() throws Exception { + testTypes("/** @param foo {String} */function bar(baz){}", + "parameter foo does not appear in bar's parameter list"); + } + + public void testFunctionArguments13() throws Exception { + // verifying that the argument type have non-inferable types + testTypes( + "/** @return {boolean} */ function u() { return true; }" + + "/** @param {boolean} b\n@return {?boolean} */" + + "function f(b) { if (u()) { b = null; } return b; }", + "assignment\n" + + "found : null\n" + + "required: boolean"); + } + + public void testFunctionArguments14() throws Exception { + testTypes( + "/**\n" + + " * @param {string} x\n" + + " * @param {number} opt_y\n" + + " * @param {boolean} var_args\n" + + " */ function f(x, opt_y, var_args) {}" + + "f('3'); f('3', 2); f('3', 2, true); f('3', 2, true, false);"); + } + + public void testFunctionArguments15() throws Exception { + testTypes( + "/** @param {?function(*)} f */" + + "function g(f) { f(1, 2); }", + "Function f: called with 2 argument(s). " + + "Function requires at least 1 argument(s) " + + "and no more than 1 argument(s)."); + } + + public void testPrintFunctionName1() throws Exception { + // Ensures that the function name is pretty. + testTypes( + "var goog = {}; goog.run = function(f) {};" + + "goog.run();", + "Function goog.run: called with 0 argument(s). " + + "Function requires at least 1 argument(s) " + + "and no more than 1 argument(s)."); + } + + public void testPrintFunctionName2() throws Exception { + testTypes( + "/** @constructor */ var Foo = function() {}; " + + "Foo.prototype.run = function(f) {};" + + "(new Foo).run();", + "Function Foo.prototype.run: called with 0 argument(s). " + + "Function requires at least 1 argument(s) " + + "and no more than 1 argument(s)."); + } + + public void testFunctionInference1() throws Exception { + testFunctionType( + "function f(a) {}", + "function (?): undefined"); + } + + public void testFunctionInference2() throws Exception { + testFunctionType( + "function f(a,b) {}", + "function (?, ?): undefined"); + } + + public void testFunctionInference3() throws Exception { + testFunctionType( + "function f(var_args) {}", + "function (...[?]): undefined"); + } + + public void testFunctionInference4() throws Exception { + testFunctionType( + "function f(a,b,c,var_args) {}", + "function (?, ?, ?, ...[?]): undefined"); + } + + public void testFunctionInference5() throws Exception { + testFunctionType( + "/** @this Date\n@return {string} */function f(a) {}", + "function (this:Date, ?): string"); + } + + public void testFunctionInference6() throws Exception { + testFunctionType( + "/** @this Date\n@return {string} */function f(opt_a) {}", + "function (this:Date, ?=): string"); + } + + public void testFunctionInference7() throws Exception { + testFunctionType( + "/** @this Date */function f(a,b,c,var_args) {}", + "function (this:Date, ?, ?, ?, ...[?]): undefined"); + } + + public void testFunctionInference8() throws Exception { + testFunctionType( + "function f() {}", + "function (): undefined"); + } + + public void testFunctionInference9() throws Exception { + testFunctionType( + "var f = function() {};", + "function (): undefined"); + } + + public void testFunctionInference10() throws Exception { + testFunctionType( + "/** @this Date\n@param {boolean} b\n@return {string} */" + + "var f = function(a,b) {};", + "function (this:Date, ?, boolean): string"); + } + + public void testFunctionInference11() throws Exception { + testFunctionType( + "var goog = {};" + + "/** @return {number}*/goog.f = function(){};", + "goog.f", + "function (): number"); + } + + public void testFunctionInference12() throws Exception { + testFunctionType( + "var goog = {};" + + "goog.f = function(){};", + "goog.f", + "function (): undefined"); + } + + public void testFunctionInference13() throws Exception { + testFunctionType( + "var goog = {};" + + "/** @constructor */ goog.Foo = function(){};" + + "/** @param {!goog.Foo} f */function eatFoo(f){};", + "eatFoo", + "function (goog.Foo): undefined"); + } + + public void testFunctionInference14() throws Exception { + testFunctionType( + "var goog = {};" + + "/** @constructor */ goog.Foo = function(){};" + + "/** @return {!goog.Foo} */function eatFoo(){ return new goog.Foo; };", + "eatFoo", + "function (): goog.Foo"); + } + + public void testFunctionInference15() throws Exception { + testFunctionType( + "/** @constructor */ function f() {};" + + "f.prototype.foo = function(){};", + "f.prototype.foo", + "function (this:f): undefined"); + } + + public void testFunctionInference16() throws Exception { + testFunctionType( + "/** @constructor */ function f() {};" + + "f.prototype.foo = function(){};", + "(new f).foo", + "function (this:f): undefined"); + } + + public void testFunctionInference17() throws Exception { + testFunctionType( + "/** @constructor */ function f() {}" + + "function abstractMethod() {}" + + "/** @param {number} x */ f.prototype.foo = abstractMethod;", + "(new f).foo", + "function (this:f, number): ?"); + } + + public void testFunctionInference18() throws Exception { + testFunctionType( + "var goog = {};" + + "/** @this {Date} */ goog.eatWithDate;", + "goog.eatWithDate", + "function (this:Date): ?"); + } + + public void testFunctionInference19() throws Exception { + testFunctionType( + "/** @param {string} x */ var f;", + "f", + "function (string): ?"); + } + + public void testFunctionInference20() throws Exception { + testFunctionType( + "/** @this {Date} */ var f;", + "f", + "function (this:Date): ?"); + } + + public void testInnerFunction1() throws Exception { + testTypes( + "function f() {" + + " /** @type {number} */ var x = 3;\n" + + " function g() { x = null; }" + + " return x;" + + "}", + "assignment\n" + + "found : null\n" + + "required: number"); + } + + public void testInnerFunction2() throws Exception { + testTypes( + "/** @return {number} */\n" + + "function f() {" + + " var x = null;\n" + + " function g() { x = 3; }" + + " g();" + + " return x;" + + "}", + "inconsistent return type\n" + + "found : (null|number)\n" + + "required: number"); + } + + public void testInnerFunction3() throws Exception { + testTypes( + "var x = null;" + + "/** @return {number} */\n" + + "function f() {" + + " x = 3;\n" + + " /** @return {number} */\n" + + " function g() { x = true; return x; }" + + " return x;" + + "}", + "inconsistent return type\n" + + "found : boolean\n" + + "required: number"); + } + + public void testInnerFunction4() throws Exception { + testTypes( + "var x = null;" + + "/** @return {number} */\n" + + "function f() {" + + " x = '3';\n" + + " /** @return {number} */\n" + + " function g() { x = 3; return x; }" + + " return x;" + + "}", + "inconsistent return type\n" + + "found : string\n" + + "required: number"); + } + + public void testInnerFunction5() throws Exception { + testTypes( + "/** @return {number} */\n" + + "function f() {" + + " var x = 3;\n" + + " /** @return {number} */" + + " function g() { var x = 3;x = true; return x; }" + + " return x;" + + "}", + "inconsistent return type\n" + + "found : boolean\n" + + "required: number"); + } + + public void testInnerFunction6() throws Exception { + testClosureTypes( + CLOSURE_DEFS + + "function f() {" + + " var x = 0 || function() {};\n" + + " function g() { if (goog.isFunction(x)) { x(1); } }" + + " g();" + + "}", + "Function x: called with 1 argument(s). " + + "Function requires at least 0 argument(s) " + + "and no more than 0 argument(s)."); + } + + public void testInnerFunction7() throws Exception { + testClosureTypes( + CLOSURE_DEFS + + "function f() {" + + " /** @type {number|function()} */" + + " var x = 0 || function() {};\n" + + " function g() { if (goog.isFunction(x)) { x(1); } }" + + " g();" + + "}", + "Function x: called with 1 argument(s). " + + "Function requires at least 0 argument(s) " + + "and no more than 0 argument(s)."); + } + + public void testInnerFunction8() throws Exception { + testClosureTypes( + CLOSURE_DEFS + + "function f() {" + + " function x() {};\n" + + " function g() { if (goog.isFunction(x)) { x(1); } }" + + " g();" + + "}", + "Function x: called with 1 argument(s). " + + "Function requires at least 0 argument(s) " + + "and no more than 0 argument(s)."); + } + + public void testInnerFunction9() throws Exception { + testTypes( + "function f() {" + + " var x = 3;\n" + + " function g() { x = null; };\n" + + " function h() { return x == null; }" + + " return h();" + + "}"); + } + + public void testAbstractMethodHandling1() throws Exception { + testTypes( + "/** @type {Function} */ var abstractFn = function() {};" + + "abstractFn(1);"); + } + + public void testAbstractMethodHandling2() throws Exception { + testTypes( + "var abstractFn = function() {};" + + "abstractFn(1);", + "Function abstractFn: called with 1 argument(s). " + + "Function requires at least 0 argument(s) " + + "and no more than 0 argument(s)."); + } + + public void testAbstractMethodHandling3() throws Exception { + testTypes( + "var goog = {};" + + "/** @type {Function} */ goog.abstractFn = function() {};" + + "goog.abstractFn(1);"); + } + + public void testAbstractMethodHandling4() throws Exception { + testTypes( + "var goog = {};" + + "goog.abstractFn = function() {};" + + "goog.abstractFn(1);", + "Function goog.abstractFn: called with 1 argument(s). " + + "Function requires at least 0 argument(s) " + + "and no more than 0 argument(s)."); + } + + public void testAbstractMethodHandling5() throws Exception { + testTypes( + "/** @type {!Function} */ var abstractFn = function() {};" + + "/** @param {number} x */ var f = abstractFn;" + + "f('x');", + "actual parameter 1 of f does not match formal parameter\n" + + "found : string\n" + + "required: number"); + } + + public void testAbstractMethodHandling6() throws Exception { + testTypes( + "var goog = {};" + + "/** @type {Function} */ goog.abstractFn = function() {};" + + "/** @param {number} x */ goog.f = abstractFn;" + + "goog.f('x');", + "actual parameter 1 of goog.f does not match formal parameter\n" + + "found : string\n" + + "required: number"); + } + + public void testMethodInference1() throws Exception { + testTypes( + "/** @constructor */ function F() {}" + + "/** @return {number} */ F.prototype.foo = function() { return 3; };" + + "/** @constructor \n * @extends {F} */ " + + "function G() {}" + + "/** @override */ G.prototype.foo = function() { return true; };", + "inconsistent return type\n" + + "found : boolean\n" + + "required: number"); + } + + public void testMethodInference2() throws Exception { + testTypes( + "var goog = {};" + + "/** @constructor */ goog.F = function() {};" + + "/** @return {number} */ goog.F.prototype.foo = " + + " function() { return 3; };" + + "/** @constructor \n * @extends {goog.F} */ " + + "goog.G = function() {};" + + "/** @override */ goog.G.prototype.foo = function() { return true; };", + "inconsistent return type\n" + + "found : boolean\n" + + "required: number"); + } + + public void testMethodInference3() throws Exception { + testTypes( + "/** @constructor */ function F() {}" + + "/** @param {boolean} x \n * @return {number} */ " + + "F.prototype.foo = function(x) { return 3; };" + + "/** @constructor \n * @extends {F} */ " + + "function G() {}" + + "/** @override */ " + + "G.prototype.foo = function(x) { return x; };", + "inconsistent return type\n" + + "found : boolean\n" + + "required: number"); + } + + public void testMethodInference4() throws Exception { + testTypes( + "/** @constructor */ function F() {}" + + "/** @param {boolean} x \n * @return {number} */ " + + "F.prototype.foo = function(x) { return 3; };" + + "/** @constructor \n * @extends {F} */ " + + "function G() {}" + + "/** @override */ " + + "G.prototype.foo = function(y) { return y; };", + "inconsistent return type\n" + + "found : boolean\n" + + "required: number"); + } + + public void testMethodInference5() throws Exception { + testTypes( + "/** @constructor */ function F() {}" + + "/** @param {number} x \n * @return {string} */ " + + "F.prototype.foo = function(x) { return 'x'; };" + + "/** @constructor \n * @extends {F} */ " + + "function G() {}" + + "/** @type {number} */ G.prototype.num = 3;" + + "/** @override */ " + + "G.prototype.foo = function(y) { return this.num + y; };", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testMethodInference6() throws Exception { + testTypes( + "/** @constructor */ function F() {}" + + "/** @param {number} x */ F.prototype.foo = function(x) { };" + + "/** @constructor \n * @extends {F} */ " + + "function G() {}" + + "/** @override */ G.prototype.foo = function() { };" + + "(new G()).foo(1);"); + } + + public void testMethodInference7() throws Exception { + testTypes( + "/** @constructor */ function F() {}" + + "F.prototype.foo = function() { };" + + "/** @constructor \n * @extends {F} */ " + + "function G() {}" + + "/** @override */ G.prototype.foo = function(x, y) { };", + "mismatch of the foo property type and the type of the property " + + "it overrides from superclass F\n" + + "original: function (this:F): undefined\n" + + "override: function (this:G, ?, ?): undefined"); + } + + public void testMethodInference8() throws Exception { + testTypes( + "/** @constructor */ function F() {}" + + "F.prototype.foo = function() { };" + + "/** @constructor \n * @extends {F} */ " + + "function G() {}" + + "/** @override */ " + + "G.prototype.foo = function(opt_b, var_args) { };" + + "(new G()).foo(1, 2, 3);"); + } + + public void testMethodInference9() throws Exception { + testTypes( + "/** @constructor */ function F() {}" + + "F.prototype.foo = function() { };" + + "/** @constructor \n * @extends {F} */ " + + "function G() {}" + + "/** @override */ " + + "G.prototype.foo = function(var_args, opt_b) { };", + "variable length argument must be last"); + } + + public void testStaticMethodDeclaration1() throws Exception { + testTypes( + "/** @constructor */ function F() { F.foo(true); }" + + "/** @param {number} x */ F.foo = function(x) {};", + "actual parameter 1 of F.foo does not match formal parameter\n" + + "found : boolean\n" + + "required: number"); + } + + public void testStaticMethodDeclaration2() throws Exception { + testTypes( + "var goog = goog || {}; function f() { goog.foo(true); }" + + "/** @param {number} x */ goog.foo = function(x) {};", + "actual parameter 1 of goog.foo does not match formal parameter\n" + + "found : boolean\n" + + "required: number"); + } + + public void testStaticMethodDeclaration3() throws Exception { + testTypes( + "var goog = goog || {}; function f() { goog.foo(true); }" + + "goog.foo = function() {};", + "Function goog.foo: called with 1 argument(s). Function requires " + + "at least 0 argument(s) and no more than 0 argument(s)."); + } + + public void testDuplicateStaticMethodDecl1() throws Exception { + testTypes( + "var goog = goog || {};" + + "/** @param {number} x */ goog.foo = function(x) {};" + + "/** @param {number} x */ goog.foo = function(x) {};", + "variable goog.foo redefined with type function (number): undefined, " + + "original definition at [testcode]:1 " + + "with type function (number): undefined"); + } + + public void testDuplicateStaticMethodDecl2() throws Exception { + testTypes( + "var goog = goog || {};" + + "/** @param {number} x */ goog.foo = function(x) {};" + + "/** @param {number} x \n * @suppress {duplicate} */ " + + "goog.foo = function(x) {};"); + } + + public void testDuplicateStaticMethodDecl3() throws Exception { + testTypes( + "var goog = goog || {};" + + "goog.foo = function(x) {};" + + "goog.foo = function(x) {};"); + } + + public void testDuplicateStaticMethodDecl4() throws Exception { + testTypes( + "var goog = goog || {};" + + "/** @type {Function} */ goog.foo = function(x) {};" + + "goog.foo = function(x) {};"); + } + + public void testDuplicateStaticMethodDecl5() throws Exception { + testTypes( + "var goog = goog || {};" + + "goog.foo = function(x) {};" + + "/** @return {undefined} */ goog.foo = function(x) {};", + "variable goog.foo redefined with type function (?): undefined, " + + "original definition at [testcode]:1 with type " + + "function (?): undefined"); + } + + public void testDuplicateStaticPropertyDecl1() throws Exception { + testTypes( + "var goog = goog || {};" + + "/** @type {Foo} */ goog.foo;" + + "/** @type {Foo} */ goog.foo;" + + "/** @constructor */ function Foo() {}"); + } + + public void testDuplicateStaticPropertyDecl2() throws Exception { + testTypes( + "var goog = goog || {};" + + "/** @type {Foo} */ goog.foo;" + + "/** @type {Foo} \n * @suppress {duplicate} */ goog.foo;" + + "/** @constructor */ function Foo() {}"); + } + + public void testDuplicateStaticPropertyDecl3() throws Exception { + testTypes( + "var goog = goog || {};" + + "/** @type {!Foo} */ goog.foo;" + + "/** @type {string} */ goog.foo;" + + "/** @constructor */ function Foo() {}", + "variable goog.foo redefined with type string, " + + "original definition at [testcode]:1 with type Foo"); + } + + public void testDuplicateStaticPropertyDecl4() throws Exception { + testClosureTypesMultipleWarnings( + "var goog = goog || {};" + + "/** @type {!Foo} */ goog.foo;" + + "/** @type {string} */ goog.foo = 'x';" + + "/** @constructor */ function Foo() {}", + Lists.newArrayList( + "assignment to property foo of goog\n" + + "found : string\n" + + "required: Foo", + "variable goog.foo redefined with type string, " + + "original definition at [testcode]:1 with type Foo")); + } + + public void testDuplicateStaticPropertyDecl5() throws Exception { + testClosureTypesMultipleWarnings( + "var goog = goog || {};" + + "/** @type {!Foo} */ goog.foo;" + + "/** @type {string}\n * @suppress {duplicate} */ goog.foo = 'x';" + + "/** @constructor */ function Foo() {}", + Lists.newArrayList( + "assignment to property foo of goog\n" + + "found : string\n" + + "required: Foo", + "variable goog.foo redefined with type string, " + + "original definition at [testcode]:1 with type Foo")); + } + + public void testDuplicateStaticPropertyDecl6() throws Exception { + testTypes( + "var goog = goog || {};" + + "/** @type {string} */ goog.foo = 'y';" + + "/** @type {string}\n * @suppress {duplicate} */ goog.foo = 'x';"); + } + + public void testDuplicateStaticPropertyDecl7() throws Exception { + testTypes( + "var goog = goog || {};" + + "/** @param {string} x */ goog.foo;" + + "/** @type {function(string)} */ goog.foo;"); + } + + public void testDuplicateStaticPropertyDecl8() throws Exception { + testTypes( + "var goog = goog || {};" + + "/** @return {EventCopy} */ goog.foo;" + + "/** @constructor */ function EventCopy() {}" + + "/** @return {EventCopy} */ goog.foo;"); + } + + public void testDuplicateStaticPropertyDecl9() throws Exception { + testTypes( + "var goog = goog || {};" + + "/** @return {EventCopy} */ goog.foo;" + + "/** @return {EventCopy} */ goog.foo;" + + "/** @constructor */ function EventCopy() {}"); + } + + public void testDuplicateLocalVarDecl() throws Exception { + testClosureTypesMultipleWarnings( + "/** @param {number} x */\n" + + "function f(x) { /** @type {string} */ var x = ''; }", + Lists.newArrayList( + "variable x redefined with type string, original definition" + + " at [testcode]:2 with type number", + "initializing variable\n" + + "found : string\n" + + "required: number")); + } + + public void testStubFunctionDeclaration1() throws Exception { + testFunctionType( + "/** @constructor */ function f() {};" + + "/** @param {number} x \n * @param {string} y \n" + + " * @return {number} */ f.prototype.foo;", + "(new f).foo", + "function (this:f, number, string): number"); + } + + public void testStubFunctionDeclaration2() throws Exception { + testExternFunctionType( + // externs + "/** @constructor */ function f() {};" + + "/** @constructor \n * @extends {f} */ f.subclass;", + "f.subclass", + "function (new:f.subclass): ?"); + } + + public void testStubFunctionDeclaration3() throws Exception { + testFunctionType( + "/** @constructor */ function f() {};" + + "/** @return {undefined} */ f.foo;", + "f.foo", + "function (): undefined"); + } + + public void testStubFunctionDeclaration4() throws Exception { + testFunctionType( + "/** @constructor */ function f() { " + + " /** @return {number} */ this.foo;" + + "}", + "(new f).foo", + "function (this:f): number"); + } + + public void testStubFunctionDeclaration5() throws Exception { + testFunctionType( + "/** @constructor */ function f() { " + + " /** @type {Function} */ this.foo;" + + "}", + "(new f).foo", + createOptionalType(createNullableType(U2U_CONSTRUCTOR_TYPE)) + .toString()); + } + + public void testStubFunctionDeclaration6() throws Exception { + testFunctionType( + "/** @constructor */ function f() {} " + + "/** @type {Function} */ f.prototype.foo;", + "(new f).foo", + createOptionalType(createNullableType(U2U_CONSTRUCTOR_TYPE)) + .toString()); + } + + public void testStubFunctionDeclaration7() throws Exception { + testFunctionType( + "/** @constructor */ function f() {} " + + "/** @type {Function} */ f.prototype.foo = function() {};", + "(new f).foo", + createOptionalType(createNullableType(U2U_CONSTRUCTOR_TYPE)) + .toString()); + } + + public void testStubFunctionDeclaration8() throws Exception { + testFunctionType( + "/** @type {Function} */ var f = function() {}; ", + "f", + createOptionalType(createNullableType(U2U_CONSTRUCTOR_TYPE)) + .toString()); + } + + public void testStubFunctionDeclaration9() throws Exception { + testFunctionType( + "/** @type {function():number} */ var f; ", + "f", + "function (): number"); + } + + public void testStubFunctionDeclaration10() throws Exception { + testFunctionType( + "/** @type {function(number):number} */ var f = function(x) {};", + "f", + "function (number): number"); + } + + public void testNestedFunctionInference1() throws Exception { + String nestedAssignOfFooAndBar = + "/** @constructor */ function f() {};" + + "f.prototype.foo = f.prototype.bar = function(){};"; + testFunctionType(nestedAssignOfFooAndBar, "(new f).bar", + "function (this:f): undefined"); + } + + /** + * Tests the type of a function definition. The function defined by + * {@code functionDef} should be named {@code "f"}. + */ + private void testFunctionType(String functionDef, String functionType) + throws Exception { + testFunctionType(functionDef, "f", functionType); + } + + /** + * Tests the type of a function definition. The function defined by + * {@code functionDef} should be named {@code functionName}. + */ + private void testFunctionType(String functionDef, String functionName, + String functionType) throws Exception { + // using the variable initialization check to verify the function's type + testTypes( + functionDef + + "/** @type number */var a=" + functionName + ";", + "initializing variable\n" + + "found : " + functionType + "\n" + + "required: number"); + } + + /** + * Tests the type of a function definition in externs. + * The function defined by {@code functionDef} should be + * named {@code functionName}. + */ + private void testExternFunctionType(String functionDef, String functionName, + String functionType) throws Exception { + testTypes( + functionDef, + "/** @type number */var a=" + functionName + ";", + "initializing variable\n" + + "found : " + functionType + "\n" + + "required: number", false); + } + + public void testTypeRedefinition() throws Exception { + testClosureTypesMultipleWarnings( + "a={};/**@enum {string}*/ a.A = {ZOR:'b'};" + + "/** @constructor */ a.A = function() {}", + Lists.newArrayList( + "variable a.A redefined with type function (new:a.A): undefined, " + + "original definition at [testcode]:1 with type enum{a.A}", + "assignment to property A of a\n" + + "found : function (new:a.A): undefined\n" + + "required: enum{a.A}")); + } + + public void testIn1() throws Exception { + testTypes("'foo' in Object"); + } + + public void testIn2() throws Exception { + testTypes("3 in Object"); + } + + public void testIn3() throws Exception { + testTypes("undefined in Object"); + } + + public void testIn4() throws Exception { + testTypes("Date in Object", + "left side of 'in'\n" + + "found : function (new:Date, ?=, ?=, ?=, ?=, ?=, ?=, ?=): string\n" + + "required: string"); + } + + public void testIn5() throws Exception { + testTypes("'x' in null", + "'in' requires an object\n" + + "found : null\n" + + "required: Object"); + } + + public void testIn6() throws Exception { + testTypes( + "/** @param {number} x */" + + "function g(x) {}" + + "g(1 in {});", + "actual parameter 1 of g does not match formal parameter\n" + + "found : boolean\n" + + "required: number"); + } + + public void testIn7() throws Exception { + // Make sure we do inference in the 'in' expression. + testTypes( + "/**\n" + + " * @param {number} x\n" + + " * @return {number}\n" + + " */\n" + + "function g(x) { return 5; }" + + "function f() {" + + " var x = {};" + + " x.foo = '3';" + + " return g(x.foo) in {};" + + "}", + "actual parameter 1 of g does not match formal parameter\n" + + "found : string\n" + + "required: number"); + } + + // TODO(nicksantos): change this to something that makes sense. +// public void testComparison1() throws Exception { +// testTypes("/**@type null */var a;" + +// "/**@type !Date */var b;" + +// "if (a==b) {}", +// "condition always evaluates to false\n" + +// "left : null\n" + +// "right: Date"); +// } + + public void testComparison2() throws Exception { + testTypes("/**@type number*/var a;" + + "/**@type !Date */var b;" + + "if (a!==b) {}", + "condition always evaluates to true\n" + + "left : number\n" + + "right: Date"); + } + + public void testComparison3() throws Exception { + // Since null == undefined in JavaScript, this code is reasonable. + testTypes("/** @type {(Object,undefined)} */var a;" + + "var b = a == null"); + } + + public void testComparison4() throws Exception { + testTypes("/** @type {(!Object,undefined)} */var a;" + + "/** @type {!Object} */var b;" + + "var c = a == b"); + } + + public void testComparison5() throws Exception { + testTypes("/** @type null */var a;" + + "/** @type null */var b;" + + "a == b", + "condition always evaluates to true\n" + + "left : null\n" + + "right: null"); + } + + public void testComparison6() throws Exception { + testTypes("/** @type null */var a;" + + "/** @type null */var b;" + + "a != b", + "condition always evaluates to false\n" + + "left : null\n" + + "right: null"); + } + + public void testComparison7() throws Exception { + testTypes("var a;" + + "var b;" + + "a == b", + "condition always evaluates to true\n" + + "left : undefined\n" + + "right: undefined"); + } + + public void testComparison8() throws Exception { + testTypes("/** @type {Array.} */ var a = [];" + + "a[0] == null || a[1] == undefined"); + } + + public void testComparison9() throws Exception { + testTypes("/** @type {Array.} */ var a = [];" + + "a[0] == null", + "condition always evaluates to true\n" + + "left : undefined\n" + + "right: null"); + } + + public void testComparison10() throws Exception { + testTypes("/** @type {Array.} */ var a = [];" + + "a[0] === null"); + } + + public void testEnumStaticMethod1() throws Exception { + testTypes( + "/** @enum */ var Foo = {AAA: 1};" + + "/** @param {number} x */ Foo.method = function(x) {};" + + "Foo.method(true);", + "actual parameter 1 of Foo.method does not match formal parameter\n" + + "found : boolean\n" + + "required: number"); + } + + public void testEnumStaticMethod2() throws Exception { + testTypes( + "/** @enum */ var Foo = {AAA: 1};" + + "/** @param {number} x */ Foo.method = function(x) {};" + + "function f() { Foo.method(true); }", + "actual parameter 1 of Foo.method does not match formal parameter\n" + + "found : boolean\n" + + "required: number"); + } + + public void testEnum1() throws Exception { + testTypes("/**@enum*/var a={BB:1,CC:2};\n" + + "/**@type {a}*/var d;d=a.BB;"); + } + + public void testEnum2() throws Exception { + testTypes("/**@enum*/var a={b:1}", + "enum key b must be a syntactic constant"); + } + + public void testEnum3() throws Exception { + testTypes("/**@enum*/var a={BB:1,BB:2}", + "variable a.BB redefined with type a., " + + "original definition at [testcode]:1 with type a."); + } + + public void testEnum4() throws Exception { + testTypes("/**@enum*/var a={BB:'string'}", + "assignment to property BB of enum{a}\n" + + "found : string\n" + + "required: number"); + } + + public void testEnum5() throws Exception { + testTypes("/**@enum {String}*/var a={BB:'string'}", + "assignment to property BB of enum{a}\n" + + "found : string\n" + + "required: (String|null|undefined)"); + } + + public void testEnum6() throws Exception { + testTypes("/**@enum*/var a={BB:1,CC:2};\n/**@type {!Array}*/var d;d=a.BB;", + "assignment\n" + + "found : a.\n" + + "required: Array"); + } + + public void testEnum7() throws Exception { + testTypes("/** @enum */var a={AA:1,BB:2,CC:3};" + + "/** @type a */var b=a.D;", + "element D does not exist on this enum"); + } + + public void testEnum8() throws Exception { + testClosureTypesMultipleWarnings("/** @enum */var a=8;", + Lists.newArrayList( + "enum initializer must be an object literal or an enum", + "initializing variable\n" + + "found : number\n" + + "required: enum{a}")); + } + + public void testEnum9() throws Exception { + testClosureTypesMultipleWarnings( + "var goog = {};" + + "/** @enum */goog.a=8;", + Lists.newArrayList( + "assignment to property a of goog\n" + + "found : number\n" + + "required: enum{goog.a}", + "enum initializer must be an object literal or an enum")); + } + + public void testEnum10() throws Exception { + testTypes( + "/** @enum {number} */" + + "goog.K = { A : 3 };"); + } + + public void testEnum11() throws Exception { + testTypes( + "/** @enum {number} */" + + "goog.K = { 502 : 3 };"); + } + + public void testEnum12() throws Exception { + testTypes( + "/** @enum {number} */ var a = {};" + + "/** @enum */ var b = a;"); + } + + public void testEnum13() throws Exception { + testTypes( + "/** @enum {number} */ var a = {};" + + "/** @enum {string} */ var b = a;", + "incompatible enum element types\n" + + "found : number\n" + + "required: string"); + } + + public void testEnum14() throws Exception { + testTypes( + "/** @enum {number} */ var a = {FOO:5};" + + "/** @enum */ var b = a;" + + "var c = b.FOO;"); + } + + public void testEnum15() throws Exception { + testTypes( + "/** @enum {number} */ var a = {FOO:5};" + + "/** @enum */ var b = a;" + + "var c = b.BAR;", + "element BAR does not exist on this enum"); + } + + public void testEnum16() throws Exception { + testTypes("var goog = {};" + + "/**@enum*/goog .a={BB:1,BB:2}", + "variable goog.a.BB redefined with type goog.a., " + + "original definition at [testcode]:1 with type goog.a."); + } + + public void testEnum17() throws Exception { + testTypes("var goog = {};" + + "/**@enum*/goog.a={BB:'string'}", + "assignment to property BB of enum{goog.a}\n" + + "found : string\n" + + "required: number"); + } + + public void testEnum18() throws Exception { + testTypes("/**@enum*/ var E = {A: 1, B: 2};" + + "/** @param {!E} x\n@return {number} */\n" + + "var f = function(x) { return x; };"); + } + + public void testEnum19() throws Exception { + testTypes("/**@enum*/ var E = {A: 1, B: 2};" + + "/** @param {number} x\n@return {!E} */\n" + + "var f = function(x) { return x; };", + "inconsistent return type\n" + + "found : number\n" + + "required: E."); + } + + public void testEnum20() throws Exception { + testTypes("/**@enum*/ var E = {A: 1, B: 2}; var x = []; x[E.A] = 0;"); + } + + public void testEnum21() throws Exception { + Node n = parseAndTypeCheck( + "/** @enum {string} */ var E = {A : 'a', B : 'b'};\n" + + "/** @param {!E} x\n@return {!E} */ function f(x) { return x; }"); + Node nodeX = n.getLastChild().getLastChild().getLastChild().getLastChild(); + JSType typeE = nodeX.getJSType(); + assertFalse(typeE.isObject()); + assertFalse(typeE.isNullable()); + } + + public void testEnum22() throws Exception { + testTypes("/**@enum*/ var E = {A: 1, B: 2};" + + "/** @param {E} x \n* @return {number} */ function f(x) {return x}"); + } + + public void testEnum23() throws Exception { + testTypes("/**@enum*/ var E = {A: 1, B: 2};" + + "/** @param {E} x \n* @return {string} */ function f(x) {return x}", + "inconsistent return type\n" + + "found : E.\n" + + "required: string"); + } + + public void testEnum24() throws Exception { + testTypes("/**@enum {Object} */ var E = {A: {}};" + + "/** @param {E} x \n* @return {!Object} */ function f(x) {return x}", + "inconsistent return type\n" + + "found : E.<(Object|null|undefined)>\n" + + "required: Object"); + } + + public void testEnum25() throws Exception { + testTypes("/**@enum {!Object} */ var E = {A: {}};" + + "/** @param {E} x \n* @return {!Object} */ function f(x) {return x}"); + } + + public void testEnum26() throws Exception { + testTypes("var a = {}; /**@enum*/ a.B = {A: 1, B: 2};" + + "/** @param {a.B} x \n* @return {number} */ function f(x) {return x}"); + } + + public void testEnum27() throws Exception { + // x is unknown + testTypes("/** @enum */ var A = {B: 1, C: 2}; " + + "function f(x) { return A == x; }"); + } + + public void testEnum28() throws Exception { + // x is unknown + testTypes("/** @enum */ var A = {B: 1, C: 2}; " + + "function f(x) { return A.B == x; }"); + } + + public void testEnum29() throws Exception { + testTypes("/** @enum */ var A = {B: 1, C: 2}; " + + "/** @return {number} */ function f() { return A; }", + "inconsistent return type\n" + + "found : enum{A}\n" + + "required: number"); + } + + public void testEnum30() throws Exception { + testTypes("/** @enum */ var A = {B: 1, C: 2}; " + + "/** @return {number} */ function f() { return A.B; }"); + } + + public void testEnum31() throws Exception { + testTypes("/** @enum */ var A = {B: 1, C: 2}; " + + "/** @return {A} */ function f() { return A; }", + "inconsistent return type\n" + + "found : enum{A}\n" + + "required: A."); + } + + public void testEnum32() throws Exception { + testTypes("/** @enum */ var A = {B: 1, C: 2}; " + + "/** @return {A} */ function f() { return A.B; }"); + } + + public void testEnum34() throws Exception { + testTypes("/** @enum */ var A = {B: 1, C: 2}; " + + "/** @param {number} x */ function f(x) { return x == A.B; }"); + } + + public void testEnum35() throws Exception { + testTypes("var a = a || {}; /** @enum */ a.b = {C: 1, D: 2};" + + "/** @return {a.b} */ function f() { return a.b.C; }"); + } + + public void testEnum36() throws Exception { + testTypes("var a = a || {}; /** @enum */ a.b = {C: 1, D: 2};" + + "/** @return {!a.b} */ function f() { return 1; }", + "inconsistent return type\n" + + "found : number\n" + + "required: a.b."); + } + + public void testEnum37() throws Exception { + testTypes( + "var goog = goog || {};" + + "/** @enum {number} */ goog.a = {};" + + "/** @enum */ var b = goog.a;"); + } + + public void testEnum38() throws Exception { + testTypes( + "/** @enum {MyEnum} */ var MyEnum = {};" + + "/** @param {MyEnum} x */ function f(x) {}", + "Parse error. Cycle detected in inheritance chain " + + "of type MyEnum"); + } + + public void testEnum39() throws Exception { + testTypes( + "/** @enum {Number} */ var MyEnum = {FOO: new Number(1)};" + + "/** @param {MyEnum} x \n * @return {number} */" + + "function f(x) { return x == MyEnum.FOO && MyEnum.FOO == x; }", + "inconsistent return type\n" + + "found : boolean\n" + + "required: number"); + } + + public void testEnum40() throws Exception { + testTypes( + "/** @enum {Number} */ var MyEnum = {FOO: new Number(1)};" + + "/** @param {number} x \n * @return {number} */" + + "function f(x) { return x == MyEnum.FOO && MyEnum.FOO == x; }", + "inconsistent return type\n" + + "found : boolean\n" + + "required: number"); + } + + public void testAliasedEnum1() throws Exception { + testTypes( + "/** @enum */ var YourEnum = {FOO: 3};" + + "/** @enum */ var MyEnum = YourEnum;" + + "/** @param {MyEnum} x */ function f(x) {} f(MyEnum.FOO);"); + } + + public void testAliasedEnum2() throws Exception { + testTypes( + "/** @enum */ var YourEnum = {FOO: 3};" + + "/** @enum */ var MyEnum = YourEnum;" + + "/** @param {YourEnum} x */ function f(x) {} f(MyEnum.FOO);"); + } + + public void testAliasedEnum3() throws Exception { + testTypes( + "/** @enum */ var YourEnum = {FOO: 3};" + + "/** @enum */ var MyEnum = YourEnum;" + + "/** @param {MyEnum} x */ function f(x) {} f(YourEnum.FOO);"); + } + + public void testAliasedEnum4() throws Exception { + testTypes( + "/** @enum */ var YourEnum = {FOO: 3};" + + "/** @enum */ var MyEnum = YourEnum;" + + "/** @param {YourEnum} x */ function f(x) {} f(YourEnum.FOO);"); + } + + public void testAliasedEnum5() throws Exception { + testTypes( + "/** @enum */ var YourEnum = {FOO: 3};" + + "/** @enum */ var MyEnum = YourEnum;" + + "/** @param {string} x */ function f(x) {} f(MyEnum.FOO);", + "actual parameter 1 of f does not match formal parameter\n" + + "found : YourEnum.\n" + + "required: string"); + } + + public void testBackwardsEnumUse1() throws Exception { + testTypes( + "/** @return {string} */ function f() { return MyEnum.FOO; }" + + "/** @enum {string} */ var MyEnum = {FOO: 'x'};"); + } + + public void testBackwardsEnumUse2() throws Exception { + testTypes( + "/** @return {number} */ function f() { return MyEnum.FOO; }" + + "/** @enum {string} */ var MyEnum = {FOO: 'x'};", + "inconsistent return type\n" + + "found : MyEnum.\n" + + "required: number"); + } + + public void testBackwardsEnumUse3() throws Exception { + testTypes( + "/** @return {string} */ function f() { return MyEnum.FOO; }" + + "/** @enum {string} */ var YourEnum = {FOO: 'x'};" + + "/** @enum {string} */ var MyEnum = YourEnum;"); + } + + public void testBackwardsEnumUse4() throws Exception { + testTypes( + "/** @return {number} */ function f() { return MyEnum.FOO; }" + + "/** @enum {string} */ var YourEnum = {FOO: 'x'};" + + "/** @enum {string} */ var MyEnum = YourEnum;", + "inconsistent return type\n" + + "found : YourEnum.\n" + + "required: number"); + } + + public void testBackwardsEnumUse5() throws Exception { + testTypes( + "/** @return {string} */ function f() { return MyEnum.BAR; }" + + "/** @enum {string} */ var YourEnum = {FOO: 'x'};" + + "/** @enum {string} */ var MyEnum = YourEnum;", + "element BAR does not exist on this enum"); + } + + public void testBackwardsConstructor1() throws Exception { + testTypes( + "function f() { (new Foo(true)); }" + + "/** \n * @constructor \n * @param {number} x */" + + "var Foo = function(x) {};", + "actual parameter 1 of Foo does not match formal parameter\n" + + "found : boolean\n" + + "required: number"); + } + + public void testBackwardsConstructor2() throws Exception { + testTypes( + "function f() { (new Foo(true)); }" + + "/** \n * @constructor \n * @param {number} x */" + + "var YourFoo = function(x) {};" + + "/** \n * @constructor \n * @param {number} x */" + + "var Foo = YourFoo;", + "actual parameter 1 of Foo does not match formal parameter\n" + + "found : boolean\n" + + "required: number"); + } + + public void testMinimalConstructorAnnotation() throws Exception { + testTypes("/** @constructor */function Foo(){}"); + } + + public void testGoodExtends1() throws Exception { + // A minimal @extends example + testTypes("/** @constructor */function base() {}\n" + + "/** @constructor\n * @extends {base} */function derived() {}\n"); + } + + public void testGoodExtends2() throws Exception { + testTypes("/** @constructor\n * @extends base */function derived() {}\n" + + "/** @constructor */function base() {}\n"); + } + + public void testGoodExtends3() throws Exception { + testTypes("/** @constructor\n * @extends {Object} */function base() {}\n" + + "/** @constructor\n * @extends {base} */function derived() {}\n"); + } + + public void testGoodExtends4() throws Exception { + // Ensure that @extends actually sets the base type of a constructor + // correctly. Because this isn't part of the human-readable Function + // definition, we need to crawl the prototype chain (eww). + Node n = parseAndTypeCheck( + "var goog = {};\n" + + "/** @constructor */goog.Base = function(){};\n" + + "/** @constructor\n" + + " * @extends {goog.Base} */goog.Derived = function(){};\n"); + Node subTypeName = n.getLastChild().getLastChild().getFirstChild(); + assertEquals("goog.Derived", subTypeName.getQualifiedName()); + + FunctionType subCtorType = + (FunctionType) subTypeName.getNext().getJSType(); + assertEquals("goog.Derived", subCtorType.getInstanceType().toString()); + + JSType superType = subCtorType.getPrototype().getImplicitPrototype(); + assertEquals("goog.Base", superType.toString()); + } + + public void testGoodExtends5() throws Exception { + // we allow for the extends annotation to be placed first + testTypes("/** @constructor */function base() {}\n" + + "/** @extends {base}\n * @constructor */function derived() {}\n"); + } + + public void testGoodExtends6() throws Exception { + testFunctionType( + CLOSURE_DEFS + + "/** @constructor */function base() {}\n" + + "/** @return {number} */ " + + " base.prototype.foo = function() { return 1; };\n" + + "/** @extends {base}\n * @constructor */function derived() {}\n" + + "goog.inherits(derived, base);", + "derived.superClass_.foo", + "function (this:base): number"); + } + + public void testGoodExtends7() throws Exception { + testFunctionType( + "Function.prototype.inherits = function(x) {};" + + "/** @constructor */function base() {}\n" + + "/** @extends {base}\n * @constructor */function derived() {}\n" + + "derived.inherits(base);", + "(new derived).constructor", + "function (new:derived, ...[?]): ?"); + } + + public void testGoodExtends8() throws Exception { + testTypes("/** @constructor \n @extends {Base} */ function Sub() {}" + + "/** @return {number} */ function f() { return (new Sub()).foo; }" + + "/** @constructor */ function Base() {}" + + "/** @type {boolean} */ Base.prototype.foo = true;", + "inconsistent return type\n" + + "found : boolean\n" + + "required: number"); + } + + public void testGoodExtends9() throws Exception { + testTypes( + "/** @constructor */ function Super() {}" + + "Super.prototype.foo = function() {};" + + "/** @constructor \n * @extends {Super} */ function Sub() {}" + + "Sub.prototype = new Super();" + + "/** @override */ Sub.prototype.foo = function() {};"); + } + + public void testGoodExtends10() throws Exception { + testTypes( + "/** @constructor */ function Super() {}" + + "/** @constructor \n * @extends {Super} */ function Sub() {}" + + "Sub.prototype = new Super();" + + "/** @return {Super} */ function foo() { return new Sub(); }"); + } + + public void testGoodExtends11() throws Exception { + testTypes( + "/** @constructor */ function Super() {}" + + "/** @param {boolean} x */ Super.prototype.foo = function(x) {};" + + "/** @constructor \n * @extends {Super} */ function Sub() {}" + + "Sub.prototype = new Super();" + + "(new Sub()).foo(0);", + "actual parameter 1 of Super.prototype.foo " + + "does not match formal parameter\n" + + "found : number\n" + + "required: boolean"); + } + + public void testBadExtends1() throws Exception { + testTypes("/** @constructor */function base() {}\n" + + "/** @constructor\n * @extends {not_base} */function derived() {}\n", + "Bad type annotation. Unknown type not_base"); + } + + public void testBadExtends2() throws Exception { + testTypes("/** @constructor */function base() {\n" + + "/** @type {!Number}*/\n" + + "this.baseMember = new Number(4);\n" + + "}\n" + + "/** @constructor\n" + + " * @extends {base} */function derived() {}\n" + + "/** @param {!String} x*/\n" + + "function foo(x){ }\n" + + "/** @type {!derived}*/var y;\n" + + "foo(y.baseMember);\n", + "actual parameter 1 of foo does not match formal parameter\n" + + "found : Number\n" + + "required: String"); + } + + public void testBadExtends3() throws Exception { + testTypes("/** @extends {Object} */function base() {}", + "@extends used without @constructor or @interface for base"); + } + + public void testLateExtends() throws Exception { + testTypes( + CLOSURE_DEFS + + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.foo = function() {};\n" + + "/** @constructor */function Bar() {}\n" + + "goog.inherits(Foo, Bar);\n", + "Missing @extends tag on type Foo"); + } + + public void testSuperclassMatch() throws Exception { + compiler.getOptions().setCodingConvention(new GoogleCodingConvention()); + testTypes("/** @constructor */ var Foo = function() {};\n" + + "/** @constructor \n @extends Foo */ var Bar = function() {};\n" + + "Bar.inherits = function(x){};" + + "Bar.inherits(Foo);\n"); + } + + public void testSuperclassMatchWithMixin() throws Exception { + compiler.getOptions().setCodingConvention(new GoogleCodingConvention()); + testTypes("/** @constructor */ var Foo = function() {};\n" + + "/** @constructor */ var Baz = function() {};\n" + + "/** @constructor \n @extends Foo */ var Bar = function() {};\n" + + "Bar.inherits = function(x){};" + + "Bar.mixin = function(y){};" + + "Bar.inherits(Foo);\n" + + "Bar.mixin(Baz);\n"); + } + + public void testSuperclassMismatch1() throws Exception { + compiler.getOptions().setCodingConvention(new GoogleCodingConvention()); + testTypes("/** @constructor */ var Foo = function() {};\n" + + "/** @constructor \n @extends Object */ var Bar = function() {};\n" + + "Bar.inherits = function(x){};" + + "Bar.inherits(Foo);\n", + "Missing @extends tag on type Bar"); + } + + public void testSuperclassMismatch2() throws Exception { + compiler.getOptions().setCodingConvention(new GoogleCodingConvention()); + testTypes("/** @constructor */ var Foo = function(){};\n" + + "/** @constructor */ var Bar = function(){};\n" + + "Bar.inherits = function(x){};" + + "Bar.inherits(Foo);", + "Missing @extends tag on type Bar"); + } + + public void testSuperClassDefinedAfterSubClass1() throws Exception { + testTypes( + "/** @constructor \n * @extends {Base} */ function A() {}" + + "/** @constructor \n * @extends {Base} */ function B() {}" + + "/** @constructor */ function Base() {}" + + "/** @param {A|B} x \n * @return {B|A} */ " + + "function foo(x) { return x; }"); + } + + public void testSuperClassDefinedAfterSubClass2() throws Exception { + testTypes( + "/** @constructor \n * @extends {Base} */ function A() {}" + + "/** @constructor \n * @extends {Base} */ function B() {}" + + "/** @param {A|B} x \n * @return {B|A} */ " + + "function foo(x) { return x; }" + + "/** @constructor */ function Base() {}"); + } + + public void testDirectPrototypeAssignment1() throws Exception { + testTypes( + "/** @constructor */ function Base() {}" + + "Base.prototype.foo = 3;" + + "/** @constructor \n * @extends {Base} */ function A() {}" + + "A.prototype = new Base();" + + "/** @return {string} */ function foo() { return (new A).foo; }", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testDirectPrototypeAssignment2() throws Exception { + // This ensures that we don't attach property 'foo' onto the Base + // instance object. + testTypes( + "/** @constructor */ function Base() {}" + + "/** @constructor \n * @extends {Base} */ function A() {}" + + "A.prototype = new Base();" + + "A.prototype.foo = 3;" + + "/** @return {string} */ function foo() { return (new Base).foo; }"); + } + + public void testGoodImplements1() throws Exception { + testTypes("/** @interface */function Disposable() {}\n" + + "/** @implements {Disposable}\n * @constructor */function f() {}"); + } + + public void testGoodImplements2() throws Exception { + testTypes("/** @interface */function Base1() {}\n" + + "/** @interface */function Base2() {}\n" + + "/** @constructor\n" + + " * @implements {Base1}\n" + + " * @implements {Base2}\n" + + " */ function derived() {}"); + } + + public void testBadImplements1() throws Exception { + testTypes("/** @interface */function Base1() {}\n" + + "/** @interface */function Base2() {}\n" + + "/** @constructor\n" + + " * @implements {nonExistent}\n" + + " * @implements {Base2}\n" + + " */ function derived() {}", + "Bad type annotation. Unknown type nonExistent"); + } + + public void testBadImplements2() throws Exception { + testTypes("/** @interface */function Disposable() {}\n" + + "/** @implements {Disposable}\n */function f() {}", + "@implements used without @constructor for f"); + } + + public void testBadImplements3() throws Exception { + testTypes("/** @interface */function Disposable() {}\n" + + "/** @implements {Disposable}\n * @interface */function f() {}", + "f cannot implement this type; an interface can only extend, " + + "but not implement interfaces"); + } + + public void testInterfaceExtends() throws Exception { + testTypes("/** @interface */function A() {}\n" + + "/** @interface \n * @extends {A} */function B() {}\n" + + "/** @constructor\n" + + " * @implements {B}\n" + + " */ function derived() {}"); + } + + public void testBadInterfaceExtends1() throws Exception { + testTypes("/** @interface \n * @extends {nonExistent} */function A() {}", + "Bad type annotation. Unknown type nonExistent"); + } + + public void testBadInterfaceExtends2() throws Exception { + testTypes("/** @constructor */function A() {}\n" + + "/** @interface \n * @extends {A} */function B() {}", + "B cannot extend this type; interfaces can only extend interfaces"); + } + + public void testBadInterfaceExtends3() throws Exception { + testTypes("/** @interface */function A() {}\n" + + "/** @constructor \n * @extends {A} */function B() {}", + "B cannot extend this type; constructors can only extend constructors"); + } + + public void testBadInterfaceExtends4() throws Exception { + // TODO(user): This should be detected as an error. Even if we enforce + // that A cannot be used in the assignment, we should still detect the + // inheritance chain as invalid. + testTypes("/** @interface */function A() {}\n" + + "/** @constructor */function B() {}\n" + + "B.prototype = A;"); + } + + public void testBadInterfaceExtends5() throws Exception { + // TODO(user): This should be detected as an error. Even if we enforce + // that A cannot be used in the assignment, we should still detect the + // inheritance chain as invalid. + testTypes("/** @constructor */function A() {}\n" + + "/** @interface */function B() {}\n" + + "B.prototype = A;"); + } + + public void testBadImplementsAConstructor() throws Exception { + testTypes("/** @constructor */function A() {}\n" + + "/** @constructor \n * @implements {A} */function B() {}", + "can only implement interfaces"); + } + + public void testBadImplementsNonInterfaceType() throws Exception { + testTypes("/** @constructor \n * @implements {Boolean} */function B() {}", + "can only implement interfaces"); + } + + public void testBadImplementsNonObjectType() throws Exception { + testTypes("/** @constructor \n * @implements {string} */function S() {}", + "can only implement interfaces"); + } + + public void testInterfaceAssignment1() throws Exception { + testTypes("/** @interface */var I = function() {};\n" + + "/** @constructor\n@implements {I} */var T = function() {};\n" + + "var t = new T();\n" + + "/** @type {!I} */var i = t;"); + } + + public void testInterfaceAssignment2() throws Exception { + testTypes("/** @interface */var I = function() {};\n" + + "/** @constructor */var T = function() {};\n" + + "var t = new T();\n" + + "/** @type {!I} */var i = t;", + "initializing variable\n" + + "found : T\n" + + "required: I"); + } + + public void testInterfaceAssignment3() throws Exception { + testTypes("/** @interface */var I = function() {};\n" + + "/** @constructor\n@implements {I} */var T = function() {};\n" + + "var t = new T();\n" + + "/** @type {I|number} */var i = t;"); + } + + public void testInterfaceAssignment4() throws Exception { + testTypes("/** @interface */var I1 = function() {};\n" + + "/** @interface */var I2 = function() {};\n" + + "/** @constructor\n@implements {I1} */var T = function() {};\n" + + "var t = new T();\n" + + "/** @type {I1|I2} */var i = t;"); + } + + public void testInterfaceAssignment5() throws Exception { + testTypes("/** @interface */var I1 = function() {};\n" + + "/** @interface */var I2 = function() {};\n" + + "/** @constructor\n@implements {I1}\n@implements {I2}*/" + + "var T = function() {};\n" + + "var t = new T();\n" + + "/** @type {I1} */var i1 = t;\n" + + "/** @type {I2} */var i2 = t;\n"); + } + + public void testInterfaceAssignment6() throws Exception { + testTypes("/** @interface */var I1 = function() {};\n" + + "/** @interface */var I2 = function() {};\n" + + "/** @constructor\n@implements {I1} */var T = function() {};\n" + + "/** @type {!I1} */var i1 = new T();\n" + + "/** @type {!I2} */var i2 = i1;\n", + "initializing variable\n" + + "found : I1\n" + + "required: I2"); + } + + public void testInterfaceAssignment7() throws Exception { + testTypes("/** @interface */var I1 = function() {};\n" + + "/** @interface\n@extends {I1}*/var I2 = function() {};\n" + + "/** @constructor\n@implements {I2}*/var T = function() {};\n" + + "var t = new T();\n" + + "/** @type {I1} */var i1 = t;\n" + + "/** @type {I2} */var i2 = t;\n" + + "i1 = i2;\n"); + } + + public void testInterfaceAssignment8() throws Exception { + testTypes("/** @interface */var I = function() {};\n" + + "/** @type {I} */var i;\n" + + "/** @type {Object} */var o = i;"); + } + + public void testInterfaceAssignment9() throws Exception { + testTypes("/** @interface */var I = function() {};\n" + + "/** @return {I?} */function f() { return null; }\n" + + "/** @type {!I} */var i = f();\n", + "initializing variable\n" + + "found : (I|null|undefined)\n" + + "required: I"); + } + + public void testInterfaceAssignment10() throws Exception { + testTypes("/** @interface */var I1 = function() {};\n" + + "/** @interface */var I2 = function() {};\n" + + "/** @constructor\n@implements {I2} */var T = function() {};\n" + + "/** @return {!I1|!I2} */function f() { return new T(); }\n" + + "/** @type {!I1} */var i1 = f();\n", + "initializing variable\n" + + "found : (I1|I2)\n" + + "required: I1"); + } + + public void testInterfaceAssignment11() throws Exception { + testTypes("/** @interface */var I1 = function() {};\n" + + "/** @interface */var I2 = function() {};\n" + + "/** @constructor */var T = function() {};\n" + + "/** @return {!I1|!I2|!T} */function f() { return new T(); }\n" + + "/** @type {!I1} */var i1 = f();\n", + "initializing variable\n" + + "found : (I1|I2|T)\n" + + "required: I1"); + } + + public void testInterfaceAssignment12() throws Exception { + testTypes("/** @interface */var I = function() {};\n" + + "/** @constructor\n@implements{I}*/var T1 = function() {};\n" + + "/** @constructor\n@extends {T1}*/var T2 = function() {};\n" + + "/** @return {I} */function f() { return new T2(); }"); + } + + public void testInterfaceAssignment13() throws Exception { + testTypes("/** @interface */var I = function() {};\n" + + "/** @constructor\n@implements {I}*/var T = function() {};\n" + + "/** @constructor */function Super() {};\n" + + "/** @return {I} */Super.prototype.foo = " + + "function() { return new T(); };\n" + + "/** @constructor\n@extends {Super} */function Sub() {}\n" + + "/** @override\n@return {T} */Sub.prototype.foo = " + + "function() { return new T(); };\n"); + } + + public void testGetprop1() throws Exception { + testTypes("/** @return {void}*/function foo(){foo().bar;}", + "No properties on this expression\n" + + "found : undefined\n" + + "required: Object"); + } + + public void testArrayAccess1() throws Exception { + testTypes("var a = []; var b = a['hi'];"); + } + + public void testArrayAccess2() throws Exception { + testTypes("var a = []; var b = a[[1,2]];", + "array access\n" + + "found : Array\n" + + "required: number"); + } + + public void testArrayAccess3() throws Exception { + testTypes("var bar = [];" + + "/** @return {void} */function baz(){};" + + "var foo = bar[baz()];", + "array access\n" + + "found : undefined\n" + + "required: number"); + } + + public void testArrayAccess4() throws Exception { + testTypes("/**@return {!Array}*/function foo(){};var bar = foo()[foo()];", + "array access\n" + + "found : Array\n" + + "required: number"); + } + + public void testArrayAccess6() throws Exception { + testTypes("var bar = null[1];", + "only arrays or objects can be accessed\n" + + "found : null\n" + + "required: Object"); + } + + public void testArrayAccess7() throws Exception { + testTypes("var bar = void 0; bar[0];", + "only arrays or objects can be accessed\n" + + "found : undefined\n" + + "required: Object"); + } + + public void testArrayAccess8() throws Exception { + // Verifies that we don't emit two warnings, because + // the var has been dereferenced after the first one. + testTypes("var bar = void 0; bar[0]; bar[1];", + "only arrays or objects can be accessed\n" + + "found : undefined\n" + + "required: Object"); + } + + public void testPropAccess() throws Exception { + testTypes("/** @param {*} x */var f = function(x) {\n" + + "var o = String(x);\n" + + "if (typeof o['a'] != 'undefined') { return o['a']; }\n" + + "return null;\n" + + "};"); + } + + public void testPropAccess2() throws Exception { + testTypes("var bar = void 0; bar.baz;", + "No properties on this expression\n" + + "found : undefined\n" + + "required: Object"); + } + + public void testPropAccess3() throws Exception { + // Verifies that we don't emit two warnings, because + // the var has been dereferenced after the first one. + testTypes("var bar = void 0; bar.baz; bar.bax;", + "No properties on this expression\n" + + "found : undefined\n" + + "required: Object"); + } + + public void testPropAccess4() throws Exception { + testTypes("/** @param {*} x */ function f(x) { return x['hi']; }"); + } + + public void testSwitchCase1() throws Exception { + testTypes("/**@type number*/var a;" + + "/**@type string*/var b;" + + "switch(a){case b:;}", + "case expression doesn't match switch\n" + + "found : string\n" + + "required: number"); + } + + public void testSwitchCase2() throws Exception { + testTypes("var a = null; switch (typeof a) { case 'foo': }"); + } + + public void testVar1() throws Exception { + TypeCheckResult p = + parseAndTypeCheckWithScope("/** @type {(string,null)} */var a = null"); + + assertTypeEquals(createUnionType(STRING_TYPE, NULL_TYPE), + p.scope.getVar("a").getType()); + } + + public void testVar2() throws Exception { + testTypes("/** @type {Function} */ var a = function(){}"); + } + + public void testVar3() throws Exception { + TypeCheckResult p = parseAndTypeCheckWithScope("var a = 3;"); + + assertTypeEquals(NUMBER_TYPE, p.scope.getVar("a").getType()); + } + + public void testVar4() throws Exception { + TypeCheckResult p = parseAndTypeCheckWithScope( + "var a = 3; a = 'string';"); + + assertTypeEquals(createUnionType(STRING_TYPE, NUMBER_TYPE), + p.scope.getVar("a").getType()); + } + + public void testVar5() throws Exception { + testTypes("var goog = {};" + + "/** @type string */goog.foo = 'hello';" + + "/** @type number */var a = goog.foo;", + "initializing variable\n" + + "found : string\n" + + "required: number"); + } + + public void testVar6() throws Exception { + testTypes( + "function f() {" + + " return function() {" + + " /** @type {!Date} */" + + " var a = 7;" + + " };" + + "}", + "initializing variable\n" + + "found : number\n" + + "required: Date"); + } + + public void testVar7() throws Exception { + testTypes("/** @type number */var a, b;", + "declaration of multiple variables with shared type information"); + } + + public void testVar8() throws Exception { + testTypes("var a, b;"); + } + + public void testVar9() throws Exception { + testTypes("/** @enum */var a;", + "enum initializer must be an object literal or an enum"); + } + + public void testVar10() throws Exception { + testTypes("/** @type !Number */var foo = 'abc';", + "initializing variable\n" + + "found : string\n" + + "required: Number"); + } + + public void testVar11() throws Exception { + testTypes("var /** @type !Date */foo = 'abc';", + "initializing variable\n" + + "found : string\n" + + "required: Date"); + } + + public void testVar12() throws Exception { + testTypes("var /** @type !Date */foo = 'abc', " + + "/** @type !RegExp */bar = 5;", + new String[] { + "initializing variable\n" + + "found : string\n" + + "required: Date", + "initializing variable\n" + + "found : number\n" + + "required: RegExp"}); + } + + public void testVar13() throws Exception { + // this caused an NPE + testTypes("var /** @type number */a,a;"); + } + + public void testVar14() throws Exception { + testTypes("/** @return {number} */ function f() { var x; return x; }", + "inconsistent return type\n" + + "found : undefined\n" + + "required: number"); + } + + public void testVar15() throws Exception { + testTypes("/** @return {number} */" + + "function f() { var x = x || {}; return x; }", + "inconsistent return type\n" + + "found : {}\n" + + "required: number"); + } + + public void testAssign1() throws Exception { + testTypes("var goog = {};" + + "/** @type number */goog.foo = 'hello';", + "assignment to property foo of goog\n" + + "found : string\n" + + "required: number"); + } + + public void testAssign2() throws Exception { + testTypes("var goog = {};" + + "/** @type number */goog.foo = 3;" + + "goog.foo = 'hello';", + "assignment to property foo of goog\n" + + "found : string\n" + + "required: number"); + } + + public void testAssign3() throws Exception { + testTypes("var goog = {};" + + "/** @type number */goog.foo = 3;" + + "goog.foo = 4;"); + } + + public void testAssign4() throws Exception { + testTypes("var goog = {};" + + "goog.foo = 3;" + + "goog.foo = 'hello';"); + } + + public void testAssignInference() throws Exception { + testTypes( + "/**" + + " * @param {Array} x" + + " * @return {number}" + + " */" + + "function f(x) {" + + " var y = null;" + + " y = x[0];" + + " if (y == null) { return 4; } else { return 6; }" + + "}"); + } + + public void testOr1() throws Exception { + testTypes("/** @type number */var a;" + + "/** @type number */var b;" + + "a + b || undefined;"); + } + + public void testOr2() throws Exception { + testTypes("/** @type number */var a;" + + "/** @type number */var b;" + + "/** @type number */var c = a + b || undefined;", + "initializing variable\n" + + "found : (number|undefined)\n" + + "required: number"); + } + + public void testOr3() throws Exception { + testTypes("/** @type {(number, undefined)} */var a;" + + "/** @type number */var c = a || 3;"); + } + + /** + * Test that type inference continues with the right side, + * when no short-circuiting is possible. + * See bugid 1205387 for more details. + */ + public void testOr4() throws Exception { + testTypes("/**@type {number} */var x;x=null || \"a\";", + "assignment\n" + + "found : string\n" + + "required: number"); + } + + /** + * @see #testOr4() + */ + public void testOr5() throws Exception { + testTypes("/**@type {number} */var x;x=undefined || \"a\";", + "assignment\n" + + "found : string\n" + + "required: number"); + } + + public void testAnd1() throws Exception { + testTypes("/** @type number */var a;" + + "/** @type number */var b;" + + "a + b && undefined;"); + } + + public void testAnd2() throws Exception { + testTypes("/** @type number */var a;" + + "/** @type number */var b;" + + "/** @type number */var c = a + b && undefined;", + "initializing variable\n" + + "found : (number|undefined)\n" + + "required: number"); + } + + public void testAnd3() throws Exception { + testTypes("/** @type {(!Array, undefined)} */var a;" + + "/** @type number */var c = a && undefined;", + "initializing variable\n" + + "found : undefined\n" + + "required: number"); + } + + public void testAnd4() throws Exception { + testTypes("/** @param {number} x */function f(x){};\n" + + "/** @type null */var x; /** @type {number?} */var y;\n" + + "if (x && y) { f(y) }"); + } + + public void testAnd5() throws Exception { + testTypes("/** @param {number} x\n@param {string} y*/function f(x,y){};\n" + + "/** @type {number?} */var x; /** @type {string?} */var y;\n" + + "if (x && y) { f(x, y) }"); + } + + public void testAnd6() throws Exception { + testTypes("/** @param {number} x */function f(x){};\n" + + "/** @type {number|undefined} */var x;\n" + + "if (x && f(x)) { f(x) }"); + } + + public void testAnd7() throws Exception { + // TODO(user): a deterministic warning should be generated for this + // case since x && x is always false. The implementation of this requires + // a more precise handling of a null value within a variable's type. + // Currently, a null value defaults to ? which passes every check. + testTypes("/** @type null */var x; if (x && x) {}"); + } + + public void testHook() throws Exception { + testTypes("/**@return {void}*/function foo(){ var x=foo()?a:b; }"); + } + + public void testHookRestrictsType1() throws Exception { + testTypes("/** @return {(string,null)} */" + + "function f() { return null;}" + + "/** @type {(string,null)} */ var a = f();" + + "/** @type string */" + + "var b = a ? a : 'default';"); + } + + public void testHookRestrictsType2() throws Exception { + testTypes("/** @type {String} */" + + "var a = null;" + + "/** @type (null|undefined) */" + + "var b = a ? null : a;"); + } + + public void testHookRestrictsType3() throws Exception { + testTypes("/** @type {String} */" + + "var a;" + + "/** @type (null|undefined) */" + + "var b = (!a) ? a : null;"); + } + + public void testHookRestrictsType4() throws Exception { + testTypes("/** @type {(boolean,undefined)} */" + + "var a;" + + "/** @type boolean */" + + "var b = a != null ? a : true;"); + } + + public void testHookRestrictsType5() throws Exception { + testTypes("/** @type {(boolean,undefined)} */" + + "var a;" + + "/** @type {(undefined)} */" + + "var b = a == null ? a : undefined;"); + } + + public void testHookRestrictsType6() throws Exception { + testTypes("/** @type {(number,null,undefined)} */" + + "var a;" + + "/** @type {number} */" + + "var b = a == null ? 5 : a;"); + } + + public void testHookRestrictsType7() throws Exception { + testTypes("/** @type {(number,null,undefined)} */" + + "var a;" + + "/** @type {number} */" + + "var b = a == undefined ? 5 : a;"); + } + + public void testWhileRestrictsType1() throws Exception { + testTypes("/** @param {null} x */ function g(x) {}" + + "/** @param {number?} x */\n" + + "function f(x) {\n" + + "while (x) {\n" + + "if (g(x)) { x = 1; }\n" + + "x = x-1;\n}\n}", + "actual parameter 1 of g does not match formal parameter\n" + + "found : number\n" + + "required: null"); + } + + public void testWhileRestrictsType2() throws Exception { + testTypes("/** @param {number?} x\n@return {number}*/\n" + + "function f(x) {\n/** @type {number} */var y = 0;" + + "while (x) {\n" + + "y = x;\n" + + "x = x-1;\n}\n" + + "return y;}"); + } + + public void testHigherOrderFunctions1() throws Exception { + testTypes( + "/** @type {function(number)} */var f;" + + "f(true);", + "actual parameter 1 of f does not match formal parameter\n" + + "found : boolean\n" + + "required: number"); + } + + public void testHigherOrderFunctions2() throws Exception { + testTypes( + "/** @type {function():!Date} */var f;" + + "/** @type boolean */var a = f();", + "initializing variable\n" + + "found : Date\n" + + "required: boolean"); + } + + public void testHigherOrderFunctions3() throws Exception { + testTypes( + "/** @type {function(this:Error):Date} */var f; new f", + "cannot instantiate non-constructor"); + } + + public void testHigherOrderFunctions4() throws Exception { + testTypes( + "/** @type {function(this:Error,...[number]):Date} */var f; new f", + "cannot instantiate non-constructor"); + } + + public void testConstructorAlias1() throws Exception { + testTypes( + "/** @constructor */ var Foo = function() {};" + + "/** @type {number} */ Foo.prototype.bar = 3;" + + "/** @constructor */ var FooAlias = Foo;" + + "/** @return {string} */ function foo() { " + + " return (new FooAlias()).bar; }", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testConstructorAlias2() throws Exception { + testTypes( + "/** @constructor */ var Foo = function() {};" + + "/** @constructor */ var FooAlias = Foo;" + + "/** @type {number} */ FooAlias.prototype.bar = 3;" + + "/** @return {string} */ function foo() { " + + " return (new Foo()).bar; }", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testConstructorAlias3() throws Exception { + testTypes( + "/** @constructor */ var Foo = function() {};" + + "/** @type {number} */ Foo.prototype.bar = 3;" + + "/** @constructor */ var FooAlias = Foo;" + + "/** @return {string} */ function foo() { " + + " return (new FooAlias()).bar; }", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testConstructorAlias4() throws Exception { + testTypes( + "/** @constructor */ var Foo = function() {};" + + "var FooAlias = Foo;" + + "/** @type {number} */ FooAlias.prototype.bar = 3;" + + "/** @return {string} */ function foo() { " + + " return (new Foo()).bar; }", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testConstructorAlias5() throws Exception { + testTypes( + "/** @constructor */ var Foo = function() {};" + + "/** @constructor */ var FooAlias = Foo;" + + "/** @return {FooAlias} */ function foo() { " + + " return new Foo(); }"); + } + + public void testConstructorAlias6() throws Exception { + testTypes( + "/** @constructor */ var Foo = function() {};" + + "/** @constructor */ var FooAlias = Foo;" + + "/** @return {Foo} */ function foo() { " + + " return new FooAlias(); }"); + } + + public void testConstructorAlias7() throws Exception { + testTypes( + "var goog = {};" + + "/** @constructor */ goog.Foo = function() {};" + + "/** @constructor */ goog.FooAlias = goog.Foo;" + + "/** @return {number} */ function foo() { " + + " return new goog.FooAlias(); }", + "inconsistent return type\n" + + "found : goog.Foo\n" + + "required: number"); + } + + public void testConstructorAlias8() throws Exception { + testTypes( + "var goog = {};" + + "/**\n * @param {number} x \n * @constructor */ " + + "goog.Foo = function(x) {};" + + "/**\n * @param {number} x \n * @constructor */ " + + "goog.FooAlias = goog.Foo;" + + "/** @return {number} */ function foo() { " + + " return new goog.FooAlias(1); }", + "inconsistent return type\n" + + "found : goog.Foo\n" + + "required: number"); + } + + public void testConstructorAlias9() throws Exception { + testTypes( + "var goog = {};" + + "/**\n * @param {number} x \n * @constructor */ " + + "goog.Foo = function(x) {};" + + "/** @constructor */ goog.FooAlias = goog.Foo;" + + "/** @return {number} */ function foo() { " + + " return new goog.FooAlias(1); }", + "inconsistent return type\n" + + "found : goog.Foo\n" + + "required: number"); + } + + public void testConstructorAlias10() throws Exception { + testTypes( + "/**\n * @param {number} x \n * @constructor */ " + + "var Foo = function(x) {};" + + "/** @constructor */ var FooAlias = Foo;" + + "/** @return {number} */ function foo() { " + + " return new FooAlias(1); }", + "inconsistent return type\n" + + "found : Foo\n" + + "required: number"); + } + + public void testClosure1() throws Exception { + testClosureTypes( + CLOSURE_DEFS + + "/** @type {string|undefined} */var a;" + + "/** @type string */" + + "var b = goog.isDef(a) ? a : 'default';", + null); + } + + public void testClosure2() throws Exception { + testClosureTypes( + CLOSURE_DEFS + + "/** @type {string|null} */var a;" + + "/** @type string */" + + "var b = goog.isNull(a) ? 'default' : a;", + null); + } + + public void testClosure3() throws Exception { + testClosureTypes( + CLOSURE_DEFS + + "/** @type {string|null|undefined} */var a;" + + "/** @type string */" + + "var b = goog.isDefAndNotNull(a) ? a : 'default';", + null); + } + + public void testClosure4() throws Exception { + testClosureTypes( + CLOSURE_DEFS + + "/** @type {string|undefined} */var a;" + + "/** @type string */" + + "var b = !goog.isDef(a) ? 'default' : a;", + null); + } + + public void testClosure5() throws Exception { + testClosureTypes( + CLOSURE_DEFS + + "/** @type {string|null} */var a;" + + "/** @type string */" + + "var b = !goog.isNull(a) ? a : 'default';", + null); + } + + public void testClosure6() throws Exception { + testClosureTypes( + CLOSURE_DEFS + + "/** @type {string|null|undefined} */var a;" + + "/** @type string */" + + "var b = !goog.isDefAndNotNull(a) ? 'default' : a;", + null); + } + + public void testReturn1() throws Exception { + testTypes("/**@return {void}*/function foo(){ return 3; }", + "inconsistent return type\n" + + "found : number\n" + + "required: undefined"); + } + + public void testReturn2() throws Exception { + testTypes("/**@return {!Number}*/function foo(){ return; }", + "inconsistent return type\n" + + "found : undefined\n" + + "required: Number"); + } + + public void testReturn3() throws Exception { + testTypes("/**@return {!Number}*/function foo(){ return 'abc'; }", + "inconsistent return type\n" + + "found : string\n" + + "required: Number"); + } + + public void testReturn4() throws Exception { + testTypes("/**@return {!Number}\n*/\n function a(){return new Array();}", + "inconsistent return type\n" + + "found : Array\n" + + "required: Number"); + } + + public void testReturn5() throws Exception { + testTypes("/** @param {number} n\n" + + "@constructor */function n(n){return};"); + } + + public void testReturn6() throws Exception { + testTypes( + "/** @param {number} opt_a\n@return {string} */" + + "function a(opt_a) { return opt_a }", + "inconsistent return type\n" + + "found : (number|undefined)\n" + + "required: string"); + } + + public void testReturn7() throws Exception { + testTypes("/** @constructor */var A = function() {};\n" + + "/** @constructor */var B = function() {};\n" + + "/** @return {!B} */A.f = function() { return 1; };", + "inconsistent return type\n" + + "found : number\n" + + "required: B"); + } + + public void testReturn8() throws Exception { + testTypes("/** @constructor */var A = function() {};\n" + + "/** @constructor */var B = function() {};\n" + + "/** @return {!B} */A.prototype.f = function() { return 1; };", + "inconsistent return type\n" + + "found : number\n" + + "required: B"); + } + + public void testThis1() throws Exception { + testTypes("var goog = {};" + + "/** @constructor */goog.A = function(){};" + + "/** @return {number} */goog.A.prototype.n = " + + " function() { return this };", + "inconsistent return type\n" + + "found : goog.A\n" + + "required: number"); + } + + public void testThis2() throws Exception { + testTypes("var goog = {};" + + "/** @constructor */goog.A = function(){" + + " this.foo = null;" + + "};" + + "/** @return {number} */" + + "goog.A.prototype.n = function() { return this.foo };", + "inconsistent return type\n" + + "found : null\n" + + "required: number"); + } + + public void testThis3() throws Exception { + testTypes("var goog = {};" + + "/** @constructor */goog.A = function(){" + + " this.foo = null;" + + " this.foo = 5;" + + "};"); + } + + public void testThis4() throws Exception { + testTypes("var goog = {};" + + "/** @constructor */goog.A = function(){" + + " /** @type {string?} */this.foo = null;" + + "};" + + "/** @return {number} */goog.A.prototype.n = function() {" + + " return this.foo };", + "inconsistent return type\n" + + "found : (null|string|undefined)\n" + + "required: number"); + } + + public void testThis5() throws Exception { + testTypes("/** @this Date\n@return {number}*/function h() { return this }", + "inconsistent return type\n" + + "found : Date\n" + + "required: number"); + } + + public void testThis6() throws Exception { + testTypes("var goog = {};" + + "/** @constructor\n@return {!Date} */" + + "goog.A = function(){ return this };", + "inconsistent return type\n" + + "found : goog.A\n" + + "required: Date"); + } + + public void testThis7() throws Exception { + testTypes("/** @constructor */function A(){};" + + "/** @return {number} */A.prototype.n = function() { return this };", + "inconsistent return type\n" + + "found : A\n" + + "required: number"); + } + + public void testThis8() throws Exception { + testTypes("/** @constructor */function A(){" + + " /** @type {string?} */this.foo = null;" + + "};" + + "/** @return {number} */A.prototype.n = function() {" + + " return this.foo };", + "inconsistent return type\n" + + "found : (null|string|undefined)\n" + + "required: number"); + } + + public void testThis9() throws Exception { + // In A.bar, the type of {@code this} is unknown. + testTypes("/** @constructor */function A(){};" + + "A.prototype.foo = 3;" + + "/** @return {string} */ A.bar = function() { return this.foo; };"); + } + + public void testThis10() throws Exception { + // In A.bar, the type of {@code this} is inferred from the @this tag. + testTypes("/** @constructor */function A(){};" + + "A.prototype.foo = 3;" + + "/** @this {A}\n@return {string} */" + + "A.bar = function() { return this.foo; };", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testGlobalThis1() throws Exception { + testTypes("/** @constructor */ function Window() {}" + + "/** @param {string} msg */ " + + "Window.prototype.alert = function(msg) {};" + + "this.alert(3);", + "actual parameter 1 of Window.prototype.alert " + + "does not match formal parameter\n" + + "found : number\n" + + "required: string"); + } + + public void testGlobalThis2() throws Exception { + testTypes("/** @constructor */ function Bindow() {}" + + "/** @param {string} msg */ " + + "Bindow.prototype.alert = function(msg) {};" + + "this.alert = 3;" + + "(new Bindow()).alert(this.alert)"); + } + + public void testGlobalThis3() throws Exception { + testTypes( + "/** @param {string} msg */ " + + "function alert(msg) {};" + + "this.alert(3);", + "actual parameter 1 of global this.alert " + + "does not match formal parameter\n" + + "found : number\n" + + "required: string"); + } + + public void testGlobalThis4() throws Exception { + testTypes( + "/** @param {string} msg */ " + + "var alert = function(msg) {};" + + "this.alert(3);", + "actual parameter 1 of global this.alert " + + "does not match formal parameter\n" + + "found : number\n" + + "required: string"); + } + + public void testGlobalThis5() throws Exception { + testTypes( + "function f() {" + + " /** @param {string} msg */ " + + " var alert = function(msg) {};" + + "}" + + "this.alert(3);", + "Property alert never defined on global this"); + } + + public void testGlobalThis6() throws Exception { + testTypes( + "/** @param {string} msg */ " + + "var alert = function(msg) {};" + + "var x = 3;" + + "x = 'msg';" + + "this.alert(this.x);"); + } + + public void testControlFlowRestrictsType1a() throws Exception { + testTypes("/** @return {String?} */ function f() { return null; }\n" + + "/** @type {String?} */ var a = f();\n" + + "/** @type String */ var b = new String('foo');\n" + + "/** @type (null|undefined) */ var c = null;\n" + + "if (a) {\n" + + " b = a;\n" + + "} else {\n" + + " c = a;\n" + + "}"); + } + + public void testControlFlowRestrictsType1b() throws Exception { + testTypes("/** @return {!String|null} */ function f() { return null; }\n" + + "/** @type {!String|null} */ var a = f();\n" + + "/** @type String */ var b = new String('foo');\n" + + "/** @type (null) */ var c = null;\n" + + "if (a) {\n" + + " b = a;\n" + + "} else {\n" + + " c = a;\n" + + "}"); + } + + public void testControlFlowRestrictsType1c() throws Exception { + testTypes("/** @return {!String|undefined} */\n" + + "function f() { return undefined; }\n" + + "/** @type {!String|undefined} */ var a = f();\n" + + "/** @type String */ var b = new String('foo');\n" + + "/** @type undefined */ var c = undefined;\n" + + "if (a) {\n" + + " b = a;\n" + + "} else {\n" + + " c = a;\n" + + "}"); + } + + public void testControlFlowRestrictsType2() throws Exception { + testTypes("/** @return {(string,null)} */ function f() { return null; }" + + "/** @type {(string,null)} */ var a = f();" + + "/** @type string */ var b = 'foo';" + + "/** @type null */ var c = null;" + + "if (a) {" + + " b = a;" + + "} else {" + + " c = a;" + + "}", + "assignment\n" + + "found : (null|string)\n" + + "required: null"); + } + + public void testControlFlowRestrictsType3() throws Exception { + testTypes("/** @type {(string,void)} */" + + "var a;" + + "/** @type string */" + + "var b = 'foo';" + + "if (a) {" + + " b = a;" + + "}"); + } + + public void testControlFlowRestrictsType4() throws Exception { + testTypes("/** @param {string} a */ function f(a){}" + + "/** @type {(string,undefined)} */ var a;" + + "a && f(a);"); + } + + public void testControlFlowRestrictsType5() throws Exception { + testTypes("/** @param {undefined} a */ function f(a){}" + + "/** @type {(!Array,undefined)} */ var a;" + + "a || f(a);"); + } + + public void testControlFlowRestrictsType6() throws Exception { + testTypes("/** @param {undefined} x */ function f(x) {}" + + "/** @type {(string,undefined)} */ var a;" + + "a && f(a);", + "actual parameter 1 of f does not match formal parameter\n" + + "found : string\n" + + "required: undefined"); + } + + public void testControlFlowRestrictsType7() throws Exception { + testTypes("/** @param {undefined} x */ function f(x) {}" + + "/** @type {(string,undefined)} */ var a;" + + "a && f(a);", + "actual parameter 1 of f does not match formal parameter\n" + + "found : string\n" + + "required: undefined"); + } + + public void testControlFlowRestrictsType8() throws Exception { + testTypes("/** @param {undefined} a */ function f(a){}" + + "/** @type {(!Array,undefined)} */ var a;" + + "if (a || f(a)) {}"); + } + + public void testControlFlowRestrictsType9() throws Exception { + testTypes("/** @param {number?} x\n * @return {number}*/\n" + + "var f = function(x) {\n" + + "if (!x || x == 1) { return 1; } else { return x; }\n" + + "};"); + } + + public void testSwitchCase3() throws Exception { + testTypes("/** @type String */" + + "var a = new String('foo');" + + "switch (a) { case 'A': }"); + } + + public void testSwitchCase4() throws Exception { + testTypes("/** @type {(string,Null)} */" + + "var a = 'foo';" + + "switch (a) { case 'A':break; case null:break; }"); + } + + public void testSwitchCase5() throws Exception { + testTypes("/** @type {(String,Null)} */" + + "var a = new String('foo');" + + "switch (a) { case 'A':break; case null:break; }"); + } + + public void testSwitchCase6() throws Exception { + testTypes("/** @type {(Number,Null)} */" + + "var a = new Number(5);" + + "switch (a) { case 5:break; case null:break; }"); + } + + public void testSwitchCase7() throws Exception { + // This really tests the inference inside the case. + testTypes( + "/**\n" + + " * @param {number} x\n" + + " * @return {number}\n" + + " */\n" + + "function g(x) { return 5; }" + + "function f() {" + + " var x = {};" + + " x.foo = '3';" + + " switch (3) { case g(x.foo): return 3; }" + + "}", + "actual parameter 1 of g does not match formal parameter\n" + + "found : string\n" + + "required: number"); + } + + public void testSwitchCase8() throws Exception { + // This really tests the inference inside the switch clause. + testTypes( + "/**\n" + + " * @param {number} x\n" + + " * @return {number}\n" + + " */\n" + + "function g(x) { return 5; }" + + "function f() {" + + " var x = {};" + + " x.foo = '3';" + + " switch (g(x.foo)) { case 3: return 3; }" + + "}", + "actual parameter 1 of g does not match formal parameter\n" + + "found : string\n" + + "required: number"); + } + + public void testNoTypeCheck1() throws Exception { + testTypes("/** @notypecheck */function foo() { new 4 }"); + } + + public void testNoTypeCheck2() throws Exception { + testTypes("/** @notypecheck */var foo = function() { new 4 }"); + } + + public void testNoTypeCheck3() throws Exception { + testTypes("/** @notypecheck */var foo = function bar() { new 4 }"); + } + + public void testNoTypeCheck4() throws Exception { + testTypes("var foo;" + + "/** @notypecheck */foo = function() { new 4 }"); + } + + public void testNoTypeCheck5() throws Exception { + testTypes("var foo;" + + "foo = /** @notypecheck */function() { new 4 }"); + } + + public void testNoTypeCheck6() throws Exception { + testTypes("var foo;" + + "/** @notypecheck */foo = function bar() { new 4 }"); + } + + public void testNoTypeCheck7() throws Exception { + testTypes("var foo;" + + "foo = /** @notypecheck */function bar() { new 4 }"); + } + + public void testNoTypeCheck8() throws Exception { + testTypes("/** @fileoverview \n * @notypecheck */ var foo;" + + "var bar = 3; /** @param {string} x */ function f(x) {} f(bar);"); + } + + public void testImplicitCast() throws Exception { + testTypes("/** @constructor */ function Element() {};\n" + + "/** @type {string}\n" + + " * @implicitCast */" + + "Element.prototype.innerHTML;", + "(new Element).innerHTML = new Array();", null, false); + } + + public void testImplicitCastSubclassAccess() throws Exception { + testTypes("/** @constructor */ function Element() {};\n" + + "/** @type {string}\n" + + " * @implicitCast */" + + "Element.prototype.innerHTML;" + + "/** @constructor \n @extends Element */" + + "function DIVElement() {};", + "(new DIVElement).innerHTML = new Array();", null, false); + } + + public void testImplicitCastNotInExterns() throws Exception { + testTypes("/** @constructor */ function Element() {};\n" + + "/** @type {string}\n" + + " * @implicitCast */" + + "Element.prototype.innerHTML;" + + "(new Element).innerHTML = new Array();", + new String[] { + "Illegal annotation on innerHTML. @implicitCast may only be " + + "used in externs.", + "assignment to property innerHTML of Element\n" + + "found : Array\n" + + "required: string" + }); + } + + public void testNumberNode() throws Exception { + Node n = typeCheck(Node.newNumber(0)); + + assertTypeEquals(NUMBER_TYPE, n.getJSType()); + } + + public void testStringNode() throws Exception { + Node n = typeCheck(Node.newString("hello")); + + assertTypeEquals(STRING_TYPE, n.getJSType()); + } + + public void testBooleanNodeTrue() throws Exception { + Node trueNode = typeCheck(new Node(Token.TRUE)); + + assertTypeEquals(BOOLEAN_TYPE, trueNode.getJSType()); + } + + public void testBooleanNodeFalse() throws Exception { + Node falseNode = typeCheck(new Node(Token.FALSE)); + + assertTypeEquals(BOOLEAN_TYPE, falseNode.getJSType()); + } + + public void testUndefinedNode() throws Exception { + Node p = new Node(Token.ADD); + Node n = Node.newString(Token.NAME, "undefined"); + p.addChildToBack(n); + p.addChildToBack(Node.newNumber(5)); + typeCheck(p); + + assertTypeEquals(VOID_TYPE, n.getJSType()); + } + + public void testNumberAutoboxing() throws Exception { + testTypes("/** @type Number */var a = 4;", + "initializing variable\n" + + "found : number\n" + + "required: (Number|null|undefined)"); + } + + public void testNumberUnboxing() throws Exception { + testTypes("/** @type number */var a = new Number(4);", + "initializing variable\n" + + "found : Number\n" + + "required: number"); + } + + public void testStringAutoboxing() throws Exception { + testTypes("/** @type String */var a = 'hello';", + "initializing variable\n" + + "found : string\n" + + "required: (String|null|undefined)"); + } + + public void testStringUnboxing() throws Exception { + testTypes("/** @type string */var a = new String('hello');", + "initializing variable\n" + + "found : String\n" + + "required: string"); + } + + public void testBooleanAutoboxing() throws Exception { + testTypes("/** @type Boolean */var a = true;", + "initializing variable\n" + + "found : boolean\n" + + "required: (Boolean|null|undefined)"); + } + + public void testBooleanUnboxing() throws Exception { + testTypes("/** @type boolean */var a = new Boolean(false);", + "initializing variable\n" + + "found : Boolean\n" + + "required: boolean"); + } + + public void testIssue86() throws Exception { + testTypes( + "/** @interface */ function I() {}" + + "/** @return {number} */ I.prototype.get = function(){};" + + "/** @constructor \n * @implements {I} */ function F() {}" + + "/** @override */ F.prototype.get = function() { return true; };", + "inconsistent return type\n" + + "found : boolean\n" + + "required: number"); + } + + public void testIssue124() throws Exception { + testTypes( + "var t = null;" + + "function test() {" + + " if (t != null) { t = null; }" + + " t = 1;" + + "}"); + } + + public void testIssue124b() throws Exception { + testTypes( + "var t = null;" + + "function test() {" + + " if (t != null) { t = null; }" + + " t = undefined;" + + "}", + "condition always evaluates to false\n" + + "left : (null|undefined)\n" + + "right: null"); + } + + /** + * Tests that the || operator is type checked correctly, that is of + * the type of the first argument or of the second argument. See + * bugid 592170 for more details. + */ + public void testBug592170() throws Exception { + testTypes( + "/** @param {Function} opt_f ... */" + + "function foo(opt_f) {" + + " /** @type {Function} */" + + " return opt_f || function () {};" + + "}", + "Type annotations are not allowed here. Are you missing parentheses?"); + } + + /** + * Tests that undefined can be compared shallowly to a value of type + * (number,undefined) regardless of the side on which the undefined + * value is. + */ + public void testBug901455() throws Exception { + testTypes("/** @return {(number,undefined)} */ function a() { return 3; }" + + "var b = undefined === a()"); + testTypes("/** @return {(number,undefined)} */ function a() { return 3; }" + + "var b = a() === undefined"); + } + + /** + * Tests that the match method of strings returns nullable arrays. + */ + public void testBug908701() throws Exception { + testTypes("/** @type {String} */var s = new String('foo');" + + "var b = s.match(/a/) != null;"); + } + + /** + * Tests that named types play nicely with subtyping. + */ + public void testBug908625() throws Exception { + testTypes("/** @constructor */function A(){}" + + "/** @constructor\n * @extends A */function B(){}" + + "/** @param {B} b" + + "\n @return {(A,undefined)} */function foo(b){return b}"); + } + + /** + * Tests that assigning two untyped functions to a variable whose type is + * inferred and calling this variable is legal. + */ + public void testBug911118() throws Exception { + // verifying the type assigned to anonymous functions assigned variables + Scope s = parseAndTypeCheckWithScope("var a = function(){};").scope; + JSType type = s.getVar("a").getType(); + assertEquals("function (): undefined", type.toString()); + + // verifying the bug example + testTypes("function nullFunction() {};" + + "var foo = nullFunction;" + + "foo = function() {};" + + "foo();"); + } + + public void testBug909000() throws Exception { + testTypes("/** @constructor */function A(){}\n" + + "/** @param {!A} a\n" + + "@return {boolean}*/\n" + + "function y(a) { return a }", + "inconsistent return type\n" + + "found : A\n" + + "required: boolean"); + } + + public void testBug930117() throws Exception { + testTypes( + "/** @param {boolean} x */function f(x){}" + + "f(null);", + "actual parameter 1 of f does not match formal parameter\n" + + "found : null\n" + + "required: boolean"); + } + + public void testBug1484445() throws Exception { + testTypes( + "/** @constructor */ function Foo() {}" + + "/** @type {number?} */ Foo.prototype.bar = null;" + + "/** @type {number?} */ Foo.prototype.baz = null;" + + "/** @param {Foo} foo */" + + "function f(foo) {" + + " while (true) {" + + " if (foo.bar == null && foo.baz == null) {" + + " foo.bar;" + + " }" + + " }" + + "}"); + } + + public void testBug1859535() throws Exception { + testTypes( + "/**\n" + + " * @param {Function} childCtor Child class.\n" + + " * @param {Function} parentCtor Parent class.\n" + + " */" + + "var inherits = function(childCtor, parentCtor) {" + + " /** @constructor */" + + " function tempCtor() {};" + + " tempCtor.prototype = parentCtor.prototype;" + + " childCtor.superClass_ = parentCtor.prototype;" + + " childCtor.prototype = new tempCtor();" + + " /** @override */ childCtor.prototype.constructor = childCtor;" + + "};" + + "/**" + + " * @param {Function} constructor\n" + + " * @param {Object} var_args\n" + + " * @return {Object}\n" + + " */" + + "var factory = function(constructor, var_args) {" + + " /** @constructor */" + + " var tempCtor = function() {};" + + " tempCtor.prototype = constructor.prototype;" + + " var obj = new tempCtor();" + + " constructor.apply(obj, arguments);" + + " return obj;" + + "};"); + } + + public void testBug1940591() throws Exception { + testTypes( + "/** @type {Object} */" + + "var a = {};\n" + + "/** @type {number} */\n" + + "a.name = 0;\n" + + "/**\n" + + " * @param {Function} x anything.\n" + + " */\n" + + "a.g = function(x) { x.name = 'a'; }"); + } + + public void testBug1942972() throws Exception { + testTypes( + "var google = {\n"+ + " gears: {\n" + + " factory: {},\n" + + " workerPool: {}\n" + + " }\n" + + "};\n" + + "\n" + + "google.gears = {factory: {}};\n"); + } + + public void testBug1943776() throws Exception { + testTypes( + "/** @return {{foo: Array}} */" + + "function bar() {" + + " return {foo: []};" + + "}"); + } + + public void testBug1987544() throws Exception { + testTypes( + "/** @param {string} x */ function foo(x) {}" + + "var duration;" + + "if (true && !(duration = 3)) {" + + " foo(duration);" + + "}", + "actual parameter 1 of foo does not match formal parameter\n" + + "found : number\n" + + "required: string"); + } + + public void testBug1940769() throws Exception { + testTypes( + "/** @return {!Object} */ " + + "function proto(obj) { return obj.prototype; }" + + "/** @constructor */ function Map() {}" + + "/**\n" + + " * @constructor\n" + + " * @extends {Map}\n" + + " */" + + "function Map2() { Map.call(this); };" + + "Map2.prototype = proto(Map);"); + } + + public void testBug2335992() throws Exception { + testTypes( + "/** @return {*} */ function f() { return 3; }" + + "var x = f();" + + "/** @type {string} */" + + "x.y = 3;", + "assignment\n" + + "found : number\n" + + "required: string"); + } + + public void testBug2341812() throws Exception { + testTypes( + "/** @interface */" + + "function EventTarget() {}" + + "/** @constructor \n * @implements {EventTarget} */" + + "function Node() {}" + + "/** @type {number} */ Node.prototype.index;" + + "/** @param {EventTarget} x \n * @return {string} */" + + "function foo(x) { return x.index; }"); + } + + public void testScopedConstructors() throws Exception { + testTypes( + "function foo1() { " + + " /** @constructor */ function Bar() { " + + " /** @type {number} */ this.x = 3;" + + " }" + + "}" + + "function foo2() { " + + " /** @constructor */ function Bar() { " + + " /** @type {string} */ this.x = 'y';" + + " }" + + " /** " + + " * @param {Bar} b\n" + + " * @return {number}\n" + + " */" + + " function baz(b) { return b.x; }" + + "}", + "inconsistent return type\n" + + "found : string\n" + + "required: number"); + } + + public void testQualifiedNameInference1() throws Exception { + testTypes( + "/** @constructor */ function Foo() {}" + + "/** @type {number?} */ Foo.prototype.bar = null;" + + "/** @type {number?} */ Foo.prototype.baz = null;" + + "/** @param {Foo} foo */" + + "function f(foo) {" + + " while (true) {" + + " if (!foo.baz) break; " + + " foo.bar = null;" + + " }" + + // Tests a bug where this condition always evaluated to true. + " return foo.bar == null;" + + "}"); + } + + public void testQualifiedNameInference2() throws Exception { + testTypes( + "var x = {};" + + "x.y = c;" + + "function f(a, b) {" + + " if (a) {" + + " if (b) " + + " x.y = 2;" + + " else " + + " x.y = 1;" + + " }" + + " return x.y == null;" + + "}"); + } + + public void testQualifiedNameInference3() throws Exception { + testTypes( + "var x = {};" + + "x.y = c;" + + "function f(a, b) {" + + " if (a) {" + + " if (b) " + + " x.y = 2;" + + " else " + + " x.y = 1;" + + " }" + + " return x.y == null;" + + "} function g() { x.y = null; }"); + } + + public void testQualifiedNameInference4() throws Exception { + testTypes( + "/** @param {string} x */ function f(x) {}\n" + + "/**\n" + + " * @param {?string} x \n" + + " * @constructor\n" + + " */" + + "function Foo(x) { this.x_ = x; }\n" + + "Foo.prototype.bar = function() {" + + " if (this.x_) { f(this.x_); }" + + "};"); + } + + public void testSheqRefinedScope() throws Exception { + Node n = parseAndTypeCheck( + "/** @constructor */function A() {}\n" + + "/** @constructor \n @extends A */ function B() {}\n" + + "/** @return {number} */\n" + + "B.prototype.p = function() { return 1; }\n" + + "/** @param {A} a\n @param {B} b */\n" + + "function f(a, b) {\n" + + " b.p();\n" + + " if (a === b) {\n" + + " b.p();\n" + + " }\n" + + "}"); + Node nodeC = n.getLastChild().getLastChild().getLastChild().getLastChild() + .getLastChild().getLastChild(); + JSType typeC = nodeC.getJSType(); + assertTrue(typeC.isNumber()); + + Node nodeB = nodeC.getFirstChild().getFirstChild(); + JSType typeB = nodeB.getJSType(); + assertEquals("B", typeB.toString()); + } + + public void testAssignToUntypedVariable() throws Exception { + Node n = parseAndTypeCheck("var z; z = 1;"); + + Node assign = n.getLastChild().getFirstChild(); + Node node = assign.getFirstChild(); + assertFalse(node.getJSType().isUnknownType()); + assertEquals("number", node.getJSType().toString()); + } + + public void testAssignToUntypedProperty() throws Exception { + Node n = parseAndTypeCheck( + "/** @constructor */ function Foo() {}\n" + + "Foo.prototype.a = 1;" + + "(new Foo).a;"); + + Node node = n.getLastChild().getFirstChild(); + assertFalse(node.getJSType().isUnknownType()); + assertTrue(node.getJSType().isNumber()); + } + + public void testNew1() throws Exception { + testTypes("new 4", TypeCheck.NOT_A_CONSTRUCTOR); + } + + public void testNew2() throws Exception { + testTypes("var Math = {}; new Math()", TypeCheck.NOT_A_CONSTRUCTOR); + } + + public void testNew3() throws Exception { + testTypes("new Date()"); + } + + public void testNew4() throws Exception { + testTypes("/** @constructor */function A(){}; new A();"); + } + + public void testNew5() throws Exception { + testTypes("function A(){}; new A();", TypeCheck.NOT_A_CONSTRUCTOR); + } + + public void testNew6() throws Exception { + TypeCheckResult p = + parseAndTypeCheckWithScope("/** @constructor */function A(){};" + + "var a = new A();"); + + JSType aType = p.scope.getVar("a").getType(); + assertTrue(aType instanceof ObjectType); + ObjectType aObjectType = (ObjectType) aType; + assertEquals("A", aObjectType.getConstructor().getReferenceName()); + } + + public void testNew7() throws Exception { + testTypes("/** @param {Function} opt_constructor */" + + "function foo(opt_constructor) {" + + "if (opt_constructor) { new opt_constructor; }" + + "}"); + } + + public void testNew8() throws Exception { + testTypes("/** @param {Function} opt_constructor */" + + "function foo(opt_constructor) {" + + "new opt_constructor;" + + "}"); + } + + public void testNew9() throws Exception { + testTypes("/** @param {Function} opt_constructor */" + + "function foo(opt_constructor) {" + + "new (opt_constructor || Array);" + + "}"); + } + + public void testNew10() throws Exception { + testTypes("var goog = {};" + + "/** @param {Function} opt_constructor */" + + "goog.Foo = function (opt_constructor) {" + + "new (opt_constructor || Array);" + + "}"); + } + + public void testNew11() throws Exception { + testTypes("/** @param {Function} c1 */" + + "function f(c1) {" + + " var c2 = function(){};" + + " c1.prototype = new c2;" + + "}", TypeCheck.NOT_A_CONSTRUCTOR); + } + + public void testNew12() throws Exception { + TypeCheckResult p = parseAndTypeCheckWithScope("var a = new Array();"); + Var a = p.scope.getVar("a"); + + assertTypeEquals(ARRAY_TYPE, a.getType()); + } + + public void testNew13() throws Exception { + TypeCheckResult p = parseAndTypeCheckWithScope( + "/** @constructor */function FooBar(){};" + + "var a = new FooBar();"); + Var a = p.scope.getVar("a"); + + assertTrue(a.getType() instanceof ObjectType); + assertEquals("FooBar", a.getType().toString()); + } + + public void testNew14() throws Exception { + TypeCheckResult p = parseAndTypeCheckWithScope( + "/** @constructor */var FooBar = function(){};" + + "var a = new FooBar();"); + Var a = p.scope.getVar("a"); + + assertTrue(a.getType() instanceof ObjectType); + assertEquals("FooBar", a.getType().toString()); + } + + public void testNew15() throws Exception { + TypeCheckResult p = parseAndTypeCheckWithScope( + "var goog = {};" + + "/** @constructor */goog.A = function(){};" + + "var a = new goog.A();"); + Var a = p.scope.getVar("a"); + + assertTrue(a.getType() instanceof ObjectType); + assertEquals("goog.A", a.getType().toString()); + } + + public void testNew16() throws Exception { + testTypes( + "/** \n" + + " * @param {string} x \n" + + " * @constructor \n" + + " */" + + "function Foo(x) {}" + + "function g() { new Foo(1); }", + "actual parameter 1 of Foo does not match formal parameter\n" + + "found : number\n" + + "required: string"); + } + + public void testName1() throws Exception { + assertTypeEquals(VOID_TYPE, testNameNode("undefined")); + } + + public void testName2() throws Exception { + assertTypeEquals(OBJECT_FUNCTION_TYPE, testNameNode("Object")); + } + + public void testName3() throws Exception { + assertTypeEquals(ARRAY_FUNCTION_TYPE, testNameNode("Array")); + } + + public void testName4() throws Exception { + assertTypeEquals(DATE_FUNCTION_TYPE, testNameNode("Date")); + } + + public void testName5() throws Exception { + assertTypeEquals(REGEXP_FUNCTION_TYPE, testNameNode("RegExp")); + } + + /** + * Type checks a NAME node and retrieve its type. + */ + private JSType testNameNode(String name) { + Node node = Node.newString(Token.NAME, name); + Node parent = new Node(Token.SCRIPT, node); + parent.setInputId(new InputId("code")); + + Node externs = new Node(Token.SCRIPT); + externs.setInputId(new InputId("externs")); + + Node externAndJsRoot = new Node(Token.BLOCK, externs, parent); + externAndJsRoot.setIsSyntheticBlock(true); + + makeTypeCheck().processForTesting(null, parent); + return node.getJSType(); + } + + public void testBitOperation1() throws Exception { + testTypes("/**@return {void}*/function foo(){ ~foo(); }", + "operator ~ cannot be applied to undefined"); + } + + public void testBitOperation2() throws Exception { + testTypes("/**@return {void}*/function foo(){var a = foo()<<3;}", + "operator << cannot be applied to undefined"); + } + + public void testBitOperation3() throws Exception { + testTypes("/**@return {void}*/function foo(){var a = 3<>>3;}", + "operator >>> cannot be applied to undefined"); + } + + public void testBitOperation5() throws Exception { + testTypes("/**@return {void}*/function foo(){var a = 3>>>foo();}", + "operator >>> cannot be applied to undefined"); + } + + public void testBitOperation6() throws Exception { + testTypes("/**@return {!Object}*/function foo(){var a = foo()&3;}", + "bad left operand to bitwise operator\n" + + "found : Object\n" + + "required: (boolean|null|number|string|undefined)"); + } + + public void testBitOperation7() throws Exception { + testTypes("var x = null; x |= undefined; x &= 3; x ^= '3'; x |= true;"); + } + + public void testBitOperation8() throws Exception { + testTypes("var x = void 0; x |= new Number(3);"); + } + + public void testBitOperation9() throws Exception { + testTypes("var x = void 0; x |= {};", + "bad right operand to bitwise operator\n" + + "found : {}\n" + + "required: (boolean|null|number|string|undefined)"); + } + + public void testCall1() throws Exception { + testTypes("3();", "number expressions are not callable"); + } + + public void testCall2() throws Exception { + testTypes("/** @param {!Number} foo*/function bar(foo){ bar('abc'); }", + "actual parameter 1 of bar does not match formal parameter\n" + + "found : string\n" + + "required: Number"); + } + + public void testCall3() throws Exception { + // We are checking that an unresolved named type can successfully + // meet with a functional type to produce a callable type. + testTypes("/** @type {Function|undefined} */var opt_f;" + + "/** @type {some.unknown.type} */var f1;" + + "var f2 = opt_f || f1;" + + "f2();", + "Bad type annotation. Unknown type some.unknown.type"); + } + + public void testCall4() throws Exception { + testTypes("/**@param {!RegExp} a*/var foo = function bar(a){ bar('abc'); }", + "actual parameter 1 of bar does not match formal parameter\n" + + "found : string\n" + + "required: RegExp"); + } + + public void testCall5() throws Exception { + testTypes("/**@param {!RegExp} a*/var foo = function bar(a){ foo('abc'); }", + "actual parameter 1 of foo does not match formal parameter\n" + + "found : string\n" + + "required: RegExp"); + } + + public void testCall6() throws Exception { + testTypes("/** @param {!Number} foo*/function bar(foo){}" + + "bar('abc');", + "actual parameter 1 of bar does not match formal parameter\n" + + "found : string\n" + + "required: Number"); + } + + public void testCall7() throws Exception { + testTypes("/** @param {!RegExp} a*/var foo = function bar(a){};" + + "foo('abc');", + "actual parameter 1 of foo does not match formal parameter\n" + + "found : string\n" + + "required: RegExp"); + } + + public void testCall8() throws Exception { + testTypes("/** @type {Function|number} */var f;f();", + "(Function|number) expressions are " + + "not callable"); + } + + public void testCall9() throws Exception { + testTypes( + "var goog = {};" + + "/** @constructor */ goog.Foo = function() {};" + + "/** @param {!goog.Foo} a */ var bar = function(a){};" + + "bar('abc');", + "actual parameter 1 of bar does not match formal parameter\n" + + "found : string\n" + + "required: goog.Foo"); + } + + public void testCall10() throws Exception { + testTypes("/** @type {Function} */var f;f();"); + } + + public void testCall11() throws Exception { + testTypes("var f = new Function(); f();"); + } + + public void testFunctionCall1() throws Exception { + testTypes( + "/** @param {number} x */ var foo = function(x) {};" + + "foo.call(null, 3);"); + } + + public void testFunctionCall2() throws Exception { + testTypes( + "/** @param {number} x */ var foo = function(x) {};" + + "foo.call(null, 'bar');", + "actual parameter 2 of foo.call does not match formal parameter\n" + + "found : string\n" + + "required: number"); + } + + public void testFunctionCall3() throws Exception { + testTypes( + "/** @param {number} x \n * @constructor */ " + + "var Foo = function(x) { this.bar.call(null, x); };" + + "/** @type {function(number)} */ Foo.prototype.bar;"); + } + + public void testFunctionCall4() throws Exception { + testTypes( + "/** @param {string} x \n * @constructor */ " + + "var Foo = function(x) { this.bar.call(null, x); };" + + "/** @type {function(number)} */ Foo.prototype.bar;", + "actual parameter 2 of this.bar.call " + + "does not match formal parameter\n" + + "found : string\n" + + "required: number"); + } + + public void testFunctionCall5() throws Exception { + testTypes( + "/** @param {Function} handler \n * @constructor */ " + + "var Foo = function(handler) { handler.call(this, x); };"); + } + + public void testFunctionCall6() throws Exception { + testTypes( + "/** @param {Function} handler \n * @constructor */ " + + "var Foo = function(handler) { handler.apply(this, x); };"); + } + + public void testFunctionCall7() throws Exception { + testTypes( + "/** @param {Function} handler \n * @param {Object} opt_context */ " + + "var Foo = function(handler, opt_context) { " + + " handler.call(opt_context, x);" + + "};"); + } + + public void testFunctionCall8() throws Exception { + testTypes( + "/** @param {Function} handler \n * @param {Object} opt_context */ " + + "var Foo = function(handler, opt_context) { " + + " handler.apply(opt_context, x);" + + "};"); + } + + public void testCast2() throws Exception { + // can upcast to a base type. + testTypes("/** @constructor */function base() {}\n" + + "/** @constructor\n @extends {base} */function derived() {}\n" + + "/** @type {base} */ var baz = new derived();\n"); + } + + public void testCast3() throws Exception { + // cannot downcast + testTypes("/** @constructor */function base() {}\n" + + "/** @constructor @extends {base} */function derived() {}\n" + + "/** @type {!derived} */ var baz = new base();\n", + "initializing variable\n" + + "found : base\n" + + "required: derived"); + } + + public void testCast4() throws Exception { + // downcast must be explicit + testTypes("/** @constructor */function base() {}\n" + + "/** @constructor\n * @extends {base} */function derived() {}\n" + + "/** @type {!derived} */ var baz = " + + "/** @type {!derived} */(new base());\n"); + } + + public void testCast5() throws Exception { + // cannot explicitly cast to an unrelated type + testTypes("/** @constructor */function foo() {}\n" + + "/** @constructor */function bar() {}\n" + + "var baz = /** @type {!foo} */(new bar);\n", + "invalid cast - must be a subtype or supertype\n" + + "from: bar\n" + + "to : foo"); + } + + public void testCast6() throws Exception { + // can explicitly cast to a subtype or supertype + testTypes("/** @constructor */function foo() {}\n" + + "/** @constructor \n @extends foo */function bar() {}\n" + + "var baz = /** @type {!bar} */(new bar);\n" + + "var baz = /** @type {!foo} */(new foo);\n" + + "var baz = /** @type {bar} */(new bar);\n" + + "var baz = /** @type {foo} */(new foo);\n" + + "var baz = /** @type {!foo} */(new bar);\n" + + "var baz = /** @type {!bar} */(new foo);\n" + + "var baz = /** @type {foo} */(new bar);\n" + + "var baz = /** @type {bar} */(new foo);\n"); + } + + public void testCast7() throws Exception { + testTypes("var x = /** @type {foo} */ (new Object());", + "Bad type annotation. Unknown type foo"); + } + + public void testCast8() throws Exception { + testTypes("function f() { return /** @type {foo} */ (new Object()); }", + "Bad type annotation. Unknown type foo"); + } + + public void testCast9() throws Exception { + testTypes("var foo = {};" + + "function f() { return /** @type {foo} */ (new Object()); }", + "Bad type annotation. Unknown type foo"); + } + + public void testCast10() throws Exception { + testTypes("var foo = function() {};" + + "function f() { return /** @type {foo} */ (new Object()); }", + "Bad type annotation. Unknown type foo"); + } + + public void testCast11() throws Exception { + testTypes("var goog = {}; goog.foo = {};" + + "function f() { return /** @type {goog.foo} */ (new Object()); }", + "Bad type annotation. Unknown type goog.foo"); + } + + public void testCast12() throws Exception { + testTypes("var goog = {}; goog.foo = function() {};" + + "function f() { return /** @type {goog.foo} */ (new Object()); }", + "Bad type annotation. Unknown type goog.foo"); + } + + public void testCast13() throws Exception { + // Test to make sure that the forward-declaration still allows for + // a warning. + testClosureTypes("var goog = {}; " + + "goog.addDependency('zzz.js', ['goog.foo'], []);" + + "goog.foo = function() {};" + + "function f() { return /** @type {goog.foo} */ (new Object()); }", + "Bad type annotation. Unknown type goog.foo"); + } + + public void testCast14() throws Exception { + // Test to make sure that the forward-declaration still prevents + // some warnings. + testClosureTypes("var goog = {}; " + + "goog.addDependency('zzz.js', ['goog.bar'], []);" + + "function f() { return /** @type {goog.bar} */ (new Object()); }", + null); + } + + public void testCast15() throws Exception { + // This fixes a bug where a type cast on an object literal + // would cause a run-time cast exception if the node was visited + // more than once. + // + // Some code assumes that an object literal must have a object type, + // while because of the cast, it could have any type (including + // a union). + testTypes( + "for (var i = 0; i < 10; i++) {" + + "var x = /** @type {Object|number} */ ({foo: 3});" + + "/** @param {number} x */ function f(x) {}" + + "f(x.foo);" + + "f([].foo);" + + "}", + "Property foo never defined on Array"); + } + + public void testCast16() throws Exception { + // Mostly verifying that rhino actually understands these JsDocs. + testTypes("/** @constructor */ function Foo() {} \n" + + "/** @type {Foo} */ var x = /** @type {Foo} */ ({})"); + + testTypes("/** @constructor */ function Foo() {} \n" + + "/** @type {Foo} */ var x = (/** @type {Foo} */ y)"); + } + + public void testNestedCasts() throws Exception { + testTypes("/** @constructor */var T = function() {};\n" + + "/** @constructor */var V = function() {};\n" + + "/**\n" + + "* @param {boolean} b\n" + + "* @return {T|V}\n" + + "*/\n" + + "function f(b) { return b ? new T() : new V(); }\n" + + "/**\n" + + "* @param {boolean} b\n" + + "* @return {boolean|undefined}\n" + + "*/\n" + + "function g(b) { return b ? true : undefined; }\n" + + "/** @return {T} */\n" + + "function h() {\n" + + "return /** @type {T} */ (f(/** @type {boolean} */ (g(true))));\n" + + "}"); + } + + public void testNativeCast1() throws Exception { + testTypes( + "/** @param {number} x */ function f(x) {}" + + "f(String(true));", + "actual parameter 1 of f does not match formal parameter\n" + + "found : string\n" + + "required: number"); + } + + public void testNativeCast2() throws Exception { + testTypes( + "/** @param {string} x */ function f(x) {}" + + "f(Number(true));", + "actual parameter 1 of f does not match formal parameter\n" + + "found : number\n" + + "required: string"); + } + + public void testNativeCast3() throws Exception { + testTypes( + "/** @param {number} x */ function f(x) {}" + + "f(Boolean(''));", + "actual parameter 1 of f does not match formal parameter\n" + + "found : boolean\n" + + "required: number"); + } + + public void testNativeCast4() throws Exception { + testTypes( + "/** @param {number} x */ function f(x) {}" + + "f(Error(''));", + "actual parameter 1 of f does not match formal parameter\n" + + "found : Error\n" + + "required: number"); + } + + public void testBadConstructorCall() throws Exception { + testTypes( + "/** @constructor */ function Foo() {}" + + "Foo();", + "Constructor function (new:Foo): undefined should be called " + + "with the \"new\" keyword"); + } + + public void testTypeof() throws Exception { + testTypes("/**@return {void}*/function foo(){ var a = typeof foo(); }"); + } + + public void testConstructorType1() throws Exception { + testTypes("/**@constructor*/function Foo(){}" + + "/**@type{!Foo}*/var f = new Date();", + "initializing variable\n" + + "found : Date\n" + + "required: Foo"); + } + + public void testConstructorType2() throws Exception { + testTypes("/**@constructor*/function Foo(){\n" + + "/**@type{Number}*/this.bar = new Number(5);\n" + + "}\n" + + "/**@type{Foo}*/var f = new Foo();\n" + + "/**@type{Number}*/var n = f.bar;"); + } + + public void testConstructorType3() throws Exception { + // Reverse the declaration order so that we know that Foo is getting set + // even on an out-of-order declaration sequence. + testTypes("/**@type{Foo}*/var f = new Foo();\n" + + "/**@type{Number}*/var n = f.bar;" + + "/**@constructor*/function Foo(){\n" + + "/**@type{Number}*/this.bar = new Number(5);\n" + + "}\n"); + } + + public void testConstructorType4() throws Exception { + testTypes("/**@constructor*/function Foo(){\n" + + "/**@type{!Number}*/this.bar = new Number(5);\n" + + "}\n" + + "/**@type{!Foo}*/var f = new Foo();\n" + + "/**@type{!String}*/var n = f.bar;", + "initializing variable\n" + + "found : Number\n" + + "required: String"); + } + + public void testConstructorType5() throws Exception { + testTypes("/**@constructor*/function Foo(){}\n" + + "if (Foo){}\n"); + } + + public void testConstructorType6() throws Exception { + testTypes("/** @constructor */\n" + + "function bar() {}\n" + + "function _foo() {\n" + + " /** @param {bar} x */\n" + + " function f(x) {}\n" + + "}"); + } + + public void testConstructorType7() throws Exception { + TypeCheckResult p = + parseAndTypeCheckWithScope("/** @constructor */function A(){};"); + + JSType type = p.scope.getVar("A").getType(); + assertTrue(type instanceof FunctionType); + FunctionType fType = (FunctionType) type; + assertEquals("A", fType.getReferenceName()); + } + + public void testAnonymousType1() throws Exception { + testTypes("function f() { return {}; }" + + "/** @constructor */\n" + + "f().bar = function() {};"); + } + + public void testAnonymousType2() throws Exception { + testTypes("function f() { return {}; }" + + "/** @interface */\n" + + "f().bar = function() {};"); + } + + public void testAnonymousType3() throws Exception { + testTypes("function f() { return {}; }" + + "/** @enum */\n" + + "f().bar = {FOO: 1};"); + } + + public void testBang1() throws Exception { + testTypes("/** @param {Object} x\n@return {!Object} */\n" + + "function f(x) { return x; }", + "inconsistent return type\n" + + "found : (Object|null|undefined)\n" + + "required: Object"); + } + + public void testBang2() throws Exception { + testTypes("/** @param {Object} x\n@return {!Object} */\n" + + "function f(x) { return x ? x : new Object(); }"); + } + + public void testBang3() throws Exception { + testTypes("/** @param {Object} x\n@return {!Object} */\n" + + "function f(x) { return /** @type {!Object} */ (x); }"); + } + + public void testBang4() throws Exception { + testTypes("/**@param {Object} x\n@param {Object} y\n@return {boolean}*/\n" + + "function f(x, y) {\n" + + "if (typeof x != 'undefined') { return x == y; }\n" + + "else { return x != y; }\n}"); + } + + public void testBang5() throws Exception { + testTypes("/**@param {Object} x\n@param {Object} y\n@return {boolean}*/\n" + + "function f(x, y) { return !!x && x == y; }"); + } + + public void testBang6() throws Exception { + testTypes("/** @param {Object?} x\n@return {Object} */\n" + + "function f(x) { return x; }"); + } + + public void testBang7() throws Exception { + testTypes("/**@param {(Object,string,null)} x\n" + + "@return {(Object,string)}*/function f(x) { return x; }"); + } + + public void testDefinePropertyOnNullableObject1() throws Exception { + testTypes("/** @type {Object} */ var n = {};\n" + + "/** @type {number} */ n.x = 1;\n" + + "/** @return {boolean} */function f() { return n.x; }", + "inconsistent return type\n" + + "found : number\n" + + "required: boolean"); + } + + public void testDefinePropertyOnNullableObject2() throws Exception { + testTypes("/** @constructor */ var T = function() {};\n" + + "/** @param {T} t\n@return {boolean} */function f(t) {\n" + + "t.x = 1; return t.x; }", + "inconsistent return type\n" + + "found : number\n" + + "required: boolean"); + } + + public void testUnknownConstructorInstanceType1() throws Exception { + testTypes("/** @return {Array} */ function g(f) { return new f(); }"); + } + + public void testUnknownConstructorInstanceType2() throws Exception { + testTypes("function g(f) { return /** @type Array */ (new f()); }"); + } + + public void testUnknownConstructorInstanceType3() throws Exception { + testTypes("function g(f) { var x = new f(); x.a = 1; return x; }"); + } + + public void testUnknownPrototypeChain() throws Exception { + testTypes("/**\n" + + "* @param {Object} co\n" + + " * @return {Object}\n" + + " */\n" + + "function inst(co) {\n" + + " /** @constructor */\n" + + " var c = function() {};\n" + + " c.prototype = co.prototype;\n" + + " return new c;\n" + + "}"); + } + + public void testNamespacedConstructor() throws Exception { + Node root = parseAndTypeCheck( + "var goog = {};" + + "/** @constructor */ goog.MyClass = function() {};" + + "/** @return {!goog.MyClass} */ " + + "function foo() { return new goog.MyClass(); }"); + + JSType typeOfFoo = root.getLastChild().getJSType(); + assert(typeOfFoo instanceof FunctionType); + + JSType retType = ((FunctionType) typeOfFoo).getReturnType(); + assert(retType instanceof ObjectType); + assertEquals("goog.MyClass", ((ObjectType) retType).getReferenceName()); + } + + public void testComplexNamespace() throws Exception { + String js = + "var goog = {};" + + "goog.foo = {};" + + "goog.foo.bar = 5;"; + + TypeCheckResult p = parseAndTypeCheckWithScope(js); + + // goog type in the scope + JSType googScopeType = p.scope.getVar("goog").getType(); + assertTrue(googScopeType instanceof ObjectType); + assertTrue("foo property not present on goog type", + ((ObjectType) googScopeType).hasProperty("foo")); + assertFalse("bar property present on goog type", + ((ObjectType) googScopeType).hasProperty("bar")); + + // goog type on the VAR node + Node varNode = p.root.getFirstChild(); + assertEquals(Token.VAR, varNode.getType()); + JSType googNodeType = varNode.getFirstChild().getJSType(); + assertTrue(googNodeType instanceof ObjectType); + + // goog scope type and goog type on VAR node must be the same + assertTrue(googScopeType == googNodeType); + + // goog type on the left of the GETPROP node (under fist ASSIGN) + Node getpropFoo1 = varNode.getNext().getFirstChild().getFirstChild(); + assertEquals(Token.GETPROP, getpropFoo1.getType()); + assertEquals("goog", getpropFoo1.getFirstChild().getString()); + JSType googGetpropFoo1Type = getpropFoo1.getFirstChild().getJSType(); + assertTrue(googGetpropFoo1Type instanceof ObjectType); + + // still the same type as the one on the variable + assertTrue(googGetpropFoo1Type == googScopeType); + + // the foo property should be defined on goog + JSType googFooType = ((ObjectType) googScopeType).getPropertyType("foo"); + assertTrue(googFooType instanceof ObjectType); + + // goog type on the left of the GETPROP lower level node + // (under second ASSIGN) + Node getpropFoo2 = varNode.getNext().getNext() + .getFirstChild().getFirstChild().getFirstChild(); + assertEquals(Token.GETPROP, getpropFoo2.getType()); + assertEquals("goog", getpropFoo2.getFirstChild().getString()); + JSType googGetpropFoo2Type = getpropFoo2.getFirstChild().getJSType(); + assertTrue(googGetpropFoo2Type instanceof ObjectType); + + // still the same type as the one on the variable + assertTrue(googGetpropFoo2Type == googScopeType); + + // goog.foo type on the left of the top-level GETPROP node + // (under second ASSIGN) + JSType googFooGetprop2Type = getpropFoo2.getJSType(); + assertTrue("goog.foo incorrectly annotated in goog.foo.bar selection", + googFooGetprop2Type instanceof ObjectType); + ObjectType googFooGetprop2ObjectType = (ObjectType) googFooGetprop2Type; + assertFalse("foo property present on goog.foo type", + googFooGetprop2ObjectType.hasProperty("foo")); + assertTrue("bar property not present on goog.foo type", + googFooGetprop2ObjectType.hasProperty("bar")); + assertTypeEquals("bar property on goog.foo type incorrectly inferred", + NUMBER_TYPE, googFooGetprop2ObjectType.getPropertyType("bar")); + } + + public void testAddingMethodsUsingPrototypeIdiomSimpleNamespace() + throws Exception { + Node js1Node = parseAndTypeCheck( + "/** @constructor */function A() {}" + + "A.prototype.m1 = 5"); + + ObjectType instanceType = getInstanceType(js1Node); + assertEquals(NATIVE_PROPERTIES_COUNT + 1, + instanceType.getPropertiesCount()); + checkObjectType(instanceType, "m1", NUMBER_TYPE); + } + + public void testAddingMethodsUsingPrototypeIdiomComplexNamespace1() + throws Exception { + TypeCheckResult p = parseAndTypeCheckWithScope( + "var goog = {};" + + "goog.A = /** @constructor */function() {};" + + "/** @type number */goog.A.prototype.m1 = 5"); + + testAddingMethodsUsingPrototypeIdiomComplexNamespace(p); + } + + public void testAddingMethodsUsingPrototypeIdiomComplexNamespace2() + throws Exception { + TypeCheckResult p = parseAndTypeCheckWithScope( + "var goog = {};" + + "/** @constructor */goog.A = function() {};" + + "/** @type number */goog.A.prototype.m1 = 5"); + + testAddingMethodsUsingPrototypeIdiomComplexNamespace(p); + } + + private void testAddingMethodsUsingPrototypeIdiomComplexNamespace( + TypeCheckResult p) { + ObjectType goog = (ObjectType) p.scope.getVar("goog").getType(); + assertEquals(NATIVE_PROPERTIES_COUNT + 1, goog.getPropertiesCount()); + JSType googA = goog.getPropertyType("A"); + assertNotNull(googA); + assertTrue(googA instanceof FunctionType); + FunctionType googAFunction = (FunctionType) googA; + ObjectType classA = googAFunction.getInstanceType(); + assertEquals(NATIVE_PROPERTIES_COUNT + 1, classA.getPropertiesCount()); + checkObjectType(classA, "m1", NUMBER_TYPE); + } + + public void testAddingMethodsPrototypeIdiomAndObjectLiteralSimpleNamespace() + throws Exception { + Node js1Node = parseAndTypeCheck( + "/** @constructor */function A() {}" + + "A.prototype = {m1: 5, m2: true}"); + + ObjectType instanceType = getInstanceType(js1Node); + assertEquals(NATIVE_PROPERTIES_COUNT + 2, + instanceType.getPropertiesCount()); + checkObjectType(instanceType, "m1", NUMBER_TYPE); + checkObjectType(instanceType, "m2", BOOLEAN_TYPE); + } + + public void testDontAddMethodsIfNoConstructor() + throws Exception { + Node js1Node = parseAndTypeCheck( + "function A() {}" + + "A.prototype = {m1: 5, m2: true}"); + + JSType functionAType = js1Node.getFirstChild().getJSType(); + assertEquals("function (): undefined", functionAType.toString()); + assertTypeEquals(UNKNOWN_TYPE, + U2U_FUNCTION_TYPE.getPropertyType("m1")); + assertTypeEquals(UNKNOWN_TYPE, + U2U_FUNCTION_TYPE.getPropertyType("m2")); + } + + public void testFunctionAssignement() throws Exception { + testTypes("/**" + + "* @param {string} ph0" + + "* @param {string} ph1" + + "* @return {string}" + + "*/" + + "function MSG_CALENDAR_ACCESS_ERROR(ph0, ph1) {return ''}" + + "/** @type {Function} */" + + "var MSG_CALENDAR_ADD_ERROR = MSG_CALENDAR_ACCESS_ERROR;"); + } + + public void testAddMethodsPrototypeTwoWays() throws Exception { + Node js1Node = parseAndTypeCheck( + "/** @constructor */function A() {}" + + "A.prototype = {m1: 5, m2: true};" + + "A.prototype.m3 = 'third property!';"); + + ObjectType instanceType = getInstanceType(js1Node); + assertEquals("A", instanceType.toString()); + assertEquals(NATIVE_PROPERTIES_COUNT + 3, + instanceType.getPropertiesCount()); + checkObjectType(instanceType, "m1", NUMBER_TYPE); + checkObjectType(instanceType, "m2", BOOLEAN_TYPE); + checkObjectType(instanceType, "m3", STRING_TYPE); + } + + public void testPrototypePropertyTypes() throws Exception { + Node js1Node = parseAndTypeCheck( + "/** @constructor */function A() {\n" + + " /** @type string */ this.m1;\n" + + " /** @type Object? */ this.m2 = {};\n" + + " /** @type boolean */ this.m3;\n" + + "}\n" + + "/** @type string */ A.prototype.m4;\n" + + "/** @type number */ A.prototype.m5 = 0;\n" + + "/** @type boolean */ A.prototype.m6;\n"); + + ObjectType instanceType = getInstanceType(js1Node); + assertEquals(NATIVE_PROPERTIES_COUNT + 6, + instanceType.getPropertiesCount()); + checkObjectType(instanceType, "m1", STRING_TYPE); + checkObjectType(instanceType, "m2", + createUnionType(createUnionType(OBJECT_TYPE, NULL_TYPE), VOID_TYPE)); + checkObjectType(instanceType, "m3", BOOLEAN_TYPE); + checkObjectType(instanceType, "m4", STRING_TYPE); + checkObjectType(instanceType, "m5", NUMBER_TYPE); + checkObjectType(instanceType, "m6", BOOLEAN_TYPE); + } + + public void testValueTypeBuiltInPrototypePropertyType() throws Exception { + Node node = parseAndTypeCheck("\"x\".charAt(0)"); + assertTypeEquals(STRING_TYPE, node.getFirstChild().getFirstChild().getJSType()); + } + + public void testDeclareBuiltInConstructor() throws Exception { + // Built-in prototype properties should be accessible + // even if the built-in constructor is declared. + Node node = parseAndTypeCheck( + "/** @constructor */ var String = function(opt_str) {};\n" + + "(new String(\"x\")).charAt(0)"); + assertTypeEquals(STRING_TYPE, node.getLastChild().getFirstChild().getJSType()); + } + + public void testExtendBuiltInType1() throws Exception { + String externs = + "/** @constructor */ var String = function(opt_str) {};\n" + + "/**\n" + + "* @param {number} start\n" + + "* @param {number} opt_length\n" + + "* @return {string}\n" + + "*/\n" + + "String.prototype.substr = function(start, opt_length) {};\n"; + Node n1 = parseAndTypeCheck(externs + "(new String(\"x\")).substr(0,1);"); + assertTypeEquals(STRING_TYPE, n1.getLastChild().getFirstChild().getJSType()); + } + + public void testExtendBuiltInType2() throws Exception { + String externs = + "/** @constructor */ var String = function(opt_str) {};\n" + + "/**\n" + + "* @param {number} start\n" + + "* @param {number} opt_length\n" + + "* @return {string}\n" + + "*/\n" + + "String.prototype.substr = function(start, opt_length) {};\n"; + Node n2 = parseAndTypeCheck(externs + "\"x\".substr(0,1);"); + assertTypeEquals(STRING_TYPE, n2.getLastChild().getFirstChild().getJSType()); + } + + public void testExtendFunction1() throws Exception { + Node n = parseAndTypeCheck("/**@return {number}*/Function.prototype.f = " + + "function() { return 1; };\n" + + "(new Function()).f();"); + JSType type = n.getLastChild().getLastChild().getJSType(); + assertTypeEquals(NUMBER_TYPE, type); + } + + public void testExtendFunction2() throws Exception { + Node n = parseAndTypeCheck("/**@return {number}*/Function.prototype.f = " + + "function() { return 1; };\n" + + "(function() {}).f();"); + JSType type = n.getLastChild().getLastChild().getJSType(); + assertTypeEquals(NUMBER_TYPE, type); + } + + public void testInheritanceCheck1() throws Exception { + testTypes( + "/** @constructor */function Super() {};" + + "/** @constructor\n @extends {Super} */function Sub() {};" + + "Sub.prototype.foo = function() {};"); + } + + public void testInheritanceCheck2() throws Exception { + testTypes( + "/** @constructor */function Super() {};" + + "/** @constructor\n @extends {Super} */function Sub() {};" + + "/** @override */Sub.prototype.foo = function() {};", + "property foo not defined on any superclass of Sub"); + } + + public void testInheritanceCheck3() throws Exception { + testTypes( + "/** @constructor */function Super() {};" + + "Super.prototype.foo = function() {};" + + "/** @constructor\n @extends {Super} */function Sub() {};" + + "Sub.prototype.foo = function() {};", + "property foo already defined on superclass Super; " + + "use @override to override it"); + } + + public void testInheritanceCheck4() throws Exception { + testTypes( + "/** @constructor */function Super() {};" + + "Super.prototype.foo = function() {};" + + "/** @constructor\n @extends {Super} */function Sub() {};" + + "/** @override */Sub.prototype.foo = function() {};"); + } + + public void testInheritanceCheck5() throws Exception { + testTypes( + "/** @constructor */function Root() {};" + + "Root.prototype.foo = function() {};" + + "/** @constructor\n @extends {Root} */function Super() {};" + + "/** @constructor\n @extends {Super} */function Sub() {};" + + "Sub.prototype.foo = function() {};", + "property foo already defined on superclass Root; " + + "use @override to override it"); + } + + public void testInheritanceCheck6() throws Exception { + testTypes( + "/** @constructor */function Root() {};" + + "Root.prototype.foo = function() {};" + + "/** @constructor\n @extends {Root} */function Super() {};" + + "/** @constructor\n @extends {Super} */function Sub() {};" + + "/** @override */Sub.prototype.foo = function() {};"); + } + + public void testInheritanceCheck7() throws Exception { + testTypes( + "var goog = {};" + + "/** @constructor */goog.Super = function() {};" + + "goog.Super.prototype.foo = 3;" + + "/** @constructor\n @extends {goog.Super} */goog.Sub = function() {};" + + "goog.Sub.prototype.foo = 5;"); + } + + public void testInheritanceCheck8() throws Exception { + testTypes( + "var goog = {};" + + "/** @constructor */goog.Super = function() {};" + + "goog.Super.prototype.foo = 3;" + + "/** @constructor\n @extends {goog.Super} */goog.Sub = function() {};" + + "/** @override */goog.Sub.prototype.foo = 5;"); + } + + public void testInheritanceCheck9_1() throws Exception { + testTypes( + "/** @constructor */function Super() {};" + + "Super.prototype.foo = function() { return 3; };" + + "/** @constructor\n @extends {Super} */function Sub() {};" + + "/** @override\n @return {number} */Sub.prototype.foo =\n" + + "function() { return 1; };"); + } + + public void testInheritanceCheck9_2() throws Exception { + testTypes( + "/** @constructor */function Super() {};" + + "/** @return {number} */" + + "Super.prototype.foo = function() { return 1; };" + + "/** @constructor\n @extends {Super} */function Sub() {};" + + "/** @override */Sub.prototype.foo =\n" + + "function() {};"); + } + + public void testInheritanceCheck9_3() throws Exception { + testTypes( + "/** @constructor */function Super() {};" + + "/** @return {number} */" + + "Super.prototype.foo = function() { return 1; };" + + "/** @constructor\n @extends {Super} */function Sub() {};" + + "/** @override\n @return {string} */Sub.prototype.foo =\n" + + "function() { return \"some string\" };", + "mismatch of the foo property type and the type of the property it " + + "overrides from superclass Super\n" + + "original: function (this:Super): number\n" + + "override: function (this:Sub): string"); + } + + public void testInheritanceCheck10_1() throws Exception { + testTypes( + "/** @constructor */function Root() {};" + + "Root.prototype.foo = function() { return 4; };" + + "/** @constructor\n @extends {Root} */function Super() {};" + + "/** @constructor\n @extends {Super} */function Sub() {};" + + "/** @override\n @return {number} */Sub.prototype.foo =\n" + + "function() { return 1; };"); + } + + public void testInheritanceCheck10_2() throws Exception { + testTypes( + "/** @constructor */function Root() {};" + + "/** @return {number} */" + + "Root.prototype.foo = function() { return 1; };" + + "/** @constructor\n @extends {Root} */function Super() {};" + + "/** @constructor\n @extends {Super} */function Sub() {};" + + "/** @override */Sub.prototype.foo =\n" + + "function() {};"); + } + + public void testInheritanceCheck10_3() throws Exception { + testTypes( + "/** @constructor */function Root() {};" + + "/** @return {number} */" + + "Root.prototype.foo = function() { return 1; };" + + "/** @constructor\n @extends {Root} */function Super() {};" + + "/** @constructor\n @extends {Super} */function Sub() {};" + + "/** @override\n @return {string} */Sub.prototype.foo =\n" + + "function() { return \"some string\" };", + "mismatch of the foo property type and the type of the property it " + + "overrides from superclass Root\n" + + "original: function (this:Root): number\n" + + "override: function (this:Sub): string"); + } + + public void testInterfaceInheritanceCheck11() throws Exception { + testTypes( + "/** @constructor */function Super() {};" + + "/** @param {number} bar */Super.prototype.foo = function(bar) {};" + + "/** @constructor\n @extends {Super} */function Sub() {};" + + "/** @override\n @param {string} bar */Sub.prototype.foo =\n" + + "function(bar) {};", + "mismatch of the foo property type and the type of the property it " + + "overrides from superclass Super\n" + + "original: function (this:Super, number): undefined\n" + + "override: function (this:Sub, string): undefined"); + } + + public void testInheritanceCheck12() throws Exception { + testTypes( + "var goog = {};" + + "/** @constructor */goog.Super = function() {};" + + "goog.Super.prototype.foo = 3;" + + "/** @constructor\n @extends {goog.Super} */goog.Sub = function() {};" + + "/** @override */goog.Sub.prototype.foo = \"some string\";"); + } + + public void testInheritanceCheck13() throws Exception { + testTypes( + "var goog = {};\n" + + "/** @constructor\n @extends {goog.Missing} */function Sub() {};" + + "/** @override */Sub.prototype.foo = function() {};", + "Bad type annotation. Unknown type goog.Missing"); + } + + public void testInheritanceCheck14() throws Exception { + testTypes( + "var goog = {};\n" + + "/** @constructor\n @extends {goog.Missing} */\n" + + "goog.Super = function() {};\n" + + "/** @constructor\n @extends {goog.Super} */function Sub() {};" + + "/** @override */Sub.prototype.foo = function() {};", + "Bad type annotation. Unknown type goog.Missing"); + } + + // TODO(user): We should support this way of declaring properties as it is + // widely used. + //public void testInheritanceCheck15() throws Exception { + // testTypes( + // "/** @constructor */function Super() {};" + + // "/** @param {number} bar */Super.prototype.foo;" + + // "/** @constructor\n @extends {Super} */function Sub() {};" + + // "/** @override\n @param {number} bar */Sub.prototype.foo =\n" + + // "function(bar) {};"); + //} + +// public void testInterfacePropertyOverride1() throws Exception { +// testTypes( +// "/** @interface */function Super() {};" + +// "/** @desc description */Super.prototype.foo = function() {};" + +// "/** @interface\n @extends {Super} */function Sub() {};" + +// "/** @desc description */Sub.prototype.foo = function() {};", +// "property foo is already defined by the Super extended interface"); +// } + +// public void testInterfacePropertyOverride2() throws Exception { +// testTypes( +// "/** @interface */function Root() {};" + +// "/** @desc description */Root.prototype.foo = function() {};" + +// "/** @interface\n @extends {Root} */function Super() {};" + +// "/** @interface\n @extends {Super} */function Sub() {};" + +// "/** @desc description */Sub.prototype.foo = function() {};", +// "property foo is already defined by the Root extended interface"); +// } + + public void testInterfaceInheritanceCheck1() throws Exception { + testTypes( + "/** @interface */function Super() {};" + + "/** @desc description */Super.prototype.foo = function() {};" + + "/** @constructor\n @implements {Super} */function Sub() {};" + + "Sub.prototype.foo = function() {};", + "property foo already defined on interface Super; use @override to " + + "override it"); + } + + public void testInterfaceInheritanceCheck2() throws Exception { + testTypes( + "/** @interface */function Super() {};" + + "/** @desc description */Super.prototype.foo = function() {};" + + "/** @constructor\n @implements {Super} */function Sub() {};" + + "/** @override */Sub.prototype.foo = function() {};"); + } + + public void testInterfaceInheritanceCheck3() throws Exception { + testTypes( + "/** @interface */function Root() {};" + + "/** @return {number} */Root.prototype.foo = function() {};" + + "/** @interface\n @extends {Root} */function Super() {};" + + "/** @constructor\n @implements {Super} */function Sub() {};" + + "/** @return {number} */Sub.prototype.foo = function() { return 1;};", + "property foo already defined on interface Root; use @override to " + + "override it"); + } + + public void testInterfaceInheritanceCheck4() throws Exception { + testTypes( + "/** @interface */function Root() {};" + + "/** @return {number} */Root.prototype.foo = function() {};" + + "/** @interface\n @extends {Root} */function Super() {};" + + "/** @constructor\n @implements {Super} */function Sub() {};" + + "/** @override\n * @return {number} */Sub.prototype.foo =\n" + + "function() { return 1;};"); + } + + public void testInterfaceInheritanceCheck5() throws Exception { + testTypes( + "/** @interface */function Super() {};" + + "/** @return {string} */Super.prototype.foo = function() {};" + + "/** @constructor\n @implements {Super} */function Sub() {};" + + "/** @override\n @return {number} */Sub.prototype.foo =\n" + + "function() { return 1; };", + "mismatch of the foo property type and the type of the property it " + + "overrides from interface Super\n" + + "original: function (this:Super): string\n" + + "override: function (this:Sub): number"); + } + + public void testInterfaceInheritanceCheck6() throws Exception { + testTypes( + "/** @interface */function Root() {};" + + "/** @return {string} */Root.prototype.foo = function() {};" + + "/** @interface\n @extends {Root} */function Super() {};" + + "/** @constructor\n @implements {Super} */function Sub() {};" + + "/** @override\n @return {number} */Sub.prototype.foo =\n" + + "function() { return 1; };", + "mismatch of the foo property type and the type of the property it " + + "overrides from interface Root\n" + + "original: function (this:Root): string\n" + + "override: function (this:Sub): number"); + } + + public void testInterfaceInheritanceCheck7() throws Exception { + testTypes( + "/** @interface */function Super() {};" + + "/** @param {number} bar */Super.prototype.foo = function(bar) {};" + + "/** @constructor\n @implements {Super} */function Sub() {};" + + "/** @override\n @param {string} bar */Sub.prototype.foo =\n" + + "function(bar) {};", + "mismatch of the foo property type and the type of the property it " + + "overrides from interface Super\n" + + "original: function (this:Super, number): undefined\n" + + "override: function (this:Sub, string): undefined"); + } + + public void testInterfaceInheritanceCheck8() throws Exception { + testTypes( + "/** @constructor\n @implements {Super} */function Sub() {};" + + "/** @override */Sub.prototype.foo = function() {};", + new String[] { + "Bad type annotation. Unknown type Super", + "property foo not defined on any superclass of Sub" + }); + } + + public void testInterfacePropertyNotImplemented() throws Exception { + testTypes( + "/** @interface */function Int() {};" + + "/** @desc description */Int.prototype.foo = function() {};" + + "/** @constructor\n @implements {Int} */function Foo() {};", + "property foo on interface Int is not implemented by type Foo"); + } + + public void testInterfacePropertyNotImplemented2() throws Exception { + testTypes( + "/** @interface */function Int() {};" + + "/** @desc description */Int.prototype.foo = function() {};" + + "/** @interface \n @extends {Int} */function Int2() {};" + + "/** @constructor\n @implements {Int2} */function Foo() {};", + "property foo on interface Int is not implemented by type Foo"); + } + + public void testStubConstructorImplementingInterface() throws Exception { + // This does not throw a warning for unimplemented property because Foo is + // just a stub. + testTypes( + // externs + "/** @interface */ function Int() {}\n" + + "/** @desc description */Int.prototype.foo = function() {};" + + "/** @constructor \n @implements {Int} */ var Foo;\n", + "", null, false); + } + + public void testObjectLiteral() throws Exception { + Node n = parseAndTypeCheck("var a = {m1: 7, m2: 'hello'}"); + + Node nameNode = n.getFirstChild().getFirstChild(); + Node objectNode = nameNode.getFirstChild(); + + // node extraction + assertEquals(Token.NAME, nameNode.getType()); + assertEquals(Token.OBJECTLIT, objectNode.getType()); + + // value's type + ObjectType objectType = + (ObjectType) objectNode.getJSType(); + assertTypeEquals(NUMBER_TYPE, objectType.getPropertyType("m1")); + assertTypeEquals(STRING_TYPE, objectType.getPropertyType("m2")); + + // variable's type + assertTypeEquals(objectType, nameNode.getJSType()); + } + + public void testObjectLiteralDeclaration1() throws Exception { + testTypes( + "var x = {" + + "/** @type {boolean} */ abc: true," + + "/** @type {number} */ 'def': 0," + + "/** @type {string} */ 3: 'fgh'" + + "};"); + } + + public void testCallDateConstructorAsFunction() throws Exception { + // ECMA-262 15.9.2: When Date is called as a function rather than as a + // constructor, it returns a string. + Node n = parseAndTypeCheck("Date()"); + assertTypeEquals(STRING_TYPE, n.getFirstChild().getFirstChild().getJSType()); + } + + // According to ECMA-262, Error & Array function calls are equivalent to + // constructor calls. + + public void testCallErrorConstructorAsFunction() throws Exception { + Node n = parseAndTypeCheck("Error('x')"); + assertTypeEquals(ERROR_TYPE, + n.getFirstChild().getFirstChild().getJSType()); + } + + public void testCallArrayConstructorAsFunction() throws Exception { + Node n = parseAndTypeCheck("Array()"); + assertTypeEquals(ARRAY_TYPE, + n.getFirstChild().getFirstChild().getJSType()); + } + + public void testPropertyTypeOfUnionType() throws Exception { + testTypes("var a = {};" + + "/** @constructor */ a.N = function() {};\n" + + "a.N.prototype.p = 1;\n" + + "/** @constructor */ a.S = function() {};\n" + + "a.S.prototype.p = 'a';\n" + + "/** @param {!a.N|!a.S} x\n@return {string} */\n" + + "var f = function(x) { return x.p; };", + "inconsistent return type\n" + + "found : (number|string)\n" + + "required: string"); + } + + // TODO(user): We should flag these as invalid. This will probably happen + // when we make sure the interface is never referenced outside of its + // definition. We might want more specific and helpful error messages. + //public void testWarningOnInterfacePrototype() throws Exception { + // testTypes("/** @interface */ u.T = function() {};\n" + + // "/** @return {number} */ u.T.prototype = function() { };", + // "cannot reference an interface outside of its definition"); + //} + // + //public void testBadPropertyOnInterface1() throws Exception { + // testTypes("/** @interface */ u.T = function() {};\n" + + // "/** @return {number} */ u.T.f = function() { return 1;};", + // "cannot reference an interface outside of its definition"); + //} + // + //public void testBadPropertyOnInterface2() throws Exception { + // testTypes("/** @interface */ function T() {};\n" + + // "/** @return {number} */ T.f = function() { return 1;};", + // "cannot reference an interface outside of its definition"); + //} + // + //public void testBadPropertyOnInterface3() throws Exception { + // testTypes("/** @interface */ u.T = function() {}; u.T.x", + // "cannot reference an interface outside of its definition"); + //} + // + //public void testBadPropertyOnInterface4() throws Exception { + // testTypes("/** @interface */ function T() {}; T.x;", + // "cannot reference an interface outside of its definition"); + //} + + public void testAnnotatedPropertyOnInterface1() throws Exception { + // For interfaces we must allow function definitions that don't have a + // return statement, even though they declare a returned type. + testTypes("/** @interface */ u.T = function() {};\n" + + "/** @return {number} */ u.T.prototype.f = function() {};"); + } + + public void testAnnotatedPropertyOnInterface2() throws Exception { + testTypes("/** @interface */ u.T = function() {};\n" + + "/** @return {number} */ u.T.prototype.f = function() { };"); + } + + public void testAnnotatedPropertyOnInterface3() throws Exception { + testTypes("/** @interface */ function T() {};\n" + + "/** @return {number} */ T.prototype.f = function() { };"); + } + + public void testAnnotatedPropertyOnInterface4() throws Exception { + testTypes( + CLOSURE_DEFS + + "/** @interface */ function T() {};\n" + + "/** @return {number} */ T.prototype.f = goog.abstractMethod;"); + } + + // TODO(user): If we want to support this syntax we have to warn about + // missing annotations. + //public void testWarnUnannotatedPropertyOnInterface1() throws Exception { + // testTypes("/** @interface */ u.T = function () {}; u.T.prototype.x;", + // "interface property x is not annotated"); + //} + // + //public void testWarnUnannotatedPropertyOnInterface2() throws Exception { + // testTypes("/** @interface */ function T() {}; T.prototype.x;", + // "interface property x is not annotated"); + //} + + public void testWarnUnannotatedPropertyOnInterface5() throws Exception { + testTypes("/** @interface */ u.T = function () {};\n" + + "/** @desc x does something */u.T.prototype.x = function() {};"); + } + + public void testWarnUnannotatedPropertyOnInterface6() throws Exception { + testTypes("/** @interface */ function T() {};\n" + + "/** @desc x does something */T.prototype.x = function() {};"); + } + + // TODO(user): If we want to support this syntax we have to warn about + // the invalid type of the interface member. + //public void testWarnDataPropertyOnInterface1() throws Exception { + // testTypes("/** @interface */ u.T = function () {};\n" + + // "/** @type {number} */u.T.prototype.x;", + // "interface members can only be plain functions"); + //} + // + //public void testWarnDataPropertyOnInterface2() throws Exception { + // testTypes("/** @interface */ function T() {};\n" + + // "/** @type {number} */T.prototype.x;", + // "interface members can only be plain functions"); + //} + + public void testWarnDataPropertyOnInterface3() throws Exception { + testTypes("/** @interface */ u.T = function () {};\n" + + "/** @type {number} */u.T.prototype.x = 1;", + "interface members can only be empty property declarations, " + + "empty functions, or goog.abstractMethod"); + } + + public void testWarnDataPropertyOnInterface4() throws Exception { + testTypes("/** @interface */ function T() {};\n" + + "/** @type {number} */T.prototype.x = 1;", + "interface members can only be empty property declarations, " + + "empty functions, or goog.abstractMethod"); + } + + // TODO(user): If we want to support this syntax we should warn about the + // mismatching types in the two tests below. + //public void testErrorMismatchingPropertyOnInterface1() throws Exception { + // testTypes("/** @interface */ u.T = function () {};\n" + + // "/** @param {Number} foo */u.T.prototype.x =\n" + + // "/** @param {String} foo */function(foo) {};", + // "found : \n" + + // "required: "); + //} + // + //public void testErrorMismatchingPropertyOnInterface2() throws Exception { + // testTypes("/** @interface */ function T() {};\n" + + // "/** @return {number} */T.prototype.x =\n" + + // "/** @return {string} */function() {};", + // "found : \n" + + // "required: "); + //} + + // TODO(user): We should warn about this (bar is missing an annotation). We + // probably don't want to warn about all missing parameter annotations, but + // we should be as strict as possible regarding interfaces. + //public void testErrorMismatchingPropertyOnInterface3() throws Exception { + // testTypes("/** @interface */ u.T = function () {};\n" + + // "/** @param {Number} foo */u.T.prototype.x =\n" + + // "function(foo, bar) {};", + // "found : \n" + + // "required: "); + //} + + public void testErrorMismatchingPropertyOnInterface4() throws Exception { + testTypes("/** @interface */ u.T = function () {};\n" + + "/** @param {Number} foo */u.T.prototype.x =\n" + + "function() {};", + "parameter foo does not appear in u.T.prototype.x's parameter list"); + } + + public void testErrorMismatchingPropertyOnInterface5() throws Exception { + testTypes("/** @interface */ function T() {};\n" + + "/** @type {number} */T.prototype.x = function() { };", + "assignment to property x of T.prototype\n" + + "found : function (): undefined\n" + + "required: number"); + } + + public void testErrorMismatchingPropertyOnInterface6() throws Exception { + testClosureTypesMultipleWarnings( + "/** @interface */ function T() {};\n" + + "/** @return {number} */T.prototype.x = 1", + Lists.newArrayList( + "assignment to property x of T.prototype\n" + + "found : number\n" + + "required: function (this:T): number", + "interface members can only be empty property declarations, " + + "empty functions, or goog.abstractMethod")); + } + + public void testInterfaceNonEmptyFunction() throws Exception { + testTypes("/** @interface */ function T() {};\n" + + "T.prototype.x = function() { return 'foo'; }", + "interface member functions must have an empty body" + ); + } + + public void testDoubleNestedInterface() throws Exception { + testTypes("/** @interface */ var I1 = function() {};\n" + + "/** @interface */ I1.I2 = function() {};\n" + + "/** @interface */ I1.I2.I3 = function() {};\n"); + } + + public void testStaticDataPropertyOnNestedInterface() throws Exception { + testTypes("/** @interface */ var I1 = function() {};\n" + + "/** @interface */ I1.I2 = function() {};\n" + + "/** @type {number} */ I1.I2.x = 1;\n"); + } + + public void testInterfaceInstantiation() throws Exception { + testTypes("/** @interface */var f = function(){}; new f", + "cannot instantiate non-constructor"); + } + + public void testPrototypeLoop() throws Exception { + testClosureTypesMultipleWarnings( + suppressMissingProperty("foo") + + "/** @constructor \n * @extends {T} */var T = function() {};" + + "alert((new T).foo);", + Lists.newArrayList( + "Parse error. Cycle detected in inheritance chain of type T", + "Could not resolve type in @extends tag of T")); + } + + public void testDirectPrototypeAssign() throws Exception { + // For now, we just ignore @type annotations on the prototype. + testTypes( + "/** @constructor */ function Foo() {}" + + "/** @constructor */ function Bar() {}" + + "/** @type {Array} */ Bar.prototype = new Foo()"); + } + + // In all testResolutionViaRegistry* tests, since u is unknown, u.T can only + // be resolved via the registry and not via properties. + + public void testResolutionViaRegistry1() throws Exception { + testTypes("/** @constructor */ u.T = function() {};\n" + + "/** @type {(number|string)} */ u.T.prototype.a;\n" + + "/**\n" + + "* @param {u.T} t\n" + + "* @return {string}\n" + + "*/\n" + + "var f = function(t) { return t.a; };", + "inconsistent return type\n" + + "found : (number|string)\n" + + "required: string"); + } + + public void testResolutionViaRegistry2() throws Exception { + testTypes( + "/** @constructor */ u.T = function() {" + + " this.a = 0; };\n" + + "/**\n" + + "* @param {u.T} t\n" + + "* @return {string}\n" + + "*/\n" + + "var f = function(t) { return t.a; };", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testResolutionViaRegistry3() throws Exception { + testTypes("/** @constructor */ u.T = function() {};\n" + + "/** @type {(number|string)} */ u.T.prototype.a = 0;\n" + + "/**\n" + + "* @param {u.T} t\n" + + "* @return {string}\n" + + "*/\n" + + "var f = function(t) { return t.a; };", + "inconsistent return type\n" + + "found : (number|string)\n" + + "required: string"); + } + + public void testResolutionViaRegistry4() throws Exception { + testTypes("/** @constructor */ u.A = function() {};\n" + + "/**\n* @constructor\n* @extends {u.A}\n*/\nu.A.A = function() {}\n;" + + "/**\n* @constructor\n* @extends {u.A}\n*/\nu.A.B = function() {};\n" + + "var ab = new u.A.B();\n" + + "/** @type {!u.A} */ var a = ab;\n" + + "/** @type {!u.A.A} */ var aa = ab;\n", + "initializing variable\n" + + "found : u.A.B\n" + + "required: u.A.A"); + } + + public void testResolutionViaRegistry5() throws Exception { + Node n = parseAndTypeCheck("/** @constructor */ u.T = function() {}; u.T"); + JSType type = n.getLastChild().getLastChild().getJSType(); + assertFalse(type.isUnknownType()); + assertTrue(type instanceof FunctionType); + assertEquals("u.T", + ((FunctionType) type).getInstanceType().getReferenceName()); + } + + public void testGatherProperyWithoutAnnotation1() throws Exception { + Node n = parseAndTypeCheck("/** @constructor */ var T = function() {};" + + "/** @type {!T} */var t; t.x; t;"); + JSType type = n.getLastChild().getLastChild().getJSType(); + assertFalse(type.isUnknownType()); + assertTrue(type instanceof ObjectType); + ObjectType objectType = (ObjectType) type; + assertFalse(objectType.hasProperty("x")); + Asserts.assertTypeCollectionEquals( + Lists.newArrayList(objectType), + registry.getTypesWithProperty("x")); + } + + public void testGatherProperyWithoutAnnotation2() throws Exception { + TypeCheckResult ns = + parseAndTypeCheckWithScope("/** @type {!Object} */var t; t.x; t;"); + Node n = ns.root; + Scope s = ns.scope; + JSType type = n.getLastChild().getLastChild().getJSType(); + assertFalse(type.isUnknownType()); + assertTypeEquals(type, OBJECT_TYPE); + assertTrue(type instanceof ObjectType); + ObjectType objectType = (ObjectType) type; + assertFalse(objectType.hasProperty("x")); + Asserts.assertTypeCollectionEquals( + Lists.newArrayList(OBJECT_TYPE), + registry.getTypesWithProperty("x")); + } + + public void testFunctionMasksVariableBug() throws Exception { + testTypes("var x = 4; var f = function x(b) { return b ? 1 : x(true); };", + "function x masks variable (IE bug)"); + } + + public void testDfa1() throws Exception { + testTypes("var x = null;\n x = 1;\n /** @type number */ var y = x;"); + } + + public void testDfa2() throws Exception { + testTypes("function u() {}\n" + + "/** @return {number} */ function f() {\nvar x = 'todo';\n" + + "if (u()) { x = 1; } else { x = 2; } return x;\n}"); + } + + public void testDfa3() throws Exception { + testTypes("function u() {}\n" + + "/** @return {number} */ function f() {\n" + + "/** @type {number|string} */ var x = 'todo';\n" + + "if (u()) { x = 1; } else { x = 2; } return x;\n}"); + } + + public void testDfa4() throws Exception { + testTypes("/** @param {Date?} d */ function f(d) {\n" + + "if (!d) { return; }\n" + + "/** @type {!Date} */ var e = d;\n}"); + } + + public void testDfa5() throws Exception { + testTypes("/** @return {string?} */ function u() {return 'a';}\n" + + "/** @param {string?} x\n@return {string} */ function f(x) {\n" + + "while (!x) { x = u(); }\nreturn x;\n}"); + } + + public void testDfa6() throws Exception { + testTypes("/** @return {Object?} */ function u() {return {};}\n" + + "/** @param {Object?} x */ function f(x) {\n" + + "while (x) { x = u(); if (!x) { x = u(); } }\n}"); + } + + public void testDfa7() throws Exception { + testTypes("/** @constructor */ var T = function() {};\n" + + "/** @type {Date?} */ T.prototype.x = null;\n" + + "/** @param {!T} t */ function f(t) {\n" + + "if (!t.x) { return; }\n" + + "/** @type {!Date} */ var e = t.x;\n}"); + } + + public void testDfa8() throws Exception { + testTypes("/** @constructor */ var T = function() {};\n" + + "/** @type {number|string} */ T.prototype.x = '';\n" + + "function u() {}\n" + + "/** @param {!T} t\n@return {number} */ function f(t) {\n" + + "if (u()) { t.x = 1; } else { t.x = 2; } return t.x;\n}"); + } + + public void testDfa9() throws Exception { + testTypes("function f() {\n/** @type {string?} */var x;\nx = null;\n" + + "if (x == null) { return 0; } else { return 1; } }", + "condition always evaluates to true\n" + + "left : null\n" + + "right: null"); + } + + public void testDfa10() throws Exception { + testTypes("/** @param {null} x */ function g(x) {}" + + "/** @param {string?} x */function f(x) {\n" + + "if (!x) { x = ''; }\n" + + "if (g(x)) { return 0; } else { return 1; } }", + "actual parameter 1 of g does not match formal parameter\n" + + "found : string\n" + + "required: null"); + } + + public void testDfa11() throws Exception { + testTypes("/** @param {string} opt_x\n@return {string} */\n" + + "function f(opt_x) { if (!opt_x) { " + + "throw new Error('x cannot be empty'); } return opt_x; }"); + } + + public void testDfa12() throws Exception { + testTypes("/** @param {string} x \n * @constructor \n */" + + "var Bar = function(x) {};" + + "/** @param {string} x */ function g(x) { return true; }" + + "/** @param {string|number} opt_x */ " + + "function f(opt_x) { " + + " if (opt_x) { new Bar(g(opt_x) && 'x'); }" + + "}", + "actual parameter 1 of g does not match formal parameter\n" + + "found : (number|string)\n" + + "required: string"); + } + + public void testDfa13() throws Exception { + testTypes( + "/**\n" + + " * @param {string} x \n" + + " * @param {number} y \n" + + " * @param {number} z \n" + + " */" + + "function g(x, y, z) {}" + + "function f() { " + + " var x = 'a'; g(x, x = 3, x);" + + "}"); + } + + public void testTypeInferenceWithCast1() throws Exception { + testTypes( + "/**@return {(number,null,undefined)}*/function u(x) {return null;}" + + "/**@param {number?} x\n@return {number?}*/function f(x) {return x;}" + + "/**@return {number?}*/function g(x) {" + + "var y = /**@type {number?}*/(u(x)); return f(y);}"); + } + + public void testTypeInferenceWithCast2() throws Exception { + testTypes( + "/**@return {(number,null,undefined)}*/function u(x) {return null;}" + + "/**@param {number?} x\n@return {number?}*/function f(x) {return x;}" + + "/**@return {number?}*/function g(x) {" + + "var y; y = /**@type {number?}*/(u(x)); return f(y);}"); + } + + public void testTypeInferenceWithCast3() throws Exception { + testTypes( + "/**@return {(number,null,undefined)}*/function u(x) {return 1;}" + + "/**@return {number}*/function g(x) {" + + "return /**@type {number}*/(u(x));}"); + } + + public void testTypeInferenceWithCast4() throws Exception { + testTypes( + "/**@return {(number,null,undefined)}*/function u(x) {return 1;}" + + "/**@return {number}*/function g(x) {" + + "return /**@type {number}*/(u(x)) && 1;}"); + } + + public void testTypeInferenceWithCast5() throws Exception { + testTypes( + "/** @param {number} x */ function foo(x) {}" + + "/** @param {{length:*}} y */ function bar(y) {" + + " /** @type {string} */ y.length;" + + " foo(y.length);" + + "}", + "actual parameter 1 of foo does not match formal parameter\n" + + "found : string\n" + + "required: number"); + } + + public void testTypeInferenceWithClosure1() throws Exception { + testTypes( + "/** @return {boolean} */" + + "function f() {" + + " /** @type {?string} */ var x = null;" + + " function g() { x = 'y'; } g(); " + + " return x == null;" + + "}"); + } + + public void testTypeInferenceWithClosure2() throws Exception { + testTypes( + "/** @return {boolean} */" + + "function f() {" + + " /** @type {?string} */ var x = null;" + + " function g() { x = 'y'; } g(); " + + " return x === 3;" + + "}", + "condition always evaluates to false\n" + + "left : (null|string|undefined)\n" + + "right: number"); + } + + public void testForwardPropertyReference() throws Exception { + testTypes("/** @constructor */ var Foo = function() { this.init(); };" + + "/** @return {string} */" + + "Foo.prototype.getString = function() {" + + " return this.number_;" + + "};" + + "Foo.prototype.init = function() {" + + " /** @type {number} */" + + " this.number_ = 3;" + + "};", + "inconsistent return type\n" + + "found : number\n" + + "required: string"); + } + + public void testNoForwardTypeDeclaration() throws Exception { + testTypes( + "/** @param {MyType} x */ function f(x) {}", + "Bad type annotation. Unknown type MyType"); + } + + public void testNoForwardTypeDeclarationAndNoBraces() throws Exception { + testTypes("/** @return The result. */ function f() {}"); + } + + public void testForwardTypeDeclaration1() throws Exception { + testClosureTypes( + // malformed addDependency calls shouldn't cause a crash + "goog.addDependency();" + + "goog.addDependency('y', [goog]);" + + + "goog.addDependency('zzz.js', ['MyType'], []);" + + "/** @param {MyType} x \n * @return {number} */" + + "function f(x) { return 3; }", null); + } + + public void testForwardTypeDeclaration2() throws Exception { + String f = "goog.addDependency('zzz.js', ['MyType'], []);" + + "/** @param {MyType} x */ function f(x) { }"; + testClosureTypes(f, null); + testClosureTypes(f + "f(3);", + "actual parameter 1 of f does not match formal parameter\n" + + "found : number\n" + + "required: (MyType|null|undefined)"); + } + + public void testForwardTypeDeclaration3() throws Exception { + testClosureTypes( + "goog.addDependency('zzz.js', ['MyType'], []);" + + "/** @param {MyType} x */ function f(x) { return x; }" + + "/** @constructor */ var MyType = function() {};" + + "f(3);", + "actual parameter 1 of f does not match formal parameter\n" + + "found : number\n" + + "required: (MyType|null|undefined)"); + } + + public void testDuplicateTypeDef() throws Exception { + testTypes( + "var goog = {};" + + "/** @constructor */ goog.Bar = function() {};" + + "/** @typedef {number} */ goog.Bar;", + "variable goog.Bar redefined with type None, " + + "original definition at [testcode]:1 " + + "with type function (new:goog.Bar): undefined"); + } + + public void testTypeDef1() throws Exception { + testTypes( + "var goog = {};" + + "/** @typedef {number} */ goog.Bar;" + + "/** @param {goog.Bar} x */ function f(x) {}" + + "f(3);"); + } + + public void testTypeDef2() throws Exception { + testTypes( + "var goog = {};" + + "/** @typedef {number} */ goog.Bar;" + + "/** @param {goog.Bar} x */ function f(x) {}" + + "f('3');", + "actual parameter 1 of f does not match formal parameter\n" + + "found : string\n" + + "required: number"); + } + + public void testTypeDef3() throws Exception { + testTypes( + "var goog = {};" + + "/** @typedef {number} */ var Bar;" + + "/** @param {Bar} x */ function f(x) {}" + + "f('3');", + "actual parameter 1 of f does not match formal parameter\n" + + "found : string\n" + + "required: number"); + } + + public void testCircularTypeDef() throws Exception { + testTypes( + "var goog = {};" + + "/** @typedef {number|Array.} */ goog.Bar;" + + "/** @param {goog.Bar} x */ function f(x) {}" + + "f(3); f([3]); f([[3]]);"); + } + + public void testGetTypedPercent1() throws Exception { + String js = "var id = function(x) { return x; }\n" + + "var id2 = function(x) { return id(x); }"; + assertEquals(50.0, getTypedPercent(js), 0.1); + } + + public void testGetTypedPercent2() throws Exception { + String js = "var x = {}; x.y = 1;"; + assertEquals(100.0, getTypedPercent(js), 0.1); + } + + public void testGetTypedPercent3() throws Exception { + String js = "var f = function(x) { x.a = x.b; }"; + assertEquals(50.0, getTypedPercent(js), 0.1); + } + + public void testGetTypedPercent4() throws Exception { + String js = "var n = {};\n /** @constructor */ n.T = function() {};\n" + + "/** @type n.T */ var x = new n.T();"; + assertEquals(100.0, getTypedPercent(js), 0.1); + } + + private double getTypedPercent(String js) throws Exception { + Node n = compiler.parseTestCode(js); + + Node externs = new Node(Token.BLOCK); + Node externAndJsRoot = new Node(Token.BLOCK, externs, n); + externAndJsRoot.setIsSyntheticBlock(true); + + TypeCheck t = makeTypeCheck(); + t.processForTesting(null, n); + return t.getTypedPercent(); + } + + private ObjectType getInstanceType(Node js1Node) { + JSType type = js1Node.getFirstChild().getJSType(); + assertNotNull(type); + assertTrue(type instanceof FunctionType); + FunctionType functionType = (FunctionType) type; + assertTrue(functionType.isConstructor()); + return functionType.getInstanceType(); + } + + public void testPrototypePropertyReference() throws Exception { + TypeCheckResult p = parseAndTypeCheckWithScope("" + + "/** @constructor */\n" + + "function Foo() {}\n" + + "/** @param {number} a */\n" + + "Foo.prototype.bar = function(a){};\n" + + "/** @param {Foo} f */\n" + + "function baz(f) {\n" + + " Foo.prototype.bar.call(f, 3);\n" + + "}"); + assertEquals(0, compiler.getErrorCount()); + assertEquals(0, compiler.getWarningCount()); + + assertTrue(p.scope.getVar("Foo").getType() instanceof FunctionType); + FunctionType fooType = (FunctionType) p.scope.getVar("Foo").getType(); + assertEquals("function (this:Foo, number): undefined", + fooType.getPrototype().getPropertyType("bar").toString()); + } + + public void testResolvingNamedTypes() throws Exception { + String js = "" + + "/** @constructor */\n" + + "var Foo = function() {}\n" + + "/** @param {number} a */\n" + + "Foo.prototype.foo = function(a) {\n" + + " return this.baz().toString();\n" + + "};\n" + + "/** @return {Baz} */\n" + + "Foo.prototype.baz = function() { return new Baz(); };\n" + + "/** @constructor\n" + + " * @extends Foo */\n" + + "var Bar = function() {};" + + "/** @constructor */\n" + + "var Baz = function() {};"; + assertEquals(100.0, getTypedPercent(js), 0.1); + } + + public void testMissingProperty1() throws Exception { + testTypes( + "/** @constructor */ function Foo() {}" + + "Foo.prototype.bar = function() { return this.a; };" + + "Foo.prototype.baz = function() { this.a = 3; };"); + } + + public void testMissingProperty2() throws Exception { + testTypes( + "/** @constructor */ function Foo() {}" + + "Foo.prototype.bar = function() { return this.a; };" + + "Foo.prototype.baz = function() { this.b = 3; };", + "Property a never defined on Foo"); + } + + public void testMissingProperty3() throws Exception { + testTypes( + "/** @constructor */ function Foo() {}" + + "Foo.prototype.bar = function() { return this.a; };" + + "(new Foo).a = 3;"); + } + + public void testMissingProperty4() throws Exception { + testTypes( + "/** @constructor */ function Foo() {}" + + "Foo.prototype.bar = function() { return this.a; };" + + "(new Foo).b = 3;", + "Property a never defined on Foo"); + } + + public void testMissingProperty5() throws Exception { + testTypes( + "/** @constructor */ function Foo() {}" + + "Foo.prototype.bar = function() { return this.a; };" + + "/** @constructor */ function Bar() { this.a = 3; };", + "Property a never defined on Foo"); + } + + public void testMissingProperty6() throws Exception { + testTypes( + "/** @constructor */ function Foo() {}" + + "Foo.prototype.bar = function() { return this.a; };" + + "/** @constructor \n * @extends {Foo} */ " + + "function Bar() { this.a = 3; };"); + } + + public void testMissingProperty7() throws Exception { + testTypes( + "/** @param {Object} obj */" + + "function foo(obj) { return obj.impossible; }", + "Property impossible never defined on Object"); + } + + public void testMissingProperty8() throws Exception { + testTypes( + "/** @param {Object} obj */" + + "function foo(obj) { return typeof obj.impossible; }"); + } + + public void testMissingProperty9() throws Exception { + testTypes( + "/** @param {Object} obj */" + + "function foo(obj) { if (obj.impossible) { return true; } }"); + } + + public void testMissingProperty10() throws Exception { + testTypes( + "/** @param {Object} obj */" + + "function foo(obj) { while (obj.impossible) { return true; } }"); + } + + public void testMissingProperty11() throws Exception { + testTypes( + "/** @param {Object} obj */" + + "function foo(obj) { for (;obj.impossible;) { return true; } }"); + } + + public void testMissingProperty12() throws Exception { + testTypes( + "/** @param {Object} obj */" + + "function foo(obj) { do { } while (obj.impossible); }"); + } + + public void testMissingProperty13() throws Exception { + testTypes( + "var goog = {}; goog.isDef = function(x) { return false; };" + + "/** @param {Object} obj */" + + "function foo(obj) { return goog.isDef(obj.impossible); }"); + } + + public void testMissingProperty14() throws Exception { + testTypes( + "var goog = {}; goog.isDef = function(x) { return false; };" + + "/** @param {Object} obj */" + + "function foo(obj) { return goog.isNull(obj.impossible); }", + "Property isNull never defined on goog"); + } + + public void testMissingProperty15() throws Exception { + testTypes( + "/** @param {Object} x */" + + "function f(x) { if (x.foo) { x.foo(); } }"); + } + + public void testMissingProperty16() throws Exception { + testTypes( + "/** @param {Object} x */" + + "function f(x) { x.foo(); if (x.foo) {} }", + "Property foo never defined on Object"); + } + + public void testMissingProperty17() throws Exception { + testTypes( + "/** @param {Object} x */" + + "function f(x) { if (typeof x.foo == 'function') { x.foo(); } }"); + } + + public void testMissingProperty18() throws Exception { + testTypes( + "/** @param {Object} x */" + + "function f(x) { if (x.foo instanceof Function) { x.foo(); } }"); + } + + public void testMissingProperty19() throws Exception { + testTypes( + "/** @param {Object} x */" + + "function f(x) { if (x.bar) { if (x.foo) {} } else { x.foo(); } }", + "Property foo never defined on Object"); + } + + public void testMissingProperty21() throws Exception { + testTypes( + "/** @param {Object} x */" + + "function f(x) { x.foo && x.foo(); }"); + } + + public void testMissingProperty22() throws Exception { + testTypes( + "/** @param {Object} x \n * @return {boolean} */" + + "function f(x) { return x.foo ? x.foo() : true; }"); + } + + public void testMissingProperty23() throws Exception { + testTypes( + "function f(x) { x.impossible(); }", + "Property impossible never defined on x"); + } + + public void testMissingProperty24() throws Exception { + testClosureTypes( + "goog.addDependency('zzz.js', ['MissingType'], []);" + + "/** @param {MissingType} x */" + + "function f(x) { x.impossible(); }", null); + } + + public void testMissingProperty25() throws Exception { + testTypes( + "/** @constructor */ var Foo = function() {};" + + "Foo.prototype.bar = function() {};" + + "/** @constructor */ var FooAlias = Foo;" + + "(new FooAlias()).bar();"); + } + + public void testMissingProperty26() throws Exception { + testTypes( + "/** @constructor */ var Foo = function() {};" + + "/** @constructor */ var FooAlias = Foo;" + + "FooAlias.prototype.bar = function() {};" + + "(new Foo()).bar();"); + } + + public void testMissingProperty27() throws Exception { + testClosureTypes( + "goog.addDependency('zzz.js', ['MissingType'], []);" + + "/** @param {?MissingType} x */" + + "function f(x) {" + + " for (var parent = x; parent; parent = parent.getParent()) {}" + + "}", null); + } + + public void testMissingProperty28() throws Exception { + testTypes( + "function f(obj) {" + + " /** @type {*} */ obj.foo;" + + " return obj.foo;" + + "}"); + testTypes( + "function f(obj) {" + + " /** @type {*} */ obj.foo;" + + " return obj.foox;" + + "}", + "Property foox never defined on obj"); + } + + public void testMissingProperty29() throws Exception { + // This used to emit a warning. + testTypes( + // externs + "/** @constructor */ var Foo;" + + "Foo.prototype.opera;" + + "Foo.prototype.opera.postError;", + "", + null, + false); + } + + public void testDeclaredNativeTypeEquality() throws Exception { + Node n = parseAndTypeCheck("/** @constructor */ function Object() {};"); + assertEquals(registry.getNativeType(JSTypeNative.OBJECT_FUNCTION_TYPE), + n.getFirstChild().getJSType()); + } + + public void testUndefinedVar() throws Exception { + Node n = parseAndTypeCheck("var undefined;"); + assertEquals(registry.getNativeType(JSTypeNative.VOID_TYPE), + n.getFirstChild().getFirstChild().getJSType()); + } + + public void testFlowScopeBug1() throws Exception { + Node n = parseAndTypeCheck("/** @param {number} a \n" + + "* @param {number} b */\n" + + "function f(a, b) {\n" + + "/** @type number */" + + "var i = 0;" + + "for (; (i + a) < b; ++i) {}}"); + + // check the type of the add node for i + f + assertEquals(registry.getNativeType(JSTypeNative.NUMBER_TYPE), + n.getFirstChild().getLastChild().getLastChild().getFirstChild() + .getNext().getFirstChild().getJSType()); + } + + public void testFlowScopeBug2() throws Exception { + Node n = parseAndTypeCheck("/** @constructor */ function Foo() {};\n" + + "Foo.prototype.hi = false;" + + "function foo(a, b) {\n" + + " /** @type Array */" + + " var arr;" + + " /** @type number */" + + " var iter;" + + " for (iter = 0; iter < arr.length; ++ iter) {" + + " /** @type Foo */" + + " var afoo = arr[iter];" + + " afoo;" + + " }" + + "}"); + + // check the type of afoo when referenced + assertTypeEquals(registry.createOptionalType( + registry.createNullableType(registry.getType("Foo"))), + n.getLastChild().getLastChild().getLastChild().getLastChild() + .getLastChild().getLastChild().getJSType()); + } + + public void testAddSingletonGetter() { + Node n = parseAndTypeCheck( + "/** @constructor */ function Foo() {};\n" + + "goog.addSingletonGetter(Foo);"); + ObjectType o = (ObjectType) n.getFirstChild().getJSType(); + assertEquals("function (): Foo", + o.getPropertyType("getInstance").toString()); + assertEquals("Foo", o.getPropertyType("instance_").toString()); + } + + public void testTypeCheckStandaloneAST() throws Exception { + Node n = compiler.parseTestCode("function Foo() { }"); + typeCheck(n); + MemoizedScopeCreator scopeCreator = + new MemoizedScopeCreator(new TypedScopeCreator(compiler)); + Scope topScope = scopeCreator.createScope(n, null); + + Node second = compiler.parseTestCode("new Foo"); + + Node externs = new Node(Token.BLOCK); + Node externAndJsRoot = new Node(Token.BLOCK, externs, second); + externAndJsRoot.setIsSyntheticBlock(true); + + new TypeCheck( + compiler, + new SemanticReverseAbstractInterpreter( + compiler.getCodingConvention(), registry), + registry, topScope, scopeCreator, CheckLevel.WARNING, CheckLevel.OFF) + .process(null, second); + + assertEquals(1, compiler.getWarningCount()); + assertEquals("cannot instantiate non-constructor", + compiler.getWarnings()[0].description); + } + + private void checkObjectType(ObjectType objectType, String propertyName, + JSType expectedType) { + assertTrue("Expected " + objectType.getReferenceName() + + " to have property " + + propertyName, objectType.hasProperty(propertyName)); + assertTypeEquals("Expected " + objectType.getReferenceName() + + "'s property " + + propertyName + " to have type " + expectedType, + expectedType, objectType.getPropertyType(propertyName)); + } + + private void testTypes(String js) throws Exception { + testTypes(js, (String) null); + } + + private void testTypes(String js, String description) throws Exception { + testTypes(js, description, false); + } + + private void testTypes(String js, DiagnosticType type) throws Exception { + testTypes(js, type.format(), false); + } + + private void testClosureTypes(String js, String description) + throws Exception { + testClosureTypesMultipleWarnings(js, + description == null ? null : Lists.newArrayList(description)); + } + + private void testClosureTypesMultipleWarnings( + String js, List descriptions) throws Exception { + Node n = compiler.parseTestCode(js); + Node externs = new Node(Token.BLOCK); + Node externAndJsRoot = new Node(Token.BLOCK, externs, n); + externAndJsRoot.setIsSyntheticBlock(true); + + assertEquals("parsing error: " + + Joiner.on(", ").join(compiler.getErrors()), + 0, compiler.getErrorCount()); + + // For processing goog.addDependency for forward typedefs. + new ProcessClosurePrimitives(compiler, null, CheckLevel.ERROR) + .process(null, n); + + CodingConvention convention = compiler.getCodingConvention(); + new TypeCheck(compiler, + new ClosureReverseAbstractInterpreter( + convention, registry).append( + new SemanticReverseAbstractInterpreter( + convention, registry)) + .getFirst(), + registry) + .processForTesting(null, n); + + assertEquals(0, compiler.getErrorCount()); + + if (descriptions == null) { + assertEquals( + "unexpected warning(s) : " + + Joiner.on(", ").join(compiler.getWarnings()), + 0, compiler.getWarningCount()); + } else { + assertEquals(descriptions.size(), compiler.getWarningCount()); + Set actualWarningDescriptions = Sets.newHashSet(); + for (int i = 0; i < descriptions.size(); i++) { + actualWarningDescriptions.add(compiler.getWarnings()[i].description); + } + assertEquals( + Sets.newHashSet(descriptions), actualWarningDescriptions); + } + } + + void testTypes(String js, String description, boolean isError) + throws Exception { + testTypes(DEFAULT_EXTERNS, js, description, isError); + } + + void testTypes(String externs, String js, String description, boolean isError) + throws Exception { + Node n = parseAndTypeCheck(externs, js); + + JSError[] errors = compiler.getErrors(); + if (description != null && isError) { + assertTrue("expected an error", errors.length > 0); + assertEquals(description, errors[0].description); + errors = Arrays.asList(errors).subList(1, errors.length).toArray( + new JSError[errors.length - 1]); + } + if (errors.length > 0) { + fail("unexpected error(s):\n" + Joiner.on("\n").join(errors)); + } + + JSError[] warnings = compiler.getWarnings(); + if (description != null && !isError) { + assertTrue("expected a warning", warnings.length > 0); + assertEquals(description, warnings[0].description); + warnings = Arrays.asList(warnings).subList(1, warnings.length).toArray( + new JSError[warnings.length - 1]); + } + if (warnings.length > 0) { + fail("unexpected warnings(s):\n" + Joiner.on("\n").join(warnings)); + } + } + + /** + * Parses and type checks the JavaScript code. + */ + private Node parseAndTypeCheck(String js) { + return parseAndTypeCheck(DEFAULT_EXTERNS, js); + } + + private Node parseAndTypeCheck(String externs, String js) { + return parseAndTypeCheckWithScope(externs, js).root; + } + + /** + * Parses and type checks the JavaScript code and returns the Scope used + * whilst type checking. + */ + private TypeCheckResult parseAndTypeCheckWithScope(String js) { + return parseAndTypeCheckWithScope(DEFAULT_EXTERNS, js); + } + + private TypeCheckResult parseAndTypeCheckWithScope( + String externs, String js) { + compiler.init( + Lists.newArrayList(SourceFile.fromCode("[externs]", externs)), + Lists.newArrayList(SourceFile.fromCode("[testcode]", js)), + compiler.getOptions()); + + Node n = compiler.getInput(new InputId("[testcode]")).getAstRoot(compiler); + Node externsNode = compiler.getInput(new InputId("[externs]")) + .getAstRoot(compiler); + Node externAndJsRoot = new Node(Token.BLOCK, externsNode, n); + externAndJsRoot.setIsSyntheticBlock(true); + + assertEquals("parsing error: " + + Joiner.on(", ").join(compiler.getErrors()), + 0, compiler.getErrorCount()); + + Scope s = makeTypeCheck().processForTesting(externsNode, n); + return new TypeCheckResult(n, s); + } + + private Node typeCheck(Node n) { + Node externsNode = new Node(Token.BLOCK); + Node externAndJsRoot = new Node(Token.BLOCK, externsNode, n); + externAndJsRoot.setIsSyntheticBlock(true); + + makeTypeCheck().processForTesting(null, n); + return n; + } + + private TypeCheck makeTypeCheck() { + return new TypeCheck( + compiler, + new SemanticReverseAbstractInterpreter( + compiler.getCodingConvention(), registry), + registry); + } + + void testTypes(String js, String[] warnings) throws Exception { + Node n = compiler.parseTestCode(js); + assertEquals(0, compiler.getErrorCount()); + Node externsNode = new Node(Token.BLOCK); + Node externAndJsRoot = new Node(Token.BLOCK, externsNode, n); + + makeTypeCheck().processForTesting(null, n); + assertEquals(0, compiler.getErrorCount()); + if (warnings != null) { + assertEquals(warnings.length, compiler.getWarningCount()); + JSError[] messages = compiler.getWarnings(); + for (int i = 0; i < warnings.length && i < compiler.getWarningCount(); + i++) { + assertEquals(warnings[i], messages[i].description); + } + } else { + assertEquals(0, compiler.getWarningCount()); + } + } + + String suppressMissingProperty(String ... props) { + String result = "function dummy(x) { "; + for (String prop : props) { + result += "x." + prop + " = 3;"; + } + return result + "}"; + } + + private static class TypeCheckResult { + private final Node root; + private final Scope scope; + + private TypeCheckResult(Node root, Scope scope) { + this.root = root; + this.scope = scope; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/MakeDeclaredNamesUniqueTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/MakeDeclaredNamesUniqueTest.java new file mode 100644 index 0000000..518fc60 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/MakeDeclaredNamesUniqueTest.java @@ -0,0 +1,359 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.MakeDeclaredNamesUnique.InlineRenamer; +import com.google.javascript.rhino.Node; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class MakeDeclaredNamesUniqueTest extends CompilerTestCase { + + private boolean useDefaultRenamer = false; + private boolean invert = false; + private boolean removeConst = false; + private final String localNamePrefix = "unique_"; + + @Override + public CompilerPass getProcessor(final Compiler compiler) { + if (!invert) { + return new CompilerPass() { + @Override + public void process(Node externs, Node root) { + compiler.resetUniqueNameId(); + MakeDeclaredNamesUnique renamer = null; + if (useDefaultRenamer) { + renamer = new MakeDeclaredNamesUnique(); + } else { + renamer = new MakeDeclaredNamesUnique( + new InlineRenamer( + compiler.getUniqueNameIdSupplier(), + localNamePrefix, + removeConst)); + } + NodeTraversal.traverseRoots( + compiler, Lists.newArrayList(externs, root), renamer); + } + }; + } else { + return MakeDeclaredNamesUnique.getContextualRenameInverter(compiler); + } + } + + @Override + protected int getNumRepetitions() { + // The normalize pass is only run once. + return 1; + } + + @Override + public void setUp() { + removeConst = false; + invert = false; + useDefaultRenamer = false; + } + + private void testWithInversion(String original, String expected) { + invert = false; + test(original, expected); + invert = true; + test(expected, original); + invert = false; + } + + private void testSameWithInversion(String externs, String original) { + invert = false; + testSame(externs, original, null); + invert = true; + testSame(externs, original, null); + invert = false; + } + + private void testSameWithInversion(String original) { + testSameWithInversion("", original); + } + + private String wrapInFunction(String s) { + return "function f(){" + s + "}"; + } + + private void testInFunction(String original, String expected) { + test(wrapInFunction(original), wrapInFunction(expected)); + } + + public void testMakeLocalNamesUniqueWithContext1() { + // Set the test type + this.useDefaultRenamer = true; + + invert = true; + test( + "var a;function foo(){var a$$inline_1; a = 1}", + "var a;function foo(){var a$$0; a = 1}"); + test( + "var a;function foo(){var a$$inline_1;}", + "var a;function foo(){var a;}"); + } + + public void testMakeLocalNamesUniqueWithContext2() { + // Set the test type + this.useDefaultRenamer = true; + + // Verify global names are untouched. + testSameWithInversion("var a;"); + + // Verify global names are untouched. + testSameWithInversion("a;"); + + // Local names are made unique. + testWithInversion( + "var a;function foo(a){var b;a}", + "var a;function foo(a$$1){var b;a$$1}"); + testWithInversion( + "var a;function foo(){var b;a}function boo(){var b;a}", + "var a;function foo(){var b;a}function boo(){var b$$1;a}"); + testWithInversion( + "function foo(a){var b}" + + "function boo(a){var b}", + "function foo(a){var b}" + + "function boo(a$$1){var b$$1}"); + + // Verify functions expressions are renamed. + testWithInversion( + "var a = function foo(){foo()};var b = function foo(){foo()};", + "var a = function foo(){foo()};var b = function foo$$1(){foo$$1()};"); + + // Verify catch exceptions names are made unique + testWithInversion( + "try { } catch(e) {e;}", + "try { } catch(e) {e;}"); + + // Inversion does not handle exceptions correctly. + test( + "try { } catch(e) {e;}; try { } catch(e) {e;}", + "try { } catch(e) {e;}; try { } catch(e$$1) {e$$1;}"); + test( + "try { } catch(e) {e; try { } catch(e) {e;}};", + "try { } catch(e) {e; try { } catch(e$$1) {e$$1;} }; "); + } + + public void testMakeLocalNamesUniqueWithContext3() { + // Set the test type + this.useDefaultRenamer = true; + + String externs = "var extern1 = {};"; + + // Verify global names are untouched. + testSameWithInversion(externs, "var extern1 = extern1 || {};"); + + // Verify global names are untouched. + testSame(externs, "var extern1 = extern1 || {};", null); + } + + + + public void testMakeLocalNamesUniqueWithContext4() { + // Set the test type + this.useDefaultRenamer = true; + + // Inversion does not handle exceptions correctly. + testInFunction( + "var e; try { } catch(e) {e;}; try { } catch(e) {e;}", + "var e; try { } catch(e$$1) {e$$1;}; try { } catch(e$$2) {e$$2;}"); + testInFunction( + "var e; try { } catch(e) {e; try { } catch(e) {e;}}", + "var e; try { } catch(e$$1) {e$$1; try { } catch(e$$2) {e$$2;} }"); + testInFunction( + "try { } catch(e) {e;}; try { } catch(e) {e;} var e;", + "try { } catch(e$$1) {e$$1;}; try { } catch(e$$2) {e$$2;} var e;"); + testInFunction( + "try { } catch(e) {e; try { } catch(e) {e;}} var e;", + "try { } catch(e$$1) {e$$1; try { } catch(e$$2) {e$$2;} } var e;"); + + invert = true; + + testInFunction( + "var e; try { } catch(e$$0) {e$$0;}; try { } catch(e$$1) {e$$1;}", + "var e; try { } catch(e$$2) {e$$2;}; try { } catch(e$$0) {e$$0;}"); + testInFunction( + "var e; try { } catch(e$$1) {e$$1; try { } catch(e$$2) {e$$2;} };", + "var e; try { } catch(e$$0) {e$$0; try { } catch(e$$1) {e$$1;} };"); + testInFunction( + "try { } catch(e) {e;}; try { } catch(e$$1) {e$$1;};var e$$2;", + "try { } catch(e) {e;}; try { } catch(e$$0) {e$$0;};var e$$1;"); + testInFunction( + "try { } catch(e) {e; try { } catch(e$$1) {e$$1;} };var e$$2", + "try { } catch(e) {e; try { } catch(e$$0) {e$$0;} };var e$$1"); + } + + public void testMakeLocalNamesUniqueWithContext5() { + // Set the test type + this.useDefaultRenamer = true; + + testWithInversion( + "function f(){var f; f = 1}", + "function f(){var f$$1; f$$1 = 1}"); + testWithInversion( + "function f(f){f = 1}", + "function f(f$$1){f$$1 = 1}"); + testWithInversion( + "function f(f){var f; f = 1}", + "function f(f$$1){var f$$1; f$$1 = 1}"); + + test( + "var fn = function f(){var f; f = 1}", + "var fn = function f(){var f$$1; f$$1 = 1}"); + test( + "var fn = function f(f){f = 1}", + "var fn = function f(f$$1){f$$1 = 1}"); + test( + "var fn = function f(f){var f; f = 1}", + "var fn = function f(f$$1){var f$$1; f$$1 = 1}"); + } + + public void testArguments() { + // Set the test type + this.useDefaultRenamer = true; + + // Don't distinguish between "arguments", it can't be made unique. + testSameWithInversion( + "function foo(){var arguments;function bar(){var arguments;}}"); + + invert = true; + + // Don't introduce new references to arguments, it is special. + test( + "function foo(){var arguments$$1;}", + "function foo(){var arguments$$0;}"); + } + + public void testMakeLocalNamesUniqueWithoutContext() { + // Set the test type + this.useDefaultRenamer = false; + + test("var a;", + "var a$$unique_0"); + + // Verify undeclared names are untouched. + testSame("a;"); + + // Local names are made unique. + test("var a;" + + "function foo(a){var b;a}", + "var a$$unique_0;" + + "function foo$$unique_1(a$$unique_2){var b$$unique_3;a$$unique_2}"); + test("var a;" + + "function foo(){var b;a}" + + "function boo(){var b;a}", + "var a$$unique_0;" + + "function foo$$unique_1(){var b$$unique_3;a$$unique_0}" + + "function boo$$unique_2(){var b$$unique_4;a$$unique_0}"); + + // Verify function expressions are renamed. + test("var a = function foo(){foo()};", + "var a$$unique_0 = function foo$$unique_1(){foo$$unique_1()};"); + + // Verify catch exceptions names are made unique + test("try { } catch(e) {e;}", + "try { } catch(e$$unique_0) {e$$unique_0;}"); + test("try { } catch(e) {e;};" + + "try { } catch(e) {e;}", + "try { } catch(e$$unique_0) {e$$unique_0;};" + + "try { } catch(e$$unique_1) {e$$unique_1;}"); + test("try { } catch(e) {e; " + + "try { } catch(e) {e;}};", + "try { } catch(e$$unique_0) {e$$unique_0; " + + "try { } catch(e$$unique_1) {e$$unique_1;} }; "); + } + + public void testOnlyInversion() { + invert = true; + test("function f(a, a$$1) {}", + "function f(a, a$$0) {}"); + test("function f(a$$1, b$$2) {}", + "function f(a, b) {}"); + test("function f(a$$1, a$$2) {}", + "function f(a, a$$0) {}"); + testSame("try { } catch(e) {e;}; try { } catch(e$$1) {e$$1;}"); + testSame("try { } catch(e) {e; try { } catch(e$$1) {e$$1;} }; "); + testSame("var a$$1;"); + testSame("function f() { var $$; }"); + test("var CONST = 3; var b = CONST;", + "var CONST = 3; var b = CONST;"); + test("function f() {var CONST = 3; var ACONST$$1 = 2;}", + "function f() {var CONST = 3; var ACONST = 2;}"); + } + + public void testOnlyInversion2() { + invert = true; + test("function f() {try { } catch(e) {e;}; try { } catch(e$$0) {e$$0;}}", + "function f() {try { } catch(e) {e;}; try { } catch(e$$1) {e$$1;}}"); + } + + public void testOnlyInversion3() { + invert = true; + test( + "function x1() {" + + " var a$$1;" + + " function x2() {" + + " var a$$2;" + + " }" + + " function x3() {" + + " var a$$3;" + + " }" + + "}", + "function x1() {" + + " var a$$0;" + + " function x2() {" + + " var a;" + + " }" + + " function x3() {" + + " var a;" + + " }" + + "}"); + } + + public void testOnlyInversion4() { + invert = true; + test( + "function x1() {" + + " var a$$0;" + + " function x2() {" + + " var a;a$$0++" + + " }" + + "}", + "function x1() {" + + " var a$$1;" + + " function x2() {" + + " var a;a$$1++" + + " }" + + "}"); + } + + public void testConstRemovingRename1() { + removeConst = true; + test("(function () {var CONST = 3; var ACONST$$1 = 2;})", + "(function () {var CONST$$unique_0 = 3; var ACONST$$unique_1 = 2;})"); + } + + public void testConstRemovingRename2() { + removeConst = true; + test("var CONST = 3; var b = CONST;", + "var CONST$$unique_0 = 3; var b$$unique_1 = CONST$$unique_0;"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/MarkNoSideEffectCallsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/MarkNoSideEffectCallsTest.java new file mode 100644 index 0000000..80852e9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/MarkNoSideEffectCallsTest.java @@ -0,0 +1,304 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import static com.google.javascript.jscomp.MarkNoSideEffectCalls.INVALID_NO_SIDE_EFFECT_ANNOTATION; +import com.google.javascript.rhino.Node; +import java.util.Collections; +import java.util.List; + +/** + * Tests for {@link MarkNoSideEffectCalls} + * + */ +public class MarkNoSideEffectCallsTest extends CompilerTestCase { + List noSideEffectCalls = Lists.newArrayList(); + + private static String kExterns = + "function externSef1(){}" + + "/**@nosideeffects*/function externNsef1(){}" + + "var externSef2 = function(){};" + + "/**@nosideeffects*/var externNsef2 = function(){};" + + "var externNsef3 = /**@nosideeffects*/function(){};" + + "var externObj;" + + "externObj.sef1 = function(){};" + + "/**@nosideeffects*/externObj.nsef1 = function(){};" + + "externObj.nsef2 = /**@nosideeffects*/function(){};" + + "externObj.sef2;" + + "/**@nosideeffects*/externObj.nsef3;"; + + public MarkNoSideEffectCallsTest() { + super(kExterns); + } + + @Override + protected int getNumRepetitions() { + // run pass once. + return 1; + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + noSideEffectCalls.clear(); + } + + public void testFunctionAnnotation() throws Exception { + testMarkCalls("/**@nosideeffects*/function f(){}", "f()", + ImmutableList.of("f")); + testMarkCalls("/**@nosideeffects*/var f = function(){};", "f()", + ImmutableList.of("f")); + testMarkCalls("var f = /**@nosideeffects*/function(){};", "f()", + ImmutableList.of("f")); + testMarkCalls("var f; f = /**@nosideeffects*/function(){};", "f()", + ImmutableList.of("f")); + testMarkCalls("var f; /**@nosideeffects*/ f = function(){};", "f()", + ImmutableList.of("f")); + + // no annotation + testMarkCalls("function f(){}", Collections.emptyList()); + testMarkCalls("function f(){} f()", Collections.emptyList()); + + // 2 annotations + testMarkCalls("/**@nosideeffects*/var f = " + + "/**@nosideeffects*/function(){};", + "f()", + ImmutableList.of("f")); + } + + public void testNamespaceAnnotation() throws Exception { + testMarkCalls("var o = {}; o.f = /**@nosideeffects*/function(){};", + "o.f()", ImmutableList.of("o.f")); + testMarkCalls("var o = {}; o.f = /**@nosideeffects*/function(){};", + "o.f()", ImmutableList.of("o.f")); + testMarkCalls("var o = {}; o.f = function(){}; o.f()", + Collections.emptyList()); + } + + public void testConstructorAnnotation() throws Exception { + testMarkCalls("/**@nosideeffects*/function c(){};", "new c", + ImmutableList.of("c")); + testMarkCalls("var c = /**@nosideeffects*/function(){};", "new c", + ImmutableList.of("c")); + testMarkCalls("/**@nosideeffects*/var c = function(){};", "new c", + ImmutableList.of("c")); + testMarkCalls("function c(){}; new c", Collections.emptyList()); + } + + public void testMultipleDefinition() throws Exception { + testMarkCalls("/**@nosideeffects*/function f(){}" + + "/**@nosideeffects*/f = function(){};", + "f()", + ImmutableList.of("f")); + testMarkCalls("function f(){}" + + "/**@nosideeffects*/f = function(){};", + "f()", + Collections.emptyList()); + testMarkCalls("/**@nosideeffects*/function f(){}", + "f = function(){};" + + "f()", + Collections.emptyList()); + } + + public void testAssignNoFunction() throws Exception { + testMarkCalls("/**@nosideeffects*/function f(){}", "f = 1; f()", + ImmutableList.of("f")); + testMarkCalls("/**@nosideeffects*/function f(){}", "f = 1 || 2; f()", + Collections.emptyList()); + } + + public void testPrototype() throws Exception { + testMarkCalls("function c(){};" + + "/**@nosideeffects*/c.prototype.g = function(){};", + "var o = new c; o.g()", + ImmutableList.of("o.g")); + testMarkCalls("function c(){};" + + "/**@nosideeffects*/c.prototype.g = function(){};", + "function f(){}" + + "var o = new c; o.g(); f()", + ImmutableList.of("o.g")); + + // replace o.f with a function that has side effects + testMarkCalls("function c(){};" + + "/**@nosideeffects*/c.prototype.g = function(){};", + "var o = new c;" + + "o.g = function(){};" + + "o.g()", + ImmutableList.of()); + // two classes with same property; neither has side effects + testMarkCalls("function c1(){};" + + "/**@nosideeffects*/c1.prototype.f = function(){};" + + "function c2(){};" + + "/**@nosideeffects*/c2.prototype.f = function(){};", + "var o = new c1;" + + "o.f()", + ImmutableList.of("o.f")); + + // two classes with same property; one has side effects + testMarkCalls("function c1(){};" + + "/**@nosideeffects*/c1.prototype.f = function(){};", + "function c2(){};" + + "c2.prototype.f = function(){};" + + "var o = new c1;" + + "o.f()", + Collections.emptyList()); + } + + public void testAnnotationInExterns() throws Exception { + testMarkCalls("externSef1()", Collections.emptyList()); + testMarkCalls("externSef2()", Collections.emptyList()); + testMarkCalls("externNsef1()", ImmutableList.of("externNsef1")); + testMarkCalls("externNsef2()", ImmutableList.of("externNsef2")); + testMarkCalls("externNsef3()", ImmutableList.of("externNsef3")); + } + + public void testNamespaceAnnotationInExterns() throws Exception { + testMarkCalls("externObj.sef1()", Collections.emptyList()); + testMarkCalls("externObj.sef2()", Collections.emptyList()); + testMarkCalls("externObj.nsef1()", ImmutableList.of("externObj.nsef1")); + testMarkCalls("externObj.nsef2()", ImmutableList.of("externObj.nsef2")); + + testMarkCalls("externObj.nsef3()", ImmutableList.of("externObj.nsef3")); + } + + public void testOverrideDefinitionInSource() throws Exception { + // both have side effects. + testMarkCalls("var obj = {}; obj.sef1 = function(){}; obj.sef1()", + Collections.emptyList()); + + // extern has side effects. + testMarkCalls("var obj = {};" + + "/**@nosideeffects*/obj.sef1 = function(){};", + "obj.sef1()", + Collections.emptyList()); + + // override in source also has side effects. + testMarkCalls("var obj = {}; obj.nsef1 = function(){}; obj.nsef1()", + Collections.emptyList()); + + // override in source also has no side effects. + testMarkCalls("var obj = {};" + + "/**@nosideeffects*/obj.nsef1 = function(){};", + "obj.nsef1()", + ImmutableList.of("obj.nsef1")); + } + + public void testApply1() throws Exception { + testMarkCalls("/**@nosideeffects*/ var f = function() {}", + "f.apply()", + ImmutableList.of("f.apply")); + } + + public void testApply2() throws Exception { + testMarkCalls("var f = function() {}", + "f.apply()", + ImmutableList.of()); + } + + public void testCall1() throws Exception { + testMarkCalls("/**@nosideeffects*/ var f = function() {}", + "f.call()", + ImmutableList.of("f.call")); + } + + public void testCall2() throws Exception { + testMarkCalls("var f = function() {}", + "f.call()", + ImmutableList.of()); + } + + public void testInvalidAnnotation1() throws Exception { + test("/** @nosideeffects */ function foo() {}", + null, INVALID_NO_SIDE_EFFECT_ANNOTATION); + } + + public void testInvalidAnnotation2() throws Exception { + test("var f = /** @nosideeffects */ function() {}", + null, INVALID_NO_SIDE_EFFECT_ANNOTATION); + } + + public void testInvalidAnnotation3() throws Exception { + test("/** @nosideeffects */ var f = function() {}", + null, INVALID_NO_SIDE_EFFECT_ANNOTATION); + } + + public void testInvalidAnnotation4() throws Exception { + test("var f = function() {};" + + "/** @nosideeffects */ f.x = function() {}", + null, INVALID_NO_SIDE_EFFECT_ANNOTATION); + } + + public void testInvalidAnnotation5() throws Exception { + test("var f = function() {};" + + "f.x = /** @nosideeffects */ function() {}", + null, INVALID_NO_SIDE_EFFECT_ANNOTATION); + } + + void testMarkCalls(String source, List expected) { + testMarkCalls("", source, expected); + } + + void testMarkCalls( + String extraExterns, String source, List expected) { + testSame(kExterns + extraExterns, source, null); + assertEquals(expected, noSideEffectCalls); + noSideEffectCalls.clear(); + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new NoSideEffectCallEnumerator(compiler); + } + + /** + * Run MarkNoSideEffectCalls, then gather a list of calls that are + * marked as having no side effects. + */ + private class NoSideEffectCallEnumerator + extends AbstractPostOrderCallback implements CompilerPass { + private final MarkNoSideEffectCalls passUnderTest; + private final Compiler compiler; + + NoSideEffectCallEnumerator(Compiler compiler) { + this.passUnderTest = new MarkNoSideEffectCalls(compiler); + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + passUnderTest.process(externs, root); + NodeTraversal.traverse(compiler, externs, this); + NodeTraversal.traverse(compiler, root, this); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isNew()) { + if (!NodeUtil.constructorCallHasSideEffects(n)) { + noSideEffectCalls.add(n.getFirstChild().getQualifiedName()); + } + } else if (n.isCall()) { + if (!NodeUtil.functionCallHasSideEffects(n)) { + noSideEffectCalls.add(n.getFirstChild().getQualifiedName()); + } + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/MaybeReachingVariableUseTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/MaybeReachingVariableUseTest.java new file mode 100644 index 0000000..c6086fe --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/MaybeReachingVariableUseTest.java @@ -0,0 +1,173 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.Node; +import junit.framework.TestCase; + +import java.util.Collection; +import java.util.List; + +/** + * Tests for {@link MaybeReachingVariableUse}. + * + */ +public class MaybeReachingVariableUseTest extends TestCase { + + private MaybeReachingVariableUse useDef = null; + private Node def = null; + private List uses = null; + + /* + * The test cases consist of a short code snippet that has an instruction + * labeled with D and one or more with label starting with U. When assertMatch + * is called, the test suite verifies that all the uses with label starting + * with U is reachable to the definition label at D. + */ + + public void testStraightLine() { + assertMatch("D:var x=1; U: x"); + assertMatch("var x; D:x=1; U: x"); + assertNotMatch("D:var x=1; x = 2; U: x"); + assertMatch("var x=1; D:x=2; U: x"); + assertNotMatch("U:x; D:var x = 1"); + assertMatch("D: var x = 1; var y = 2; y; U:x"); + } + + public void testIf() { + assertMatch("var x; if(a){ D:x=1 }else { x=2 }; U:x"); + assertMatch("var x; if(a){ x=1 }else { D:x=2 }; U:x"); + assertMatch("D:var x=1; if(a){ U1: x }else { U2: x };"); + } + + public void testLoops() { + assertMatch("var x=0; while(a){ D:x=1 }; U:x"); + assertMatch("var x=0; for(;;) { D:x=1 }; U:x"); + + assertMatch("D:var x=1; while(a) { U:x }"); + assertMatch("D:var x=1; for(;;) { U:x }"); + } + + public void testConditional() { + assertMatch("var x=0; var y; D:(x=1)&&y; U:x"); + assertMatch("var x=0; var y; D:y&&(x=1); U:x"); + assertMatch("var x=0; var y=0; D:(x=1)&&(y=0); U:x"); + assertMatch("var x=0; var y=0; D:(y=0)&&(x=1); U:x"); + assertNotMatch("D: var x=0; var y=0; (x=1)&&(y=0); U:x"); + assertMatch("D: var x=0; var y=0; (y=1)&&((y=2)||(x=1)); U:x"); + assertMatch("D: var x=0; var y=0; (y=0)&&(x=1); U:x"); + } + + public void testUseAndDefInSameInstruction() { + assertNotMatch("D:var x=0; U:x=1,x"); + assertMatch("D:var x=0; U:x,x=1"); + } + + public void testAssignmentInExpressions() { + assertMatch("var x=0; D:foo(bar(x=1)); U:x"); + assertMatch("var x=0; D:foo(bar + (x = 1)); U:x"); + } + + public void testHook() { + assertMatch("var x=0; D:foo() ? x=1 : bar(); U:x"); + assertMatch("var x=0; D:foo() ? x=1 : x=2; U:x"); + } + + public void testAssignmentOps() { + assertNotMatch("D: var x = 0; U: x = 100"); + assertMatch("D: var x = 0; U: x += 100"); + assertMatch("D: var x = 0; U: x -= 100"); + } + + public void testInc() { + assertMatch("D: var x = 0; U:x++"); + assertMatch("var x = 0; D:x++; U:x"); + } + + public void testForIn() { + // Uses within FOR-IN header are hard to test. They are covered + // by the tests in the flow sensitive inliner. + assertNotMatch("D: var x = [], foo; U: for (x in foo) { }"); + assertNotMatch("D: var x = [], foo; for (x in foo) { U:x }"); + assertMatch("var x = [], foo; D: for (x in foo) { U:x }"); + } + + public void testTryCatch() { + assertMatch( + "D: var x = 1; " + + "try { U: var y = foo() + x; } catch (e) {} " + + "U: var z = x;"); + } + + /** + * The def of x at D: may be used by the read of x at U:. + */ + private void assertMatch(String src) { + computeUseDef(src); + Collection result = useDef.getUses("x", def); + assertTrue(result.size() == uses.size()); + assertTrue(result.containsAll(uses)); + } + + /** + * The def of x at D: is not used by the read of x at U:. + */ + private void assertNotMatch(String src) { + computeUseDef(src); + assertFalse(useDef.getUses("x", def).contains(uses)); + } + + /** + * Computes reaching use on given source. + */ + private void computeUseDef(String src) { + Compiler compiler = new Compiler(); + src = "function _FUNCTION(param1, param2){" + src + "}"; + Node n = compiler.parseTestCode(src).getFirstChild(); + assertEquals(0, compiler.getErrorCount()); + Scope scope = new SyntacticScopeCreator(compiler).createScope(n, null); + ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); + cfa.process(null, n); + ControlFlowGraph cfg = cfa.getCfg(); + useDef = new MaybeReachingVariableUse(cfg, scope, compiler); + useDef.analyze(); + def = null; + uses = Lists.newArrayList(); + new NodeTraversal(compiler,new LabelFinder()).traverse(n); + assertNotNull("Code should have an instruction labeled D", def); + assertFalse("Code should have an instruction labeled starting withing U", + uses.isEmpty()); + } + + /** + * Finds the D: and U: label and store which node they point to. + */ + private class LabelFinder extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isLabel()) { + if (n.getFirstChild().getString().equals("D")) { + def = n.getLastChild(); + } else if (n.getFirstChild().getString().startsWith("U")) { + uses.add(n.getLastChild()); + } + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/MemoizedScopeCreatorTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/MemoizedScopeCreatorTest.java new file mode 100644 index 0000000..2294198 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/MemoizedScopeCreatorTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import junit.framework.TestCase; + +/** + * A dopey test for {@link MemoizedScopeCreator}. This is mostly here + * just so it's easy to write more tests if this becomes more complicated. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class MemoizedScopeCreatorTest extends TestCase { + + public void testMemoization() throws Exception { + Node trueNode = new Node(Token.TRUE); + Node falseNode = new Node(Token.FALSE); + // Wow, is there really a circular dependency between JSCompiler and + // SyntacticScopeCreator? + Compiler compiler = new Compiler(); + compiler.initOptions(new CompilerOptions()); + ScopeCreator creator = new MemoizedScopeCreator( + new SyntacticScopeCreator(compiler)); + Scope scopeA = creator.createScope(trueNode, null); + assertSame(scopeA, creator.createScope(trueNode, null)); + assertNotSame(scopeA, creator.createScope(falseNode, null)); + } + + public void testPreconditionCheck() throws Exception { + Compiler compiler = new Compiler(); + compiler.initOptions(new CompilerOptions()); + Node trueNode = new Node(Token.TRUE); + ScopeCreator creator = new MemoizedScopeCreator( + new SyntacticScopeCreator(compiler)); + Scope scopeA = creator.createScope(trueNode, null); + + boolean handled = false; + try { + creator.createScope(trueNode, scopeA); + } catch (IllegalStateException e) { + handled = true; + } + assertTrue(handled); + } + +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/MinimizeExitPointsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/MinimizeExitPointsTest.java new file mode 100644 index 0000000..8a3cff0 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/MinimizeExitPointsTest.java @@ -0,0 +1,270 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class MinimizeExitPointsTest extends CompilerTestCase { + + @Override + public void setUp() { + super.enableLineNumberCheck(true); + } + + @Override + protected CompilerPass getProcessor(final Compiler compiler) { + return new CompilerPass() { + @Override + public void process(Node externs, Node js) { + NodeTraversal.traverse(compiler, js, new MinimizeExitPoints(compiler)); + } + }; + } + + @Override + protected int getNumRepetitions() { + return 1; + } + + void foldSame(String js) { + testSame(js); + } + + void fold(String js, String expected) { + test(js, expected); + } + + void fold(String js, String expected, DiagnosticType warning) { + test(js, expected, warning); + } + + public void testBreakOptimization() throws Exception { + fold("f:{if(true){a();break f;}else;b();}", + "f:{if(true){a()}else{b()}}"); + fold("f:{if(false){a();break f;}else;b();break f;}", + "f:{if(false){a()}else{b()}}"); + fold("f:{if(a()){b();break f;}else;c();}", + "f:{if(a()){b();}else{c();}}"); + fold("f:{if(a()){b()}else{c();break f;}}", + "f:{if(a()){b()}else{c();}}"); + fold("f:{if(a()){b();break f;}else;}", + "f:{if(a()){b();}else;}"); + fold("f:{if(a()){break f;}else;}", + "f:{if(a()){}else;}"); + + fold("f:while(a())break f;", + "f:while(a())break f"); + foldSame("f:for(x in a())break f"); + + fold("f:{while(a())break;}", + "f:{while(a())break;}"); + foldSame("f:{for(x in a())break}"); + + fold("f:try{break f;}catch(e){break f;}", + "f:try{}catch(e){}"); + fold("f:try{if(a()){break f;}else{break f;} break f;}catch(e){}", + "f:try{if(a()){}else{}}catch(e){}"); + + fold("f:g:break f", + ""); + fold("f:g:{if(a()){break f;}else{break f;} break f;}", + "f:g:{if(a()){}else{}}"); + } + + public void testFunctionReturnOptimization() throws Exception { + fold("function f(){if(a()){b();if(c())return;}}", + "function f(){if(a()){b();if(c());}}"); + fold("function f(){if(x)return; x=3; return; }", + "function f(){if(x); else x=3}"); + fold("function f(){if(true){a();return;}else;b();}", + "function f(){if(true){a();}else{b();}}"); + fold("function f(){if(false){a();return;}else;b();return;}", + "function f(){if(false){a();}else{b();}}"); + fold("function f(){if(a()){b();return;}else;c();}", + "function f(){if(a()){b();}else{c();}}"); + fold("function f(){if(a()){b()}else{c();return;}}", + "function f(){if(a()){b()}else{c();}}"); + fold("function f(){if(a()){b();return;}else;}", + "function f(){if(a()){b();}else;}"); + fold("function f(){if(a()){return;}else{return;} return;}", + "function f(){if(a()){}else{}}"); + fold("function f(){if(a()){return;}else{return;} b();}", + "function f(){if(a()){}else{return;b()}}"); + + fold("function f(){while(a())return;}", + "function f(){while(a())return}"); + foldSame("function f(){for(x in a())return}"); + + fold("function f(){while(a())break;}", + "function f(){while(a())break}"); + foldSame("function f(){for(x in a())break}"); + + fold("function f(){try{return;}catch(e){return;}finally{return}}", + "function f(){try{}catch(e){}finally{}}"); + fold("function f(){try{return;}catch(e){return;}}", + "function f(){try{}catch(e){}}"); + fold("function f(){try{return;}finally{return;}}", + "function f(){try{}finally{}}"); + fold("function f(){try{if(a()){return;}else{return;} return;}catch(e){}}", + "function f(){try{if(a()){}else{}}catch(e){}}"); + + fold("function f(){g:return}", + "function f(){}"); + fold("function f(){g:if(a()){return;}else{return;} return;}", + "function f(){g:if(a()){}else{}}"); + fold("function f(){try{g:if(a()){} return;}finally{return}}", + "function f(){try{g:if(a()){}}finally{}}"); + } + + public void testWhileContinueOptimization() throws Exception { + fold("while(true){if(x)continue; x=3; continue; }", + "while(true)if(x);else x=3"); + foldSame("while(true){a();continue;b();}"); + fold("while(true){if(true){a();continue;}else;b();}", + "while(true){if(true){a();}else{b()}}"); + fold("while(true){if(false){a();continue;}else;b();continue;}", + "while(true){if(false){a()}else{b();}}"); + fold("while(true){if(a()){b();continue;}else;c();}", + "while(true){if(a()){b();}else{c();}}"); + fold("while(true){if(a()){b();}else{c();continue;}}", + "while(true){if(a()){b();}else{c();}}"); + fold("while(true){if(a()){b();continue;}else;}", + "while(true){if(a()){b();}else;}"); + fold("while(true){if(a()){continue;}else{continue;} continue;}", + "while(true){if(a()){}else{}}"); + fold("while(true){if(a()){continue;}else{continue;} b();}", + "while(true){if(a()){}else{continue;b();}}"); + + fold("while(true)while(a())continue;", + "while(true)while(a());"); + fold("while(true)for(x in a())continue", + "while(true)for(x in a());"); + + fold("while(true)while(a())break;", + "while(true)while(a())break"); + fold("while(true)for(x in a())break", + "while(true)for(x in a())break"); + + fold("while(true){try{continue;}catch(e){continue;}}", + "while(true){try{}catch(e){}}"); + fold("while(true){try{if(a()){continue;}else{continue;}" + + "continue;}catch(e){}}", + "while(true){try{if(a()){}else{}}catch(e){}}"); + + fold("while(true){g:continue}", + "while(true){}"); + // This case could be improved. + fold("while(true){g:if(a()){continue;}else{continue;} continue;}", + "while(true){g:if(a());else;}"); + } + + public void testDoContinueOptimization() throws Exception { + fold("do{if(x)continue; x=3; continue; }while(true)", + "do if(x); else x=3; while(true)"); + foldSame("do{a();continue;b()}while(true)"); + fold("do{if(true){a();continue;}else;b();}while(true)", + "do{if(true){a();}else{b();}}while(true)"); + fold("do{if(false){a();continue;}else;b();continue;}while(true)", + "do{if(false){a();}else{b();}}while(true)"); + fold("do{if(a()){b();continue;}else;c();}while(true)", + "do{if(a()){b();}else{c()}}while(true)"); + fold("do{if(a()){b();}else{c();continue;}}while(true)", + "do{if(a()){b();}else{c();}}while(true)"); + fold("do{if(a()){b();continue;}else;}while(true)", + "do{if(a()){b();}else;}while(true)"); + fold("do{if(a()){continue;}else{continue;} continue;}while(true)", + "do{if(a()){}else{}}while(true)"); + fold("do{if(a()){continue;}else{continue;} b();}while(true)", + "do{if(a()){}else{continue; b();}}while(true)"); + + fold("do{while(a())continue;}while(true)", + "do while(a());while(true)"); + fold("do{for(x in a())continue}while(true)", + "do for(x in a());while(true)"); + + fold("do{while(a())break;}while(true)", + "do while(a())break;while(true)"); + fold("do for(x in a())break;while(true)", + "do for(x in a())break;while(true)"); + + fold("do{try{continue;}catch(e){continue;}}while(true)", + "do{try{}catch(e){}}while(true)"); + fold("do{try{if(a()){continue;}else{continue;}" + + "continue;}catch(e){}}while(true)", + "do{try{if(a()){}else{}}catch(e){}}while(true)"); + + fold("do{g:continue}while(true)", + "do{}while(true)"); + // This case could be improved. + fold("do{g:if(a()){continue;}else{continue;} continue;}while(true)", + "do{g:if(a());else;}while(true)"); + + fold("do { foo(); continue; } while(false)", + "do { foo(); } while(false)"); + fold("do { foo(); break; } while(false)", + "do { foo(); } while(false)"); + } + + public void testForContinueOptimization() throws Exception { + fold("for(x in y){if(x)continue; x=3; continue; }", + "for(x in y)if(x);else x=3"); + foldSame("for(x in y){a();continue;b()}"); + fold("for(x in y){if(true){a();continue;}else;b();}", + "for(x in y){if(true)a();else b();}"); + fold("for(x in y){if(false){a();continue;}else;b();continue;}", + "for(x in y){if(false){a();}else{b()}}"); + fold("for(x in y){if(a()){b();continue;}else;c();}", + "for(x in y){if(a()){b();}else{c();}}"); + fold("for(x in y){if(a()){b();}else{c();continue;}}", + "for(x in y){if(a()){b();}else{c();}}"); + fold("for(x=0;x cfg = cfa.getCfg(); + defUse = new MustBeReachingVariableDef(cfg, scope, compiler); + defUse.analyze(); + def = null; + use = null; + new NodeTraversal(compiler,new LabelFinder()).traverse(root); + assertNotNull("Code should have an instruction labeled D", def); + assertNotNull("Code should have an instruction labeled U", use); + } + + /** + * Finds the D: and U: label and store which node they point to. + */ + private class LabelFinder extends AbstractPostOrderCallback { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + if (n.isLabel()) { + if (n.getFirstChild().getString().equals("D")) { + def = n.getLastChild(); + } else if (n.getFirstChild().getString().equals("U")) { + use = n.getLastChild(); + } + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NameAnalyzerTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NameAnalyzerTest.java new file mode 100644 index 0000000..0bc5ae4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NameAnalyzerTest.java @@ -0,0 +1,1946 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + * Tests for {@link NameAnalyzer} + * + */ +public class NameAnalyzerTest extends CompilerTestCase { + + private static String kExterns = + "var window, top;" + + "var document;" + + "var Function;" + + "var Array;" + + "var externfoo; methods.externfoo;"; + + public NameAnalyzerTest() { + super(kExterns); + } + + @Override + protected void setUp() { + super.enableNormalize(); + super.enableLineNumberCheck(true); + } + + @Override + protected int getNumRepetitions() { + // pass reaches steady state after 1 iteration. + return 1; + } + + public void testRemoveVarDeclaration1() { + test("var foo = 3;", ""); + } + + public void testRemoveVarDeclaration2() { + test("var foo = 3, bar = 4; externfoo = foo;", + "var foo = 3; externfoo = foo;"); + } + + public void testRemoveVarDeclaration3() { + test("var a = f(), b = 1, c = 2; b; c", "f();var b = 1, c = 2; b; c"); + } + + public void testRemoveVarDeclaration4() { + test("var a = 0, b = f(), c = 2; a; c", "var a = 0;f();var c = 2; a; c"); + } + + public void testRemoveVarDeclaration5() { + test("var a = 0, b = 1, c = f(); a; b", "var a = 0, b = 1; f(); a; b"); + } + + public void testRemoveVarDeclaration6() { + test("var a = 0, b = a = 1; a", "var a = 0; a = 1; a"); + } + + public void testRemoveVarDeclaration7() { + test("var a = 0, b = a = 1", ""); + } + + public void testRemoveVarDeclaration8() { + test("var a;var b = 0, c = a = b = 1", ""); + } + + + public void testRemoveDeclaration1() { + test("var a;var b = 0, c = a = b = 1", ""); + } + + public void testRemoveDeclaration2() { + test("var a,b,c; c = a = b = 1", ""); + } + + public void testRemoveDeclaration3() { + test("var a,b,c; c = a = b = {}; a.x = 1;", ""); + } + + public void testRemoveDeclaration4() { + testSame("var a,b,c; c = a = b = {}; a.x = 1;alert(c.x);"); + } + + public void testRemoveDeclaration5() { + test("var a,b,c; c = a = b = null; use(b)", "var b;b=null;use(b)"); + } + + public void testRemoveDeclaration6() { + test("var a,b,c; c = a = b = 'str';use(b)", "var b;b='str';use(b)"); + } + + public void testRemoveDeclaration7() { + test("var a,b,c; c = a = b = true;use(b)", "var b;b=true;use(b)"); + } + + public void testRemoveFunction1() { + test("var foo = function(){};", ""); + } + + public void testRemoveFunction2() { + test("var foo; foo = function(){};", ""); + } + + public void testRemoveFunction3() { + test("var foo = {}; foo.bar = function() {};", ""); + } + + public void testRemoveFunction4() { + test("var a = {}; a.b = {}; a.b.c = function() {};", ""); + } + + public void testReferredToByWindow() { + testSame("var foo = {}; foo.bar = function() {}; window['fooz'] = foo.bar"); + } + + public void testExtern() { + testSame("externfoo = 5"); + } + + public void testRemoveNamedFunction() { + test("function foo(){}", ""); + } + + public void testRemoveRecursiveFunction1() { + test("function f(){f()}", ""); + } + + public void testRemoveRecursiveFunction2() { + test("var f = function (){f()}", ""); + } + + public void testRemoveRecursiveFunction2a() { + test("var f = function g(){g()}", ""); + } + + public void testRemoveRecursiveFunction3() { + test("var f;f = function (){f()}", ""); + } + + public void testRemoveRecursiveFunction4() { + // don't removed if name definition doesn't exist. + testSame("f = function (){f()}"); + } + + public void testRemoveRecursiveFunction5() { + test("function g(){f()}function f(){g()}", ""); + } + + public void testRemoveRecursiveFunction6() { + test("var f=function(){g()};function g(){f()}", ""); + } + + public void testRemoveRecursiveFunction7() { + test("var g = function(){f()};var f = function(){g()}", ""); + } + + public void testRemoveRecursiveFunction8() { + test("var o = {};o.f = function(){o.f()}", ""); + } + + public void testRemoveRecursiveFunction9() { + testSame("var o = {};o.f = function(){o.f()};o.f()"); + } + + public void testSideEffectClassification1() { + test("foo();", "foo();"); + } + + public void testSideEffectClassification2() { + test("var a = foo();", "foo();"); + } + + public void testSideEffectClassification3() { + testSame("var a = foo();window['b']=a;"); + } + + public void testSideEffectClassification4() { + testSame("function sef(){} sef();"); + } + + public void testSideEffectClassification5() { + testSame("function nsef(){} var a = nsef();window['b']=a;"); + } + + public void testSideEffectClassification6() { + test("function sef(){} sef();", "function sef(){} sef();"); + } + + public void testSideEffectClassification7() { + testSame("function sef(){} var a = sef();window['b']=a;"); + } + + public void testNoSideEffectAnnotation1() { + test("function f(){} var a = f();", + "function f(){} f()"); + } + + public void testNoSideEffectAnnotation2() { + test("/**@nosideeffects*/function f(){}", "var a = f();", + "", null, null); + } + + public void testNoSideEffectAnnotation3() { + test("var f = function(){}; var a = f();", + "var f = function(){}; f();"); + } + + public void testNoSideEffectAnnotation4() { + test("var f = /**@nosideeffects*/function(){};", "var a = f();", + "", null, null); + } + + public void testNoSideEffectAnnotation5() { + test("var f; f = function(){}; var a = f();", + "var f; f = function(){}; f();"); + } + + public void testNoSideEffectAnnotation6() { + test("var f; f = /**@nosideeffects*/function(){};", "var a = f();", + "", null, null); + } + + public void testNoSideEffectAnnotation7() { + test("var f;" + + "f = /**@nosideeffects*/function(){};", + "f = function(){};" + + "var a = f();", + "f = function(){}; f();", null, null); + } + + public void testNoSideEffectAnnotation8() { + test("var f;" + + "f = function(){};" + + "f = /**@nosideeffects*/function(){};", + "var a = f();", + "f();", null, null); + } + + public void testNoSideEffectAnnotation9() { + test("var f;" + + "f = /**@nosideeffects*/function(){};" + + "f = /**@nosideeffects*/function(){};", + "var a = f();", + "", null, null); + + test("var f; f = /**@nosideeffects*/function(){};", "var a = f();", + "", null, null); + } + + public void testNoSideEffectAnnotation10() { + test("var o = {}; o.f = function(){}; var a = o.f();", + "var o = {}; o.f = function(){}; o.f();"); + } + + public void testNoSideEffectAnnotation11() { + test("var o = {}; o.f = /**@nosideeffects*/function(){};", + "var a = o.f();", "", null, null); + } + + public void testNoSideEffectAnnotation12() { + test("function c(){} var a = new c", + "function c(){} new c"); + } + + public void testNoSideEffectAnnotation13() { + test("/**@nosideeffects*/function c(){}", "var a = new c", + "", null, null); + } + + public void testNoSideEffectAnnotation14() { + String common = "function c(){};" + + "c.prototype.f = /**@nosideeffects*/function(){};"; + test(common, "var o = new c; var a = o.f()", "new c", null, null); + } + + public void testNoSideEffectAnnotation15() { + test("function c(){}; c.prototype.f = function(){}; var a = (new c).f()", + "function c(){}; c.prototype.f = function(){}; (new c).f()"); + } + + public void testNoSideEffectAnnotation16() { + test("/**@nosideeffects*/function c(){}" + + "c.prototype.f = /**@nosideeffects*/function(){};", + "var a = (new c).f()", + "", + null, null); + } + + public void testFunctionPrototype() { + testSame("var a = 5; Function.prototype.foo = function() {return a;}"); + } + + public void testTopLevelClass1() { + test("var Point = function() {}; Point.prototype.foo = function() {}", ""); + } + + public void testTopLevelClass2() { + testSame("var Point = {}; Point.prototype.foo = function() {};" + + "externfoo = new Point()"); + } + + public void testTopLevelClass3() { + test("function Point() {this.me_ = Point}", ""); + } + + public void testTopLevelClass4() { + test("function f(){} function A(){} A.prototype = {x: function() {}}; f();", + "function f(){} f();"); + } + + public void testTopLevelClass5() { + testSame("function f(){} function A(){}" + + "A.prototype = {x: function() { f(); }}; new A();"); + } + + public void testTopLevelClass6() { + testSame("function f(){} function A(){}" + + "A.prototype = {x: function() { f(); }}; new A().x();"); + } + + public void testTopLevelClass7() { + test("A.prototype.foo = function(){}; function A() {}", ""); + } + + public void testNamespacedClass1() { + test("var foo = {};foo.bar = {};foo.bar.prototype.baz = {}", ""); + } + + public void testNamespacedClass2() { + testSame("var foo = {};foo.bar = {};foo.bar.prototype.baz = {};" + + "window.z = new foo.bar()"); + } + + public void testNamespacedClass3() { + test("var a = {}; a.b = function() {}; a.b.prototype = {x: function() {}};", + ""); + } + + public void testNamespacedClass4() { + testSame("function f(){} var a = {}; a.b = function() {};" + + "a.b.prototype = {x: function() { f(); }}; new a.b();"); + } + + public void testNamespacedClass5() { + testSame("function f(){} var a = {}; a.b = function() {};" + + "a.b.prototype = {x: function() { f(); }}; new a.b().x();"); + } + + public void testAssignmentToThisPrototype() { + testSame("Function.prototype.inherits = function(parentCtor) {" + + " function tempCtor() {};" + + " tempCtor.prototype = parentCtor.prototype;" + + " this.superClass_ = parentCtor.prototype;" + + " this.prototype = new tempCtor();" + + " this.prototype.constructor = this;" + + "};"); + } + + public void testAssignmentToCallResultPrototype() { + testSame("function f() { return function(){}; } f().prototype = {};"); + } + + public void testAssignmentToExternPrototype() { + testSame("externfoo.prototype = {};"); + } + + public void testAssignmentToUnknownPrototype() { + testSame( + "/** @suppress {duplicate} */ var window;" + + "window['a'].prototype = {};"); + } + + public void testBug2099540() { + testSame( + "/** @suppress {duplicate} */ var document;\n" + + "/** @suppress {duplicate} */ var window;\n" + + "var klass;\n" + + "window[klass].prototype = " + + "document.createElement(tagName)['__proto__'];"); + } + + public void testOtherGlobal() { + testSame("goog.global.foo = bar(); function bar(){}"); + } + + public void testExternName1() { + testSame("top.z = bar(); function bar(){}"); + } + + public void testExternName2() { + testSame("top['z'] = bar(); function bar(){}"); + } + + public void testInherits1() { + test("var a = {}; var b = {}; b.inherits(a)", ""); + } + + public void testInherits2() { + test("var a = {}; var b = {}; var goog = {}; goog.inherits(b, a)", ""); + } + + public void testInherits3() { + testSame("var a = {}; this.b = {}; b.inherits(a);"); + } + + public void testInherits4() { + testSame("var a = {}; this.b = {}; var goog = {}; goog.inherits(b, a);"); + } + + public void testInherits5() { + test("this.a = {}; var b = {}; b.inherits(a);", + "this.a = {}"); + } + + public void testInherits6() { + test("this.a = {}; var b = {}; var goog = {}; goog.inherits(b, a);", + "this.a = {}"); + } + + public void testInherits7() { + testSame("var a = {}; this.b = {}; var goog = {};" + + " goog.inherits = function() {}; goog.inherits(b, a);"); + } + + public void testInherits8() { + // Make sure that exceptions aren't thrown if inherits() is used as + // an R-value + test("this.a = {}; var b = {}; var c = b.inherits(a);", "this.a = {};"); + } + + public void testMixin1() { + testSame("var goog = {}; goog.mixin = function() {};" + + "Function.prototype.mixin = function(base) {" + + " goog.mixin(this.prototype, base); " + + "};"); + } + + public void testMixin2() { + testSame("var a = {}; this.b = {}; var goog = {};" + + " goog.mixin = function() {}; goog.mixin(b.prototype, a.prototype);"); + } + + public void testMixin3() { + test("this.a = {}; var b = {}; var goog = {};" + + " goog.mixin = function() {}; goog.mixin(b.prototype, a.prototype);", + "this.a = {};"); + } + + public void testMixin4() { + testSame("this.a = {}; var b = {}; var goog = {};" + + "goog.mixin = function() {};" + + "goog.mixin(b.prototype, a.prototype);" + + "new b()"); + } + + public void testMixin5() { + test("this.a = {}; var b = {}; var c = {}; var goog = {};" + + "goog.mixin = function() {};" + + "goog.mixin(b.prototype, a.prototype);" + + "goog.mixin(c.prototype, a.prototype);" + + "new b()", + "this.a = {}; var b = {}; var goog = {};" + + "goog.mixin = function() {};" + + "goog.mixin(b.prototype, a.prototype);" + + "new b()"); + } + + public void testMixin6() { + testSame("this.a = {}; var b = {}; var c = {}; var goog = {};" + + "goog.mixin = function() {};" + + "goog.mixin(c.prototype, a.prototype) + " + + "goog.mixin(b.prototype, a.prototype);" + + "new b()"); + } + + public void testMixin7() { + test("this.a = {}; var b = {}; var c = {}; var goog = {};" + + "goog.mixin = function() {};" + + "var d = goog.mixin(c.prototype, a.prototype) + " + + "goog.mixin(b.prototype, a.prototype);" + + "new b()", + "this.a = {}; var b = {}; var goog = {};" + + "goog.mixin = function() {};" + + "goog.mixin(b.prototype, a.prototype);" + + "new b()"); + } + + public void testConstants1() { + testSame("var bar = function(){}; var EXP_FOO = true; if (EXP_FOO) bar();"); + } + + public void testConstants2() { + test("var bar = function(){}; var EXP_FOO = true; var EXP_BAR = true;" + + "if (EXP_FOO) bar();", + "var bar = function(){}; var EXP_FOO = true; if (EXP_FOO) bar();"); + } + + public void testExpressions1() { + test("var foo={}; foo.A='A'; foo.AB=foo.A+'B'; foo.ABC=foo.AB+'C'", + ""); + } + + public void testExpressions2() { + testSame("var foo={}; foo.A='A'; foo.AB=foo.A+'B'; this.ABC=foo.AB+'C'"); + } + + public void testExpressions3() { + testSame("var foo = 2; window.bar(foo + 3)"); + } + + public void testSetCreatingReference() { + testSame("var foo; var bar = function(){foo=6;}; bar();"); + } + + public void testAnonymous1() { + testSame("function foo() {}; function bar() {}; foo(function() {bar()})"); + } + + public void testAnonymous2() { + test("var foo;(function(){foo=6;})()", "(function(){})()"); + } + + public void testAnonymous3() { + testSame("var foo; (function(){ if(!foo)foo=6; })()"); + } + + public void testAnonymous4() { + testSame("var foo; (function(){ foo=6; })(); externfoo=foo;"); + } + + public void testAnonymous5() { + testSame("var foo;" + + "(function(){ foo=function(){ bar() }; function bar(){} })();" + + "foo();"); + } + + public void testAnonymous6() { + testSame("function foo(){}" + + "function bar(){}" + + "foo(function(){externfoo = bar});"); + } + + public void testAnonymous7() { + testSame("var foo;" + + "(function (){ function bar(){ externfoo = foo; } bar(); })();"); + } + + public void testAnonymous8() { + testSame("var foo;" + + "(function (){ var g=function(){ externfoo = foo; }; g(); })();"); + } + + public void testAnonymous9() { + testSame("function foo(){}" + + "function bar(){}" + + "foo(function(){ function baz(){ externfoo = bar; } baz(); });"); + } + + public void testFunctions1() { + testSame("var foo = null; function baz() {}" + + "function bar() {foo=baz();} bar();"); + } + + public void testFunctions2() { + testSame("var foo; foo = function() {var a = bar()};" + + "var bar = function(){}; foo();"); + } + + public void testGetElem1() { + testSame("var foo = {}; foo.bar = {}; foo.bar.baz = {a: 5, b: 10};" + + "var fn = function() {window[foo.bar.baz.a] = 5;}; fn()"); + } + + public void testGetElem2() { + testSame("var foo = {}; foo.bar = {}; foo.bar.baz = {a: 5, b: 10};" + + "var fn = function() {this[foo.bar.baz.a] = 5;}; fn()"); + } + + public void testGetElem3() { + testSame("var foo = {'i': 0, 'j': 1}; foo['k'] = 2; top.foo = foo;"); + } + + public void testIf1() { + test("var foo = {};if(e)foo.bar=function(){};", "if(e);"); + } + + public void testIf2() { + test("var e = false;var foo = {};if(e)foo.bar=function(){};", + "var e = false;if(e);"); + } + + public void testIf3() { + test("var e = false;var foo = {};if(e + 1)foo.bar=function(){};", + "var e = false;if(e + 1);"); + } + + public void testIf4() { + test("var e = false, f;var foo = {};if(f=e)foo.bar=function(){};", + "var e = false;if(e);"); + } + + public void testIf4a() { + // TODO(johnlenz): fix this. + testSame("var e = [], f;if(f=e);f[0] = 1;"); + } + + public void testIf4b() { + // TODO(johnlenz): fix this. + test("var e = [], f;if(e=f);f[0] = 1;", + "var f;if(f);f[0] = 1;"); + } + + public void testIf4c() { + test("var e = [], f;if(f=e);e[0] = 1;", + "var e = [];if(e);e[0] = 1;"); + } + + public void testIf5() { + test("var e = false, f;var foo = {};if(f = e + 1)foo.bar=function(){};", + "var e = false;if(e + 1);"); + } + + public void testIfElse() { + test("var foo = {};if(e)foo.bar=function(){};else foo.bar=function(){};", + "if(e);else;"); + } + + public void testWhile() { + test("var foo = {};while(e)foo.bar=function(){};", "while(e);"); + } + + public void testFor() { + test("var foo = {};for(e in x)foo.bar=function(){};", "for(e in x);"); + } + + public void testDo() { + test("var cond = false;do {var a = 1} while (cond)", + "var cond = false;do {} while (cond)"); + } + + public void testSetterInForStruct1() { + test("var j = 0; for (var i = 1; i = 0; j++);", + "var j = 0; for (; 0; j++);"); + } + + public void testSetterInForStruct2() { + test("var Class = function() {}; " + + "for (var i = 1; Class.prototype.property_ = 0; i++);", + "for (var i = 1; 0; i++);"); + } + + public void testSetterInForStruct3() { + test("var j = 0; for (var i = 1 + f() + g() + h(); i = 0; j++);", + "var j = 0; f(); g(); h(); for (; 0; j++);"); + } + + public void testSetterInForStruct4() { + test("var i = 0;var j = 0; for (i = 1 + f() + g() + h(); i = 0; j++);", + "var j = 0; f(); g(); h(); for (; 0; j++);"); + } + + public void testSetterInForStruct5() { + test("var i = 0, j = 0; for (i = f(), j = g(); 0;);", + "for (f(), g(); 0;);"); + } + + public void testSetterInForStruct6() { + test("var i = 0, j = 0, k = 0; for (i = f(), j = g(), k = h(); i = 0;);", + "for (f(), g(), h(); 0;);"); + } + + public void testSetterInForStruct7() { + test("var i = 0, j = 0, k = 0; for (i = 1, j = 2, k = 3; i = 0;);", + "for (1, 2, 3; 0;);"); + } + + public void testSetterInForStruct8() { + test("var i = 0, j = 0, k = 0; for (i = 1, j = i, k = 2; i = 0;);", + "var i = 0; for(i = 1, i , 2; i = 0;);"); + } + + public void testSetterInForStruct9() { + test("var Class = function() {}; " + + "for (var i = 1; Class.property_ = 0; i++);", + "for (var i = 1; 0; i++);"); + } + + public void testSetterInForStruct10() { + test("var Class = function() {}; " + + "for (var i = 1; Class.property_ = 0; i = 2);", + "for (; 0;);"); + } + + public void testSetterInForStruct11() { + test("var Class = function() {}; " + + "for (;Class.property_ = 0;);", + "for (;0;);"); + } + + public void testSetterInForStruct12() { + test("var a = 1; var Class = function() {}; " + + "for (;Class.property_ = a;);", + "var a = 1; for (; a;);"); + } + + public void testSetterInForStruct13() { + test("var a = 1; var Class = function() {}; " + + "for (Class.property_ = a; 0 ;);", + "for (; 0;);"); + } + + public void testSetterInForStruct14() { + test("var a = 1; var Class = function() {}; " + + "for (; 0; Class.property_ = a);", + "for (; 0;);"); + } + + public void testSetterInForStruct15() { + test("var Class = function() {}; " + + "for (var i = 1; 0; Class.prototype.property_ = 0);", + "for (; 0; 0);"); + } + + public void testSetterInForStruct16() { + test("var Class = function() {}; " + + "for (var i = 1; i = 0; Class.prototype.property_ = 0);", + "for (; 0; 0);"); + } + + public void testSetterInForIn1() { + test("var foo = {}; var bar; for(e in bar = foo.a);", + "var foo = {}; for(e in foo.a);"); + } + + public void testSetterInForIn2() { + testSame("var foo = {}; var bar; for(e in bar = foo.a); bar"); + } + + public void testSetterInForIn3() { + testSame("var foo = {}; var bar; for(e in bar = foo.a); bar.b = 3"); + } + + public void testSetterInForIn4() { + testSame("var foo = {}; var bar; for (e in bar = foo.a); bar.b = 3; foo.a"); + } + + public void testSetterInForIn5() { + // TODO(user) Fix issue similar to b/2316773: bar should be preserved + // but isn't due to missing references between e and foo.a + test("var foo = {}; var bar; for (e in foo.a) { bar = e } bar.b = 3; foo.a", + "var foo={};for(e in foo.a);foo.a"); + } + + public void testSetterInForIn6() { + testSame("var foo = {};for(e in foo);"); + } + + public void testSetterInIfPredicate() { + // TODO(user) Make NameAnalyzer smarter so it can remove "Class". + testSame("var a = 1;" + + "var Class = function() {}; " + + "if (Class.property_ = a);"); + } + + public void testSetterInWhilePredicate() { + test("var a = 1;" + + "var Class = function() {}; " + + "while (Class.property_ = a);", + "var a = 1; for (;a;) {}"); + } + + public void testSetterInDoWhilePredicate() { + // TODO(user) Make NameAnalyzer smarter so it can remove "Class". + testSame("var a = 1;" + + "var Class = function() {}; " + + "do {} while(Class.property_ = a);"); + } + + public void testSetterInSwitchInput() { + // TODO(user) Make NameAnalyzer smarter so it can remove "Class". + testSame("var a = 1;" + + "var Class = function() {}; " + + "switch (Class.property_ = a) {" + + " default:" + + "}"); + } + + public void testComplexAssigns() { + // Complex assigns are not removed by the current pass. + testSame("var x = 0; x += 3; x *= 5;"); + } + + public void testNestedAssigns1() { + test("var x = 0; var y = x = 3; window.alert(y);", + "var y = 3; window.alert(y);"); + } + + public void testNestedAssigns2() { + testSame("var x = 0; var y = x = {}; x.b = 3; window.alert(y);"); + } + + public void testComplexNestedAssigns1() { + // TODO(nicksantos): Make NameAnalyzer smarter, so that we can eliminate y. + testSame("var x = 0; var y = 2; y += x = 3; window.alert(x);"); + } + + public void testComplexNestedAssigns2() { + test("var x = 0; var y = 2; y += x = 3; window.alert(y);", + "var y = 2; y += 3; window.alert(y);"); + } + + public void testComplexNestedAssigns3() { + test("var x = 0; var y = x += 3; window.alert(x);", + "var x = 0; x += 3; window.alert(x);"); + } + + public void testComplexNestedAssigns4() { + testSame("var x = 0; var y = x += 3; window.alert(y);"); + } + + public void testUnintendedUseOfInheritsInLocalScope1() { + testSame("goog.mixin = function() {}; " + + "(function() { var x = {}; var y = {}; goog.mixin(x, y); })();"); + } + + public void testUnintendedUseOfInheritsInLocalScope2() { + testSame("goog.mixin = function() {}; " + + "var x = {}; var y = {}; (function() { goog.mixin(x, y); })();"); + } + + public void testUnintendedUseOfInheritsInLocalScope3() { + testSame("goog.mixin = function() {}; " + + "var x = {}; var y = {}; (function() { goog.mixin(x, y); })(); " + + "window.alert(x);"); + } + + public void testUnintendedUseOfInheritsInLocalScope4() { + // Ensures that the "goog$mixin" variable doesn't get stripped out, + // even when it's only used in a local scope. + testSame("var goog$mixin = function() {}; " + + "(function() { var x = {}; var y = {}; goog$mixin(x, y); })();"); + } + + public void testPrototypePropertySetInLocalScope1() { + testSame("(function() { var x = function(){}; x.prototype.bar = 3; })();"); + } + + public void testPrototypePropertySetInLocalScope2() { + testSame("var x = function(){}; (function() { x.prototype.bar = 3; })();"); + } + + public void testPrototypePropertySetInLocalScope3() { + test("var x = function(){ x.prototype.bar = 3; };", ""); + } + + public void testPrototypePropertySetInLocalScope4() { + test("var x = {}; x.foo = function(){ x.foo.prototype.bar = 3; };", ""); + } + + public void testPrototypePropertySetInLocalScope5() { + test("var x = {}; x.prototype.foo = 3;", ""); + } + + public void testPrototypePropertySetInLocalScope6() { + testSame("var x = {}; x.prototype.foo = 3; bar(x.prototype.foo)"); + } + + public void testPrototypePropertySetInLocalScope7() { + testSame("var x = {}; x.foo = 3; bar(x.foo)"); + } + + public void testRValueReference1() { + testSame("var a = 1; a"); + } + + public void testRValueReference2() { + testSame("var a = 1; 1+a"); + } + + public void testRValueReference3() { + testSame("var x = {}; x.prototype.foo = 3; var a = x.prototype.foo; 1+a"); + } + + public void testRValueReference4() { + testSame("var x = {}; x.prototype.foo = 3; x.prototype.foo"); + } + + public void testRValueReference5() { + testSame("var x = {}; x.prototype.foo = 3; 1+x.prototype.foo"); + } + + public void testRValueReference6() { + testSame("var x = {}; var idx = 2; x[idx]"); + } + + public void testUnhandledTopNode() { + testSame("function Foo() {}; Foo.prototype.isBar = function() {};" + + "function Bar() {}; Bar.prototype.isFoo = function() {};" + + "var foo = new Foo(); var bar = new Bar();" + + // The boolean AND here is currently unhandled by this pass, but + // it should not cause it to blow up. + "var cond = foo.isBar() && bar.isFoo();" + + "if (cond) {window.alert('hello');}"); + } + + public void testPropertyDefinedInGlobalScope() { + testSame("function Foo() {}; var x = new Foo(); x.cssClass = 'bar';" + + "window.alert(x);"); + } + + public void testConditionallyDefinedFunction1() { + testSame("var g; externfoo.x || (externfoo.x = function() { g; })"); + } + + public void testConditionallyDefinedFunction2() { + testSame("var g; 1 || (externfoo.x = function() { g; })"); + } + + public void testConditionallyDefinedFunction3() { + testSame("var a = {};" + + "rand() % 2 || (a.f = function() { externfoo = 1; } || alert());"); + } + + public void testGetElemOnThis() { + testSame("var a = 3; this['foo'] = a;"); + testSame("this['foo'] = 3;"); + } + + public void testRemoveInstanceOfOnly() { + test("function Foo() {}; Foo.prototype.isBar = function() {};" + + "var x; if (x instanceof Foo) { window.alert(x); }", + ";var x; if (false) { window.alert(x); }"); + } + + public void testRemoveLocalScopedInstanceOfOnly() { + test("function Foo() {}; function Bar(x) { this.z = x instanceof Foo; };" + + "externfoo.x = new Bar({});", + ";function Bar(x) { this.z = false }; externfoo.x = new Bar({});"); + } + + public void testRemoveInstanceOfWithReferencedMethod() { + test("function Foo() {}; Foo.prototype.isBar = function() {};" + + "var x; if (x instanceof Foo) { window.alert(x.isBar()); }", + ";var x; if (false) { window.alert(x.isBar()); }"); + } + + public void testDoNotChangeReferencedInstanceOf() { + testSame("function Foo() {}; Foo.prototype.isBar = function() {};" + + "var x = new Foo(); if (x instanceof Foo) { window.alert(x); }"); + } + + public void testDoNotChangeReferencedLocalScopedInstanceOf() { + testSame("function Foo() {}; externfoo.x = new Foo();" + + "function Bar() { if (x instanceof Foo) { window.alert(x); } };" + + "externfoo.y = new Bar();"); + } + + public void testDoNotChangeLocalScopeReferencedInstanceOf() { + testSame("function Foo() {}; Foo.prototype.isBar = function() {};" + + "function Bar() { this.z = new Foo(); }; externfoo.x = new Bar();" + + "if (x instanceof Foo) { window.alert(x); }"); + } + + public void testDoNotChangeLocalScopeReferencedLocalScopedInstanceOf() { + testSame("function Foo() {}; Foo.prototype.isBar = function() {};" + + "function Bar() { this.z = new Foo(); };" + + "Bar.prototype.func = function(x) {" + + "if (x instanceof Foo) { window.alert(x); }" + + "}; new Bar().func();"); + } + + public void testDoNotChangeLocalScopeReferencedLocalScopedInstanceOf2() { + test( + "function Foo() {}" + + "var createAxis = function(f) { return window.passThru(f); };" + + "var axis = createAxis(function(test) {" + + " return test instanceof Foo;" + + "});", + "var createAxis = function(f) { return window.passThru(f); };" + + "createAxis(function(test) {" + + " return false;" + + "});"); + } + + public void testDoNotChangeInstanceOfGetElem() { + testSame("var goog = {};" + + "function f(obj, name) {" + + " if (obj instanceof goog[name]) {" + + " return name;" + + " }" + + "}" + + "window['f'] = f;"); + } + + public void testWeirdnessOnLeftSideOfPrototype() { + // This checks a bug where 'x' was removed, but the function referencing + // it was not, causing problems. + testSame("var x = 3; " + + "(function() { this.bar = 3; }).z = function() {" + + " return x;" + + "};"); + } + + public void testShortCircuit1() { + test("var a = b() || 1", "b()"); + } + + public void testShortCircuit2() { + test("var a = 1 || c()", "1 || c()"); + } + + public void testShortCircuit3() { + test("var a = b() || c()", "b() || c()"); + } + + public void testShortCircuit4() { + test("var a = b() || 3 || c()", "b() || 3 || c()"); + } + + public void testShortCircuit5() { + test("var a = b() && 1", "b()"); + } + + public void testShortCircuit6() { + test("var a = 1 && c()", "1 && c()"); + } + + public void testShortCircuit7() { + test("var a = b() && c()", "b() && c()"); + } + + public void testShortCircuit8() { + test("var a = b() && 3 && c()", "b() && 3 && c()"); + } + + public void testRhsReference1() { + testSame("var a = 1; a"); + } + + public void testRhsReference2() { + testSame("var a = 1; a || b()"); + } + + public void testRhsReference3() { + testSame("var a = 1; 1 || a"); + } + + public void testRhsReference4() { + test("var a = 1; var b = a || foo()", "var a = 1; a || foo()"); + } + + public void testRhsReference5() { + test("var a = 1, b = 5; a; foo(b)", "var a = 1, b = 5; a; foo(b)"); + } + + public void testRhsAssign1() { + test("var foo, bar; foo || (bar = 1)", + "var foo; foo || 1"); + } + + public void testRhsAssign2() { + test("var foo, bar, baz; foo || (baz = bar = 1)", + "var foo; foo || 1"); + } + + public void testRhsAssign3() { + testSame("var foo = null; foo || (foo = 1)"); + } + + public void testRhsAssign4() { + test("var foo = null; foo = (foo || 1)", ""); + } + + public void testRhsAssign5() { + test("var a = 3, foo, bar; foo || (bar = a)", "var a = 3, foo; foo || a"); + } + + public void testRhsAssign6() { + test("function Foo(){} var foo = null;" + + "var f = function () {foo || (foo = new Foo()); return foo}", + ""); + } + + public void testRhsAssign7() { + testSame("function Foo(){} var foo = null;" + + "var f = function () {foo || (foo = new Foo())}; f()"); + } + + public void testRhsAssign8() { + testSame("function Foo(){} var foo = null;" + + "var f = function () {(foo = new Foo()) || g()}; f()"); + } + + public void testRhsAssign9() { + test("function Foo(){} var foo = null;" + + "var f = function () {1 + (foo = new Foo()); return foo}", + ""); + } + + public void testAssignWithOr1() { + testSame("var foo = null;" + + "var f = window.a || function () {return foo}; f()"); + } + + public void testAssignWithOr2() { + test("var foo = null;" + + "var f = window.a || function () {return foo};", + "var foo = null"); // why is this left? + } + + public void testAssignWithAnd1() { + testSame("var foo = null;" + + "var f = window.a && function () {return foo}; f()"); + } + + public void testAssignWithAnd2() { + test("var foo = null;" + + "var f = window.a && function () {return foo};", + "var foo = null;"); // why is this left? + } + + public void testAssignWithHook1() { + testSame("function Foo(){} var foo = null;" + + "var f = window.a ? " + + " function () {return new Foo()} : function () {return foo}; f()"); + } + + public void testAssignWithHook2() { + test("function Foo(){} var foo = null;" + + "var f = window.a ? " + + " function () {return new Foo()} : function () {return foo};", + ""); + } + + public void testAssignWithHook2a() { + test("function Foo(){} var foo = null;" + + "var f; f = window.a ? " + + " function () {return new Foo()} : function () {return foo};", + ""); + } + + public void testAssignWithHook3() { + testSame("function Foo(){} var foo = null; var f = {};" + + "f.b = window.a ? " + + " function () {return new Foo()} : function () {return foo}; f.b()"); + } + + public void testAssignWithHook4() { + test("function Foo(){} var foo = null; var f = {};" + + "f.b = window.a ? " + + " function () {return new Foo()} : function () {return foo};", + ""); + } + + public void testAssignWithHook5() { + testSame("function Foo(){} var foo = null; var f = {};" + + "f.b = window.a ? function () {return new Foo()} :" + + " window.b ? function () {return foo} :" + + " function() { return Foo }; f.b()"); + } + + public void testAssignWithHook6() { + test("function Foo(){} var foo = null; var f = {};" + + "f.b = window.a ? function () {return new Foo()} :" + + " window.b ? function () {return foo} :" + + " function() { return Foo };", + ""); + } + + public void testAssignWithHook7() { + testSame("function Foo(){} var foo = null;" + + "var f = window.a ? new Foo() : foo;" + + "f()"); + } + + public void testAssignWithHook8() { + test("function Foo(){} var foo = null;" + + "var f = window.a ? new Foo() : foo;", + "function Foo(){}" + + "window.a && new Foo()"); + } + + public void testAssignWithHook9() { + test("function Foo(){} var foo = null; var f = {};" + + "f.b = window.a ? new Foo() : foo;", + "function Foo(){} window.a && new Foo()"); + } + + public void testAssign1() { + test("function Foo(){} var foo = null; var f = {};" + + "f.b = window.a;", + ""); + } + + public void testAssign2() { + test("function Foo(){} var foo = null; var f = {};" + + "f.b = window;", + ""); + } + + public void testAssign3() { + test("var f = {};" + + "f.b = window;", + ""); + } + + public void testAssign4() { + test("function Foo(){} var foo = null; var f = {};" + + "f.b = new Foo();", + "function Foo(){} new Foo()"); + } + + public void testAssign5() { + test("function Foo(){} var foo = null; var f = {};" + + "f.b = foo;", + ""); + } + + public void testNestedAssign1() { + test("var a, b = a = 1, c = 2", ""); + } + + public void testNestedAssign2() { + test("var a, b = a = 1; foo(b)", + "var b = 1; foo(b)"); + } + + public void testNestedAssign3() { + test("var a, b = a = 1; a = b = 2; foo(b)", + "var b = 1; b = 2; foo(b)"); + } + + public void testNestedAssign4() { + test("var a, b = a = 1; b = a = 2; foo(b)", + "var b = 1; b = 2; foo(b)"); + } + + public void testNestedAssign5() { + test("var a, b = a = 1; b = a = 2", ""); + } + + public void testNestedAssign15() { + test("var a, b, c; c = b = a = 2", ""); + } + + public void testNestedAssign6() { + testSame("var a, b, c; a = b = c = 1; foo(a, b, c)"); + } + + public void testNestedAssign7() { + testSame("var a = 0; a = i[j] = 1; b(a, i[j])"); + } + + public void testNestedAssign8() { + testSame("function f(){" + + "this.lockedToken_ = this.lastToken_ = " + + "SETPROP_value(this.hiddenInput_, a)}f()"); + } + + public void testRefChain1() { + test("var a = 1; var b = a; var c = b; var d = c", ""); + } + + public void testRefChain2() { + test("var a = 1; var b = a; var c = b; var d = c || f()", + "var a = 1; var b = a; var c = b; c || f()"); + } + + public void testRefChain3() { + test("var a = 1; var b = a; var c = b; var d = c + f()", "f()"); + } + + public void testRefChain4() { + test("var a = 1; var b = a; var c = b; var d = f() || c", + "f()"); + } + + public void testRefChain5() { + test("var a = 1; var b = a; var c = b; var d = f() ? g() : c", + "f() && g()"); + } + + public void testRefChain6() { + test("var a = 1; var b = a; var c = b; var d = c ? f() : g()", + "var a = 1; var b = a; var c = b; c ? f() : g()"); + } + + public void testRefChain7() { + test("var a = 1; var b = a; var c = b; var d = (b + f()) ? g() : c", + "var a = 1; var b = a; (b+f()) && g()"); + } + + public void testRefChain8() { + test("var a = 1; var b = a; var c = b; var d = f()[b] ? g() : 0", + "var a = 1; var b = a; f()[b] && g()"); + } + + public void testRefChain9() { + test("var a = 1; var b = a; var c = 5; var d = f()[b+c] ? g() : 0", + "var a = 1; var b = a; var c = 5; f()[b+c] && g()"); + } + + public void testRefChain10() { + test("var a = 1; var b = a; var c = b; var d = f()[b] ? g() : 0", + "var a = 1; var b = a; f()[b] && g()"); + } + + public void testRefChain11() { + test("var a = 1; var b = a; var d = f()[b] ? g() : 0", + "var a = 1; var b = a; f()[b] && g()"); + } + + public void testRefChain12() { + testSame("var a = 1; var b = a; f()[b] ? g() : 0"); + } + + + public void testRefChain13() { + test("function f(){}var a = 1; var b = a; var d = f()[b] ? g() : 0", + "function f(){}var a = 1; var b = a; f()[b] && g()"); + } + + public void testRefChain14() { + testSame("function f(){}var a = 1; var b = a; f()[b] ? g() : 0"); + } + + public void testRefChain15() { + test("function f(){}var a = 1, b = a; var c = f(); var d = c[b] ? g() : 0", + "function f(){}var a = 1, b = a; var c = f(); c[b] && g()"); + } + + public void testRefChain16() { + testSame("function f(){}var a = 1; var b = a; var c = f(); c[b] ? g() : 0"); + } + + public void testRefChain17() { + test("function f(){}var a = 1; var b = a; var c = f(); var d = c[b]", + "function f(){} f()"); + } + + public void testRefChain18() { + testSame("var a = 1; f()[a] && g()"); + } + + + public void testRefChain19() { + test("var a = 1; var b = [a]; var c = b; b[f()] ? g() : 0", + "var a=1; var b=[a]; b[f()] ? g() : 0"); + } + + public void testRefChain20() { + test("var a = 1; var b = [a]; var c = b; var d = b[f()] ? g() : 0", + "var a=1; var b=[a]; b[f()]&&g()"); + } + + public void testRefChain21() { + testSame("var a = 1; var b = 2; var c = a + b; f(c)"); + } + + public void testRefChain22() { + test("var a = 2; var b = a = 4; f(a)", "var a = 2; a = 4; f(a)"); + } + + public void testRefChain23() { + test("var a = {}; var b = a[1] || f()", "var a = {}; a[1] || f()"); + } + + /** + * Expressions that cannot be attributed to any enclosing dependency + * scope should be treated as global references. + * @bug 1739062 + */ + public void testAssignmentWithComplexLhs() { + testSame("function f() { return this; }" + + "var o = {'key': 'val'};" + + "f().x_ = o['key'];"); + } + + public void testAssignmentWithComplexLhs2() { + testSame("function f() { return this; }" + + "var o = {'key': 'val'};" + + "f().foo = function() {" + + " o" + + "};"); + } + + public void testAssignmentWithComplexLhs3() { + String source = + "var o = {'key': 'val'};" + + "function init_() {" + + " this.x = o['key']" + + "}"; + + test(source, ""); + testSame(source + ";init_()"); + } + + public void testAssignmentWithComplexLhs4() { + testSame("function f() { return this; }" + + "var o = {'key': 'val'};" + + "f().foo = function() {" + + " this.x = o['key']" + + "};"); + } + + /** + * Do not "prototype" property of variables that are not being + * tracked (because they are local). + * @bug 1809442 + */ + public void testNoRemovePrototypeDefinitionsOutsideGlobalScope1() { + testSame("function f(arg){}" + + "" + + "(function(){" + + " var O = {};" + + " O.prototype = 'foo';" + + " f(O);" + + "})()"); + } + + public void testNoRemovePrototypeDefinitionsOutsideGlobalScope2() { + testSame("function f(arg){}" + + "(function h(){" + + " var L = {};" + + " L.prototype = 'foo';" + + " f(L);" + + "})()"); + } + + public void testNoRemovePrototypeDefinitionsOutsideGlobalScope4() { + testSame("function f(arg){}" + + "function g(){" + + " var N = {};" + + " N.prototype = 'foo';" + + " f(N);" + + "}" + + "g()"); + } + + public void testNoRemovePrototypeDefinitionsOutsideGlobalScope5() { + // function body not removed due to @bug 1898561 + testSame("function g(){ var R = {}; R.prototype = 'foo' } g()"); + } + + public void testRemovePrototypeDefinitionsInGlobalScope1() { + testSame("function f(arg){}" + + "var M = {};" + + "M.prototype = 'foo';" + + "f(M);"); + } + + public void testRemovePrototypeDefinitionsInGlobalScope2() { + test("var Q = {}; Q.prototype = 'foo'", ""); + } + + public void testRemoveLabeledStatment() { + test("LBL: var x = 1;", "LBL: {}"); + } + + public void testRemoveLabeledStatment2() { + test("var x; LBL: x = f() + g()", "LBL: { f() ; g()}"); + } + + public void testRemoveLabeledStatment3() { + test("var x; LBL: x = 1;", "LBL: {}"); + } + + public void testRemoveLabeledStatment4() { + test("var a; LBL: a = f()", "LBL: f()"); + } + + public void testPreservePropertyMutationsToAlias1() { + // Test for issue b/2316773 - property get case + // Since a is referenced, property mutations via a's alias b must + // be preserved. + testSame("var a = {}; var b = a; b.x = 1; a"); + } + + public void testPreservePropertyMutationsToAlias2() { + // Test for issue b/2316773 - property get case, don't keep 'c' + test("var a = {}; var b = a; var c = a; b.x = 1; a", + "var a = {}; var b = a; b.x = 1; a"); + } + + public void testPreservePropertyMutationsToAlias3() { + // Test for issue b/2316773 - property get case, chain + testSame("var a = {}; var b = a; var c = b; c.x = 1; a"); + } + + public void testPreservePropertyMutationsToAlias4() { + // Test for issue b/2316773 - element get case + testSame("var a = {}; var b = a; b['x'] = 1; a"); + } + + public void testPreservePropertyMutationsToAlias5() { + // From issue b/2316773 description + testSame("function testCall(o){}" + + "var DATA = {'prop': 'foo','attr': {}};" + + "var SUBDATA = DATA['attr'];" + + "SUBDATA['subprop'] = 'bar';" + + "testCall(DATA);"); + } + + public void testPreservePropertyMutationsToAlias6() { + // Longer GETELEM chain + testSame("function testCall(o){}" + + "var DATA = {'prop': 'foo','attr': {}};" + + "var SUBDATA = DATA['attr'];" + + "var SUBSUBDATA = SUBDATA['subprop'];" + + "SUBSUBDATA['subsubprop'] = 'bar';" + + "testCall(DATA);"); + } + + public void testPreservePropertyMutationsToAlias7() { + // Make sure that the base class does not depend on the derived class. + test("var a = {}; var b = {}; b.x = 0;" + + "var goog = {}; goog.inherits(b, a); a", + "var a = {}; a"); + } + + public void testPreservePropertyMutationsToAlias8() { + // Make sure that the derived classes don't end up depending on each other. + test("var a = {};" + + "var b = {}; b.x = 0;" + + "var c = {}; c.y = 0;" + + "var goog = {}; goog.inherits(b, a); goog.inherits(c, a); c", + "var a = {}; var c = {}; c.y = 0;" + + "var goog = {}; goog.inherits(c, a); c"); + } + + public void testPreservePropertyMutationsToAlias9() { + testSame("var a = {b: {}};" + + "var c = a.b; c.d = 3;" + + "a.d = 3; a.d;"); + } + + public void testRemoveAlias() { + test("var a = {b: {}};" + + "var c = a.b;" + + "a.d = 3; a.d;", + "var a = {b: {}}; a.d = 3; a.d;"); + } + + public void testSingletonGetter1() { + test("function Foo() {} goog.addSingletonGetter(Foo);", ""); + } + + public void testSingletonGetter2() { + test("function Foo() {} goog$addSingletonGetter(Foo);", ""); + } + + public void testSingletonGetter3() { + // addSingletonGetter adds a getInstance method to a class. + testSame("function Foo() {} goog$addSingletonGetter(Foo);" + + "this.x = Foo.getInstance();"); + } + + + + public void testNoRemoveWindowPropertyAlias1() { + testSame( + "var self_ = window.gbar;\n" + + "self_.qs = function() {};"); + } + + public void testNoRemoveWindowPropertyAlias2() { + testSame( + "var self_ = window;\n" + + "self_.qs = function() {};"); + } + + public void testNoRemoveWindowPropertyAlias3() { + testSame( + "var self_ = window;\n" + + "self_['qs'] = function() {};"); + } + + public void testNoRemoveWindowPropertyAlias4() { + // TODO(johnlenz): fix this. "self_" should remain. + test( + "var self_ = window['gbar'] || {};\n" + + "self_.qs = function() {};", + ""); + } + + public void testNoRemoveWindowPropertyAlias4a() { + // TODO(johnlenz): fix this. "self_" should remain. + test( + "var self_; self_ = window.gbar || {};\n" + + "self_.qs = function() {};", + ""); + } + + public void testNoRemoveWindowPropertyAlias5() { + // TODO(johnlenz): fix this. "self_" should remain. + test( + "var self_ = window || {};\n" + + "self_['qs'] = function() {};", + ""); + } + + public void testNoRemoveWindowPropertyAlias5a() { + // TODO(johnlenz): fix this. + test( + "var self_; self_ = window || {};\n" + + "self_['qs'] = function() {};", + ""); + } + + public void testNoRemoveWindowPropertyAlias6() { + testSame( + "var self_ = (window.gbar = window.gbar || {});\n" + + "self_.qs = function() {};"); + } + + public void testNoRemoveWindowPropertyAlias6a() { + testSame( + "var self_; self_ = (window.gbar = window.gbar || {});\n" + + "self_.qs = function() {};"); + } + + public void testNoRemoveWindowPropertyAlias7() { + testSame( + "var self_ = (window = window || {});\n" + + "self_['qs'] = function() {};"); + } + + public void testNoRemoveWindowPropertyAlias7a() { + testSame( + "var self_; self_ = (window = window || {});\n" + + "self_['qs'] = function() {};"); + } + + public void testNoRemoveAlias0() { + testSame( + "var x = {}; function f() { return x; }; " + + "f().style.display = 'block';" + + "alert(x.style)"); + } + + public void testNoRemoveAlias1() { + testSame( + "var x = {}; function f() { return x; };" + + "var map = f();\n" + + "map.style.display = 'block';" + + "alert(x.style)"); + } + + public void testNoRemoveAlias2() { + testSame( + "var x = {};" + + "var map = (function () { return x; })();\n" + + "map.style = 'block';" + + "alert(x.style)"); + } + + public void testNoRemoveAlias3() { + testSame( + "var x = {}; function f() { return x; };" + + "var map = {}\n" + + "map[1] = f();\n" + + "map[1].style.display = 'block';"); + } + + public void testNoRemoveAliasOfExternal0() { + testSame( + "document.getElementById('foo').style.display = 'block';"); + } + + public void testNoRemoveAliasOfExternal1() { + testSame( + "var map = document.getElementById('foo');\n" + + "map.style.display = 'block';"); + } + + public void testNoRemoveAliasOfExternal2() { + testSame( + "var map = {}\n" + + "map[1] = document.getElementById('foo');\n" + + "map[1].style.display = 'block';"); + } + + public void testNoRemoveThrowReference1() { + testSame( + "var e = {}\n" + + "throw e;"); + } + + public void testNoRemoveThrowReference2() { + testSame( + "function e() {}\n" + + "throw new e();"); + } + + public void testClassDefinedInObjectLit1() { + test( + "var data = {Foo: function() {}};" + + "data.Foo.prototype.toString = function() {};", + ""); + } + + public void testClassDefinedInObjectLit2() { + test( + "var data = {}; data.bar = {Foo: function() {}};" + + "data.bar.Foo.prototype.toString = function() {};", + ""); + } + + public void testClassDefinedInObjectLit3() { + test( + "var data = {bar: {Foo: function() {}}};" + + "data.bar.Foo.prototype.toString = function() {};", + ""); + } + + public void testClassDefinedInObjectLit4() { + test( + "var data = {};" + + "data.baz = {bar: {Foo: function() {}}};" + + "data.baz.bar.Foo.prototype.toString = function() {};", + ""); + } + + public void testVarReferencedInClassDefinedInObjectLit1() { + testSame( + "var ref = 3;" + + "var data = {Foo: function() { this.x = ref; }};" + + "window.Foo = data.Foo;"); + } + + public void testVarReferencedInClassDefinedInObjectLit2() { + testSame( + "var ref = 3;" + + "var data = {Foo: function() { this.x = ref; }," + + " Bar: function() {}};" + + "window.Bar = data.Bar;"); + } + + public void testArrayExt() { + testSame( + "Array.prototype.foo = function() { return 1 };" + + "var y = [];" + + "switch (y.foo()) {" + + "}"); + } + + public void testArrayAliasExt() { + testSame( + "Array$X = Array;" + + "Array$X.prototype.foo = function() { return 1 };" + + "function Array$X() {}" + + "var y = [];" + + "switch (y.foo()) {" + + "}"); + } + + public void testExternalAliasInstanceof1() { + test( + "Array$X = Array;" + + "function Array$X() {}" + + "var y = [];" + + "if (y instanceof Array) {}", + "var y = [];" + + "if (y instanceof Array) {}" + ); + } + + public void testExternalAliasInstanceof2() { + testSame( + "Array$X = Array;" + + "function Array$X() {}" + + "var y = [];" + + "if (y instanceof Array$X) {}"); + } + + public void testExternalAliasInstanceof3() { + testSame( + "var b = Array;" + + "var y = [];" + + "if (y instanceof b) {}"); + } + + public void testAliasInstanceof4() { + testSame( + "function Foo() {};" + + "var b = Foo;" + + "var y = new Foo();" + + "if (y instanceof b) {}"); + } + + public void testAliasInstanceof5() { + // TODO(johnlenz): fix this. "b" should remain. + test( + "function Foo() {}" + + "function Bar() {}" + + "var b = x ? Foo : Bar;" + + "var y = new Foo();" + + "if (y instanceof b) {}", + "function Foo() {}" + + "var y = new Foo;" + + "if (false){}"); + } + + // We cannot leave x.a.prototype there because it will + // fail sanity var check. + public void testBrokenNamespaceWithPrototypeAssignment() { + test("var x = {}; x.a.prototype = 1", ""); + } + + public void testRemovePrototypeAliases() { + test( + "function g() {}" + + "function F() {} F.prototype.bar = g;" + + "window.g = g;", + "function g() {}" + + "window.g = g;"); + } + + public void testIssue284() { + test( + "var goog = {};" + + "goog.inherits = function(x, y) {};" + + "var ns = {};" + + "/** @constructor */" + + "ns.PageSelectionModel = function() {};" + + "/** @constructor */" + + "ns.PageSelectionModel.FooEvent = function() {};" + + "/** @constructor */" + + "ns.PageSelectionModel.SelectEvent = function() {};" + + "goog.inherits(ns.PageSelectionModel.ChangeEvent," + + " ns.PageSelectionModel.FooEvent);", + ""); + } + + public void testIssue838a() { + testSame("var z = window['z'] || (window['z'] = {});\n" + + "z['hello'] = 'Hello';\n" + + "z['world'] = 'World';"); + } + + public void testIssue838b() { + testSame( + "var z;" + + "window['z'] = z || (z = {});\n" + + "z['hello'] = 'Hello';\n" + + "z['world'] = 'World';"); + } + + + public void testIssue874a() { + testSame( + "var a = a || {};\n" + + "var b = a;\n" + + "b.View = b.View || {}\n" + + "var c = b.View;\n" + + "c.Editor = function f(d, e) {\n" + + " return d + e\n" + + "};\n" + + "window.ImageEditor.View.Editor = a.View.Editor;"); + } + + public void testIssue874b() { + testSame( + "var b;\n" + + "var c = b = {};\n" + + "c.Editor = function f(d, e) {\n" + + " return d + e\n" + + "};\n" + + "window['Editor'] = b.Editor;"); + } + + public void testIssue874c() { + testSame( + "var b, c;\n" + + "c = b = {};\n" + + "c.Editor = function f(d, e) {\n" + + " return d + e\n" + + "};\n" + + "window['Editor'] = b.Editor;"); + } + + public void testIssue874d() { + testSame( + "var b = {}, c;\n" + + "c = b;\n" + + "c.Editor = function f(d, e) {\n" + + " return d + e\n" + + "};\n" + + "window['Editor'] = b.Editor;"); + } + + public void testIssue874e() { + testSame( + "var a;\n" + + "var b = a || (a = {});\n" + + "var c = b.View || (b.View = {});\n" + + "c.Editor = function f(d, e) {\n" + + " return d + e\n" + + "};\n" + + "window.ImageEditor.View.Editor = a.View.Editor;"); + } + + public void testBug6575051() { + testSame( + "var hackhack = window['__o_o_o__'] = window['__o_o_o__'] || {};\n" + + "window['__o_o_o__']['va'] = 1;\n" + + "hackhack['Vb'] = 1;"); + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new MarkNoSideEffectCallsAndNameAnalyzerRunner(compiler); + } + + private class MarkNoSideEffectCallsAndNameAnalyzerRunner + implements CompilerPass { + MarkNoSideEffectCalls markNoSideEffectCalls; + NameAnalyzer analyzer; + MarkNoSideEffectCallsAndNameAnalyzerRunner(Compiler compiler) { + this.markNoSideEffectCalls = new MarkNoSideEffectCalls(compiler); + this.analyzer = new NameAnalyzer(compiler, true); + } + + @Override + public void process(Node externs, Node root) { + markNoSideEffectCalls.process(externs, root); + analyzer.process(externs, root); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NameAnonymousFunctionsMappedTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NameAnonymousFunctionsMappedTest.java new file mode 100644 index 0000000..893b8bb --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NameAnonymousFunctionsMappedTest.java @@ -0,0 +1,183 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableMap; + +/** + * Test cases for {@link NameAnonymousFunctionsMapped}. + * + */ +public class NameAnonymousFunctionsMappedTest extends CompilerTestCase { + private static final String EXTERNS = "var document;"; + + private NameAnonymousFunctionsMapped pass; + private VariableMap previous; + + public NameAnonymousFunctionsMappedTest() { + super(EXTERNS); + } + + @Override + protected int getNumRepetitions() { + return 1; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + previous = null; + } + + @Override + public CompilerPass getProcessor(Compiler compiler) { + return pass = new NameAnonymousFunctionsMapped(compiler, previous); + } + + private void assertMapping(String... pairs) { + VariableMap functionMap = pass.getFunctionMap(); + assertTrue(pairs.length % 2 == 0); + for (int i = 0; i < pairs.length; i += 2) { + String s = functionMap.lookupSourceName(pairs[i]); + assertEquals(pairs[i + 1], s); + } + assertEquals(pairs.length / 2, + functionMap.getNewNameToOriginalNameMap().size()); + } + + public void testSimpleVarAssignment1() { + test("var a = function() { return 1; }", + "var a = function $() { return 1; }"); + assertMapping("$", "a"); + } + + public void testSimpleVarAssignment2() { + previous = VariableMap.fromMap(ImmutableMap.of( + "a", "previous")); + + test("var a = function() { return 1; }", + "var a = function previous() { return 1; }"); + + assertMapping("previous", "a"); + } + + public void testSimpleVarAssignment3() { + previous = VariableMap.fromMap(ImmutableMap.of( + "unused", "$")); + + test("var fn = function() { return 1; }", + "var fn = function $a() { return 1; }"); + + assertMapping("$a", "fn"); + } + + public void testAssignmentToProperty() { + test("var a = {}; a.b = function() { return 1; }", + "var a = {}; a.b = function $() { return 1; }"); + assertMapping("$", "a.b"); + } + + public void testAssignmentToPrototype() { + test("function a() {} a.prototype.b = function() { return 1; };", + "function a() {} " + + "a.prototype.b = function $() { return 1; };"); + assertMapping("$", "a.prototype.b"); + } + + public void testAssignmentToPrototype2() { + test("var a = {}; " + + "a.b = function() {}; " + + "a.b.prototype.c = function() { return 1; };", + "var a = {}; " + + "a.b = function $() {}; " + + "a.b.prototype.c = function $a() { return 1; };"); + assertMapping("$", "a.b", "$a", "a.b.prototype.c"); + } + + public void testAssignmentToPrototype3() { + test("function a() {} a.prototype['XXX'] = function() { return 1; };", + "function a() {} " + + "a.prototype['XXX'] = function $() { return 1; };"); + assertMapping("$", "a.prototype[\"XXX\"]"); + test("function a() {} a.prototype['\\n'] = function() { return 1; };", + "function a() {} " + + "a.prototype['\\n'] = function $() { return 1; };"); + assertMapping("$", "a.prototype[\"\\n\"]"); + } + + public void testAssignmentToPrototype4() { + test("var Y = 1; function a() {} " + + "a.prototype[Y] = function() { return 1; };", + "var Y = 1; function a() {} " + + "a.prototype[Y] = function $() { return 1; };"); + assertMapping("$", "a.prototype[Y]"); + } + + public void testAssignmentToPrototype5() { + test("function a() {} a['prototype'].b = function() { return 1; };", + "function a() {} " + + "a['prototype'].b = function $() { return 1; };"); + assertMapping("$", "a[\"prototype\"].b"); + } + + + public void testPrototypeInitializer() { + test("function a(){} a.prototype = {b: function() { return 1; }};", + "function a(){} " + + "a.prototype = {b: function $() { return 1; }};"); + assertMapping("$", "a.prototype.b"); + } + + public void testAssignmentToPropertyOfCallReturnValue() { + test("document.getElementById('x').onClick = function() {};", + "document.getElementById('x').onClick = " + + "function $() {};"); + assertMapping("$", "document.getElementById(\"x\").onClick"); + } + + public void testAssignmentToPropertyOfArrayElement() { + test("var a = {}; a.b = [{}]; a.b[0].c = function() {};", + "var a = {}; a.b = [{}]; a.b[0].c = function $() {};"); + assertMapping("$", "a.b[0].c"); + test("var a = {b: {'c': {}}}; a.b['c'].d = function() {};", + "var a = {b: {'c': {}}}; a.b['c'].d = function $() {};"); + assertMapping("$", "a.b[\"c\"].d"); + test("var a = {b: {'c': {}}}; a.b[x()].d = function() {};", + "var a = {b: {'c': {}}}; a.b[x()].d = function $() {};"); + assertMapping("$", "a.b[x()].d"); + } + + public void testAssignmentToGetElem() { + test("function f() { win['x' + this.id] = function(a){}; }", + "function f() { win['x' + this.id] = function $(a){}; }"); + + // TODO - could probably do a better job encoding these + assertMapping("$", "win[\"x\"+this.id]"); + } + + public void testGetElemWithDashes() { + test("var foo = {}; foo['-'] = function() {};", + "var foo = {}; foo['-'] = function $() {};"); + assertMapping("$", "foo[\"-\"]"); + } + + public void testDuplicateNames() { + test("var a = function() { return 1; };a = function() { return 2; }", + "var a = function $() { return 1; };a = function $() { return 2; }"); + assertMapping("$", "a"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NameAnonymousFunctionsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NameAnonymousFunctionsTest.java new file mode 100644 index 0000000..0191bb1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NameAnonymousFunctionsTest.java @@ -0,0 +1,130 @@ +/* + * Copyright 2006 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Unit test for {@link NameAnonymousFunctionsTest}. + * + */ +public class NameAnonymousFunctionsTest extends CompilerTestCase { + + private static final String EXTERNS = "var document;"; + + public NameAnonymousFunctionsTest() { + super(EXTERNS); + } + + @Override public CompilerPass getProcessor(Compiler compiler) { + return new NameAnonymousFunctions(compiler); + } + + public void testSimpleVarAssignment() { + test("var a = function() { return 1; }", + "var a = function $a$() { return 1; }"); + } + + public void testAssignmentToProperty() { + test("var a = {}; a.b = function() { return 1; }", + "var a = {}; a.b = function $a$b$() { return 1; }"); + } + + public void testAssignmentToPrototype() { + test("function a() {} a.prototype.b = function() { return 1; };", + "function a() {} " + + "a.prototype.b = function $a$$b$() { return 1; };"); + } + + public void testAssignmentToPrototype2() { + test("var a = {}; " + + "a.b = function() {}; " + + "a.b.prototype.c = function() { return 1; };", + "var a = {}; " + + "a.b = function $a$b$() {}; " + + "a.b.prototype.c = function $a$b$$c$() { return 1; };"); + } + + public void testAssignmentToPrototype3() { + test("function a() {} a.prototype['b'] = function() { return 1; };", + "function a() {} " + + "a.prototype['b'] = function $a$$b$() { return 1; };"); + } + + public void testAssignmentToPrototype4() { + test("function a() {} a['prototype']['b'] = function() { return 1; };", + "function a() {} " + + "a['prototype']['b'] = function $a$$b$() { return 1; };"); + } + + public void testPrototypeInitializer() { + test("function a(){} a.prototype = {b: function() { return 1; }};", + "function a(){} " + + "a.prototype = {b: function $a$$b$() { return 1; }};"); + } + + public void testMultiplePrototypeInitializer() { + test("function a(){} a.prototype = {b: function() { return 1; }, " + + "c: function() { return 2; }};", + "function a(){} " + + "a.prototype = {b: function $a$$b$() { return 1; }," + + "c: function $a$$c$() { return 2; }};"); + } + + public void testRecursiveObjectLiteral() { + test("function a(){} a.prototype = {b: {c: function() { return 1; }}}", + "function a(){}a.prototype={b:{c:function $a$$b$c$(){return 1}}}"); + } + + public void testAssignmentToPropertyOfCallReturnValue() { + test("document.getElementById('x').onClick = function() {};", + "document.getElementById('x').onClick = " + + "function $document$getElementById$onClick$() {};"); + } + + public void testAssignmentToPropertyOfArrayElement() { + test("var a = {}; a.b = [{}]; a.b[0].c = function() {};", + "var a = {}; a.b = [{}]; a.b[0].c = function $a$b$0$c$() {};"); + test("var a = {b: {'c': {}}}; a.b['c'].d = function() {};", + "var a = {b: {'c': {}}}; a.b['c'].d = function $a$b$c$d$() {};"); + test("var a = {b: {'c': {}}}; a.b[x()].d = function() {};", + "var a = {b: {'c': {}}}; a.b[x()].d = function $a$b$x$d$() {};"); + } + + public void testAssignmentToGetElem() { + test("function f() {win['x' + this.id] = function(a){};}", + "function f() {win['x' + this.id] = function $win$x$this$id$(a){};}"); + } + + public void testGetElemWithDashes() { + test("var foo = {}; foo['-'] = function() {};", + "var foo = {}; foo['-'] = function $foo$__0$() {};"); + } + + public void testWhatCausedIeToFail() { + // If the function was given the name main, for some reason IE failed to + // handle this case properly. That's why we give it the name $main$. FF + // handled the case fine. + test("var main;" + + "(function() {" + + " main = function() {" + + " return 5;" + + " };" + + "})();" + + "" + + "main();", + "var main;(function(){main=function $main$(){return 5}})();main()"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NodeIteratorsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NodeIteratorsTest.java new file mode 100644 index 0000000..48e0f69 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NodeIteratorsTest.java @@ -0,0 +1,180 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.jscomp.NodeIterators.FunctionlessLocalScope; +import com.google.javascript.jscomp.NodeIterators.LocalVarMotion; + +import java.util.Iterator; +import java.util.List; +import junit.framework.TestCase; + +/** + * Tests for NodeIterators. + * @author nicksantos@google.com (Nick Santos) + */ +public class NodeIteratorsTest extends TestCase { + + // In each test, we find the declaration of "X" in the local scope, + // construct a list of all nodes where X is guaranteed to retain its + // original value, and compare those nodes against an expected list of + // tokens. + + public void testBasic() { + testVarMotionWithCode("var X = 3;", Token.VAR, Token.SCRIPT); + } + + public void testNamedFunction() { + testVarMotionWithCode("var X = 3; function f() {}", + Token.VAR, Token.SCRIPT); + } + + public void testNamedFunction2() { + testVarMotionWithCode("var X = 3; function f() {} var Y;", + Token.VAR, Token.NAME, Token.VAR, Token.SCRIPT); + } + + public void testFunctionExpression() { + testVarMotionWithCode("var X = 3, Y = function() {}; 3;", + Token.NAME, Token.VAR, Token.NUMBER, Token.EXPR_RESULT, Token.SCRIPT); + } + + public void testFunctionExpression2() { + testVarMotionWithCode("var X = 3; var Y = function() {}; 3;", + Token.VAR, Token.NAME, Token.VAR, Token.NUMBER, + Token.EXPR_RESULT, Token.SCRIPT); + } + + public void testHaltAtVarRef() { + testVarMotionWithCode("var X, Y = 3; var Z = X;", + Token.NUMBER, Token.NAME, Token.VAR, Token.NAME); + } + + public void testHaltAtVarRef2() { + testVarMotionWithCode("var X, Y = 3; (function() {})(3, X);", + Token.NUMBER, Token.NAME, Token.VAR, Token.NUMBER, Token.NAME); + } + + public void testHaltAtVarRef3() { + testVarMotionWithCode("var X, Y = 3; X;", + Token.NUMBER, Token.NAME, Token.VAR, Token.NAME); + } + + public void testHaltAtSideEffects() { + testVarMotionWithCode("var X, Y = 3; var Z = B(3);", + Token.NUMBER, Token.NAME, Token.VAR, Token.NAME, Token.NUMBER); + } + + public void testHaltAtSideEffects2() { + testVarMotionWithCode("var A = 1, X = A, Y = 3; delete A;", + Token.NUMBER, Token.NAME, Token.VAR, Token.NAME); + } + + public void testHaltAtSideEffects3() { + testVarMotionWithCode("var A = 1, X = A, Y = 3; A++;", + Token.NUMBER, Token.NAME, Token.VAR, Token.NAME); + } + + public void testHaltAtSideEffects4() { + testVarMotionWithCode("var A = 1, X = A, Y = 3; A--;", + Token.NUMBER, Token.NAME, Token.VAR, Token.NAME); + } + + public void testHaltAtSideEffects5() { + testVarMotionWithCode("var A = 1, X = A, Y = 3; A = 'a';", + Token.NUMBER, Token.NAME, Token.VAR, Token.NAME, Token.STRING); + } + + public void testNoHaltReadWhenValueIsImmutable() { + testVarMotionWithCode("var X = 1, Y = 3; alert();", + Token.NUMBER, Token.NAME, Token.VAR, Token.NAME); + } + + public void testHaltReadWhenValueHasSideEffects() { + testVarMotionWithCode("var X = f(), Y = 3; alert();", + Token.NUMBER, Token.NAME, Token.VAR); + } + + public void testCatchBlock() { + testVarMotionWithCode("var X = 1; try { 4; } catch (X) {}", + Token.VAR, Token.NUMBER, Token.EXPR_RESULT, Token.BLOCK); + } + + public void testIfBranch() { + testVarMotionWithCode("var X = foo(); if (X) {}", + Token.VAR, Token.NAME); + } + + /** + * Parses the given code, finds the variable X in the global scope, and runs + * the VarMotion iterator. Asserts that the iteration order matches the + * tokens given. + */ + private void testVarMotionWithCode(String code, int ... expectedTokens) { + List expectedList = Lists.newArrayList(); + for (int token : expectedTokens) { + expectedList.add(token); + } + testVarMotionWithCode(code, expectedList); + } + + /** + * @see #testVarMotionWithCode(String, int ...) + */ + private void testVarMotionWithCode(String code, + List expectedTokens) { + List ancestors = Lists.newArrayList(); + + // Add an empty node to the beginning of the code and start there. + Node root = (new Compiler()).parseTestCode(";" + code); + for (Node n = root; n != null; n = n.getFirstChild()) { + ancestors.add(0, n); + } + + FunctionlessLocalScope searchIt = new FunctionlessLocalScope( + ancestors.toArray(new Node[ancestors.size()])); + + boolean found = false; + while (searchIt.hasNext()) { + Node n = searchIt.next(); + if (n.isName() && + searchIt.currentParent().isVar() && + n.getString().equals("X")) { + found = true; + break; + } + } + + assertTrue("Variable X not found! " + root.toStringTree(), found); + + List currentAncestors = searchIt.currentAncestors(); + assert(currentAncestors.size() >= 3); + Iterator moveIt = LocalVarMotion.forVar( + currentAncestors.get(0), + currentAncestors.get(1), + currentAncestors.get(2)); + List actualTokens = Lists.newArrayList(); + while (moveIt.hasNext()) { + actualTokens.add(moveIt.next().getType()); + } + + assertEquals(expectedTokens, actualTokens); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NodeTraversalTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NodeTraversalTest.java new file mode 100644 index 0000000..6c27ca1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NodeTraversalTest.java @@ -0,0 +1,237 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableSet; +import com.google.javascript.jscomp.NodeTraversal.AbstractNodeTypePruningCallback; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Tests for {@link NodeTraversal}. + */ +public class NodeTraversalTest extends TestCase { + public void testPruningCallbackShouldTraverse1() { + PruningCallback include = + new PruningCallback(ImmutableSet.of(Token.SCRIPT, Token.VAR), true); + + Node script = new Node(Token.SCRIPT); + assertTrue(include.shouldTraverse(null, script, null)); + assertTrue(include.shouldTraverse(null, new Node(Token.VAR), null)); + assertFalse(include.shouldTraverse(null, new Node(Token.NAME), null)); + assertFalse(include.shouldTraverse(null, new Node(Token.ADD), null)); + } + + public void testPruningCallbackShouldTraverse2() { + PruningCallback include = + new PruningCallback(ImmutableSet.of(Token.SCRIPT, Token.VAR), false); + + Node script = new Node(Token.SCRIPT); + assertFalse(include.shouldTraverse(null, script, null)); + assertFalse(include.shouldTraverse(null, new Node(Token.VAR), null)); + assertTrue(include.shouldTraverse(null, new Node(Token.NAME), null)); + assertTrue(include.shouldTraverse(null, new Node(Token.ADD), null)); + } + + /** + * Concrete implementation of AbstractPrunedCallback to test the + * AbstractNodeTypePruningCallback shouldTraverse method. + */ + static class PruningCallback extends AbstractNodeTypePruningCallback { + public PruningCallback(Set nodeTypes, boolean include) { + super(nodeTypes, include); + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + throw new UnsupportedOperationException(); + } + } + + public void testReport() { + final List errors = new ArrayList(); + + Compiler compiler = new Compiler(new BasicErrorManager() { + + @Override public void report(CheckLevel level, JSError error) { + errors.add(error); + } + + @Override public void println(CheckLevel level, JSError error) { + } + + @Override protected void printSummary() { + } + }); + compiler.initCompilerOptionsIfTesting(); + + NodeTraversal t = new NodeTraversal(compiler, null); + DiagnosticType dt = DiagnosticType.warning("FOO", "{0}, {1} - {2}"); + + t.report(null, dt, "Foo", "Bar", "Hello"); + assertEquals(1, errors.size()); + assertEquals("Foo, Bar - Hello", errors.get(0).description); + } + + public void testUnexpectedException() { + final String TEST_EXCEPTION = "test me"; + + NodeTraversal.Callback cb = new NodeTraversal.AbstractPostOrderCallback() { + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + throw new RuntimeException(TEST_EXCEPTION); + } + }; + + Compiler compiler = new Compiler(); + NodeTraversal t = new NodeTraversal(compiler, cb); + String code = "function foo() {}"; + Node tree = parse(compiler, code); + + try { + t.traverse(tree); + fail("Expected RuntimeException"); + } catch (RuntimeException e) { + assertTrue(e.getMessage().startsWith( + "INTERNAL COMPILER ERROR.\n" + + "Please report this problem.\n" + + "test me")); + } + } + + + public void testGetScopeRoot() { + Compiler compiler = new Compiler(); + NodeTraversal t = new NodeTraversal(compiler, + new NodeTraversal.ScopedCallback() { + + @Override + public void enterScope(NodeTraversal t) { + Node root1 = t.getScopeRoot(); + Node root2 = t.getScope().getRootNode(); + assertEquals(root1, root2); + } + + @Override + public void exitScope(NodeTraversal t) { + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + } + } + ); + + String code = "" + + "var a; " + + "function foo() {" + + " var b" + + "}"; + Node tree = parse(compiler, code); + t.traverse(tree); + } + + public void testGetCurrentNode() { + Compiler compiler = new Compiler(); + ScopeCreator creator = new SyntacticScopeCreator(compiler); + ExpectNodeOnEnterScope callback = new ExpectNodeOnEnterScope(); + NodeTraversal t = new NodeTraversal(compiler, callback, creator); + + String code = "" + + "var a; " + + "function foo() {" + + " var b;" + + "}"; + + Node tree = parse(compiler, code); + Scope topScope = creator.createScope(tree, null); + + // Calling #traverseWithScope uses the given scope but starts traversal at + // the given node. + callback.expect(tree.getFirstChild(), tree); + t.traverseWithScope(tree.getFirstChild(), topScope); + callback.assertEntered(); + + // Calling #traverse creates a new scope with the given node as the root. + callback.expect(tree.getFirstChild(), tree.getFirstChild()); + t.traverse(tree.getFirstChild()); + callback.assertEntered(); + + // Calling #traverseAtScope starts traversal from the scope's root. + Node fn = tree.getFirstChild().getNext(); + Scope fnScope = creator.createScope(fn, topScope); + callback.expect(fn, fn); + t.traverseAtScope(fnScope); + callback.assertEntered(); + } + + + // Helper class used to test getCurrentNode + private static class ExpectNodeOnEnterScope implements + NodeTraversal.ScopedCallback { + private Node node; + private Node scopeRoot; + private boolean entered = false; + + private void expect(Node node, Node scopeRoot) { + this.node = node; + this.scopeRoot = scopeRoot; + entered = false; + } + + private void assertEntered() { + assertTrue(entered); + } + + @Override + public void enterScope(NodeTraversal t) { + assertEquals(node, t.getCurrentNode()); + assertEquals(scopeRoot, t.getScopeRoot()); + entered = true; + } + + @Override + public void exitScope(NodeTraversal t) { + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + } + } + + private static Node parse(Compiler compiler, String js) { + Node n = compiler.parseTestCode(js); + assertEquals(0, compiler.getErrorCount()); + return n; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NodeUtilTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NodeUtilTest.java new file mode 100644 index 0000000..8790308 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NodeUtilTest.java @@ -0,0 +1,1717 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.CompilerOptions.LanguageMode; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.TernaryValue; + +import junit.framework.TestCase; + +import java.util.Collection; +import java.util.Set; + +/** + * Tests for NodeUtil + */ +public class NodeUtilTest extends TestCase { + + private static Node parse(String js) { + Compiler compiler = new Compiler(); + compiler.initCompilerOptionsIfTesting(); + compiler.getOptions().setLanguageIn(LanguageMode.ECMASCRIPT5); + Node n = compiler.parseTestCode(js); + assertEquals(0, compiler.getErrorCount()); + return n; + } + + static Node getNode(String js) { + Node root = parse("var a=(" + js + ");"); + Node expr = root.getFirstChild(); + Node var = expr.getFirstChild(); + return var.getFirstChild(); + } + + public void testIsLiteralOrConstValue() { + assertLiteralAndImmutable(getNode("10")); + assertLiteralAndImmutable(getNode("-10")); + assertLiteralButNotImmutable(getNode("[10, 20]")); + assertLiteralButNotImmutable(getNode("{'a': 20}")); + assertLiteralButNotImmutable(getNode("[10, , 1.0, [undefined], 'a']")); + assertLiteralButNotImmutable(getNode("/abc/")); + assertLiteralAndImmutable(getNode("\"string\"")); + assertLiteralAndImmutable(getNode("'aaa'")); + assertLiteralAndImmutable(getNode("null")); + assertLiteralAndImmutable(getNode("undefined")); + assertLiteralAndImmutable(getNode("void 0")); + assertNotLiteral(getNode("abc")); + assertNotLiteral(getNode("[10, foo(), 20]")); + assertNotLiteral(getNode("foo()")); + assertNotLiteral(getNode("c + d")); + assertNotLiteral(getNode("{'a': foo()}")); + assertNotLiteral(getNode("void foo()")); + } + + public void assertLiteralAndImmutable(Node n) { + assertTrue(NodeUtil.isLiteralValue(n, true)); + assertTrue(NodeUtil.isLiteralValue(n, false)); + assertTrue(NodeUtil.isImmutableValue(n)); + } + + public void assertLiteralButNotImmutable(Node n) { + assertTrue(NodeUtil.isLiteralValue(n, true)); + assertTrue(NodeUtil.isLiteralValue(n, false)); + assertFalse(NodeUtil.isImmutableValue(n)); + } + + public void assertNotLiteral(Node n) { + assertFalse(NodeUtil.isLiteralValue(n, true)); + assertFalse(NodeUtil.isLiteralValue(n, false)); + assertFalse(NodeUtil.isImmutableValue(n)); + } + + public void testGetBooleanValue() { + assertPureBooleanTrue("true"); + assertPureBooleanTrue("10"); + assertPureBooleanTrue("'0'"); + assertPureBooleanTrue("/a/"); + assertPureBooleanTrue("{}"); + assertPureBooleanTrue("[]"); + assertPureBooleanFalse("false"); + assertPureBooleanFalse("null"); + assertPureBooleanFalse("0"); + assertPureBooleanFalse("''"); + assertPureBooleanFalse("undefined"); + assertPureBooleanFalse("void 0"); + assertPureBooleanUnknown("void foo()"); + assertPureBooleanUnknown("b"); + assertPureBooleanUnknown("-'0.0'"); + + // Known but getBooleanValue return false for expressions with side-effects + assertPureBooleanUnknown("{a:foo()}"); + assertPureBooleanUnknown("[foo()]"); + } + + private void assertPureBooleanTrue(String val) { + assertEquals(TernaryValue.TRUE, NodeUtil.getPureBooleanValue(getNode(val))); + } + + private void assertPureBooleanFalse(String val) { + assertEquals( + TernaryValue.FALSE, NodeUtil.getPureBooleanValue(getNode(val))); + } + + private void assertPureBooleanUnknown(String val) { + assertEquals( + TernaryValue.UNKNOWN, NodeUtil.getPureBooleanValue(getNode(val))); + } + + public void testGetExpressionBooleanValue() { + assertImpureBooleanTrue("a=true"); + assertImpureBooleanFalse("a=false"); + + assertImpureBooleanTrue("a=(false,true)"); + assertImpureBooleanFalse("a=(true,false)"); + + assertImpureBooleanTrue("a=(false || true)"); + assertImpureBooleanFalse("a=(true && false)"); + + assertImpureBooleanTrue("a=!(true && false)"); + + assertImpureBooleanTrue("a,true"); + assertImpureBooleanFalse("a,false"); + + assertImpureBooleanTrue("true||false"); + assertImpureBooleanFalse("false||false"); + + assertImpureBooleanTrue("true&&true"); + assertImpureBooleanFalse("true&&false"); + + assertImpureBooleanFalse("!true"); + assertImpureBooleanTrue("!false"); + assertImpureBooleanTrue("!''"); + + // Assignment ops other than ASSIGN are unknown. + assertImpureBooleanUnknown("a *= 2"); + + // Complex expressions that contain anything other then "=", ",", or "!" are + // unknown. + assertImpureBooleanUnknown("2 + 2"); + + assertImpureBooleanTrue("a=1"); + assertImpureBooleanTrue("a=/a/"); + assertImpureBooleanTrue("a={}"); + + assertImpureBooleanTrue("true"); + assertImpureBooleanTrue("10"); + assertImpureBooleanTrue("'0'"); + assertImpureBooleanTrue("/a/"); + assertImpureBooleanTrue("{}"); + assertImpureBooleanTrue("[]"); + assertImpureBooleanFalse("false"); + assertImpureBooleanFalse("null"); + assertImpureBooleanFalse("0"); + assertImpureBooleanFalse("''"); + assertImpureBooleanFalse("undefined"); + assertImpureBooleanFalse("void 0"); + assertImpureBooleanFalse("void foo()"); + + assertImpureBooleanTrue("a?true:true"); + assertImpureBooleanFalse("a?false:false"); + assertImpureBooleanUnknown("a?true:false"); + assertImpureBooleanUnknown("a?true:foo()"); + + assertImpureBooleanUnknown("b"); + assertImpureBooleanUnknown("-'0.0'"); + + assertImpureBooleanTrue("{a:foo()}"); + assertImpureBooleanTrue("[foo()]"); + } + + private void assertImpureBooleanTrue(String val) { + assertEquals(TernaryValue.TRUE, + NodeUtil.getImpureBooleanValue(getNode(val))); + } + + private void assertImpureBooleanFalse(String val) { + assertEquals(TernaryValue.FALSE, + NodeUtil.getImpureBooleanValue(getNode(val))); + } + + private void assertImpureBooleanUnknown(String val) { + assertEquals(TernaryValue.UNKNOWN, + NodeUtil.getImpureBooleanValue(getNode(val))); + } + + public void testGetStringValue() { + assertEquals("true", NodeUtil.getStringValue(getNode("true"))); + assertEquals("10", NodeUtil.getStringValue(getNode("10"))); + assertEquals("1", NodeUtil.getStringValue(getNode("1.0"))); + assertEquals("0", NodeUtil.getStringValue(getNode("'0'"))); + assertEquals(null, NodeUtil.getStringValue(getNode("/a/"))); + assertEquals("[object Object]", NodeUtil.getStringValue(getNode("{}"))); + assertEquals("", NodeUtil.getStringValue(getNode("[]"))); + assertEquals("false", NodeUtil.getStringValue(getNode("false"))); + assertEquals("null", NodeUtil.getStringValue(getNode("null"))); + assertEquals("0", NodeUtil.getStringValue(getNode("0"))); + assertEquals("", NodeUtil.getStringValue(getNode("''"))); + assertEquals("undefined", NodeUtil.getStringValue(getNode("undefined"))); + assertEquals("undefined", NodeUtil.getStringValue(getNode("void 0"))); + assertEquals("undefined", NodeUtil.getStringValue(getNode("void foo()"))); + + assertEquals("NaN", NodeUtil.getStringValue(getNode("NaN"))); + assertEquals("Infinity", NodeUtil.getStringValue(getNode("Infinity"))); + assertEquals(null, NodeUtil.getStringValue(getNode("x"))); + } + + public void testGetArrayStringValue() { + assertEquals("", NodeUtil.getStringValue(getNode("[]"))); + assertEquals("", NodeUtil.getStringValue(getNode("['']"))); + assertEquals("", NodeUtil.getStringValue(getNode("[null]"))); + assertEquals("", NodeUtil.getStringValue(getNode("[undefined]"))); + assertEquals("", NodeUtil.getStringValue(getNode("[void 0]"))); + assertEquals("NaN", NodeUtil.getStringValue(getNode("[NaN]"))); + assertEquals(",", NodeUtil.getStringValue(getNode("[,'']"))); + assertEquals(",,", NodeUtil.getStringValue(getNode("[[''],[''],['']]"))); + assertEquals("1,2", NodeUtil.getStringValue(getNode("[[1.0],[2.0]]"))); + assertEquals(null, NodeUtil.getStringValue(getNode("[a]"))); + assertEquals(null, NodeUtil.getStringValue(getNode("[1,a]"))); + } + + public void testIsObjectLiteralKey1() throws Exception { + testIsObjectLiteralKey( + parseExpr("({})"), false); + testIsObjectLiteralKey( + parseExpr("a"), false); + testIsObjectLiteralKey( + parseExpr("'a'"), false); + testIsObjectLiteralKey( + parseExpr("1"), false); + testIsObjectLiteralKey( + parseExpr("({a: 1})").getFirstChild(), true); + testIsObjectLiteralKey( + parseExpr("({1: 1})").getFirstChild(), true); + testIsObjectLiteralKey( + parseExpr("({get a(){}})").getFirstChild(), true); + testIsObjectLiteralKey( + parseExpr("({set a(b){}})").getFirstChild(), true); + } + + private Node parseExpr(String js) { + Compiler compiler = new Compiler(); + CompilerOptions options = new CompilerOptions(); + options.setLanguageIn(LanguageMode.ECMASCRIPT5); + compiler.initOptions(options); + Node root = compiler.parseTestCode(js); + return root.getFirstChild().getFirstChild(); + } + + private void testIsObjectLiteralKey(Node node, boolean expected) { + assertEquals(expected, NodeUtil.isObjectLitKey(node, node.getParent())); + } + + public void testGetFunctionName1() throws Exception { + Compiler compiler = new Compiler(); + Node parent = compiler.parseTestCode("function name(){}"); + + testGetFunctionName(parent.getFirstChild(), "name"); + } + + public void testGetFunctionName2() throws Exception { + Compiler compiler = new Compiler(); + Node parent = compiler.parseTestCode("var name = function(){}") + .getFirstChild().getFirstChild(); + + testGetFunctionName(parent.getFirstChild(), "name"); + } + + public void testGetFunctionName3() throws Exception { + Compiler compiler = new Compiler(); + Node parent = compiler.parseTestCode("qualified.name = function(){}") + .getFirstChild().getFirstChild(); + + testGetFunctionName(parent.getLastChild(), "qualified.name"); + } + + public void testGetFunctionName4() throws Exception { + Compiler compiler = new Compiler(); + Node parent = compiler.parseTestCode("var name2 = function name1(){}") + .getFirstChild().getFirstChild(); + + testGetFunctionName(parent.getFirstChild(), "name2"); + } + + public void testGetFunctionName5() throws Exception { + Compiler compiler = new Compiler(); + Node n = compiler.parseTestCode("qualified.name2 = function name1(){}"); + Node parent = n.getFirstChild().getFirstChild(); + + testGetFunctionName(parent.getLastChild(), "qualified.name2"); + } + + private void testGetFunctionName(Node function, String name) { + assertEquals(Token.FUNCTION, function.getType()); + assertEquals(name, NodeUtil.getFunctionName(function)); + } + + public void testContainsFunctionDeclaration() { + assertTrue(NodeUtil.containsFunction( + getNode("function foo(){}"))); + assertTrue(NodeUtil.containsFunction( + getNode("(b?function(){}:null)"))); + + assertFalse(NodeUtil.containsFunction( + getNode("(b?foo():null)"))); + assertFalse(NodeUtil.containsFunction( + getNode("foo()"))); + } + + private void assertSideEffect(boolean se, String js) { + Node n = parse(js); + assertEquals(se, NodeUtil.mayHaveSideEffects(n.getFirstChild())); + } + + private void assertSideEffect(boolean se, String js, boolean globalRegExp) { + Node n = parse(js); + Compiler compiler = new Compiler(); + compiler.setHasRegExpGlobalReferences(globalRegExp); + assertEquals(se, NodeUtil.mayHaveSideEffects(n.getFirstChild(), compiler)); + } + + public void testMayHaveSideEffects() { + assertSideEffect(true, "i++"); + assertSideEffect(true, "[b, [a, i++]]"); + assertSideEffect(true, "i=3"); + assertSideEffect(true, "[0, i=3]"); + assertSideEffect(true, "b()"); + assertSideEffect(true, "[1, b()]"); + assertSideEffect(true, "b.b=4"); + assertSideEffect(true, "b.b--"); + assertSideEffect(true, "i--"); + assertSideEffect(true, "a[0][i=4]"); + assertSideEffect(true, "a += 3"); + assertSideEffect(true, "a, b, z += 4"); + assertSideEffect(true, "a ? c : d++"); + assertSideEffect(true, "a + c++"); + assertSideEffect(true, "a + c - d()"); + assertSideEffect(true, "a + c - d()"); + + assertSideEffect(true, "function foo() {}"); + assertSideEffect(true, "while(true);"); + assertSideEffect(true, "if(true){a()}"); + + assertSideEffect(false, "if(true){a}"); + assertSideEffect(false, "(function() { })"); + assertSideEffect(false, "(function() { i++ })"); + assertSideEffect(false, "[function a(){}]"); + + assertSideEffect(false, "a"); + assertSideEffect(false, "[b, c [d, [e]]]"); + assertSideEffect(false, "({a: x, b: y, c: z})"); + assertSideEffect(false, "/abc/gi"); + assertSideEffect(false, "'a'"); + assertSideEffect(false, "0"); + assertSideEffect(false, "a + c"); + assertSideEffect(false, "'c' + a[0]"); + assertSideEffect(false, "a[0][1]"); + assertSideEffect(false, "'a' + c"); + assertSideEffect(false, "'a' + a.name"); + assertSideEffect(false, "1, 2, 3"); + assertSideEffect(false, "a, b, 3"); + assertSideEffect(false, "(function(a, b) { })"); + assertSideEffect(false, "a ? c : d"); + assertSideEffect(false, "'1' + navigator.userAgent"); + + assertSideEffect(false, "new RegExp('foobar', 'i')"); + assertSideEffect(true, "new RegExp(SomethingWacky(), 'i')"); + assertSideEffect(false, "new Array()"); + assertSideEffect(false, "new Array"); + assertSideEffect(false, "new Array(4)"); + assertSideEffect(false, "new Array('a', 'b', 'c')"); + assertSideEffect(true, "new SomeClassINeverHeardOf()"); + assertSideEffect(true, "new SomeClassINeverHeardOf()"); + + assertSideEffect(false, "({}).foo = 4"); + assertSideEffect(false, "([]).foo = 4"); + assertSideEffect(false, "(function() {}).foo = 4"); + + assertSideEffect(true, "this.foo = 4"); + assertSideEffect(true, "a.foo = 4"); + assertSideEffect(true, "(function() { return n; })().foo = 4"); + assertSideEffect(true, "([]).foo = bar()"); + + assertSideEffect(false, "undefined"); + assertSideEffect(false, "void 0"); + assertSideEffect(true, "void foo()"); + assertSideEffect(false, "-Infinity"); + assertSideEffect(false, "Infinity"); + assertSideEffect(false, "NaN"); + + assertSideEffect(false, "({}||[]).foo = 2;"); + assertSideEffect(false, "(true ? {} : []).foo = 2;"); + assertSideEffect(false, "({},[]).foo = 2;"); + + assertSideEffect(true, "delete a.b"); + } + + public void testObjectMethodSideEffects() { + // "toString" and "valueOf" are assumed to be side-effect free + assertSideEffect(false, "o.toString()"); + assertSideEffect(false, "o.valueOf()"); + + // other methods depend on the extern definitions + assertSideEffect(true, "o.watch()"); + } + + public void testRegExpSideEffect() { + // A RegExp Object by itself doesn't have any side-effects + assertSideEffect(false, "/abc/gi", true); + assertSideEffect(false, "/abc/gi", false); + + // RegExp instance methods have global side-effects, so whether they are + // considered side-effect free depends on whether the global properties + // are referenced. + assertSideEffect(true, "(/abc/gi).test('')", true); + assertSideEffect(false, "(/abc/gi).test('')", false); + assertSideEffect(true, "(/abc/gi).test(a)", true); + assertSideEffect(false, "(/abc/gi).test(b)", false); + + assertSideEffect(true, "(/abc/gi).exec('')", true); + assertSideEffect(false, "(/abc/gi).exec('')", false); + + // Some RegExp object method that may have side-effects. + assertSideEffect(true, "(/abc/gi).foo('')", true); + assertSideEffect(true, "(/abc/gi).foo('')", false); + + // Try the string RegExp ops. + assertSideEffect(true, "''.match('a')", true); + assertSideEffect(false, "''.match('a')", false); + assertSideEffect(true, "''.match(/(a)/)", true); + assertSideEffect(false, "''.match(/(a)/)", false); + + assertSideEffect(true, "''.replace('a')", true); + assertSideEffect(false, "''.replace('a')", false); + + assertSideEffect(true, "''.search('a')", true); + assertSideEffect(false, "''.search('a')", false); + + assertSideEffect(true, "''.split('a')", true); + assertSideEffect(false, "''.split('a')", false); + + // Some non-RegExp string op that may have side-effects. + assertSideEffect(true, "''.foo('a')", true); + assertSideEffect(true, "''.foo('a')", false); + + // 'a' might be a RegExp object with the 'g' flag, in which case + // the state might change by running any of the string ops. + // Specifically, using these methods resets the "lastIndex" if used + // in combination with a RegExp instance "exec" method. + assertSideEffect(true, "''.match(a)", true); + assertSideEffect(true, "''.match(a)", false); + } + + private void assertMutableState(boolean se, String js) { + Node n = parse(js); + assertEquals(se, NodeUtil.mayEffectMutableState(n.getFirstChild())); + } + + public void testMayEffectMutableState() { + assertMutableState(true, "i++"); + assertMutableState(true, "[b, [a, i++]]"); + assertMutableState(true, "i=3"); + assertMutableState(true, "[0, i=3]"); + assertMutableState(true, "b()"); + assertMutableState(true, "void b()"); + assertMutableState(true, "[1, b()]"); + assertMutableState(true, "b.b=4"); + assertMutableState(true, "b.b--"); + assertMutableState(true, "i--"); + assertMutableState(true, "a[0][i=4]"); + assertMutableState(true, "a += 3"); + assertMutableState(true, "a, b, z += 4"); + assertMutableState(true, "a ? c : d++"); + assertMutableState(true, "a + c++"); + assertMutableState(true, "a + c - d()"); + assertMutableState(true, "a + c - d()"); + + assertMutableState(true, "function foo() {}"); + assertMutableState(true, "while(true);"); + assertMutableState(true, "if(true){a()}"); + + assertMutableState(false, "if(true){a}"); + assertMutableState(true, "(function() { })"); + assertMutableState(true, "(function() { i++ })"); + assertMutableState(true, "[function a(){}]"); + + assertMutableState(false, "a"); + assertMutableState(true, "[b, c [d, [e]]]"); + assertMutableState(true, "({a: x, b: y, c: z})"); + // Note: RegExp objects are not immutable, for instance, the exec + // method maintains state for "global" searches. + assertMutableState(true, "/abc/gi"); + assertMutableState(false, "'a'"); + assertMutableState(false, "0"); + assertMutableState(false, "a + c"); + assertMutableState(false, "'c' + a[0]"); + assertMutableState(false, "a[0][1]"); + assertMutableState(false, "'a' + c"); + assertMutableState(false, "'a' + a.name"); + assertMutableState(false, "1, 2, 3"); + assertMutableState(false, "a, b, 3"); + assertMutableState(true, "(function(a, b) { })"); + assertMutableState(false, "a ? c : d"); + assertMutableState(false, "'1' + navigator.userAgent"); + + assertMutableState(true, "new RegExp('foobar', 'i')"); + assertMutableState(true, "new RegExp(SomethingWacky(), 'i')"); + assertMutableState(true, "new Array()"); + assertMutableState(true, "new Array"); + assertMutableState(true, "new Array(4)"); + assertMutableState(true, "new Array('a', 'b', 'c')"); + assertMutableState(true, "new SomeClassINeverHeardOf()"); + } + + + public void testIsFunctionExpression() { + assertContainsAnonFunc(true, "(function(){})"); + assertContainsAnonFunc(true, "[function a(){}]"); + assertContainsAnonFunc(false, "{x: function a(){}}"); + assertContainsAnonFunc(true, "(function a(){})()"); + assertContainsAnonFunc(true, "x = function a(){};"); + assertContainsAnonFunc(true, "var x = function a(){};"); + assertContainsAnonFunc(true, "if (function a(){});"); + assertContainsAnonFunc(true, "while (function a(){});"); + assertContainsAnonFunc(true, "do; while (function a(){});"); + assertContainsAnonFunc(true, "for (function a(){};;);"); + assertContainsAnonFunc(true, "for (;function a(){};);"); + assertContainsAnonFunc(true, "for (;;function a(){});"); + assertContainsAnonFunc(true, "for (p in function a(){});"); + assertContainsAnonFunc(true, "with (function a(){}) {}"); + assertContainsAnonFunc(false, "function a(){}"); + assertContainsAnonFunc(false, "if (x) function a(){};"); + assertContainsAnonFunc(false, "if (x) { function a(){} }"); + assertContainsAnonFunc(false, "if (x); else function a(){};"); + assertContainsAnonFunc(false, "while (x) function a(){};"); + assertContainsAnonFunc(false, "do function a(){} while (0);"); + assertContainsAnonFunc(false, "for (;;) function a(){}"); + assertContainsAnonFunc(false, "for (p in o) function a(){};"); + assertContainsAnonFunc(false, "with (x) function a(){}"); + } + + private void assertContainsAnonFunc(boolean expected, String js) { + Node funcParent = findParentOfFuncDescendant(parse(js)); + assertNotNull("Expected function node in parse tree of: " + js, funcParent); + Node funcNode = getFuncChild(funcParent); + assertEquals(expected, NodeUtil.isFunctionExpression(funcNode)); + } + + private Node findParentOfFuncDescendant(Node n) { + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + if (c.isFunction()) { + return n; + } + Node result = findParentOfFuncDescendant(c); + if (result != null) { + return result; + } + } + return null; + } + + private Node getFuncChild(Node n) { + for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { + if (c.isFunction()) { + return c; + } + } + return null; + } + + public void testContainsType() { + assertTrue(NodeUtil.containsType( + parse("this"), Token.THIS)); + assertTrue(NodeUtil.containsType( + parse("function foo(){}(this)"), Token.THIS)); + assertTrue(NodeUtil.containsType( + parse("b?this:null"), Token.THIS)); + + assertFalse(NodeUtil.containsType( + parse("a"), Token.THIS)); + assertFalse(NodeUtil.containsType( + parse("function foo(){}"), Token.THIS)); + assertFalse(NodeUtil.containsType( + parse("(b?foo():null)"), Token.THIS)); + } + + public void testReferencesThis() { + assertTrue(NodeUtil.referencesThis( + parse("this"))); + // Don't descend into functions (starts at the script node) + assertFalse(NodeUtil.referencesThis( + parse("function foo(){this}"))); + // But starting with a function properly check for 'this' + Node n = parse("function foo(){this}").getFirstChild(); + assertEquals(n.getType(), Token.FUNCTION); + assertTrue(NodeUtil.referencesThis(n)); + assertTrue(NodeUtil.referencesThis( + parse("b?this:null"))); + + assertFalse(NodeUtil.referencesThis( + parse("a"))); + n = parse("function foo(){}").getFirstChild(); + assertEquals(n.getType(), Token.FUNCTION); + assertFalse(NodeUtil.referencesThis(n)); + assertFalse(NodeUtil.referencesThis( + parse("(b?foo():null)"))); + } + + public void testGetNodeTypeReferenceCount() { + assertEquals(0, NodeUtil.getNodeTypeReferenceCount( + parse("function foo(){}"), Token.THIS, + Predicates.alwaysTrue())); + assertEquals(1, NodeUtil.getNodeTypeReferenceCount( + parse("this"), Token.THIS, + Predicates.alwaysTrue())); + assertEquals(2, NodeUtil.getNodeTypeReferenceCount( + parse("this;function foo(){}(this)"), Token.THIS, + Predicates.alwaysTrue())); + } + + public void testIsNameReferenceCount() { + assertTrue(NodeUtil.isNameReferenced( + parse("function foo(){}"), "foo")); + assertTrue(NodeUtil.isNameReferenced( + parse("var foo = function(){}"), "foo")); + assertFalse(NodeUtil.isNameReferenced( + parse("function foo(){}"), "undefined")); + assertTrue(NodeUtil.isNameReferenced( + parse("undefined"), "undefined")); + assertTrue(NodeUtil.isNameReferenced( + parse("undefined;function foo(){}(undefined)"), "undefined")); + + assertTrue(NodeUtil.isNameReferenced( + parse("goo.foo"), "goo")); + assertFalse(NodeUtil.isNameReferenced( + parse("goo.foo"), "foo")); + } + + + public void testGetNameReferenceCount() { + assertEquals(0, NodeUtil.getNameReferenceCount( + parse("function foo(){}"), "undefined")); + assertEquals(1, NodeUtil.getNameReferenceCount( + parse("undefined"), "undefined")); + assertEquals(2, NodeUtil.getNameReferenceCount( + parse("undefined;function foo(){}(undefined)"), "undefined")); + + assertEquals(1, NodeUtil.getNameReferenceCount( + parse("goo.foo"), "goo")); + assertEquals(0, NodeUtil.getNameReferenceCount( + parse("goo.foo"), "foo")); + assertEquals(1, NodeUtil.getNameReferenceCount( + parse("function foo(){}"), "foo")); + assertEquals(1, NodeUtil.getNameReferenceCount( + parse("var foo = function(){}"), "foo")); + } + + public void testGetVarsDeclaredInBranch() { + Compiler compiler = new Compiler(); + + assertNodeNames(Sets.newHashSet("foo"), + NodeUtil.getVarsDeclaredInBranch( + parse("var foo;"))); + assertNodeNames(Sets.newHashSet("foo", "goo"), + NodeUtil.getVarsDeclaredInBranch( + parse("var foo,goo;"))); + assertNodeNames(Sets.newHashSet(), + NodeUtil.getVarsDeclaredInBranch( + parse("foo();"))); + assertNodeNames(Sets.newHashSet(), + NodeUtil.getVarsDeclaredInBranch( + parse("function f(){var foo;}"))); + assertNodeNames(Sets.newHashSet("goo"), + NodeUtil.getVarsDeclaredInBranch( + parse("var goo;function f(){var foo;}"))); + } + + private void assertNodeNames(Set nodeNames, Collection nodes) { + Set actualNames = Sets.newHashSet(); + for (Node node : nodes) { + actualNames.add(node.getString()); + } + assertEquals(nodeNames, actualNames); + } + + public void testIsControlStructureCodeBlock() { + Node root = parse("if (x) foo(); else boo();"); + Node ifNode = root.getFirstChild(); + + Node ifCondition = ifNode.getFirstChild(); + Node ifCase = ifNode.getFirstChild().getNext(); + Node elseCase = ifNode.getLastChild(); + + assertFalse(NodeUtil.isControlStructureCodeBlock(ifNode, ifCondition)); + assertTrue(NodeUtil.isControlStructureCodeBlock(ifNode, ifCase)); + assertTrue(NodeUtil.isControlStructureCodeBlock(ifNode, elseCase)); + } + + public void testIsFunctionExpression1() { + Node root = parse("(function foo() {})"); + Node statementNode = root.getFirstChild(); + assertTrue(statementNode.isExprResult()); + Node functionNode = statementNode.getFirstChild(); + assertTrue(functionNode.isFunction()); + assertTrue(NodeUtil.isFunctionExpression(functionNode)); + } + + public void testIsFunctionExpression2() { + Node root = parse("function foo() {}"); + Node functionNode = root.getFirstChild(); + assertTrue(functionNode.isFunction()); + assertFalse(NodeUtil.isFunctionExpression(functionNode)); + } + + public void testRemoveChildBlock() { + // Test removing the inner block. + Node actual = parse("{{x()}}"); + + Node outerBlockNode = actual.getFirstChild(); + Node innerBlockNode = outerBlockNode.getFirstChild(); + innerBlockNode.setIsSyntheticBlock(true); + + NodeUtil.removeChild(outerBlockNode, innerBlockNode); + String expected = "{{}}"; + String difference = parse(expected).checkTreeEquals(actual); + if (difference != null) { + assertTrue("Nodes do not match:\n" + difference, false); + } + } + + public void testRemoveTryChild1() { + // Test removing the finally clause. + Node actual = parse("try {foo()} catch(e) {} finally {}"); + + Node tryNode = actual.getFirstChild(); + Node tryBlock = tryNode.getFirstChild(); + Node catchBlocks = tryNode.getFirstChild().getNext(); + Node finallyBlock = tryNode.getLastChild(); + + NodeUtil.removeChild(tryNode, finallyBlock); + String expected = "try {foo()} catch(e) {}"; + String difference = parse(expected).checkTreeEquals(actual); + if (difference != null) { + assertTrue("Nodes do not match:\n" + difference, false); + } + } + + public void testRemoveTryChild2() { + // Test removing the try clause. + Node actual = parse("try {foo()} catch(e) {} finally {}"); + + Node tryNode = actual.getFirstChild(); + Node tryBlock = tryNode.getFirstChild(); + Node catchBlocks = tryNode.getFirstChild().getNext(); + + NodeUtil.removeChild(tryNode, tryBlock); + String expected = "try {} catch(e) {} finally {}"; + String difference = parse(expected).checkTreeEquals(actual); + if (difference != null) { + assertTrue("Nodes do not match:\n" + difference, false); + } + } + + public void testRemoveTryChild3() { + // Test removing the catch clause. + Node actual = parse("try {foo()} catch(e) {} finally {}"); + + Node tryNode = actual.getFirstChild(); + Node tryBlock = tryNode.getFirstChild(); + Node catchBlocks = tryNode.getFirstChild().getNext(); + Node catchBlock = catchBlocks.getFirstChild(); + Node finallyBlock = tryNode.getLastChild(); + + NodeUtil.removeChild(catchBlocks, catchBlock); + String expected = "try {foo()} finally {}"; + String difference = parse(expected).checkTreeEquals(actual); + if (difference != null) { + assertTrue("Nodes do not match:\n" + difference, false); + } + } + + public void testRemoveTryChild4() { + // Test removing the catch clause without a finally. + Node actual = parse("try {foo()} catch(e) {} finally {}"); + + Node tryNode = actual.getFirstChild(); + Node tryBlock = tryNode.getFirstChild(); + Node catchBlocks = tryNode.getFirstChild().getNext(); + Node catchBlock = catchBlocks.getFirstChild(); + Node finallyBlock = tryNode.getLastChild(); + + NodeUtil.removeChild(tryNode, catchBlocks); + String expected = "try {foo()} finally {}"; + String difference = parse(expected).checkTreeEquals(actual); + if (difference != null) { + assertTrue("Nodes do not match:\n" + difference, false); + } + } + + public void testRemoveTryChild5() { + Node actual = parse("try {foo()} catch(e) {} finally {}"); + + Node tryNode = actual.getFirstChild(); + Node tryBlock = tryNode.getFirstChild(); + Node catchBlocks = tryNode.getFirstChild().getNext(); + Node catchBlock = catchBlocks.getFirstChild(); + Node finallyBlock = tryNode.getLastChild(); + + NodeUtil.removeChild(catchBlocks, catchBlock); + String expected = "try {foo()} finally {}"; + String difference = parse(expected).checkTreeEquals(actual); + if (difference != null) { + assertTrue("Nodes do not match:\n" + difference, false); + } + } + + public void testRemoveVarChild() { + Compiler compiler = new Compiler(); + + // Test removing the first child. + Node actual = parse("var foo, goo, hoo"); + + Node varNode = actual.getFirstChild(); + Node nameNode = varNode.getFirstChild(); + + NodeUtil.removeChild(varNode, nameNode); + String expected = "var goo, hoo"; + String difference = parse(expected).checkTreeEquals(actual); + if (difference != null) { + assertTrue("Nodes do not match:\n" + difference, false); + } + + + // Test removing the second child. + actual = parse("var foo, goo, hoo"); + + varNode = actual.getFirstChild(); + nameNode = varNode.getFirstChild().getNext(); + + NodeUtil.removeChild(varNode, nameNode); + expected = "var foo, hoo"; + difference = parse(expected).checkTreeEquals(actual); + if (difference != null) { + assertTrue("Nodes do not match:\n" + difference, false); + } + + // Test removing the last child of several children. + actual = parse("var foo, hoo"); + + varNode = actual.getFirstChild(); + nameNode = varNode.getFirstChild().getNext(); + + NodeUtil.removeChild(varNode, nameNode); + expected = "var foo"; + difference = parse(expected).checkTreeEquals(actual); + if (difference != null) { + assertTrue("Nodes do not match:\n" + difference, false); + } + + // Test removing the last. + actual = parse("var hoo"); + + varNode = actual.getFirstChild(); + nameNode = varNode.getFirstChild(); + + NodeUtil.removeChild(varNode, nameNode); + expected = ""; + difference = parse(expected).checkTreeEquals(actual); + if (difference != null) { + assertTrue("Nodes do not match:\n" + difference, false); + } + } + + public void testRemoveLabelChild1() { + Compiler compiler = new Compiler(); + + // Test removing the first child. + Node actual = parse("foo: goo()"); + + Node labelNode = actual.getFirstChild(); + Node callExpressNode = labelNode.getLastChild(); + + NodeUtil.removeChild(labelNode, callExpressNode); + String expected = ""; + String difference = parse(expected).checkTreeEquals(actual); + if (difference != null) { + assertTrue("Nodes do not match:\n" + difference, false); + } + } + + public void testRemoveLabelChild2() { + // Test removing the first child. + Node actual = parse("achoo: foo: goo()"); + + Node labelNode = actual.getFirstChild(); + Node callExpressNode = labelNode.getLastChild(); + + NodeUtil.removeChild(labelNode, callExpressNode); + String expected = ""; + String difference = parse(expected).checkTreeEquals(actual); + if (difference != null) { + assertTrue("Nodes do not match:\n" + difference, false); + } + } + + public void testRemoveForChild() { + Compiler compiler = new Compiler(); + + // Test removing the initializer. + Node actual = parse("for(var a=0;a<0;a++)foo()"); + + Node forNode = actual.getFirstChild(); + Node child = forNode.getFirstChild(); + + NodeUtil.removeChild(forNode, child); + String expected = "for(;a<0;a++)foo()"; + String difference = parse(expected).checkTreeEquals(actual); + assertNull("Nodes do not match:\n" + difference, difference); + + + // Test removing the condition. + actual = parse("for(var a=0;a<0;a++)foo()"); + + forNode = actual.getFirstChild(); + child = forNode.getFirstChild().getNext(); + + NodeUtil.removeChild(forNode, child); + expected = "for(var a=0;;a++)foo()"; + difference = parse(expected).checkTreeEquals(actual); + assertNull("Nodes do not match:\n" + difference, difference); + + + // Test removing the increment. + actual = parse("for(var a=0;a<0;a++)foo()"); + + forNode = actual.getFirstChild(); + child = forNode.getFirstChild().getNext().getNext(); + + NodeUtil.removeChild(forNode, child); + expected = "for(var a=0;a<0;)foo()"; + difference = parse(expected).checkTreeEquals(actual); + assertNull("Nodes do not match:\n" + difference, difference); + + + // Test removing the body. + actual = parse("for(var a=0;a<0;a++)foo()"); + + forNode = actual.getFirstChild(); + child = forNode.getLastChild(); + + NodeUtil.removeChild(forNode, child); + expected = "for(var a=0;a<0;a++);"; + difference = parse(expected).checkTreeEquals(actual); + assertNull("Nodes do not match:\n" + difference, difference); + + + // Test removing the body. + actual = parse("for(a in ack)foo();"); + + forNode = actual.getFirstChild(); + child = forNode.getLastChild(); + + NodeUtil.removeChild(forNode, child); + expected = "for(a in ack);"; + difference = parse(expected).checkTreeEquals(actual); + assertNull("Nodes do not match:\n" + difference, difference); + } + + public void testMergeBlock1() { + Compiler compiler = new Compiler(); + + // Test removing the initializer. + Node actual = parse("{{a();b();}}"); + + Node parentBlock = actual.getFirstChild(); + Node childBlock = parentBlock.getFirstChild(); + + assertTrue(NodeUtil.tryMergeBlock(childBlock)); + String expected = "{a();b();}"; + String difference = parse(expected).checkTreeEquals(actual); + assertNull("Nodes do not match:\n" + difference, difference); + } + + public void testMergeBlock2() { + Compiler compiler = new Compiler(); + + // Test removing the initializer. + Node actual = parse("foo:{a();}"); + + Node parentLabel = actual.getFirstChild(); + Node childBlock = parentLabel.getLastChild(); + + assertFalse(NodeUtil.tryMergeBlock(childBlock)); + } + + public void testMergeBlock3() { + Compiler compiler = new Compiler(); + + // Test removing the initializer. + String code = "foo:{a();boo()}"; + Node actual = parse("foo:{a();boo()}"); + + Node parentLabel = actual.getFirstChild(); + Node childBlock = parentLabel.getLastChild(); + + assertFalse(NodeUtil.tryMergeBlock(childBlock)); + String expected = code; + String difference = parse(expected).checkTreeEquals(actual); + assertNull("Nodes do not match:\n" + difference, difference); + } + + public void testGetSourceName() { + Node n = new Node(Token.BLOCK); + Node parent = new Node(Token.BLOCK, n); + parent.setSourceFileForTesting("foo"); + + assertEquals("foo", NodeUtil.getSourceName(n)); + } + + public void testLocalValue1() throws Exception { + // Names are not known to be local. + assertFalse(testLocalValue("x")); + assertFalse(testLocalValue("x()")); + assertFalse(testLocalValue("this")); + assertFalse(testLocalValue("arguments")); + + // We can't know if new objects are local unless we know + // that they don't alias themselves. + assertFalse(testLocalValue("new x()")); + + // property references are assume to be non-local + assertFalse(testLocalValue("(new x()).y")); + assertFalse(testLocalValue("(new x())['y']")); + + // Primitive values are local + assertTrue(testLocalValue("null")); + assertTrue(testLocalValue("undefined")); + assertTrue(testLocalValue("Infinity")); + assertTrue(testLocalValue("NaN")); + assertTrue(testLocalValue("1")); + assertTrue(testLocalValue("'a'")); + assertTrue(testLocalValue("true")); + assertTrue(testLocalValue("false")); + assertTrue(testLocalValue("[]")); + assertTrue(testLocalValue("{}")); + + // The contents of arrays and objects don't matter + assertTrue(testLocalValue("[x]")); + assertTrue(testLocalValue("{'a':x}")); + + // Pre-increment results in primitive number + assertTrue(testLocalValue("++x")); + assertTrue(testLocalValue("--x")); + + // Post-increment, the previous value matters. + assertFalse(testLocalValue("x++")); + assertFalse(testLocalValue("x--")); + + // The left side of an only assign matters if it is an alias or mutable. + assertTrue(testLocalValue("x=1")); + assertFalse(testLocalValue("x=[]")); + assertFalse(testLocalValue("x=y")); + // The right hand side of assignment opts don't matter, as they force + // a local result. + assertTrue(testLocalValue("x+=y")); + assertTrue(testLocalValue("x*=y")); + // Comparisons always result in locals, as they force a local boolean + // result. + assertTrue(testLocalValue("x==y")); + assertTrue(testLocalValue("x!=y")); + assertTrue(testLocalValue("x>y")); + // Only the right side of a comma matters + assertTrue(testLocalValue("(1,2)")); + assertTrue(testLocalValue("(x,1)")); + assertFalse(testLocalValue("(x,y)")); + + // Both the operands of OR matter + assertTrue(testLocalValue("1||2")); + assertFalse(testLocalValue("x||1")); + assertFalse(testLocalValue("x||y")); + assertFalse(testLocalValue("1||y")); + + // Both the operands of AND matter + assertTrue(testLocalValue("1&&2")); + assertFalse(testLocalValue("x&&1")); + assertFalse(testLocalValue("x&&y")); + assertFalse(testLocalValue("1&&y")); + + // Only the results of HOOK matter + assertTrue(testLocalValue("x?1:2")); + assertFalse(testLocalValue("x?x:2")); + assertFalse(testLocalValue("x?1:x")); + assertFalse(testLocalValue("x?x:y")); + + // Results of ops are local values + assertTrue(testLocalValue("!y")); + assertTrue(testLocalValue("~y")); + assertTrue(testLocalValue("y + 1")); + assertTrue(testLocalValue("y + z")); + assertTrue(testLocalValue("y * z")); + + assertTrue(testLocalValue("'a' in x")); + assertTrue(testLocalValue("typeof x")); + assertTrue(testLocalValue("x instanceof y")); + + assertTrue(testLocalValue("void x")); + assertTrue(testLocalValue("void 0")); + + assertFalse(testLocalValue("{}.x")); + + assertTrue(testLocalValue("{}.toString()")); + assertTrue(testLocalValue("o.toString()")); + + assertFalse(testLocalValue("o.valueOf()")); + + assertTrue(testLocalValue("delete a.b")); + } + + public void testLocalValue2() { + Node newExpr = getNode("new x()"); + assertFalse(NodeUtil.evaluatesToLocalValue(newExpr)); + + Preconditions.checkState(newExpr.isNew()); + Node.SideEffectFlags flags = new Node.SideEffectFlags(); + + flags.clearAllFlags(); + newExpr.setSideEffectFlags(flags.valueOf()); + + assertTrue(NodeUtil.evaluatesToLocalValue(newExpr)); + + flags.clearAllFlags(); + flags.setMutatesThis(); + newExpr.setSideEffectFlags(flags.valueOf()); + + assertTrue(NodeUtil.evaluatesToLocalValue(newExpr)); + + flags.clearAllFlags(); + flags.setReturnsTainted(); + newExpr.setSideEffectFlags(flags.valueOf()); + + assertTrue(NodeUtil.evaluatesToLocalValue(newExpr)); + + flags.clearAllFlags(); + flags.setThrows(); + newExpr.setSideEffectFlags(flags.valueOf()); + + assertFalse(NodeUtil.evaluatesToLocalValue(newExpr)); + + flags.clearAllFlags(); + flags.setMutatesArguments(); + newExpr.setSideEffectFlags(flags.valueOf()); + + assertFalse(NodeUtil.evaluatesToLocalValue(newExpr)); + + flags.clearAllFlags(); + flags.setMutatesGlobalState(); + newExpr.setSideEffectFlags(flags.valueOf()); + + assertFalse(NodeUtil.evaluatesToLocalValue(newExpr)); + } + + public void testCallSideEffects() { + Node callExpr = getNode("new x().method()"); + assertTrue(NodeUtil.functionCallHasSideEffects(callExpr)); + + Node newExpr = callExpr.getFirstChild().getFirstChild(); + Preconditions.checkState(newExpr.isNew()); + Node.SideEffectFlags flags = new Node.SideEffectFlags(); + + // No side effects, local result + flags.clearAllFlags(); + newExpr.setSideEffectFlags(flags.valueOf()); + flags.clearAllFlags(); + callExpr.setSideEffectFlags(flags.valueOf()); + + assertTrue(NodeUtil.evaluatesToLocalValue(callExpr)); + assertFalse(NodeUtil.functionCallHasSideEffects(callExpr)); + assertFalse(NodeUtil.mayHaveSideEffects(callExpr)); + + // Modifies this, local result + flags.clearAllFlags(); + newExpr.setSideEffectFlags(flags.valueOf()); + flags.clearAllFlags(); + flags.setMutatesThis(); + callExpr.setSideEffectFlags(flags.valueOf()); + + assertTrue(NodeUtil.evaluatesToLocalValue(callExpr)); + assertFalse(NodeUtil.functionCallHasSideEffects(callExpr)); + assertFalse(NodeUtil.mayHaveSideEffects(callExpr)); + + // Modifies this, non-local result + flags.clearAllFlags(); + newExpr.setSideEffectFlags(flags.valueOf()); + flags.clearAllFlags(); + flags.setMutatesThis(); + flags.setReturnsTainted(); + callExpr.setSideEffectFlags(flags.valueOf()); + + assertFalse(NodeUtil.evaluatesToLocalValue(callExpr)); + assertFalse(NodeUtil.functionCallHasSideEffects(callExpr)); + assertFalse(NodeUtil.mayHaveSideEffects(callExpr)); + + // No modifications, non-local result + flags.clearAllFlags(); + newExpr.setSideEffectFlags(flags.valueOf()); + flags.clearAllFlags(); + flags.setReturnsTainted(); + callExpr.setSideEffectFlags(flags.valueOf()); + + assertFalse(NodeUtil.evaluatesToLocalValue(callExpr)); + assertFalse(NodeUtil.functionCallHasSideEffects(callExpr)); + assertFalse(NodeUtil.mayHaveSideEffects(callExpr)); + + // The new modifies global state, no side-effect call, non-local result + // This call could be removed, but not the new. + flags.clearAllFlags(); + flags.setMutatesGlobalState(); + newExpr.setSideEffectFlags(flags.valueOf()); + flags.clearAllFlags(); + callExpr.setSideEffectFlags(flags.valueOf()); + + assertTrue(NodeUtil.evaluatesToLocalValue(callExpr)); + assertFalse(NodeUtil.functionCallHasSideEffects(callExpr)); + assertTrue(NodeUtil.mayHaveSideEffects(callExpr)); + } + + private boolean testLocalValue(String js) { + return NodeUtil.evaluatesToLocalValue(getNode(js)); + } + + public void testValidDefine() { + assertTrue(testValidDefineValue("1")); + assertTrue(testValidDefineValue("-3")); + assertTrue(testValidDefineValue("true")); + assertTrue(testValidDefineValue("false")); + assertTrue(testValidDefineValue("'foo'")); + + assertFalse(testValidDefineValue("x")); + assertFalse(testValidDefineValue("null")); + assertFalse(testValidDefineValue("undefined")); + assertFalse(testValidDefineValue("NaN")); + + assertTrue(testValidDefineValue("!true")); + assertTrue(testValidDefineValue("-true")); + assertTrue(testValidDefineValue("1 & 8")); + assertTrue(testValidDefineValue("1 + 8")); + assertTrue(testValidDefineValue("'a' + 'b'")); + + assertFalse(testValidDefineValue("1 & foo")); + } + + private boolean testValidDefineValue(String js) { + Node script = parse("var test = " + js + ";"); + Node var = script.getFirstChild(); + Node name = var.getFirstChild(); + Node value = name.getFirstChild(); + + ImmutableSet defines = ImmutableSet.of(); + return NodeUtil.isValidDefineValue(value, defines); + } + + public void testGetNumberValue() { + // Strings + assertEquals(1.0, NodeUtil.getNumberValue(getNode("'\\uFEFF1'"))); + assertEquals(0.0, NodeUtil.getNumberValue(getNode("''"))); + assertEquals(0.0, NodeUtil.getNumberValue(getNode("' '"))); + assertEquals(0.0, NodeUtil.getNumberValue(getNode("' \\t'"))); + assertEquals(0.0, NodeUtil.getNumberValue(getNode("'+0'"))); + assertEquals(-0.0, NodeUtil.getNumberValue(getNode("'-0'"))); + assertEquals(2.0, NodeUtil.getNumberValue(getNode("'+2'"))); + assertEquals(-1.6, NodeUtil.getNumberValue(getNode("'-1.6'"))); + assertEquals(16.0, NodeUtil.getNumberValue(getNode("'16'"))); + assertEquals(16.0, NodeUtil.getNumberValue(getNode("' 16 '"))); + assertEquals(16.0, NodeUtil.getNumberValue(getNode("' 16 '"))); + assertEquals(12300.0, NodeUtil.getNumberValue(getNode("'123e2'"))); + assertEquals(12300.0, NodeUtil.getNumberValue(getNode("'123E2'"))); + assertEquals(1.23, NodeUtil.getNumberValue(getNode("'123e-2'"))); + assertEquals(1.23, NodeUtil.getNumberValue(getNode("'123E-2'"))); + assertEquals(-1.23, NodeUtil.getNumberValue(getNode("'-123e-2'"))); + assertEquals(-1.23, NodeUtil.getNumberValue(getNode("'-123E-2'"))); + assertEquals(1.23, NodeUtil.getNumberValue(getNode("'+123e-2'"))); + assertEquals(1.23, NodeUtil.getNumberValue(getNode("'+123E-2'"))); + assertEquals(12300.0, NodeUtil.getNumberValue(getNode("'+123e+2'"))); + assertEquals(12300.0, NodeUtil.getNumberValue(getNode("'+123E+2'"))); + + assertEquals(15.0, NodeUtil.getNumberValue(getNode("'0xf'"))); + assertEquals(15.0, NodeUtil.getNumberValue(getNode("'0xF'"))); + + // Chrome and rhino behavior differently from FF and IE. FF and IE + // consider a negative hex number to be invalid + assertEquals(null, NodeUtil.getNumberValue(getNode("'-0xf'"))); + assertEquals(null, NodeUtil.getNumberValue(getNode("'-0xF'"))); + assertEquals(null, NodeUtil.getNumberValue(getNode("'+0xf'"))); + assertEquals(null, NodeUtil.getNumberValue(getNode("'+0xF'"))); + + assertEquals(16.0, NodeUtil.getNumberValue(getNode("'0X10'"))); + assertEquals(Double.NaN, NodeUtil.getNumberValue(getNode("'0X10.8'"))); + assertEquals(77.0, NodeUtil.getNumberValue(getNode("'077'"))); + assertEquals(-77.0, NodeUtil.getNumberValue(getNode("'-077'"))); + assertEquals(-77.5, NodeUtil.getNumberValue(getNode("'-077.5'"))); + assertEquals( + Double.NEGATIVE_INFINITY, + NodeUtil.getNumberValue(getNode("'-Infinity'"))); + assertEquals( + Double.POSITIVE_INFINITY, + NodeUtil.getNumberValue(getNode("'Infinity'"))); + assertEquals( + Double.POSITIVE_INFINITY, + NodeUtil.getNumberValue(getNode("'+Infinity'"))); + // Firefox treats "infinity" as "Infinity", IE treats it as NaN + assertEquals(null, NodeUtil.getNumberValue(getNode("'-infinity'"))); + assertEquals(null, NodeUtil.getNumberValue(getNode("'infinity'"))); + assertEquals(null, NodeUtil.getNumberValue(getNode("'+infinity'"))); + + assertEquals(Double.NaN, NodeUtil.getNumberValue(getNode("'NaN'"))); + assertEquals( + Double.NaN, NodeUtil.getNumberValue(getNode("'some unknown string'"))); + assertEquals(Double.NaN, NodeUtil.getNumberValue(getNode("'123 blah'"))); + + // Literals + assertEquals(1.0, NodeUtil.getNumberValue(getNode("1"))); + // "-1" is parsed as a literal + assertEquals(-1.0, NodeUtil.getNumberValue(getNode("-1"))); + // "+1" is parse as an op + literal + assertEquals(null, NodeUtil.getNumberValue(getNode("+1"))); + assertEquals(22.0, NodeUtil.getNumberValue(getNode("22"))); + assertEquals(18.0, NodeUtil.getNumberValue(getNode("022"))); + assertEquals(34.0, NodeUtil.getNumberValue(getNode("0x22"))); + + assertEquals( + 1.0, NodeUtil.getNumberValue(getNode("true"))); + assertEquals( + 0.0, NodeUtil.getNumberValue(getNode("false"))); + assertEquals( + 0.0, NodeUtil.getNumberValue(getNode("null"))); + assertEquals( + Double.NaN, NodeUtil.getNumberValue(getNode("void 0"))); + assertEquals( + Double.NaN, NodeUtil.getNumberValue(getNode("void f"))); + // values with side-effects are ignored. + assertEquals( + null, NodeUtil.getNumberValue(getNode("void f()"))); + assertEquals( + Double.NaN, NodeUtil.getNumberValue(getNode("NaN"))); + assertEquals( + Double.POSITIVE_INFINITY, + NodeUtil.getNumberValue(getNode("Infinity"))); + assertEquals( + Double.NEGATIVE_INFINITY, + NodeUtil.getNumberValue(getNode("-Infinity"))); + + // "infinity" is not a known name. + assertEquals(null, NodeUtil.getNumberValue(getNode("infinity"))); + assertEquals(null, NodeUtil.getNumberValue(getNode("-infinity"))); + + // getNumberValue only converts literals + assertEquals(null, NodeUtil.getNumberValue(getNode("x"))); + assertEquals(null, NodeUtil.getNumberValue(getNode("x.y"))); + assertEquals(null, NodeUtil.getNumberValue(getNode("1/2"))); + assertEquals(null, NodeUtil.getNumberValue(getNode("1-2"))); + assertEquals(null, NodeUtil.getNumberValue(getNode("+1"))); + } + + public void testIsNumbericResult() { + assertTrue(NodeUtil.isNumericResult(getNode("1"))); + assertFalse(NodeUtil.isNumericResult(getNode("true"))); + assertTrue(NodeUtil.isNumericResult(getNode("+true"))); + assertTrue(NodeUtil.isNumericResult(getNode("+1"))); + assertTrue(NodeUtil.isNumericResult(getNode("-1"))); + assertTrue(NodeUtil.isNumericResult(getNode("-Infinity"))); + assertTrue(NodeUtil.isNumericResult(getNode("Infinity"))); + assertTrue(NodeUtil.isNumericResult(getNode("NaN"))); + assertFalse(NodeUtil.isNumericResult(getNode("undefined"))); + assertFalse(NodeUtil.isNumericResult(getNode("void 0"))); + + assertTrue(NodeUtil.isNumericResult(getNode("a << b"))); + assertTrue(NodeUtil.isNumericResult(getNode("a >> b"))); + assertTrue(NodeUtil.isNumericResult(getNode("a >>> b"))); + + assertFalse(NodeUtil.isNumericResult(getNode("a == b"))); + assertFalse(NodeUtil.isNumericResult(getNode("a != b"))); + assertFalse(NodeUtil.isNumericResult(getNode("a === b"))); + assertFalse(NodeUtil.isNumericResult(getNode("a !== b"))); + assertFalse(NodeUtil.isNumericResult(getNode("a < b"))); + assertFalse(NodeUtil.isNumericResult(getNode("a > b"))); + assertFalse(NodeUtil.isNumericResult(getNode("a <= b"))); + assertFalse(NodeUtil.isNumericResult(getNode("a >= b"))); + assertFalse(NodeUtil.isNumericResult(getNode("a in b"))); + assertFalse(NodeUtil.isNumericResult(getNode("a instanceof b"))); + + assertFalse(NodeUtil.isNumericResult(getNode("'a'"))); + assertFalse(NodeUtil.isNumericResult(getNode("'a'+b"))); + assertFalse(NodeUtil.isNumericResult(getNode("a+'b'"))); + assertFalse(NodeUtil.isNumericResult(getNode("a+b"))); + assertFalse(NodeUtil.isNumericResult(getNode("a()"))); + assertFalse(NodeUtil.isNumericResult(getNode("''.a"))); + assertFalse(NodeUtil.isNumericResult(getNode("a.b"))); + assertFalse(NodeUtil.isNumericResult(getNode("a.b()"))); + assertFalse(NodeUtil.isNumericResult(getNode("a().b()"))); + assertFalse(NodeUtil.isNumericResult(getNode("new a()"))); + + // Definitely not numeric + assertFalse(NodeUtil.isNumericResult(getNode("([1,2])"))); + assertFalse(NodeUtil.isNumericResult(getNode("({a:1})"))); + + // Recurse into the expression when necessary. + assertTrue(NodeUtil.isNumericResult(getNode("1 && 2"))); + assertTrue(NodeUtil.isNumericResult(getNode("1 || 2"))); + assertTrue(NodeUtil.isNumericResult(getNode("a ? 2 : 3"))); + assertTrue(NodeUtil.isNumericResult(getNode("a,1"))); + assertTrue(NodeUtil.isNumericResult(getNode("a=1"))); + } + + public void testIsBooleanResult() { + assertFalse(NodeUtil.isBooleanResult(getNode("1"))); + assertTrue(NodeUtil.isBooleanResult(getNode("true"))); + assertFalse(NodeUtil.isBooleanResult(getNode("+true"))); + assertFalse(NodeUtil.isBooleanResult(getNode("+1"))); + assertFalse(NodeUtil.isBooleanResult(getNode("-1"))); + assertFalse(NodeUtil.isBooleanResult(getNode("-Infinity"))); + assertFalse(NodeUtil.isBooleanResult(getNode("Infinity"))); + assertFalse(NodeUtil.isBooleanResult(getNode("NaN"))); + assertFalse(NodeUtil.isBooleanResult(getNode("undefined"))); + assertFalse(NodeUtil.isBooleanResult(getNode("void 0"))); + + assertFalse(NodeUtil.isBooleanResult(getNode("a << b"))); + assertFalse(NodeUtil.isBooleanResult(getNode("a >> b"))); + assertFalse(NodeUtil.isBooleanResult(getNode("a >>> b"))); + + assertTrue(NodeUtil.isBooleanResult(getNode("a == b"))); + assertTrue(NodeUtil.isBooleanResult(getNode("a != b"))); + assertTrue(NodeUtil.isBooleanResult(getNode("a === b"))); + assertTrue(NodeUtil.isBooleanResult(getNode("a !== b"))); + assertTrue(NodeUtil.isBooleanResult(getNode("a < b"))); + assertTrue(NodeUtil.isBooleanResult(getNode("a > b"))); + assertTrue(NodeUtil.isBooleanResult(getNode("a <= b"))); + assertTrue(NodeUtil.isBooleanResult(getNode("a >= b"))); + assertTrue(NodeUtil.isBooleanResult(getNode("a in b"))); + assertTrue(NodeUtil.isBooleanResult(getNode("a instanceof b"))); + + assertFalse(NodeUtil.isBooleanResult(getNode("'a'"))); + assertFalse(NodeUtil.isBooleanResult(getNode("'a'+b"))); + assertFalse(NodeUtil.isBooleanResult(getNode("a+'b'"))); + assertFalse(NodeUtil.isBooleanResult(getNode("a+b"))); + assertFalse(NodeUtil.isBooleanResult(getNode("a()"))); + assertFalse(NodeUtil.isBooleanResult(getNode("''.a"))); + assertFalse(NodeUtil.isBooleanResult(getNode("a.b"))); + assertFalse(NodeUtil.isBooleanResult(getNode("a.b()"))); + assertFalse(NodeUtil.isBooleanResult(getNode("a().b()"))); + assertFalse(NodeUtil.isBooleanResult(getNode("new a()"))); + assertTrue(NodeUtil.isBooleanResult(getNode("delete a"))); + + // Definitely not boolean + assertFalse(NodeUtil.isBooleanResult(getNode("([true,false])"))); + assertFalse(NodeUtil.isBooleanResult(getNode("({a:true})"))); + + // These are boolean but aren't handled yet, "false" here means "unknown". + assertTrue(NodeUtil.isBooleanResult(getNode("true && false"))); + assertTrue(NodeUtil.isBooleanResult(getNode("true || false"))); + assertTrue(NodeUtil.isBooleanResult(getNode("a ? true : false"))); + assertTrue(NodeUtil.isBooleanResult(getNode("a,true"))); + assertTrue(NodeUtil.isBooleanResult(getNode("a=true"))); + assertFalse(NodeUtil.isBooleanResult(getNode("a=1"))); + } + + public void testMayBeString() { + assertFalse(NodeUtil.mayBeString(getNode("1"))); + assertFalse(NodeUtil.mayBeString(getNode("true"))); + assertFalse(NodeUtil.mayBeString(getNode("+true"))); + assertFalse(NodeUtil.mayBeString(getNode("+1"))); + assertFalse(NodeUtil.mayBeString(getNode("-1"))); + assertFalse(NodeUtil.mayBeString(getNode("-Infinity"))); + assertFalse(NodeUtil.mayBeString(getNode("Infinity"))); + assertFalse(NodeUtil.mayBeString(getNode("NaN"))); + assertFalse(NodeUtil.mayBeString(getNode("undefined"))); + assertFalse(NodeUtil.mayBeString(getNode("void 0"))); + assertFalse(NodeUtil.mayBeString(getNode("null"))); + + assertFalse(NodeUtil.mayBeString(getNode("a << b"))); + assertFalse(NodeUtil.mayBeString(getNode("a >> b"))); + assertFalse(NodeUtil.mayBeString(getNode("a >>> b"))); + + assertFalse(NodeUtil.mayBeString(getNode("a == b"))); + assertFalse(NodeUtil.mayBeString(getNode("a != b"))); + assertFalse(NodeUtil.mayBeString(getNode("a === b"))); + assertFalse(NodeUtil.mayBeString(getNode("a !== b"))); + assertFalse(NodeUtil.mayBeString(getNode("a < b"))); + assertFalse(NodeUtil.mayBeString(getNode("a > b"))); + assertFalse(NodeUtil.mayBeString(getNode("a <= b"))); + assertFalse(NodeUtil.mayBeString(getNode("a >= b"))); + assertFalse(NodeUtil.mayBeString(getNode("a in b"))); + assertFalse(NodeUtil.mayBeString(getNode("a instanceof b"))); + + assertTrue(NodeUtil.mayBeString(getNode("'a'"))); + assertTrue(NodeUtil.mayBeString(getNode("'a'+b"))); + assertTrue(NodeUtil.mayBeString(getNode("a+'b'"))); + assertTrue(NodeUtil.mayBeString(getNode("a+b"))); + assertTrue(NodeUtil.mayBeString(getNode("a()"))); + assertTrue(NodeUtil.mayBeString(getNode("''.a"))); + assertTrue(NodeUtil.mayBeString(getNode("a.b"))); + assertTrue(NodeUtil.mayBeString(getNode("a.b()"))); + assertTrue(NodeUtil.mayBeString(getNode("a().b()"))); + assertTrue(NodeUtil.mayBeString(getNode("new a()"))); + + // These can't be strings but they aren't handled yet. + assertFalse(NodeUtil.mayBeString(getNode("1 && 2"))); + assertFalse(NodeUtil.mayBeString(getNode("1 || 2"))); + assertFalse(NodeUtil.mayBeString(getNode("1 ? 2 : 3"))); + assertFalse(NodeUtil.mayBeString(getNode("1,2"))); + assertFalse(NodeUtil.mayBeString(getNode("a=1"))); + assertFalse(NodeUtil.mayBeString(getNode("1+1"))); + assertFalse(NodeUtil.mayBeString(getNode("true+true"))); + assertFalse(NodeUtil.mayBeString(getNode("null+null"))); + assertFalse(NodeUtil.mayBeString(getNode("NaN+NaN"))); + + // These are not strings but they aren't primitives either + assertTrue(NodeUtil.mayBeString(getNode("([1,2])"))); + assertTrue(NodeUtil.mayBeString(getNode("({a:1})"))); + assertTrue(NodeUtil.mayBeString(getNode("({}+1)"))); + assertTrue(NodeUtil.mayBeString(getNode("(1+{})"))); + assertTrue(NodeUtil.mayBeString(getNode("([]+1)"))); + assertTrue(NodeUtil.mayBeString(getNode("(1+[])"))); + } + + public void testValidNames() { + assertTrue(NodeUtil.isValidPropertyName("a")); + assertTrue(NodeUtil.isValidPropertyName("a3")); + assertFalse(NodeUtil.isValidPropertyName("3a")); + assertFalse(NodeUtil.isValidPropertyName("a.")); + assertFalse(NodeUtil.isValidPropertyName(".a")); + assertFalse(NodeUtil.isValidPropertyName("a.b")); + assertFalse(NodeUtil.isValidPropertyName("true")); + assertFalse(NodeUtil.isValidPropertyName("a.true")); + assertFalse(NodeUtil.isValidPropertyName("a..b")); + + assertTrue(NodeUtil.isValidSimpleName("a")); + assertTrue(NodeUtil.isValidSimpleName("a3")); + assertFalse(NodeUtil.isValidSimpleName("3a")); + assertFalse(NodeUtil.isValidSimpleName("a.")); + assertFalse(NodeUtil.isValidSimpleName(".a")); + assertFalse(NodeUtil.isValidSimpleName("a.b")); + assertFalse(NodeUtil.isValidSimpleName("true")); + assertFalse(NodeUtil.isValidSimpleName("a.true")); + assertFalse(NodeUtil.isValidSimpleName("a..b")); + + assertTrue(NodeUtil.isValidQualifiedName("a")); + assertTrue(NodeUtil.isValidQualifiedName("a3")); + assertFalse(NodeUtil.isValidQualifiedName("3a")); + assertFalse(NodeUtil.isValidQualifiedName("a.")); + assertFalse(NodeUtil.isValidQualifiedName(".a")); + assertTrue(NodeUtil.isValidQualifiedName("a.b")); + assertFalse(NodeUtil.isValidQualifiedName("true")); + assertFalse(NodeUtil.isValidQualifiedName("a.true")); + assertFalse(NodeUtil.isValidQualifiedName("a..b")); + } + + public void testGetNearestFunctionName() { + testFunctionName("(function() {})()", null); + testFunctionName("function a() {}", "a"); + testFunctionName("(function a() {})", "a"); + testFunctionName("({a:function () {}})", "a"); + testFunctionName("({get a() {}})", "a"); + testFunctionName("({set a(b) {}})", "a"); + testFunctionName("({set a(b) {}})", "a"); + testFunctionName("({1:function () {}})", "1"); + testFunctionName("var a = function a() {}", "a"); + testFunctionName("var a;a = function a() {}", "a"); + testFunctionName("var o;o.a = function a() {}", "o.a"); + testFunctionName("this.a = function a() {}", "this.a"); + } + + public void testGetBestLValue() { + assertEquals("x", getFunctionLValue("var x = function() {};")); + assertEquals("x", getFunctionLValue("x = function() {};")); + assertEquals("x", getFunctionLValue("function x() {};")); + assertEquals("x", getFunctionLValue("var x = y ? z : function() {};")); + assertEquals("x", getFunctionLValue("var x = y ? function() {} : z;")); + assertEquals("x", getFunctionLValue("var x = y && function() {};")); + assertEquals("x", getFunctionLValue("var x = y || function() {};")); + assertEquals("x", getFunctionLValue("var x = (y, function() {});")); + } + + public void testIsNaN() { + assertEquals(true, NodeUtil.isNaN(getNode("NaN"))); + assertEquals(false, NodeUtil.isNaN(getNode("Infinity"))); + assertEquals(false, NodeUtil.isNaN(getNode("x"))); + assertEquals(true, NodeUtil.isNaN(getNode("0/0"))); + assertEquals(false, NodeUtil.isNaN(getNode("1/0"))); + assertEquals(false, NodeUtil.isNaN(getNode("0/1"))); + assertEquals(false, NodeUtil.isNaN(IR.number(0.0))); + } + + public void testIsExecutedExactlyOnce() { + assertEquals(true, executedOnceTestCase("x;")); + + assertEquals(true, executedOnceTestCase("x && 1;")); + assertEquals(false, executedOnceTestCase("1 && x;")); + + assertEquals(false, executedOnceTestCase("1 && (x && 1);")); + + assertEquals(true, executedOnceTestCase("x || 1;")); + assertEquals(false, executedOnceTestCase("1 || x;")); + + assertEquals(false, executedOnceTestCase("1 && (x || 1);")); + + assertEquals(true, executedOnceTestCase("x ? 1 : 2;")); + assertEquals(false, executedOnceTestCase("1 ? 1 : x;")); + assertEquals(false, executedOnceTestCase("1 ? x : 2;")); + + assertEquals(false, executedOnceTestCase("1 && (x ? 1 : 2);")); + + assertEquals(true, executedOnceTestCase("if (x) {}")); + assertEquals(false, executedOnceTestCase("if (true) {x;}")); + assertEquals(false, executedOnceTestCase("if (true) {} else {x;}")); + + assertEquals(false, executedOnceTestCase("if (1) { if (x) {} }")); + + assertEquals(true, executedOnceTestCase("for(x;;){}")); + assertEquals(false, executedOnceTestCase("for(;x;){}")); + assertEquals(false, executedOnceTestCase("for(;;x){}")); + assertEquals(false, executedOnceTestCase("for(;;){x;}")); + + assertEquals(false, executedOnceTestCase("if (1) { for(x;;){} }")); + + assertEquals(false, executedOnceTestCase("for(x in {}){}")); + assertEquals(true, executedOnceTestCase("for({}.a in x){}")); + assertEquals(false, executedOnceTestCase("for({}.a in {}){x}")); + + assertEquals(false, executedOnceTestCase("if (1) { for(x in {}){} }")); + + assertEquals(true, executedOnceTestCase("switch (x) {}")); + assertEquals(false, executedOnceTestCase("switch (1) {case x:}")); + assertEquals(false, executedOnceTestCase("switch (1) {case 1: x}")); + assertEquals(false, executedOnceTestCase("switch (1) {default: x}")); + + assertEquals(false, executedOnceTestCase("if (1) { switch (x) {} }")); + + assertEquals(false, executedOnceTestCase("while (x) {}")); + assertEquals(false, executedOnceTestCase("while (1) {x}")); + + assertEquals(false, executedOnceTestCase("do {} while (x)")); + assertEquals(false, executedOnceTestCase("do {x} while (1)")); + + assertEquals(false, executedOnceTestCase("try {x} catch (e) {}")); + assertEquals(false, executedOnceTestCase("try {} catch (e) {x}")); + assertEquals(true, executedOnceTestCase("try {} finally {x}")); + + assertEquals(false, executedOnceTestCase("if (1) { try {} finally {x} }")); + } + + private boolean executedOnceTestCase(String code) { + Node ast = parse(code); + Node nameNode = getNameNode(ast, "x"); + return NodeUtil.isExecutedExactlyOnce(nameNode); + } + + private String getFunctionLValue(String js) { + Node lVal = NodeUtil.getBestLValue(getFunctionNode(js)); + return lVal == null ? null : lVal.getString(); + } + + static void testFunctionName(String js, String expected) { + assertEquals( + expected, + NodeUtil.getNearestFunctionName(getFunctionNode(js))); + } + + static Node getFunctionNode(String js) { + Node root = parse(js); + return getFunctionNode(root); + } + + static Node getFunctionNode(Node n) { + if (n.isFunction()) { + return n; + } + for (Node c : n.children()) { + Node result = getFunctionNode(c); + if (result != null) { + return result; + } + } + return null; + } + + static Node getNameNode(Node n, String name) { + if (n.isName() && n.getString().equals(name)) { + return n; + } + for (Node c : n.children()) { + Node result = getNameNode(c, name); + if (result != null) { + return result; + } + } + return null; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NormalizeTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NormalizeTest.java new file mode 100644 index 0000000..4d06859 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/NormalizeTest.java @@ -0,0 +1,545 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; +import com.google.javascript.rhino.Node; + +import java.util.Set; + +/** + * @author johnlenz@google.com (John Lenz) + * + */ +public class NormalizeTest extends CompilerTestCase { + + private static final String EXTERNS = "var window;"; + + public NormalizeTest() { + super(EXTERNS); + super.enableLineNumberCheck(true); + } + + @Override + public CompilerPass getProcessor(final Compiler compiler) { + return new Normalize(compiler, false); + } + + @Override + protected int getNumRepetitions() { + // The normalize pass is only run once. + return 1; + } + + public void testSplitVar() { + testSame("var a"); + test("var a, b", + "var a; var b"); + test("var a, b, c", + "var a; var b; var c"); + testSame("var a = 0 "); + test("var a = 0 , b = foo()", + "var a = 0; var b = foo()"); + test("var a = 0, b = 1, c = 2", + "var a = 0; var b = 1; var c = 2"); + test("var a = foo(1), b = foo(2), c = foo(3)", + "var a = foo(1); var b = foo(2); var c = foo(3)"); + + // Verify vars extracted from FOR nodes are split. + test("for(var a = 0, b = foo(1), c = 1; c < b; c++) foo(2)", + "var a = 0; var b = foo(1); var c = 1; for(; c < b; c++) foo(2)"); + + // Verify split vars properly introduce blocks when needed. + test("for(;;) var b = foo(1), c = foo(2);", + "for(;;){var b = foo(1); var c = foo(2)}"); + test("for(;;){var b = foo(1), c = foo(2);}", + "for(;;){var b = foo(1); var c = foo(2)}"); + + test("try{var b = foo(1), c = foo(2);} finally foo(3);", + "try{var b = foo(1); var c = foo(2)} finally foo(3);"); + test("try{var b = foo(1),c = foo(2);} finally;", + "try{var b = foo(1); var c = foo(2)} finally;"); + test("try{foo(0);} finally var b = foo(1), c = foo(2);", + "try{foo(0);} finally {var b = foo(1); var c = foo(2)}"); + + test("switch(a) {default: var b = foo(1), c = foo(2); break;}", + "switch(a) {default: var b = foo(1); var c = foo(2); break;}"); + + test("do var a = foo(1), b; while(false);", + "do{var a = foo(1); var b} while(false);"); + test("a:var a,b,c;", + "a:{ var a;var b; var c; }"); + test("a:for(var a,b,c;;);", + "var a;var b; var c;a:for(;;);"); + test("if (true) a:var a,b;", + "if (true)a:{ var a; var b; }"); + } + + public void testDuplicateVarInExterns() { + test("var extern;", + "/** @suppress {duplicate} */ var extern = 3;", "var extern = 3;", + null, null); + } + + public void testUnhandled() { + testSame("var x = y = 1"); + } + + public void testFor() { + // Verify assignments are extracted from the FOR init node. + test("for(a = 0; a < 2 ; a++) foo();", + "a = 0; for(; a < 2 ; a++) foo()"); + // Verify vars are extracted from the FOR init node. + test("for(var a = 0; c < b ; c++) foo()", + "var a = 0; for(; c < b ; c++) foo()"); + + // Verify vars are extracted from the FOR init before the label node. + test("a:for(var a = 0; c < b ; c++) foo()", + "var a = 0; a:for(; c < b ; c++) foo()"); + // Verify vars are extracted from the FOR init before the labels node. + test("a:b:for(var a = 0; c < b ; c++) foo()", + "var a = 0; a:b:for(; c < b ; c++) foo()"); + + // Verify block are properly introduced for ifs. + test("if(x) for(var a = 0; c < b ; c++) foo()", + "if(x){var a = 0; for(; c < b ; c++) foo()}"); + + // Any other expression. + test("for(init(); a < 2 ; a++) foo();", + "init(); for(; a < 2 ; a++) foo()"); + } + + public void testForIn1() { + // Verify nothing happens with simple for-in + testSame("for(a in b) foo();"); + + // Verify vars are extracted from the FOR-IN node. + test("for(var a in b) foo()", + "var a; for(a in b) foo()"); + + // Verify vars are extracted from the FOR init before the label node. + test("a:for(var a in b) foo()", + "var a; a:for(a in b) foo()"); + // Verify vars are extracted from the FOR init before the labels node. + test("a:b:for(var a in b) foo()", + "var a; a:b:for(a in b) foo()"); + + // Verify block are properly introduced for ifs. + test("if (x) for(var a in b) foo()", + "if (x) { var a; for(a in b) foo() }"); + } + + public void testForIn2() { + // Verify vars are extracted from the FOR-IN node. + test("for(var a = foo() in b) foo()", + "var a = foo(); for(a in b) foo()"); + } + + public void testWhile() { + // Verify while loops are converted to FOR loops. + test("while(c < b) foo()", + "for(; c < b;) foo()"); + } + + public void testMoveFunctions1() throws Exception { + test("function f() { if (x) return; foo(); function foo() {} }", + "function f() {function foo() {} if (x) return; foo(); }"); + test("function f() { " + + "function foo() {} " + + "if (x) return;" + + "foo(); " + + "function bar() {} " + + "}", + "function f() {" + + "function foo() {}" + + "function bar() {}" + + "if (x) return;" + + "foo();" + + "}"); + } + + public void testMoveFunctions2() throws Exception { + testSame("function f() { function foo() {} }"); + test("function f() { f(); a:function bar() {} }", + "function f() { f(); a:{ var bar = function () {} }}"); + test("function f() { f(); {function bar() {}}}", + "function f() { f(); {var bar = function () {}}}"); + test("function f() { f(); if (true) {function bar() {}}}", + "function f() { f(); if (true) {var bar = function () {}}}"); + } + + private String inFunction(String code) { + return "(function(){" + code + "})"; + } + + private void testSameInFunction(String code) { + testSame(inFunction(code)); + } + + private void testInFunction(String code, String expected) { + test(inFunction(code), inFunction(expected)); + } + + public void testNormalizeFunctionDeclarations() throws Exception { + testSame("function f() {}"); + testSame("var f = function () {}"); + test("var f = function f() {}", + "var f = function f$$1() {}"); + testSame("var f = function g() {}"); + test("a:function g() {}", + "a:{ var g = function () {} }"); + test("{function g() {}}", + "{var g = function () {}}"); + testSame("if (function g() {}) {}"); + test("if (true) {function g() {}}", + "if (true) {var g = function () {}}"); + test("if (true) {} else {function g() {}}", + "if (true) {} else {var g = function () {}}"); + testSame("switch (function g() {}) {}"); + test("switch (1) { case 1: function g() {}}", + "switch (1) { case 1: var g = function () {}}"); + + + testSameInFunction("function f() {}"); + testInFunction("f(); a:function g() {}", + "f(); a:{ var g = function () {} }"); + testInFunction("f(); {function g() {}}", + "f(); {var g = function () {}}"); + testInFunction("f(); if (true) {function g() {}}", + "f(); if (true) {var g = function () {}}"); + testInFunction("if (true) {} else {function g() {}}", + "if (true) {} else {var g = function () {}}"); + } + + + public void testMakeLocalNamesUnique() { + if (!Normalize.MAKE_LOCAL_NAMES_UNIQUE) { + return; + } + + // Verify global names are untouched. + testSame("var a;"); + + // Verify global names are untouched. + testSame("a;"); + + // Local names are made unique. + test("var a;function foo(a){var b;a}", + "var a;function foo(a$$1){var b;a$$1}"); + test("var a;function foo(){var b;a}function boo(){var b;a}", + "var a;function foo(){var b;a}function boo(){var b$$1;a}"); + test("function foo(a){var b}" + + "function boo(a){var b}", + "function foo(a){var b}" + + "function boo(a$$1){var b$$1}"); + + // Verify function expressions are renamed. + test("var a = function foo(){foo()};var b = function foo(){foo()};", + "var a = function foo(){foo()};var b = function foo$$1(){foo$$1()};"); + + // Verify catch exceptions names are made unique + test("try { } catch(e) {e;}", + "try { } catch(e) {e;}"); + test("try { } catch(e) {e;}; try { } catch(e) {e;}", + "try { } catch(e) {e;}; try { } catch(e$$1) {e$$1;}"); + test("try { } catch(e) {e; try { } catch(e) {e;}};", + "try { } catch(e) {e; try { } catch(e$$1) {e$$1;} }; "); + + // Verify the 1st global redefinition of extern definition is not removed. + test("/** @suppress {duplicate} */\nvar window;", "var window;"); + + // Verify the 2nd global redefinition of extern definition is removed. + test("/** @suppress {duplicate} */\nvar window;" + + "/** @suppress {duplicate} */\nvar window;", "var window;"); + + // Verify local masking extern made unique. + test("function f() {var window}", + "function f() {var window$$1}"); + } + + public void testRemoveDuplicateVarDeclarations1() { + test("function f() { var a; var a }", + "function f() { var a; }"); + test("function f() { var a = 1; var a = 2 }", + "function f() { var a = 1; a = 2 }"); + test("var a = 1; function f(){ var a = 2 }", + "var a = 1; function f(){ var a$$1 = 2 }"); + test("function f() { var a = 1; lable1:var a = 2 }", + "function f() { var a = 1; lable1:{a = 2}}"); + test("function f() { var a = 1; lable1:var a }", + "function f() { var a = 1; lable1:{} }"); + test("function f() { var a = 1; for(var a in b); }", + "function f() { var a = 1; for(a in b); }"); + } + + public void testRemoveDuplicateVarDeclarations2() { + test("var e = 1; function f(){ try {} catch (e) {} var e = 2 }", + "var e = 1; function f(){ try {} catch (e$$2) {} var e$$1 = 2 }"); + } + + public void testRemoveDuplicateVarDeclarations3() { + test("var f = 1; function f(){}", + "f = 1; function f(){}"); + test("var f; function f(){}", + "function f(){}"); + test("if (a) { var f = 1; } else { function f(){} }", + "if (a) { var f = 1; } else { f = function (){} }"); + + test("function f(){} var f = 1;", + "function f(){} f = 1;"); + test("function f(){} var f;", + "function f(){}"); + test("if (a) { function f(){} } else { var f = 1; }", + "if (a) { var f = function (){} } else { f = 1; }"); + + // TODO(johnlenz): Do we need to handle this differently for "third_party" + // mode? Remove the previous function definitions? + test("function f(){} function f(){}", + "function f(){} function f(){}", + SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR); + test("if (a) { function f(){} } else { function f(){} }", + "if (a) { var f = function (){} } else { f = function (){} }"); + } + + public void testRenamingConstants() { + test("var ACONST = 4;var b = ACONST;", + "var ACONST = 4; var b = ACONST;"); + + test("var a, ACONST = 4;var b = ACONST;", + "var a; var ACONST = 4; var b = ACONST;"); + + test("var ACONST; ACONST = 4; var b = ACONST;", + "var ACONST; ACONST = 4;" + + "var b = ACONST;"); + + test("var ACONST = new Foo(); var b = ACONST;", + "var ACONST = new Foo(); var b = ACONST;"); + + test("/** @const */var aa; aa=1;", "var aa;aa=1"); + } + + public void testSkipRenamingExterns() { + test("var EXTERN; var ext; ext.FOO;", "var b = EXTERN; var c = ext.FOO", + "var b = EXTERN; var c = ext.FOO", null, null); + } + + public void testIssue166a() { + test("try { throw 1 } catch(e) { /** @suppress {duplicate} */ var e=2 }", + "try { throw 1 } catch(e) { var e=2 }", + Normalize.CATCH_BLOCK_VAR_ERROR); + } + + public void testIssue166b() { + test("function a() {" + + "try { throw 1 } catch(e) { /** @suppress {duplicate} */ var e=2 }" + + "};", + "function a() {" + + "try { throw 1 } catch(e) { var e=2 }" + + "}", + Normalize.CATCH_BLOCK_VAR_ERROR); + } + + public void testIssue166c() { + test("var e = 0; try { throw 1 } catch(e) {" + + "/** @suppress {duplicate} */ var e=2 }", + "var e = 0; try { throw 1 } catch(e) { var e=2 }", + Normalize.CATCH_BLOCK_VAR_ERROR); + } + + public void testIssue166d() { + test("function a() {" + + "var e = 0; try { throw 1 } catch(e) {" + + "/** @suppress {duplicate} */ var e=2 }" + + "};", + "function a() {" + + "var e = 0; try { throw 1 } catch(e) { var e=2 }" + + "}", + Normalize.CATCH_BLOCK_VAR_ERROR); + } + + public void testIssue166e() { + test("var e = 2; try { throw 1 } catch(e) {}", + "var e = 2; try { throw 1 } catch(e$$1) {}"); + } + + public void testIssue166f() { + test("function a() {" + + "var e = 2; try { throw 1 } catch(e) {}" + + "}", + "function a() {" + + "var e = 2; try { throw 1 } catch(e$$1) {}" + + "}"); + } + + public void testIssue() { + super.allowExternsChanges(true); + test("var a,b,c; var a,b", "a(), b()", "a(), b()", null, null); + } + + public void testNormalizeSyntheticCode() { + Compiler compiler = new Compiler(); + compiler.init( + Lists.newArrayList(), + Lists.newArrayList(), new CompilerOptions()); + Node code = Normalize.parseAndNormalizeSyntheticCode( + compiler, "function f(x) {} function g(x) {}", "prefix_"); + assertEquals( + "function f(x$$prefix_0){}function g(x$$prefix_1){}", + compiler.toSource(code)); + } + + public void testIsConstant() throws Exception { + testSame("var CONST = 3; var b = CONST;"); + Node n = getLastCompiler().getRoot(); + + Set constantNodes = findNodesWithProperty(n, Node.IS_CONSTANT_NAME); + assertEquals(2, constantNodes.size()); + for (Node hasProp : constantNodes) { + assertEquals("CONST", hasProp.getString()); + } + } + + public void testPropertyIsConstant1() throws Exception { + testSame("var a = {};a.CONST = 3; var b = a.CONST;"); + Node n = getLastCompiler().getRoot(); + + Set constantNodes = findNodesWithProperty(n, Node.IS_CONSTANT_NAME); + assertEquals(2, constantNodes.size()); + for (Node hasProp : constantNodes) { + assertEquals("CONST", hasProp.getString()); + } + } + + public void testPropertyIsConstant2() throws Exception { + testSame("var a = {CONST: 3}; var b = a.CONST;"); + Node n = getLastCompiler().getRoot(); + + Set constantNodes = findNodesWithProperty(n, Node.IS_CONSTANT_NAME); + assertEquals(2, constantNodes.size()); + for (Node hasProp : constantNodes) { + assertEquals("CONST", hasProp.getString()); + } + } + + public void testGetterPropertyIsConstant() throws Exception { + testSame("var a = { get CONST() {return 3} }; " + + "var b = a.CONST;"); + Node n = getLastCompiler().getRoot(); + + Set constantNodes = findNodesWithProperty(n, Node.IS_CONSTANT_NAME); + assertEquals(2, constantNodes.size()); + for (Node hasProp : constantNodes) { + assertEquals("CONST", hasProp.getString()); + } + } + + public void testSetterPropertyIsConstant() throws Exception { + // Verifying that a SET is properly annotated. + testSame("var a = { set CONST(b) {throw 'invalid'} }; " + + "var c = a.CONST;"); + Node n = getLastCompiler().getRoot(); + + Set constantNodes = findNodesWithProperty(n, Node.IS_CONSTANT_NAME); + assertEquals(2, constantNodes.size()); + for (Node hasProp : constantNodes) { + assertEquals("CONST", hasProp.getString()); + } + } + + public void testExposeSimple() { + test("var x = {}; /** @expose */ x.y = 3; x.y = 5;", + "var x = {}; x['y'] = 3; x['y'] = 5;"); + } + + public void testExposeComplex() { + test( + "var x = {/** @expose */ a: 1, b: 2};" + + "x.a = 3; /** @expose */ x.b = 5;", + "var x = {'a': 1, 'b': 2};" + + "x['a'] = 3; x['b'] = 5;"); + } + + private Set findNodesWithProperty(Node root, final int prop) { + final Set set = Sets.newHashSet(); + NodeTraversal.traverse( + getLastCompiler(), root, new AbstractPostOrderCallback() { + @Override + public void visit(NodeTraversal t, Node node, Node parent) { + if (node.getBooleanProp(prop)) { + set.add(node); + } + } + }); + return set; + } + + public void testRenamingConstantProperties() { + // In order to detect that foo.BAR is a constant, we need collapse + // properties to run first so that we can tell if the initial value is + // non-null and immutable. + new WithCollapse().testConstantProperties(); + } + + private class WithCollapse extends CompilerTestCase { + WithCollapse() { + enableNormalize(); + } + + private void testConstantProperties() { + test("var a={}; a.ACONST = 4;var b = a.ACONST;", + "var a$ACONST = 4; var b = a$ACONST;"); + + test("var a={b:{}}; a.b.ACONST = 4;var b = a.b.ACONST;", + "var a$b$ACONST = 4;var b = a$b$ACONST;"); + + test("var a = {FOO: 1};var b = a.FOO;", + "var a$FOO = 1; var b = a$FOO;"); + + test("var EXTERN; var ext; ext.FOO;", "var b = EXTERN; var c = ext.FOO", + "var b = EXTERN; var c = ext.FOO", null, null); + + test("var a={}; a.ACONST = 4; var b = a.ACONST;", + "var a$ACONST = 4; var b = a$ACONST;"); + + test("var a = {}; function foo() { var d = a.CONST; };" + + "(function(){a.CONST=4})();", + "var a$CONST;function foo(){var d = a$CONST;};" + + "(function(){a$CONST = 4})();"); + + test("var a = {}; a.ACONST = new Foo(); var b = a.ACONST;", + "var a$ACONST = new Foo(); var b = a$ACONST;"); + } + + @Override + protected int getNumRepetitions() { + // The normalize pass is only run once. + return 1; + } + + @Override + public CompilerPass getProcessor(final Compiler compiler) { + return new CompilerPass() { + @Override + public void process(Node externs, Node root) { + new CollapseProperties(compiler, false, true).process(externs, root); + } + }; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ObjectPropertyStringPostprocessTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ObjectPropertyStringPostprocessTest.java new file mode 100644 index 0000000..be57ae5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ObjectPropertyStringPostprocessTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.google.javascript.jscomp; + +/** + * Tests for {@link ObjectPropertyStringPostprocess}. + * + */ +public class ObjectPropertyStringPostprocessTest extends CompilerTestCase { + @Override + protected CompilerPass getProcessor(final Compiler compiler) { + return new ObjectPropertyStringPostprocess(compiler); + } + + @Override + protected int getNumRepetitions() { + return 1; + } + + public void testFooDotBar() { + testPass("goog.global, foo.bar", "foo, 'bar'"); + } + + public void testFooGetElemBar() { + testPass("goog.global, foo[bar]", "foo, bar"); + } + + public void testFooBar() { + testPass("goog.global, foo$bar", "goog.global, 'foo$bar'"); + } + + private void testPass(String input, String expected) { + test("new JSCompiler_ObjectPropertyString(" + input + ")", + "new JSCompiler_ObjectPropertyString(" + expected + ")"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ObjectPropertyStringPreprocessTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ObjectPropertyStringPreprocessTest.java new file mode 100644 index 0000000..a5d110c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ObjectPropertyStringPreprocessTest.java @@ -0,0 +1,72 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Tests for {@link ObjectPropertyStringPreprocess} + * + */ +public class ObjectPropertyStringPreprocessTest extends CompilerTestCase { + @Override + protected CompilerPass getProcessor(final Compiler compiler) { + return new ObjectPropertyStringPreprocess(compiler); + } + + @Override + protected int getNumRepetitions() { + return 1; + } + + @Override + protected void setUp() { + super.allowExternsChanges(true); + } + + public void testDeclaration() { + test("goog.testing.ObjectPropertyString = function() {}", + "JSCompiler_ObjectPropertyString = function() {}"); + } + + public void testFooBar() { + test("new goog.testing.ObjectPropertyString(foo, 'bar')", + "new JSCompiler_ObjectPropertyString(goog.global, foo.bar)"); + } + + public void testFooPrototypeBar() { + test("new goog.testing.ObjectPropertyString(foo.prototype, 'bar')", + "new JSCompiler_ObjectPropertyString(goog.global, " + + "foo.prototype.bar)"); + } + + public void testInvalidNumArgumentsError() { + testSame(new String[] {"new goog.testing.ObjectPropertyString()"}, + ObjectPropertyStringPreprocess.INVALID_NUM_ARGUMENTS_ERROR); + } + + public void testQualifedNameExpectedError() { + testSame( + new String[] { + "new goog.testing.ObjectPropertyString(foo[a], 'bar')" + }, + ObjectPropertyStringPreprocess.QUALIFIED_NAME_EXPECTED_ERROR); + } + + public void testStringLiteralExpectedError() { + testSame(new String[] {"new goog.testing.ObjectPropertyString(foo, bar)"}, + ObjectPropertyStringPreprocess.STRING_LITERAL_EXPECTED_ERROR); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/OptimizeArgumentsArrayTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/OptimizeArgumentsArrayTest.java new file mode 100644 index 0000000..cfe4ce9 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/OptimizeArgumentsArrayTest.java @@ -0,0 +1,166 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Unit tests for {@link OptimizeArgumentsArray}. + * + */ +public class OptimizeArgumentsArrayTest extends CompilerTestCase { + + public OptimizeArgumentsArrayTest() { + /* + * arguments is a builtin variable of the javascript language and + * OptimizeArgumentsArray does not make any attempt to resolve it. However, + * I am leaving "var arguments" in the externs to emulate the current + * behavior we have for JS compilation where var arguments in defined in + * externs/es3.js as extern. + */ + super("var arguments, alert" /* Externs */); + } + + @Override + public void setUp() { + super.enableLineNumberCheck(false); + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new OptimizeArgumentsArray(compiler, "p"); + } + + public void testSimple() { + test("function foo() { alert(arguments[0]); }", + "function foo(p0) { alert(p0); }"); + } + + public void testNoVarArgs() { + testSame("function f(a,b,c) { alert(a + b + c) }"); + + test("function f(a,b,c) { alert(arguments[0]) }", + "function f(a,b,c) { alert(a) }"); + } + + public void testMissingVarArgs() { + testSame("function f() { alert(arguments[x]) }"); + } + + public void testArgumentRefOnNamedParameter() { + test("function f(a,b) { alert(arguments[0]) }", + "function f(a,b) { alert(a) }"); + } + + public void testTwoVarArgs() { + test("function foo(a) { alert(arguments[1] + arguments[2]); }", + "function foo(a, p0, p1) { alert(p0 + p1); }"); + } + + public void testTwoFourArgsTwoUsed() { + test("function foo() { alert(arguments[0] + arguments[3]); }", + "function foo(p0, p1, p2, p3) { alert(p0 + p3); }"); + } + + public void testOneRequired() { + test("function foo(req0, var_args) { alert(req0 + arguments[1]); }", + "function foo(req0, var_args) { alert(req0 + var_args); }"); + } + + public void testTwoRequiredSixthVarArgReferenced() { + test("function foo(r0, r1, var_args) {alert(r0 + r1 + arguments[5]);}", + "function foo(r0, r1, var_args, p0, p1, p2) { alert(r0 + r1 + p2); }"); + } + + public void testTwoRequiredOneOptionalFifthVarArgReferenced() { + test("function foo(r0, r1, opt_1)" + + " {alert(r0 + r1 + opt_1 + arguments[4]);}", + "function foo(r0, r1, opt_1, p0, p1)" + + " {alert(r0 + r1 + opt_1 + p1); }"); + } + + public void testTwoRequiredTwoOptionalSixthVarArgReferenced() { + test("function foo(r0, r1, opt_1, opt_2)" + + " {alert(r0 + r1 + opt_1 + opt_2 + arguments[5]);}", + "function foo(r0, r1, opt_1, opt_2, p0, p1)" + + " {alert(r0 + r1 + opt_1 + opt_2 + p1); }"); + } + + public void testInnerFunctions() { + test("function f() { function b( ) { arguments[0] }}", + "function f() { function b(p0) { p0 }}"); + + test("function f( ) { function b() { } arguments[0] }", + "function f(p0) { function b() { } p0 }"); + + test("function f( ) { arguments[0]; function b( ) { arguments[0] }}", + "function f(p1) { p1; function b(p0) { p0 }}"); + } + + public void testInnerFunctionsWithNamedArgumentInInnerFunction() { + test("function f() { function b(x ) { arguments[1] }}", + "function f() { function b(x,p0) { p0 }}"); + + test("function f( ) { function b(x) { } arguments[0] }", + "function f(p0) { function b(x) { } p0 }"); + + test("function f( ) { arguments[0]; function b(x ) { arguments[1] }}", + "function f(p1) { p1; function b(x,p0) { p0 }}"); + } + + public void testInnerFunctionsWithNamedArgumentInOutterFunction() { + test("function f(x) { function b( ) { arguments[0] }}", + "function f(x) { function b(p0) { p0 }}"); + + test("function f(x ) { function b() { } arguments[1] }", + "function f(x,p0) { function b() { } p0 }"); + + test("function f(x ) { arguments[1]; function b( ) { arguments[0] }}", + "function f(x,p1) { p1; function b(p0) { p0 }}"); + } + + public void testInnerFunctionsWithNamedArgumentInInnerAndOutterFunction() { + test("function f(x) { function b(x ) { arguments[1] }}", + "function f(x) { function b(x,p0) { p0 }}"); + + test("function f(x ) { function b(x) { } arguments[1] }", + "function f(x,p0) { function b(x) { } p0 }"); + + test("function f(x ) { arguments[1]; function b(x ) { arguments[1] }}", + "function f(x,p1) { p1; function b(x,p0) { p0 }}"); + } + + public void testInnerFunctionsAfterArguments() { + // This caused a bug earlier due to incorrect push and pop of the arguments + // access stack. + test("function f( ) { arguments[0]; function b() { function c() { }} }", + "function f(p0) { p0; function b() { function c() { }} }"); + } + + public void testNoOptimizationWhenGetProp() { + testSame("function f() { arguments[0]; arguments.size }"); + } + + public void testNoOptimizationWhenIndexIsNotNumberConstant() { + testSame("function f() { arguments[0]; arguments['callee'].length}"); + testSame("function f() { arguments[0]; arguments.callee.length}"); + testSame( + "function f() { arguments[0]; var x = 'callee'; arguments[x].length}"); + } + + public void testNoOptimizationWhenArgumentIsUsedAsFunctionCall() { + testSame("function f() {arguments[0]()}"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/OptimizeCallsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/OptimizeCallsTest.java new file mode 100644 index 0000000..884c3a1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/OptimizeCallsTest.java @@ -0,0 +1,64 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + * Unit tests for {#link {@link OptimizeCalls} + * + */ +public class OptimizeCallsTest extends CompilerTestCase { + + @Override + protected CompilerPass getProcessor(final Compiler compiler) { + final OptimizeCalls passes = new OptimizeCalls(compiler); + passes.addPass(new OptimizeReturns(compiler)); + passes.addPass(new OptimizeParameters(compiler)); + passes.addPass(new RemoveUnusedVars(compiler, true, false, true)); + return new CompilerPass() { + + @Override + public void process(Node externs, Node root) { + new PureFunctionIdentifier(compiler, + new SimpleDefinitionFinder(compiler)).process(externs, root); + passes.process(externs, root); + } + }; + } + + public void testRemovingReturnCallToFunctionWithUnusedParams() { + test("function foo() {var x; return x = bar(1)} foo(); function bar(x) {}", + "function foo() { bar(); return;} foo(); function bar() {}"); + } + + public void testNestingFunctionCallWithUnsedParams() { + test("function f1(x) { } function f2(x) { }" + + "function f3(x) { } function f4(x) { }" + + "f3(f1(f2()));", + "function f1() {f2()} function f2() { }" + + "function f3() {f1()} " + + "f3();" + ); + } + + public void testUnusedAssignOnFunctionWithUnusedParams() { + test("var foo = function(a){}; function bar(){var x;x = foo} bar(); foo(1)", + "var foo = function( ){}; function bar(){ } bar(); foo()"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/OptimizeParametersTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/OptimizeParametersTest.java new file mode 100644 index 0000000..14eb3a3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/OptimizeParametersTest.java @@ -0,0 +1,620 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.google.javascript.jscomp; + +/** + * Tests for {@link OptimizeParameters} + * + */ +public class OptimizeParametersTest extends CompilerTestCase { + @Override + public CompilerPass getProcessor(Compiler compiler) { + return new OptimizeParameters(compiler); + } + + @Override + public void setUp() { + super.enableNormalize(); + super.enableLineNumberCheck(false); + } + + public void testNoRemoval() { + testSame("function foo(p1) { } foo(1); foo(2)"); + testSame("function foo(p1) { } foo(1,2); foo(3,4)"); + } + + public void testSimpleRemoval() { + test("function foo(p1) { } foo(); foo()", + "function foo() {var p1;} foo(); foo()"); + test("function foo(p1) { } foo(1); foo(1)", + "function foo() {var p1 = 1;} foo(); foo()"); + test("function foo(p1) { } foo(1,2); foo(1,4)", + "function foo() {var p1 = 1;} foo(2); foo(4)"); + } + + public void testNotAFunction() { + testSame("var x = 1; x; x = 2"); + } + + public void testRemoveOneOptionalNamedFunction() { + test("function foo(p1) { } foo()", "function foo() {var p1} foo()"); + } + + public void testDifferentScopes() { + test("function f(a, b) {} f(1, 2); f(1, 3); " + + "function h() {function g(a) {} g(4); g(5);} f(1, 2);", + "function f(b) {var a = 1} f(2); f(3); " + + "function h() {function g(a) {} g(4); g(5);} f(2);"); + } + + public void testOptimizeOnlyImmutableValues() { + test("function foo(a) {}; foo(undefined);", + "function foo() {var a = undefined}; foo()"); + test("function foo(a) {}; foo(null);", + "function foo() {var a = null}; foo()"); + test("function foo(a) {}; foo(1);", + "function foo() {var a = 1}; foo()"); + test("function foo(a) {}; foo('abc');", + "function foo() {var a = 'abc'}; foo()"); + + test("var foo = function(a) {}; foo(undefined);", + "var foo = function() {var a = undefined}; foo()"); + test("var foo = function(a) {}; foo(null);", + "var foo = function() {var a = null}; foo()"); + test("var foo = function(a) {}; foo(1);", + "var foo = function() {var a = 1}; foo()"); + test("var foo = function(a) {}; foo('abc');", + "var foo = function() {var a = 'abc'}; foo()"); + } + + public void testRemoveOneOptionalVarAssignment() { + test("var foo = function (p1) { }; foo()", + "var foo = function () {var p1}; foo()"); + } + + public void testDoOptimizeCall() { + testSame("var foo = function () {}; foo(); foo.call();"); + // TODO(johnlenz): support foo.call + testSame("var foo = function () {}; foo(); foo.call(this);"); + testSame("var foo = function (a, b) {}; foo(1); foo.call(this, 1);"); + testSame("var foo = function () {}; foo(); foo.call(null);"); + testSame("var foo = function (a, b) {}; foo(1); foo.call(null, 1);"); + + testSame("var foo = function () {}; foo.call();"); + // TODO(johnlenz): support foo.call + testSame("var foo = function () {}; foo.call(this);"); + testSame("var foo = function (a, b) {}; foo.call(this, 1);"); + testSame("var foo = function () {}; foo.call(null);"); + testSame("var foo = function (a, b) {}; foo.call(null, 1);"); + } + + public void testDoOptimizeApply() { + testSame("var foo = function () {}; foo(); foo.apply();"); + testSame("var foo = function () {}; foo(); foo.apply(this);"); + testSame("var foo = function (a, b) {}; foo(1); foo.apply(this, 1);"); + testSame("var foo = function () {}; foo(); foo.apply(null);"); + testSame("var foo = function (a, b) {}; foo(1); foo.apply(null, []);"); + + testSame("var foo = function () {}; foo.apply();"); + testSame("var foo = function () {}; foo.apply(this);"); + testSame("var foo = function (a, b) {}; foo.apply(this, 1);"); + testSame("var foo = function () {}; foo.apply(null);"); + testSame("var foo = function (a, b) {}; foo.apply(null, []);"); + } + + public void testRemoveOneOptionalExpressionAssign() { + // TODO(johnlenz): There are two definitions of "foo" here, ignore the + // one that can't be called. + testSame("var foo; foo = function (p1) { }; foo()"); + } + + public void testRemoveOneOptionalOneRequired() { + test("function foo(p1, p2) { } foo(1); foo(2)", + "function foo(p1) {var p2} foo(1); foo(2)"); + } + + public void testRemoveOneOptionalMultipleCalls() { + test( "function foo(p1, p2) { } foo(1); foo(2); foo()", + "function foo(p1) {var p2} foo(1); foo(2); foo()"); + } + + public void testRemoveOneOptionalMultiplePossibleDefinition() { + String src = "var goog = {};" + + "goog.foo = function (p1, p2) { };" + + "goog.foo = function (q1, q2) { };" + + "goog.foo = function (r1, r2) { };" + + "goog.foo(1); goog.foo(2); goog.foo()"; + + String expected = "var goog = {};" + + "goog.foo = function (p1) { var p2 };" + + "goog.foo = function (q1) { var q2 };" + + "goog.foo = function (r1) { var r2 };" + + "goog.foo(1); goog.foo(2); goog.foo()"; + // TODO(johnlenz): Support multiple valid definitions. + testSame(src); + } + + public void testRemoveTwoOptionalMultiplePossibleDefinition() { + String src = "var goog = {};" + + "goog.foo = function (p1, p2, p3, p4) { };" + + "goog.foo = function (q1, q2, q3, q4) { };" + + "goog.foo = function (r1, r2, r3, r4) { };" + + "goog.foo(1,0); goog.foo(2,1); goog.foo()"; + + String expected = "var goog = {};" + + "goog.foo = function(p1, p2) { var p4; var p3};" + + "goog.foo = function(q1, q2) { var q4; var q3};" + + "goog.foo = function(r1, r2) { var r4; var r3};" + + "goog.foo(1,0); goog.foo(2,1); goog.foo()"; + // TODO(johnlenz): Support multiple valid definitions. + testSame(src); + } + + public void testConstructorOptArgsNotRemoved() { + String src = + "/** @constructor */" + + "var goog = function(){};" + + "goog.prototype.foo = function(a,b) {};" + + "goog.prototype.bar = function(a) {};" + + "goog.bar.inherits(goog.foo);" + + "new goog.foo(2,3);" + + "new goog.foo(1,2);"; + testSame(src); + } + + public void testMultipleUnknown() { + String src = "var goog1 = {};" + + "goog1.foo = function () { };" + + "var goog2 = {};" + + "goog2.foo = function (p1) { };" + + "var x = getGoog();" + + "x.foo()"; + + String expected = "var goog1 = {};" + + "goog1.foo = function () { };" + + "var goog2 = {};" + + "goog2.foo = function () { var p1 };" + + "var x = getGoog();" + + "x.foo()"; + // TODO(johnlenz): Support multiple definitions. + testSame(src); + } + + public void testSingleUnknown() { + String src = + "var goog2 = {};" + + "goog2.foo = function (p1) { };" + + "var x = getGoog();" + + "x.foo()"; + + String expected = + "var goog2 = {};" + + "goog2.foo = function () { var p1 };" + + "var x = getGoog();" + + "x.foo()"; + test(src, expected); + } + + public void testRemoveVarArg() { + test("function foo(p1, var_args) { } foo(1); foo(2)", + "function foo(p1) { var var_args } foo(1); foo(2)"); + } + + public void testAliasMethodsDontGetOptimize() { + String src = + "var foo = function(a, b) {};" + + "var goog = {};" + + "goog.foo = foo;" + + "goog.prototype.bar = goog.foo;" + + "new goog().bar(1,2);" + + "foo(2);"; + testSame(src); + } + + public void testAliasMethodsDontGetOptimize2() { + String src = + "var foo = function(a, b) {};" + + "var bar = foo;" + + "foo(1);" + + "bar(2,3);"; + testSame(src); + } + + public void testAliasMethodsDontGetOptimize3() { + String src = + "var array = {};" + + "array[0] = function(a, b) {};" + + "var foo = array[0];" + // foo should be marked as aliased. + "foo(1);"; + testSame(src); + } + + public void testAliasMethodsDontGetOptimize4() { + // Don't change the call to baz as it has been aliased. + + test( + "function foo(bar) {};" + + "baz = function(a) {};" + + "baz(1);" + + "foo(baz);", + "function foo() {var bar = baz};" + + "baz = function(a) {};" + + "baz(1);" + + "foo();"); + } + + public void testMethodsDefinedInArraysDontGetOptimized() { + String src = + "var array = [true, function (a) {}];" + + "array[1](1)"; + testSame(src); + } + + public void testMethodsDefinedInObjectDontGetOptimized() { + String src = + "var object = { foo: function bar() {} };" + + "object.foo(1)"; + testSame(src); + src = + "var object = { foo: function bar() {} };" + + "object['foo'](1)"; + testSame(src); + } + + public void testRemoveConstantArgument() { + // Remove only one parameter + test("function foo(p1, p2) {}; foo(1,2); foo(2,2);", + "function foo(p1) {var p2 = 2}; foo(1); foo(2)"); + + // Remove nothing + testSame("function foo(p1, p2) {}; foo(1); foo(2,3);"); + + // Remove middle parameter + test("function foo(a,b,c){}; foo(1, 2, 3); foo(1, 2, 4); foo(2, 2, 3)", + "function foo(a,c){var b=2}; foo(1, 3); foo(1, 4); foo(2, 3)"); + + // Number are equals + test("function foo(a) {}; foo(1); foo(1.0);", + "function foo() {var a = 1;}; foo(); foo();"); + + // A more OO test + String src = + "/** @constructor */" + + "function Person(){}; Person.prototype.run = function(a, b) {};" + + "Person.run(1, 'a'); Person.run(2, 'a')"; + String expected = + "function Person(){}; Person.prototype.run = " + + "function(a) {var b = 'a'};" + + "Person.run(1); Person.run(2)"; + test(src, expected); + + } + + public void testCanDeleteArgumentsAtAnyPosition() { + // Argument removed in middle and end + String src = + "function foo(a,b,c,d,e) {};" + + "foo(1,2,3,4,5);" + + "foo(2,2,4,4,5);"; + String expected = + "function foo(a,c) {var b=2; var d=4; var e=5;};" + + "foo(1,3);" + + "foo(2,4);"; + test(src, expected); + } + + public void testNoOptimizationForExternsFunctions() { + testSame("function _foo(x, y, z){}; _foo(1);"); + } + + public void testNoOptimizationForGoogExportSymbol() { + testSame("goog.exportSymbol('foo', foo);" + + "function foo(x, y, z){}; foo(1);"); + } + + public void testNoArgumentRemovalNonEqualNodes() { + testSame("function foo(a){}; foo('bar'); foo('baz');"); + testSame("function foo(a){}; foo(1.0); foo(2.0);"); + testSame("function foo(a){}; foo(true); foo(false);"); + testSame("var a = 1, b = 2; function foo(a){}; foo(a); foo(b);"); + testSame("function foo(a){}; foo(/&/g); foo(/ undefined", "false"); + fold("undefined >= undefined", "false"); + fold("undefined <= undefined", "false"); + + fold("0 < undefined", "false"); + fold("true > undefined", "false"); + fold("'hi' >= undefined", "false"); + fold("null <= undefined", "false"); + + fold("undefined < 0", "false"); + fold("undefined > true", "false"); + fold("undefined >= 'hi'", "false"); + fold("undefined <= null", "false"); + + fold("null == undefined", "true"); + fold("0 == undefined", "false"); + fold("1 == undefined", "false"); + fold("'hi' == undefined", "false"); + fold("true == undefined", "false"); + fold("false == undefined", "false"); + fold("null === undefined", "false"); + fold("void 0 === undefined", "true"); + + fold("undefined == NaN", "false"); + fold("NaN == undefined", "false"); + fold("undefined == Infinity", "false"); + fold("Infinity == undefined", "false"); + fold("undefined == -Infinity", "false"); + fold("-Infinity == undefined", "false"); + fold("({}) == undefined", "false"); + fold("undefined == ({})", "false"); + fold("([]) == undefined", "false"); + fold("undefined == ([])", "false"); + fold("(/a/g) == undefined", "false"); + fold("undefined == (/a/g)", "false"); + fold("(function(){}) == undefined", "false"); + fold("undefined == (function(){})", "false"); + + fold("undefined != NaN", "true"); + fold("NaN != undefined", "true"); + fold("undefined != Infinity", "true"); + fold("Infinity != undefined", "true"); + fold("undefined != -Infinity", "true"); + fold("-Infinity != undefined", "true"); + fold("({}) != undefined", "true"); + fold("undefined != ({})", "true"); + fold("([]) != undefined", "true"); + fold("undefined != ([])", "true"); + fold("(/a/g) != undefined", "true"); + fold("undefined != (/a/g)", "true"); + fold("(function(){}) != undefined", "true"); + fold("undefined != (function(){})", "true"); + + foldSame("this == undefined"); + foldSame("x == undefined"); + } + + public void testUndefinedComparison2() { + fold("\"123\" !== void 0", "true"); + fold("\"123\" === void 0", "false"); + + fold("void 0 !== \"123\"", "true"); + fold("void 0 === \"123\"", "false"); + } + + public void testUndefinedComparison3() { + fold("\"123\" !== undefined", "true"); + fold("\"123\" === undefined", "false"); + + fold("undefined !== \"123\"", "true"); + fold("undefined === \"123\"", "false"); + } + + public void testUndefinedComparison4() { + fold("1 !== void 0", "true"); + fold("1 === void 0", "false"); + + fold("null !== void 0", "true"); + fold("null === void 0", "false"); + + fold("undefined !== void 0", "false"); + fold("undefined === void 0", "true"); + } + + public void testNullComparison1() { + fold("null == undefined", "true"); + fold("null == null", "true"); + fold("null == void 0", "true"); + + fold("null == 0", "false"); + fold("null == 1", "false"); + fold("null == 'hi'", "false"); + fold("null == true", "false"); + fold("null == false", "false"); + + fold("null === undefined", "false"); + fold("null === null", "true"); + fold("null === void 0", "false"); + + foldSame("null == this"); + foldSame("null == x"); + + fold("null != undefined", "false"); + fold("null != null", "false"); + fold("null != void 0", "false"); + + fold("null != 0", "true"); + fold("null != 1", "true"); + fold("null != 'hi'", "true"); + fold("null != true", "true"); + fold("null != false", "true"); + + fold("null !== undefined", "true"); + fold("null !== void 0", "true"); + fold("null !== null", "false"); + + foldSame("null != this"); + foldSame("null != x"); + + fold("null < null", "false"); + fold("null > null", "false"); + fold("null >= null", "true"); + fold("null <= null", "true"); + + foldSame("0 < null"); // foldable + fold("true > null", "true"); + foldSame("'hi' >= null"); // foldable + fold("null <= null", "true"); + + foldSame("null < 0"); // foldable + fold("null > true", "false"); + foldSame("null >= 'hi'"); // foldable + fold("null <= null", "true"); + + fold("null == null", "true"); + fold("0 == null", "false"); + fold("1 == null", "false"); + fold("'hi' == null", "false"); + fold("true == null", "false"); + fold("false == null", "false"); + fold("null === null", "true"); + fold("void 0 === null", "false"); + + fold("null == NaN", "false"); + fold("NaN == null", "false"); + fold("null == Infinity", "false"); + fold("Infinity == null", "false"); + fold("null == -Infinity", "false"); + fold("-Infinity == null", "false"); + fold("({}) == null", "false"); + fold("null == ({})", "false"); + fold("([]) == null", "false"); + fold("null == ([])", "false"); + fold("(/a/g) == null", "false"); + fold("null == (/a/g)", "false"); + fold("(function(){}) == null", "false"); + fold("null == (function(){})", "false"); + + fold("null != NaN", "true"); + fold("NaN != null", "true"); + fold("null != Infinity", "true"); + fold("Infinity != null", "true"); + fold("null != -Infinity", "true"); + fold("-Infinity != null", "true"); + fold("({}) != null", "true"); + fold("null != ({})", "true"); + fold("([]) != null", "true"); + fold("null != ([])", "true"); + fold("(/a/g) != null", "true"); + fold("null != (/a/g)", "true"); + fold("(function(){}) != null", "true"); + fold("null != (function(){})", "true"); + + foldSame("({a:f()}) == null"); + foldSame("null == ({a:f()})"); + foldSame("([f()]) == null"); + foldSame("null == ([f()])"); + + foldSame("this == null"); + foldSame("x == null"); + } + + public void testUnaryOps() { + // These cases are handled by PeepholeRemoveDeadCode. + foldSame("!foo()"); + foldSame("~foo()"); + foldSame("-foo()"); + + // These cases are handled here. + fold("a=!true", "a=false"); + fold("a=!10", "a=false"); + fold("a=!false", "a=true"); + fold("a=!foo()", "a=!foo()"); + fold("a=-0", "a=-0.0"); + fold("a=-(0)", "a=-0.0"); + fold("a=-Infinity", "a=-Infinity"); + fold("a=-NaN", "a=NaN"); + fold("a=-foo()", "a=-foo()"); + fold("a=~~0", "a=0"); + fold("a=~~10", "a=10"); + fold("a=~-7", "a=6"); + + fold("a=+true", "a=1"); + fold("a=+10", "a=10"); + fold("a=+false", "a=0"); + foldSame("a=+foo()"); + foldSame("a=+f"); + fold("a=+(f?true:false)", "a=+(f?1:0)"); // TODO(johnlenz): foldable + fold("a=+0", "a=0"); + fold("a=+Infinity", "a=Infinity"); + fold("a=+NaN", "a=NaN"); + fold("a=+-7", "a=-7"); + fold("a=+.5", "a=.5"); + + fold("a=~0x100000000", "a=~0x100000000", + PeepholeFoldConstants.BITWISE_OPERAND_OUT_OF_RANGE); + fold("a=~-0x100000000", "a=~-0x100000000", + PeepholeFoldConstants.BITWISE_OPERAND_OUT_OF_RANGE); + testSame("a=~.5", PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND); + } + + public void testUnaryOpsStringCompare() { + // Negatives are folded into a single number node. + assertResultString("a=-1", "a=-1"); + assertResultString("a=~0", "a=-1"); + assertResultString("a=~1", "a=-2"); + assertResultString("a=~101", "a=-102"); + } + + public void testFoldLogicalOp() { + fold("x = true && x", "x = x"); + foldSame("x = [foo()] && x"); + + fold("x = false && x", "x = false"); + fold("x = true || x", "x = true"); + fold("x = false || x", "x = x"); + fold("x = 0 && x", "x = 0"); + fold("x = 3 || x", "x = 3"); + fold("x = false || 0", "x = 0"); + + // unfoldable, because the right-side may be the result + fold("a = x && true", "a=x&&true"); + fold("a = x && false", "a=x&&false"); + fold("a = x || 3", "a=x||3"); + fold("a = x || false", "a=x||false"); + fold("a = b ? c : x || false", "a=b?c:x||false"); + fold("a = b ? x || false : c", "a=b?x||false:c"); + fold("a = b ? c : x && true", "a=b?c:x&&true"); + fold("a = b ? x && true : c", "a=b?x&&true:c"); + + // folded, but not here. + foldSame("a = x || false ? b : c"); + foldSame("a = x && true ? b : c"); + + fold("x = foo() || true || bar()", "x = foo()||true"); + fold("x = foo() || false || bar()", "x = foo()||bar()"); + fold("x = foo() || true && bar()", "x = foo()||bar()"); + fold("x = foo() || false && bar()", "x = foo()||false"); + fold("x = foo() && false && bar()", "x = foo()&&false"); + fold("x = foo() && true && bar()", "x = foo()&&bar()"); + fold("x = foo() && false || bar()", "x = foo()&&false||bar()"); + + fold("1 && b()", "b()"); + fold("a() && (1 && b())", "a() && b()"); + // TODO(johnlenz): Consider folding the following to: + // "(a(),1) && b(); + fold("(a() && 1) && b()", "(a() && 1) && b()"); + + // Really not foldable, because it would change the type of the + // expression if foo() returns something equivalent, but not + // identical, to true. Cf. FoldConstants.tryFoldAndOr(). + foldSame("x = foo() && true || bar()"); + foldSame("foo() && true || bar()"); + } + + public void testFoldBitwiseOp() { + fold("x = 1 & 1", "x = 1"); + fold("x = 1 & 2", "x = 0"); + fold("x = 3 & 1", "x = 1"); + fold("x = 3 & 3", "x = 3"); + + fold("x = 1 | 1", "x = 1"); + fold("x = 1 | 2", "x = 3"); + fold("x = 3 | 1", "x = 3"); + fold("x = 3 | 3", "x = 3"); + + fold("x = 1 ^ 1", "x = 0"); + fold("x = 1 ^ 2", "x = 3"); + fold("x = 3 ^ 1", "x = 2"); + fold("x = 3 ^ 3", "x = 0"); + + fold("x = -1 & 0", "x = 0"); + fold("x = 0 & -1", "x = 0"); + fold("x = 1 & 4", "x = 0"); + fold("x = 2 & 3", "x = 2"); + + // make sure we fold only when we are supposed to -- not when doing so would + // lose information or when it is performed on nonsensical arguments. + fold("x = 1 & 1.1", "x = 1"); + fold("x = 1.1 & 1", "x = 1"); + fold("x = 1 & 3000000000", "x = 0"); + fold("x = 3000000000 & 1", "x = 0"); + + // Try some cases with | as well + fold("x = 1 | 4", "x = 5"); + fold("x = 1 | 3", "x = 3"); + fold("x = 1 | 1.1", "x = 1"); + foldSame("x = 1 | 3E9"); + fold("x = 1 | 3000000001", "x = -1294967295"); + } + + public void testFoldBitwiseOp2() { + fold("x = y & 1 & 1", "x = y & 1"); + fold("x = y & 1 & 2", "x = y & 0"); + fold("x = y & 3 & 1", "x = y & 1"); + fold("x = 3 & y & 1", "x = y & 1"); + fold("x = y & 3 & 3", "x = y & 3"); + fold("x = 3 & y & 3", "x = y & 3"); + + fold("x = y | 1 | 1", "x = y | 1"); + fold("x = y | 1 | 2", "x = y | 3"); + fold("x = y | 3 | 1", "x = y | 3"); + fold("x = 3 | y | 1", "x = y | 3"); + fold("x = y | 3 | 3", "x = y | 3"); + fold("x = 3 | y | 3", "x = y | 3"); + + fold("x = y ^ 1 ^ 1", "x = y ^ 0"); + fold("x = y ^ 1 ^ 2", "x = y ^ 3"); + fold("x = y ^ 3 ^ 1", "x = y ^ 2"); + fold("x = 3 ^ y ^ 1", "x = y ^ 2"); + fold("x = y ^ 3 ^ 3", "x = y ^ 0"); + fold("x = 3 ^ y ^ 3", "x = y ^ 0"); + + fold("x = Infinity | NaN", "x=0"); + fold("x = 12 | NaN", "x=12"); + } + + public void testFoldingMixTypesLate() { + late = true; + fold("x = x + '2'", "x+='2'"); + fold("x = +x + +'2'", "x = +x + 2"); + fold("x = x - '2'", "x-=2"); + fold("x = x ^ '2'", "x^=2"); + fold("x = '2' ^ x", "x^=2"); + fold("x = '2' & x", "x&=2"); + fold("x = '2' | x", "x|=2"); + + fold("x = '2' | y", "x=2|y"); + fold("x = y | '2'", "x=y|2"); + fold("x = y | (a && '2')", "x=y|(a&&2)"); + fold("x = y | (a,'2')", "x=y|(a,2)"); + fold("x = y | (a?'1':'2')", "x=y|(a?1:2)"); + fold("x = y | ('x'?'1':'2')", "x=y|('x'?1:2)"); + } + + public void testFoldingMixTypesEarly() { + late = false; + foldSame("x = x + '2'"); + fold("x = +x + +'2'", "x = +x + 2"); + fold("x = x - '2'", "x = x - 2"); + fold("x = x ^ '2'", "x = x ^ 2"); + fold("x = '2' ^ x", "x = 2 ^ x"); + fold("x = '2' & x", "x = 2 & x"); + fold("x = '2' | x", "x = 2 | x"); + + fold("x = '2' | y", "x=2|y"); + fold("x = y | '2'", "x=y|2"); + fold("x = y | (a && '2')", "x=y|(a&&2)"); + fold("x = y | (a,'2')", "x=y|(a,2)"); + fold("x = y | (a?'1':'2')", "x=y|(a?1:2)"); + fold("x = y | ('x'?'1':'2')", "x=y|('x'?1:2)"); + } + + public void testFoldingAdd() { + fold("x = null + true", "x=1"); + foldSame("x = a + true"); + } + + public void testFoldBitwiseOpStringCompare() { + assertResultString("x = -1 | 0", "x=-1"); + // EXPR_RESULT case is in in PeepholeIntegrationTest + } + + public void testFoldBitShifts() { + fold("x = 1 << 0", "x = 1"); + fold("x = -1 << 0", "x = -1"); + fold("x = 1 << 1", "x = 2"); + fold("x = 3 << 1", "x = 6"); + fold("x = 1 << 8", "x = 256"); + + fold("x = 1 >> 0", "x = 1"); + fold("x = -1 >> 0", "x = -1"); + fold("x = 1 >> 1", "x = 0"); + fold("x = 2 >> 1", "x = 1"); + fold("x = 5 >> 1", "x = 2"); + fold("x = 127 >> 3", "x = 15"); + fold("x = 3 >> 1", "x = 1"); + fold("x = 3 >> 2", "x = 0"); + fold("x = 10 >> 1", "x = 5"); + fold("x = 10 >> 2", "x = 2"); + fold("x = 10 >> 5", "x = 0"); + + fold("x = 10 >>> 1", "x = 5"); + fold("x = 10 >>> 2", "x = 2"); + fold("x = 10 >>> 5", "x = 0"); + fold("x = -1 >>> 1", "x = 2147483647"); // 0x7fffffff + fold("x = -1 >>> 0", "x = 4294967295"); // 0xffffffff + fold("x = -2 >>> 0", "x = 4294967294"); // 0xfffffffe + + testSame("3000000000 << 1", + PeepholeFoldConstants.BITWISE_OPERAND_OUT_OF_RANGE); + testSame("1 << 32", + PeepholeFoldConstants.SHIFT_AMOUNT_OUT_OF_BOUNDS); + testSame("1 << -1", + PeepholeFoldConstants.SHIFT_AMOUNT_OUT_OF_BOUNDS); + testSame("3000000000 >> 1", + PeepholeFoldConstants.BITWISE_OPERAND_OUT_OF_RANGE); + testSame("1 >> 32", + PeepholeFoldConstants.SHIFT_AMOUNT_OUT_OF_BOUNDS); + testSame("1.5 << 0", + PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND); + testSame("1 << .5", + PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND); + testSame("1.5 >>> 0", + PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND); + testSame("1 >>> .5", + PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND); + testSame("1.5 >> 0", + PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND); + testSame("1 >> .5", + PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND); + } + + public void testFoldBitShiftsStringCompare() { + // Negative numbers. + assertResultString("x = -1 << 1", "x=-2"); + assertResultString("x = -1 << 8", "x=-256"); + assertResultString("x = -1 >> 1", "x=-1"); + assertResultString("x = -2 >> 1", "x=-1"); + assertResultString("x = -1 >> 0", "x=-1"); + } + + public void testStringAdd() { + fold("x = 'a' + \"bc\"", "x = \"abc\""); + fold("x = 'a' + 5", "x = \"a5\""); + fold("x = 5 + 'a'", "x = \"5a\""); + fold("x = 'a' + ''", "x = \"a\""); + fold("x = \"a\" + foo()", "x = \"a\"+foo()"); + fold("x = foo() + 'a' + 'b'", "x = foo()+\"ab\""); + fold("x = (foo() + 'a') + 'b'", "x = foo()+\"ab\""); // believe it! + fold("x = foo() + 'a' + 'b' + 'cd' + bar()", "x = foo()+\"abcd\"+bar()"); + fold("x = foo() + 2 + 'b'", "x = foo()+2+\"b\""); // don't fold! + fold("x = foo() + 'a' + 2", "x = foo()+\"a2\""); + fold("x = '' + null", "x = \"null\""); + fold("x = true + '' + false", "x = \"truefalse\""); + fold("x = '' + []", "x = ''"); // cannot fold (but nice if we can) + } + + public void testIssue821() { + foldSame("var a =(Math.random()>0.5? '1' : 2 ) + 3 + 4;"); + foldSame("var a = ((Math.random() ? 0 : 1) ||" + + "(Math.random()>0.5? '1' : 2 )) + 3 + 4;"); + } + + public void testFoldConstructor() { + fold("x = this[new String('a')]", "x = this['a']"); + fold("x = ob[new String(12)]", "x = ob['12']"); + fold("x = ob[new String(false)]", "x = ob['false']"); + fold("x = ob[new String(null)]", "x = ob['null']"); + fold("x = 'a' + new String('b')", "x = 'ab'"); + fold("x = 'a' + new String(23)", "x = 'a23'"); + fold("x = 2 + new String(1)", "x = '21'"); + foldSame("x = ob[new String(a)]"); + foldSame("x = new String('a')"); + foldSame("x = (new String('a'))[3]"); + } + + public void testFoldArithmetic() { + fold("x = 10 + 20", "x = 30"); + fold("x = 2 / 4", "x = 0.5"); + fold("x = 2.25 * 3", "x = 6.75"); + fold("z = x * y", "z = x * y"); + fold("x = y * 5", "x = y * 5"); + fold("x = 1 / 0", "x = 1 / 0"); + fold("x = 3 % 2", "x = 1"); + fold("x = 3 % -2", "x = 1"); + fold("x = -1 % 3", "x = -1"); + fold("x = 1 % 0", "x = 1 % 0"); + } + + public void testFoldArithmetic2() { + foldSame("x = y + 10 + 20"); + foldSame("x = y / 2 / 4"); + fold("x = y * 2.25 * 3", "x = y * 6.75"); + fold("z = x * y", "z = x * y"); + fold("x = y * 5", "x = y * 5"); + fold("x = y + (z * 24 * 60 * 60 * 1000)", "x = y + z * 864E5"); + } + + public void testFoldArithmetic3() { + fold("x = null * undefined", "x = NaN"); + fold("x = null * 1", "x = 0"); + fold("x = (null - 1) * 2", "x = -2"); + fold("x = (null + 1) * 2", "x = 2"); + } + + public void testFoldArithmeticInfinity() { + fold("x=-Infinity-2", "x=-Infinity"); + fold("x=Infinity-2", "x=Infinity"); + fold("x=Infinity*5", "x=Infinity"); + } + + public void testFoldArithmeticStringComp() { + // Negative Numbers. + assertResultString("x = 10 - 20", "x=-10"); + } + + public void testFoldComparison() { + fold("x = 0 == 0", "x = true"); + fold("x = 1 == 2", "x = false"); + fold("x = 'abc' == 'def'", "x = false"); + fold("x = 'abc' == 'abc'", "x = true"); + fold("x = \"\" == ''", "x = true"); + fold("x = foo() == bar()", "x = foo()==bar()"); + + fold("x = 1 != 0", "x = true"); + fold("x = 'abc' != 'def'", "x = true"); + fold("x = 'a' != 'a'", "x = false"); + + fold("x = 1 < 20", "x = true"); + fold("x = 3 < 3", "x = false"); + fold("x = 10 > 1.0", "x = true"); + fold("x = 10 > 10.25", "x = false"); + fold("x = y == y", "x = y==y"); + fold("x = y < y", "x = false"); + fold("x = y > y", "x = false"); + fold("x = 1 <= 1", "x = true"); + fold("x = 1 <= 0", "x = false"); + fold("x = 0 >= 0", "x = true"); + fold("x = -1 >= 9", "x = false"); + + fold("x = true == true", "x = true"); + fold("x = false == false", "x = true"); + fold("x = false == null", "x = false"); + fold("x = false == true", "x = false"); + fold("x = true == null", "x = false"); + + fold("0 == 0", "true"); + fold("1 == 2", "false"); + fold("'abc' == 'def'", "false"); + fold("'abc' == 'abc'", "true"); + fold("\"\" == ''", "true"); + foldSame("foo() == bar()"); + + fold("1 != 0", "true"); + fold("'abc' != 'def'", "true"); + fold("'a' != 'a'", "false"); + + fold("1 < 20", "true"); + fold("3 < 3", "false"); + fold("10 > 1.0", "true"); + fold("10 > 10.25", "false"); + foldSame("x == x"); + fold("x < x", "false"); + fold("x > x", "false"); + fold("1 <= 1", "true"); + fold("1 <= 0", "false"); + fold("0 >= 0", "true"); + fold("-1 >= 9", "false"); + + fold("true == true", "true"); + fold("false == null", "false"); + fold("false == true", "false"); + fold("true == null", "false"); + } + + // ===, !== comparison tests + public void testFoldComparison2() { + fold("x = 0 === 0", "x = true"); + fold("x = 1 === 2", "x = false"); + fold("x = 'abc' === 'def'", "x = false"); + fold("x = 'abc' === 'abc'", "x = true"); + fold("x = \"\" === ''", "x = true"); + fold("x = foo() === bar()", "x = foo()===bar()"); + + fold("x = 1 !== 0", "x = true"); + fold("x = 'abc' !== 'def'", "x = true"); + fold("x = 'a' !== 'a'", "x = false"); + + fold("x = y === y", "x = y===y"); + + fold("x = true === true", "x = true"); + fold("x = false === false", "x = true"); + fold("x = false === null", "x = false"); + fold("x = false === true", "x = false"); + fold("x = true === null", "x = false"); + + fold("0 === 0", "true"); + fold("1 === 2", "false"); + fold("'abc' === 'def'", "false"); + fold("'abc' === 'abc'", "true"); + fold("\"\" === ''", "true"); + foldSame("foo() === bar()"); + + // TODO(johnlenz): It would be nice to handle these cases as well. + foldSame("1 === '1'"); + foldSame("1 === true"); + foldSame("1 !== '1'"); + foldSame("1 !== true"); + + fold("1 !== 0", "true"); + fold("'abc' !== 'def'", "true"); + fold("'a' !== 'a'", "false"); + + foldSame("x === x"); + + fold("true === true", "true"); + fold("false === null", "false"); + fold("false === true", "false"); + fold("true === null", "false"); + } + + public void testFoldComparison3() { + fold("x = !1 == !0", "x = false"); + + fold("x = !0 == !0", "x = true"); + fold("x = !1 == !1", "x = true"); + fold("x = !1 == null", "x = false"); + fold("x = !1 == !0", "x = false"); + fold("x = !0 == null", "x = false"); + + fold("!0 == !0", "true"); + fold("!1 == null", "false"); + fold("!1 == !0", "false"); + fold("!0 == null", "false"); + + fold("x = !0 === !0", "x = true"); + fold("x = !1 === !1", "x = true"); + fold("x = !1 === null", "x = false"); + fold("x = !1 === !0", "x = false"); + fold("x = !0 === null", "x = false"); + + fold("!0 === !0", "true"); + fold("!1 === null", "false"); + fold("!1 === !0", "false"); + fold("!0 === null", "false"); + } + + public void testFoldGetElem() { + fold("x = [,10][0]", "x = void 0"); + fold("x = [10, 20][0]", "x = 10"); + fold("x = [10, 20][1]", "x = 20"); + + testSame("x = [10, 20][0.5]", + PeepholeFoldConstants.INVALID_GETELEM_INDEX_ERROR); + testSame("x = [10, 20][-1]", + PeepholeFoldConstants.INDEX_OUT_OF_BOUNDS_ERROR); + testSame("x = [10, 20][2]", + PeepholeFoldConstants.INDEX_OUT_OF_BOUNDS_ERROR); + + foldSame("x = [foo(), 0][1]"); + fold("x = [0, foo()][1]", "x = foo()"); + foldSame("x = [0, foo()][0]"); + } + + public void testFoldComplex() { + fold("x = (3 / 1.0) + (1 * 2)", "x = 5"); + fold("x = (1 == 1.0) && foo() && true", "x = foo()&&true"); + fold("x = 'abc' + 5 + 10", "x = \"abc510\""); + } + + public void testFoldLeft() { + foldSame("(+x - 1) + 2"); // not yet + fold("(+x + 1) + 2", "+x + 3"); + } + + public void testFoldArrayLength() { + // Can fold + fold("x = [].length", "x = 0"); + fold("x = [1,2,3].length", "x = 3"); + fold("x = [a,b].length", "x = 2"); + + // Not handled yet + fold("x = [,,1].length", "x = 3"); + + // Cannot fold + fold("x = [foo(), 0].length", "x = [foo(),0].length"); + fold("x = y.length", "x = y.length"); + } + + public void testFoldStringLength() { + // Can fold basic strings. + fold("x = ''.length", "x = 0"); + fold("x = '123'.length", "x = 3"); + + // Test Unicode escapes are accounted for. + fold("x = '123\u01dc'.length", "x = 4"); + } + + public void testFoldTypeof() { + fold("x = typeof 1", "x = \"number\""); + fold("x = typeof 'foo'", "x = \"string\""); + fold("x = typeof true", "x = \"boolean\""); + fold("x = typeof false", "x = \"boolean\""); + fold("x = typeof null", "x = \"object\""); + fold("x = typeof undefined", "x = \"undefined\""); + fold("x = typeof void 0", "x = \"undefined\""); + fold("x = typeof []", "x = \"object\""); + fold("x = typeof [1]", "x = \"object\""); + fold("x = typeof [1,[]]", "x = \"object\""); + fold("x = typeof {}", "x = \"object\""); + fold("x = typeof function() {}", "x = 'function'"); + + foldSame("x = typeof[1,[foo()]]"); + foldSame("x = typeof{bathwater:baby()}"); + } + + public void testFoldInstanceOf() { + // Non object types are never instances of anything. + fold("64 instanceof Object", "false"); + fold("64 instanceof Number", "false"); + fold("'' instanceof Object", "false"); + fold("'' instanceof String", "false"); + fold("true instanceof Object", "false"); + fold("true instanceof Boolean", "false"); + fold("!0 instanceof Object", "false"); + fold("!0 instanceof Boolean", "false"); + fold("false instanceof Object", "false"); + fold("null instanceof Object", "false"); + fold("undefined instanceof Object", "false"); + fold("NaN instanceof Object", "false"); + fold("Infinity instanceof Object", "false"); + + // Array and object literals are known to be objects. + fold("[] instanceof Object", "true"); + fold("({}) instanceof Object", "true"); + + // These cases is foldable, but no handled currently. + foldSame("new Foo() instanceof Object"); + // These would require type information to fold. + foldSame("[] instanceof Foo"); + foldSame("({}) instanceof Foo"); + + fold("(function() {}) instanceof Object", "true"); + + // An unknown value should never be folded. + foldSame("x instanceof Foo"); + } + + public void testDivision() { + // Make sure the 1/3 does not expand to 0.333333 + fold("print(1/3)", "print(1/3)"); + + // Decimal form is preferable to fraction form when strings are the + // same length. + fold("print(1/2)", "print(0.5)"); + } + + public void testAssignOpsLate() { + late = true; + fold("x=x+y", "x+=y"); + foldSame("x=y+x"); + fold("x=x*y", "x*=y"); + fold("x=y*x", "x*=y"); + fold("x.y=x.y+z", "x.y+=z"); + foldSame("next().x = next().x + 1"); + + fold("x=x-y", "x-=y"); + foldSame("x=y-x"); + fold("x=x|y", "x|=y"); + fold("x=y|x", "x|=y"); + fold("x=x*y", "x*=y"); + fold("x=y*x", "x*=y"); + fold("x.y=x.y+z", "x.y+=z"); + foldSame("next().x = next().x + 1"); + // This is OK, really. + fold("({a:1}).a = ({a:1}).a + 1", "({a:1}).a = 2"); + } + + public void testAssignOpsEarly() { + late = false; + foldSame("x=x+y"); + foldSame("x=y+x"); + foldSame("x=x*y"); + foldSame("x=y*x"); + foldSame("x.y=x.y+z"); + foldSame("next().x = next().x + 1"); + + foldSame("x=x-y"); + foldSame("x=y-x"); + foldSame("x=x|y"); + foldSame("x=y|x"); + foldSame("x=x*y"); + foldSame("x=y*x"); + foldSame("x.y=x.y+z"); + foldSame("next().x = next().x + 1"); + // This is OK, really. + fold("({a:1}).a = ({a:1}).a + 1", "({a:1}).a = 2"); + } + + public void testFoldAdd1() { + fold("x=false+1","x=1"); + fold("x=true+1","x=2"); + fold("x=1+false","x=1"); + fold("x=1+true","x=2"); + } + + public void testFoldLiteralNames() { + foldSame("NaN == NaN"); + foldSame("Infinity == Infinity"); + foldSame("Infinity == NaN"); + fold("undefined == NaN", "false"); + fold("undefined == Infinity", "false"); + + foldSame("Infinity >= Infinity"); + foldSame("NaN >= NaN"); + } + + public void testFoldLiteralsTypeMismatches() { + fold("true == true", "true"); + fold("true == false", "false"); + fold("true == null", "false"); + fold("false == null", "false"); + + // relational operators convert its operands + fold("null <= null", "true"); // 0 = 0 + fold("null >= null", "true"); + fold("null > null", "false"); + fold("null < null", "false"); + + fold("false >= null", "true"); // 0 = 0 + fold("false <= null", "true"); + fold("false > null", "false"); + fold("false < null", "false"); + + fold("true >= null", "true"); // 1 > 0 + fold("true <= null", "false"); + fold("true > null", "true"); + fold("true < null", "false"); + + fold("true >= false", "true"); // 1 > 0 + fold("true <= false", "false"); + fold("true > false", "true"); + fold("true < false", "false"); + } + + public void testFoldLeftChildConcat() { + foldSame("x +5 + \"1\""); + fold("x+\"5\" + \"1\"", "x + \"51\""); + // fold("\"a\"+(c+\"b\")","\"a\"+c+\"b\""); + fold("\"a\"+(\"b\"+c)","\"ab\"+c"); + } + + public void testFoldLeftChildOp() { + fold("x * Infinity * 2", "x * Infinity"); + foldSame("x - Infinity - 2"); // want "x-Infinity" + foldSame("x - 1 + Infinity"); + foldSame("x - 2 + 1"); + foldSame("x - 2 + 3"); + foldSame("1 + x - 2 + 1"); + foldSame("1 + x - 2 + 3"); + foldSame("1 + x - 2 + 3 - 1"); + foldSame("f(x)-0"); + foldSame("x-0-0"); + foldSame("x+2-2+2"); + foldSame("x+2-2+2-2"); + foldSame("x-2+2"); + foldSame("x-2+2-2"); + foldSame("x-2+2-2+2"); + + foldSame("1+x-0-NaN"); + foldSame("1+f(x)-0-NaN"); + foldSame("1+x-0+NaN"); + foldSame("1+f(x)-0+NaN"); + + foldSame("1+x+NaN"); // unfoldable + foldSame("x+2-2"); // unfoldable + foldSame("x+2"); // nothing to do + foldSame("x-2"); // nothing to do + } + + public void testFoldSimpleArithmeticOp() { + foldSame("x*NaN"); + foldSame("NaN/y"); + foldSame("f(x)-0"); + foldSame("f(x)*1"); + foldSame("1*f(x)"); + foldSame("0+a+b"); + foldSame("0-a-b"); + foldSame("a+b-0"); + foldSame("(1+x)*NaN"); + + foldSame("(1+f(x))*NaN"); // don't fold side-effects + } + + public void testFoldLiteralsAsNumbers() { + fold("x/'12'","x/12"); + fold("x/('12'+'6')", "x/126"); + fold("true*x", "1*x"); + fold("x/false", "x/0"); // should we add an error check? :) + } + + public void testNotFoldBackToTrueFalse() { + late = false; + fold("!0", "true"); + fold("!1", "false"); + fold("!3", "false"); + + late = true; + foldSame("!0"); + foldSame("!1"); + fold("!3", "false"); + foldSame("false"); + foldSame("true"); + } + + public void testFoldBangConstants() { + fold("1 + !0", "2"); + fold("1 + !1", "1"); + fold("'a ' + !1", "'a false'"); + fold("'a ' + !0", "'a true'"); + } + + public void testFoldMixed() { + fold("''+[1]", "'1'"); + foldSame("false+[]"); // would like: "\"false\"" + } + + public void testFoldVoid() { + foldSame("void 0"); + fold("void 1", "void 0"); + fold("void x", "void 0"); + fold("void x()", "void x()"); + } + + public void testObjectLiteral() { + test("(!{})", "false"); + test("(!{a:1})", "false"); + testSame("(!{a:foo()})"); + testSame("(!{'a':foo()})"); + } + + public void testArrayLiteral() { + test("(![])", "false"); + test("(![1])", "false"); + test("(![a])", "false"); + testSame("(![foo()])"); + } + + public void testIssue601() { + testSame("'\\v' == 'v'"); + testSame("'v' == '\\v'"); + testSame("'\\u000B' == '\\v'"); + } + + public void testFoldObjectLiteralRef1() { + // Leave extra side-effects in place + testSame("var x = ({a:foo(),b:bar()}).a"); + testSame("var x = ({a:1,b:bar()}).a"); + testSame("function f() { return {b:foo(), a:2}.a; }"); + + // on the LHS the object act as a temporary leave it in place. + testSame("({a:x}).a = 1"); + test("({a:x}).a += 1", "({a:x}).a = x + 1"); + testSame("({a:x}).a ++"); + testSame("({a:x}).a --"); + + // functions can't reference the object through 'this'. + testSame("({a:function(){return this}}).a"); + testSame("({get a() {return this}}).a"); + testSame("({set a(b) {return this}}).a"); + + // Leave unknown props alone, the might be on the prototype + testSame("({}).a"); + + // setters by themselves don't provide a definition + testSame("({}).a"); + testSame("({set a(b) {}}).a"); + // sets don't hide other definitions. + test("({a:1,set a(b) {}}).a", "1"); + + // get is transformed to a call (gets don't have self referential names) + test("({get a() {}}).a", "(function (){})()"); + // sets don't hide other definitions. + test("({get a() {},set a(b) {}}).a", "(function (){})()"); + + // a function remains a function not a call. + test("var x = ({a:function(){return 1}}).a", + "var x = function(){return 1}"); + + test("var x = ({a:1}).a", "var x = 1"); + test("var x = ({a:1, a:2}).a", "var x = 2"); + test("var x = ({a:1, a:foo()}).a", "var x = foo()"); + test("var x = ({a:foo()}).a", "var x = foo()"); + + test("function f() { return {a:1, b:2}.a; }", + "function f() { return 1; }"); + + // GETELEM is handled the same way. + test("var x = ({'a':1})['a']", "var x = 1"); + } + + public void testFoldObjectLiteralRef2() { + late = false; + test("({a:x}).a += 1", "({a:x}).a = x + 1"); + late = true; + testSame("({a:x}).a += 1"); + } + + public void testIEString() { + testSame("!+'\\v1'"); + } + + public void testIssue522() { + testSame("[][1] = 1;"); + } + + private static final List LITERAL_OPERANDS = + ImmutableList.of( + "null", + "undefined", + "void 0", + "true", + "false", + "!0", + "!1", + "0", + "1", + "''", + "'123'", + "'abc'", + "'def'", + "NaN", + "Infinity", + // TODO(nicksantos): Add more literals + "-Infinity" + //"({})", + // "[]" + //"[0]", + //"Object", + //"(function() {})" + ); + + public void testInvertibleOperators() { + Map inverses = ImmutableMap.builder() + .put("==", "!=") + .put("===", "!==") + .put("<=", ">") + .put("<", ">=") + .put(">=", "<") + .put(">", "<=") + .put("!=", "==") + .put("!==", "===") + .build(); + Set comparators = ImmutableSet.of("<=", "<", ">=", ">"); + Set equalitors = ImmutableSet.of("==", "==="); + Set uncomparables = ImmutableSet.of("undefined", "void 0"); + List operators = ImmutableList.copyOf(inverses.values()); + for (int iOperandA = 0; iOperandA < LITERAL_OPERANDS.size(); iOperandA++) { + for (int iOperandB = 0; + iOperandB < LITERAL_OPERANDS.size(); + iOperandB++) { + for (int iOp = 0; iOp < operators.size(); iOp++) { + String a = LITERAL_OPERANDS.get(iOperandA); + String b = LITERAL_OPERANDS.get(iOperandB); + String op = operators.get(iOp); + String inverse = inverses.get(op); + + // Test invertability. + if (comparators.contains(op) && + (uncomparables.contains(a) || uncomparables.contains(b))) { + assertSameResults(join(a, op, b), "false"); + assertSameResults(join(a, inverse, b), "false"); + } else if (a.equals(b) && equalitors.contains(op)) { + if (a.equals("NaN") || + a.equals("Infinity") || + a.equals("-Infinity")) { + foldSame(join(a, op, b)); + foldSame(join(a, inverse, b)); + } else { + assertSameResults(join(a, op, b), "true"); + assertSameResults(join(a, inverse, b), "false"); + } + } else { + assertNotSameResults(join(a, op, b), join(a, inverse, b)); + } + } + } + } + } + + public void testCommutativeOperators() { + late = true; + List operators = + ImmutableList.of( + "==", + "!=", + "===", + "!==", + "*", + "|", + "&", + "^"); + for (int iOperandA = 0; iOperandA < LITERAL_OPERANDS.size(); iOperandA++) { + for (int iOperandB = iOperandA; + iOperandB < LITERAL_OPERANDS.size(); + iOperandB++) { + for (int iOp = 0; iOp < operators.size(); iOp++) { + String a = LITERAL_OPERANDS.get(iOperandA); + String b = LITERAL_OPERANDS.get(iOperandB); + String op = operators.get(iOp); + + // Test commutativity. + // TODO(nicksantos): Eventually, all cases should be collapsed. + assertSameResultsOrUncollapsed(join(a, op, b), join(b, op, a)); + } + } + } + } + + public void testConvertToNumberNegativeInf() { + foldSame("var x = 3 * (r ? Infinity : -Infinity);"); + } + + private String join(String operandA, String op, String operandB) { + return operandA + " " + op + " " + operandB; + } + + private void assertSameResultsOrUncollapsed(String exprA, String exprB) { + String resultA = process(exprA); + String resultB = process(exprB); // TODO: why is nothing done with this? + if (resultA.equals(print(exprA))) { + foldSame(exprA); + foldSame(exprB); + } else { + assertSameResults(exprA, exprB); + } + } + + private void assertSameResults(String exprA, String exprB) { + assertEquals( + "Expressions did not fold the same\nexprA: " + + exprA + "\nexprB: " + exprB, + process(exprA), process(exprB)); + } + + private void assertNotSameResults(String exprA, String exprB) { + assertFalse( + "Expressions folded the same\nexprA: " + + exprA + "\nexprB: " + exprB, + process(exprA).equals(process(exprB))); + } + + private String process(String js) { + return printHelper(js, true); + } + + private String print(String js) { + return printHelper(js, false); + } + + private String printHelper(String js, boolean runProcessor) { + Compiler compiler = createCompiler(); + CompilerOptions options = getOptions(); + compiler.init( + ImmutableList.of(), + ImmutableList.of(SourceFile.fromCode("testcode", js)), + options); + Node root = compiler.parseInputs(); + assertTrue("Unexpected parse error(s): " + + Joiner.on("\n").join(compiler.getErrors()) + + "\nEXPR: " + js, + root != null); + Node externsRoot = root.getFirstChild(); + Node mainRoot = externsRoot.getNext(); + if (runProcessor) { + getProcessor(compiler).process(externsRoot, mainRoot); + } + return compiler.toSource(mainRoot); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeFoldWithTypesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeFoldWithTypesTest.java new file mode 100644 index 0000000..a9fb30d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeFoldWithTypesTest.java @@ -0,0 +1,91 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Tests for {@link ExternExportsPass}. + * + * @author dcc@google.com (Devin Coughlin) + */ +public class PeepholeFoldWithTypesTest extends CompilerTestCase { + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new PeepholeOptimizationsPass(compiler, new PeepholeFoldWithTypes()); + } + + @Override + public void setUp() { + enableTypeCheck(CheckLevel.WARNING); + } + + public void testFoldTypeofObject() { + test("var x = {};typeof x", + "var x = {};\"object\""); + + test("var x = [];typeof x", + "var x = [];\"object\""); + + // typeof null is "object" in JavaScript + test("var x = null;typeof x", + "var x = null;\"object\""); + } + + public void testFoldTypeofString() { + test("var x = \"foo\";typeof x", + "var x = \"foo\";\"string\""); + + test("var x = new String(\"foo\");typeof x", + "var x = new String(\"foo\");\"object\""); + } + + public void testFoldTypeofNumber() { + test("var x = 10;typeof x", + "var x = 10;\"number\""); + + test("var x = new Number(6);typeof x", + "var x = new Number(6);\"object\""); + } + + public void testFoldTypeofBoolean() { + test("var x = false;typeof x", + "var x = false;\"boolean\""); + + test("var x = new Boolean(true);typeof x", + "var x = new Boolean(true);\"object\""); + } + + public void testFoldTypeofUndefined() { + test("var x = undefined;typeof x", + "var x = undefined;\"undefined\""); + } + + public void testDontFoldTypeofUnionTypes() { + // For now we don't do anything with union types + testSame("var x = (unknown ? {} : null);typeof x"); + } + + public void testDontFoldTypeofSideEffects() { + // Shouldn't fold if argument to typeof has side effects + testSame("var x = 6 ;typeof (x++)"); + } + + public void testDontFoldTypeofWithTypeCheckDisabled() { + disableTypeCheck(); + testSame("var x = {};typeof x"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeIntegrationTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeIntegrationTest.java new file mode 100644 index 0000000..b06d42a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeIntegrationTest.java @@ -0,0 +1,392 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Tests for the interaction between multiple peephole passes. + */ +public class PeepholeIntegrationTest extends CompilerTestCase { + + private boolean late = true; + + // TODO(user): Remove this when we no longer need to do string comparison. + private PeepholeIntegrationTest(boolean compareAsTree) { + super("", compareAsTree); + } + + public PeepholeIntegrationTest() { + super(""); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + this.late = false; + enableLineNumberCheck(true); + + // TODO(nicksantos): Turn this on. There are some normalizations + // that cause weirdness here. + disableNormalize(); + } + + @Override + public CompilerPass getProcessor(final Compiler compiler) { + PeepholeOptimizationsPass peepholePass = + new PeepholeOptimizationsPass(compiler, + new PeepholeSubstituteAlternateSyntax(late), + new PeepholeRemoveDeadCode(), + new PeepholeFoldConstants(late) + ); + + return peepholePass; + } + + @Override + protected int getNumRepetitions() { + // Reduce this to 2 if we get better expression evaluators. + return 4; + } + + private void foldSame(String js) { + testSame(js); + } + + private void fold(String js, String expected) { + test(js, expected); + } + + // TODO(user): This is same as fold() except it uses string comparison. Any + // test that needs tell us where a folding is constructing an invalid AST. + private void assertResultString(String js, String expected) { + PeepholeIntegrationTest scTest = new PeepholeIntegrationTest(false); + + scTest.disableNormalize(); + + scTest.test(js, expected); + } + + public void testTrueFalse() { + late = false; + foldSame("x = true"); + foldSame("x = false"); + fold("x = !1", "x = false"); + fold("x = !0", "x = true"); + late = true; + fold("x = true", "x = !0"); + fold("x = false", "x = !1"); + foldSame("x = !1"); + foldSame("x = !0"); + } + + /** Check that removing blocks with 1 child works */ + public void testFoldOneChildBlocksIntegration() { + fold("function f(){switch(foo()){default:{break}}}", + "function f(){foo()}"); + + fold("function f(){switch(x){default:{break}}}", + "function f(){}"); + + fold("function f(){switch(x){default:x;case 1:return 2}}", + "function f(){switch(x){default:case 1:return 2}}"); + + // ensure that block folding does not break hook ifs + fold("if(x){if(true){foo();foo()}else{bar();bar()}}", + "if(x){foo();foo()}"); + + fold("if(x){if(false){foo();foo()}else{bar();bar()}}", + "if(x){bar();bar()}"); + + // Cases where the then clause has no side effects. + fold("if(x()){}", "x()"); + + fold("if(x()){} else {x()}", "x()||x()"); + fold("if(x){}", ""); // Even the condition has no side effect. + fold("if(a()){A()} else if (b()) {} else {C()}", "a()?A():b()||C()"); + + fold("if(a()){} else if (b()) {} else {C()}", + "a()||b()||C()"); + fold("if(a()){A()} else if (b()) {} else if (c()) {} else{D()}", + "a()?A():b()||c()||D()"); + fold("if(a()){} else if (b()) {} else if (c()) {} else{D()}", + "a()||b()||c()||D()"); + fold("if(a()){A()} else if (b()) {} else if (c()) {} else{}", + "a()?A():b()||c()"); + + // Verify that non-global scope works. + fold("function foo(){if(x()){}}", "function foo(){x()}"); + + } + + public void testFoldOneChildBlocksStringCompare() { + // The expected parse tree has a BLOCK structure around the true branch. + assertResultString("if(x){if(y){var x;}}else{var z;}", + "if(x){if(y)var x}else var z"); + } + + /** Test a particularly hairy edge case. */ + public void testNecessaryDanglingElse() { + // The extra block is added by CodeGenerator. The logic to avoid ambiguous + // else clauses used to be in FoldConstants, so the test is here for + // legacy reasons. + assertResultString( + "if(x)if(y){y();z()}else;else x()", "if(x){if(y){y();z()}}else x()"); + } + + /** Try to minimize returns */ + public void testFoldReturnsIntegration() { + // if-then-else duplicate statement removal handles this case: + fold("function f(){if(x)return;else return}", + "function f(){}"); + } + + public void testBug1059649() { + // ensure that folding blocks with a single var node doesn't explode + fold("if(x){var y=3;}var z=5", "if(x)var y=3;var z=5"); + + // With normalization, we no longer have this case. + foldSame("if(x){var y=3;}else{var y=4;}var z=5"); + fold("while(x){var y=3;}var z=5", "while(x)var y=3;var z=5"); + fold("for(var i=0;i<10;i++){var y=3;}var z=5", + "for(var i=0;i<10;i++)var y=3;var z=5"); + fold("for(var i in x){var y=3;}var z=5", + "for(var i in x)var y=3;var z=5"); + fold("do{var y=3;}while(x);var z=5", "do var y=3;while(x);var z=5"); + } + + public void testHookIfIntegration() { + fold("if (false){ x = 1; } else if (cond) { x = 2; } else { x = 3; }", + "x=cond?2:3"); + + fold("x?void 0:y()", "x||y()"); + fold("!x?void 0:y()", "(!x)||y()"); + fold("x?y():void 0", "x&&y()"); + } + + public void testRemoveDuplicateStatementsIntegration() { + fold("function z() {if (a) { return true }" + + "else if (b) { return true }" + + "else { return true }}", + "function z() {return true;}"); + + fold("function z() {if (a()) { return true }" + + "else if (b()) { return true }" + + "else { return true }}", + "function z() {a()||b();return true;}"); + } + + public void testFoldLogicalOpIntegration() { + test("if(x && true) z()", "x&&z()"); + test("if(x && false) z()", ""); + fold("if(x || 3) z()", "z()"); + fold("if(x || false) z()", "x&&z()"); + test("if(x==y && false) z()", ""); + // TODO(user): This can be further optimized. + fold("if(y() || x || 3) z()", "(y()||1)&&z()"); + } + + public void testFoldBitwiseOpStringCompareIntegration() { + assertResultString("while(-1 | 0){}", "while(1);"); + } + + public void testVarLiftingIntegration() { + fold("if(true);else var a;", "var a"); + fold("if(false) foo();else var a;", "var a"); + fold("if(true)var a;else;", "var a"); + fold("if(false)var a;else;", "var a"); + fold("if(false)var a,b;", "var b; var a"); + fold("if(false){var a;var a;}", "var a"); + fold("if(false)var a=function(){var b};", "var a"); + fold("if(a)if(false)var a;else var b;", "var a;if(a)var b"); + } + + public void testBug1438784() throws Exception { + fold("for(var i=0;i<10;i++)if(x)x.y;", "for(var i=0;i<10;i++);"); + } + + public void testFoldUselessWhileIntegration() { + fold("while(!true) { foo() }", ""); + fold("while(!false) foo() ", "while(1) foo()"); + fold("while(!void 0) foo()", "while(1) foo()"); + + // Make sure proper empty nodes are inserted. + fold("if(foo())while(false){foo()}else bar()", "foo()||bar()"); + } + + public void testFoldUselessForIntegration() { + fold("for(;!true;) { foo() }", ""); + fold("for(;void 0;) { foo() }", ""); + fold("for(;undefined;) { foo() }", ""); + fold("for(;1;) foo()", "for(;;) foo()"); + fold("for(;!void 0;) foo()", "for(;;) foo()"); + + // Make sure proper empty nodes are inserted. + fold("if(foo())for(;false;){foo()}else bar()", "foo()||bar()"); + } + + public void testFoldUselessDoIntegration() { + test("do { foo() } while(!true);", "foo()"); + fold("do { foo() } while(void 0);", "foo()"); + fold("do { foo() } while(undefined);", "foo()"); + fold("do { foo() } while(!void 0);", "do { foo() } while(1);"); + + // Make sure proper empty nodes are inserted. + test("if(foo())do {foo()} while(false) else bar()", "foo()?foo():bar()"); + } + + public void testMinimizeWhileConstantConditionIntegration() { + fold("while(!false) foo()", "while(1) foo()"); + fold("while(202) foo()", "while(1) foo()"); + fold("while(Infinity) foo()", "while(1) foo()"); + fold("while('text') foo()", "while(1) foo()"); + fold("while([]) foo()", "while(1) foo()"); + fold("while({}) foo()", "while(1) foo()"); + fold("while(/./) foo()", "while(1) foo()"); + } + + public void testMinimizeExpr() { + test("!!true", ""); + + fold("!!x()", "x()"); + test("!(!x()&&!y())", "x()||y()"); + fold("x()||!!y()", "x()||y()"); + + /* This is similar to the !!true case */ + fold("!!x()&&y()", "x()&&y()"); + } + + public void testBug1509085() { + PeepholeIntegrationTest oneRepetitiontest = new PeepholeIntegrationTest() { + @Override + protected int getNumRepetitions() { + return 1; + } + }; + + oneRepetitiontest.test("x ? x() : void 0", "x&&x();"); + oneRepetitiontest.foldSame("y = x ? x() : void 0"); + } + + public void testBugIssue3() { + foldSame("function foo() {" + + " if(sections.length != 1) children[i] = 0;" + + " else var selectedid = children[i]" + + "}"); + } + + public void testBugIssue43() { + foldSame("function foo() {" + + " if (a) { var b = 1; } else { a.b = 1; }" + + "}"); + } + + public void testFoldNegativeBug() { + fold("while(-3){};", "while(1);"); + } + + public void testNoNormalizeLabeledExpr() { + enableNormalize(true); + foldSame("var x; foo:{x = 3;}"); + foldSame("var x; foo:x = 3;"); + } + + public void testShortCircuit1() { + test("1 && a()", "a()"); + } + + public void testShortCircuit2() { + test("1 && a() && 2", "a()"); + } + + public void testShortCircuit3() { + test("a() && 1 && 2", "a()"); + } + + public void testShortCircuit4() { + test("a() && (1 && b())", "a() && b()"); + test("a() && 1 && b()", "a() && b()"); + test("(a() && 1) && b()", "a() && b()"); + } + + public void testMinimizeExprCondition() { + fold("(x || true) && y()", "y()"); + fold("(x || false) && y()", "x&&y()"); + fold("(x && true) && y()", "x && y()"); + fold("(x && false) && y()", ""); + fold("a = x || false ? b : c", "a=x?b:c"); + fold("do {x()} while((x && false) && y())", "x()"); + } + + public void testTrueFalseFolding() { + late = true; + fold("x = true", "x = !0"); + fold("x = false", "x = !1"); + fold("x = !3", "x = !1"); + fold("x = true && !0", "x = !0"); + fold("x = !!!!!!!!!!!!3", "x = !0"); + fold("if(!3){x()}", ""); + fold("if(!!3){x()}", "x()"); + } + + public void testCommaSplitingConstantCondition() { + late = false; + fold("(b=0,b=1);if(b)x=b;", "b=0;b=1;x=b;"); + fold("(b=0,b=1);if(b)x=b;", "b=0;b=1;x=b;"); + } + + public void testAvoidCommaSplitting() { + late = false; + fold("x(),y(),z()", "x();y();z()"); + late = true; + foldSame("x(),y(),z()"); + } + + public void testObjectLiteral() { + test("({})", ""); + test("({a:1})", ""); + test("({a:foo()})", "foo()"); + test("({'a':foo()})", "foo()"); + } + + public void testArrayLiteral() { + test("([])", ""); + test("([1])", ""); + test("([a])", ""); + test("([foo()])", "foo()"); + } + + public void testFoldIfs1() { + fold("function f() {if (x) return 1; else if (y) return 1;}", + "function f() {if (x||y) return 1;}"); + fold("function f() {if (x) return 1; else {if (y) return 1; else foo();}}", + "function f() {if (x||y) return 1; foo();}"); + } + + public void testFoldIfs2() { + fold("function f() {if (x) { a(); } else if (y) { a() }}", + "function f() {x?a():y&&a();}"); + } + + public void testFoldHook2() { + fold("function f(a) {if (!a) return a; else return a;}", + "function f(a) {return a}"); + } + + public void disable_testFoldHook1() { + fold("function f(a) {return (!a)?a:a;}", + "function f(a) {return a}"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeOptimizationsPassTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeOptimizationsPassTest.java new file mode 100644 index 0000000..d1d733f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeOptimizationsPassTest.java @@ -0,0 +1,258 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import java.util.List; +import java.util.Set; + +/** + * Unit tests for PeepholeOptimizationsPass. + * + */ +public class PeepholeOptimizationsPassTest extends CompilerTestCase { + + private ImmutableList currentPeepholePasses; + + @Override + public void setUp() throws Exception { + super.setUp(); + super.enableLineNumberCheck(true); + } + + @Override + public CompilerPass getProcessor(final Compiler compiler) { + return new PeepholeOptimizationsPass(compiler, + currentPeepholePasses.toArray( + new AbstractPeepholeOptimization[currentPeepholePasses.size()])); + } + + @Override + protected int getNumRepetitions() { + // Our tests do not require multiple passes to reach a fixed-point. + return 1; + } + + /** + * PeepholeOptimizationsPass should handle the case when no peephole + * optimizations are turned on. + */ + public void testEmptyPass() { + currentPeepholePasses = ImmutableList.of(); + + testSame("var x; var y;"); + } + + public void testOptimizationOrder() { + /* + * We need to make sure that: 1) We are only traversing the AST once 2) For + * each node, we visit the optimizations in the client-supplied order + * + * To test this, we create two fake optimizations that each make an entry in + * the visitationLog when they are passed a name node to optimize. + * + * Each entry is of the form nameX where 'name' is the name of the name node + * visited and X is the identity of the optimization (1 or 2 in this case). + * After the pass is run, we verify the correct ordering by querying the + * log. + * + * Using a log, rather than, say, transforming nodes, allows us to ensure + * not only that we are visiting each node but that our visits occur in the + * right order (i.e. we need to make sure we're not traversing the entire + * AST for the first optimization and then a second time for the second). + */ + + final List visitationLog = Lists.newArrayList(); + + AbstractPeepholeOptimization note1Applied = + new AbstractPeepholeOptimization() { + @Override + public Node optimizeSubtree(Node node) { + if (node.isName()) { + visitationLog.add(node.getString() + "1"); + } + + return node; + } + }; + + AbstractPeepholeOptimization note2Applied = + new AbstractPeepholeOptimization() { + @Override + public Node optimizeSubtree(Node node) { + if (node.isName()) { + visitationLog.add(node.getString() + "2"); + } + + return node; + } + }; + + currentPeepholePasses = + ImmutableList.< + AbstractPeepholeOptimization>of(note1Applied, note2Applied); + + test("var x; var y", "var x; var y"); + + /* + * We expect the optimization order to be: "x" visited by optimization1 "x" + * visited by optimization2 "y" visited by optimization1 "y" visited by + * optimization2 + */ + + assertEquals(4, visitationLog.size()); + assertEquals("x1", visitationLog.get(0)); + assertEquals("x2", visitationLog.get(1)); + assertEquals("y1", visitationLog.get(2)); + assertEquals("y2", visitationLog.get(3)); + } + + /** + * A peephole optimization that, given a subtree consisting of a VAR node, + * removes children of that node named "x". + */ + private static class RemoveNodesNamedXUnderVarOptimization + extends AbstractPeepholeOptimization { + @Override + public Node optimizeSubtree(Node node) { + if (node.isVar()) { + Set nodesToRemove = Sets.newHashSet(); + + for (Node child : node.children()) { + if ("x".equals(child.getString())) { + nodesToRemove.add(child); + } + } + + for (Node childToRemove : nodesToRemove) { + node.removeChild(childToRemove); + reportCodeChange(); + } + } + + return node; + } + } + + /** + * A peephole optimization that, given a subtree consisting of a name node + * named "x" removes that node. + */ + private static class RemoveNodesNamedXOptimization + extends AbstractPeepholeOptimization { + @Override + public Node optimizeSubtree(Node node) { + if (node.isName() && "x".equals(node.getString())) { + node.getParent().removeChild(node); + reportCodeChange(); + + return null; + } + + return node; + } + } + + /** + * A peephole optimization that, given a subtree consisting of a name node + * named "x" whose parent is a VAR node, removes the parent VAR node. + */ + private static class RemoveParentVarsForNodesNamedX + extends AbstractPeepholeOptimization { + @Override + public Node optimizeSubtree(Node node) { + if (node.isName() && "x".equals(node.getString())) { + Node parent = node.getParent(); + if (parent.isVar()) { + parent.getParent().removeChild(parent); + reportCodeChange(); + return null; + } + } + return node; + } + } + + /** + * A peephole optimization that, given a subtree consisting of a name node + * named "y", replaces it with a name node named "x"; + */ + private static class RenameYToX extends AbstractPeepholeOptimization { + @Override + public Node optimizeSubtree(Node node) { + if (node.isName() && "y".equals(node.getString())) { + Node replacement = Node.newString(Token.NAME, "x"); + + node.getParent().replaceChild(node, replacement); + reportCodeChange(); + + return replacement; + } + return node; + } + } + + public void testOptimizationRemovingSubtreeChild() { + currentPeepholePasses = ImmutableList.of(new + RemoveNodesNamedXUnderVarOptimization()); + + test("var x,y;", "var y;"); + test("var y,x;", "var y;"); + test("var x,y,x;", "var y;"); + } + + public void testOptimizationRemovingSubtree() { + currentPeepholePasses = ImmutableList.of(new + RemoveNodesNamedXOptimization()); + + test("var x,y;", "var y;"); + test("var y,x;", "var y;"); + test("var x,y,x;", "var y;"); + } + + public void testOptimizationRemovingSubtreeParent() { + currentPeepholePasses = ImmutableList.of(new + RemoveParentVarsForNodesNamedX()); + + test("var x; var y", "var y"); + } + + /** + * Test the case where the first peephole optimization removes a node and the + * second wants to remove (the now nonexistent) parent of that node. + */ + public void testOptimizationsRemoveParentAfterRemoveChild() { + currentPeepholePasses = ImmutableList.of( + new RemoveNodesNamedXOptimization(), + new RemoveParentVarsForNodesNamedX()); + + test("var x,y; var z;", "var y; var z;"); + } + + public void testOptimizationReplacingNode() { + currentPeepholePasses = ImmutableList.of( + new RenameYToX(), + new RemoveParentVarsForNodesNamedX()); + + test("var y; var z;", "var z;"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeRemoveDeadCodeTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeRemoveDeadCodeTest.java new file mode 100644 index 0000000..50c8532 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeRemoveDeadCodeTest.java @@ -0,0 +1,741 @@ +/* + * Copyright 2004 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +/** + * Tests for PeepholeRemoveDeadCodeTest in isolation. Tests for the interaction + * of multiple peephole passes are in PeepholeIntegrationTest. + */ +public class PeepholeRemoveDeadCodeTest extends CompilerTestCase { + + private static final String MATH = + "/** @const */ var Math = {};" + + "/** @nosideeffects */ Math.random = function(){};" + + "/** @nosideeffects */ Math.sin = function(){};"; + + public PeepholeRemoveDeadCodeTest() { + super(MATH); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + enableLineNumberCheck(true); + } + + @Override + public CompilerPass getProcessor(final Compiler compiler) { + return new CompilerPass() { + @Override + public void process(Node externs, Node root) { + SimpleDefinitionFinder definitionFinder = + new SimpleDefinitionFinder(compiler); + definitionFinder.process(externs, root); + new PureFunctionIdentifier(compiler, definitionFinder) + .process(externs, root); + PeepholeOptimizationsPass peepholePass = + new PeepholeOptimizationsPass( + compiler, new PeepholeRemoveDeadCode()); + peepholePass.process(externs, root); + } + }; + } + + @Override + protected int getNumRepetitions() { + // Reduce this to 2 if we get better expression evaluators. + return 2; + } + + private void foldSame(String js) { + testSame(js); + } + + private void fold(String js, String expected) { + test(js, expected); + } + + public void testFoldBlock() { + fold("{{foo()}}", "foo()"); + fold("{foo();{}}", "foo()"); + fold("{{foo()}{}}", "foo()"); + fold("{{foo()}{bar()}}", "foo();bar()"); + fold("{if(false)foo(); {bar()}}", "bar()"); + fold("{if(false)if(false)if(false)foo(); {bar()}}", "bar()"); + + fold("{'hi'}", ""); + fold("{x==3}", ""); + fold("{ (function(){x++}) }", ""); + fold("function f(){return;}", "function f(){return;}"); + fold("function f(){return 3;}", "function f(){return 3}"); + fold("function f(){if(x)return; x=3; return; }", + "function f(){if(x)return; x=3; return; }"); + fold("{x=3;;;y=2;;;}", "x=3;y=2"); + + // Cases to test for empty block. + fold("while(x()){x}", "while(x());"); + fold("while(x()){x()}", "while(x())x()"); + fold("for(x=0;x<100;x++){x}", "for(x=0;x<100;x++);"); + fold("for(x in y){x}", "for(x in y);"); + } + + /** Try to remove spurious blocks with multiple children */ + public void testFoldBlocksWithManyChildren() { + fold("function f() { if (false) {} }", "function f(){}"); + fold("function f() { { if (false) {} if (true) {} {} } }", + "function f(){}"); + fold("{var x; var y; var z; function f() { { var a; { var b; } } } }", + "var x;var y;var z;function f(){var a;var b}"); + } + + public void testIf() { + fold("if (1){ x=1; } else { x = 2;}", "x=1"); + fold("if (false){ x = 1; } else { x = 2; }", "x=2"); + fold("if (undefined){ x = 1; } else { x = 2; }", "x=2"); + fold("if (null){ x = 1; } else { x = 2; }", "x=2"); + fold("if (void 0){ x = 1; } else { x = 2; }", "x=2"); + fold("if (void foo()){ x = 1; } else { x = 2; }", + "foo();x=2"); + fold("if (false){ x = 1; } else if (true) { x = 3; } else { x = 2; }", + "x=3"); + fold("if (x){ x = 1; } else if (false) { x = 3; }", + "if(x)x=1"); + } + + public void testHook() { + fold("true ? a() : b()", "a()"); + fold("false ? a() : b()", "b()"); + + fold("a() ? b() : true", "a() && b()"); + fold("a() ? true : b()", "a() || b()"); + + fold("(a = true) ? b() : c()", "a = true, b()"); + fold("(a = false) ? b() : c()", "a = false, c()"); + fold("do {f()} while((a = true) ? b() : c())", + "do {f()} while((a = true) , b())"); + fold("do {f()} while((a = false) ? b() : c())", + "do {f()} while((a = false) , c())"); + + fold("var x = (true) ? 1 : 0", "var x=1"); + fold("var y = (true) ? ((false) ? 12 : (cond ? 1 : 2)) : 13", + "var y=cond?1:2"); + + foldSame("var z=x?void 0:y()"); + foldSame("z=x?void 0:y()"); + foldSame("z*=x?void 0:y()"); + + foldSame("var z=x?y():void 0"); + foldSame("(w?x:void 0).y=z"); + foldSame("(w?x:void 0).y+=z"); + + fold("y = (x ? void 0 : void 0)", "y = void 0"); + fold("y = (x ? f() : f())", "y = f()"); + } + + public void testConstantConditionWithSideEffect1() { + fold("if (b=true) x=1;", "b=true;x=1"); + fold("if (b=/ab/) x=1;", "b=/ab/;x=1"); + fold("if (b=/ab/){ x=1; } else { x=2; }", "b=/ab/;x=1"); + fold("var b;b=/ab/;if(b)x=1;", "var b;b=/ab/;x=1"); + foldSame("var b;b=f();if(b)x=1;"); + fold("var b=/ab/;if(b)x=1;", "var b=/ab/;x=1"); + foldSame("var b=f();if(b)x=1;"); + foldSame("b=b++;if(b)x=b;"); + fold("(b=0,b=1);if(b)x=b;", "b=0,b=1;if(b)x=b;"); + fold("b=1;if(foo,b)x=b;","b=1;x=b;"); + foldSame("b=1;if(foo=1,b)x=b;"); + } + + public void testConstantConditionWithSideEffect2() { + fold("(b=true)?x=1:x=2;", "b=true,x=1"); + fold("(b=false)?x=1:x=2;", "b=false,x=2"); + fold("if (b=/ab/) x=1;", "b=/ab/;x=1"); + fold("var b;b=/ab/;(b)?x=1:x=2;", "var b;b=/ab/;x=1"); + foldSame("var b;b=f();(b)?x=1:x=2;"); + fold("var b=/ab/;(b)?x=1:x=2;", "var b=/ab/;x=1"); + foldSame("var b=f();(b)?x=1:x=2;"); + } + + public void testVarLifting() { + fold("if(true)var a", "var a"); + fold("if(false)var a", "var a"); + + // More var lifting tests in PeepholeIntegrationTests + } + + public void testFoldUselessWhile() { + fold("while(false) { foo() }", ""); + + fold("while(void 0) { foo() }", ""); + fold("while(undefined) { foo() }", ""); + + foldSame("while(true) foo()"); + + fold("while(false) { var a = 0; }", "var a"); + + // Make sure it plays nice with minimizing + fold("while(false) { foo(); continue }", ""); + + fold("while(0) { foo() }", ""); + } + + public void testFoldUselessFor() { + fold("for(;false;) { foo() }", ""); + fold("for(;void 0;) { foo() }", ""); + fold("for(;undefined;) { foo() }", ""); + fold("for(;true;) foo() ", "for(;;) foo() "); + foldSame("for(;;) foo()"); + fold("for(;false;) { var a = 0; }", "var a"); + + // Make sure it plays nice with minimizing + fold("for(;false;) { foo(); continue }", ""); + } + + public void testFoldUselessDo() { + fold("do { foo() } while(false);", "foo()"); + fold("do { foo() } while(void 0);", "foo()"); + fold("do { foo() } while(undefined);", "foo()"); + fold("do { foo() } while(true);", "do { foo() } while(true);"); + fold("do { var a = 0; } while(false);", "var a=0"); + + fold("do { var a = 0; } while(!{a:foo()});", "var a=0;foo()"); + + // Can't fold with break or continues. + foldSame("do { foo(); continue; } while(0)"); + foldSame("do { foo(); break; } while(0)"); + } + + public void testMinimizeWhileConstantCondition() { + fold("while(true) foo()", "while(true) foo()"); + fold("while(0) foo()", ""); + fold("while(0.0) foo()", ""); + fold("while(NaN) foo()", ""); + fold("while(null) foo()", ""); + fold("while(undefined) foo()", ""); + fold("while('') foo()", ""); + } + + public void testFoldConstantCommaExpressions() { + fold("if (true, false) {foo()}", ""); + fold("if (false, true) {foo()}", "foo()"); + fold("true, foo()", "foo()"); + fold("(1 + 2 + ''), foo()", "foo()"); + } + + + public void testRemoveUselessOps() { + // There are four place where expression results are discarded: + // - a top-level expression EXPR_RESULT + // - the LHS of a COMMA + // - the FOR init expression + // - the FOR increment expression + + // Known side-effect free functions calls are removed. + fold("Math.random()", ""); + fold("Math.random(f() + g())", "f(),g();"); + fold("Math.random(f(),g(),h())", "f(),g(),h();"); + + // Calls to functions with unknown side-effects are are left. + foldSame("f();"); + foldSame("(function () { f(); })();"); + + // We know that this function has no side effects because of the + // PureFunctionIdentifier. + fold("(function () {})();", ""); + + // Uncalled function expressions are removed + fold("(function () {});", ""); + fold("(function f() {});", ""); + // ... including any code they contain. + fold("(function () {foo();});", ""); + + // Useless operators are removed. + fold("+f()", "f()"); + fold("a=(+f(),g())", "a=(f(),g())"); + fold("a=(true,g())", "a=g()"); + fold("f(),true", "f()"); + fold("f() + g()", "f(),g()"); + + fold("for(;;+f()){}", "for(;;f()){}"); + fold("for(+f();;g()){}", "for(f();;g()){}"); + fold("for(;;Math.random(f(),g(),h())){}", "for(;;f(),g(),h()){}"); + + // The optimization cascades into conditional expressions: + fold("g() && +f()", "g() && f()"); + fold("g() || +f()", "g() || f()"); + fold("x ? g() : +f()", "x ? g() : f()"); + + fold("+x()", "x()"); + fold("+x() * 2", "x()"); + fold("-(+x() * 2)", "x()"); + fold("2 -(+x() * 2)", "x()"); + fold("x().foo", "x()"); + foldSame("x().foo()"); + + foldSame("x++"); + foldSame("++x"); + foldSame("x--"); + foldSame("--x"); + foldSame("x = 2"); + foldSame("x *= 2"); + + // Sanity check, other expression are left alone. + foldSame("function f() {}"); + foldSame("var x;"); + } + + public void testOptimizeSwitch() { + fold("switch(a){}", ""); + fold("switch(foo()){}", "foo()"); + fold("switch(a){default:}", ""); + fold("switch(a){default:break;}", ""); + fold("switch(a){default:var b;break;}", "var b"); + fold("switch(a){case 1: default:}", ""); + fold("switch(a){default: case 1:}", ""); + fold("switch(a){default: break; case 1:break;}", ""); + fold("switch(a){default: var b; break; case 1: var c; break;}", + "var c; var b;"); + + // Can't remove cases if a default exists. + foldSame("function f() {switch(a){default: return; case 1: break;}}"); + foldSame("function f() {switch(a){case 1: foo();}}"); + foldSame("function f() {switch(a){case 3: case 2: case 1: foo();}}"); + + fold("function f() {switch(a){case 2: case 1: default: foo();}}", + "function f() {switch(a){default: foo();}}"); + fold("switch(a){case 1: default:break; case 2: foo()}", + "switch(a){case 2: foo()}"); + foldSame("switch(a){case 1: goo(); default:break; case 2: foo()}"); + + // TODO(johnlenz): merge the useless "case 2" + foldSame("switch(a){case 1: goo(); case 2:break; case 3: foo()}"); + + // Can't remove unused code with a "var" in it. + fold("switch(1){case 2: var x=0;}", "var x;"); + fold("switch ('repeated') {\n" + + "case 'repeated':\n" + + " foo();\n" + + " break;\n" + + "case 'repeated':\n" + + " var x=0;\n" + + " break;\n" + + "}", + "var x; {foo();}"); + + // Can't remove cases if something useful is done. + foldSame("switch(a){case 1: var c =2; break;}"); + foldSame("function f() {switch(a){case 1: return;}}"); + foldSame("x:switch(a){case 1: break x;}"); + + fold("switch ('foo') {\n" + + "case 'foo':\n" + + " foo();\n" + + " break;\n" + + "case 'bar':\n" + + " bar();\n" + + " break;\n" + + "}", + "{foo();}"); + fold("switch ('noMatch') {\n" + + "case 'foo':\n" + + " foo();\n" + + " break;\n" + + "case 'bar':\n" + + " bar();\n" + + " break;\n" + + "}", + ""); + foldSame("switch ('fallThru') {\n" + + "case 'fallThru':\n" + + " if (foo(123) > 0) {\n" + + " foobar(1);\n" + + " break;\n" + + " }\n" + + " foobar(2);\n" + + "case 'bar':\n" + + " bar();\n" + + "}"); + foldSame("switch ('fallThru') {\n" + + "case 'fallThru':\n" + + " foo();\n" + + "case 'bar':\n" + + " bar();\n" + + "}"); + foldSame("switch ('hasDefaultCase') {\n" + + "case 'foo':\n" + + " foo();\n" + + " break;\n" + + "default:\n" + + " bar();\n" + + " break;\n" + + "}"); + fold("switch ('repeated') {\n" + + "case 'repeated':\n" + + " foo();\n" + + " break;\n" + + "case 'repeated':\n" + + " bar();\n" + + " break;\n" + + "}", + "{foo();}"); + fold("switch ('foo') {\n" + + "case 'bar':\n" + + " bar();\n" + + " break;\n" + + "case notConstant:\n" + + " foobar();\n" + + " break;\n" + + "case 'foo':\n" + + " foo();\n" + + " break;\n" + + "}", + "switch ('foo') {\n" + + "case notConstant:\n" + + " foobar();\n" + + " break;\n" + + "case 'foo':\n" + + " foo();\n" + + " break;\n" + + "}"); + fold("switch (1) {\n" + + "case 1:\n" + + " foo();\n" + + " break;\n" + + "case 2:\n" + + " bar();\n" + + " break;\n" + + "}", + "{foo();}"); + fold("switch (1) {\n" + + "case 1.1:\n" + + " foo();\n" + + " break;\n" + + "case 2:\n" + + " bar();\n" + + " break;\n" + + "}", + ""); + foldSame("switch (0) {\n" + + "case NaN:\n" + + " foobar();\n" + + " break;\n" + + "case -0.0:\n" + + " foo();\n" + + " break;\n" + + "case 2:\n" + + " bar();\n" + + " break;\n" + + "}"); + foldSame("switch ('\\v') {\n" + + "case '\\u000B':\n" + + " foo();\n" + + "}"); + foldSame("switch ('empty') {\n" + + "case 'empty':\n" + + "case 'foo':\n" + + " foo();\n" + + "}"); + } + + public void testRemoveNumber() { + test("3", ""); + } + + public void testRemoveVarGet1() { + test("a", ""); + } + + public void testRemoveVarGet2() { + test("var a = 1;a", "var a = 1"); + } + + public void testRemoveNamespaceGet1() { + test("var a = {};a.b", "var a = {}"); + } + + public void testRemoveNamespaceGet2() { + test("var a = {};a.b=1;a.b", "var a = {};a.b=1"); + } + + public void testRemovePrototypeGet1() { + test("var a = {};a.prototype.b", "var a = {}"); + } + + public void testRemovePrototypeGet2() { + test("var a = {};a.prototype.b = 1;a.prototype.b", + "var a = {};a.prototype.b = 1"); + } + + public void testRemoveAdd1() { + test("1 + 2", ""); + } + + public void testNoRemoveVar1() { + testSame("var a = 1"); + } + + public void testNoRemoveVar2() { + testSame("var a = 1, b = 2"); + } + + public void testNoRemoveAssign1() { + testSame("a = 1"); + } + + public void testNoRemoveAssign2() { + testSame("a = b = 1"); + } + + public void testNoRemoveAssign3() { + test("1 + (a = 2)", "a = 2"); + } + + public void testNoRemoveAssign4() { + testSame("x.a = 1"); + } + + public void testNoRemoveAssign5() { + testSame("x.a = x.b = 1"); + } + + public void testNoRemoveAssign6() { + test("1 + (x.a = 2)", "x.a = 2"); + } + + public void testNoRemoveCall1() { + testSame("a()"); + } + + public void testNoRemoveCall2() { + test("a()+b()", "a(),b()"); + } + + public void testNoRemoveCall3() { + testSame("a() && b()"); + } + + public void testNoRemoveCall4() { + testSame("a() || b()"); + } + + public void testNoRemoveCall5() { + test("a() || 1", "a()"); + } + + public void testNoRemoveCall6() { + testSame("1 || a()"); + } + + public void testNoRemoveThrow1() { + testSame("function f(){throw a()}"); + } + + public void testNoRemoveThrow2() { + testSame("function f(){throw a}"); + } + + public void testNoRemoveThrow3() { + testSame("function f(){throw 10}"); + } + + public void testRemoveInControlStructure1() { + test("if(x()) 1", "x()"); + } + + public void testRemoveInControlStructure2() { + test("while(2) 1", "while(2);"); + } + + public void testRemoveInControlStructure3() { + test("for(1;2;3) 4", "for(;;);"); + } + + public void testHook1() { + test("1 ? 2 : 3", ""); + } + + public void testHook2() { + test("x ? a() : 3", "x && a()"); + } + + public void testHook3() { + test("x ? 2 : a()", "x || a()"); + } + + public void testHook4() { + testSame("x ? a() : b()"); + } + + public void testHook5() { + test("a() ? 1 : 2", "a()"); + } + + public void testHook6() { + test("a() ? b() : 2", "a() && b()"); + } + + // TODO(johnlenz): Consider adding a post optimization pass to + // convert OR into HOOK to save parentheses when the operator + // precedents would require them. + public void testHook7() { + test("a() ? 1 : b()", "a() || b()"); + } + + public void testHook8() { + testSame("a() ? b() : c()"); + } + + public void testShortCircuit1() { + testSame("1 && a()"); + } + + public void testShortCircuit2() { + test("1 && a() && 2", "1 && a()"); + } + + public void testShortCircuit3() { + test("a() && 1 && 2", "a()"); + } + + public void testShortCircuit4() { + testSame("a() && 1 && b()"); + } + + public void testComplex1() { + test("1 && a() + b() + c()", "1 && (a(), b(), c())"); + } + + public void testComplex2() { + test("1 && (a() ? b() : 1)", "1 && a() && b()"); + } + + public void testComplex3() { + test("1 && (a() ? b() : 1 + c())", "1 && (a() ? b() : c())"); + } + + public void testComplex4() { + test("1 && (a() ? 1 : 1 + c())", "1 && (a() || c())"); + } + + public void testComplex5() { + // can't simplify LHS of short circuit statements with side effects + testSame("(a() ? 1 : 1 + c()) && foo()"); + } + + public void testNoRemoveFunctionDeclaration1() { + testSame("function foo(){}"); + } + + public void testNoRemoveFunctionDeclaration2() { + testSame("var foo = function (){}"); + } + + public void testNoSimplifyFunctionArgs1() { + testSame("f(1 + 2, 3 + g())"); + } + + public void testNoSimplifyFunctionArgs2() { + testSame("1 && f(1 + 2, 3 + g())"); + } + + public void testNoSimplifyFunctionArgs3() { + testSame("1 && foo(a() ? b() : 1 + c())"); + } + + public void testNoRemoveInherits1() { + testSame("var a = {}; this.b = {}; var goog = {}; goog.inherits(b, a)"); + } + + public void testNoRemoveInherits2() { + test("var a = {}; this.b = {}; var goog = {}; goog.inherits(b, a) + 1", + "var a = {}; this.b = {}; var goog = {}; goog.inherits(b, a)"); + } + + public void testNoRemoveInherits3() { + testSame("this.a = {}; var b = {}; b.inherits(a);"); + } + + public void testNoRemoveInherits4() { + test("this.a = {}; var b = {}; b.inherits(a) + 1;", + "this.a = {}; var b = {}; b.inherits(a)"); + } + + public void testRemoveFromLabel1() { + test("LBL: void 0", "LBL: {}"); + } + + public void testRemoveFromLabel2() { + test("LBL: foo() + 1 + bar()", "LBL: foo(),bar()"); + } + + public void testCall1() { + test("Math.sin(0);", ""); + } + + public void testCall2() { + test("1 + Math.sin(0);", ""); + } + + public void testNew1() { + test("new Date;", ""); + } + + public void testNew2() { + test("1 + new Date;", ""); + } + + public void testFoldAssign() { + test("x=x", ""); + testSame("x=xy"); + testSame("x=x + 1"); + testSame("x.a=x.a"); + test("var y=(x=x)", "var y=x"); + test("y=1 + (x=x)", "y=1 + x"); + } + + public void testTryCatchFinally() { + testSame("try {foo()} catch (e) {bar()}"); + testSame("try { try {foo()} catch (e) {bar()}} catch (x) {bar()}"); + test("try {var x = 1} finally {}", "var x = 1;"); + testSame("try {var x = 1} finally {x()}"); + test("function f() { return; try{var x = 1}finally{} }", + "function f() { return; var x = 1; }"); + test("try {} finally {x()}", "x()"); + test("try {} catch (e) { bar()} finally {x()}", "x()"); + test("try {} catch (e) { bar()}", ""); + test("try {} catch (e) { var a = 0; } finally {x()}", "var a; x()"); + test("try {} catch (e) {}", ""); + test("try {} finally {}", ""); + test("try {} catch (e) {} finally {}", ""); + } + + public void testObjectLiteral() { + test("({})", ""); + test("({a:1})", ""); + test("({a:foo()})", "foo()"); + test("({'a':foo()})", "foo()"); + } + + public void testArrayLiteral() { + test("([])", ""); + test("([1])", ""); + test("([a])", ""); + test("([foo()])", "foo()"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeReplaceKnownMethodsTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeReplaceKnownMethodsTest.java new file mode 100644 index 0000000..bcd6a17 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeReplaceKnownMethodsTest.java @@ -0,0 +1,321 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +/** + * Unit tests for {#link {@link PeepholeReplaceKnownMethods} + * + */ +public class PeepholeReplaceKnownMethodsTest extends CompilerTestCase { + + private boolean late = true; + + public PeepholeReplaceKnownMethodsTest() { + super(""); + } + + @Override + public void setUp() { + enableLineNumberCheck(true); + } + + @Override + public CompilerPass getProcessor(final Compiler compiler) { + CompilerPass peepholePass = new PeepholeOptimizationsPass(compiler, + new PeepholeReplaceKnownMethods(late)); + return peepholePass; + } + + public void testStringIndexOf() { + fold("x = 'abcdef'.indexOf('b')", "x = 1"); + fold("x = 'abcdefbe'.indexOf('b', 2)", "x = 6"); + fold("x = 'abcdef'.indexOf('bcd')", "x = 1"); + fold("x = 'abcdefsdfasdfbcdassd'.indexOf('bcd', 4)", "x = 13"); + + fold("x = 'abcdef'.lastIndexOf('b')", "x = 1"); + fold("x = 'abcdefbe'.lastIndexOf('b')", "x = 6"); + fold("x = 'abcdefbe'.lastIndexOf('b', 5)", "x = 1"); + + // Both elements must be strings. Don't do anything if either one is not + // string. + fold("x = 'abc1def'.indexOf(1)", "x = 3"); + fold("x = 'abcNaNdef'.indexOf(NaN)", "x = 3"); + fold("x = 'abcundefineddef'.indexOf(undefined)", "x = 3"); + fold("x = 'abcnulldef'.indexOf(null)", "x = 3"); + fold("x = 'abctruedef'.indexOf(true)", "x = 3"); + + // The following test case fails with JSC_PARSE_ERROR. Hence omitted. + // foldSame("x = 1.indexOf('bcd');"); + foldSame("x = NaN.indexOf('bcd')"); + foldSame("x = undefined.indexOf('bcd')"); + foldSame("x = null.indexOf('bcd')"); + foldSame("x = true.indexOf('bcd')"); + foldSame("x = false.indexOf('bcd')"); + + // Avoid dealing with regex or other types. + foldSame("x = 'abcdef'.indexOf(/b./)"); + foldSame("x = 'abcdef'.indexOf({a:2})"); + foldSame("x = 'abcdef'.indexOf([1,2])"); + } + + public void testStringJoinAddSparse() { + fold("x = [,,'a'].join(',')", "x = ',,a'"); + } + + public void testNoStringJoin() { + foldSame("x = [].join(',',2)"); + foldSame("x = [].join(f)"); + } + + public void testStringJoinAdd() { + fold("x = ['a', 'b', 'c'].join('')", "x = \"abc\""); + fold("x = [].join(',')", "x = \"\""); + fold("x = ['a'].join(',')", "x = \"a\""); + fold("x = ['a', 'b', 'c'].join(',')", "x = \"a,b,c\""); + fold("x = ['a', foo, 'b', 'c'].join(',')", + "x = [\"a\",foo,\"b,c\"].join()"); + fold("x = [foo, 'a', 'b', 'c'].join(',')", + "x = [foo,\"a,b,c\"].join()"); + fold("x = ['a', 'b', 'c', foo].join(',')", + "x = [\"a,b,c\",foo].join()"); + + // Works with numbers + fold("x = ['a=', 5].join('')", "x = \"a=5\""); + fold("x = ['a', '5'].join(7)", "x = \"a75\""); + + // Works on boolean + fold("x = ['a=', false].join('')", "x = \"a=false\""); + fold("x = ['a', '5'].join(true)", "x = \"atrue5\""); + fold("x = ['a', '5'].join(false)", "x = \"afalse5\""); + + // Only optimize if it's a size win. + fold("x = ['a', '5', 'c'].join('a very very very long chain')", + "x = [\"a\",\"5\",\"c\"].join(\"a very very very long chain\")"); + + // TODO(user): Its possible to fold this better. + foldSame("x = ['', foo].join('-')"); + foldSame("x = ['', foo, ''].join()"); + + fold("x = ['', '', foo, ''].join(',')", + "x = [',', foo, ''].join()"); + fold("x = ['', '', foo, '', ''].join(',')", + "x = [',', foo, ','].join()"); + + fold("x = ['', '', foo, '', '', bar].join(',')", + "x = [',', foo, ',', bar].join()"); + + fold("x = [1,2,3].join('abcdef')", + "x = '1abcdef2abcdef3'"); + + fold("x = [1,2].join()", "x = '1,2'"); + fold("x = [null,undefined,''].join(',')", "x = ',,'"); + fold("x = [null,undefined,0].join(',')", "x = ',,0'"); + // This can be folded but we don't currently. + foldSame("x = [[1,2],[3,4]].join()"); // would like: "x = '1,2,3,4'" + } + + public void testStringJoinAdd_b1992789() { + fold("x = ['a'].join('')", "x = \"a\""); + fold("x = [foo()].join('')", "x = '' + foo()"); + fold("[foo()].join('')", "'' + foo()"); + } + + public void testFoldStringSubstr() { + fold("x = 'abcde'.substr(0,2)", "x = 'ab'"); + fold("x = 'abcde'.substr(1,2)", "x = 'bc'"); + fold("x = 'abcde'['substr'](1,3)", "x = 'bcd'"); + fold("x = 'abcde'.substr(2)", "x = 'cde'"); + + // we should be leaving negative indexes alone for now + foldSame("x = 'abcde'.substr(-1)"); + foldSame("x = 'abcde'.substr(1, -2)"); + foldSame("x = 'abcde'.substr(1, 2, 3)"); + foldSame("x = 'a'.substr(0, 2)"); + } + + public void testFoldStringSubstring() { + fold("x = 'abcde'.substring(0,2)", "x = 'ab'"); + fold("x = 'abcde'.substring(1,2)", "x = 'b'"); + fold("x = 'abcde'['substring'](1,3)", "x = 'bc'"); + fold("x = 'abcde'.substring(2)", "x = 'cde'"); + + // we should be leaving negative indexes alone for now + foldSame("x = 'abcde'.substring(-1)"); + foldSame("x = 'abcde'.substring(1, -2)"); + foldSame("x = 'abcde'.substring(1, 2, 3)"); + foldSame("x = 'a'.substring(0, 2)"); + } + + public void testFoldStringCharAt() { + fold("x = 'abcde'.charAt(0)", "x = 'a'"); + fold("x = 'abcde'.charAt(1)", "x = 'b'"); + fold("x = 'abcde'.charAt(2)", "x = 'c'"); + fold("x = 'abcde'.charAt(3)", "x = 'd'"); + fold("x = 'abcde'.charAt(4)", "x = 'e'"); + foldSame("x = 'abcde'.charAt(5)"); // or x = '' + foldSame("x = 'abcde'.charAt(-1)"); // or x = '' + foldSame("x = 'abcde'.charAt(y)"); + foldSame("x = 'abcde'.charAt()"); // or x = 'a' + foldSame("x = 'abcde'.charAt(0, ++z)"); // or (++z, 'a') + foldSame("x = 'abcde'.charAt(null)"); // or x = 'a' + foldSame("x = 'abcde'.charAt(true)"); // or x = 'b' + fold("x = '\\ud834\udd1e'.charAt(0)", "x = '\\ud834'"); + fold("x = '\\ud834\udd1e'.charAt(1)", "x = '\\udd1e'"); + } + + public void testFoldStringCharCodeAt() { + fold("x = 'abcde'.charCodeAt(0)", "x = 97"); + fold("x = 'abcde'.charCodeAt(1)", "x = 98"); + fold("x = 'abcde'.charCodeAt(2)", "x = 99"); + fold("x = 'abcde'.charCodeAt(3)", "x = 100"); + fold("x = 'abcde'.charCodeAt(4)", "x = 101"); + foldSame("x = 'abcde'.charCodeAt(5)"); // or x = (0/0) + foldSame("x = 'abcde'.charCodeAt(-1)"); // or x = (0/0) + foldSame("x = 'abcde'.charCodeAt(y)"); + foldSame("x = 'abcde'.charCodeAt()"); // or x = 97 + foldSame("x = 'abcde'.charCodeAt(0, ++z)"); // or (++z, 97) + foldSame("x = 'abcde'.charCodeAt(null)"); // or x = 97 + foldSame("x = 'abcde'.charCodeAt(true)"); // or x = 98 + fold("x = '\\ud834\udd1e'.charCodeAt(0)", "x = 55348"); + fold("x = '\\ud834\udd1e'.charCodeAt(1)", "x = 56606"); + } + + public void testFoldStringSplit() { + late = false; + fold("x = 'abcde'.split('foo')", "x = ['abcde']"); + fold("x = 'abcde'.split()", "x = ['abcde']"); + fold("x = 'abcde'.split(null)", "x = ['abcde']"); + fold("x = 'a b c d e'.split(' ')", "x = ['a','b','c','d','e']"); + fold("x = 'a b c d e'.split(' ', 0)", "x = []"); + fold("x = 'abcde'.split('cd')", "x = ['ab','e']"); + fold("x = 'a b c d e'.split(' ', 1)", "x = ['a']"); + fold("x = 'a b c d e'.split(' ', 3)", "x = ['a','b','c']"); + fold("x = 'a b c d e'.split(null, 1)", "x = ['a b c d e']"); + fold("x = 'aaaaa'.split('a')", "x = ['', '', '', '', '', '']"); + fold("x = 'xyx'.split('x')", "x = ['', 'y', '']"); + + // Empty separator + fold("x = 'abcde'.split('')", "x = ['a','b','c','d','e']"); + fold("x = 'abcde'.split('', 3)", "x = ['a','b','c']"); + + // Empty separator AND empty string + fold("x = ''.split('')", "x = []"); + + // Separator equals string + fold("x = 'aaa'.split('aaa')", "x = ['','']"); + fold("x = ' '.split(' ')", "x = ['','']"); + + foldSame("x = 'abcde'.split(/ /)"); + foldSame("x = 'abcde'.split(' ', -1)"); + + late = true; + foldSame("x = 'a b c d e'.split(' ')"); + } + + public void testJoinBug() { + fold("var x = [].join();", "var x = '';"); + fold("var x = [x].join();", "var x = '' + x;"); + foldSame("var x = [x,y].join();"); + foldSame("var x = [x,y,z].join();"); + + foldSame("shape['matrix'] = [\n" + + " Number(headingCos2).toFixed(4),\n" + + " Number(-headingSin2).toFixed(4),\n" + + " Number(headingSin2 * yScale).toFixed(4),\n" + + " Number(headingCos2 * yScale).toFixed(4),\n" + + " 0,\n" + + " 0\n" + + " ].join()"); + } + + public void testToUpper() { + fold("'a'.toUpperCase()", "'A'"); + fold("'A'.toUpperCase()", "'A'"); + fold("'aBcDe'.toUpperCase()", "'ABCDE'"); + } + + public void testToLower() { + fold("'A'.toLowerCase()", "'a'"); + fold("'a'.toLowerCase()", "'a'"); + fold("'aBcDe'.toLowerCase()", "'abcde'"); + } + + public void testFoldParseNumbers() { + enableNormalize(); + enableEcmaScript5(true); + + fold("x = parseInt('123')", "x = 123"); + fold("x = parseInt(' 123')", "x = 123"); + fold("x = parseInt('123', 10)", "x = 123"); + fold("x = parseInt('0xA')", "x = 10"); + fold("x = parseInt('0xA', 16)", "x = 10"); + fold("x = parseInt('07', 8)", "x = 7"); + fold("x = parseInt('08')", "x = 8"); + fold("x = parseInt('0')", "x = 0"); + fold("x = parseFloat('0')", "x = 0"); + fold("x = parseFloat('1.23')", "x = 1.23"); + fold("x = parseFloat('1.2300')", "x = 1.23"); + fold("x = parseFloat(' 0.3333')", "x = 0.3333"); + fold("x = parseFloat('0100')", "x = 100"); + fold("x = parseFloat('0100.000')", "x = 100"); + + //Mozilla Dev Center test cases + fold("x = parseInt(' 0xF', 16)", "x = 15"); + fold("x = parseInt(' F', 16)", "x = 15"); + fold("x = parseInt('17', 8)", "x = 15"); + fold("x = parseInt('015', 10)", "x = 15"); + fold("x = parseInt('1111', 2)", "x = 15"); + fold("x = parseInt('12', 13)", "x = 15"); + fold("x = parseInt(021, 8)", "x = 15"); + fold("x = parseInt(15.99, 10)", "x = 15"); + fold("x = parseFloat('3.14')", "x = 3.14"); + fold("x = parseFloat(3.14)", "x = 3.14"); + + //Valid calls - unable to fold + foldSame("x = parseInt('FXX123', 16)"); + foldSame("x = parseInt('15*3', 10)"); + foldSame("x = parseInt('15e2', 10)"); + foldSame("x = parseInt('15px', 10)"); + foldSame("x = parseInt('-0x08')"); + foldSame("x = parseInt('1', -1)"); + foldSame("x = parseFloat('3.14more non-digit characters')"); + foldSame("x = parseFloat('314e-2')"); + foldSame("x = parseFloat('0.0314E+2')"); + foldSame("x = parseFloat('3.333333333333333333333333')"); + + //Invalid calls + foldSame("x = parseInt('0xa', 10)"); + foldSame("x = parseInt('')"); + + enableEcmaScript5(false); + foldSame("x = parseInt('08')"); + } + + @Override + protected int getNumRepetitions() { + // Reduce this to 2 if we get better expression evaluators. + return 2; + } + + private void foldSame(String js) { + testSame(js); + } + + private void fold(String js, String expected) { + test(js, expected); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeSimplifyRegExpTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeSimplifyRegExpTest.java new file mode 100644 index 0000000..f903f92 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/PeepholeSimplifyRegExpTest.java @@ -0,0 +1,346 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp; + +import com.google.javascript.rhino.Node; + +public class PeepholeSimplifyRegExpTest extends CompilerTestCase { + + public final void testWaysOfMatchingEmptyString() { + testSame("/(?:)/"); + test("/(?:)/i", "/(?:)/"); // We can get rid of i flag when no letters. + test("/.{0}/i", "/(?:)/"); + test("/[^\\0-\\uffff]{0}/", "/(?:)/"); + // Cannot get rid of capturing groups. + testSame("/(){0}/"); + } + + public final void testCharsetOptimizations() { + testSame("/./"); + test("/[\\0-\\uffff]/", "/[\\S\\s]/"); + test("/[^\\0-\\uffff]/", "/(?!)/"); + test("/[^\\0-\\x40\\x42-\\uffff]/", "/A/"); + test("/[0-9a-fA-F]/i", "/[\\da-f]/i"); + test("/[0-9a-zA-Z_$]/i", "/[\\w$]/"); + test("/[()*+\\-,]/g", "/[(--]/g"); + test("/[()*+\\-,z]/g", "/[(--z]/g"); + test("/[\\-\\.\\/0]/g", "/[--0]/g"); + test("/[\\-\\.\\/0\\n]/g", "/[\\n\\--0]/g"); + test("/[\\[\\\\\\]]/g", "/[[-\\]]/g"); + test("/[\\[\\\\\\]\\^]/g", "/[[-^]/g"); + test("/[\\^`_]/g", "/[\\^-`]/g"); + test("/[^\\^`_]/g", "/[^^-`]/g"); + test("/^(?=[^a-z])/i", "/^(?=[\\W\\d_])/"); + test("/^[^a-z0-9]/i", "/^[\\W_]/"); + test("/[0-FA-Z]/", "/[0-Z]/"); + test("/[0-9]/", "/\\d/"); + test("/[^0-9]/", "/\\D/"); + testSame("/\\D/"); + test("/[_a-z0-9]/i", "/\\w/"); + test("/[0-9_a-z]/i", "/\\w/"); + test("/[_a-z0-9]/", "/[\\d_a-z]/"); + test("/[_E-Za-f0-9]/i", "/\\w/"); + test("/[E-Za-f]/i", "/[a-z]/i"); + test("/[_E-Za-f0-9]/", "/[\\dE-Z_a-f]/"); + // Test case normalization. + // U+00CA and U+00EA are E and e with ^ above + test("/[\\u00ca\\u00ea]/", "/[\\xca\\xea]/"); + test("/[\\u00ca\\u00ea]/i", "/\\xca/i"); + // IE (at least 6, 7, and 8) do not include \xA0 in \s so when an author + // explicitly includes it make sure it appears in the output. + testSame("/^[\\s\\xa0]*$/"); + test("/^(?:\\s|\\xA0)*$/", "/^[\\s\\xa0]*$/"); + } + + public final void testCharsetFixup() { + testSame("/[a-z]/i"); + // This is the case. The below produces no output in squarefree. + // (function () { + // // Runs to just before the letter 'a' and starts right after 'z'. + // var re = /[^\0-`{-\uffff]/i + // for (var i = 0; i < 0x10000; ++i) { + // var s = String.fromCharCode(i); + // if (re.test(s)) { print(s + ' : ' + s.charCodeAt(0).toString(16)); } + // } + // })() + test("/[^\\0-`{-\\uffff]/i", "/(?!)/"); + // This looks a bit odd, but + // /[^a-z]/i is the same as all non-word characters, all digits, and _ and + // /[\W\d_]/ is the same length. + test("/[^a-z]/i", "/[\\W\\d_]/"); + } + + public final void testGroups() { + testSame("/foo(bar)baz/"); + } + + public final void testBackReferences() { + testSame("/foo(bar)baz(?:\\1|\\x01)boo/"); + // But when there is no group to refer to, then the back-reference *is* + // the same as an octal escape. + test("/foo(?:bar)baz(?:\\1|\\x01)boo/", "/foobarbaz\\x01boo/"); + // \\8 is never an octal escape. If there is no 8th group, then it + // is the literal character '8' + test("/foo(?:bar)baz(?:\\8|8)boo/", "/foobarbaz8boo/"); + // \10 can be a capturing group. + test("/(1?)(2?)(3?)(4?)(5?)(6?)(7?)(8?)(9?)(A?)(B?)" + + "\\12\\11\\10\\9\\8\\7\\6\\5\\4\\3\\2\\1\\0/", + "/(1?)(2?)(3?)(4?)(5?)(6?)(7?)(8?)(9?)(A?)(B?)" + // \\12 does not match any group, so is treated as group 1 followed + // by literal 2. + + "\\1(?:2)\\11\\10\\9\\8\\7\\6\\5\\4\\3\\2\\1\\0/"); + // But \1 should not be emitted followed by a digit un-parenthesized. + test("/(1?)(2?)(3?)(4?)(5?)(6?)(7?)(8?)(9?)(A?)(B?)(?:\\1)0/", + "/(1?)(2?)(3?)(4?)(5?)(6?)(7?)(8?)(9?)(A?)(B?)\\1(?:0)/"); + // \012 is never treated as a group even when there are 12 groups. + test("/(1?)(2?)(3?)(4?)(5?)(6?)(7?)(8?)(9?)(A?)(B?)(C?)" + + "\\012\\11\\10\\9\\8\\7\\6\\5\\4\\3\\2\\1\\0/", + "/(1?)(2?)(3?)(4?)(5?)(6?)(7?)(8?)(9?)(A?)(B?)(C?)" + + "\\n\\11\\10\\9\\8\\7\\6\\5\\4\\3\\2\\1\\0/"); + } + + public final void testSingleCharAlterations() { + test("/a|B|c|D/i", "/[a-d]/i"); + test("/a|B|c|D/", "/[BDac]/"); + test("/a|[Bc]|D/", "/[BDac]/"); + test("/[aB]|[cD]/", "/[BDac]/"); + test("/a|B|c|D|a|B/i", "/[a-d]/i"); // Duplicates. + test("/a|A|/i", "/a?/i"); + } + + public final void testAlterations() { + testSame("/foo|bar/"); + test("/Foo|BAR/i", "/foo|bar/i"); + test("/Foo||BAR/", "/Foo||BAR/"); + test("/Foo|BAR|/", "/Foo|BAR|/"); + } + + public final void testNestedAlterations() { + test("/foo|bar|(?:baz|boo)|far/", "/foo|bar|baz|boo|far/"); + } + + public final void testEscapeSequencesAndNonLatinChars() { + test("/\u1234/i", "/\\u1234/"); + testSame("/\\u1234/"); + test("/\u00A0/", "/\\xa0/"); + test("/\\u00A0/", "/\\xa0/"); + test("/\\u00a0/", "/\\xa0/"); + } + + public final void testAnchors() { + // m changes the meaning of anchors which is useless if there are none. + testSame("/foo(?!$)/gm"); + test("/./m", "/./"); + test("/\\^/m", "/\\^/"); + test("/[\\^]/m", "/\\^/"); + testSame("/(^|foo)bar/"); + testSame("/^.|.$/gm"); + test("/foo(?=)$/m", "/foo$/m"); + // We can get rid of the g when there are no capturing groups and the + // pattern is fully anchored. + test("/^foo$/g", "/^foo$/"); + } + + public final void testRepetitions() { + testSame("/a*/"); + testSame("/a+/"); + testSame("/a+?/"); + testSame("/a?/"); + testSame("/a{6}/"); + testSame("/a{4,}/"); + test("/a{3,}/", + "/aaa+/"); + testSame("/a{4,6}/"); + testSame("/a{4,6}?/"); + test("/(?:a?)?/", "/a?/"); + test("/(?:a?)*/", "/a*/"); + test("/(?:a*)?/", "/a*/"); + test("/a(?:a*)?/", "/a+/"); + test("/(?:a{2,3}){3,4}/", "/a{6,12}/"); + test("/a{2,3}a{3,4}/", "/a{5,7}/"); + testSame("/a{5,7}b{5,6}/"); + test("/a{2,3}b{3,4}/", + "/aaa?bbbb?/"); + test("/a{3}b{3,4}/", + "/aaabbbb?/"); + testSame("/[a-z]{1,2}/"); + test("/\\d{1,2}/", + "/\\d\\d?/"); + test("/a*a*/", "/a*/"); + test("/a+a+/", "/aa+/"); + test("/a+a*/", "/a+/"); + // We don't conflate literal curly brackets with repetitions. + testSame("/a\\{3,1}/"); + test("/a(?:{3,1})/", "/a\\{3,1}/"); + test("/a{3\\,1}/", "/a\\{3,1}/"); + testSame("/a\\{3}/"); + testSame("/a\\{3,}/"); + testSame("/a\\{1,3}/"); + // We don't over-escape curly brackets. + testSame("/a{/"); + testSame("/a{}/"); + testSame("/a{x}/"); + testSame("/a{-1}/"); + testSame("/a{,3}/"); + testSame("/{{[a-z]+}}/"); + testSame("/{\\{0}}/"); + testSame("/{\\{0?}}/"); + } + + public final void testMoreCharsets() { + test("var a = /[\\x00\\x22\\x26\\x27\\x3c\\x3e]/g", + "var a = /[\\0\"&'<>]/g"); + test("var b = /[\\x00\\x22\\x27\\x3c\\x3e]/g", + "var b = /[\\0\"'<>]/g"); + test("var c = /[\\x00\\x09-\\x0d \\x22\\x26\\x27\\x2d\\/\\x3c-\\x3e`" + + "\\x85\\xa0\\u2028\\u2029]/g", + "var c = /[\\0\\t-\\r \"&'/<->`\\x85\\xa0\\u2028\\u2029-]/g"); + test("var d = /[\\x00\\x09-\\x0d \\x22\\x27\\x2d\\/\\x3c-\\x3e`" + + "\\x85\\xa0\\u2028\\u2029]/g", + "var d = /[\\0\\t-\\r \"'/<->`\\x85\\xa0\\u2028\\u2029-]/g"); + test("var e = /[\\x00\\x08-\\x0d\\x22\\x26\\x27\\/\\x3c-\\x3e\\\\" + + "\\x85\\u2028\\u2029]/g", + "var e = /[\\0\\b-\\r\"&'/<->\\\\\\x85\\u2028\\u2029]/g"); + test("var f = /[\\x00\\x08-\\x0d\\x22\\x24\\x26-\\/\\x3a\\x3c-\\x3f" + + "\\x5b-\\x5e\\x7b-\\x7d\\x85\\u2028\\u2029]/g", + "var f = /[\\0\\b-\\r\"$&-/:<-?[-^{-}\\x85\\u2028\\u2029]/g"); + test("var g = /[\\x00\\x08-\\x0d\\x22\\x26-\\x2a\\/\\x3a-\\x3e@\\\\" + + "\\x7b\\x7d\\x85\\xa0\\u2028\\u2029]/g", + "var g = /[\\0\\b-\\r\"&-*/:->@\\\\{}\\x85\\xa0\\u2028\\u2029]/g"); + test("var h = /^(?!-*(?:expression|(?:moz-)?binding))(?:[.#]?-?" + + "(?:[_a-z0-9][_a-z0-9-]*)(?:-[_a-z][_a-z0-9-]*)*-?|-?" + + "(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9])(?:[a-z]{1,2}|%)?|!important|)$/i", + "var h = /^(?!-*(?:expression|(?:moz-)?binding))(?:[#.]?-?" + + "\\w[\\w-]*(?:-[_a-z][\\w-]*)*-?|-?" + + "(?:\\d+(?:\\.\\d*)?|\\.\\d)(?:[a-z]{1,2}|%)?|!important|)$/i"); + test("var i = /^(?:(?:https?|mailto):|[^&:\\/?#]*(?:[\\/?#]|$))/i", + "var i = /^(?:(?:https?|mailto):|[^#&/:?]*(?:[#/?]|$))/i"); + test("var j = /^(?!style|on|action|archive|background|cite|classid" + + "|codebase|data|dsync|href|longdesc|src|usemap)(?:[a-z0-9_$:-]*" + + "|dir=(?:ltr|rtl))$/i", + "var j = /^(?!style|on|action|archive|background|cite|classid" + + "|codebase|data|dsync|href|longdesc|src|usemap)(?:[\\w$:-]*" + + "|dir=(?:ltr|rtl))$/i"); + test("var k = /^(?!script|style|title|textarea|xmp|no)[a-z0-9_$:-]*$/i", + "var k = /^(?!script|style|title|textarea|xmp|no)[\\w$:-]*$/i"); + test("var l = /<(?:!|\\/?[a-z])(?:[^>'\"]|\"[^\"]*\"|'[^']*')*>/gi", + "var l = /<(?:!|\\/?[a-z])(?:[^\"'>]|\"[^\"]*\"|'[^']*')*>/gi"); + } + + public final void testMoreRegularExpression() { + testSame("/\"/"); + testSame("/'/"); + test("/(?:[^<\\/\"'\\s\\\\]|<(?!\\/script))+/i", + "/(?:[^\\s\"'/<\\\\]|<(?!\\/script))+/i"); + testSame("/-->/"); + testSame("/", "B"); + graph.connect("B", "-->", "C"); + graph.connect("C", "-->", "D"); + graph.connect("D", "-->", "E"); + graph.connect("E", "-->", "A"); + + Comparator lexicographic = new Comparator() { + @Override + public int compare(String o1, String o2) { + return o1.toString().compareTo(o2.toString()); + } + }; + GraphColoring coloring = + new GreedyGraphColoring(graph, lexicographic); + assertEquals(3, coloring.color()); + validateColoring(graph); + assertEquals("A", coloring.getPartitionSuperNode("A")); + assertEquals("A", coloring.getPartitionSuperNode("C")); + + Comparator biasD = new Comparator() { + @Override + public int compare(String o1, String o2) { + return o1.replaceAll("D", "@").compareTo(o2.replaceAll("D", "@")); + } + }; + + coloring = new GreedyGraphColoring(graph, biasD); + assertEquals(3, coloring.color()); + validateColoring(graph); + assertEquals("A", coloring.getPartitionSuperNode("A")); + assertFalse("A".equals(coloring.getPartitionSuperNode("C"))); + } + + /** + * Validate that each node has been colored and connected nodes have different + * coloring. + */ + private static void validateColoring(Graph graph) { + for (GraphNode node : graph.getNodes()) { + assertTrue(node.getAnnotation() != null); + } + for (GraphEdge edge : graph.getEdges()) { + Color c1 = edge.getNodeA().getAnnotation(); + Color c2 = edge.getNodeB().getAnnotation(); + assertTrue(c1 != null); + assertTrue(c2 != null); + assertTrue(!c1.equals(c2)); + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/graph/GraphPrunerTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/graph/GraphPrunerTest.java new file mode 100644 index 0000000..4cc22b7 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/graph/GraphPrunerTest.java @@ -0,0 +1,115 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + +import com.google.common.base.Predicates; +import com.google.common.collect.Lists; + +import junit.framework.TestCase; + +/** + * @author nicksantos@google.com (Nick Santos) + */ +public class GraphPrunerTest extends TestCase { + + public void testThreeNodesConnected() { + DiGraph graph = LinkedDirectedGraph.create(); + graph.createNode("A"); + graph.createNode("B"); + graph.createNode("C"); + graph.connect("A", "--", "B"); + graph.connect("B", "--", "C"); + + DiGraph pruned = + new GraphPruner(graph).prune( + Predicates.in(Lists.newArrayList("A", "C"))); + assertEquals(2, pruned.getNodes().size()); + assertTrue(pruned.isConnectedInDirection("A", "C")); + } + + public void testThreeNodesDisconnected() { + DiGraph graph = LinkedDirectedGraph.create(); + graph.createNode("A"); + graph.createNode("B"); + graph.createNode("C"); + graph.connect("A", "--", "B"); + graph.connect("C", "--", "B"); + + DiGraph pruned = + new GraphPruner(graph).prune( + Predicates.in(Lists.newArrayList("A", "C"))); + assertEquals(2, pruned.getNodes().size()); + assertFalse(pruned.isConnectedInDirection("A", "C")); + } + + public void testFourNodesConnected1() { + DiGraph graph = LinkedDirectedGraph.create(); + graph.createNode("A"); + graph.createNode("B"); + graph.createNode("C"); + graph.createNode("D"); + graph.connect("A", "--", "C"); + graph.connect("B", "--", "C"); + graph.connect("C", "--", "D"); + graph.connect("A", "--", "D"); + + DiGraph pruned = + new GraphPruner(graph).prune( + Predicates.not(Predicates.equalTo("C"))); + assertEquals(3, pruned.getNodes().size()); + assertTrue(pruned.isConnectedInDirection("A", "D")); + assertTrue(pruned.isConnectedInDirection("B", "D")); + assertFalse(pruned.isConnectedInDirection("A", "B")); + } + + public void testFourNodesConnected2() { + DiGraph graph = LinkedDirectedGraph.create(); + graph.createNode("A"); + graph.createNode("B"); + graph.createNode("C"); + graph.createNode("D"); + graph.connect("A", "--", "B"); + graph.connect("B", "--", "C"); + graph.connect("C", "--", "D"); + + DiGraph pruned = + new GraphPruner(graph).prune( + Predicates.not(Predicates.in(Lists.newArrayList("B", "C")))); + assertEquals(2, pruned.getNodes().size()); + assertTrue(pruned.isConnectedInDirection("A", "D")); + } + + public void testFiveNodesConnected() { + DiGraph graph = LinkedDirectedGraph.create(); + graph.createNode("A"); + graph.createNode("B"); + graph.createNode("C"); + graph.createNode("D"); + graph.createNode("E"); + graph.connect("A", "--", "B"); + graph.connect("B", "--", "C"); + graph.connect("C", "--", "D"); + graph.connect("D", "--", "E"); + graph.connect("D", "--", "B"); + + DiGraph pruned = + new GraphPruner(graph).prune( + Predicates.not(Predicates.in(Lists.newArrayList("B", "C", "D")))); + assertEquals(2, pruned.getNodes().size()); + assertTrue(pruned.isConnectedInDirection("A", "E")); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/graph/GraphReachabilityTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/graph/GraphReachabilityTest.java new file mode 100644 index 0000000..9d96dc5 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/graph/GraphReachabilityTest.java @@ -0,0 +1,87 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + +import com.google.javascript.jscomp.graph.GraphReachability; +import com.google.javascript.jscomp.graph.LinkedDirectedGraph; +import com.google.javascript.jscomp.graph.DiGraph; + +import junit.framework.TestCase; + +/** + * Tests for {@link GraphReachability}. + * + */ +public class GraphReachabilityTest extends TestCase { + GraphReachability reachability = null; + DiGraph graph = null; + + public void testSimple() { + graph = LinkedDirectedGraph.create(); + graph.createNode("A"); + reachability = new GraphReachability(graph); + reachability.compute("A"); + assertReachable("A"); + + graph.createNode("B"); + reachability = new GraphReachability(graph); + reachability.compute("A"); + assertReachable("A"); + assertNotReachable("B"); + + graph.connect("A", "--->", "B"); + reachability = new GraphReachability(graph); + reachability.compute("B"); + assertNotReachable("A"); + assertReachable("B"); + + graph.connect("B", "--->", "A"); + reachability = new GraphReachability(graph); + reachability.compute("B"); + assertReachable("A"); + assertReachable("B"); + + graph.createNode("C"); + reachability = new GraphReachability(graph); + reachability.compute("A"); + assertReachable("A"); + assertReachable("B"); + assertNotReachable("C"); + + graph.createNode("D"); + graph.connect("C", "--->", "D"); + reachability = new GraphReachability(graph); + reachability.compute("A"); + assertReachable("A"); + assertReachable("B"); + assertNotReachable("C"); + assertNotReachable("D"); + reachability.recompute("C"); + assertReachable("C"); + assertReachable("D"); + } + + public void assertReachable(String s) { + assertSame(s + " should be reachable", graph.getNode(s).getAnnotation(), + GraphReachability.REACHABLE); + } + + public void assertNotReachable(String s) { + assertNotSame(s + " should not be reachable", + graph.getNode(s).getAnnotation(), GraphReachability.REACHABLE); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/graph/GraphTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/graph/GraphTest.java new file mode 100644 index 0000000..aeeb0be --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/graph/GraphTest.java @@ -0,0 +1,384 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + +import com.google.javascript.jscomp.graph.Graph; +import com.google.javascript.jscomp.graph.LinkedDirectedGraph; +import com.google.javascript.jscomp.graph.LinkedUndirectedGraph; +import com.google.javascript.jscomp.graph.Annotatable; +import com.google.javascript.jscomp.graph.Annotation; +import com.google.javascript.jscomp.graph.GraphNode; +import com.google.javascript.jscomp.graph.SubGraph; +import com.google.javascript.jscomp.graph.DiGraph; +import com.google.javascript.jscomp.graph.Graph.GraphEdge; +import com.google.javascript.jscomp.graph.UndiGraph; + +import junit.framework.TestCase; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Tests for the graph data structure. + * + */ +public class GraphTest extends TestCase { + + public void testDirectedSimple() { + DiGraph graph = + LinkedDirectedGraph.create(); + graph.createNode("a"); + graph.createNode("b"); + graph.createNode("c"); + graph.connect("a", "->", "b"); + assertTrue(graph.hasNode("a")); + assertTrue(graph.hasNode("b")); + assertTrue(graph.hasNode("c")); + assertFalse(graph.hasNode("d")); + assertTrue(graph.isConnected("a", "b")); + assertTrue(graph.isConnected("b", "a")); + assertFalse(graph.isConnected("a", "c")); + assertFalse(graph.isConnected("b", "c")); + assertFalse(graph.isConnected("c", "a")); + assertFalse(graph.isConnected("c", "b")); + assertFalse(graph.isConnected("a", "a")); + assertFalse(graph.isConnected("b", "b")); + assertFalse(graph.isConnected("b", "c")); + assertTrue(graph.isConnectedInDirection("a", "b")); + assertFalse(graph.isConnectedInDirection("b", "a")); + assertFalse(graph.isConnectedInDirection("a", "c")); + assertFalse(graph.isConnectedInDirection("b", "c")); + assertFalse(graph.isConnectedInDirection("c", "a")); + assertFalse(graph.isConnectedInDirection("c", "b")); + + // Removal. + graph.disconnect("a", "b"); + assertFalse(graph.isConnected("a", "b")); + assertFalse(graph.isConnected("b", "a")); + + // Disconnect both ways. + graph.connect("a", "->", "b"); + graph.connect("b", "->", "a"); + graph.disconnect("a", "b"); + assertFalse(graph.isConnected("a", "b")); + assertFalse(graph.isConnected("b", "a")); + + // Disconnect one way. + graph.connect("a", "->", "b"); + graph.connect("b", "->", "a"); + graph.disconnectInDirection("a", "b"); + assertTrue(graph.isConnected("b", "a")); + assertTrue(graph.isConnected("a", "b")); + assertFalse(graph.isConnectedInDirection("a", "b")); + assertTrue(graph.isConnectedInDirection("b", "a")); + } + + public void testUndirectedSimple() { + UndiGraph graph = + LinkedUndirectedGraph.create(); + graph.createNode("a"); + graph.createNode("b"); + graph.createNode("c"); + graph.connect("a", "--", "b"); + assertTrue(graph.hasNode("a")); + assertTrue(graph.hasNode("b")); + assertTrue(graph.hasNode("c")); + assertFalse(graph.hasNode("d")); + assertTrue(graph.isConnected("a", "b")); + assertTrue(graph.isConnected("b", "a")); + assertFalse(graph.isConnected("a", "c")); + assertFalse(graph.isConnected("b", "c")); + assertFalse(graph.isConnected("c", "a")); + assertFalse(graph.isConnected("c", "b")); + assertFalse(graph.isConnected("a", "a")); + assertFalse(graph.isConnected("b", "b")); + assertFalse(graph.isConnected("b", "c")); + + // Removal. + graph.disconnect("a", "b"); + assertFalse(graph.isConnected("a", "b")); + assertFalse(graph.isConnected("b", "a")); + } + + public void testDirectedSelfLoop() { + DiGraph graph = + LinkedDirectedGraph.create(); + graph.createNode("a"); + graph.createNode("b"); + graph.connect("a", "->", "a"); + assertTrue(graph.isConnected("a", "a")); + assertFalse(graph.isConnected("a", "b")); + assertFalse(graph.isConnected("b", "a")); + assertTrue(graph.isConnectedInDirection("a", "a")); + assertFalse(graph.isConnectedInDirection("a", "b")); + assertFalse(graph.isConnectedInDirection("b", "a")); + + // Removal. + graph.disconnect("a", "a"); + assertFalse(graph.isConnected("a", "a")); + + // Disconnect both ways. + graph.connect("a", "->", "a"); + graph.disconnect("a", "a"); + assertFalse(graph.isConnected("a", "a")); + assertFalse(graph.isConnected("a", "a")); + + // Disconnect one way. + graph.connect("a", "->", "a"); + graph.disconnectInDirection("a", "a"); + assertFalse(graph.isConnected("a", "a")); + } + + public void testUndirectedSelfLoop() { + UndiGraph graph = + LinkedUndirectedGraph.create(); + graph.createNode("a"); + graph.createNode("b"); + graph.connect("a", "--", "a"); + assertTrue(graph.isConnected("a", "a")); + assertFalse(graph.isConnected("a", "b")); + assertFalse(graph.isConnected("b", "a")); + + // Removal. + graph.disconnect("a", "a"); + assertFalse(graph.isConnected("a", "a")); + } + + public void testDirectedInAndOutEdges() { + DiGraph graph = + LinkedDirectedGraph.create(); + graph.createNode("a"); + graph.createNode("b"); + graph.createNode("c"); + graph.createNode("d"); + graph.connect("a", "->", "b"); + graph.connect("a", "-->", "b"); + graph.connect("a", "--->", "b"); + graph.connect("a", "->", "c"); + graph.connect("c", "->", "d"); + assertSetEquals(graph.getDirectedSuccNodes("a"), "b", "c"); + assertSetEquals(graph.getDirectedPredNodes("b"), "a"); + assertSetEquals(graph.getDirectedPredNodes("c"), "a"); + assertListCount(graph.getDirectedSuccNodes("a"), "b", 3); + + // Removal. + graph.disconnect("a", "b"); + assertFalse(graph.isConnected("a", "b")); + } + + public void testUndirectedNeighbors() { + UndiGraph graph = + LinkedUndirectedGraph.create(); + graph.createNode("a"); + graph.createNode("b"); + graph.createNode("c"); + graph.createNode("d"); + graph.connect("a", "-", "b"); + graph.connect("a", "--", "b"); + graph.connect("a", "---", "b"); + graph.connect("a", "-", "c"); + graph.connect("c", "-", "d"); + assertSetEquals(graph.getNeighborNodes("a"), "b", "c"); + assertSetEquals(graph.getNeighborNodes("b"), "a"); + assertSetEquals(graph.getNeighborNodes("c"), "a", "d"); + assertListCount(graph.getNeighborNodes("a"), "b", 3); + + // Removal. + graph.disconnect("a", "b"); + assertFalse(graph.isConnected("a", "b")); + } + + public void testDirectedGetFirstEdge() { + DiGraph graph = + LinkedDirectedGraph.create(); + graph.createNode("a"); + graph.createNode("b"); + graph.createNode("c"); + graph.connect("a", "-", "b"); + assertEquals(graph.getFirstEdge("a", "b").getValue(), "-"); + assertEquals(graph.getFirstEdge("b", "a").getValue(), "-"); + assertNull(graph.getFirstEdge("a", "c")); + } + + public void testUndirectedGetFirstEdge() { + UndiGraph graph = + LinkedUndirectedGraph.create(); + graph.createNode("a"); + graph.createNode("b"); + graph.createNode("c"); + graph.connect("a", "-", "b"); + assertEquals(graph.getFirstEdge("a", "b").getValue(), "-"); + assertEquals(graph.getFirstEdge("b", "a").getValue(), "-"); + assertNull(graph.getFirstEdge("a", "c")); + } + + public void testNodeAnnotations() { + Graph graph = LinkedUndirectedGraph.create(); + GraphNode a = graph.createNode("a"); + GraphNode b = graph.createNode("b"); + checkAnnotations(graph, a, b); + } + + public void testEdgeAnnotations() { + Graph graph = LinkedUndirectedGraph.create(); + graph.createNode("1"); + graph.createNode("2"); + graph.createNode("3"); + graph.connect("1", "a", "2"); + graph.connect("2", "b", "3"); + GraphEdge a = graph.getEdges("1", "2").get(0); + GraphEdge b = graph.getEdges("2", "3").get(0); + checkAnnotations(graph, a, b); + } + + private static void checkAnnotations( + Graph graph, Annotatable a, Annotatable b) { + final Annotation A = new Annotation() {}; + final Annotation B = new Annotation() {}; + + // Initially null. + assertNull(a.getAnnotation()); + assertNull(b.getAnnotation()); + + // Test basic setting. + a.setAnnotation(A); + b.setAnnotation(B); + assertSame(A, a.getAnnotation()); + assertSame(B, b.getAnnotation()); + + // Test clearing. + graph.clearEdgeAnnotations(); + graph.clearNodeAnnotations(); + assertNull(a.getAnnotation()); + assertNull(b.getAnnotation()); + + a.setAnnotation(A); + b.setAnnotation(B); + // Pushing clears. + graph.pushEdgeAnnotations(); + graph.pushNodeAnnotations(); + assertNull(a.getAnnotation()); + assertNull(b.getAnnotation()); + a.setAnnotation(B); + b.setAnnotation(B); + graph.pushEdgeAnnotations(); + graph.pushNodeAnnotations(); + a.setAnnotation(B); + b.setAnnotation(A); + + // Test restoring then restoring old values with pop. + assertSame(B, a.getAnnotation()); + assertSame(A, b.getAnnotation()); + graph.popEdgeAnnotations(); + graph.popNodeAnnotations(); + assertSame(B, a.getAnnotation()); + assertSame(B, b.getAnnotation()); + graph.popEdgeAnnotations(); + graph.popNodeAnnotations(); + assertSame(A, a.getAnnotation()); + assertSame(B, b.getAnnotation()); + } + + public void testDegree() { + testDirectedDegree(LinkedDirectedGraph.create()); + testDirectedDegree(LinkedUndirectedGraph.create()); + } + + public void testDirectedDegree(Graph graph) { + graph.createNode("a"); + graph.createNode("b"); + graph.createNode("c"); + graph.createNode("d"); + assertEquals(0, graph.getNodeDegree("a")); + graph.connect("a", "-", "b"); + assertEquals(1, graph.getNodeDegree("a")); + graph.connect("b", "-", "c"); + assertEquals(1, graph.getNodeDegree("a")); + graph.connect("a", "-", "c"); + assertEquals(2, graph.getNodeDegree("a")); + graph.connect("d", "-", "a"); + assertEquals(3, graph.getNodeDegree("a")); + } + + public void testDirectedConnectIfNotFound() { + testDirectedConnectIfNotFound( + LinkedDirectedGraph.create()); + testDirectedConnectIfNotFound( + LinkedUndirectedGraph.create()); + } + + public void testDirectedConnectIfNotFound(Graph graph) { + graph.createNode("a"); + graph.createNode("b"); + graph.connectIfNotFound("a", "-", "b"); + assertEquals(1, graph.getNodeDegree("a")); + graph.connectIfNotFound("a", "-", "b"); + assertEquals(1, graph.getNodeDegree("a")); + graph.connectIfNotFound("a", null, "b"); + assertEquals(2, graph.getNodeDegree("a")); + graph.connectIfNotFound("a", null, "b"); + assertEquals(2, graph.getNodeDegree("a")); + } + + public void testSimpleSubGraph() { + UndiGraph graph = + LinkedUndirectedGraph.create(); + graph.createNode("a"); + graph.createNode("b"); + graph.createNode("c"); + graph.connect("a", "--", "b"); + + SubGraph subGraph = graph.newSubGraph(); + subGraph.addNode("a"); + subGraph.addNode("b"); + + try { + subGraph.addNode("d"); + fail("SubGraph should not allow add for node that is not in graph."); + } catch (IllegalArgumentException e) { + // exception expected + } + + assertFalse(subGraph.isIndependentOf("a")); + assertFalse(subGraph.isIndependentOf("b")); + assertTrue(subGraph.isIndependentOf("c")); + } + + private > void assertListCount( + List list, String target, int count) { + for (GraphNode node : list) { + if (node.getValue().equals(target)) { + count--; + } + } + assertTrue(count == 0); + } + + private > void assertSetEquals( + List list, String ... targets) { + Set set = new HashSet(); + for (GraphNode node : list) { + set.add(node.getValue()); + } + Set otherSet = new HashSet(); + for (String target : targets) { + otherSet.add(target); + } + assertTrue(otherSet.equals(set)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/graph/StandardUnionFindTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/graph/StandardUnionFindTest.java new file mode 100644 index 0000000..344fd4a --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/graph/StandardUnionFindTest.java @@ -0,0 +1,240 @@ +/* + * Copyright 2008 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.graph; + +import com.google.common.collect.HashMultiset; +import com.google.common.collect.ImmutableSet; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +/** + * Unit test for the {@link StandardUnionFind} data structure. + * + */ +public class StandardUnionFindTest extends TestCase { + private StandardUnionFind union; + + @Override protected void setUp() { + union = new StandardUnionFind(); + } + + public void testEmpty() { + assertEquals(0, union.allEquivalenceClasses().size()); + } + + public void testAdd() { + union.add("foo"); + union.add("bar"); + assertTrue(null != union.find("foo")); + assertEquals(2, union.allEquivalenceClasses().size()); + } + + public void testUnion() { + union.union("A", "B"); + union.union("C", "D"); + assertEquals(union.find("A"), union.find("B")); + assertEquals(union.find("C"), union.find("D")); + assertFalse(union.find("A").equals(union.find("D"))); + } + + public void testSetSize() { + union.union("A", "B"); + union.union("B", "C"); + union.union("D", "E"); + union.union("F", "F"); + + assertEquals(3, union.findAll("A").size()); + assertEquals(3, union.findAll("B").size()); + assertEquals(3, union.findAll("C").size()); + assertEquals(2, union.findAll("D").size()); + assertEquals(1, union.findAll("F").size()); + + } + + public void testFind() { + union.add("A"); + union.add("B"); + assertEquals("A", union.find("A")); + assertEquals("B", union.find("B")); + + union.union("A", "B"); + assertEquals(union.find("A"), union.find("B")); + + try { + union.find("Z"); + fail("find() on unknown element should not be allowed."); + } catch (IllegalArgumentException e) { + } + } + + public void testAllEquivalenceClasses() { + union.union("A", "B"); + union.union("A", "B"); + union.union("B", "A"); + union.union("B", "C"); + union.union("D", "E"); + union.union("F", "F"); + + Collection> classes = union.allEquivalenceClasses(); + assertEquals(3, classes.size()); + assertContentsAnyOrder(classes, + ImmutableSet.of("A", "B", "C"), + ImmutableSet.of("D", "E"), + ImmutableSet.of("F")); + } + + public void testFindAll() { + union.union("A", "B"); + union.union("A", "B"); + union.union("B", "A"); + union.union("D", "E"); + union.union("F", "F"); + + Set aSet = union.findAll("A"); + assertEquals(2, aSet.size()); + assertTrue(aSet.contains("A")); + assertTrue(aSet.contains("B")); + assertFalse(aSet.contains("C")); + assertFalse(aSet.contains("D")); + assertFalse(aSet.contains("E")); + assertFalse(aSet.contains("F")); + + union.union("B", "C"); + assertTrue(aSet.contains("C")); + assertEquals(3, aSet.size()); + + try { + union.findAll("Z"); + fail("findAll() on unknown element should not be allowed."); + } catch (IllegalArgumentException e) { + } + } + + public void testFindAllIterator() { + union.union("A", "B"); + union.union("B", "C"); + union.union("A", "B"); + union.union("D", "E"); + + Set aSet = union.findAll("A"); + Iterator aIter = aSet.iterator(); + assertTrue(aIter.hasNext()); + assertEquals("A", aIter.next()); + assertEquals("B", aIter.next()); + assertEquals("C", aIter.next()); + assertFalse(aIter.hasNext()); + + Set dSet = union.findAll("D"); + Iterator dIter = dSet.iterator(); + assertTrue(dIter.hasNext()); + assertEquals("D", dIter.next()); + assertEquals("E", dIter.next()); + assertFalse(dIter.hasNext()); + } + + public void testFindAllSize() { + union.union("A", "B"); + union.union("B", "C"); + assertEquals(3, union.findAll("A").size()); + assertEquals(3, union.findAll("B").size()); + assertEquals(3, union.findAll("C").size()); + union.union("D", "E"); + assertEquals(3, union.findAll("C").size()); + assertEquals(2, union.findAll("D").size()); + union.union("B", "E"); + assertEquals(5, union.findAll("C").size()); + assertEquals(5, union.findAll("D").size()); + } + + public void testElements(){ + union.union("A", "B"); + union.union("B", "C"); + union.union("A", "B"); + union.union("D", "E"); + + Set elements = union.elements(); + assertEquals(ImmutableSet.of("A", "B", "C", "D", "E"), elements); + assertFalse(elements.contains("F")); + } + + public void testCopy() { + union.union("A", "B"); + union.union("B", "Z"); + union.union("X", "Y"); + UnionFind copy = new StandardUnionFind(union); + assertContentsAnyOrder(copy.findAll("Z"), "A", "B", "Z"); + assertContentsAnyOrder(copy.findAll("X"), "X", "Y"); + } + + public void testChangesToCopyDontAffectOriginal() { + union.union("A", "B"); + union.union("X", "Y"); + union.union("A", "C"); + UnionFind copy = new StandardUnionFind(union); + copy.union("A", "D"); + assertContentsAnyOrder(copy.findAll("D"), "A", "B", "C", "D"); + assertContentsAnyOrder(union.findAll("A"), "A", "B", "C"); + assertContentsAnyOrder(copy.findAll("X"), "X", "Y"); + try { + union.findAll("D"); + fail("D has been inserted to the original collection"); + } catch (IllegalArgumentException e) { + // Expected. + } + } + + public void testCheckEquivalent() { + union.union("A", "B"); + union.add("C"); + assertTrue(union.areEquivalent("A", "B")); + assertFalse(union.areEquivalent("C", "A")); + assertFalse(union.areEquivalent("C", "B")); + try { + union.areEquivalent("A", "F"); + } catch (IllegalArgumentException e) { + // Expected. + } + } + + /** + * Asserts that {@code actual} contains precisely the elements + * {@code expected}, in any order. Both collections may contain + * duplicates, and this method will only pass if the quantities are + * exactly the same. + */ + private static void assertContentsAnyOrder( + String message, Iterable actual, Object... expected) { + Assert.assertEquals(message, + HashMultiset.create(Arrays.asList(expected)), + HashMultiset.create(actual)); + } + + /** + * Variant of {@link #assertContentsAnyOrder(String,Iterable,Object...)} + * using a generic message. + */ + private static void assertContentsAnyOrder( + Iterable actual, Object... expected) { + assertContentsAnyOrder((String) null, actual, expected); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/jsonml/JsonMLConversionTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/jsonml/JsonMLConversionTest.java new file mode 100644 index 0000000..6a0723f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/jsonml/JsonMLConversionTest.java @@ -0,0 +1,598 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.jsonml; + +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.Compiler; +import com.google.javascript.jscomp.CompilerPass; +import com.google.javascript.jscomp.CompilerTestCase; +import com.google.javascript.rhino.Token; +import com.google.javascript.jscomp.jsonml.Writer; +import com.google.javascript.jscomp.jsonml.JsonML; +import com.google.javascript.jscomp.jsonml.JsonMLAst; +import com.google.javascript.jscomp.jsonml.JsonMLUtil; + +import com.google.javascript.rhino.Node; + +import com.google.caja.parser.js.JsonMLParser; + +/** + * Tests for parsing JsonML to AST and vice versa. + * @author dhans@google.com (Daniel Hans) + * + */ +public class JsonMLConversionTest extends CompilerTestCase { + + @Override + public CompilerPass getProcessor(Compiler compiler) { + return null; // unused + } + + @Override + public void setUp() { + enableEcmaScript5(true); + } + + private void testJsonMLToAstConversion(String js) throws Exception { + JsonML jsonml = JsonMLParser.parse(js); + Node root = parseExpectedJs(js); + Node ast = root.getFirstChild(); + Preconditions.checkState(ast.getType() == Token.SCRIPT); + + testJsonMLToAstConversion(ast, jsonml, js); + } + + private void testJsonMLToAstConversion(Node astRoot, JsonML jsonmlRoot, + String js) { + Compiler compiler = new Compiler(); + JsonMLAst ast = new JsonMLAst(jsonmlRoot); + Node resultAstRoot = ast.getAstRoot(compiler); + + String explanation = resultAstRoot.checkTreeEquals(astRoot); + assertNull("JsonML -> AST converter returned incorect result for " + js + + "\n" + explanation, explanation); + } + + private void testAstToJsonMLConverstion(Node astRoot, JsonML jsonmlRoot, + String js) { + JsonML resultJsonMLRoot = (new Writer()).processAst(astRoot); + String explanation = JsonMLUtil.compare(resultJsonMLRoot, jsonmlRoot); + assertNull("AST -> JsonML converter returned incorrect result for " + js + + "\n" + explanation, explanation); + } + + private void testConversion(String js) throws Exception { + JsonML jsonml = JsonMLParser.parse(js); + Node root = parseExpectedJs(js); + Node ast = root.getFirstChild(); + Preconditions.checkState(ast.getType() == Token.SCRIPT); + + testJsonMLToAstConversion(ast, jsonml, js); + + jsonml = JsonMLParser.parse(js); + testAstToJsonMLConverstion(ast, jsonml, js); + } + + public void testArray() throws Exception { + testConversion("[,]"); + testConversion("[]"); + testConversion("[function (x) {}]"); + testConversion("[[], [a, [], [[[]], 1], f([a])], 1];"); + testConversion("x = [1, 2, 3]"); + testConversion("var x = [1, 2, 3]"); + testConversion("[, 1, Object(), , , 2]"); + testConversion("[{x: 'abc', y: 1}]"); + } + + public void testArray1() throws Exception { + testConversion("[,]"); + } + + public void testAssignOperators() throws Exception { + testConversion("x += 1, x -= 1, x *= 1, x /= 1, x %= 1"); + testConversion("x |= 1, x ^= x, x &= 0"); + testConversion("x <<= 1, x >>= 1, x >>>= 1"); + testConversion("y = x += 1"); + } + + public void testCalls() throws Exception { + testConversion("f()"); + testConversion("f(1)"); + testConversion("f('a')"); + testConversion("f(true)"); + testConversion("f(null)"); + testConversion("f(undefined)"); + + testConversion("f(a + b)"); + testConversion("f(g(h(a)) * h(g(u(z('a')))))"); + + testConversion("x = f()"); + testConversion("x = f(1)"); + testConversion("x = f(a + b)"); + testConversion("x = f(g(h(a)) * h(g(u(z('a')))))"); + + testConversion("String('a')"); + testConversion("Number(1)"); + testConversion("Boolean(0)"); + testConversion("Object()"); + testConversion("Array('a', 1, false, null, Object(), String('a'))"); + + testConversion("(function() {})()"); + testConversion("(function(x) {})(x)"); + testConversion("(function(x) {var y = x << 1; return y})(x)"); + testConversion("(function(x) {y = x << 1; return y})(x)"); + testConversion("var x = (function(x) {y = x << 1; return y})(x)"); + testConversion("var x = (function(x) {return x << 1})(x)"); + + testConversion("eval()"); + testConversion("eval('x')"); + testConversion("x = eval('x')"); + testConversion("var x = eval('x')"); + testConversion("eval(Template('foo${bar}baz')); var Template;"); + + testConversion("a.x()"); + testConversion("a[x]()"); + testConversion("z = a.x()"); + testConversion("var z = a.x()"); + testConversion("z = a[x]()"); + testConversion("z = a['x']()"); + testConversion("var z = a[x]()"); + testConversion("var z = a['x']()"); + testConversion("a.x(y)"); + testConversion("a[x](y)"); + testConversion("a['x'](y)"); + testConversion("a[x](y, z, 'a', null, true, f(y))"); + testConversion("a['x'](y, z, 'a', null, true, f(y))"); + testConversion("a[b[c[d]]()].x"); + + testConversion("(f())()"); + testConversion("(f(x))(y)"); + testConversion("(f = getFn())()"); + } + + public void testConditionals() throws Exception { + testConversion("x ? y : z"); + testConversion("result = x ? y : z"); + } + public void testDecIncOperators() throws Exception { + testConversion("x--"); + testConversion("--x"); + testConversion("x++"); + testConversion("++x"); + testConversion("var y=x++, z=++x; var s=y--, r=++y;"); + } + + public void testDelete() throws Exception { + testConversion("delete a"); + testConversion("delete a.x"); + testConversion("delete a[0]"); + testConversion("delete a.x[0]"); + } + + public void testDirectives() throws Exception { + testConversion("'use strict'"); + testConversion("function foo() {'use strict'}"); + testConversion("'use strict'; function foo() {'use strict'}"); + } + + public void testDoWhile() throws Exception { + // testConversion("do; while (true)"); + testConversion("do {} while (true)"); + testConversion("do {;} while (true)"); + testConversion("do {} while (f(x, y))"); + testConversion("do {} while (f(f(f(x, y))))"); + testConversion("do {} while ((f(f(f(x, y))))())"); + testConversion("do {2 + 3; q = 2 + 3; var v = y * z;" + + "g = function(a) {true; var b = a + 1; return a * a}} while (--x)"); + } + + public void testFor() throws Exception { + testConversion("for (;true;) {;}"); + testJsonMLToAstConversion("for (i = 0; i < 10; ++i) x++"); + testConversion("for (i = 0; i < 10; ++i) {x++}"); + testConversion("for (i = 0; i < 10; ++i) {2 + 3; q = 2 + 3; " + + "var v = y * z; g = function(a) {true; var b = a + 1;" + + "return a * a}}"); + + testConversion("for(;true;) {break}"); + testConversion("for(i = 0; i < 10; ++i) {if (i > 5) {break}}"); + testConversion("s: for(i = 0; i < 10; ++i) {if (i > 5) {break s}}"); + testConversion("for (i = 0;true; ++i) {" + + "if (i % 2) {continue} else {var x = i / 3; f(x)}}"); + } + + public void testForIn() throws Exception { + testConversion("for (var i in x) {}"); + testConversion("for (var i in x) {;}"); + testConversion("for (var i in x) {f(x)}"); + testConversion("s: for(var i in x) {if (i > 5) {break s}}"); + testConversion("for (var i in x) {if (i % 2) {" + + "continue} else {var x = i / 3; f(x)}}"); + testConversion("for (var i in x) {2 + 3; q = 2 + 3; var v = y * z; " + + "g = function(a) {true; var b = a + 1; return a * a}}"); + + testConversion("for (i in x) {}"); + testConversion("for (i in x) {;}"); + testConversion("for (i in x) {f(x)}"); + testConversion("s: for (i in x) {if (i > 5) {break s}}"); + testConversion("for (i in x) {if (i % 2) {" + + "continue} else {var x = i / 3; f(x)}}"); + testConversion("for (i in x) {2 + 3; q = 2 + 3; var v = y * z; " + + "g = function(a) {true; var b = a + 1; return a * a}}"); + + } + + public void testFunctions() throws Exception { + testConversion("(function () {})"); + testConversion("(function (x, y) {})"); + testConversion("(function () {})()"); + testConversion("(function (x, y) {})()"); + testConversion("[ function f() {} ]"); + testConversion("var f = function f() {};"); + testConversion("for (function f() {};true;) {}"); + testConversion("x = (function (x, y) {})"); + + testConversion("function f() {}"); + testConversion("for (;true;) { function f() {} }"); + + testConversion("function f() {;}"); + testConversion("function f() {x}"); + testConversion("function f() {x;y;z}"); + testConversion("function f() {{}}"); + } + + public void testIfElse1() throws Exception { + testConversion("if (true) {x = 1}"); + testConversion("if (true) {x = 1} else {x = 2}"); + testConversion("if (f(f(f()))) {x = 1} else {x = 2}"); + testConversion("if ((f(f(f())))()) {x = 1} else {x = 2}"); + testConversion("if (true) {x = 1}; x = 1;"); + } + + public void testLabels() throws Exception { + testConversion("s: ;"); + testConversion("s: {;}"); + testConversion("s: while(true) {;}"); + testConversion("s: switch (x) {case 'a': break s;}"); + } + + public void testLogicalExpr() throws Exception { + testConversion("a && b"); + testConversion("a || b"); + testConversion("a && b || c"); + testConversion("a && (b || c)"); + testConversion("f(x) && (function (x) {" + + "return x % 2 == 0 })(z) || z % 3 == 0 ? true : false"); + } + + public void testMathExpr() throws Exception { + testConversion("2 + 3 * 4"); + testConversion("(2 + 3) * 4"); + testConversion("2 * (3 + 4)"); + } + + public void testMember() throws Exception { + testConversion("o.x"); + testConversion("a.b.c"); + testConversion("a.b.c.d"); + testConversion("o[x]"); + testConversion("o[0]"); + testConversion("o[2 + 3 * 4]"); + testConversion("o[(function (x){var y = g(x) << 1; return y * x})()]"); + testConversion("o[o.x]"); + testConversion("o.x[x]"); + testConversion("a.b[o.x]"); + testConversion("a.b[1]"); + testConversion("a[b[c[d]]].x"); + } + + public void testNew() throws Exception { + testConversion("new A"); + testConversion("new A()"); + + testConversion("new A(x, y, z)"); + testConversion("new A(f(x), g(y), h(z))"); + testConversion("new A(x, new B(x, y), z)"); + testConversion("new A(1), new B()"); + testConversion("new A, B"); + + testConversion("x = new A(a)"); + testConversion("var x = new A(a, b)"); + testConversion("var x = new A(1), y = new B()"); + } + + public void testObject0() throws Exception { + // TODO(johnlenz): quoted object literal properties are not noted. + // testConversion("({'a':0})"); + // TODO(johnlenz): numbers are represented as strings + // testConversion("({1:0})"); + } + + public void testObject() throws Exception { + testConversion("x = {}"); + testConversion("var x = {}"); + testConversion("x = {x: 1, y: 2}"); + // testConversion("var x = {'2': 1, 'a': 2}"); + // testConversion("var x = {2: 1, a: 2}"); + testConversion("x = {x: null}"); + testConversion("x = {a: function f() {}}"); + // testConversion("x = {1: function f() {}}"); + testConversion("x = {a: f()}"); + // testConversion("x = {1: f()}"); + testConversion("x = {a: function f() {2 + 3; q = 2 + 3; var v = y * z; " + + "g = function(a) {true; var b = a + 1; return a * a}}}"); + // testConversion("x = {1: function f() {2 + 3; q = 2 + 3; var v = y * z; " + // + "g = function(a) {true; var b = a + 1; return a * a}}}"); + testConversion("x = {get a() {return 1}}"); + testConversion("x = {set a(b) {}}"); + } + + public void testOperators() throws Exception { + testConversion("x instanceof Null"); + testConversion("!x instanceof A"); + testConversion("!(x instanceof A)"); + + testConversion("'a' in x"); + testConversion("if('a' in x) {f(x)}"); + testConversion("undefined in A"); + testConversion("!(Number(1) in [2, 3, 4])"); + + testConversion("true ? x : y"); + testConversion("(function() {var y = 2 + 3 * 4; return y >> 1})() ? x : y"); + } + + public void testReturnStatement() throws Exception { + testConversion("x = function f() {return}"); + testConversion("x = function f() {return 1}"); + testConversion("x = function f() {return 2 + 3 / 4}"); + testConversion("x = function f() {return function() {}}"); + testConversion("x = function f() {var y = 2; " + + "return function() {return y * 3}}"); + testConversion("x = function f() {z = 2 + 3; " + + "return (function(z) {return z * y})(z)}"); + } + + public void testRegExp() throws Exception { + testConversion("/ab/"); + testConversion("/ab/g"); + testConversion("x = /ab/"); + testConversion("x = /ab/g"); + testConversion("var x = /ab/"); + testConversion("var x = /ab/g"); + testConversion("function f() {" + + "/ab/; var x = /ab/; (function g() {/ab/; var x = /ab/})()}"); + testConversion("var f = function () {return /ab/g;}"); + } + + public void testSimplePrograms() throws Exception { + testConversion(";"); + testConversion("1"); + testConversion("x"); + testConversion("x=1"); + testConversion("{}"); + testConversion("{;}"); + testConversion("{x=1}"); + testConversion("x='a'"); + + testConversion("true"); + testConversion("false"); + testConversion("x=true"); + testConversion("x=false"); + + testConversion("undefined"); + testConversion("x=undefined"); + + testConversion("null"); + testConversion("x = null"); + + testConversion("this"); + testConversion("2 + 3; q = 2 + 3; var v = y * z; " + + "g = function(a) {true; var b = a + 1; return a * a}"); + + testConversion("a; b"); + testConversion("a; b; c; d"); + + testConversion("x = function () {}"); + testConversion("x = function f() {}"); + + testConversion("x = function (arg1, arg2) {}"); + testConversion("x = function f(arg1, arg2) {}"); + + testConversion("x = function f(arg1, arg2) {1}"); + testConversion("x = function f(arg1, arg2) {x}"); + + testConversion("x = function f(arg1, arg2) {x = 1 + 1}"); + + testConversion("var re = new RegExp(document.a.b.c);" + + "var m = re.exec(document.a.b.c);"); + + } + + public void testSwitch() throws Exception { + testConversion("switch (x) {}"); + testConversion("switch (x) {case 'a':}"); + testConversion("switch (x) {case 'a':case 'b':}"); + testConversion("switch (x) {case 'a':case 'b': x}"); + testConversion("switch (x) {case 'a':case 'b': {;}}"); + testConversion("switch (x) {case 'a':case 'b': f()}"); + testConversion("switch (x) {case 'x': case 'y': {;} case 'a':case 'b': f()}"); + testConversion("switch (x) {case 'a': f(x)}"); + testConversion("switch (x) {case 'a': {f()} {g(x)}}"); + testConversion("switch (x) {case 'a': f(); g(x)}"); + testConversion("switch (x) {default: ;}"); + testConversion("switch (x) {default:case 'a': ;}"); + testConversion("switch (x) {case 'a':case'b':default: f()}"); + testConversion("switch (x) {default:f(x); g(); case 'a': ; case 'b': g(x)}"); + testConversion("switch (x) {case 'a': default: {f(x); g(z)} case 'b': g(x)}"); + testConversion("switch (x) {case x: {;}}"); +} + + public void testType() throws Exception { + testConversion("undefined"); + testConversion("null"); + + testConversion("0"); + testConversion("+0"); + testConversion("0.0"); + + testConversion("3.14"); + testConversion("+3.14"); + + testConversion("true"); + testConversion("false"); + } + + public void testThis() throws Exception { + testConversion("this"); + testConversion("var x = this"); + testConversion("this.foo()"); + testConversion("var x = this.foo()"); + testConversion("this.bar"); + testConversion("var x = this.bar()"); + testConversion("switch(this) {}"); + testConversion("x + this"); + } + + public void testThrow() throws Exception { + testConversion("throw e"); + testConversion("throw 2 + 3 * 4"); + testConversion("throw (function () {2 + 3; q = 2 + 3; var v = y * z; " + + "g = function(a) {true; var b = a + 1; return a * a}})()"); + testConversion("throw f(x)"); + testConversion("throw f(f(f(x)))"); + testConversion("throw (f(f(x), y))()"); + } + + public void testTry() throws Exception { + testConversion("try {} catch (e) {}"); + testConversion("try {;} catch (e) {;}"); + testConversion("try {var x = 0; y / x} catch (e) {f(e)}"); + testConversion("try {2 + 3; q = 2 + 3; var v = y * z; " + + "g = function(a) {true; var b = a + 1; h(q); return a * a}; " + + "h(q)} catch (e) {f(x)}"); + + testConversion("try {} finally {}"); + testConversion("try {;} finally {;}"); + testConversion("try {var x = 0; y / x} finally {f(y)}"); + testConversion("try {2 + 3; q = 2 + 3; var v = y * z; " + + "g = function(a) {true; var b = a + 1; h(q); return a * a}; " + + "h(q)} finally {f(x)}"); + + testConversion("try {} catch (e) {} finally {}"); + testConversion("try {;} catch (e) {;} finally {;}"); + testConversion("try {var x = 0; y / x} catch (e) {;} finally {;}"); + testConversion("try {2 + 3; q = 2 + 3; var v = y * z; " + + "g = function(a) {true; var b = a + 1; h(q); return a * a}; h(q)} " + + "catch (e) {f(x)} finally {f(x)}"); + } + + public void testTypeof() throws Exception { + testConversion("typeof undefined"); + testConversion("typeof null"); + testConversion("typeof 1"); + testConversion("typeof 'a'"); + testConversion("typeof false"); + + testConversion("typeof Null()"); + testConversion("typeof Number(1)"); + testConversion("typeof String('a')"); + testConversion("typeof Boolean(0)"); + + testConversion("typeof x"); + testConversion("typeof new A()"); + testConversion("typeof new A(x)"); + testConversion("typeof f(x)"); + testConversion("typeof (function() {})()"); + testConversion("typeof 2 + 3 * 4"); + + testConversion("typeof typeof x"); + testConversion("typeof typeof typeof x"); + } + + public void testUnaryExpressions() throws Exception { + testConversion("!x"); + testConversion("!null"); + testConversion("!3.14"); + testConversion("!true"); + + testConversion("~x"); + testConversion("~null"); + testConversion("~3.14"); + testConversion("~true"); + + testConversion("+x"); + testConversion("+null"); + testConversion("+3.14"); + testConversion("+true"); + + testConversion("-x"); + testConversion("-null"); + testConversion("-true"); + + testConversion("!~+-z"); + testConversion("void x"); + testConversion("void null"); + testConversion("void void !x"); + testConversion("void (x + 1)"); + } + + public void testVarDeclarations() throws Exception { + testConversion("var x"); + testConversion("var x = 1"); + testConversion("var x = 1 + 1"); + testConversion("var x = 'a' + 'b'"); + + testConversion("var x, y, z"); + testConversion("var x = 2, y = 2 * x, z"); + + testConversion("var x = function () {}"); + testConversion("var x = function f() {}"); + testConversion("var x = function f(arg1, arg2) {}"); + + testConversion("var x = function f(arg1, arg2) {1}"); + testConversion("var x = function f(arg1, arg2) {x}"); + testConversion("var x = function f(arg1, arg2) {x = 2 * 3}"); + + testConversion("var x = function f() {var x}"); + testConversion("var x = function f() {var y = (z + 2) * q}"); + + testConversion("var x = function f(a, b) {" + + "var y = function g(a, b) {z = a + b}}"); + } + + public void testWhile() throws Exception { + testConversion("while (true) {;}"); + testConversion("while (true) {f()}"); + testConversion("while (f(x, y)) {break;}"); + testConversion("while (f(f(f(x, y)))) {}"); + testConversion("while ((f(f(f(x, y))))()) {}"); + + testConversion("while (x--) {2 + 3; q = 2 + 3; var v = y * z; " + + "g = function(a) {true; var b = a + 1; return a * a}}"); + } + + public void testWith() throws Exception { + testConversion("with ({}) {}"); + testConversion("with ({}) {;}"); + testConversion("with (x) {}"); + testConversion("with (x) {f(x)}"); + testConversion("with ({a: function f() {}}) {f(1)}"); + testConversion("with ({z: function f() {2 + 3; q = 2 + 3; var v = y * z;" + + "g = function(a) {true; var b = a + 1; return a * a}}}) {f(1)}"); + testConversion("with (x in X) {x++}"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/jsonml/JsonMLValidationTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/jsonml/JsonMLValidationTest.java new file mode 100644 index 0000000..c8bc663 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/jsonml/JsonMLValidationTest.java @@ -0,0 +1,850 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.jsonml; + +import com.google.javascript.jscomp.jsonml.JsonML; +import com.google.javascript.jscomp.jsonml.JsonMLUtil; +import com.google.javascript.jscomp.jsonml.TagAttr; +import com.google.javascript.jscomp.jsonml.TagType; +import com.google.javascript.jscomp.jsonml.Validator; + +import junit.framework.TestCase; + +/** + * Tests validation of particular JsonML elements. + * + * @author dhans@google.com (Daniel Hans) + */ +public class JsonMLValidationTest extends TestCase { + + // Used for correct statements - error message should be null + private void testValidation(String jsonml) throws Exception { + JsonML jsonMLRoot = JsonMLUtil.parseString(jsonml); + String msg = Validator.validate(jsonMLRoot); + if (msg != null) { + String errorMsg = String.format( + "Validation error for %s.\n Received: %s\n", jsonml, msg); + } + } + + private void testValidation(String jsonml, String expected) + throws Exception { + JsonML jsonMLRoot = JsonMLUtil.parseString(jsonml); + String msg = Validator.validate(jsonMLRoot); + if (!msg.equals(expected)) { + String errorMsg = String.format( + "Validation error for %s.\n Received: %s\n Expected: %s\n", + jsonml, msg, expected); + assertEquals(errorMsg, expected, msg); + } + } + + private void testNotEnoughChildrenValidation(String jsonml, TagType type, + int expected, int actual) throws Exception { + testValidation(jsonml, + String.format(Validator.NOT_ENOUGH_CHILDREN_FMT, + type, expected, actual)); + } + + private void testTooManyChildrenValidation(String jsonml, TagType type, + int expected, int actual) throws Exception { + testValidation(jsonml, + String.format(Validator.TOO_MANY_CHILDREN_FMT, + type, expected, actual)); + } + + private void testWrongChildTypeValidation(String jsonml, TagType type, + TagType expected, TagType actual, int index) throws Exception { + testWrongChildTypeValidation(jsonml, type, new TagType[] { expected }, + actual, index); + } + + private void testWrongChildTypeValidation(String jsonml, TagType type, + TagType[] expected, TagType actual, int index) throws Exception { + testValidation(jsonml, + String.format(Validator.WRONG_CHILD_TYPE_FMT, + index, type, Validator.printList(expected), actual)); + } + + private void testMissingArgument(String jsonml, TagAttr attr, TagType type) + throws Exception { + testValidation(jsonml, + String.format(Validator.MISSING_ARGUMENT, attr, type)); + } + + public void testAssignExpr() throws Exception { + // correct statement + testValidation("" + + "['AssignExpr',{'op':'='}," + + "['IdExpr',{'name':'x'}]," + + "['LiteralExpr',{'type':'number','value':1}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['AssignExpr',{'op':'='}," + + "['IdExpr',{'name':'x'}]]", + TagType.AssignExpr, 2, 1); + testTooManyChildrenValidation("" + + "['AssignExpr',{'op':'='}," + + "['IdExpr',{'name':'x'}]," + + "['IdExpr',{'name':'y'}]," + + "['IdExpr',{'name':'z'}]]", + TagType.AssignExpr, 2, 3); + // missing attribute + testMissingArgument("" + + "['AssignExpr',{}," + + "['IdExpr',{'name':'x'}]," + + "['LiteralExpr',{'type':'number','value':1}]]", + TagAttr.OP, TagType.AssignExpr); + } + + public void testBinaryExpr() throws Exception { + // correct statement + testValidation("" + + "['BinaryExpr',{'op':'+'}," + + "['IdExpr',{'name':'a'}]," + + "['LiteralExpr',{'type':'number','value':1}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['BinaryExpr',{'op':'+'}," + + "['IdExpr',{'name':'a'}]]", + TagType.BinaryExpr, 2, 1); + testTooManyChildrenValidation("" + + "['BinaryExpr',{'op':'&&'}," + + "['IdExpr',{'name':'a'}]," + + "['IdExpr',{'name':'b'}]," + + "['IdExpr',{'name':'c'}]]", + TagType.BinaryExpr, 2, 3); + // missing attribute + testMissingArgument("" + + "['BinaryExpr',{}," + + "['IdExpr',{'name':'a'}]," + + "['LiteralExpr',{'type':'number','value':1}]]", + TagAttr.OP, TagType.BinaryExpr); + } + + public void testCaseValidation() throws Exception { + // correct statement + testValidation("" + + "['Case',{}," + + "['IdExpr',{'name':'a'}]]"); + testValidation("" + + "['Case',{}," + + "['IdExpr',{'name':'a'}]," + + "['CallExpr',{}," + + "['IdExpr',{'name':'foo'}]]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['Case',{}]", + TagType.Case, 1, 0); + } + + public void testCatchValidation() throws Exception { + // correct statement + testValidation("" + + "['CatchClause',{}," + + "['IdPatt',{'name':'e'}]," + + "['BlockStmt',{}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['CatchClause',{}," + + "['IdPatt',{'name':'e'}]]", + TagType.CatchClause, 2, 1); + // wrong children types + testWrongChildTypeValidation("" + + "['CatchClause',{}," + + "['IdExpr',{'name':'e'}]," + + "['BlockStmt',{}]]", + TagType.CatchClause, TagType.IdPatt, TagType.IdExpr, 0); + } + + public void testConditionalExprValidation() throws Exception { + // correct statement + testValidation("" + + "['ConditionalExpr',{}," + + "['BinaryExpr',{'op':'=='}," + + "['IdExpr',{'name':'x'}]," + + "['LiteralExpr',{'type':'number','value':0}]]," + + "['LiteralExpr',{'type':'number','value':0}]," + + "['LiteralExpr',{'type':'number','value':1}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['ConditionalExpr',{}," + + "['BinaryExpr',{'op':'=='}," + + "['IdExpr',{'name':'x'}]," + + "['LiteralExpr',{'type':'number','value':0}]]]", + TagType.ConditionalExpr, 3, 1); + testNotEnoughChildrenValidation("" + + "['ConditionalExpr',{}," + + "['BinaryExpr',{'op':'=='}," + + "['IdExpr',{'name':'x'}]," + + "['LiteralExpr',{'type':'number','value':0}]]," + + "['LiteralExpr',{'type':'number','value':1}]]", + TagType.ConditionalExpr, 3, 2); + } + + public void testCountExprValidation() throws Exception { + // correct statement + testValidation("" + + "['CountExpr',{'isPrefix':false,'op':'++'}," + + "['IdExpr',{'name':'x'}]]"); + // wrong number of children + testTooManyChildrenValidation("" + + "['CountExpr',{'isPrefix':false,'op':'++'}," + + "['IdExpr',{'name':'x'}]," + + "['IdExpr',{'name':'y'}]]", + TagType.CountExpr, 1, 2); + // missing attribute + testMissingArgument("" + + "['CountExpr',{'op':'++'}," + + "['IdExpr',{'name':'x'}]]", + TagAttr.IS_PREFIX, TagType.CountExpr); + testMissingArgument("" + + "['CountExpr',{'isPrefix':false}," + + "['IdExpr',{'name':'x'}]]", + TagAttr.OP, TagType.CountExpr); + } + + public void testDataProp() throws Exception { + // correct statement + testValidation("" + + "['DataProp',{'name':'x'}," + + "['LiteralExpr',{'type':'number','value':1}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['DataProp',{'name':'x'}]", + TagType.DataProp, 1, 0); + // missing argument + testMissingArgument("" + + "['DataProp', {}," + + "['LiteralExpr',{'type':'number','value':1}]]", + TagAttr.NAME, TagType.DataProp); + } + + public void testDeleteExpr() throws Exception { + // correct statement + testValidation("" + + "['DeleteExpr',{}," + + "['IdExpr',{'name':'x'}]]"); + //wrong number of children + testNotEnoughChildrenValidation("" + + "['DeleteExpr',{}]", + TagType.DeleteExpr, 1, 0); + testTooManyChildrenValidation("" + + "['DeleteExpr',{}," + + "['IdExpr',{'name':'x'}]," + + "['IdExpr',{'name':'y'}]]", + TagType.DeleteExpr, 1, 2); + } + + public void testDoWhileStmtValidation() throws Exception { + // correct statement + testValidation("" + + "['DoWhileStmt',{}," + + "['BlockStmt',{}]," + + "['LiteralExpr',{'type':'boolean','value':true}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['DoWhileStmt',{}]", + TagType.DoWhileStmt, 2, 0); + testTooManyChildrenValidation("" + + "['DoWhileStmt',{}," + + "['BlockStmt',{}]," + + "['BlockStmt',{}]," + + "['LiteralExpr',{'type':'boolean','value':true}]]", + TagType.DoWhileStmt, 2, 3); + } + + public void testEmptyStmtValidation() throws Exception { + // correct statement + testValidation("" + + "['EmptyStmt',{}]"); + // wrong number of children + testTooManyChildrenValidation("" + + "['EmptyStmt',{}," + + "['BlockStmt',{}]]", + TagType.EmptyStmt, 0, 1); + } + + public void testForInStmtValidation() throws Exception { + // correct statement + testValidation("" + + "['ForInStmt',{}," + + "['IdExpr',{'name':'x'}]," + + "['ObjectExpr',{}]," + + "['BlockStmt',{}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['ForInStmt',{}," + + "['IdExpr',{'name':'x'}]," + + "['ObjectExpr',{}]],", + TagType.ForInStmt, 3, 2); + testTooManyChildrenValidation("" + + "['ForInStmt',{}," + + "['IdExpr',{'name':'x'}]," + + "['ObjectExpr',{}]," + + "['BlockStmt',{}]," + + "['BlockStmt',{}]]", + TagType.ForInStmt, 3, 4); + } + + public void testForStmtValidation() throws Exception { + // correct statement + testValidation("" + + "['ForStmt',{}," + + "['AssignExpr',{'op':'='}," + + "['IdExpr',{'name':'i'}]," + + "['LiteralExpr',{'type':'number','value':0}]]," + + "['BinaryExpr',{'op':'<'}," + + "['IdExpr',{'name':'i'}]," + + "['IdExpr',{'name':'n'}]]," + + "['CountExpr',{'isPrefix':true,'op':'++'}," + + "['IdExpr',{'name':'i'}]]," + + "['BlockStmt',{}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['ForStmt',{}," + + "['BinaryExpr',{'op':'<'}," + + "['IdExpr',{'name':'i'}]," + + "['IdExpr',{'name':'n'}]]," + + "['CountExpr',{'isPrefix':true,'op':'++'}," + + "['IdExpr',{'name':'i'}]]," + + "['BlockStmt',{}]]", + TagType.ForStmt, 4, 3); + testTooManyChildrenValidation("" + + "['ForStmt',{}," + + "['AssignExpr',{'op':'='}," + + "['IdExpr',{'name':'i'}]," + + "['LiteralExpr',{'type':'number','value':0}]]," + + "['BinaryExpr',{'op':'<'}," + + "['IdExpr',{'name':'i'}]," + + "['IdExpr',{'name':'n'}]]," + + "['CountExpr',{'isPrefix':true,'op':'++'}," + + "['IdExpr',{'name':'i'}]]," + + "['BlockStmt',{}]," + + "['BlockStmt',{}]]", + TagType.ForStmt, 4, 5); + } + + public void testFunctionDeclValidation() throws Exception { + // correct statement + testValidation("" + + "['FunctionDecl',{}," + + "['IdPatt',{'name':'f'}]," + + "['ParamDecl',{}]]"); + testValidation("" + + "['FunctionDecl',{}," + + "['IdPatt',{'name':'f'}]," + + "['ParamDecl',{}]," + + "['IdExpr',{'name':'foo'}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['FunctionDecl',{}," + + "['IdPatt',{'name':'f'}]]", + TagType.FunctionDecl, 2, 1); + // function name not specified + testWrongChildTypeValidation("" + + "['FunctionDecl',{}," + + "['Empty', {}]," + + "['ParamDecl',{}]]", + TagType.FunctionDecl, TagType.IdPatt, TagType.Empty, 0); + // list of formal arguments not specified + testWrongChildTypeValidation("" + + "['FunctionDecl',{}," + + "['IdPatt',{'name':'f'}]," + + "['Empty',{}]]", + TagType.FunctionDecl, TagType.ParamDecl, TagType.Empty, 1); + } + + public void testFunctionExprValidation() throws Exception { + // correct statement + testValidation("" + + "['FunctionExpr',{}," + + "['IdPatt',{'name':'f'}]," + + "['ParamDecl',{}]]"); + testValidation("" + + "['FunctionExpr',{}," + + "['IdPatt',{'name':'f'}]," + + "['ParamDecl',{}]," + + "['IdExpr',{'name':'foo'}]]"); + testValidation("" + + "['FunctionExpr',{}," + + "['Empty', {}]," + + "['ParamDecl',{}]]"); + } + + public void testIdExprValidation() throws Exception { + // correct statement + testValidation("" + + "['IdExpr',{'name':'x'}]"); + // wrong number of children + testTooManyChildrenValidation("" + + "['IdExpr',{'name':'x'}," + + "['BlockStmt',{}]]", + TagType.IdExpr, 0, 1); + // missing name argument + testMissingArgument("" + + "['IdExpr', {}]", + TagAttr.NAME, TagType.IdExpr); + } + + public void testIdPattValidation() throws Exception { + // correct statement + testValidation("" + + "['IdPatt',{'name':'x'}]"); + // wrong number of children + testTooManyChildrenValidation("" + + "['IdPatt',{'name':'x'}," + + "['BlockStmt',{}]]", + TagType.IdPatt, 0, 1); + // missing name argument + testMissingArgument("" + + "['IdPatt', {}]", + TagAttr.NAME, TagType.IdPatt); + } + + public void testIfStmtValidation() throws Exception { + // correct statement + testValidation("" + + "['IfStmt',{}," + + "['LiteralExpr',{'type':'boolean','value':true}]," + + "['BlockStmt',{}]," + + "['EmptyStmt',{}]]"); + testValidation("" + + "['IfStmt',{}," + + "['LiteralExpr',{'type':'boolean','value':true}]," + + "['BlockStmt',{}]," + + "['BlockStmt',{}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['IfStmt',{}," + + "['LiteralExpr',{'type':'boolean','value':true}]," + + "['BlockStmt',{}]]", + TagType.IfStmt, 3, 2); + } + + public void testInvokeExprValidation() throws Exception { + // correct statement + testValidation("" + + "['InvokeExpr',{'op':'.'}," + + "['IdExpr',{'name':'x'}]," + + "['LiteralExpr',{'type':'string','value':'foo'}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['InvokeExpr',{'op':'[]'}," + + "['IdExpr',{'name':'x'}]]", + TagType.InvokeExpr, 2, 1); + // missing attribute + testMissingArgument("" + + "['InvokeExpr',{}," + + "['IdExpr',{'name':'x'}]," + + "['LiteralExpr',{'type':'string','value':'foo'}]]", + TagAttr.OP, TagType.InvokeExpr); + } + + public void testJmpStmtValidation() throws Exception { + // correct statement + testValidation("" + + "['BreakStmt',{}]"); + testValidation("" + + "['BreakStmt',{'label':'s'}]"); + testValidation("" + + "['ContinueStmt',{}]"); + testValidation("" + + "['ContinueStmt',{'label':'s'}]"); + // wrong number of children + testTooManyChildrenValidation("" + + "['BreakStmt',{}," + + "['IdExpr',{'name':'s'}]]", + TagType.BreakStmt, 0, 1); + testTooManyChildrenValidation("" + + "['ContinueStmt',{}," + + "['IdExpr',{'name':'s'}]]", + TagType.ContinueStmt, 0, 1); + } + + public void testLabelledStmtValidation() throws Exception { + // correct statement + testValidation("" + + "['LabelledStmt',{'label':'s'}," + + "['IdExpr',{'name':'x'}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['LabelledStmt',{'label':'s'}]", + TagType.LabelledStmt, 1, 0); + testTooManyChildrenValidation("" + + "['LabelledStmt',{'label':'s'}," + + "['IdExpr',{'name':'x'}]," + + "['IdExpr',{'name':'y'}]]", + TagType.LabelledStmt, 1, 2); + // missing attribute + testMissingArgument("" + + "['LabelledStmt',{}," + + "['IdExpr',{'name':'x'}]]", + TagAttr.LABEL, TagType.LabelledStmt); + } + + public void testLiteralExprValidation() throws Exception { + // correct statement + testValidation("" + + "['LiteralExpr',{'type':'string','value':'x'}]"); + testValidation("" + + "['LiteralExpr',{'type':'boolean','value':'true'}]"); + testValidation("" + + "['LiteralExpr',{'type':'number','value':'1.0'}]"); + // wrong number of children + testTooManyChildrenValidation("" + + "['LiteralExpr',{'type':'number','value':'1.0'}," + + "['BlockStmt',{}]]", + TagType.LiteralExpr, 0, 1); + // missing attribute + testMissingArgument("" + + "['LiteralExpr',{'type':'string'}]", + TagAttr.VALUE, TagType.LiteralExpr); + testMissingArgument("" + + "['LiteralExpr',{'value':'1.0'}]", + TagAttr.TYPE, TagType.LiteralExpr); + } + + public void testLogicalExprValidation() throws Exception { + // correct statement + testValidation("" + + "['LogicalAndExpr',{}," + + "['IdExpr',{'name':'a'}]," + + "['IdExpr',{'name':'b'}]]"); + testValidation("" + + "['LogicalOrExpr',{}," + + "['IdExpr',{'name':'a'}]," + + "['IdExpr',{'name':'b'}]]"); + // wrong number of children + testTooManyChildrenValidation("" + + "['LogicalAndExpr',{}," + + "['IdExpr',{'name':'a'}]," + + "['IdExpr',{'name':'b'}]," + + "['IdExpr',{'name':'c'}]]", + TagType.LogicalAndExpr, 2, 3); + testNotEnoughChildrenValidation("" + + "['LogicalAndExpr',{}," + + "['IdExpr',{'name':'a'}]]", + TagType.LogicalAndExpr, 2, 1); + } + + public void testNewExprValidation() throws Exception { + // correct statement + testValidation("" + + "['NewExpr',{}," + + "['IdExpr',{'name':'A'}]," + + "['IdExpr',{'name':'x'}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['NewExpr',{}]", + TagType.NewExpr, 1, 0); + } + + public void testObjectExprValidation() throws Exception { + // correct statement + testValidation("" + + "['ObjectExpr',{}]"); + testValidation("" + + "['ObjectExpr',{}," + + "['DataProp',{'name':'x'}," + + "['LiteralExpr',{'type':'number','value':1}]]," + + "['DataProp',{'name':'y'}," + + "['LiteralExpr',{'type':'number','value':2}]]]"); + // wrong types of children + TagType[] tags = + {TagType.DataProp, TagType.GetterProp, TagType.SetterProp }; + testWrongChildTypeValidation("" + + "['ObjectExpr',{}," + + "['DataProp',{'name':'x'}," + + "['LiteralExpr',{'type':'number','value':1}]]," + + "['IdExpr',{'name':'y'}]]", + TagType.ObjectExpr, tags, TagType.IdExpr, 1); + } + + public void testParamDeclValidation() throws Exception { + // correct statement + testValidation("" + + "['ParamDecl',{}]"); + testValidation("" + + "['ParamDecl',{}," + + "['IdPatt',{'name':'x'}]," + + "['IdPatt',{'name':'y'}]]"); + // wrong types of children + testWrongChildTypeValidation("" + + "['ParamDecl',{}," + + "['IdPatt',{'name':'x'}]," + + "['IdExpr',{'name':'y'}]]", + TagType.ParamDecl, TagType.IdPatt, TagType.IdExpr, 1); + } + + public void testRegExpExprValidation() throws Exception { + // correct statement + testValidation("" + + "['RegExpExpr',{'body':'abc','flags':''}]"); + testValidation("" + + "['RegExpExpr',{'body':'abc','flags':'g'}]"); + // wrong number of children + testTooManyChildrenValidation("" + + "['RegExpExpr',{'body':'abc','flags':'g'}," + + "['IdExpr',{'name':'a'}]]", + TagType.RegExpExpr, 0, 1); + // missing attribute + testMissingArgument("" + + "['RegExpExpr',{'body':'abc'}]", + TagAttr.FLAGS, TagType.RegExpExpr); + testMissingArgument("" + + "['RegExpExpr',{'flags':'g'}]", + TagAttr.BODY, TagType.RegExpExpr); + } + + public void testReturnStmtValidation() throws Exception { + // correct statement + testValidation("" + + "['ReturnStmt',{}]"); + testValidation("" + + "['ReturnStmt',{}," + + "['LiteralExpr',{'type':'number','value':1}]]"); + // wrong number of children + testTooManyChildrenValidation("" + + "['ReturnStmt',{}," + + "['IdExpr',{'name':'a'}]," + + "['IdExpr',{'name':'b'}]]", + TagType.ReturnStmt, 1, 2); + } + + public void testSwitchStmtValidation() throws Exception { + // correct statement + testValidation("" + + "['SwitchStmt',{}," + + "['IdExpr',{'name':'x'}]," + + "['Case',{}," + + "['LiteralExpr',{'type':'number','value':1}]," + + "['CallExpr',{}," + + "['IdExpr',{'name':'foo'}]]]," + + "['DefaultCase',{}," + + "['CallExpr',{}," + + "['IdExpr',{'name':'bar'}]]]]"); + testValidation("" + + "['SwitchStmt',{}," + + "['IdExpr',{'name':'x'}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['SwitchStmt',{}]", + TagType.SwitchStmt, 1, 0); + // wrong types of children + testWrongChildTypeValidation("" + + "['SwitchStmt',{}," + + "['IdExpr',{'name':'x'}]," + + "['AssignExpr',{'op': '='}," + + "['LiteralExpr',{'type':'number','value':1}]," + + "['CallExpr',{}," + + "['IdExpr',{'name':'foo'}]]]," + + "['DefaultCase',{}," + + "['CallExpr',{}," + + "['IdExpr',{'name':'bar'}]]]]", + TagType.SwitchStmt, + new TagType[] { TagType.Case, TagType.DefaultCase }, + TagType.AssignExpr, 1); + testWrongChildTypeValidation("" + + "['SwitchStmt',{}," + + "['IdExpr',{'name':'x'}]," + + "['DefaultCase',{}," + + "['CallExpr',{}," + + "['IdExpr',{'name':'foo'}]]]," + + "['DefaultCase',{}," + + "['CallExpr',{}," + + "['IdExpr',{'name':'bar'}]]]]", + TagType.SwitchStmt, TagType.Case, TagType.DefaultCase, 2); + } + + public void testThisExprValidation() throws Exception { + // correct statement + testValidation("" + + "['ThisExpr',{}]"); + // wrong number of children + testTooManyChildrenValidation("" + + "['ThisExpr',{}," + + "['IdExpr',{'name':'a'}]]", + TagType.ThisExpr, 0, 1); + } + + public void testThrowStmtValidation() throws Exception { + // correct statement + testValidation("" + + "['ThrowStmt',{}," + + "['IdExpr',{'name':'e'}]]"); + // wrong number of children + testTooManyChildrenValidation("" + + "['ThrowStmt',{}," + + "['IdExpr',{'name':'a'}]," + + "['IdExpr',{'name':'b'}]]", + TagType.ThrowStmt, 1, 2); + } + + public void testTryStmtValidation() throws Exception { + // correct statement + testValidation("" + + "['TryStmt',{}," + + "['BlockStmt',{}]," + + "['CatchClause',{}," + + "['IdPatt',{'name':'e'}]," + + "['BlockStmt',{}]]]"); + testValidation("" + + "['TryStmt',{}," + + "['BlockStmt',{}]," + + "['CatchClause',{}," + + "['IdPatt',{'name':'e'}]," + + "['BlockStmt',{}]]," + + "['BlockStmt',{}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['TryStmt',{}," + + "['CatchClause',{}," + + "['IdPatt',{'name':'e'}]," + + "['BlockStmt',{}]]]", + TagType.TryStmt, 2, 1); + testTooManyChildrenValidation("" + + "['TryStmt',{}," + + "['BlockStmt',{}]," + + "['CatchClause',{}," + + "['IdPatt',{'name':'e'}]," + + "['BlockStmt',{}]]," + + "['BlockStmt',{}]," + + "['BlockStmt',{}]]", + TagType.TryStmt, 3, 4); + // wrong type of children + testWrongChildTypeValidation("" + + "['TryStmt',{}," + + "['BlockStmt',{}]," + + "['BlockStmt',{}," + + "['IdPatt',{'name':'e'}]," + + "['BlockStmt',{}]]," + + "['BlockStmt',{}]]", + TagType.TryStmt, + new TagType[] { TagType.CatchClause, TagType.Empty }, + TagType.BlockStmt, 1); + testWrongChildTypeValidation("" + + "['TryStmt',{}," + + "['BlockStmt',{}]," + + "['CatchClause',{}," + + "['IdPatt',{'name':'e'}]," + + "['BlockStmt',{}]]," + + "['IdExpr',{'name': 'x'}]]", + TagType.TryStmt, TagType.BlockStmt, TagType.IdExpr, 2); + } + + public void testUnaryExprValidation() throws Exception { + // correct statement + testValidation("" + + "['UnaryExpr',{'op':'-'}," + + "['IdExpr',{'name':'x'}]]"); + testValidation("" + + "['UnaryExpr',{'op':'!'}," + + "['CallExpr',{}," + + "['IdExpr',{'name':'f'}]," + + "['IdExpr',{'name':'x'}]]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['UnaryExpr',{'op':'-'}]", + TagType.UnaryExpr, 1, 0); + testTooManyChildrenValidation("" + + "['UnaryExpr',{'op':'+'}," + + "['IdExpr',{'name':'x'}]," + + "['IdExpr',{'name':'y'}]]", + TagType.UnaryExpr, 1, 2); + // missing attribute + testMissingArgument("" + + "['UnaryExpr',{}," + + "['IdExpr',{'name':'x'}]]", + TagAttr.OP, TagType.UnaryExpr); + } + + public void testVarDeclValidation() throws Exception { + // correct statement + testValidation("" + + "['VarDecl',{}," + + "['IdPatt',{'name':'x'}]]"); + testValidation("" + + "['VarDecl',{}," + + "['InitPatt',{}," + + "['IdPatt',{'name':'x'}]," + + "['LiteralExpr',{'type':'number','value':0}]]]"); + testValidation("" + + "['VarDecl',{}," + + "['InitPatt',{}," + + "['IdPatt',{'name':'x'}]," + + "['LiteralExpr',{'type':'number','value':0}]]," + + "['IdPatt',{'name':'y'}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['VarDecl',{}]", + TagType.VarDecl, 1, 0); + // wrong type of children + testWrongChildTypeValidation("" + + "['VarDecl',{}," + + "['InitPatt',{}," + + "['IdPatt',{'name':'x'}]," + + "['LiteralExpr',{'type':'number','value':0}]]," + + "['IdExpr',{'name':'y'}]," + + "['IdPatt',{'name':'z'}]]", + TagType.VarDecl, + new TagType[] { TagType.InitPatt, TagType.IdPatt }, + TagType.IdExpr, 1); + } + + public void testWhileStmtValidation() throws Exception { + // correct statement + testValidation("" + + "['WhileStmt',{}," + + "['LiteralExpr',{'type':'boolean','value':true}]," + + "['BlockStmt',{}]]"); + testValidation("" + + "['WhileStmt',{}," + + "['LiteralExpr',{'type':'boolean','value':true}]," + + "['IdExpr',{'name':'x'}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['WhileStmt',{}," + + "['BlockStmt',{}]]", + TagType.WhileStmt, 2, 1); + testTooManyChildrenValidation("" + + "['WhileStmt',{}," + + "['LiteralExpr',{'type':'boolean','value':true}]," + + "['IdExpr',{'name':'x'}]," + + "['IdExpr',{'name':'y'}]]", + TagType.WhileStmt, 2, 3); + } + + public void testWithStmtValidation() throws Exception { + // correct statement + testValidation("" + + "['WithStmt',{}," + + "['IdExpr',{'name':'x'}]," + + "['BlockStmt',{}]]"); + // wrong number of children + testNotEnoughChildrenValidation("" + + "['WithStmt',{}," + + "['BlockStmt',{}]]", + TagType.WithStmt, 2, 1); + testTooManyChildrenValidation("" + + "['WithStmt',{}," + + "['IdExpr',{'name':'A'}]," + + "['IdExpr',{'name':'x'}]," + + "['IdExpr',{'name':'y'}]]", + TagType.WithStmt, 2, 3); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/jsonml/SecureCompilerTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/jsonml/SecureCompilerTest.java new file mode 100644 index 0000000..0496348 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/jsonml/SecureCompilerTest.java @@ -0,0 +1,91 @@ +/* + * Copyright 2010 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.jsonml; + +import com.google.javascript.jscomp.jsonml.SecureCompiler; +import com.google.javascript.jscomp.jsonml.JsonML; +import com.google.javascript.jscomp.jsonml.JsonMLUtil; +import com.google.javascript.jscomp.jsonml.SecureCompiler.Report; + +import junit.framework.TestCase; + +/** + * Test class for secure compilation. + * + * @author dhans@google.com (Daniel Hans) + * + */ +public class SecureCompilerTest extends TestCase { + + // simple correct source + // var x = 1; var t = x; + private static final String SIMPLE_SOURCE = + "['Program',{}," + + "['VarDecl',{}," + + "['InitPatt',{}," + + "['IdPatt',{'name':'x'}]," + + "['LiteralExpr',{'type':'number','value':1}]]]," + + "['VarDecl',{}," + + "['InitPatt',{}," + + "['IdPatt',{'name':'t'}]," + + "['IdExpr',{'name':'x'}]]]]"; + + // syntax error source + // missing InitPatt element + private static final String SYNTAX_ERROR = + "['Program',{}," + + "['VarDecl',{}," + + "['InitPatt',{}," + + "['IdPatt',{'name':'x'}]," + + "['LiteralExpr',{'type':'number','value':1}]]]," + + "['VarDecl',{}," + + "['IdPatt',{'name':'t'}]," + + "['IdExpr',{'name':'x'}]]]]"; + + private void testSuccess(JsonML source) throws Exception { + SecureCompiler compiler = new SecureCompiler(); + compiler.compile(source); + Report report = compiler.getReport(); + assertTrue(report.isSuccessful()); + assertEquals(0, report.getErrors().length); + assertEquals(0, report.getWarnings().length); + } + + private void testError(JsonML source) throws Exception { + SecureCompiler compiler = new SecureCompiler(); + compiler.compile(source); + Report report = compiler.getReport(); + assertFalse(report.isSuccessful()); + } + + private void testString(String jsonml) throws Exception { + JsonML source = JsonMLUtil.parseString(jsonml); + testSuccess(source); + } + + private void testInvalidString(String jsonml) throws Exception { + JsonML source = JsonMLUtil.parseString(jsonml); + testError(source); + } + + public void testCompilerInterface() throws Exception { + testString(SIMPLE_SOURCE); + testInvalidString(SYNTAX_ERROR); + } + + +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/parsing/IRFactoryTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/parsing/IRFactoryTest.java new file mode 100644 index 0000000..5ae2b6f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/parsing/IRFactoryTest.java @@ -0,0 +1,997 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.parsing; + +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.SourceFile; +import com.google.javascript.jscomp.parsing.Config.LanguageMode; +import com.google.javascript.jscomp.testing.TestErrorReporter; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.head.CompilerEnvirons; +import com.google.javascript.rhino.head.Parser; +import com.google.javascript.rhino.head.ast.AstRoot; +import com.google.javascript.rhino.testing.BaseJSTypeTestCase; + +/** + * Tests {@link IRFactory}. + */ +public class IRFactoryTest extends BaseJSTypeTestCase { + + private LanguageMode mode = LanguageMode.ECMASCRIPT3; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mode = LanguageMode.ECMASCRIPT3; + } + + public void testStrictScript() throws Exception { + assertNull(newParse("").getDirectives()); + assertEquals( + Sets.newHashSet("use strict"), + newParse("'use strict'").getDirectives()); + } + + public void testArrayLiteral2() throws Exception { + testNewParser("[a, , b]", + "SCRIPT 1 [source_file: FileName.js] [length: 8]\n" + + " EXPR_RESULT 1 [source_file: FileName.js] [length: 8]\n" + + " ARRAYLIT 1 [source_file: FileName.js] [length: 8]\n" + + " NAME a 1 [source_file: FileName.js] [length: 1]\n" + + " EMPTY 1 [source_file: FileName.js] [length: 1]\n" + + " NAME b 1 [source_file: FileName.js] [length: 1]\n"); + } + + public void testArrayLiteral4() throws Exception { + testNewParser("[,,,a,,b]", + "SCRIPT 1 [source_file: FileName.js] [length: 9]\n" + + " EXPR_RESULT 1 [source_file: FileName.js] [length: 9]\n" + + " ARRAYLIT 1 [source_file: FileName.js] [length: 9]\n" + + " EMPTY 1 [source_file: FileName.js] [length: 1]\n" + + " EMPTY 1 [source_file: FileName.js] [length: 1]\n" + + " EMPTY 1 [source_file: FileName.js] [length: 1]\n" + + " NAME a 1 [source_file: FileName.js] [length: 1]\n" + + " EMPTY 1 [source_file: FileName.js] [length: 1]\n" + + " NAME b 1 [source_file: FileName.js] [length: 1]\n"); + } + + public void testObjectLiteral() { + newParse("var o = {}"); + } + + public void testObjectLiteral2() { + newParse("var o = {a: 1}"); + } + + public void testObjectLiteral3() { + newParse("var o = {a: 1, b: 2}"); + } + + public void testObjectLiteral4() { + newParse("var o = {1: 'a'}"); + } + + public void testObjectLiteral5() { + newParse("var o = {'a': 'a'}"); + } + + public void testObjectLiteral6() { + testNewParser("({1: true})", + "SCRIPT 1 [source_file: FileName.js] [length: 11]\n" + + " EXPR_RESULT 1 [source_file: FileName.js] [length: 10]\n" + + " OBJECTLIT 1 [source_file: FileName.js] [length: 9]\n" + + " STRING_KEY 1 1 [quoted: 1] [source_file: FileName.js] [length: 1]\n" + + " TRUE 1 [source_file: FileName.js] [length: 4]\n"); + } + + public void testObjectLiteral7() { + mode = LanguageMode.ECMASCRIPT5; + + testNewParser("({get 1() {}})", + "SCRIPT 1 [source_file: FileName.js] [length: 14]\n" + + " EXPR_RESULT 1 [source_file: FileName.js] [length: 13]\n" + + " OBJECTLIT 1 [source_file: FileName.js] [length: 12]\n" + + " GETTER_DEF 1 1 [quoted: 1] [source_file: FileName.js] [length: 1]\n" + + " FUNCTION 1 [source_file: FileName.js] [length: 6]\n" + + " NAME 1 [source_file: FileName.js]\n" + + " PARAM_LIST 1 [source_file: FileName.js]\n" + + " BLOCK 1 [source_file: FileName.js] [length: 2]\n"); + } + + public void testObjectLiteral8() { + mode = LanguageMode.ECMASCRIPT5; + + testNewParser("({set 1(a) {}})", + "SCRIPT 1 [source_file: FileName.js] [length: 15]\n" + + " EXPR_RESULT 1 [source_file: FileName.js] [length: 14]\n" + + " OBJECTLIT 1 [source_file: FileName.js] [length: 13]\n" + + " SETTER_DEF 1 1 [quoted: 1] [source_file: FileName.js] [length: 1]\n" + + " FUNCTION 1 [source_file: FileName.js] [length: 7]\n" + + " NAME 1 [source_file: FileName.js]\n" + + " PARAM_LIST 1 [source_file: FileName.js]\n" + + " NAME a 1 [source_file: FileName.js] [length: 1]\n" + + " BLOCK 1 [source_file: FileName.js] [length: 2]\n"); + } + + // The old and new parser produce different results now with labels, and + // named breaks and continues, so disable these tests. + public void testLabel() { + testNewParser("foo: bar", + "SCRIPT 1 [source_file: FileName.js] [length: 8]\n" + + " LABEL 1 [source_file: FileName.js] [length: 4]\n" + + " LABEL_NAME foo 1 [source_file: FileName.js] [length: 4]\n" + + " EXPR_RESULT 1 [source_file: FileName.js] [length: 3]\n" + + " NAME bar 1 [source_file: FileName.js] [length: 3]\n"); + } + + public void testLabel2() { + testNewParser("l: while (f()) { if (g()) { continue l; } }", + "SCRIPT 1 [source_file: FileName.js] [length: 43]\n" + + " LABEL 1 [source_file: FileName.js] [length: 2]\n" + + " LABEL_NAME l 1 [source_file: FileName.js] [length: 2]\n" + + " WHILE 1 [source_file: FileName.js] [length: 40]\n" + + " CALL 1 [source_file: FileName.js] [length: 3]\n" + + " NAME f 1 [source_file: FileName.js] [length: 1]\n" + + " BLOCK 1 [source_file: FileName.js] [length: 28]\n" + + " IF 1 [source_file: FileName.js] [length: 24]\n" + + " CALL 1 [source_file: FileName.js] [length: 3]\n" + + " NAME g 1 [source_file: FileName.js] [length: 1]\n" + + " BLOCK 1 [source_file: FileName.js] [length: 15]\n" + + " CONTINUE 1 [source_file: FileName.js] [length: 11]\n" + + " LABEL_NAME l 1 [source_file: FileName.js] [length: 1]\n"); + } + + public void testLabel3() { + testNewParser("Foo:Bar:X:{ break Bar; }", + "SCRIPT 1 [source_file: FileName.js] [length: 24]\n" + + " LABEL 1 [source_file: FileName.js] [length: 4]\n" + + " LABEL_NAME Foo 1 [source_file: FileName.js] [length: 4]\n" + + " LABEL 1 [source_file: FileName.js] [length: 4]\n" + + " LABEL_NAME Bar 1 [source_file: FileName.js] [length: 4]\n" + + " LABEL 1 [source_file: FileName.js] [length: 2]\n" + + " LABEL_NAME X 1 [source_file: FileName.js] [length: 2]\n" + + " BLOCK 1 [source_file: FileName.js] [length: 14]\n" + + " BREAK 1 [source_file: FileName.js] [length: 10]\n" + + " LABEL_NAME Bar 1 [source_file: FileName.js] [length: 3]\n"); + } + + public void testNegation1() { + testNewParser("-a", + "SCRIPT 1 [source_file: FileName.js] [length: 2]\n" + + " EXPR_RESULT 1 [source_file: FileName.js] [length: 2]\n" + + " NEG 1 [source_file: FileName.js] [length: 2]\n" + + " NAME a 1 [source_file: FileName.js] [length: 1]\n"); + } + + public void testNegation2() { + testNewParser("-2", + "SCRIPT 1 [source_file: FileName.js] [length: 2]\n" + + " EXPR_RESULT 1 [source_file: FileName.js] [length: 2]\n" + + " NUMBER -2.0 1 [source_file: FileName.js] [length: 1]\n"); + } + + public void testNegation3() { + testNewParser("1 - -2", + "SCRIPT 1 [source_file: FileName.js] [length: 6]\n" + + " EXPR_RESULT 1 [source_file: FileName.js] [length: 6]\n" + + " SUB 1 [source_file: FileName.js] [length: 6]\n" + + " NUMBER 1.0 1 [source_file: FileName.js] [length: 1]\n" + + " NUMBER -2.0 1 [source_file: FileName.js] [length: 1]\n"); + } + + public void testGetter() { + mode = LanguageMode.ECMASCRIPT5; + testNewParser("({get a() {}})", + "SCRIPT 1 [source_file: FileName.js] [length: 14]\n" + + " EXPR_RESULT 1 [source_file: FileName.js] [length: 13]\n" + + " OBJECTLIT 1 [source_file: FileName.js] [length: 12]\n" + + " GETTER_DEF a 1 [source_file: FileName.js] [length: 1]\n" + + " FUNCTION 1 [source_file: FileName.js] [length: 6]\n" + + " NAME 1 [source_file: FileName.js]\n" + + " PARAM_LIST 1 [source_file: FileName.js]\n" + + " BLOCK 1 [source_file: FileName.js] [length: 2]\n"); + } + + public void testSetter() { + mode = LanguageMode.ECMASCRIPT5; + testNewParser("({set a(x) {}})", + "SCRIPT 1 [source_file: FileName.js] [length: 15]\n" + + " EXPR_RESULT 1 [source_file: FileName.js] [length: 14]\n" + + " OBJECTLIT 1 [source_file: FileName.js] [length: 13]\n" + + " SETTER_DEF a 1 [source_file: FileName.js] [length: 1]\n" + + " FUNCTION 1 [source_file: FileName.js] [length: 7]\n" + + " NAME 1 [source_file: FileName.js]\n" + + " PARAM_LIST 1 [source_file: FileName.js]\n" + + " NAME x 1 [source_file: FileName.js] [length: 1]\n" + + " BLOCK 1 [source_file: FileName.js] [length: 2]\n"); + } + + public void testDelete1() { + testNoParseError("delete a.b;"); + } + + public void testDelete2() { + testNoParseError("delete a['b'];"); + } + + public void testDelete3() { + // This is allowed in ES3 and ES5, but not in ES5/strict. There + // is a strict mode check for this. + testNoParseError("delete a;"); + } + + public void testDelete4() { + testParseError("delete 'x';", + "Invalid delete operand. Only properties can be deleted."); + } + + public void testCommentPositions1() { + Node root = newParse("/** @param {string} x */function a(x) {};" + + "/** @param {string} x */function b(x) {}"); + Node a = root.getFirstChild(); + Node b = root.getLastChild(); + assertMarkerPosition(a, 1, 4); + assertMarkerPosition(b, 1, 45); + } + + public void testCommentPositions2() { + Node root = newParse( + "/* foo \n" + + " bar \n" + + "*/\n" + + "/** @param {string} x */\n" + + "function a(x) {};\n" + + "\n" + + "/* bar \n" + + " foo \n" + + " foo */\n" + + "\n" + + "/** @param {string} x */\n" + + "function b(x) {};"); + assertMarkerPosition(root.getFirstChild(), 4, 4); + assertMarkerPosition(root.getFirstChild().getNext().getNext(), 11, 6); + } + + public void testLiteralLocation() { + Node root = newParse( + "var d =\n" + + " \"foo\";\n" + + "var e =\n" + + " 1;\n" + + "var f = \n" + + " 1.2;\n" + + "var g = \n" + + " 2e5;\n" + + "var h = \n" + + " 'bar';\n"); + + Node firstStmt = root.getFirstChild(); + Node firstLiteral = firstStmt.getFirstChild().getFirstChild(); + Node secondStmt = firstStmt.getNext(); + Node secondLiteral = secondStmt.getFirstChild().getFirstChild(); + Node thirdStmt = secondStmt.getNext(); + Node thirdLiteral = thirdStmt.getFirstChild().getFirstChild(); + Node fourthStmt = thirdStmt.getNext(); + Node fourthLiteral = fourthStmt.getFirstChild().getFirstChild(); + Node fifthStmt = fourthStmt.getNext(); + Node fifthLiteral = fifthStmt.getFirstChild().getFirstChild(); + + assertNodePosition(2, 4, firstLiteral); + assertNodePosition(4, 4, secondLiteral); + assertNodePosition(6, 4, thirdLiteral); + assertNodePosition(8, 4, fourthLiteral); + assertNodePosition(10, 4, fifthLiteral); + } + + public void testSwitchLocation() { + Node root = newParse( + "switch (a) {\n" + + " //{\n" + + " case 1:\n" + + " b++;\n" + + " case 2:\n" + + " default:\n" + + " b--;\n" + + " }\n"); + + Node switchStmt = root.getFirstChild(); + Node switchVar = switchStmt.getFirstChild(); + Node firstCase = switchVar.getNext(); + Node caseArg = firstCase.getFirstChild(); + Node caseBody = caseArg.getNext(); + Node caseExprStmt = caseBody.getFirstChild(); + Node incrExpr = caseExprStmt.getFirstChild(); + Node incrVar = incrExpr.getFirstChild(); + Node secondCase = firstCase.getNext(); + Node defaultCase = secondCase.getNext(); + + assertNodePosition(1, 0, switchStmt); + assertNodePosition(1, 8, switchVar); + assertNodePosition(3, 3, firstCase); + assertNodePosition(3, 8, caseArg); + assertNodePosition(3, 3, caseBody); + assertNodePosition(4, 5, caseExprStmt); + assertNodePosition(4, 5, incrExpr); + assertNodePosition(4, 5, incrVar); + assertNodePosition(5, 3, secondCase); + assertNodePosition(6, 3, defaultCase); + } + + public void testFunctionParamLocation() { + Node root = newParse( + "function\n" + + " foo(a,\n" + + " b,\n" + + " c)\n" + + "{}\n"); + + Node function = root.getFirstChild(); + Node functionName = function.getFirstChild(); + Node params = functionName.getNext(); + Node param1 = params.getFirstChild(); + Node param2 = param1.getNext(); + Node param3 = param2.getNext(); + Node body = params.getNext(); + + assertNodePosition(1, 0, function); + assertNodePosition(2, 5, functionName); + // params corresponds to the LP token. + // Can't be on a separate line because of inferred + // semicolons. + assertNodePosition(2, 8, params); + assertNodePosition(2, 9, param1); + assertNodePosition(3, 5, param2); + assertNodePosition(4, 5, param3); + assertNodePosition(5, 0, body); + } + + public void testVarDeclLocation() { + Node root = newParse( + "var\n" + + " a =\n" + + " 3\n"); + Node varDecl = root.getFirstChild(); + Node varName = varDecl.getFirstChild(); + Node varExpr = varName.getFirstChild(); + + assertNodePosition(1, 0, varDecl); + assertNodePosition(2, 4, 1, varName); + assertNodePosition(3, 4, 1, varExpr); + } + + public void testReturnLocation() { + Node root = newParse( + "function\n" + + " foo(\n" + + " a,\n" + + " b,\n" + + " c) {\n" + + " return\n" + + " 4;\n" + + "}\n"); + + Node function = root.getFirstChild(); + Node functionName = function.getFirstChild(); + Node params = functionName.getNext(); + Node body = params.getNext(); + Node returnStmt = body.getFirstChild(); + Node exprStmt = returnStmt.getNext(); + Node returnVal = exprStmt.getFirstChild(); + + assertNodePosition(6, 4, returnStmt); + assertNodePosition(7, 4, exprStmt); + assertNodePosition(7, 4, returnVal); + } + + public void testLinenoFor() { + Node root = newParse( + "for(\n" + + ";\n" + + ";\n" + + ") {\n" + + "}\n"); + + Node forNode = root.getFirstChild(); + Node initClause= forNode.getFirstChild(); + Node condClause = initClause.getNext(); + Node incrClause = condClause.getNext(); + + assertNodePosition(1, 0, forNode); + assertNodePosition(2, 0, initClause); + assertNodePosition(3, 0, condClause); + // TODO(bowdidge) Incorrectly gets charno position when EmptyExpression + // has its absolute position on the carriage return. For now, the + // line number gets reported correctly (on the next line) but the + // character position is -1, so the overall line/char pair in our tree + // is -1. + //assertNodePosition(4, 0, incrClause); + } + + public void testBinaryExprLocation() { + Node root = newParse( + "var d = a\n" + + " + \n" + + " b;\n" + + "var\n" + + " e =\n" + + " a +\n" + + " c;\n" + + "var f = b\n" + + " / c;\n"); + + Node firstVarDecl = root.getFirstChild(); + Node firstVar = firstVarDecl.getFirstChild(); + Node firstVarAdd = firstVar.getFirstChild(); + + Node secondVarDecl = firstVarDecl.getNext(); + Node secondVar = secondVarDecl.getFirstChild(); + Node secondVarAdd = secondVar.getFirstChild(); + + Node thirdVarDecl = secondVarDecl.getNext(); + Node thirdVar = thirdVarDecl.getFirstChild(); + Node thirdVarAdd = thirdVar.getFirstChild(); + + assertNodePosition(1, 0, firstVarDecl); + assertNodePosition(1, 4, firstVar); + assertNodePosition(1, 8, firstVarAdd); + assertNodePosition(1, 8, firstVarAdd.getFirstChild()); + assertNodePosition(3, 4, firstVarAdd.getLastChild()); + + assertNodePosition(4, 0, secondVarDecl); + assertNodePosition(5, 4, secondVar); + assertNodePosition(6, 4, secondVarAdd); + assertNodePosition(6, 4, secondVarAdd.getFirstChild()); + assertNodePosition(7, 4, secondVarAdd.getLastChild()); + + assertNodePosition(8, 0, thirdVarDecl); + assertNodePosition(8, 4, thirdVar); + assertNodePosition(8, 8, thirdVarAdd); + assertNodePosition(8, 8, thirdVarAdd.getFirstChild()); + assertNodePosition(9, 6, thirdVarAdd.getLastChild()); + } + + public void testPrefixLocation() { + Node root = newParse( + "a++;\n" + + "--\n" + + "b;\n"); + + Node firstStmt = root.getFirstChild(); + Node secondStmt = firstStmt.getNext(); + Node firstOp = firstStmt.getFirstChild(); + Node secondOp = secondStmt.getFirstChild(); + + assertNodePosition(1, 0, firstOp); + assertNodePosition(2, 0, secondOp); + } + + public void testIfLocation() { + Node root = newParse( + "if\n" + + " (a == 3)\n" + + "{\n" + + " b = 0;\n" + + "}\n" + + " else\n" + + "{\n" + + " c = 1;\n" + + "}\n"); + + Node ifStmt = root.getFirstChild(); + Node eqClause = ifStmt.getFirstChild(); + Node thenClause = eqClause.getNext(); + Node elseClause = thenClause.getNext(); + + assertNodePosition(1, 0, ifStmt); + assertNodePosition(2, 3, eqClause); + assertNodePosition(3, 0, thenClause); + assertNodePosition(7, 0, elseClause); + } + + public void testTryLocation() { + Node root = newParse( + "try {\n" + + " var x = 1;\n" + + "} catch\n" + + " (err)\n" + + "{\n" + + "} finally {\n" + + " var y = 2;\n" + + "}\n"); + + Node tryStmt = root.getFirstChild(); + Node tryBlock = tryStmt.getFirstChild(); + Node catchBlock = tryBlock.getNext(); + Node catchVarBlock = catchBlock.getFirstChild(); + Node catchVar = catchVarBlock.getFirstChild(); + Node finallyBlock = catchBlock.getNext(); + Node finallyStmt = finallyBlock.getFirstChild(); + + assertNodePosition(1, 0, tryStmt); + assertNodePosition(1, 4, tryBlock); + assertNodePosition(3, 2, catchVarBlock); + assertNodePosition(4, 4, catchVar); + assertNodePosition(3, 0, catchBlock); + assertNodePosition(6, 10, finallyBlock); + assertNodePosition(7, 2, finallyStmt); + } + + public void testHookLocation() { + Node root = newParse( + "a\n" + + "?\n" + + "b\n" + + ":\n" + + "c\n" + + ";\n"); + + Node hookExpr = root.getFirstChild().getFirstChild(); + Node condExpr = hookExpr.getFirstChild(); + Node thenExpr = condExpr.getNext(); + Node elseExpr = thenExpr.getNext(); + + assertNodePosition(2, 0, hookExpr); + assertNodePosition(1, 0, condExpr); + assertNodePosition(3, 0, thenExpr); + assertNodePosition(5, 0, elseExpr); + } + + public void testLabelLocation() { + Node root = newParse( + "foo:\n" + + "a = 1;\n" + + "bar:\n" + + "b = 2;\n"); + + Node firstStmt = root.getFirstChild(); + Node secondStmt = firstStmt.getNext(); + + assertNodePosition(1, 0, firstStmt); + assertNodePosition(3, 0, secondStmt); + } + + public void testCompareLocation() { + Node root = newParse( + "a\n" + + "<\n" + + "b\n"); + + Node condClause = root.getFirstChild().getFirstChild(); + Node lhs = condClause.getFirstChild(); + Node rhs = lhs.getNext(); + + assertNodePosition(1, 0, condClause); + assertNodePosition(1, 0, lhs); + assertNodePosition(3, 0, rhs); + } + + public void testEqualityLocation() { + Node root = newParse( + "a\n" + + "==\n" + + "b\n"); + + Node condClause = root.getFirstChild().getFirstChild(); + Node lhs = condClause.getFirstChild(); + Node rhs = lhs.getNext(); + + assertNodePosition(1, 0, condClause); + assertNodePosition(1, 0, lhs); + assertNodePosition(3, 0, rhs); + } + + public void testPlusEqLocation() { + Node root = newParse( + "a\n" + + "+=\n" + + "b\n"); + + Node condClause = root.getFirstChild().getFirstChild(); + Node lhs = condClause.getFirstChild(); + Node rhs = lhs.getNext(); + + assertNodePosition(1, 0, condClause); + assertNodePosition(1, 0, lhs); + assertNodePosition(3, 0, rhs); + } + + public void testCommaLocation() { + Node root = newParse( + "a,\n" + + "b,\n" + + "c;\n"); + + Node statement = root.getFirstChild(); + Node comma1 = statement.getFirstChild(); + Node comma2 = comma1.getFirstChild(); + Node cRef = comma2.getNext(); + Node aRef = comma2.getFirstChild(); + Node bRef = aRef.getNext(); + + assertNodePosition(1, 0, comma2); + assertNodePosition(1, 0, aRef); + assertNodePosition(2, 0, bRef); + assertNodePosition(3, 0, cRef); + } + + public void testRegexpLocation() { + Node root = newParse( + "var path =\n" + + "replace(\n" + + "/a/g," + + "'/');\n"); + + Node firstVarDecl = root.getFirstChild(); + Node firstVar = firstVarDecl.getFirstChild(); + Node callNode = firstVar.getFirstChild(); + Node fnName = callNode.getFirstChild(); + Node regexObject = fnName.getNext(); + Node aString = regexObject.getFirstChild(); + Node endRegexString = regexObject.getNext(); + + assertNodePosition(1, 0, firstVarDecl); + assertNodePosition(1, 4, 4, firstVar); + assertNodePosition(2, 0, 18, callNode); + assertNodePosition(2, 0, 7, fnName); + assertNodePosition(3, 0, regexObject); + assertNodePosition(3, 0, aString); + assertNodePosition(3, 5, endRegexString); + } + + public void testNestedOr() { + Node root = newParse( + "if (a && \n" + + " b() || \n" + + " /* comment */\n" + + " c) {\n" + + "}\n" + ); + + Node ifStmt = root.getFirstChild(); + Node orClause = ifStmt.getFirstChild(); + Node andClause = orClause.getFirstChild(); + Node cName = andClause.getNext(); + + assertNodePosition(1, 0, ifStmt); + assertNodePosition(1, 4, orClause); + assertNodePosition(1, 4, andClause); + assertNodePosition(4, 4, cName); + + } + + public void testBitwiseOps() { + Node root = newParse( + "if (a & \n" + + " b() | \n" + + " /* comment */\n" + + " c) {\n" + + "}\n" + ); + + Node ifStmt = root.getFirstChild(); + Node bitOr = ifStmt.getFirstChild(); + Node bitAnd = bitOr.getFirstChild(); + Node cName = bitAnd.getNext(); + + assertNodePosition(1, 0, ifStmt); + assertNodePosition(1, 4, bitOr); + assertNodePosition(1, 4, bitAnd); + assertNodePosition(4, 4, cName); + + } + + public void testObjectLitLocation() { + Node root = newParse( + "var foo =\n" + + "{ \n" + + "'A' : 'A', \n" + + "'B' : 'B', \n" + + "'C' :\n" + + " 'C' \n" + + "};\n"); + + Node firstVarDecl = root.getFirstChild(); + Node firstVar = firstVarDecl.getFirstChild(); + Node firstObjectLit = firstVar.getFirstChild(); + Node firstKey = firstObjectLit.getFirstChild(); + Node firstValue = firstKey.getFirstChild(); + + Node secondKey = firstKey.getNext(); + Node secondValue = secondKey.getFirstChild(); + + Node thirdKey = secondKey.getNext(); + Node thirdValue = thirdKey.getFirstChild(); + + assertNodePosition(1, 4, firstVar); + assertNodePosition(2, 0, firstObjectLit); + + assertNodePosition(3, 0, firstKey); + assertNodePosition(3, 6, firstValue); + + assertNodePosition(4, 0, secondKey); + assertNodePosition(4, 6, secondValue); + + assertNodePosition(5, 0, thirdKey); + assertNodePosition(6, 4, thirdValue); + } + + public void testTryWithoutCatchLocation() { + Node root = newParse( + "try {\n" + + " var x = 1;\n" + + "} finally {\n" + + " var y = 2;\n" + + "}\n"); + + Node tryStmt = root.getFirstChild(); + Node tryBlock = tryStmt.getFirstChild(); + Node catchBlock = tryBlock.getNext(); + Node finallyBlock = catchBlock.getNext(); + Node finallyStmt = finallyBlock.getFirstChild(); + + assertNodePosition(1, 0, tryStmt); + assertNodePosition(1, 4, tryBlock); + assertNodePosition(3, 0, catchBlock); + assertNodePosition(3, 10, finallyBlock); + assertNodePosition(4, 2, finallyStmt); + } + + public void testTryWithoutFinallyLocation() { + Node root = newParse( + "try {\n" + + " var x = 1;\n" + + "} catch (ex) {\n" + + " var y = 2;\n" + + "}\n"); + + Node tryStmt = root.getFirstChild(); + Node tryBlock = tryStmt.getFirstChild(); + Node catchBlock = tryBlock.getNext(); + Node catchStmt = catchBlock.getFirstChild(); + Node exceptionVar = catchStmt.getFirstChild(); + Node exceptionBlock = exceptionVar.getNext(); + Node varDecl = exceptionBlock.getFirstChild(); + + assertNodePosition(1, 0, tryStmt); + assertNodePosition(1, 4, tryBlock); + assertNodePosition(3, 0, catchBlock); + assertNodePosition(3, 2, catchStmt); + assertNodePosition(3, 9, exceptionVar); + assertNodePosition(3, 13, exceptionBlock); + assertNodePosition(4, 2, varDecl); + } + + public void testMultilineEqLocation() { + Node root = newParse( + "if\n" + + " (((a == \n" + + " 3) && \n" + + " (b == 2)) || \n" + + " (c == 1)) {\n" + + "}\n"); + Node ifStmt = root.getFirstChild(); + Node orTest = ifStmt.getFirstChild(); + Node andTest = orTest.getFirstChild(); + Node cTest = andTest.getNext(); + Node aTest = andTest.getFirstChild(); + Node bTest = aTest.getNext(); + + assertNodePosition(1, 0, ifStmt); + assertNodePosition(2, 7, orTest); + assertNodePosition(2, 7, andTest); + assertNodePosition(2, 7, aTest); + assertNodePosition(4, 3, bTest); + assertNodePosition(5, 2, cTest); + } + + public void testMultilineBitTestLocation() { + Node root = newParse( + "if (\n" + + " ((a \n" + + " | 3 \n" + + " ) == \n" + + " (b \n" + + " & 2)) && \n" + + " ((a \n" + + " ^ 0xffff) \n" + + " != \n" + + " (c \n" + + " << 1))) {\n" + + "}\n"); + + Node ifStmt = root.getFirstChild(); + Node andTest = ifStmt.getFirstChild(); + Node eqTest = andTest.getFirstChild(); + Node notEqTest = eqTest.getNext(); + + Node bitOrTest = eqTest.getFirstChild(); + Node bitAndTest = bitOrTest.getNext(); + + Node bitXorTest = notEqTest.getFirstChild(); + Node bitShiftTest = bitXorTest.getNext(); + + assertNodePosition(1, 0, ifStmt); + + assertNodePosition(2, 8, eqTest); + assertNodePosition(7, 8, notEqTest); + + assertNodePosition(2, 8, bitOrTest); + assertNodePosition(5, 8, bitAndTest); + assertNodePosition(7, 8, bitXorTest); + assertNodePosition(10, 8, bitShiftTest); + } + + public void testCallLocation() { + Node root = newParse( + "a.\n" + + "b.\n" + + "cccc(1);\n"); + + Node exprStmt = root.getFirstChild(); + Node functionCall = exprStmt.getFirstChild(); + Node functionProp = functionCall.getFirstChild(); + Node firstNameComponent = functionProp.getFirstChild(); + Node lastNameComponent = firstNameComponent.getNext(); + Node aNameComponent = firstNameComponent.getFirstChild(); + Node bNameComponent = aNameComponent.getNext(); + + assertNodePosition(1, 0, 13, functionCall); + assertNodePosition(1, 0, 10, functionProp); + // TODO(bowdidge) New Rhino doesn't keep the position of the dot handy. + // New Rhino treats the location of the qualified name as the beginning of + // the whole name. + assertNodePosition(1, 0, 4, firstNameComponent); + assertNodePosition(3, 0, 4, lastNameComponent); + + assertNodePosition(1, 0, 1, aNameComponent); + assertNodePosition(2, 0, 1, bNameComponent); + } + + public void testNewLocation() { + Node root = newParse( + "new c();\n"); + + Node exprStmt = root.getFirstChild(); + Node newExpr = exprStmt.getFirstChild(); + assertNodePosition(1, 0, 7, newExpr); + } + + public void testNewLocationMultiLine() { + Node root = newParse( + "new \n" + + "c();\n"); + + Node exprStmt = root.getFirstChild(); + Node newExpr = exprStmt.getFirstChild(); + assertNodePosition(1, 0, 10, newExpr); + } + + public void testLinenoDeclaration() { + Node root = newParse( + "a.\n" + + "b=\n" + + "function() {};\n"); + + Node exprStmt = root.getFirstChild(); + Node fnAssignment = exprStmt.getFirstChild(); + Node aDotbName = fnAssignment.getFirstChild(); + Node aName = aDotbName.getFirstChild(); + Node bName = aName.getNext(); + Node fnNode = aDotbName.getNext(); + Node fnName = fnNode.getFirstChild(); + + assertNodePosition(1, 0, fnAssignment); + // TODO(bowdidge) New Rhino doesn't keep track of the position of the dot. + //assertNodePosition(1, 1, aDotbName); + assertNodePosition(1, 0, aName); + assertNodePosition(2, 0, bName); + assertNodePosition(3, 0, fnNode); + assertNodePosition(3, 8, fnName); + } + + final String INVALID_ASSIGNMENT_TARGET = "invalid assignment target"; + final String INVALID_INCREMENT_TARGET = "invalid increment target"; + final String INVALID_DECREMENT_TARGET = "invalid decrement target"; + + final String INVALID_INC_OPERAND = "Invalid increment operand"; + final String INVALID_DEC_OPERAND = "Invalid decrement operand"; + + public void testAssignmentValidation() { + testNoParseError("x=1"); + testNoParseError("x.y=1"); + testNoParseError("f().y=1"); + testParseError("(x||y)=1", INVALID_ASSIGNMENT_TARGET); + testParseError("(x?y:z)=1", INVALID_ASSIGNMENT_TARGET); + testParseError("f()=1", INVALID_ASSIGNMENT_TARGET); + + testNoParseError("x+=1"); + testNoParseError("x.y+=1"); + testNoParseError("f().y+=1"); + testParseError("(x||y)+=1", INVALID_ASSIGNMENT_TARGET); + testParseError("(x?y:z)+=1", INVALID_ASSIGNMENT_TARGET); + testParseError("f()+=1", INVALID_ASSIGNMENT_TARGET); + + testParseError("f()++", INVALID_INCREMENT_TARGET); + testParseError("f()--", INVALID_DECREMENT_TARGET); + testParseError("++f()", INVALID_INCREMENT_TARGET); + testParseError("--f()", INVALID_DECREMENT_TARGET); + } + + private void testNoParseError(String string) { + testParseError(string, (String)null); + } + + private void testParseError(String string, String error) { + testParseError(string, error == null ? null : new String[] { error }); + } + + private void testParseError(String string, String[] errors) { + Node root = newParse(string, new TestErrorReporter(errors, null)); + assertTrue("unexpected warnings reported", + errorReporter.hasEncounteredAllWarnings()); + assertTrue("expected error were not reported", + errorReporter.hasEncounteredAllErrors()); + } + + private void assertMarkerPosition(Node n, int lineno, int charno) { + int count = 0; + for (JSDocInfo.Marker marker : n.getJSDocInfo().getMarkers()) { + assertEquals(lineno, marker.getAnnotation().getStartLine()); + assertEquals(charno, marker.getAnnotation().getPositionOnStartLine()); + count++; + } + assertEquals(1, count); + } + + private void assertNodePosition(int lineno, int charno, Node n) { + assertEquals("Line number", lineno, n.getLineno()); + assertEquals("Column position", charno, n.getCharno()); + } + + private void assertNodePosition(int lineno, int charno, int length, Node n) { + assertEquals("Line number", lineno, n.getLineno()); + assertEquals("Column position", charno, n.getCharno()); + assertEquals("Length", length, n.getLength()); + } + + + private void testNewParser(String code, String expected) { + String actual = newParse(code).toStringTree(); + assertEquals(expected, actual); + } + + private Node newParse(String string) { + return newParse(string, new TestErrorReporter(null, null)); + } + + private Node newParse(String string, TestErrorReporter errorReporter) { + CompilerEnvirons environment = new CompilerEnvirons(); + + environment.setRecordingComments(true); + environment.setRecordingLocalJsDocComments(true); + + Parser p = new Parser(environment); + AstRoot script = p.parse(string, null, 1); + + Config config = ParserRunner.createConfig(true, mode, false); + Node root = IRFactory.transformTree( + script, SourceFile.fromCode("FileName.js", string), string, config, errorReporter); + + return root; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/parsing/JsDocInfoParserTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/parsing/JsDocInfoParserTest.java new file mode 100644 index 0000000..7337f3e --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/parsing/JsDocInfoParserTest.java @@ -0,0 +1,2957 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.parsing; + +import com.google.common.collect.Sets; +import com.google.javascript.jscomp.parsing.Config.LanguageMode; +import com.google.javascript.jscomp.testing.TestErrorReporter; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.JSDocInfo.Visibility; +import com.google.javascript.rhino.JSTypeExpression; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.head.CompilerEnvirons; +import com.google.javascript.rhino.head.Parser; +import com.google.javascript.rhino.head.Token.CommentType; +import com.google.javascript.rhino.head.ast.AstRoot; +import com.google.javascript.rhino.head.ast.Comment; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.ObjectType; +import com.google.javascript.rhino.jstype.SimpleSourceFile; +import com.google.javascript.rhino.jstype.StaticSourceFile; +import com.google.javascript.rhino.testing.BaseJSTypeTestCase; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +public class JsDocInfoParserTest extends BaseJSTypeTestCase { + + private Set extraAnnotations; + private Set extraSuppressions; + private Node.FileLevelJsDocBuilder fileLevelJsDocBuilder = null; + + @Override + public void setUp() throws Exception { + super.setUp(); + extraAnnotations = + Sets.newHashSet( + ParserRunner.createConfig(true, LanguageMode.ECMASCRIPT3, false) + .annotationNames.keySet()); + extraSuppressions = + Sets.newHashSet( + ParserRunner.createConfig(true, LanguageMode.ECMASCRIPT3, false) + .suppressionNames); + + extraSuppressions.add("x"); + extraSuppressions.add("y"); + extraSuppressions.add("z"); + } + + public void testParseTypeViaStatic1() throws Exception { + Node typeNode = parseType("null"); + assertTypeEquals(NULL_TYPE, typeNode); + } + + public void testParseTypeViaStatic2() throws Exception { + Node typeNode = parseType("string"); + assertTypeEquals(STRING_TYPE, typeNode); + } + + public void testParseTypeViaStatic3() throws Exception { + Node typeNode = parseType("!Date"); + assertTypeEquals(DATE_TYPE, typeNode); + } + + public void testParseTypeViaStatic4() throws Exception { + Node typeNode = parseType("boolean|string"); + assertTypeEquals(createUnionType(BOOLEAN_TYPE, STRING_TYPE), typeNode); + } + + public void testParseInvalidTypeViaStatic() throws Exception { + Node typeNode = parseType("sometype. */"); + assertTypeEquals(parameterize(ARRAY_TYPE, NUMBER_TYPE), info.getType()); + } + + public void testParseParametrizedType2() throws Exception { + JSDocInfo info = parse("@type {!Array.}*/"); + assertTypeEquals(parameterize(ARRAY_TYPE, NUMBER_TYPE), info.getType()); + } + + public void testParseParametrizedType3() throws Exception { + JSDocInfo info = parse("@type !Array.<(number,null)>*/"); + assertTypeEquals( + parameterize(ARRAY_TYPE, createUnionType(NUMBER_TYPE, NULL_TYPE)), + info.getType()); + } + + public void testParseParametrizedType4() throws Exception { + JSDocInfo info = parse("@type {!Array.<(number|null)>}*/"); + assertTypeEquals( + parameterize(ARRAY_TYPE, createUnionType(NUMBER_TYPE, NULL_TYPE)), + info.getType()); + } + + public void testParseParametrizedType5() throws Exception { + JSDocInfo info = parse("@type {!Array.>}*/"); + assertTypeEquals( + parameterize(ARRAY_TYPE, + createUnionType(NULL_TYPE, + parameterize(ARRAY_TYPE, + createUnionType(NUMBER_TYPE, NULL_TYPE)))), + info.getType()); + } + + public void testParseParametrizedType6() throws Exception { + JSDocInfo info = parse("@type {!Array.>}*/"); + assertTypeEquals( + parameterize(ARRAY_TYPE, + parameterize(ARRAY_TYPE, + createUnionType(NUMBER_TYPE, NULL_TYPE))), + info.getType()); + } + + public void testParseParametrizedType7() throws Exception { + JSDocInfo info = parse("@type {!Array.}*/"); + assertTypeEquals( + parameterize(ARRAY_TYPE, + registry.createFunctionType( + createUnionType(DATE_TYPE, NULL_TYPE))), + info.getType()); + } + + public void testParseParametrizedType8() throws Exception { + JSDocInfo info = parse("@type {!Array.}*/"); + assertTypeEquals( + parameterize(ARRAY_TYPE, + registry.createFunctionType(DATE_TYPE)), + info.getType()); + } + + public void testParseParametrizedType9() throws Exception { + JSDocInfo info = parse("@type {!Array.}*/"); + assertTypeEquals( + parameterize(ARRAY_TYPE, + createUnionType(DATE_TYPE, NUMBER_TYPE, NULL_TYPE)), + info.getType()); + } + + public void testParseParametrizedType10() throws Exception { + JSDocInfo info = parse("@type {!Array.}*/"); + assertTypeEquals( + parameterize(ARRAY_TYPE, + createUnionType(DATE_TYPE, NUMBER_TYPE, BOOLEAN_TYPE, NULL_TYPE)), + info.getType()); + } + + public void testParseParameterizedType11() throws Exception { + JSDocInfo info = parse("@type {!Object.}*/"); + assertTypeEquals( + parameterize(OBJECT_TYPE, NUMBER_TYPE), + info.getType()); + assertParameterTypeEquals(NUMBER_TYPE, info.getType()); + } + + public void testParseParameterizedType12() throws Exception { + JSDocInfo info = parse("@type {!Object.}*/"); + assertTypeEquals( + parameterize(OBJECT_TYPE, NUMBER_TYPE), info.getType()); + assertParameterTypeEquals(NUMBER_TYPE, info.getType()); + assertIndexTypeEquals(STRING_TYPE, info.getType()); + } + + public void testParseParametrizedType13() throws Exception { + JSDocInfo info = parse("@type !Array. */"); + assertTypeEquals(parameterize(ARRAY_TYPE, UNKNOWN_TYPE), info.getType()); + } + + public void testParseUnionType1() throws Exception { + JSDocInfo info = parse("@type {(boolean,null)}*/"); + assertTypeEquals(createUnionType(BOOLEAN_TYPE, NULL_TYPE), info.getType()); + } + + public void testParseUnionType2() throws Exception { + JSDocInfo info = parse("@type {boolean|null}*/"); + assertTypeEquals(createUnionType(BOOLEAN_TYPE, NULL_TYPE), info.getType()); + } + + public void testParseUnionType3() throws Exception { + JSDocInfo info = parse("@type {boolean||null}*/"); + assertTypeEquals(createUnionType(BOOLEAN_TYPE, NULL_TYPE), info.getType()); + } + + public void testParseUnionType4() throws Exception { + JSDocInfo info = parse("@type {(Array.,null)}*/"); + assertTypeEquals(createUnionType( + parameterize(ARRAY_TYPE, BOOLEAN_TYPE), NULL_TYPE), info.getType()); + } + + public void testParseUnionType5() throws Exception { + JSDocInfo info = parse("@type {(null, Array.)}*/"); + assertTypeEquals(createUnionType( + parameterize(ARRAY_TYPE, BOOLEAN_TYPE), NULL_TYPE), info.getType()); + } + + public void testParseUnionType6() throws Exception { + JSDocInfo info = parse("@type {Array.|null}*/"); + assertTypeEquals(createUnionType( + parameterize(ARRAY_TYPE, BOOLEAN_TYPE), NULL_TYPE), info.getType()); + } + + public void testParseUnionType7() throws Exception { + JSDocInfo info = parse("@type {null|Array.}*/"); + assertTypeEquals(createUnionType( + parameterize(ARRAY_TYPE, BOOLEAN_TYPE), NULL_TYPE), info.getType()); + } + + public void testParseUnionType8() throws Exception { + JSDocInfo info = parse("@type {null||Array.}*/"); + assertTypeEquals(createUnionType( + parameterize(ARRAY_TYPE, BOOLEAN_TYPE), NULL_TYPE), info.getType()); + } + + public void testParseUnionType9() throws Exception { + JSDocInfo info = parse("@type {Array.||null}*/"); + assertTypeEquals(createUnionType( + parameterize(ARRAY_TYPE, BOOLEAN_TYPE), NULL_TYPE), info.getType()); + } + + public void testParseUnionType10() throws Exception { + parse("@type {string|}*/", + "Bad type annotation. type not recognized due to syntax error"); + } + + public void testParseUnionType11() throws Exception { + parse("@type {(string,)}*/", + "Bad type annotation. type not recognized due to syntax error"); + } + + public void testParseUnionType12() throws Exception { + parse("@type {()}*/", + "Bad type annotation. type not recognized due to syntax error"); + } + + public void testParseUnionType13() throws Exception { + testParseType( + "(function(this:Date),function(this:String):number)", + "Function"); + } + + public void testParseUnionType14() throws Exception { + testParseType( + "(function(...[function(number):boolean]):number)|" + + "function(this:String, string):number", + "Function"); + } + + public void testParseUnionType15() throws Exception { + testParseType("*|number", "*"); + } + + public void testParseUnionType16() throws Exception { + testParseType("number|*", "*"); + } + + public void testParseUnionType17() throws Exception { + testParseType("string|number|*", "*"); + } + + public void testParseUnionType18() throws Exception { + testParseType("(string,*,number)", "*"); + } + + public void testParseUnionTypeError1() throws Exception { + parse("@type {(string,|number)} */", + "Bad type annotation. type not recognized due to syntax error"); + } + + public void testParseUnknownType1() throws Exception { + testParseType("?"); + } + + public void testParseUnknownType2() throws Exception { + testParseType("(?|number)", "?"); + } + + public void testParseUnknownType3() throws Exception { + testParseType("(number|?)", "?"); + } + + public void testParseFunctionalType1() throws Exception { + testParseType("function (): number"); + } + + public void testParseFunctionalType2() throws Exception { + testParseType("function (number, string): boolean"); + } + + public void testParseFunctionalType3() throws Exception { + testParseType( + "function(this:Array)", "function (this:Array): ?"); + } + + public void testParseFunctionalType4() throws Exception { + testParseType("function (...[number]): boolean"); + } + + public void testParseFunctionalType5() throws Exception { + testParseType("function (number, ...[string]): boolean"); + } + + public void testParseFunctionalType6() throws Exception { + testParseType( + "function (this:Date, number): (boolean|number|string)"); + } + + public void testParseFunctionalType7() throws Exception { + testParseType("function()", "function (): ?"); + } + + public void testParseFunctionalType8() throws Exception { + testParseType( + "function(this:Array,...[boolean])", + "function (this:Array, ...[boolean]): ?"); + } + + public void testParseFunctionalType9() throws Exception { + testParseType( + "function(this:Array,!Date,...[boolean?])", + "function (this:Array, Date, ...[(boolean|null)]): ?"); + } + + public void testParseFunctionalType10() throws Exception { + testParseType( + "function(...[Object?]):boolean?", + "function (...[(Object|null)]): (boolean|null)"); + } + + public void testParseFunctionalType11() throws Exception { + testParseType( + "function(...[[number]]):[number?]", + "function (...[Array]): Array"); + } + + public void testParseFunctionalType12() throws Exception { + testParseType( + "function(...)", + "function (...[?]): ?"); + } + + public void testParseFunctionalType13() throws Exception { + testParseType( + "function(...): void", + "function (...[?]): undefined"); + } + + public void testParseFunctionalType14() throws Exception { + testParseType("function (*, string, number): boolean"); + } + + public void testParseFunctionalType15() throws Exception { + testParseType("function (?, string): boolean"); + } + + public void testParseFunctionalType16() throws Exception { + testParseType("function (string, ?): ?"); + } + + public void testParseFunctionalType17() throws Exception { + testParseType("(function (?): ?|number)"); + } + + public void testParseFunctionalType18() throws Exception { + testParseType("function (?): (?|number)", "function (?): ?"); + } + + public void testParseFunctionalType19() throws Exception { + testParseType( + "function(...[?]): void", + "function (...[?]): undefined"); + } + + public void testStructuralConstructor() throws Exception { + JSType type = testParseType( + "function (new:Object)", "function (new:Object): ?"); + assertTrue(type.isConstructor()); + assertFalse(type.isNominalConstructor()); + } + + public void testNominalConstructor() throws Exception { + ObjectType type = testParseType("Array", "(Array|null)").dereference(); + assertTrue(type.getConstructor().isNominalConstructor()); + } + + public void testBug1419535() throws Exception { + parse("@type {function(Object, string, *)?} */"); + parse("@type {function(Object, string, *)|null} */"); + } + + public void testIssue477() throws Exception { + parse("@type function */", + "Bad type annotation. missing opening ("); + } + + public void testMalformedThisAnnotation() throws Exception { + parse("@this */", + "Bad type annotation. type not recognized due to syntax error"); + } + + public void testParseFunctionalTypeError1() throws Exception { + parse("@type {function number):string}*/", + "Bad type annotation. missing opening ("); + } + + public void testParseFunctionalTypeError2() throws Exception { + parse("@type {function( number}*/", + "Bad type annotation. missing closing )"); + } + + public void testParseFunctionalTypeError3() throws Exception { + parse("@type {function(...[number], string)}*/", + "Bad type annotation. variable length argument must be last"); + } + + public void testParseFunctionalTypeError4() throws Exception { + parse("@type {function(string, ...[number], boolean):string}*/", + "Bad type annotation. variable length argument must be last"); + } + + public void testParseFunctionalTypeError5() throws Exception { + parse("@type {function (thi:Array)}*/", + "Bad type annotation. missing closing )"); + } + + public void testParseFunctionalTypeError6() throws Exception { + resolve(parse("@type {function (this:number)}*/").getType(), + "this type must be an object type"); + } + + public void testParseFunctionalTypeError7() throws Exception { + parse("@type {function(...[number)}*/", + "Bad type annotation. missing closing ]"); + } + + public void testParseFunctionalTypeError8() throws Exception { + parse("@type {function(...number])}*/", + "Bad type annotation. missing opening ["); + } + + public void testParseFunctionalTypeError9() throws Exception { + parse("@type {function (new:Array, this:Object)} */", + "Bad type annotation. missing closing )"); + } + + public void testParseFunctionalTypeError10() throws Exception { + parse("@type {function (this:Array, new:Object)} */", + "Bad type annotation. missing closing )"); + } + + public void testParseFunctionalTypeError11() throws Exception { + parse("@type {function (Array, new:Object)} */", + "Bad type annotation. missing closing )"); + } + + public void testParseFunctionalTypeError12() throws Exception { + resolve(parse("@type {function (new:number)}*/").getType(), + "constructed type must be an object type"); + } + + public void testParseArrayType1() throws Exception { + testParseType("[number]", "Array"); + } + + public void testParseArrayType2() throws Exception { + testParseType("[(number,boolean,[Object?])]", "Array"); + } + + public void testParseArrayType3() throws Exception { + testParseType("[[number],[string]]?", "(Array|null)"); + } + + public void testParseArrayTypeError1() throws Exception { + parse("@type {[number}*/", + "Bad type annotation. missing closing ]"); + } + + public void testParseArrayTypeError2() throws Exception { + parse("@type {number]}*/", + "Bad type annotation. expected closing }"); + } + + public void testParseArrayTypeError3() throws Exception { + parse("@type {[(number,boolean,Object?])]}*/", + "Bad type annotation. missing closing )"); + } + + public void testParseArrayTypeError4() throws Exception { + parse("@type {(number,boolean,[Object?)]}*/", + "Bad type annotation. missing closing ]"); + } + + private JSType testParseType(String type) throws Exception { + return testParseType(type, type); + } + + private JSType testParseType( + String type, String typeExpected) throws Exception { + JSDocInfo info = parse("@type {" + type + "}*/"); + + assertNotNull(info); + assertTrue(info.hasType()); + + JSType actual = resolve(info.getType()); + assertEquals(typeExpected, actual.toString()); + return actual; + } + + public void testParseNullableModifiers1() throws Exception { + JSDocInfo info = parse("@type {string?}*/"); + assertTypeEquals(createNullableType(STRING_TYPE), info.getType()); + } + + public void testParseNullableModifiers2() throws Exception { + JSDocInfo info = parse("@type {!Array.}*/"); + assertTypeEquals( + parameterize(ARRAY_TYPE, createUnionType(STRING_TYPE, NULL_TYPE)), + info.getType()); + } + + public void testParseNullableModifiers3() throws Exception { + JSDocInfo info = parse("@type {Array.?}*/"); + assertTypeEquals( + createNullableType(parameterize(ARRAY_TYPE, BOOLEAN_TYPE)), + info.getType()); + } + + public void testParseNullableModifiers4() throws Exception { + JSDocInfo info = parse("@type {(string,boolean)?}*/"); + assertTypeEquals( + createNullableType(createUnionType(STRING_TYPE, BOOLEAN_TYPE)), + info.getType()); + } + + public void testParseNullableModifiers5() throws Exception { + JSDocInfo info = parse("@type {(string?,boolean)}*/"); + assertTypeEquals( + createUnionType(createNullableType(STRING_TYPE), BOOLEAN_TYPE), + info.getType()); + } + + public void testParseNullableModifiers6() throws Exception { + JSDocInfo info = parse("@type {(string,boolean?)}*/"); + assertTypeEquals( + createUnionType(STRING_TYPE, createNullableType(BOOLEAN_TYPE)), + info.getType()); + } + + public void testParseNullableModifiers7() throws Exception { + JSDocInfo info = parse("@type {string?|boolean}*/"); + assertTypeEquals( + createUnionType(createNullableType(STRING_TYPE), BOOLEAN_TYPE), + info.getType()); + } + + public void testParseNullableModifiers8() throws Exception { + JSDocInfo info = parse("@type {string|boolean?}*/"); + assertTypeEquals( + createUnionType(STRING_TYPE, createNullableType(BOOLEAN_TYPE)), + info.getType()); + } + + public void testParseNullableModifiers9() throws Exception { + JSDocInfo info = parse("@type {foo.Hello.World?}*/"); + assertTypeEquals( + createNullableType( + registry.createNamedType( + "foo.Hello.World", null, -1, -1)), + info.getType()); + } + + public void testParseOptionalModifier() throws Exception { + JSDocInfo info = parse("@type {function(number=)}*/"); + assertTypeEquals( + registry.createFunctionType( + UNKNOWN_TYPE, registry.createOptionalParameters(NUMBER_TYPE)), + info.getType()); + } + + public void testParseNewline1() throws Exception { + JSDocInfo info = parse("@type {string\n* }\n*/"); + assertTypeEquals(STRING_TYPE, info.getType()); + } + + public void testParseNewline2() throws Exception { + JSDocInfo info = parse("@type !Array.<\n* number\n* > */"); + assertTypeEquals(parameterize(ARRAY_TYPE, NUMBER_TYPE), info.getType()); + } + + public void testParseNewline3() throws Exception { + JSDocInfo info = parse("@type !Array.<(number,\n* null)>*/"); + assertTypeEquals( + parameterize(ARRAY_TYPE, createUnionType(NUMBER_TYPE, NULL_TYPE)), + info.getType()); + } + + public void testParseNewline4() throws Exception { + JSDocInfo info = parse("@type !Array.<(number|\n* null)>*/"); + assertTypeEquals( + parameterize(ARRAY_TYPE, createUnionType(NUMBER_TYPE, NULL_TYPE)), + info.getType()); + } + + public void testParseNewline5() throws Exception { + JSDocInfo info = parse("@type !Array.*/"); + assertTypeEquals( + parameterize(ARRAY_TYPE, + registry.createFunctionType( + createUnionType(DATE_TYPE, NULL_TYPE))), + info.getType()); + } + + public void testParseReturnType1() throws Exception { + JSDocInfo info = + parse("@return {null|string|Array.}*/"); + assertTypeEquals( + createUnionType(parameterize(ARRAY_TYPE, BOOLEAN_TYPE), + NULL_TYPE, STRING_TYPE), + info.getReturnType()); + } + + public void testParseReturnType2() throws Exception { + JSDocInfo info = + parse("@returns {null|(string,Array.)}*/"); + assertTypeEquals( + createUnionType(parameterize(ARRAY_TYPE, BOOLEAN_TYPE), + NULL_TYPE, STRING_TYPE), + info.getReturnType()); + } + + public void testParseReturnType3() throws Exception { + JSDocInfo info = + parse("@return {((null||Array.,string),boolean)}*/"); + assertTypeEquals( + createUnionType(parameterize(ARRAY_TYPE, BOOLEAN_TYPE), + NULL_TYPE, STRING_TYPE, BOOLEAN_TYPE), + info.getReturnType()); + } + + public void testParseThisType1() throws Exception { + JSDocInfo info = + parse("@this {goog.foo.Bar}*/"); + assertTypeEquals( + registry.createNamedType("goog.foo.Bar", null, -1, -1), + info.getThisType()); + } + + public void testParseThisType2() throws Exception { + JSDocInfo info = + parse("@this goog.foo.Bar*/"); + assertTypeEquals( + registry.createNamedType("goog.foo.Bar", null, -1, -1), + info.getThisType()); + } + + public void testParseThisType3() throws Exception { + parse("@type {number}\n@this goog.foo.Bar*/", + "Bad type annotation. type annotation incompatible " + + "with other annotations"); + } + + public void testParseThisType4() throws Exception { + resolve(parse("@this number*/").getThisType(), + "@this must specify an object type"); + } + + public void testParseThisType5() throws Exception { + parse("@this {Date|Error}*/"); + } + + public void testParseThisType6() throws Exception { + resolve(parse("@this {Date|number}*/").getThisType(), + "@this must specify an object type"); + } + + public void testParseParam1() throws Exception { + JSDocInfo info = parse("@param {number} index*/"); + assertEquals(1, info.getParameterCount()); + assertTypeEquals(NUMBER_TYPE, info.getParameterType("index")); + } + + public void testParseParam2() throws Exception { + JSDocInfo info = parse("@param index*/"); + assertEquals(1, info.getParameterCount()); + assertEquals(null, info.getParameterType("index")); + } + + public void testParseParam3() throws Exception { + JSDocInfo info = parse("@param {number} index useful comments*/"); + assertEquals(1, info.getParameterCount()); + assertTypeEquals(NUMBER_TYPE, info.getParameterType("index")); + } + + public void testParseParam4() throws Exception { + JSDocInfo info = parse("@param index useful comments*/"); + assertEquals(1, info.getParameterCount()); + assertEquals(null, info.getParameterType("index")); + } + + public void testParseParam5() throws Exception { + // Test for multi-line @param. + JSDocInfo info = parse("@param {number} \n index */"); + assertEquals(1, info.getParameterCount()); + assertTypeEquals(NUMBER_TYPE, info.getParameterType("index")); + } + + public void testParseParam6() throws Exception { + // Test for multi-line @param. + JSDocInfo info = parse("@param {number} \n * index */"); + assertEquals(1, info.getParameterCount()); + assertTypeEquals(NUMBER_TYPE, info.getParameterType("index")); + } + + public void testParseParam7() throws Exception { + // Optional @param + JSDocInfo info = parse("@param {number=} index */"); + assertTypeEquals( + registry.createOptionalType(NUMBER_TYPE), + info.getParameterType("index")); + } + + public void testParseParam8() throws Exception { + // Var args @param + JSDocInfo info = parse("@param {...number} index */"); + assertTypeEquals( + registry.createOptionalType(NUMBER_TYPE), + info.getParameterType("index")); + } + + public void testParseParam9() throws Exception { + parse("@param {...number=} index */", + "Bad type annotation. expected closing }", + "Bad type annotation. expecting a variable name in a @param tag"); + } + + public void testParseParam10() throws Exception { + parse("@param {...number index */", + "Bad type annotation. expected closing }"); + } + + public void testParseParam11() throws Exception { + parse("@param {number= index */", + "Bad type annotation. expected closing }"); + } + + public void testParseParam12() throws Exception { + JSDocInfo info = parse("@param {...number|string} index */"); + assertTypeEquals( + registry.createOptionalType( + registry.createUnionType(STRING_TYPE, NUMBER_TYPE)), + info.getParameterType("index")); + } + + public void testParseParam13() throws Exception { + JSDocInfo info = parse("@param {...(number|string)} index */"); + assertTypeEquals( + registry.createOptionalType( + registry.createUnionType(STRING_TYPE, NUMBER_TYPE)), + info.getParameterType("index")); + } + + public void testParseParam14() throws Exception { + JSDocInfo info = parse("@param {string} [index] */"); + assertEquals(1, info.getParameterCount()); + assertTypeEquals( + registry.createOptionalType(STRING_TYPE), + info.getParameterType("index")); + } + + public void testParseParam15() throws Exception { + JSDocInfo info = parse("@param {string} [index */", + "Bad type annotation. missing closing ]"); + assertEquals(1, info.getParameterCount()); + assertTypeEquals(STRING_TYPE, info.getParameterType("index")); + } + + public void testParseParam16() throws Exception { + JSDocInfo info = parse("@param {string} index] */"); + assertEquals(1, info.getParameterCount()); + assertTypeEquals(STRING_TYPE, info.getParameterType("index")); + } + + public void testParseParam17() throws Exception { + JSDocInfo info = parse("@param {string=} [index] */"); + assertEquals(1, info.getParameterCount()); + assertTypeEquals( + registry.createOptionalType(STRING_TYPE), + info.getParameterType("index")); + } + + public void testParseParam18() throws Exception { + JSDocInfo info = parse("@param {...string} [index] */"); + assertEquals(1, info.getParameterCount()); + assertTypeEquals( + registry.createOptionalType(STRING_TYPE), + info.getParameterType("index")); + } + + public void testParseParam19() throws Exception { + JSDocInfo info = parse("@param {...} [index] */"); + assertEquals(1, info.getParameterCount()); + assertTypeEquals( + registry.createOptionalType(UNKNOWN_TYPE), + info.getParameterType("index")); + assertTrue(info.getParameterType("index").isVarArgs()); + } + + public void testParseParam20() throws Exception { + JSDocInfo info = parse("@param {?=} index */"); + assertEquals(1, info.getParameterCount()); + assertTypeEquals( + UNKNOWN_TYPE, info.getParameterType("index")); + } + + public void testParseParam21() throws Exception { + JSDocInfo info = parse("@param {...?} index */"); + assertEquals(1, info.getParameterCount()); + assertTypeEquals( + UNKNOWN_TYPE, info.getParameterType("index")); + assertTrue(info.getParameterType("index").isVarArgs()); + } + + public void testParseThrows1() throws Exception { + JSDocInfo info = parse("@throws {number} Some number */"); + assertEquals(1, info.getThrownTypes().size()); + assertTypeEquals(NUMBER_TYPE, info.getThrownTypes().get(0)); + } + + public void testParseThrows2() throws Exception { + JSDocInfo info = parse("@throws {number} Some number\n " + + "*@throws {String} A string */"); + assertEquals(2, info.getThrownTypes().size()); + assertTypeEquals(NUMBER_TYPE, info.getThrownTypes().get(0)); + } + + public void testParseRecordType1() throws Exception { + parseFull("/** @param {{x}} n\n*/"); + } + + public void testParseRecordType2() throws Exception { + parseFull("/** @param {{z, y}} n\n*/"); + } + + public void testParseRecordType3() throws Exception { + parseFull("/** @param {{z, y, x, q, hello, thisisatest}} n\n*/"); + } + + public void testParseRecordType4() throws Exception { + parseFull("/** @param {{a, 'a', 'hello', 2, this, do, while, for}} n\n*/"); + } + + public void testParseRecordType5() throws Exception { + parseFull("/** @param {{x : hello}} n\n*/"); + } + + public void testParseRecordType6() throws Exception { + parseFull("/** @param {{'x' : hello}} n\n*/"); + } + + public void testParseRecordType7() throws Exception { + parseFull("/** @param {{'x' : !hello}} n\n*/"); + } + + public void testParseRecordType8() throws Exception { + parseFull("/** @param {{'x' : !hello, y : bar}} n\n*/"); + } + + public void testParseRecordType9() throws Exception { + parseFull("/** @param {{'x' : !hello, y : {z : bar, 3 : meh}}} n\n*/"); + } + + public void testParseRecordType10() throws Exception { + parseFull("/** @param {{__proto__ : moo}} n\n*/"); + } + + public void testParseRecordType11() throws Exception { + parseFull("/** @param {{a : b} n\n*/", + "Bad type annotation. expected closing }"); + } + + public void testParseRecordType12() throws Exception { + parseFull("/** @param {{!hello : hey}} n\n*/", + "Bad type annotation. type not recognized due to syntax error"); + } + + public void testParseRecordType13() throws Exception { + parseFull("/** @param {{x}|number} n\n*/"); + } + + public void testParseRecordType14() throws Exception { + parseFull("/** @param {{x : y}|number} n\n*/"); + } + + public void testParseRecordType15() throws Exception { + parseFull("/** @param {{'x' : y}|number} n\n*/"); + } + + public void testParseRecordType16() throws Exception { + parseFull("/** @param {{x, y}|number} n\n*/"); + } + + public void testParseRecordType17() throws Exception { + parseFull("/** @param {{x : hello, 'y'}|number} n\n*/"); + } + + public void testParseRecordType18() throws Exception { + parseFull("/** @param {number|{x : hello, 'y'}} n\n*/"); + } + + public void testParseRecordType19() throws Exception { + parseFull("/** @param {?{x : hello, 'y'}} n\n*/"); + } + + public void testParseRecordType20() throws Exception { + parseFull("/** @param {!{x : hello, 'y'}} n\n*/"); + } + + public void testParseRecordType21() throws Exception { + parseFull("/** @param {{x : hello, 'y'}|boolean} n\n*/"); + } + + public void testParseRecordType22() throws Exception { + parseFull("/** @param {{x : hello, 'y'}|function()} n\n*/"); + } + + public void testParseRecordType23() throws Exception { + parseFull("/** @param {{x : function(), 'y'}|function()} n\n*/"); + } + + public void testParseParamError1() throws Exception { + parseFull("/** @param\n*/", + "Bad type annotation. expecting a variable name in a @param tag"); + } + + public void testParseParamError2() throws Exception { + parseFull("/** @param {Number}*/", + "Bad type annotation. expecting a variable name in a @param tag"); + } + + public void testParseParamError3() throws Exception { + parseFull("/** @param {Number}\n*/", + "Bad type annotation. expecting a variable name in a @param tag"); + } + + public void testParseParamError4() throws Exception { + parseFull("/** @param {Number}\n* * num */", + "Bad type annotation. expecting a variable name in a @param tag"); + } + + public void testParseParamError5() throws Exception { + parse("@param {number} x \n * @param {string} x */", + "Bad type annotation. duplicate variable name \"x\""); + } + + public void testParseExtends1() throws Exception { + assertTypeEquals(STRING_OBJECT_TYPE, + parse("@extends String*/").getBaseType()); + } + + public void testParseExtends2() throws Exception { + JSDocInfo info = parse("@extends com.google.Foo.Bar.Hello.World*/"); + assertTypeEquals( + registry.createNamedType( + "com.google.Foo.Bar.Hello.World", null, -1, -1), + info.getBaseType()); + } + + public void testParseExtendsGenerics() throws Exception { + JSDocInfo info = + parse("@extends com.google.Foo.Bar.Hello.World.*/"); + assertTypeEquals( + registry.createNamedType( + "com.google.Foo.Bar.Hello.World", null, -1, -1), + info.getBaseType()); + } + + public void testParseImplementsGenerics() throws Exception { + // we ignore things inside <> for now + List interfaces = + parse("@implements {SomeInterface.<*>} */") + .getImplementedInterfaces(); + assertEquals(1, interfaces.size()); + assertTypeEquals(registry.createNamedType("SomeInterface", null, -1, -1), + interfaces.get(0)); + } + + public void testParseExtends4() throws Exception { + assertTypeEquals(STRING_OBJECT_TYPE, + parse("@extends {String}*/").getBaseType()); + } + + public void testParseExtends5() throws Exception { + assertTypeEquals(STRING_OBJECT_TYPE, + parse("@extends {String*/", + "Bad type annotation. expected closing }").getBaseType()); + } + + public void testParseExtends6() throws Exception { + // Multi-line extends + assertTypeEquals(STRING_OBJECT_TYPE, + parse("@extends \n * {String}*/").getBaseType()); + } + + public void testParseExtendsInvalidName() throws Exception { + // This looks bad, but for the time being it should be OK, as + // we will not find a type with this name in the JS parsed tree. + // If this is fixed in the future, change this test to check for a + // warning/error message. + assertTypeEquals( + registry.createNamedType("some_++#%$%_UglyString", null, -1, -1), + parse("@extends {some_++#%$%_UglyString} */").getBaseType()); + } + + public void testParseExtendsNullable1() throws Exception { + parse("@extends {Base?} */", "Bad type annotation. expected closing }"); + } + + public void testParseExtendsNullable2() throws Exception { + parse("@extends Base? */", + "Bad type annotation. expected end of line or comment"); + } + + public void testParseEnum1() throws Exception { + assertTypeEquals(NUMBER_TYPE, parse("@enum*/").getEnumParameterType()); + } + + public void testParseEnum2() throws Exception { + assertTypeEquals(STRING_TYPE, + parse("@enum {string}*/").getEnumParameterType()); + } + + public void testParseEnum3() throws Exception { + assertTypeEquals(STRING_TYPE, + parse("@enum string*/").getEnumParameterType()); + } + + public void testParseDesc1() throws Exception { + assertEquals("hello world!", + parse("@desc hello world!*/").getDescription()); + } + + public void testParseDesc2() throws Exception { + assertEquals("hello world!", + parse("@desc hello world!\n*/").getDescription()); + } + + public void testParseDesc3() throws Exception { + assertEquals("", parse("@desc*/").getDescription()); + } + + public void testParseDesc4() throws Exception { + assertEquals("", parse("@desc\n*/").getDescription()); + } + + public void testParseDesc5() throws Exception { + assertEquals("hello world!", + parse("@desc hello\nworld!\n*/").getDescription()); + } + + public void testParseDesc6() throws Exception { + assertEquals("hello world!", + parse("@desc hello\n* world!\n*/").getDescription()); + } + + public void testParseDesc7() throws Exception { + assertEquals("a b c", parse("@desc a\n\nb\nc*/").getDescription()); + } + + public void testParseDesc8() throws Exception { + assertEquals("a b c d", + parse("@desc a\n *b\n\n *c\n\nd*/").getDescription()); + } + + public void testParseDesc9() throws Exception { + String comment = "@desc\n.\n,\n{\n)\n}\n|\n.<\n>\n<\n?\n~\n+\n-\n;\n:\n*/"; + + assertEquals(". , { ) } | .< > < ? ~ + - ; :", + parse(comment).getDescription()); + } + + public void testParseDesc10() throws Exception { + String comment = "@desc\n?\n?\n?\n?*/"; + + assertEquals("? ? ? ?", parse(comment).getDescription()); + } + + public void testParseDesc11() throws Exception { + String comment = "@desc :[]*/"; + + assertEquals(":[]", parse(comment).getDescription()); + } + + public void testParseDesc12() throws Exception { + String comment = "@desc\n:\n[\n]\n...*/"; + + assertEquals(": [ ] ...", parse(comment).getDescription()); + } + + public void testParseMeaning1() throws Exception { + assertEquals("tigers", + parse("@meaning tigers */").getMeaning()); + } + + public void testParseMeaning2() throws Exception { + assertEquals("tigers and lions and bears", + parse("@meaning tigers\n * and lions\n * and bears */").getMeaning()); + } + + public void testParseMeaning3() throws Exception { + JSDocInfo info = + parse("@meaning tigers\n * and lions\n * @desc and bears */"); + assertEquals("tigers and lions", info.getMeaning()); + assertEquals("and bears", info.getDescription()); + } + + public void testParseMeaning4() throws Exception { + parse("@meaning tigers\n * @meaning and lions */", + "extra @meaning tag"); + } + + public void testParseLends1() throws Exception { + JSDocInfo info = parse("@lends {name} */"); + assertEquals("name", info.getLendsName()); + } + + public void testParseLends2() throws Exception { + JSDocInfo info = parse("@lends foo.bar */"); + assertEquals("foo.bar", info.getLendsName()); + } + + public void testParseLends3() throws Exception { + parse("@lends {name */", "Bad type annotation. expected closing }"); + } + + public void testParseLends4() throws Exception { + parse("@lends {} */", + "Bad type annotation. missing object name in @lends tag"); + } + + public void testParseLends5() throws Exception { + parse("@lends } */", + "Bad type annotation. missing object name in @lends tag"); + } + + public void testParseLends6() throws Exception { + parse("@lends {string} \n * @lends {string} */", + "Bad type annotation. @lends tag incompatible with other annotations"); + } + + public void testParseLends7() throws Exception { + parse("@type {string} \n * @lends {string} */", + "Bad type annotation. @lends tag incompatible with other annotations"); + } + + public void testParsePreserve() throws Exception { + Node node = new Node(1); + this.fileLevelJsDocBuilder = node.getJsDocBuilderForNode(); + String comment = "@preserve Foo\nBar\n\nBaz*/"; + parse(comment); + assertEquals(" Foo\nBar\n\nBaz", node.getJSDocInfo().getLicense()); + } + + public void testParseLicense() throws Exception { + Node node = new Node(1); + this.fileLevelJsDocBuilder = node.getJsDocBuilderForNode(); + String comment = "@license Foo\nBar\n\nBaz*/"; + parse(comment); + assertEquals(" Foo\nBar\n\nBaz", node.getJSDocInfo().getLicense()); + } + + public void testParseLicenseAscii() throws Exception { + Node node = new Node(1); + this.fileLevelJsDocBuilder = node.getJsDocBuilderForNode(); + String comment = "@license Foo\n * Bar\n\n Baz*/"; + parse(comment); + assertEquals(" Foo\n Bar\n\n Baz", node.getJSDocInfo().getLicense()); + } + + public void testParseLicenseWithAnnotation() throws Exception { + Node node = new Node(1); + this.fileLevelJsDocBuilder = node.getJsDocBuilderForNode(); + String comment = "@license Foo \n * @author Charlie Brown */"; + parse(comment); + assertEquals(" Foo \n @author Charlie Brown ", + node.getJSDocInfo().getLicense()); + } + + public void testParseDefine1() throws Exception { + assertTypeEquals(STRING_TYPE, + parse("@define {string}*/").getType()); + } + + public void testParseDefine2() throws Exception { + assertTypeEquals(STRING_TYPE, + parse("@define {string*/", + "Bad type annotation. expected closing }").getType()); + } + + public void testParseDefine3() throws Exception { + JSDocInfo info = parse("@define {boolean}*/"); + assertTrue(info.isConstant()); + assertTrue(info.isDefine()); + assertTypeEquals(BOOLEAN_TYPE, info.getType()); + } + + public void testParseDefine4() throws Exception { + assertTypeEquals(NUMBER_TYPE, parse("@define {number}*/").getType()); + } + + public void testParseDefine5() throws Exception { + assertTypeEquals(createUnionType(NUMBER_TYPE, BOOLEAN_TYPE), + parse("@define {number|boolean}*/").getType()); + } + + public void testParseDefineErrors1() throws Exception { + parse("@enum {string}\n @define {string} */", "conflicting @define tag"); + } + + public void testParseDefineErrors2() throws Exception { + parse("@define {string}\n @enum {string} */", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testParseDefineErrors3() throws Exception { + parse("@const\n @define {string} */", "conflicting @define tag"); + } + + public void testParseDefineErrors4() throws Exception { + parse("@type string \n @define {string} */", "conflicting @define tag"); + } + + public void testParseDefineErrors5() throws Exception { + parse("@return {string}\n @define {string} */", "conflicting @define tag"); + } + + public void testParseDefineErrors7() throws Exception { + parse("@define {string}\n @const */", "conflicting @const tag"); + } + + public void testParseDefineErrors8() throws Exception { + parse("@define {string}\n @type string */", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testParseNoCheck1() throws Exception { + assertTrue(parse("@notypecheck*/").isNoTypeCheck()); + } + + public void testParseNoCheck2() throws Exception { + parse("@notypecheck\n@notypecheck*/", "extra @notypecheck tag"); + } + + public void testParseOverride1() throws Exception { + assertTrue(parse("@override*/").isOverride()); + } + + public void testParseOverride2() throws Exception { + parse("@override\n@override*/", + "Bad type annotation. extra @override/@inheritDoc tag"); + } + + public void testParseInheritDoc1() throws Exception { + assertTrue(parse("@inheritDoc*/").isOverride()); + } + + public void testParseInheritDoc2() throws Exception { + parse("@override\n@inheritDoc*/", + "Bad type annotation. extra @override/@inheritDoc tag"); + } + + public void testParseInheritDoc3() throws Exception { + parse("@inheritDoc\n@inheritDoc*/", + "Bad type annotation. extra @override/@inheritDoc tag"); + } + + public void testParseNoAlias1() throws Exception { + assertTrue(parse("@noalias*/").isNoAlias()); + } + + public void testParseNoAlias2() throws Exception { + parse("@noalias\n@noalias*/", "extra @noalias tag"); + } + + public void testParseDeprecated1() throws Exception { + assertTrue(parse("@deprecated*/").isDeprecated()); + } + + public void testParseDeprecated2() throws Exception { + parse("@deprecated\n@deprecated*/", "extra @deprecated tag"); + } + + public void testParseExport1() throws Exception { + assertTrue(parse("@export*/").isExport()); + } + + public void testParseExport2() throws Exception { + parse("@export\n@export*/", "extra @export tag"); + } + + public void testParseExpose1() throws Exception { + assertTrue(parse("@expose*/").isExpose()); + } + + public void testParseExpose2() throws Exception { + parse("@expose\n@expose*/", "extra @expose tag"); + } + + public void testParseExterns1() throws Exception { + assertTrue(parseFileOverview("@externs*/").isExterns()); + } + + public void testParseExterns2() throws Exception { + parseFileOverview("@externs\n@externs*/", "extra @externs tag"); + } + + public void testParseExterns3() throws Exception { + assertNull(parse("@externs*/")); + } + + public void testParseJavaDispatch1() throws Exception { + assertTrue(parse("@javadispatch*/").isJavaDispatch()); + } + + public void testParseJavaDispatch2() throws Exception { + parse("@javadispatch\n@javadispatch*/", + "extra @javadispatch tag"); + } + + public void testParseJavaDispatch3() throws Exception { + assertNull(parseFileOverview("@javadispatch*/")); + } + + public void testParseNoCompile1() throws Exception { + assertTrue(parseFileOverview("@nocompile*/").isNoCompile()); + } + + public void testParseNoCompile2() throws Exception { + parseFileOverview("@nocompile\n@nocompile*/", "extra @nocompile tag"); + } + + public void testBugAnnotation() throws Exception { + parse("@bug */"); + } + + public void testDescriptionAnnotation() throws Exception { + parse("@description */"); + } + + public void testRegression1() throws Exception { + String comment = + " * @param {number} index the index of blah\n" + + " * @return {boolean} whatever\n" + + " * @private\n" + + " */"; + + JSDocInfo info = parse(comment); + assertEquals(1, info.getParameterCount()); + assertTypeEquals(NUMBER_TYPE, info.getParameterType("index")); + assertTypeEquals(BOOLEAN_TYPE, info.getReturnType()); + assertEquals(Visibility.PRIVATE, info.getVisibility()); + } + + public void testRegression2() throws Exception { + String comment = + " * @return {boolean} whatever\n" + + " * but important\n" + + " *\n" + + " * @param {number} index the index of blah\n" + + " * some more comments here\n" + + " * @param name the name of the guy\n" + + " *\n" + + " * @protected\n" + + " */"; + + JSDocInfo info = parse(comment); + assertEquals(2, info.getParameterCount()); + assertTypeEquals(NUMBER_TYPE, info.getParameterType("index")); + assertEquals(null, info.getParameterType("name")); + assertTypeEquals(BOOLEAN_TYPE, info.getReturnType()); + assertEquals(Visibility.PROTECTED, info.getVisibility()); + } + + public void testRegression3() throws Exception { + String comment = + " * @param mediaTag this specified whether the @media tag is ....\n" + + " *\n" + + "\n" + + "@public\n" + + " *\n" + + "\n" + + " **********\n" + + " * @final\n" + + " */"; + + JSDocInfo info = parse(comment); + assertEquals(1, info.getParameterCount()); + assertEquals(null, info.getParameterType("mediaTag")); + assertEquals(Visibility.PUBLIC, info.getVisibility()); + assertTrue(info.isConstant()); + } + + public void testRegression4() throws Exception { + String comment = + " * @const\n" + + " * @hidden\n" + + " * @preserveTry\n" + + " * @constructor\n" + + " */"; + + JSDocInfo info = parse(comment); + assertTrue(info.isConstant()); + assertFalse(info.isDefine()); + assertTrue(info.isConstructor()); + assertTrue(info.isHidden()); + assertTrue(info.shouldPreserveTry()); + } + + public void testRegression5() throws Exception { + String comment = "@const\n@enum {string}\n@public*/"; + + JSDocInfo info = parse(comment); + assertTrue(info.isConstant()); + assertFalse(info.isDefine()); + assertTypeEquals(STRING_TYPE, info.getEnumParameterType()); + assertEquals(Visibility.PUBLIC, info.getVisibility()); + } + + public void testRegression6() throws Exception { + String comment = "@hidden\n@enum\n@public*/"; + + JSDocInfo info = parse(comment); + assertTrue(info.isHidden()); + assertTypeEquals(NUMBER_TYPE, info.getEnumParameterType()); + assertEquals(Visibility.PUBLIC, info.getVisibility()); + } + + public void testRegression7() throws Exception { + String comment = + " * @desc description here\n" + + " * @param {boolean} flag and some more description\n" + + " * nicely formatted\n" + + " */"; + + JSDocInfo info = parse(comment); + assertEquals(1, info.getParameterCount()); + assertTypeEquals(BOOLEAN_TYPE, info.getParameterType("flag")); + assertEquals("description here", info.getDescription()); + } + + public void testRegression8() throws Exception { + String comment = + " * @name random tag here\n" + + " * @desc description here\n" + + " *\n" + + " * @param {boolean} flag and some more description\n" + + " * nicely formatted\n" + + " */"; + + JSDocInfo info = parse(comment); + assertEquals(1, info.getParameterCount()); + assertTypeEquals(BOOLEAN_TYPE, info.getParameterType("flag")); + assertEquals("description here", info.getDescription()); + } + + public void testRegression9() throws Exception { + JSDocInfo jsdoc = parse( + " * @param {string} p0 blah blah blah\n" + + " */"); + + assertNull(jsdoc.getBaseType()); + assertFalse(jsdoc.isConstant()); + assertNull(jsdoc.getDescription()); + assertNull(jsdoc.getEnumParameterType()); + assertFalse(jsdoc.isHidden()); + assertEquals(1, jsdoc.getParameterCount()); + assertTypeEquals(STRING_TYPE, jsdoc.getParameterType("p0")); + assertNull(jsdoc.getReturnType()); + assertNull(jsdoc.getType()); + assertEquals(Visibility.INHERITED, jsdoc.getVisibility()); + } + + public void testRegression10() throws Exception { + JSDocInfo jsdoc = parse( + " * @param {!String} p0 blah blah blah\n" + + " * @param {boolean} p1 fobar\n" + + " * @return {!Date} jksjkash dshad\n" + + " */"); + + assertNull(jsdoc.getBaseType()); + assertFalse(jsdoc.isConstant()); + assertNull(jsdoc.getDescription()); + assertNull(jsdoc.getEnumParameterType()); + assertFalse(jsdoc.isHidden()); + assertEquals(2, jsdoc.getParameterCount()); + assertTypeEquals(STRING_OBJECT_TYPE, jsdoc.getParameterType("p0")); + assertTypeEquals(BOOLEAN_TYPE, jsdoc.getParameterType("p1")); + assertTypeEquals(DATE_TYPE, jsdoc.getReturnType()); + assertNull(jsdoc.getType()); + assertEquals(Visibility.INHERITED, jsdoc.getVisibility()); + } + + public void testRegression11() throws Exception { + JSDocInfo jsdoc = parse( + " * @constructor\n" + + " */"); + + assertNull(jsdoc.getBaseType()); + assertFalse(jsdoc.isConstant()); + assertNull(jsdoc.getDescription()); + assertNull(jsdoc.getEnumParameterType()); + assertFalse(jsdoc.isHidden()); + assertEquals(0, jsdoc.getParameterCount()); + assertNull(jsdoc.getReturnType()); + assertNull(jsdoc.getType()); + assertEquals(Visibility.INHERITED, jsdoc.getVisibility()); + } + + public void testRegression12() throws Exception { + JSDocInfo jsdoc = parse( + " * @extends FooBar\n" + + " */"); + + assertTypeEquals(registry.createNamedType("FooBar", null, 0, 0), + jsdoc.getBaseType()); + assertFalse(jsdoc.isConstant()); + assertNull(jsdoc.getDescription()); + assertNull(jsdoc.getEnumParameterType()); + assertFalse(jsdoc.isHidden()); + assertEquals(0, jsdoc.getParameterCount()); + assertNull(jsdoc.getReturnType()); + assertNull(jsdoc.getType()); + assertEquals(Visibility.INHERITED, jsdoc.getVisibility()); + } + + public void testRegression13() throws Exception { + JSDocInfo jsdoc = parse( + " * @type {!RegExp}\n" + + " * @protected\n" + + " */"); + + assertNull(jsdoc.getBaseType()); + assertFalse(jsdoc.isConstant()); + assertNull(jsdoc.getDescription()); + assertNull(jsdoc.getEnumParameterType()); + assertFalse(jsdoc.isHidden()); + assertEquals(0, jsdoc.getParameterCount()); + assertNull(jsdoc.getReturnType()); + assertTypeEquals(REGEXP_TYPE, jsdoc.getType()); + assertEquals(Visibility.PROTECTED, jsdoc.getVisibility()); + } + + public void testRegression14() throws Exception { + JSDocInfo jsdoc = parse( + " * @const\n" + + " * @private\n" + + " */"); + + assertNull(jsdoc.getBaseType()); + assertTrue(jsdoc.isConstant()); + assertNull(jsdoc.getDescription()); + assertNull(jsdoc.getEnumParameterType()); + assertFalse(jsdoc.isHidden()); + assertEquals(0, jsdoc.getParameterCount()); + assertNull(jsdoc.getReturnType()); + assertNull(jsdoc.getType()); + assertEquals(Visibility.PRIVATE, jsdoc.getVisibility()); + } + + public void testRegression15() throws Exception { + JSDocInfo jsdoc = parse( + " * @desc Hello,\n" + + " * World!\n" + + " */"); + + assertNull(jsdoc.getBaseType()); + assertFalse(jsdoc.isConstant()); + assertEquals("Hello, World!", jsdoc.getDescription()); + assertNull(jsdoc.getEnumParameterType()); + assertFalse(jsdoc.isHidden()); + assertEquals(0, jsdoc.getParameterCount()); + assertNull(jsdoc.getReturnType()); + assertNull(jsdoc.getType()); + assertEquals(Visibility.INHERITED, jsdoc.getVisibility()); + assertFalse(jsdoc.isExport()); + } + + public void testRegression16() throws Exception { + JSDocInfo jsdoc = parse( + " Email is plp@foo.bar\n" + + " @type {string}\n" + + " */"); + + assertNull(jsdoc.getBaseType()); + assertFalse(jsdoc.isConstant()); + assertTypeEquals(STRING_TYPE, jsdoc.getType()); + assertFalse(jsdoc.isHidden()); + assertEquals(0, jsdoc.getParameterCount()); + assertNull(jsdoc.getReturnType()); + assertEquals(Visibility.INHERITED, jsdoc.getVisibility()); + } + + public void testRegression17() throws Exception { + // verifying that if no @desc is present the description is empty + assertNull(parse("@private*/").getDescription()); + } + + public void testFullRegression1() throws Exception { + parseFull("/** @param (string,number) foo*/function bar(foo){}", + "Bad type annotation. expecting a variable name in a @param tag"); + } + + public void testFullRegression2() throws Exception { + parseFull("/** @param {string,number) foo*/function bar(foo){}", + "Bad type annotation. expected closing }", + "Bad type annotation. expecting a variable name in a @param tag"); + } + + public void testFullRegression3() throws Exception { + parseFull("/**..\n*/"); + } + + public void testBug907488() throws Exception { + parse("@type {number,null} */", + "Bad type annotation. expected closing }"); + } + + public void testBug907494() throws Exception { + parse("@return {Object,undefined} */", + "Bad type annotation. expected closing }"); + } + + public void testBug909468() throws Exception { + parse("@extends {(x)}*/", + "Bad type annotation. expecting a type name"); + } + + public void testParseInterface() throws Exception { + assertTrue(parse("@interface*/").isInterface()); + } + + public void testParseImplicitCast1() throws Exception { + assertTrue(parse("@type {string} \n * @implicitCast*/").isImplicitCast()); + } + + public void testParseImplicitCast2() throws Exception { + assertFalse(parse("@type {string}*/").isImplicitCast()); + } + + public void testParseDuplicateImplicitCast() throws Exception { + parse("@type {string} \n * @implicitCast \n * @implicitCast*/", + "Bad type annotation. extra @implicitCast tag"); + } + + public void testParseInterfaceDoubled() throws Exception { + parse( + "* @interface\n" + + "* @interface\n" + + "*/", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testParseImplements() throws Exception { + List interfaces = parse("@implements {SomeInterface}*/") + .getImplementedInterfaces(); + assertEquals(1, interfaces.size()); + assertTypeEquals(registry.createNamedType("SomeInterface", null, -1, -1), + interfaces.get(0)); + } + + public void testParseImplementsTwo() throws Exception { + List interfaces = + parse( + "* @implements {SomeInterface1}\n" + + "* @implements {SomeInterface2}\n" + + "*/") + .getImplementedInterfaces(); + assertEquals(2, interfaces.size()); + assertTypeEquals(registry.createNamedType("SomeInterface1", null, -1, -1), + interfaces.get(0)); + assertTypeEquals(registry.createNamedType("SomeInterface2", null, -1, -1), + interfaces.get(1)); + } + + public void testParseImplementsSameTwice() throws Exception { + parse( + "* @implements {Smth}\n" + + "* @implements {Smth}\n" + + "*/", + "Bad type annotation. duplicate @implements tag"); + } + + public void testParseImplementsNoName() throws Exception { + parse("* @implements {} */", + "Bad type annotation. expecting a type name"); + } + + public void testParseImplementsMissingRC() throws Exception { + parse("* @implements {Smth */", + "Bad type annotation. expected closing }"); + } + + public void testParseImplementsNullable1() throws Exception { + parse("@implements {Base?} */", "Bad type annotation. expected closing }"); + } + + public void testParseImplementsNullable2() throws Exception { + parse("@implements Base? */", + "Bad type annotation. expected end of line or comment"); + } + + public void testInterfaceExtends() throws Exception { + JSDocInfo jsdoc = parse( + " * @interface \n" + + " * @extends {Extended} */"); + assertTrue(jsdoc.isInterface()); + assertEquals(1, jsdoc.getExtendedInterfacesCount()); + List types = jsdoc.getExtendedInterfaces(); + assertTypeEquals(registry.createNamedType("Extended", null, -1, -1), + types.get(0)); + } + + public void testInterfaceMultiExtends1() throws Exception { + JSDocInfo jsdoc = parse( + " * @interface \n" + + " * @extends {Extended1} \n" + + " * @extends {Extended2} */"); + assertTrue(jsdoc.isInterface()); + assertNull(jsdoc.getBaseType()); + assertEquals(2, jsdoc.getExtendedInterfacesCount()); + List types = jsdoc.getExtendedInterfaces(); + assertTypeEquals(registry.createNamedType("Extended1", null, -1, -1), + types.get(0)); + assertTypeEquals(registry.createNamedType("Extended2", null, -1, -1), + types.get(1)); + } + + public void testInterfaceMultiExtends2() throws Exception { + JSDocInfo jsdoc = parse( + " * @extends {Extended1} \n" + + " * @interface \n" + + " * @extends {Extended2} \n" + + " * @extends {Extended3} */"); + assertTrue(jsdoc.isInterface()); + assertNull(jsdoc.getBaseType()); + assertEquals(3, jsdoc.getExtendedInterfacesCount()); + List types = jsdoc.getExtendedInterfaces(); + assertTypeEquals(registry.createNamedType("Extended1", null, -1, -1), + types.get(0)); + assertTypeEquals(registry.createNamedType("Extended2", null, -1, -1), + types.get(1)); + assertTypeEquals(registry.createNamedType("Extended3", null, -1, -1), + types.get(2)); + } + + public void testBadClassMultiExtends() throws Exception { + parse(" * @extends {Extended1} \n" + + " * @constructor \n" + + " * @extends {Extended2} */", + "Bad type annotation. type annotation incompatible with other " + + "annotations"); + } + + public void testBadExtendsWithNullable() throws Exception { + JSDocInfo jsdoc = parse("@constructor\n * @extends {Object?} */", + "Bad type annotation. expected closing }"); + assertTrue(jsdoc.isConstructor()); + assertTypeEquals(OBJECT_TYPE, jsdoc.getBaseType()); + } + + public void testBadImplementsWithNullable() throws Exception { + JSDocInfo jsdoc = parse("@implements {Disposable?}\n * @constructor */", + "Bad type annotation. expected closing }"); + assertTrue(jsdoc.isConstructor()); + assertTypeEquals(registry.createNamedType("Disposable", null, -1, -1), + jsdoc.getImplementedInterfaces().get(0)); + } + + public void testBadTypeDefInterfaceAndConstructor1() throws Exception { + JSDocInfo jsdoc = parse("@interface\n@constructor*/", + "Bad type annotation. cannot be both an interface and a constructor"); + assertTrue(jsdoc.isInterface()); + } + + public void testBadTypeDefInterfaceAndConstructor2() throws Exception { + JSDocInfo jsdoc = parse("@constructor\n@interface*/", + "Bad type annotation. cannot be both an interface and a constructor"); + assertTrue(jsdoc.isConstructor()); + } + + public void testDocumentationParameter() throws Exception { + JSDocInfo jsdoc + = parse("@param {Number} number42 This is a description.*/", true); + + assertTrue(jsdoc.hasDescriptionForParameter("number42")); + assertEquals("This is a description.", + jsdoc.getDescriptionForParameter("number42")); + } + + public void testMultilineDocumentationParameter() throws Exception { + JSDocInfo jsdoc + = parse("@param {Number} number42 This is a description" + + "\n* on multiple \n* lines.*/", true); + + assertTrue(jsdoc.hasDescriptionForParameter("number42")); + assertEquals("This is a description on multiple lines.", + jsdoc.getDescriptionForParameter("number42")); + + } + + public void testDocumentationMultipleParameter() throws Exception { + JSDocInfo jsdoc + = parse("@param {Number} number42 This is a description." + + "\n* @param {Integer} number87 This is another description.*/" + , true); + + assertTrue(jsdoc.hasDescriptionForParameter("number42")); + assertEquals("This is a description.", + jsdoc.getDescriptionForParameter("number42")); + + assertTrue(jsdoc.hasDescriptionForParameter("number87")); + assertEquals("This is another description.", + jsdoc.getDescriptionForParameter("number87")); + } + + public void testDocumentationMultipleParameter2() throws Exception { + JSDocInfo jsdoc + = parse("@param {number} delta = 0 results in a redraw\n" + + " != 0 ..... */", true); + assertTrue(jsdoc.hasDescriptionForParameter("delta")); + assertEquals("= 0 results in a redraw != 0 .....", + jsdoc.getDescriptionForParameter("delta")); + } + + + public void testAuthors() throws Exception { + JSDocInfo jsdoc + = parse("@param {Number} number42 This is a description." + + "\n* @param {Integer} number87 This is another description." + + "\n* @author a@google.com (A Person)" + + "\n* @author b@google.com (B Person)" + + "\n* @author c@google.com (C Person)*/" + , true); + + Collection authors = jsdoc.getAuthors(); + + assertTrue(authors != null); + assertTrue(authors.size() == 3); + + assertContains(authors, "a@google.com (A Person)"); + assertContains(authors, "b@google.com (B Person)"); + assertContains(authors, "c@google.com (C Person)"); + } + + public void testSuppress1() throws Exception { + JSDocInfo info = parse("@suppress {x} */"); + assertEquals(Sets.newHashSet("x"), info.getSuppressions()); + } + + public void testSuppress2() throws Exception { + JSDocInfo info = parse("@suppress {x|y|x|z} */"); + assertEquals(Sets.newHashSet("x", "y", "z"), info.getSuppressions()); + } + + public void testBadSuppress1() throws Exception { + parse("@suppress {} */", "malformed @suppress tag"); + } + + public void testBadSuppress2() throws Exception { + parse("@suppress {x|} */", "malformed @suppress tag"); + } + + public void testBadSuppress3() throws Exception { + parse("@suppress {|x} */", "malformed @suppress tag"); + } + + public void testBadSuppress4() throws Exception { + parse("@suppress {x|y */", "malformed @suppress tag"); + } + + public void testBadSuppress5() throws Exception { + parse("@suppress {x,y} */", "malformed @suppress tag"); + } + + public void testBadSuppress6() throws Exception { + parse("@suppress {x} \n * @suppress {y} */", "duplicate @suppress tag"); + } + + public void testBadSuppress7() throws Exception { + parse("@suppress {impossible} */", + "unknown @suppress parameter: impossible"); + } + + public void testModifies1() throws Exception { + JSDocInfo info = parse("@modifies {this} */"); + assertEquals(Sets.newHashSet("this"), info.getModifies()); + } + + public void testModifies2() throws Exception { + JSDocInfo info = parse("@modifies {arguments} */"); + assertEquals(Sets.newHashSet("arguments"), info.getModifies()); + } + + public void testModifies3() throws Exception { + JSDocInfo info = parse("@modifies {this|arguments} */"); + assertEquals(Sets.newHashSet("this", "arguments"), info.getModifies()); + } + + public void testModifies4() throws Exception { + JSDocInfo info = parse("@param {*} x\n * @modifies {x} */"); + assertEquals(Sets.newHashSet("x"), info.getModifies()); + } + + public void testModifies5() throws Exception { + JSDocInfo info = parse( + "@param {*} x\n" + + " * @param {*} y\n" + + " * @modifies {x} */"); + assertEquals(Sets.newHashSet("x"), info.getModifies()); + } + + public void testModifies6() throws Exception { + JSDocInfo info = parse( + "@param {*} x\n" + + " * @param {*} y\n" + + " * @modifies {x|y} */"); + assertEquals(Sets.newHashSet("x", "y"), info.getModifies()); + } + + + public void testBadModifies1() throws Exception { + parse("@modifies {} */", "malformed @modifies tag"); + } + + public void testBadModifies2() throws Exception { + parse("@modifies {this|} */", "malformed @modifies tag"); + } + + public void testBadModifies3() throws Exception { + parse("@modifies {|this} */", "malformed @modifies tag"); + } + + public void testBadModifies4() throws Exception { + parse("@modifies {this|arguments */", "malformed @modifies tag"); + } + + public void testBadModifies5() throws Exception { + parse("@modifies {this,arguments} */", "malformed @modifies tag"); + } + + public void testBadModifies6() throws Exception { + parse("@modifies {this} \n * @modifies {this} */", + "conflicting @modifies tag"); + } + + public void testBadModifies7() throws Exception { + parse("@modifies {impossible} */", + "unknown @modifies parameter: impossible"); + } + + public void testBadModifies8() throws Exception { + parse("@modifies {this}\n" + + "@nosideeffects */", "conflicting @nosideeffects tag"); + } + + public void testBadModifies9() throws Exception { + parse("@nosideeffects\n" + + "@modifies {this} */", "conflicting @modifies tag"); + } + + //public void testNoParseFileOverview() throws Exception { + // JSDocInfo jsdoc = parseFileOverviewWithoutDoc("@fileoverview Hi mom! */"); + // assertNull(jsdoc.getFileOverview()); + // assertTrue(jsdoc.hasFileOverview()); + //} + + public void testFileOverviewSingleLine() throws Exception { + JSDocInfo jsdoc = parseFileOverview("@fileoverview Hi mom! */"); + assertEquals("Hi mom!", jsdoc.getFileOverview()); + } + + public void testFileOverviewMultiLine() throws Exception { + JSDocInfo jsdoc = parseFileOverview("@fileoverview Pie is \n * good! */"); + assertEquals("Pie is\n good!", jsdoc.getFileOverview()); + } + + public void testFileOverviewDuplicate() throws Exception { + JSDocInfo jsdoc = parseFileOverview( + "@fileoverview Pie \n * @fileoverview Cake */", + "extra @fileoverview tag"); + } + + public void testReferences() throws Exception { + JSDocInfo jsdoc + = parse("@see A cool place!" + + "\n* @see The world." + + "\n* @see SomeClass#SomeMember" + + "\n* @see A boring test case*/" + , true); + + Collection references = jsdoc.getReferences(); + + assertTrue(references != null); + assertTrue(references.size() == 4); + + assertContains(references, "A cool place!"); + assertContains(references, "The world."); + assertContains(references, "SomeClass#SomeMember"); + assertContains(references, "A boring test case"); + } + + public void testSingleTags() throws Exception { + JSDocInfo jsdoc + = parse("@version Some old version" + + "\n* @deprecated In favor of the new one!" + + "\n* @return {SomeType} The most important object :-)*/" + , true); + + assertTrue(jsdoc.isDeprecated()); + assertEquals("In favor of the new one!", jsdoc.getDeprecationReason()); + assertEquals("Some old version", jsdoc.getVersion()); + assertEquals("The most important object :-)", jsdoc.getReturnDescription()); + } + + public void testSingleTagsReordered() throws Exception { + JSDocInfo jsdoc + = parse("@deprecated In favor of the new one!" + + "\n * @return {SomeType} The most important object :-)" + + "\n * @version Some old version*/" + , true); + + assertTrue(jsdoc.isDeprecated()); + assertEquals("In favor of the new one!", jsdoc.getDeprecationReason()); + assertEquals("Some old version", jsdoc.getVersion()); + assertEquals("The most important object :-)", jsdoc.getReturnDescription()); + } + + public void testVersionDuplication() throws Exception { + parse("* @version Some old version" + + "\n* @version Another version*/", true, + "conflicting @version tag"); + } + + public void testVersionMissing() throws Exception { + parse("* @version */", true, + "@version tag missing version information"); + } + + public void testAuthorMissing() throws Exception { + parse("* @author */", true, + "@author tag missing author"); + } + + public void testSeeMissing() throws Exception { + parse("* @see */", true, + "@see tag missing description"); + } + + public void testSourceName() throws Exception { + JSDocInfo jsdoc = parse("@deprecated */", true); + assertEquals("testcode", jsdoc.getAssociatedNode().getSourceFileName()); + } + + public void testParseBlockComment() throws Exception { + JSDocInfo jsdoc = parse("this is a nice comment\n " + + "* that is multiline \n" + + "* @author abc@google.com */", true); + + assertEquals("this is a nice comment\nthat is multiline", + jsdoc.getBlockDescription()); + + assertDocumentationInMarker( + assertAnnotationMarker(jsdoc, "author", 2, 2), + "abc@google.com", 9, 2, 23); + } + + public void testParseBlockComment2() throws Exception { + JSDocInfo jsdoc = parse("this is a nice comment\n " + + "* that is *** multiline \n" + + "* @author abc@google.com */", true); + + assertEquals("this is a nice comment\nthat is *** multiline", + jsdoc.getBlockDescription()); + + assertDocumentationInMarker( + assertAnnotationMarker(jsdoc, "author", 2, 2), + "abc@google.com", 9, 2, 23); + } + + public void testParseBlockComment3() throws Exception { + JSDocInfo jsdoc = parse("\n " + + "* hello world \n" + + "* @author abc@google.com */", true); + + assertEquals("hello world", jsdoc.getBlockDescription()); + + assertDocumentationInMarker( + assertAnnotationMarker(jsdoc, "author", 2, 2), + "abc@google.com", 9, 2, 23); + } + + public void testParseWithMarkers1() throws Exception { + JSDocInfo jsdoc = parse("@author abc@google.com */", true); + + assertDocumentationInMarker( + assertAnnotationMarker(jsdoc, "author", 0, 0), + "abc@google.com", 7, 0, 21); + } + + public void testParseWithMarkers2() throws Exception { + JSDocInfo jsdoc = parse("@param {Foo} somename abc@google.com */", true); + + assertDocumentationInMarker( + assertAnnotationMarker(jsdoc, "param", 0, 0), + "abc@google.com", 21, 0, 37); + } + + public void testParseWithMarkers3() throws Exception { + JSDocInfo jsdoc = + parse("@return {Foo} some long \n * multiline" + + " \n * description */", true); + + JSDocInfo.Marker returnDoc = + assertAnnotationMarker(jsdoc, "return", 0, 0); + assertDocumentationInMarker(returnDoc, + "some long multiline description", 13, 2, 15); + assertEquals(8, returnDoc.getType().getPositionOnStartLine()); + assertEquals(12, returnDoc.getType().getPositionOnEndLine()); + } + + public void testParseWithMarkers4() throws Exception { + JSDocInfo jsdoc = + parse("@author foobar \n * @param {Foo} somename abc@google.com */", + true); + + assertAnnotationMarker(jsdoc, "author", 0, 0); + assertAnnotationMarker(jsdoc, "param", 1, 3); + } + + public void testParseWithMarkers5() throws Exception { + JSDocInfo jsdoc = + parse("@return some long \n * multiline" + + " \n * description */", true); + + assertDocumentationInMarker( + assertAnnotationMarker(jsdoc, "return", 0, 0), + "some long multiline description", 8, 2, 15); + } + + public void testParseWithMarkers6() throws Exception { + JSDocInfo jsdoc = + parse("@param x some long \n * multiline" + + " \n * description */", true); + + assertDocumentationInMarker( + assertAnnotationMarker(jsdoc, "param", 0, 0), + "some long multiline description", 8, 2, 15); + } + + public void testParseWithMarkerNames1() throws Exception { + JSDocInfo jsdoc = parse("@param {SomeType} name somedescription */", true); + + assertNameInMarker( + assertAnnotationMarker(jsdoc, "param", 0, 0), + "name", 0, 18); + } + + public void testParseWithMarkerNames2() throws Exception { + JSDocInfo jsdoc = parse("@param {SomeType} name somedescription \n" + + "* @param {AnotherType} anothername des */", true); + + assertTypeInMarker( + assertNameInMarker( + assertAnnotationMarker(jsdoc, "param", 0, 0, 0), + "name", 0, 18), + "SomeType", 0, 7, 0, 16, true); + + assertTypeInMarker( + assertNameInMarker( + assertAnnotationMarker(jsdoc, "param", 1, 2, 1), + "anothername", 1, 23), + "AnotherType", 1, 9, 1, 21, true); + } + + public void testParseWithMarkerNames3() throws Exception { + JSDocInfo jsdoc = parse( + "@param {Some.Long.Type.\n * Name} name somedescription */", true); + + assertTypeInMarker( + assertNameInMarker( + assertAnnotationMarker(jsdoc, "param", 0, 0, 0), + "name", 1, 10), + "Some.Long.Type.Name", 0, 7, 1, 8, true); + } + + @SuppressWarnings("deprecation") + public void testParseWithoutMarkerName() throws Exception { + JSDocInfo jsdoc = parse("@author helloworld*/", true); + assertNull(assertAnnotationMarker(jsdoc, "author", 0, 0).getName()); + } + + public void testParseWithMarkerType() throws Exception { + JSDocInfo jsdoc = parse("@extends {FooBar}*/", true); + + assertTypeInMarker( + assertAnnotationMarker(jsdoc, "extends", 0, 0), + "FooBar", 0, 9, 0, 16, true); + } + + public void testParseWithMarkerType2() throws Exception { + JSDocInfo jsdoc = parse("@extends FooBar*/", true); + + assertTypeInMarker( + assertAnnotationMarker(jsdoc, "extends", 0, 0), + "FooBar", 0, 9, 0, 15, false); + } + + public void testTypeTagConflict1() throws Exception { + parse("@constructor \n * @constructor */", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict2() throws Exception { + parse("@interface \n * @interface */", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict3() throws Exception { + parse("@constructor \n * @interface */", + "Bad type annotation. cannot be both an interface and a constructor"); + } + + public void testTypeTagConflict4() throws Exception { + parse("@interface \n * @constructor */", + "Bad type annotation. cannot be both an interface and a constructor"); + } + + public void testTypeTagConflict5() throws Exception { + parse("@interface \n * @type {string} */", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict6() throws Exception { + parse("@typedef {string} \n * @type {string} */", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict7() throws Exception { + parse("@typedef {string} \n * @constructor */", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict8() throws Exception { + parse("@typedef {string} \n * @return {boolean} */", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict9() throws Exception { + parse("@enum {string} \n * @return {boolean} */", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict10() throws Exception { + parse("@this {Object} \n * @enum {boolean} */", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict11() throws Exception { + parse("@param {Object} x \n * @type {boolean} */", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict12() throws Exception { + parse("@typedef {boolean} \n * @param {Object} x */", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict13() throws Exception { + parse("@typedef {boolean} \n * @extends {Object} */", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict14() throws Exception { + parse("@return x \n * @return y */", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict15() throws Exception { + parse("/**\n" + + " * @struct\n" + + " * @struct\n" + + " */\n" + + "function StrStr() {}", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict16() throws Exception { + parse("/**\n" + + " * @struct\n" + + " * @interface\n" + + " */\n" + + "function StrIntf() {}", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict17() throws Exception { + parse("/**\n" + + " * @interface\n" + + " * @struct\n" + + " */\n" + + "function StrIntf() {}", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict18() throws Exception { + parse("/**\n" + + " * @dict\n" + + " * @dict\n" + + " */\n" + + "function DictDict() {}", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict19() throws Exception { + parse("/**\n" + + " * @dict\n" + + " * @interface\n" + + " */\n" + + "function DictDict() {}", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict20() throws Exception { + parse("/**\n" + + " * @interface\n" + + " * @dict\n" + + " */\n" + + "function DictDict() {}", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict21() throws Exception { + parse("/**\n" + + " * @private {string}\n" + + " * @type {number}\n" + + " */\n" + + "function DictDict() {}", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict22() throws Exception { + parse("/**\n" + + " * @protected {string}\n" + + " * @param {string} x\n" + + " */\n" + + "function DictDict(x) {}", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict23() throws Exception { + parse("/**\n" + + " * @public {string}\n" + + " * @return {string} x\n" + + " */\n" + + "function DictDict() {}", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testTypeTagConflict24() throws Exception { + parse("/**\n" + + " * @const {string}\n" + + " * @return {string} x\n" + + " */\n" + + "function DictDict() {}", + "Bad type annotation. " + + "type annotation incompatible with other annotations"); + } + + public void testPrivateType() throws Exception { + JSDocInfo jsdoc = parse("@private {string} */"); + assertTypeEquals(STRING_TYPE, jsdoc.getType()); + } + + public void testProtectedType() throws Exception { + JSDocInfo jsdoc = parse("@protected {string} */"); + assertTypeEquals(STRING_TYPE, jsdoc.getType()); + } + + public void testPublicType() throws Exception { + JSDocInfo jsdoc = parse("@public {string} */"); + assertTypeEquals(STRING_TYPE, jsdoc.getType()); + } + + public void testConstType() throws Exception { + JSDocInfo jsdoc = parse("@const {string} */"); + assertTypeEquals(STRING_TYPE, jsdoc.getType()); + } + + public void testStableIdGeneratorConflict() throws Exception { + parse("/**\n" + + " * @stableIdGenerator\n" + + " * @stableIdGenerator\n" + + " */\n" + + "function getId() {}", + "extra @stableIdGenerator tag"); + } + + public void testParserWithTemplateTypeNameMissing() { + parse("@template */", + "Bad type annotation. @template tag missing type name"); + } + + public void testParserWithTemplateDuplicated() { + parse("@template T\n@template V */", + "Bad type annotation. @template tag at most once"); + } + + public void testParserWithTwoTemplates() { + parse("@template T,V */"); + } + + public void testWhitelistedNewAnnotations() { + parse("@foobar */", + "illegal use of unknown JSDoc tag \"foobar\"; ignoring it"); + extraAnnotations.add("foobar"); + parse("@foobar */"); + } + + public void testWhitelistedConflictingAnnotation() { + extraAnnotations.add("param"); + JSDocInfo info = parse("@param {number} index */"); + assertTypeEquals(NUMBER_TYPE, info.getParameterType("index")); + } + + public void testNonIdentifierAnnotation() { + // Try to whitelist an annotation that is not a valid JS identifier. + // It should not work. + extraAnnotations.add("123"); + parse("@123 */", "illegal use of unknown JSDoc tag \"\"; ignoring it"); + } + + public void testUnsupportedJsDocSyntax1() { + JSDocInfo info = + parse("@param {string} [accessLevel=\"author\"] The user level */", + true); + assertEquals(1, info.getParameterCount()); + assertTypeEquals( + registry.createOptionalType(STRING_TYPE), + info.getParameterType("accessLevel")); + assertEquals("The user level", + info.getDescriptionForParameter("accessLevel")); + } + + public void testUnsupportedJsDocSyntax2() { + JSDocInfo info = + parse("@param userInfo The user info. \n" + + " * @param userInfo.name The name of the user */", true); + assertEquals(1, info.getParameterCount()); + assertEquals("The user info.", + info.getDescriptionForParameter("userInfo")); + } + + public void testWhitelistedAnnotations() { + parse( + "* @addon \n" + + "* @augments \n" + + "* @base \n" + + "* @borrows \n" + + "* @bug \n" + + "* @class \n" + + "* @config \n" + + "* @constructs \n" + + "* @default \n" + + "* @description \n" + + "* @event \n" + + "* @example \n" + + "* @exception \n" + + "* @exec \n" + + "* @externs \n" + + "* @field \n" + + "* @function \n" + + "* @id \n" + + "* @ignore \n" + + "* @inner \n" + + "* @lends {string} \n" + + "* @link \n" + + "* @member \n" + + "* @memberOf \n" + + "* @modName \n" + + "* @mods \n" + + "* @name \n" + + "* @namespace \n" + + "* @nocompile \n" + + "* @property \n" + + "* @requires \n" + + "* @since \n" + + "* @static \n" + + "* @supported */"); + } + + public void testGetOriginalCommentString() throws Exception { + String comment = "* @desc This is a comment */"; + JSDocInfo info = parse(comment); + assertNull(info.getOriginalCommentString()); + info = parse(comment, true /* parseDocumentation */); + assertEquals(comment, info.getOriginalCommentString()); + } + + /** + * Asserts that a documentation field exists on the given marker. + * + * @param description The text of the documentation field expected. + * @param startCharno The starting character of the text. + * @param endLineno The ending line of the text. + * @param endCharno The ending character of the text. + * @return The marker, for chaining purposes. + */ + private JSDocInfo.Marker assertDocumentationInMarker(JSDocInfo.Marker marker, + String description, + int startCharno, + int endLineno, + int endCharno) { + assertTrue(marker.getDescription() != null); + assertEquals(description, marker.getDescription().getItem()); + + // Match positional information. + assertEquals(marker.getAnnotation().getStartLine(), + marker.getDescription().getStartLine()); + assertEquals(startCharno, marker.getDescription().getPositionOnStartLine()); + assertEquals(endLineno, marker.getDescription().getEndLine()); + assertEquals(endCharno, marker.getDescription().getPositionOnEndLine()); + + return marker; + } + + /** + * Asserts that a type field exists on the given marker. + * + * @param typeName The name of the type expected in the type field. + * @param startCharno The starting character of the type declaration. + * @param hasBrackets Whether the type in the type field is expected + * to have brackets. + * @return The marker, for chaining purposes. + */ + private JSDocInfo.Marker assertTypeInMarker( + JSDocInfo.Marker marker, String typeName, + int startLineno, int startCharno, int endLineno, int endCharno, + boolean hasBrackets) { + + assertTrue(marker.getType() != null); + assertTrue(marker.getType().getItem().isString()); + + // Match the name and brackets information. + String foundName = marker.getType().getItem().getString(); + + assertEquals(typeName, foundName); + assertEquals(hasBrackets, marker.getType().hasBrackets()); + + // Match position information. + assertEquals(startCharno, marker.getType().getPositionOnStartLine()); + assertEquals(endCharno, marker.getType().getPositionOnEndLine()); + assertEquals(startLineno, marker.getType().getStartLine()); + assertEquals(endLineno, marker.getType().getEndLine()); + + return marker; + } + + /** + * Asserts that a name field exists on the given marker. + * + * @param name The name expected in the name field. + * @param startCharno The starting character of the text. + * @return The marker, for chaining purposes. + */ + @SuppressWarnings("deprecation") + private JSDocInfo.Marker assertNameInMarker(JSDocInfo.Marker marker, + String name, int startLine, int startCharno) { + assertTrue(marker.getName() != null); + assertEquals(name, marker.getName().getItem()); + + assertEquals(startCharno, marker.getName().getPositionOnStartLine()); + assertEquals(startCharno + name.length(), + marker.getName().getPositionOnEndLine()); + + assertEquals(startLine, marker.getName().getStartLine()); + assertEquals(startLine, marker.getName().getEndLine()); + + return marker; + } + + /** + * Asserts that an annotation marker of a given annotation name + * is found in the given JSDocInfo. + * + * @param jsdoc The JSDocInfo in which to search for the annotation marker. + * @param annotationName The name/type of the annotation for which to + * search. Example: "author" for an "@author" annotation. + * @param startLineno The expected starting line number of the marker. + * @param startCharno The expected character on the starting line. + * @return The marker found, for further testing. + */ + private JSDocInfo.Marker assertAnnotationMarker(JSDocInfo jsdoc, + String annotationName, + int startLineno, + int startCharno) { + return assertAnnotationMarker(jsdoc, annotationName, startLineno, + startCharno, 0); + } + + /** + * Asserts that the index-th annotation marker of a given annotation name + * is found in the given JSDocInfo. + * + * @param jsdoc The JSDocInfo in which to search for the annotation marker. + * @param annotationName The name/type of the annotation for which to + * search. Example: "author" for an "@author" annotation. + * @param startLineno The expected starting line number of the marker. + * @param startCharno The expected character on the starting line. + * @param index The index of the marker. + * @return The marker found, for further testing. + */ + private JSDocInfo.Marker assertAnnotationMarker(JSDocInfo jsdoc, + String annotationName, + int startLineno, + int startCharno, + int index) { + + Collection markers = jsdoc.getMarkers(); + + assertTrue(markers.size() > 0); + + int counter = 0; + + for (JSDocInfo.Marker marker : markers) { + if (marker.getAnnotation() != null) { + if (annotationName.equals(marker.getAnnotation().getItem())) { + + if (counter == index) { + assertEquals(startLineno, marker.getAnnotation().getStartLine()); + assertEquals(startCharno, + marker.getAnnotation().getPositionOnStartLine()); + assertEquals(startLineno, marker.getAnnotation().getEndLine()); + assertEquals(startCharno + annotationName.length(), + marker.getAnnotation().getPositionOnEndLine()); + + return marker; + } + + counter++; + } + } + } + + fail("No marker found"); + return null; + } + + private void assertContains(Collection collection, T item) { + assertTrue(collection.contains(item)); + } + + private void parseFull(String code, String... warnings) { + CompilerEnvirons environment = new CompilerEnvirons(); + + TestErrorReporter testErrorReporter = new TestErrorReporter(null, warnings); + environment.setErrorReporter(testErrorReporter); + + environment.setRecordingComments(true); + environment.setRecordingLocalJsDocComments(true); + + Parser p = new Parser(environment, testErrorReporter); + AstRoot script = p.parse(code, null, 0); + + Config config = + new Config(extraAnnotations, extraSuppressions, + true, LanguageMode.ECMASCRIPT3, false); + StaticSourceFile file = new SimpleSourceFile(script.getSourceName(), false); + for (Comment comment : script.getComments()) { + JsDocInfoParser jsdocParser = + new JsDocInfoParser( + new JsDocTokenStream(comment.getValue().substring(3), + comment.getLineno()), + comment, + null, + config, + testErrorReporter); + jsdocParser.parse(); + jsdocParser.retrieveAndResetParsedJSDocInfo(); + } + + assertTrue("some expected warnings were not reported", + testErrorReporter.hasEncounteredAllWarnings()); + } + + @SuppressWarnings("unused") + private JSDocInfo parseFileOverviewWithoutDoc(String comment, + String... warnings) { + return parse(comment, false, true, warnings); + } + + private JSDocInfo parseFileOverview(String comment, String... warnings) { + return parse(comment, true, true, warnings); + } + + private JSDocInfo parse(String comment, String... warnings) { + return parse(comment, false, warnings); + } + + private JSDocInfo parse(String comment, boolean parseDocumentation, + String... warnings) { + return parse(comment, parseDocumentation, false, warnings); + } + + private JSDocInfo parse(String comment, boolean parseDocumentation, + boolean parseFileOverview, String... warnings) { + TestErrorReporter errorReporter = new TestErrorReporter(null, warnings); + + Config config = new Config(extraAnnotations, extraSuppressions, + parseDocumentation, LanguageMode.ECMASCRIPT3, false); + StaticSourceFile file = new SimpleSourceFile("testcode", false); + Node associatedNode = new Node(Token.SCRIPT); + associatedNode.setInputId(new InputId(file.getName())); + associatedNode.setStaticSourceFile(file); + JsDocInfoParser jsdocParser = new JsDocInfoParser( + stream(comment), + new Comment(0, 0, CommentType.JSDOC, comment), + associatedNode, + config, errorReporter); + + if (fileLevelJsDocBuilder != null) { + jsdocParser.setFileLevelJsDocBuilder(fileLevelJsDocBuilder); + } + + jsdocParser.parse(); + + assertTrue("expected warnings were not reported", + errorReporter.hasEncounteredAllWarnings()); + + if (parseFileOverview) { + return jsdocParser.getFileOverviewJSDocInfo(); + } else { + return jsdocParser.retrieveAndResetParsedJSDocInfo(); + } + } + + private Node parseType(String typeComment) { + return JsDocInfoParser.parseTypeString(typeComment); + } + + private JsDocTokenStream stream(String source) { + return new JsDocTokenStream(source, 0); + } + + private void assertParameterTypeEquals(JSType expected, JSTypeExpression te) { + assertEquals(expected, ((ObjectType) resolve(te)).getParameterType()); + } + + private void assertIndexTypeEquals(JSType expected, JSTypeExpression te) { + assertEquals(expected, ((ObjectType) resolve(te)).getIndexType()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/parsing/JsDocTokenStreamTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/parsing/JsDocTokenStreamTest.java new file mode 100644 index 0000000..b36ee5b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/parsing/JsDocTokenStreamTest.java @@ -0,0 +1,304 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.parsing; + +import static com.google.javascript.jscomp.parsing.JsDocToken.ANNOTATION; +import static com.google.javascript.jscomp.parsing.JsDocToken.BANG; +import static com.google.javascript.jscomp.parsing.JsDocToken.COLON; +import static com.google.javascript.jscomp.parsing.JsDocToken.COMMA; +import static com.google.javascript.jscomp.parsing.JsDocToken.ELLIPSIS; +import static com.google.javascript.jscomp.parsing.JsDocToken.EOC; +import static com.google.javascript.jscomp.parsing.JsDocToken.EOF; +import static com.google.javascript.jscomp.parsing.JsDocToken.EOL; +import static com.google.javascript.jscomp.parsing.JsDocToken.EQUALS; +import static com.google.javascript.jscomp.parsing.JsDocToken.GT; +import static com.google.javascript.jscomp.parsing.JsDocToken.LB; +import static com.google.javascript.jscomp.parsing.JsDocToken.LC; +import static com.google.javascript.jscomp.parsing.JsDocToken.LP; +import static com.google.javascript.jscomp.parsing.JsDocToken.LT; +import static com.google.javascript.jscomp.parsing.JsDocToken.PIPE; +import static com.google.javascript.jscomp.parsing.JsDocToken.QMARK; +import static com.google.javascript.jscomp.parsing.JsDocToken.RB; +import static com.google.javascript.jscomp.parsing.JsDocToken.RC; +import static com.google.javascript.jscomp.parsing.JsDocToken.RP; +import static com.google.javascript.jscomp.parsing.JsDocToken.STAR; +import static com.google.javascript.jscomp.parsing.JsDocToken.STRING; + +import com.google.common.collect.ImmutableList; + +import junit.framework.TestCase; + +import java.util.List; + +/** + * Tests for {@link JsDocTokenStream}. + */ +public class JsDocTokenStreamTest extends TestCase { + + public void testJsDocTokenization1() throws Exception { + List tokens = ImmutableList.of( + STAR, ANNOTATION, LC, STRING, RC, EOL, STAR, ANNOTATION); + List strings = ImmutableList.of("type", "string", "private"); + testJSDocTokenStream(" * @type {string}\n * @private", tokens, strings); + testJSDocTokenStream(" * @type { string } \n * @private", + tokens, strings); + testJSDocTokenStream(" * @type { string}\n * @private", tokens, strings); + testJSDocTokenStream(" * @type {string }\n * @private", tokens, strings); + testJSDocTokenStream(" * @type {string}\n * @private", tokens, strings); + testJSDocTokenStream(" * @type {string} \n * @private", tokens, strings); + } + + public void testJsDocTokenization2() throws Exception { + List tokens = ImmutableList.of( + ANNOTATION, LC, STRING, LT, STRING, PIPE, STRING, GT, RC); + List strings = ImmutableList.of("param", "Array", "string", "null"); + testJSDocTokenStream("@param {Array.}", tokens, strings); + testJSDocTokenStream("@param {Array.}", tokens, strings); + testJSDocTokenStream("@param {Array.}", tokens, strings); + testJSDocTokenStream(" @param {Array.}", tokens, strings); + testJSDocTokenStream(" @param {Array.}", tokens, strings); + testJSDocTokenStream("@param {Array .}", tokens, strings); + testJSDocTokenStream("@param {Array.}", tokens, strings); + testJSDocTokenStream("@param { Array.}", tokens, strings); + testJSDocTokenStream("@param {Array.} ", tokens, strings); + testJSDocTokenStream("@param {Array.}", tokens, strings); + testJSDocTokenStream(" @param { Array .< string |null > } ", + tokens, strings); + } + + public void testJsDocTokenization3() throws Exception { + List tokens = ImmutableList.of( + ANNOTATION, LC, STRING, LT, STRING, PIPE, STRING, GT, RC); + List strings = ImmutableList.of("param", "Array", "string", "null"); + testJSDocTokenStream("@param {Array.}", tokens, strings); + testJSDocTokenStream("@param {Array.< string || null> }", tokens, strings); + testJSDocTokenStream("@param {Array. } ", + tokens, strings); + testJSDocTokenStream("@param {Array .}", tokens, strings); + testJSDocTokenStream("@param {Array.< string||null>}", tokens, strings); + testJSDocTokenStream("@param { Array.}", tokens, strings); + testJSDocTokenStream(" @param {Array.}", tokens, strings); + testJSDocTokenStream("@param { Array. }", + tokens, strings); + } + + public void testJsDocTokenization4() throws Exception { + List tokens = ImmutableList.of( + ANNOTATION, LC, STRING, LT, LP, STRING, COMMA, STRING, RP, GT, RC, EOF); + List strings = ImmutableList.of("param", "Array", "string", "null"); + testJSDocTokenStream("@param {Array.<(string,null)>}", tokens, strings); + testJSDocTokenStream("@param {Array .<(string,null)> } ", tokens, strings); + testJSDocTokenStream(" @param {Array.< ( string,null)>}", + tokens, strings); + testJSDocTokenStream("@param {Array.<(string , null)>}", tokens, strings); + testJSDocTokenStream("@param {Array.<(string, null) > } ", + tokens, strings); + testJSDocTokenStream("@param { Array .< (string,null)>} ", + tokens, strings); + } + + public void testJsDocTokenization5() throws Exception { + List tokens = ImmutableList.of(ANNOTATION, STRING, EOC, EOF); + List strings = ImmutableList.of("param", "foo.Bar"); + testJSDocTokenStream("@param foo.Bar*/", tokens, strings); + testJSDocTokenStream(" @param foo.Bar*/", tokens, strings); + testJSDocTokenStream(" @param foo.Bar */", tokens, strings); + } + + public void testJsDocTokenization6() throws Exception { + List tokens = ImmutableList.of( + ANNOTATION, EOL, ANNOTATION, EOL, ANNOTATION, EOC); + List strings = ImmutableList.of("hidden", "static", "desc"); + testJSDocTokenStream("@hidden\n@static\n@desc*/", tokens, strings); + testJSDocTokenStream("@hidden\n @static\n@desc*/", tokens, strings); + testJSDocTokenStream("@hidden\n@static\n @desc*/", tokens, strings); + testJSDocTokenStream("@hidden\n@static\n@desc */", tokens, strings); + testJSDocTokenStream(" @hidden \n@static\n @desc*/", tokens, strings); + testJSDocTokenStream("@hidden\n@static \n @desc */", tokens, strings); + testJSDocTokenStream("@hidden\n@static\n@desc*/", tokens, strings); + testJSDocTokenStream("@hidden \n@static \n @desc*/", tokens, strings); + } + + public void testJsDocTokenization7() throws Exception { + List tokens = ImmutableList.of( + ELLIPSIS, ELLIPSIS, ELLIPSIS, ELLIPSIS, ELLIPSIS, LT, EOC); + List strings = ImmutableList.of(); + + testJSDocTokenStream("................<*/", tokens, strings); + testJSDocTokenStream("............... .<*/", tokens, strings); + testJSDocTokenStream("................< */", tokens, strings); + testJSDocTokenStream("............... .< */", tokens, strings); + testJSDocTokenStream("............... .< */ ", tokens, strings); + testJSDocTokenStream(" ............... .< */ ", tokens, strings); + } + + public void testJsDocTokenization8() throws Exception { + List tokens = ImmutableList.of( + STAR, ANNOTATION, STRING, STRING, STRING, STRING, STRING, STRING, + STRING, EOL, EOC); + List strings = ImmutableList.of( + "param", "foo.Bar", "opt_name", "this", "parameter", "is", "a", "name"); + testJSDocTokenStream( + " * @param foo.Bar opt_name this parameter is a name\n" + + " */", tokens, strings); + testJSDocTokenStream( + " * @param foo.Bar opt_name this parameter is a name \n" + + " */ ", tokens, strings); + } + + public void testJsDocTokenization9() throws Exception { + List tokens = ImmutableList.of( + STAR, ANNOTATION, STRING, STRING, STRING, STRING, STRING, ANNOTATION, + STRING, EOL, EOC); + List strings = ImmutableList.of( + "param", "foo.Bar", "opt_name", "this", "parameter", "does", + "media", "blah"); + testJSDocTokenStream( + " * @param foo.Bar opt_name this parameter does @media blah\n" + + " */", tokens, strings); + } + + public void testJsDocTokenization10() throws Exception { + List tokens = ImmutableList.of(STRING, GT, EOC); + List strings = ImmutableList.of("Array*/", tokens, strings); + } + + public void testJsDocTokenization11() throws Exception { + List tokens = ImmutableList.of( + ANNOTATION, LC, STRING, QMARK, RC, EOC, EOF); + List strings = ImmutableList.of("param", "string"); + testJSDocTokenStream("@param {string?}*/", tokens, strings); + testJSDocTokenStream(" @param {string?}*/", tokens, strings); + testJSDocTokenStream("@param { string?}*/", tokens, strings); + testJSDocTokenStream("@param {string ?}*/", tokens, strings); + testJSDocTokenStream("@param {string ? } */", tokens, strings); + testJSDocTokenStream("@param { string ? }*/", tokens, strings); + testJSDocTokenStream("@param {string? }*/", tokens, strings); + } + + public void testJsDocTokenization12() throws Exception { + List tokens = ImmutableList.of(STRING, ELLIPSIS, EOC); + List strings = ImmutableList.of("function"); + + testJSDocTokenStream("function ...*/", tokens, strings); + } + + public void testJsDocTokenization13() throws Exception { + List tokens = ImmutableList.of(ELLIPSIS, LB, STRING, RB, EOC); + List strings = ImmutableList.of("number"); + + testJSDocTokenStream("...[number]*/", tokens, strings); + } + + public void testJsDocTokenization14() throws Exception { + // Since ES4 type parsing only requires to parse an ellipsis when it is + // followed by a comma (,) we are allowing this case to parse this way. + // This is a simplification of the tokenizer, but the extra complexity is + // never used. + List tokens = ImmutableList.of(STRING, LB, STRING, EOC); + List strings = ImmutableList.of("foo", "bar..."); + + testJSDocTokenStream("foo[ bar...*/", tokens, strings); + } + + public void testJsDocTokenization15() throws Exception { + List tokens = ImmutableList.of( + STRING, LB, STRING, COMMA, ELLIPSIS, EOC); + List strings = ImmutableList.of("foo", "bar"); + + testJSDocTokenStream("foo[ bar,...*/", tokens, strings); + testJSDocTokenStream("foo[ bar ,...*/", tokens, strings); + testJSDocTokenStream("foo[bar, ...*/", tokens, strings); + testJSDocTokenStream("foo[ bar , ... */", tokens, strings); + testJSDocTokenStream("foo [bar,... */", tokens, strings); + } + + public void testJsDocTokenization16() throws Exception { + List tokens = ImmutableList.of( + STRING, COLON, COLON, COLON, ELLIPSIS, STRING, COLON, STRING, EOC); + List strings = ImmutableList.of("foo", "bar", "bar2"); + + testJSDocTokenStream("foo:::...bar:bar2*/", tokens, strings); + } + + public void testJsDocTokenization17() throws Exception { + List tokens = ImmutableList.of(STRING, EOL, EOC); + List strings = ImmutableList.of(".."); + + testJSDocTokenStream("..\n*/", tokens, strings); + } + + public void testJsDocTokenization18() throws Exception { + List tokens = ImmutableList.of(STRING, EOL, EOC); + List strings = ImmutableList.of("."); + + testJSDocTokenStream(".\n*/", tokens, strings); + } + + public void testJsDocTokenization19() throws Exception { + List tokens = ImmutableList.of(ANNOTATION, LC, STAR, RC, EOC); + List strings = ImmutableList.of("type", "*"); + + testJSDocTokenStream("@type {*}*/", tokens, strings); + } + + public void testJsDocTokenization20() throws Exception { + List tokens = ImmutableList.of( + ANNOTATION, LC, BANG, STRING, RC, EOC, EOF); + List strings = ImmutableList.of("param", "Object"); + testJSDocTokenStream("@param {!Object}*/", tokens, strings); + testJSDocTokenStream(" @param {!Object}*/", tokens, strings); + testJSDocTokenStream("@param {! Object}*/", tokens, strings); + testJSDocTokenStream("@param { !Object}*/", tokens, strings); + testJSDocTokenStream("@param {!Object } */", tokens, strings); + testJSDocTokenStream("@param { ! Object }*/", tokens, strings); + testJSDocTokenStream("@param {!Object }*/", tokens, strings); + } + + public void testJsDocTokenization21() throws Exception { + List tokens = ImmutableList.of( + ANNOTATION, LC, STRING, EQUALS, RC, EOC, EOF); + List strings = ImmutableList.of("param", "Object"); + testJSDocTokenStream("@param {Object=}*/", tokens, strings); + testJSDocTokenStream(" @param {Object=}*/", tokens, strings); + testJSDocTokenStream("@param { Object =}*/", tokens, strings); + testJSDocTokenStream("@param { Object=}*/", tokens, strings); + testJSDocTokenStream("@param {Object= } */", tokens, strings); + testJSDocTokenStream("@param { Object = }*/", tokens, strings); + testJSDocTokenStream("@param {Object= }*/", tokens, strings); + } + + private void testJSDocTokenStream(String comment, List tokens, + List strings) { + JsDocTokenStream stream = new JsDocTokenStream(comment, 0); + int stringsIndex = 0; + for (JsDocToken token : tokens) { + JsDocToken readToken = stream.getJsDocToken(); + + // token equality + if (token != readToken) { + assertEquals(token, readToken); + } + + // string equality + if (token == ANNOTATION || token == STRING) { + assertEquals(strings.get(stringsIndex++), stream.getString()); + } + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/parsing/ParserTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/parsing/ParserTest.java new file mode 100644 index 0000000..ecad6ae --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/parsing/ParserTest.java @@ -0,0 +1,1095 @@ +/* + * Copyright 2007 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.parsing; + +import com.google.common.collect.ImmutableList; +import com.google.javascript.jscomp.parsing.Config.LanguageMode; +import com.google.javascript.jscomp.testing.TestErrorReporter; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.head.ScriptRuntime; +import com.google.javascript.rhino.jstype.SimpleSourceFile; +import com.google.javascript.rhino.jstype.StaticSourceFile; +import com.google.javascript.rhino.testing.BaseJSTypeTestCase; + +import java.io.IOException; +import java.util.List; +import java.util.logging.Logger; + +public class ParserTest extends BaseJSTypeTestCase { + private static final String SUSPICIOUS_COMMENT_WARNING = + IRFactory.SUSPICIOUS_COMMENT_WARNING; + + private static final String TRAILING_COMMA_MESSAGE = + ScriptRuntime.getMessage0("msg.extra.trailing.comma"); + + private static final String BAD_PROPERTY_MESSAGE = + ScriptRuntime.getMessage0("msg.bad.prop"); + + private static final String MISSING_GT_MESSAGE = + "Bad type annotation. " + + com.google.javascript.rhino.ScriptRuntime.getMessage0( + "msg.jsdoc.missing.gt"); + + private static final String MISPLACED_TYPE_ANNOTATION = + IRFactory.MISPLACED_TYPE_ANNOTATION; + + private Config.LanguageMode mode; + private boolean isIdeMode = false; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mode = LanguageMode.ECMASCRIPT3; + isIdeMode = false; + } + + public void testLinenoCharnoAssign1() throws Exception { + Node assign = parse("a = b").getFirstChild().getFirstChild(); + + assertEquals(Token.ASSIGN, assign.getType()); + assertEquals(1, assign.getLineno()); + assertEquals(0, assign.getCharno()); + } + + public void testLinenoCharnoAssign2() throws Exception { + Node assign = parse("\n a.g.h.k = 45").getFirstChild().getFirstChild(); + + assertEquals(Token.ASSIGN, assign.getType()); + assertEquals(2, assign.getLineno()); + assertEquals(1, assign.getCharno()); + } + + public void testLinenoCharnoCall() throws Exception { + Node call = parse("\n foo(123);").getFirstChild().getFirstChild(); + + assertEquals(Token.CALL, call.getType()); + assertEquals(2, call.getLineno()); + assertEquals(1, call.getCharno()); + } + + public void testLinenoCharnoGetProp1() throws Exception { + Node getprop = parse("\n foo.bar").getFirstChild().getFirstChild(); + + assertEquals(Token.GETPROP, getprop.getType()); + assertEquals(2, getprop.getLineno()); + assertEquals(1, getprop.getCharno()); + + Node name = getprop.getFirstChild().getNext(); + assertEquals(Token.STRING, name.getType()); + assertEquals(2, name.getLineno()); + assertEquals(5, name.getCharno()); + } + + public void testLinenoCharnoGetProp2() throws Exception { + Node getprop = parse("\n foo.\nbar").getFirstChild().getFirstChild(); + + assertEquals(Token.GETPROP, getprop.getType()); + assertEquals(2, getprop.getLineno()); + assertEquals(1, getprop.getCharno()); + + Node name = getprop.getFirstChild().getNext(); + assertEquals(Token.STRING, name.getType()); + assertEquals(3, name.getLineno()); + assertEquals(0, name.getCharno()); + } + + public void testLinenoCharnoGetelem1() throws Exception { + Node call = parse("\n foo[123]").getFirstChild().getFirstChild(); + + assertEquals(Token.GETELEM, call.getType()); + assertEquals(2, call.getLineno()); + assertEquals(1, call.getCharno()); + } + + public void testLinenoCharnoGetelem2() throws Exception { + Node call = parse("\n \n foo()[123]").getFirstChild().getFirstChild(); + + assertEquals(Token.GETELEM, call.getType()); + assertEquals(3, call.getLineno()); + assertEquals(1, call.getCharno()); + } + + public void testLinenoCharnoGetelem3() throws Exception { + Node call = parse("\n \n (8 + kl)[123]").getFirstChild().getFirstChild(); + + assertEquals(Token.GETELEM, call.getType()); + assertEquals(3, call.getLineno()); + assertEquals(2, call.getCharno()); + } + + public void testLinenoCharnoForComparison() throws Exception { + Node lt = + parse("for (; i < j;){}").getFirstChild().getFirstChild().getNext(); + + assertEquals(Token.LT, lt.getType()); + assertEquals(1, lt.getLineno()); + assertEquals(7, lt.getCharno()); + } + + public void testLinenoCharnoHook() throws Exception { + Node n = parse("\n a ? 9 : 0").getFirstChild().getFirstChild(); + + assertEquals(Token.HOOK, n.getType()); + assertEquals(2, n.getLineno()); + assertEquals(1, n.getCharno()); + } + + public void testLinenoCharnoArrayLiteral() throws Exception { + Node n = parse("\n [8, 9]").getFirstChild().getFirstChild(); + + assertEquals(Token.ARRAYLIT, n.getType()); + assertEquals(2, n.getLineno()); + assertEquals(2, n.getCharno()); + + n = n.getFirstChild(); + + assertEquals(Token.NUMBER, n.getType()); + assertEquals(2, n.getLineno()); + assertEquals(3, n.getCharno()); + + n = n.getNext(); + + assertEquals(Token.NUMBER, n.getType()); + assertEquals(2, n.getLineno()); + assertEquals(6, n.getCharno()); + } + + public void testLinenoCharnoObjectLiteral() throws Exception { + Node n = parse("\n\n var a = {a:0\n,b :1};") + .getFirstChild().getFirstChild().getFirstChild(); + + assertEquals(Token.OBJECTLIT, n.getType()); + assertEquals(3, n.getLineno()); + assertEquals(9, n.getCharno()); + + Node key = n.getFirstChild(); + + assertEquals(Token.STRING_KEY, key.getType()); + assertEquals(3, key.getLineno()); + assertEquals(10, key.getCharno()); + + Node value = key.getFirstChild(); + + assertEquals(Token.NUMBER, value.getType()); + assertEquals(3, value.getLineno()); + assertEquals(12, value.getCharno()); + + key = key.getNext(); + + assertEquals(Token.STRING_KEY, key.getType()); + assertEquals(4, key.getLineno()); + assertEquals(1, key.getCharno()); + + value = key.getFirstChild(); + + assertEquals(Token.NUMBER, value.getType()); + assertEquals(4, value.getLineno()); + assertEquals(4, value.getCharno()); + } + + public void testLinenoCharnoAdd() throws Exception { + testLinenoCharnoBinop("+"); + } + + public void testLinenoCharnoSub() throws Exception { + testLinenoCharnoBinop("-"); + } + + public void testLinenoCharnoMul() throws Exception { + testLinenoCharnoBinop("*"); + } + + public void testLinenoCharnoDiv() throws Exception { + testLinenoCharnoBinop("/"); + } + + public void testLinenoCharnoMod() throws Exception { + testLinenoCharnoBinop("%"); + } + + public void testLinenoCharnoShift() throws Exception { + testLinenoCharnoBinop("<<"); + } + + public void testLinenoCharnoBinaryAnd() throws Exception { + testLinenoCharnoBinop("&"); + } + + public void testLinenoCharnoAnd() throws Exception { + testLinenoCharnoBinop("&&"); + } + + public void testLinenoCharnoBinaryOr() throws Exception { + testLinenoCharnoBinop("|"); + } + + public void testLinenoCharnoOr() throws Exception { + testLinenoCharnoBinop("||"); + } + + public void testLinenoCharnoLt() throws Exception { + testLinenoCharnoBinop("<"); + } + + public void testLinenoCharnoLe() throws Exception { + testLinenoCharnoBinop("<="); + } + + public void testLinenoCharnoGt() throws Exception { + testLinenoCharnoBinop(">"); + } + + public void testLinenoCharnoGe() throws Exception { + testLinenoCharnoBinop(">="); + } + + private void testLinenoCharnoBinop(String binop) { + Node op = parse("var a = 89 " + binop + " 76").getFirstChild(). + getFirstChild().getFirstChild(); + + assertEquals(1, op.getLineno()); + assertEquals(8, op.getCharno()); + } + + public void testJSDocAttachment1() { + Node varNode = parse("/** @type number */var a;").getFirstChild(); + + // VAR + assertEquals(Token.VAR, varNode.getType()); + JSDocInfo info = varNode.getJSDocInfo(); + assertNotNull(info); + assertTypeEquals(NUMBER_TYPE, info.getType()); + + // NAME + Node nameNode = varNode.getFirstChild(); + assertEquals(Token.NAME, nameNode.getType()); + assertNull(nameNode.getJSDocInfo()); + } + + public void testJSDocAttachment2() { + Node varNode = parse("/** @type number */var a,b;").getFirstChild(); + + // VAR + assertEquals(Token.VAR, varNode.getType()); + JSDocInfo info = varNode.getJSDocInfo(); + assertNotNull(info); + assertTypeEquals(NUMBER_TYPE, info.getType()); + + // First NAME + Node nameNode1 = varNode.getFirstChild(); + assertEquals(Token.NAME, nameNode1.getType()); + assertNull(nameNode1.getJSDocInfo()); + + // Second NAME + Node nameNode2 = nameNode1.getNext(); + assertEquals(Token.NAME, nameNode2.getType()); + assertNull(nameNode2.getJSDocInfo()); + } + + public void testJSDocAttachment3() { + Node assignNode = parse( + "/** @type number */goog.FOO = 5;").getFirstChild().getFirstChild(); + + // ASSIGN + assertEquals(Token.ASSIGN, assignNode.getType()); + JSDocInfo info = assignNode.getJSDocInfo(); + assertNotNull(info); + assertTypeEquals(NUMBER_TYPE, info.getType()); + } + + public void testJSDocAttachment4() { + Node varNode = parse( + "var a, /** @define {number} */b = 5;").getFirstChild(); + + // ASSIGN + assertEquals(Token.VAR, varNode.getType()); + assertNull(varNode.getJSDocInfo()); + + // a + Node a = varNode.getFirstChild(); + assertNull(a.getJSDocInfo()); + + // b + Node b = a.getNext(); + JSDocInfo info = b.getJSDocInfo(); + assertNotNull(info); + assertTrue(info.isDefine()); + assertTypeEquals(NUMBER_TYPE, info.getType()); + } + + public void testJSDocAttachment5() { + Node varNode = parse( + "var /** @type number */a, /** @define {number} */b = 5;") + .getFirstChild(); + + // ASSIGN + assertEquals(Token.VAR, varNode.getType()); + assertNull(varNode.getJSDocInfo()); + + // a + Node a = varNode.getFirstChild(); + assertNotNull(a.getJSDocInfo()); + JSDocInfo info = a.getJSDocInfo(); + assertNotNull(info); + assertFalse(info.isDefine()); + assertTypeEquals(NUMBER_TYPE, info.getType()); + + // b + Node b = a.getNext(); + info = b.getJSDocInfo(); + assertNotNull(info); + assertTrue(info.isDefine()); + assertTypeEquals(NUMBER_TYPE, info.getType()); + } + + /** + * Tests that a JSDoc comment in an unexpected place of the code does not + * propagate to following code due to {@link JSDocInfo} aggregation. + */ + public void testJSDocAttachment6() throws Exception { + Node functionNode = parse( + "var a = /** @param {number} index */5;" + + "/** @return boolean */function f(index){}") + .getFirstChild().getNext(); + + assertEquals(Token.FUNCTION, functionNode.getType()); + JSDocInfo info = functionNode.getJSDocInfo(); + assertNotNull(info); + assertFalse(info.hasParameter("index")); + assertTrue(info.hasReturnType()); + assertTypeEquals(UNKNOWN_TYPE, info.getReturnType()); + } + + public void testJSDocAttachment7() { + Node varNode = parse("/** */var a;").getFirstChild(); + + // VAR + assertEquals(Token.VAR, varNode.getType()); + + // NAME + Node nameNode = varNode.getFirstChild(); + assertEquals(Token.NAME, nameNode.getType()); + assertNull(nameNode.getJSDocInfo()); + } + + public void testJSDocAttachment8() { + Node varNode = parse("/** x */var a;").getFirstChild(); + + // VAR + assertEquals(Token.VAR, varNode.getType()); + + // NAME + Node nameNode = varNode.getFirstChild(); + assertEquals(Token.NAME, nameNode.getType()); + assertNull(nameNode.getJSDocInfo()); + } + + public void testJSDocAttachment9() { + Node varNode = parse("/** \n x */var a;").getFirstChild(); + + // VAR + assertEquals(Token.VAR, varNode.getType()); + + // NAME + Node nameNode = varNode.getFirstChild(); + assertEquals(Token.NAME, nameNode.getType()); + assertNull(nameNode.getJSDocInfo()); + } + + public void testJSDocAttachment10() { + Node varNode = parse("/** x\n */var a;").getFirstChild(); + + // VAR + assertEquals(Token.VAR, varNode.getType()); + + // NAME + Node nameNode = varNode.getFirstChild(); + assertEquals(Token.NAME, nameNode.getType()); + assertNull(nameNode.getJSDocInfo()); + } + + public void testJSDocAttachment11() { + Node varNode = + parse("/** @type {{x : number, 'y' : string, z}} */var a;") + .getFirstChild(); + + // VAR + assertEquals(Token.VAR, varNode.getType()); + JSDocInfo info = varNode.getJSDocInfo(); + assertNotNull(info); + + assertTypeEquals(createRecordTypeBuilder(). + addProperty("x", NUMBER_TYPE, null). + addProperty("y", STRING_TYPE, null). + addProperty("z", UNKNOWN_TYPE, null). + build(), + info.getType()); + + // NAME + Node nameNode = varNode.getFirstChild(); + assertEquals(Token.NAME, nameNode.getType()); + assertNull(nameNode.getJSDocInfo()); + } + + public void testJSDocAttachment12() { + Node varNode = + parse("var a = {/** @type {Object} */ b: c};") + .getFirstChild(); + Node objectLitNode = varNode.getFirstChild().getFirstChild(); + assertEquals(Token.OBJECTLIT, objectLitNode.getType()); + assertNotNull(objectLitNode.getFirstChild().getJSDocInfo()); + } + + public void testJSDocAttachment13() { + Node varNode = parse("/** foo */ var a;").getFirstChild(); + assertNotNull(varNode.getJSDocInfo()); + } + + public void testJSDocAttachment14() { + Node varNode = parse("/** */ var a;").getFirstChild(); + assertNull(varNode.getJSDocInfo()); + } + + public void testJSDocAttachment15() { + Node varNode = parse("/** \n * \n */ var a;").getFirstChild(); + assertNull(varNode.getJSDocInfo()); + } + + public void testJSDocAttachment16() { + Node exprCall = + parse("/** @private */ x(); function f() {};").getFirstChild(); + assertEquals(Token.EXPR_RESULT, exprCall.getType()); + assertNull(exprCall.getNext().getJSDocInfo()); + assertNotNull(exprCall.getFirstChild().getJSDocInfo()); + } + + public void testIncorrectJSDocDoesNotAlterJSParsing1() throws Exception { + assertNodeEquality( + parse("var a = [1,2]"), + parse("/** @type Array. testCases = ImmutableList.of( + new ParserResult( + "3;", + createScript(new Node(Token.EXPR_RESULT, Node.newNumber(3.0)))), + new ParserResult( + "var a = b;", + createScript(new Node(Token.VAR, a))), + new ParserResult( + "\"hell\\\no\\ world\\\n\\\n!\"", + createScript(new Node(Token.EXPR_RESULT, + Node.newString(Token.STRING, "hello world!"))))); + + for (ParserResult testCase : testCases) { + assertNodeEquality(testCase.node, parse(testCase.code)); + } + } + + private Node createScript(Node n) { + Node script = new Node(Token.SCRIPT); + script.addChildToBack(n); + return script; + } + + public void testTrailingCommaWarning1() { + parse("var a = ['foo', 'bar'];"); + } + + public void testTrailingCommaWarning2() { + parse("var a = ['foo',,'bar'];"); + } + + public void testTrailingCommaWarning3() { + parse("var a = ['foo', 'bar',];", TRAILING_COMMA_MESSAGE); + mode = LanguageMode.ECMASCRIPT5; + parse("var a = ['foo', 'bar',];"); + } + + public void testTrailingCommaWarning4() { + parse("var a = [,];", TRAILING_COMMA_MESSAGE); + mode = LanguageMode.ECMASCRIPT5; + parse("var a = [,];"); + } + + public void testTrailingCommaWarning5() { + parse("var a = {'foo': 'bar'};"); + } + + public void testTrailingCommaWarning6() { + parse("var a = {'foo': 'bar',};", TRAILING_COMMA_MESSAGE); + mode = LanguageMode.ECMASCRIPT5; + parse("var a = {'foo': 'bar',};"); + } + + public void testTrailingCommaWarning7() { + parseError("var a = {,};", BAD_PROPERTY_MESSAGE); + } + + public void testSuspiciousBlockCommentWarning1() { + parse("/* @type {number} */ var x = 3;", SUSPICIOUS_COMMENT_WARNING); + } + + public void testSuspiciousBlockCommentWarning2() { + parse("/* \n * @type {number} */ var x = 3;", SUSPICIOUS_COMMENT_WARNING); + } + + public void testCatchClauseForbidden() { + parseError("try { } catch (e if true) {}", + "Catch clauses are not supported"); + } + + public void testConstForbidden() { + parseError("const x = 3;", "Unsupported syntax: CONST"); + } + + public void testDestructuringAssignForbidden() { + parseError("var [x, y] = foo();", "destructuring assignment forbidden"); + } + + public void testDestructuringAssignForbidden2() { + parseError("var {x, y} = foo();", "missing : after property id"); + } + + public void testDestructuringAssignForbidden3() { + parseError("var {x: x, y: y} = foo();", + "destructuring assignment forbidden"); + } + + public void testDestructuringAssignForbidden4() { + parseError("[x, y] = foo();", + "destructuring assignment forbidden", + "invalid assignment target"); + } + + public void testLetForbidden() { + parseError("function f() { let (x = 3) { alert(x); }; }", + "missing ; before statement", "syntax error"); + } + + public void testYieldForbidden() { + parseError("function f() { yield 3; }", "missing ; before statement"); + } + + public void testBracelessFunctionForbidden() { + parseError("var sq = function(x) x * x;", + "missing { before function body"); + } + + public void testGeneratorsForbidden() { + parseError("var i = (x for (x in obj));", + "Unsupported syntax: GENEXPR"); + } + + public void testGettersForbidden1() { + parseError("var x = {get foo() { return 3; }};", + IRFactory.GETTER_ERROR_MESSAGE); + } + + public void testGettersForbidden2() { + parseError("var x = {get foo bar() { return 3; }};", + "invalid property id"); + } + + public void testGettersForbidden3() { + parseError("var x = {a getter:function b() { return 3; }};", + "missing : after property id", "syntax error"); + } + + public void testGettersForbidden4() { + parseError("var x = {\"a\" getter:function b() { return 3; }};", + "missing : after property id", "syntax error"); + } + + public void testGettersForbidden5() { + parseError("var x = {a: 2, get foo() { return 3; }};", + IRFactory.GETTER_ERROR_MESSAGE); + } + + public void testSettersForbidden() { + parseError("var x = {set foo() { return 3; }};", + IRFactory.SETTER_ERROR_MESSAGE); + } + + public void testSettersForbidden2() { + parseError("var x = {a setter:function b() { return 3; }};", + "missing : after property id", "syntax error"); + } + + public void testFileOverviewJSDoc1() { + Node n = parse("/** @fileoverview Hi mom! */ function Foo() {}"); + assertEquals(Token.FUNCTION, n.getFirstChild().getType()); + assertTrue(n.getJSDocInfo() != null); + assertNull(n.getFirstChild().getJSDocInfo()); + assertEquals("Hi mom!", + n.getJSDocInfo().getFileOverview()); + } + + public void testFileOverviewJSDocDoesNotHoseParsing() { + assertEquals( + Token.FUNCTION, + parse("/** @fileoverview Hi mom! \n */ function Foo() {}") + .getFirstChild().getType()); + assertEquals( + Token.FUNCTION, + parse("/** @fileoverview Hi mom! \n * * * */ function Foo() {}") + .getFirstChild().getType()); + assertEquals( + Token.FUNCTION, + parse("/** @fileoverview \n * x */ function Foo() {}") + .getFirstChild().getType()); + assertEquals( + Token.FUNCTION, + parse("/** @fileoverview \n * x \n */ function Foo() {}") + .getFirstChild().getType()); + } + + public void testFileOverviewJSDoc2() { + Node n = parse("/** @fileoverview Hi mom! */ " + + "/** @constructor */ function Foo() {}"); + assertTrue(n.getJSDocInfo() != null); + assertEquals("Hi mom!", n.getJSDocInfo().getFileOverview()); + assertTrue(n.getFirstChild().getJSDocInfo() != null); + assertFalse(n.getFirstChild().getJSDocInfo().hasFileOverview()); + assertTrue(n.getFirstChild().getJSDocInfo().isConstructor()); + } + + public void testObjectLiteralDoc1() { + Node n = parse("var x = {/** @type {number} */ 1: 2};"); + + Node objectLit = n.getFirstChild().getFirstChild().getFirstChild(); + assertEquals(Token.OBJECTLIT, objectLit.getType()); + + Node number = objectLit.getFirstChild(); + assertEquals(Token.STRING_KEY, number.getType()); + assertNotNull(number.getJSDocInfo()); + } + + public void testDuplicatedParam() { + parse("function foo(x, x) {}", "Duplicate parameter name \"x\"."); + } + + public void testGetter() { + mode = LanguageMode.ECMASCRIPT3; + parseError("var x = {get 1(){}};", + IRFactory.GETTER_ERROR_MESSAGE); + parseError("var x = {get 'a'(){}};", + IRFactory.GETTER_ERROR_MESSAGE); + parseError("var x = {get a(){}};", + IRFactory.GETTER_ERROR_MESSAGE); + mode = LanguageMode.ECMASCRIPT5; + parse("var x = {get 1(){}};"); + parse("var x = {get 'a'(){}};"); + parse("var x = {get a(){}};"); + parseError("var x = {get a(b){}};", "getters may not have parameters"); + } + + public void testSetter() { + mode = LanguageMode.ECMASCRIPT3; + parseError("var x = {set 1(x){}};", + IRFactory.SETTER_ERROR_MESSAGE); + parseError("var x = {set 'a'(x){}};", + IRFactory.SETTER_ERROR_MESSAGE); + parseError("var x = {set a(x){}};", + IRFactory.SETTER_ERROR_MESSAGE); + mode = LanguageMode.ECMASCRIPT5; + parse("var x = {set 1(x){}};"); + parse("var x = {set 'a'(x){}};"); + parse("var x = {set a(x){}};"); + parseError("var x = {set a(){}};", + "setters must have exactly one parameter"); + } + + public void testLamestWarningEver() { + // This used to be a warning. + parse("var x = /** @type {undefined} */ (y);"); + parse("var x = /** @type {void} */ (y);"); + } + + public void testUnfinishedComment() { + parseError("/** this is a comment ", "unterminated comment"); + } + + public void testParseBlockDescription() { + Node n = parse("/** This is a variable. */ var x;"); + Node var = n.getFirstChild(); + assertNotNull(var.getJSDocInfo()); + assertEquals("This is a variable.", + var.getJSDocInfo().getBlockDescription()); + } + + public void testUnnamedFunctionStatement() { + // Statements + parseError("function() {};", "unnamed function statement"); + parseError("if (true) { function() {}; }", "unnamed function statement"); + parse("function f() {};"); + // Expressions + parse("(function f() {});"); + parse("(function () {});"); + } + + public void testReservedKeywords() { + boolean isIdeMode = false; + + mode = LanguageMode.ECMASCRIPT3; + + parseError("var boolean;", "missing variable name"); + parseError("function boolean() {};", + "missing ( before function parameters."); + parseError("boolean = 1;", "identifier is a reserved word"); + parseError("class = 1;", "identifier is a reserved word"); + parseError("public = 2;", "identifier is a reserved word"); + + mode = LanguageMode.ECMASCRIPT5; + + parse("var boolean;"); + parse("function boolean() {};"); + parse("boolean = 1;"); + parseError("class = 1;", "identifier is a reserved word"); + parse("public = 2;"); + + mode = LanguageMode.ECMASCRIPT5_STRICT; + + parse("var boolean;"); + parse("function boolean() {};"); + parse("boolean = 1;"); + parseError("class = 1;", "identifier is a reserved word"); + parseError("public = 2;", "identifier is a reserved word"); + } + + public void testKeywordsAsProperties() { + boolean isIdeMode = false; + + mode = LanguageMode.ECMASCRIPT3; + + parseError("var x = {function: 1};", "invalid property id"); + parseError("x.function;", "missing name after . operator"); + parseError("var x = {get x(){} };", + IRFactory.GETTER_ERROR_MESSAGE); + parseError("var x = {get function(){} };", "invalid property id"); + parseError("var x = {get 'function'(){} };", + IRFactory.GETTER_ERROR_MESSAGE); + parseError("var x = {get 1(){} };", + IRFactory.GETTER_ERROR_MESSAGE); + parseError("var x = {set function(a){} };", "invalid property id"); + parseError("var x = {set 'function'(a){} };", + IRFactory.SETTER_ERROR_MESSAGE); + parseError("var x = {set 1(a){} };", + IRFactory.SETTER_ERROR_MESSAGE); + parseError("var x = {class: 1};", "invalid property id"); + parseError("x.class;", "missing name after . operator"); + parse("var x = {let: 1};"); + parse("x.let;"); + parse("var x = {yield: 1};"); + parse("x.yield;"); + + mode = LanguageMode.ECMASCRIPT5; + + parse("var x = {function: 1};"); + parse("x.function;"); + parse("var x = {get function(){} };"); + parse("var x = {get 'function'(){} };"); + parse("var x = {get 1(){} };"); + parse("var x = {set function(a){} };"); + parse("var x = {set 'function'(a){} };"); + parse("var x = {set 1(a){} };"); + parse("var x = {class: 1};"); + parse("x.class;"); + parse("var x = {let: 1};"); + parse("x.let;"); + parse("var x = {yield: 1};"); + parse("x.yield;"); + + mode = LanguageMode.ECMASCRIPT5_STRICT; + + parse("var x = {function: 1};"); + parse("x.function;"); + parse("var x = {get function(){} };"); + parse("var x = {get 'function'(){} };"); + parse("var x = {get 1(){} };"); + parse("var x = {set function(a){} };"); + parse("var x = {set 'function'(a){} };"); + parse("var x = {set 1(a){} };"); + parse("var x = {class: 1};"); + parse("x.class;"); + parse("var x = {let: 1};"); + parse("x.let;"); + parse("var x = {yield: 1};"); + parse("x.yield;"); + } + + public void testGetPropFunctionName() { + parseError("function a.b() {}", + "missing ( before function parameters."); + parseError("var x = function a.b() {}", + "missing ( before function parameters."); + } + + public void testGetPropFunctionNameIdeMode() { + // In IDE mode, we try to fix up the tree, but sometimes + // this leads to even more errors. + isIdeMode = true; + parseError("function a.b() {}", + "missing ( before function parameters.", + "missing formal parameter", + "missing ) after formal parameters", + "missing { before function body", + "syntax error", + "missing ; before statement", + "missing ; before statement", + "missing } after function body", + "Unsupported syntax: ERROR", + "Unsupported syntax: ERROR"); + parseError("var x = function a.b() {}", + "missing ( before function parameters.", + "missing formal parameter", + "missing ) after formal parameters", + "missing { before function body", + "syntax error", + "missing ; before statement", + "missing ; before statement", + "missing } after function body", + "Unsupported syntax: ERROR", + "Unsupported syntax: ERROR"); + } + + public void testIdeModePartialTree() { + Node partialTree = parseError("function Foo() {} f.", + "missing name after . operator"); + assertNull(partialTree); + + isIdeMode = true; + partialTree = parseError("function Foo() {} f.", + "missing name after . operator"); + assertNotNull(partialTree); + } + + public void testForEach() { + parseError( + "function f(stamp, status) {\n" + + " for each ( var curTiming in this.timeLog.timings ) {\n" + + " if ( curTiming.callId == stamp ) {\n" + + " curTiming.flag = status;\n" + + " break;\n" + + " }\n" + + " }\n" + + "};", + "unsupported language extension: for each"); + } + + public void testMisplacedTypeAnnotation1() { + // misuse with COMMA + parse( + "var o = {};" + + "/** @type {string} */ o.prop1 = 1, o.prop2 = 2;", + MISPLACED_TYPE_ANNOTATION); + } + + public void testMisplacedTypeAnnotation2() { + // missing parenthese for the cast. + parse( + "var o = /** @type {string} */ getValue();", + MISPLACED_TYPE_ANNOTATION); + } + + public void testMisplacedTypeAnnotation3() { + // missing parenthese for the cast. + parse( + "var o = 1 + /** @type {string} */ value;", + MISPLACED_TYPE_ANNOTATION); + } + + public void testMisplacedTypeAnnotation4() { + // missing parenthese for the cast. + parse( + "var o = /** @type {!Array.} */ ['hello', 'you'];", + MISPLACED_TYPE_ANNOTATION); + } + + public void testMisplacedTypeAnnotation5() { + // missing parenthese for the cast. + parse( + "var o = (/** @type {!Foo} */ {});", + MISPLACED_TYPE_ANNOTATION); + } + + public void testMisplacedTypeAnnotation6() { + parse("var o = /** @type {function():string} */ function() {return 'str';}", + MISPLACED_TYPE_ANNOTATION); + } + + public void testValidTypeAnnotation1() { + parse("/** @type {string} */ var o = 'str';"); + parse("var /** @type {string} */ o = 'str', /** @type {number} */ p = 0;"); + parse("/** @type {function():string} */ function o() { return 'str'; }"); + parse("var o = {}; /** @type {string} */ o.prop = 'str';"); + parse("var o = {}; /** @type {string} */ o['prop'] = 'str';"); + parse("var o = { /** @type {string} */ prop : 'str' };"); + parse("var o = { /** @type {string} */ 'prop' : 'str' };"); + parse("var o = { /** @type {string} */ 1 : 'str' };"); + } + + public void testValidTypeAnnotation2() { + mode = LanguageMode.ECMASCRIPT5; + parse("var o = { /** @type {string} */ get prop() { return 'str' }};"); + parse("var o = { /** @type {string} */ set prop(s) {}};"); + } + + public void testValidTypeAnnotation3() { + // These two we don't currently support in the type checker but + // we would like to. + parse("try {} catch (/** @type {Error} */ e) {}"); + parse("function f(/** @type {string} */ a) {}"); + } + + /** + * Verify that the given code has the given parse errors. + * @return If in IDE mode, returns a partial tree. + */ + private Node parseError(String string, String... errors) { + TestErrorReporter testErrorReporter = new TestErrorReporter(errors, null); + Node script = null; + try { + StaticSourceFile file = new SimpleSourceFile("input", false); + script = ParserRunner.parse( + file, string, ParserRunner.createConfig(isIdeMode, mode, false), + testErrorReporter, Logger.getAnonymousLogger()).ast; + } catch (IOException e) { + throw new RuntimeException(e); + } + + // verifying that all warnings were seen + assertTrue(testErrorReporter.hasEncounteredAllErrors()); + assertTrue(testErrorReporter.hasEncounteredAllWarnings()); + + return script; + } + + private Node parse(String string, String... warnings) { + TestErrorReporter testErrorReporter = new TestErrorReporter(null, warnings); + Node script = null; + try { + StaticSourceFile file = new SimpleSourceFile("input", false); + script = ParserRunner.parse( + file, string, ParserRunner.createConfig(true, mode, false), + testErrorReporter, Logger.getAnonymousLogger()).ast; + } catch (IOException e) { + throw new RuntimeException(e); + } + + // verifying that all warnings were seen + assertTrue(testErrorReporter.hasEncounteredAllErrors()); + assertTrue(testErrorReporter.hasEncounteredAllWarnings()); + + return script; + } + + private static class ParserResult { + private final String code; + private final Node node; + + private ParserResult(String code, Node node) { + this.code = code; + this.node = node; + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/regex/CharRangesTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/regex/CharRangesTest.java new file mode 100644 index 0000000..3055779 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/regex/CharRangesTest.java @@ -0,0 +1,224 @@ +/* + * Copyright 2011 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.regex; + +import java.util.BitSet; +import java.util.Random; + +import com.google.javascript.jscomp.regex.CharRanges; + +import junit.framework.TestCase; + +public class CharRangesTest extends TestCase { + + static final long SEED = Long.parseLong(System.getProperty( + "junit.random.seed", "" + System.currentTimeMillis())); + + public final void testAgainstRegularImplementation() { + Random rnd = new Random(SEED); + + for (int run = 10; --run >= 0;) { + // Fill with bits in the range [0x1000, 0x3000). + BitSet bs = new BitSet(); + for (int i = 0x1000; --i >= 0;) { + bs.set(0x1000 + rnd.nextInt(0x3000)); + } + + // Create an equivalent sparse bit set + int[] members = new int[bs.cardinality()]; + for (int i = -1, k = 0; k < members.length; ++k) { + members[k] = i = bs.nextSetBit(i + 1); + } + CharRanges sbs = CharRanges.withMembers(members); + + // Check all bits including past the min/max bit + for (int i = 0; i < 0x5000; ++i) { + if (bs.get(i) != sbs.contains(i)) { + fail("sbs=" + sbs + ", bs=" + bs + ", difference at bit " + i); + } + } + } + } + + public final void testEmptyCharRanges() { + CharRanges sbs = CharRanges.EMPTY; + for (int i = -1000; i < 1000; ++i) { + assertFalse(sbs.contains(i)); + } + assertEquals("[]", sbs.toString()); + } + + public final void testCharRangesFactories() { + CharRanges isbs = CharRanges.withMembers(new int[] { 0, 1, 4, 9 }); + CharRanges isbs2 = CharRanges.withMembers(new int[] { 0, 1, 4, 9 }); + assertEquals("[0x0-0x1 0x4 0x9]", isbs.toString()); + + CharRanges esbs = CharRanges.withMembers(new int[0]); + + assertEquals(isbs, isbs); + assertEquals(isbs, isbs2); + assertFalse(isbs.equals(esbs)); + assertFalse(isbs.equals(null)); + assertFalse(isbs.equals(new Object())); + + assertEquals(isbs.hashCode(), isbs2.hashCode()); + assertFalse(isbs.hashCode() == esbs.hashCode()); + } + + public final void testRangeConstructor() { + try { + CharRanges.withRanges(new int[] { 1 }); + fail("Mismatched ranges"); + } catch (IllegalArgumentException ex) { + // pass + } + + try { + CharRanges.withRanges(new int[] { 1, 4, 4, 5 }); + fail("Discontiguous ranges"); + } catch (IllegalArgumentException ex) { + // pass + } + + try { + CharRanges.withRanges(new int[] { 4, 5, 1, 3 }); + fail("Misordered ranges"); + } catch (IllegalArgumentException ex) { + // pass + } + + try { + CharRanges.withRanges(new int[] { 0, 0 }); + fail("Empty range"); + } catch (IllegalArgumentException ex) { + // pass + } + } + + public final void testDupeMembers() { + CharRanges sbs1 = CharRanges.withMembers(new int[] { 0, 1, 4, 9 }); + assertEquals(sbs1.toString(), "[0x0-0x1 0x4 0x9]", sbs1.toString()); + + CharRanges sbs2 = CharRanges.withMembers(new int[] { 9, 1, 4, 1, 0 }); + assertEquals(sbs2.toString(), "[0x0-0x1 0x4 0x9]", sbs2.toString()); + + assertEquals(sbs1, sbs2); + assertEquals(sbs1.hashCode(), sbs2.hashCode()); + + for (int i = -10; i < 20; ++i) { + assertEquals("" + i, sbs1.contains(i), sbs2.contains(i)); + } + } + + public final void testDifference() { + // 1 2 3 + // 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0 + // b-a DD DD DDD D DDD + // a AAAAAAAAA A A A A A AAA AAA A A + // b BBB BBB BBB BBB B B BBB + // a-b DD DD D D D D DDD DDD D D + CharRanges a = CharRanges.withRanges(new int[] { + 0x03, 0x0C, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1C, 0x1D, 0x1E, 0x21, 0x24, 0x27, 0x28, 0x29, 0x2A, 0x2B }); + CharRanges b = CharRanges.withRanges(new int[] { + 0x01, 0x04, 0x06, 0x09, 0x0B, 0x0E, 0x0F, 0x12, 0x1A, 0x1B, + 0x1C, 0x1D, 0x21, 0x24 }); + CharRanges empty = CharRanges.withMembers(new int[0]); + + assertEquals(empty, empty.union(empty)); + assertEquals(a, a.union(empty)); + assertEquals(b, empty.union(b)); + + CharRanges aSb = a.difference(b); + assertEquals( + "[0x4-0x5 0x9-0xa 0x12 0x14 0x16 0x18 0x1e-0x20 0x24-0x26 0x28 0x2a]", + aSb.toString()); + assertTrue(a.containsAll(aSb)); + assertFalse(aSb.containsAll(a)); + assertFalse(aSb.containsAll(b)); + + CharRanges bSa = b.difference(a); + assertEquals( + "[0x1-0x2 0xc-0xd 0xf-0x11 0x1a 0x21-0x23]", + bSa.toString()); + assertTrue(b.containsAll(bSa)); + assertFalse(bSa.containsAll(a)); + assertFalse(bSa.containsAll(b)); + + // Check that a and b not changed by operation + assertEquals( + "[0x3-0xb 0x12 0x14 0x16 0x18 0x1c 0x1e-0x20 0x24-0x26 0x28 0x2a]", + a.toString()); + assertEquals( + "[0x1-0x3 0x6-0x8 0xb-0xd 0xf-0x11 0x1a 0x1c 0x21-0x23]", + b.toString()); + + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + // m: * * * * * * * * * + // s: * * * * * * * * + // d: * * * * * + CharRanges m = CharRanges.withMembers(0, 1, 2, 3, 6, 9, 0xa, 0xe, 0xf); + CharRanges s = CharRanges.withMembers(2, 5, 6, 7, 0xa, 0xb, 0xd, 0xe); + CharRanges d = m.difference(s); + assertEquals("[0x0-0x1 0x3 0x9 0xf]", d.toString()); + assertTrue(m.containsAll(d)); + assertFalse(d.containsAll(m)); + assertFalse(d.containsAll(s)); + assertFalse(s.containsAll(d)); + assertTrue(d.containsAll(d)); + } + + public final void testUnion() { + // 1 2 3 + // 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0 + // AAAAAAAAA A A A A A AAA AAA A A + // BBB BBB BBB BBB B B BBB + // UUUUUUUUUUUUU UUUU U U U U U UUUUUUUUU U U + CharRanges a = CharRanges.withRanges(new int[] { + 0x03, 0x0C, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1C, 0x1D, 0x1E, 0x21, 0x24, 0x27, 0x28, 0x29, 0x2A, 0x2B }); + CharRanges b = CharRanges.withRanges(new int[] { + 0x01, 0x04, 0x06, 0x09, 0x0B, 0x0E, 0x0F, 0x12, 0x1A, 0x1B, + 0x1C, 0x1D, 0x21, 0x24 }); + CharRanges empty = CharRanges.withMembers(new int[0]); + + assertEquals(empty, empty.union(empty)); + assertEquals(a, a.union(empty)); + assertEquals(b, empty.union(b)); + + CharRanges aUb = a.union(b); + assertEquals( + "[0x1-0xd 0xf-0x12 0x14 0x16 0x18 0x1a 0x1c 0x1e-0x26 0x28 0x2a]", + aUb.toString()); + assertEquals(aUb, b.union(a)); + assertTrue(aUb.containsAll(a)); + assertTrue(aUb.containsAll(b)); + assertFalse(a.containsAll(b)); + assertFalse(b.containsAll(a)); + assertTrue(a.containsAll(a)); + assertTrue(b.containsAll(b)); + assertTrue(aUb.containsAll(aUb)); + + // Check that a and b not changed by operation + assertEquals( + "[0x3-0xb 0x12 0x14 0x16 0x18 0x1c 0x1e-0x20 0x24-0x26 0x28 0x2a]", + a.toString()); + assertEquals( + "[0x1-0x3 0x6-0x8 0xb-0xd 0xf-0x11 0x1a 0x1c 0x21-0x23]", + b.toString()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/regtests/CompileEachLineOfProgramOutput.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/regtests/CompileEachLineOfProgramOutput.java new file mode 100644 index 0000000..8e97dbb --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/regtests/CompileEachLineOfProgramOutput.java @@ -0,0 +1,111 @@ +/* + * Copyright 2009 Peter Burns + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.regtests; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.logging.Level; + +import com.google.javascript.jscomp.CompilationLevel; +import com.google.javascript.jscomp.Compiler; +import com.google.javascript.jscomp.CompilerOptions; +import com.google.javascript.jscomp.SourceFile; +import com.google.javascript.jscomp.Result; +import com.google.javascript.jscomp.WarningLevel; + +public class CompileEachLineOfProgramOutput { + private static final SourceFile extern = + SourceFile.fromCode("externs.js", ""); + private static final CompilerOptions options = + new CompilerOptions(); + + static { + CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel( + options); + WarningLevel.QUIET.setOptionsForWarningLevel(options); + Compiler.setLoggingLevel(Level.OFF); + } + + public static void main(String[] args) throws IOException { + if (args.length == 0){ + usage(); + } + Runtime r = Runtime.getRuntime(); + Process p = null; + try { + p = r.exec(args); + } catch (IOException e) { + if (args[0].equals("generatejs")) { + // assuming that the command wasn't found + System.out.println("generatejs not found, required for generating " + + "fuzz test cases"); + System.out.println("See: http://github.com/rictic/generatejs"); + System.exit(2); + } else { + throw e; + } + } + + BufferedReader br = new BufferedReader( + new InputStreamReader(p.getInputStream())); + int programsCompiled = 0, compilerErrors = 0; + for (String program = br.readLine(); program != null; program = + br.readLine()) { + try { + compile(program, programsCompiled); + } catch(Exception e) { + System.out.println("Compiler error on program #" + + programsCompiled + ":"); + System.out.println(program); + System.out.println("Details:"); + e.printStackTrace(System.out); + System.out.println("\n\n\n"); + compilerErrors++; + } + + programsCompiled++; + } + + if (compilerErrors == 0){ + System.out.println(programsCompiled + + " programs compiled without error"); + System.exit(0); + } else { + System.out.println("==========FAILURE==========="); + System.out.println(compilerErrors + + " programs caused an error within the compiler out of " + + programsCompiled + " tested."); + System.exit(1); + } + } + + public static Result compile(String program, int num) { + SourceFile input = SourceFile.fromCode(""+num, program); + Compiler compiler = new Compiler(); + Result result = compiler.compile(extern, input, options); + return result; + } + + private static void usage() { + System.out.println( + "Usage: pass in a program to execute (with arguments)"); + System.out.println( + "The program is expected to produce JS programs to stdout, " + + "one per line"); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/IRTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/IRTest.java new file mode 100644 index 0000000..e770aa1 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/IRTest.java @@ -0,0 +1,206 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Lenz + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino; + +import com.google.common.collect.Lists; + +import junit.framework.TestCase; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class IRTest extends TestCase { + + public void testEmpty() { + testIR(IR.empty(), "EMPTY\n"); + } + + public void testFunction() { + testIR(IR.function(IR.name("hi"), IR.paramList(), IR.block()), + "FUNCTION hi\n" + + " NAME hi\n" + + " PARAM_LIST\n" + + " BLOCK\n"); + } + + public void testParamList() { + testIR(IR.paramList(), + "PARAM_LIST\n"); + + testIR(IR.paramList(IR.name("a"), IR.name("b")), + "PARAM_LIST\n" + + " NAME a\n" + + " NAME b\n"); + + testIR(IR.paramList(Lists.newArrayList(IR.name("a"), IR.name("b"))), + "PARAM_LIST\n" + + " NAME a\n" + + " NAME b\n"); + } + + public void testBlock() { + testIR(IR.block(), + "BLOCK\n"); + + testIR(IR.block(IR.empty(), IR.empty()), + "BLOCK\n" + + " EMPTY\n" + + " EMPTY\n"); + + testIR(IR.block(Lists.newArrayList(IR.empty(), IR.empty())), + "BLOCK\n" + + " EMPTY\n" + + " EMPTY\n"); + } + + public void testScript() { + testIR(IR.script(), + "SCRIPT\n"); + + testIR(IR.script(IR.empty(), IR.empty()), + "SCRIPT\n" + + " EMPTY\n" + + " EMPTY\n"); + + testIR(IR.script(Lists.newArrayList(IR.empty(), IR.empty())), + "SCRIPT\n" + + " EMPTY\n" + + " EMPTY\n"); + } + + public void testScriptThrows() { + boolean caught = false; + try { + IR.script(IR.returnNode()); + } catch(IllegalStateException e) { + caught = true; + } + assertTrue("expected exception was not seen", caught); + } + + public void testVar() { + testIR(IR.var(IR.name("a")), + "VAR\n" + + " NAME a\n"); + + testIR(IR.var(IR.name("a"), IR.trueNode()), + "VAR\n" + + " NAME a\n" + + " TRUE\n"); + } + + public void testReturn() { + testIR(IR.returnNode(), + "RETURN\n"); + + testIR(IR.returnNode(IR.name("a")), + "RETURN\n" + + " NAME a\n"); + } + + public void testThrow() { + testIR(IR.throwNode(IR.name("a")), + "THROW\n" + + " NAME a\n"); + } + + public void testExprResult() { + testIR(IR.exprResult(IR.name("a")), + "EXPR_RESULT\n" + + " NAME a\n"); + } + + public void testIf() { + testIR(IR.ifNode(IR.name("a"), IR.block()), + "IF\n" + + " NAME a\n" + + " BLOCK\n"); + + testIR(IR.ifNode(IR.name("a"), IR.block(), IR.block()), + "IF\n" + + " NAME a\n" + + " BLOCK\n" + + " BLOCK\n"); + } + + public void testIssue727_1() { + testIR( + IR.tryFinally( + IR.block(), + IR.block()), + "TRY\n" + + " BLOCK\n" + + " BLOCK\n" + + " BLOCK\n"); + } + + public void testIssue727_2() { + testIR( + IR.tryCatch( + IR.block(), + IR.catchNode( + IR.name("e"), + IR.block())), + "TRY\n" + + " BLOCK\n" + + " BLOCK\n" + + " CATCH\n" + + " NAME e\n" + + " BLOCK\n"); + } + + public void testIssue727_3() { + testIR( + IR.tryCatchFinally( + IR.block(), + IR.catchNode(IR.name("e"), IR.block()), + IR.block()), + "TRY\n" + + " BLOCK\n" + + " BLOCK\n" + + " CATCH\n" + + " NAME e\n" + + " BLOCK\n" + + " BLOCK\n"); + } + + private void testIR(Node node, String expectedStructure) { + assertEquals(expectedStructure, node.toStringTree()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/JSDocInfoTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/JSDocInfoTest.java new file mode 100644 index 0000000..badd7b3 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/JSDocInfoTest.java @@ -0,0 +1,450 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino; + +import static com.google.javascript.rhino.JSDocInfo.Visibility.PRIVATE; +import static com.google.javascript.rhino.JSDocInfo.Visibility.PROTECTED; +import static com.google.javascript.rhino.JSDocInfo.Visibility.PUBLIC; +import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_OBJECT_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_TYPE; +import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_TYPE; + +import com.google.common.collect.Sets; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.testing.Asserts; +import com.google.javascript.rhino.testing.TestErrorReporter; + +import junit.framework.TestCase; + +public class JSDocInfoTest extends TestCase { + private TestErrorReporter errorReporter = new TestErrorReporter(null, null); + private JSTypeRegistry registry = new JSTypeRegistry(errorReporter); + + private JSType getNativeType(JSTypeNative typeId) { + return registry.getNativeType(typeId); + } + + /** + * Tests the assigned ordinal of the elements of the + * {@link JSDocInfo.Visibility} enum. + */ + public void testVisibilityOrdinal() { + assertEquals(0, PRIVATE.ordinal()); + assertEquals(1, PROTECTED.ordinal()); + assertEquals(2, PUBLIC.ordinal()); + } + + public void testSetType() { + JSDocInfo info = new JSDocInfo(); + info.setType(fromString("string")); + + assertNull(info.getBaseType()); + assertNull(info.getDescription()); + assertNull(info.getEnumParameterType()); + assertEquals(0, info.getParameterCount()); + assertNull(info.getReturnType()); + assertTypeEquals(STRING_TYPE, resolve(info.getType())); + assertNull(info.getVisibility()); + assertTrue(info.hasType()); + assertFalse(info.isConstant()); + assertFalse(info.isConstructor()); + assertFalse(info.isHidden()); + assertFalse(info.shouldPreserveTry()); + } + + public void testSetTypeAndVisibility() { + JSDocInfo info = new JSDocInfo(); + info.setType(fromString("string")); + info.setVisibility(PROTECTED); + + assertNull(info.getBaseType()); + assertNull(info.getDescription()); + assertNull(info.getEnumParameterType()); + assertEquals(0, info.getParameterCount()); + assertNull(info.getReturnType()); + assertTypeEquals(STRING_TYPE, resolve(info.getType())); + assertEquals(PROTECTED, info.getVisibility()); + assertTrue(info.hasType()); + assertFalse(info.isConstant()); + assertFalse(info.isConstructor()); + assertFalse(info.isHidden()); + assertFalse(info.shouldPreserveTry()); + } + + public void testSetReturnType() { + JSDocInfo info = new JSDocInfo(); + info.setReturnType(fromString("string")); + + assertNull(info.getBaseType()); + assertNull(info.getDescription()); + assertNull(info.getEnumParameterType()); + assertEquals(0, info.getParameterCount()); + assertTypeEquals(STRING_TYPE, resolve(info.getReturnType())); + assertNull(info.getType()); + assertNull(info.getVisibility()); + assertFalse(info.hasType()); + assertFalse(info.isConstant()); + assertFalse(info.isConstructor()); + assertFalse(info.isHidden()); + assertFalse(info.shouldPreserveTry()); + } + + public void testSetReturnTypeAndBaseType() { + JSDocInfo info = new JSDocInfo(); + info.setBaseType( + new JSTypeExpression( + new Node(Token.BANG, Node.newString("Number")), "")); + info.setReturnType(fromString("string")); + + assertTypeEquals(NUMBER_OBJECT_TYPE, + resolve(info.getBaseType())); + assertNull(info.getDescription()); + assertNull(info.getEnumParameterType()); + assertEquals(0, info.getParameterCount()); + assertTypeEquals(STRING_TYPE, resolve(info.getReturnType())); + assertNull(info.getType()); + assertNull(info.getVisibility()); + assertFalse(info.hasType()); + assertFalse(info.isConstant()); + assertFalse(info.isConstructor()); + assertFalse(info.isHidden()); + assertFalse(info.shouldPreserveTry()); + } + + public void testSetEnumParameterType() { + JSDocInfo info = new JSDocInfo(); + info.setEnumParameterType(fromString("string")); + + assertNull(info.getBaseType()); + assertNull(info.getDescription()); + assertTypeEquals(STRING_TYPE, + resolve(info.getEnumParameterType())); + assertEquals(0, info.getParameterCount()); + assertNull(info.getReturnType()); + assertNull(info.getType()); + assertNull(info.getVisibility()); + assertFalse(info.hasType()); + assertFalse(info.isConstant()); + assertFalse(info.isConstructor()); + assertFalse(info.isHidden()); + assertFalse(info.shouldPreserveTry()); + } + + public void testMultipleSetType() { + JSDocInfo info = new JSDocInfo(); + info.setType(fromString("number")); + + try { + info.setReturnType(fromString("boolean")); + fail("Expected exception"); + } catch (IllegalStateException e) {} + + try { + info.setEnumParameterType(fromString("string")); + fail("Expected exception"); + } catch (IllegalStateException e) {} + + try { + info.setTypedefType(fromString("string")); + fail("Expected exception"); + } catch (IllegalStateException e) {} + + assertTypeEquals(NUMBER_TYPE, resolve(info.getType())); + assertNull(info.getReturnType()); + assertNull(info.getEnumParameterType()); + assertNull(info.getTypedefType()); + assertTrue(info.hasType()); + } + + public void testMultipleSetType2() { + JSDocInfo info = new JSDocInfo(); + + info.setReturnType(fromString("boolean")); + + try { + info.setType(fromString("number")); + fail("Expected exception"); + } catch (IllegalStateException e) {} + + try { + info.setEnumParameterType(fromString("string")); + fail("Expected exception"); + } catch (IllegalStateException e) {} + + try { + info.setTypedefType(fromString("string")); + fail("Expected exception"); + } catch (IllegalStateException e) {} + + assertTypeEquals(BOOLEAN_TYPE, + resolve(info.getReturnType())); + assertNull(info.getEnumParameterType()); + assertNull(info.getType()); + assertNull(info.getTypedefType()); + assertFalse(info.hasType()); + } + + public void testMultipleSetType3() { + JSDocInfo info = new JSDocInfo(); + info.setEnumParameterType(fromString("boolean")); + + try { + info.setType(fromString("number")); + fail("Expected exception"); + } catch (IllegalStateException e) {} + + try { + info.setReturnType(fromString("string")); + fail("Expected exception"); + } catch (IllegalStateException e) {} + + try { + info.setTypedefType(fromString("string")); + fail("Expected exception"); + } catch (IllegalStateException e) {} + + assertNull(info.getType()); + assertNull(info.getTypedefType()); + assertNull(info.getReturnType()); + assertTypeEquals(BOOLEAN_TYPE, + resolve(info.getEnumParameterType())); + } + + public void testSetTypedefType() { + JSDocInfo info = new JSDocInfo(); + info.setTypedefType(fromString("boolean")); + + assertTypeEquals(BOOLEAN_TYPE, + resolve(info.getTypedefType())); + assertTrue(info.hasTypedefType()); + assertFalse(info.hasType()); + assertFalse(info.hasEnumParameterType()); + assertFalse(info.hasReturnType()); + } + + public void testSetConstant() { + JSDocInfo info = new JSDocInfo(); + info.setConstant(true); + + assertFalse(info.hasType()); + assertTrue(info.isConstant()); + assertFalse(info.isConstructor()); + assertFalse(info.isDefine()); + assertFalse(info.isHidden()); + assertFalse(info.shouldPreserveTry()); + } + + public void testSetConstructor() { + JSDocInfo info = new JSDocInfo(); + info.setConstructor(true); + + assertFalse(info.isConstant()); + assertTrue(info.isConstructor()); + assertFalse(info.isDefine()); + assertFalse(info.isHidden()); + assertFalse(info.shouldPreserveTry()); + } + + public void testSetDefine() { + JSDocInfo info = new JSDocInfo(); + info.setDefine(true); + + assertTrue(info.isConstant()); + assertFalse(info.isConstructor()); + assertTrue(info.isDefine()); + assertFalse(info.isHidden()); + assertFalse(info.shouldPreserveTry()); + } + + public void testSetHidden() { + JSDocInfo info = new JSDocInfo(); + info.setHidden(true); + + assertFalse(info.hasType()); + assertFalse(info.isConstant()); + assertFalse(info.isConstructor()); + assertFalse(info.isDefine()); + assertTrue(info.isHidden()); + assertFalse(info.shouldPreserveTry()); + } + + public void testSetShouldPreserveTry() { + JSDocInfo info = new JSDocInfo(); + info.setShouldPreserveTry(true); + + assertFalse(info.isConstant()); + assertFalse(info.isConstructor()); + assertFalse(info.isDefine()); + assertFalse(info.isHidden()); + assertTrue(info.shouldPreserveTry()); + } + + public void testSetNoTypeCheck() { + JSDocInfo info = new JSDocInfo(); + info.setNoCheck(true); + + assertFalse(info.isDeprecated()); + assertFalse(info.isNoAlias()); + assertFalse(info.isOverride()); + assertTrue(info.isNoTypeCheck()); + } + + public void testSetOverride() { + JSDocInfo info = new JSDocInfo(); + info.setOverride(true); + + assertFalse(info.isDeprecated()); + assertFalse(info.isNoAlias()); + assertTrue(info.isOverride()); + } + + public void testSetExport() { + JSDocInfo info = new JSDocInfo(); + info.setExport(true); + + assertTrue(info.isExport()); + } + + public void testSetNoAlias() { + JSDocInfo info = new JSDocInfo(); + info.setNoAlias(true); + + assertFalse(info.isDeprecated()); + assertFalse(info.isOverride()); + assertTrue(info.isNoAlias()); + } + + public void testSetDeprecated() { + JSDocInfo info = new JSDocInfo(); + info.setDeprecated(true); + + assertFalse(info.isNoAlias()); + assertFalse(info.isOverride()); + assertTrue(info.isDeprecated()); + } + + public void testMultipleSetFlags1() { + JSDocInfo info = new JSDocInfo(); + info.setConstant(true); + info.setConstructor(true); + info.setHidden(true); + info.setShouldPreserveTry(true); + + assertFalse(info.hasType()); + assertTrue(info.isConstant()); + assertTrue(info.isConstructor()); + assertFalse(info.isDefine()); + assertTrue(info.isHidden()); + assertTrue(info.shouldPreserveTry()); + + info.setHidden(false); + + assertTrue(info.isConstant()); + assertTrue(info.isConstructor()); + assertFalse(info.isDefine()); + assertFalse(info.isHidden()); + assertTrue(info.shouldPreserveTry()); + + info.setConstant(false); + info.setConstructor(false); + + assertFalse(info.isConstant()); + assertFalse(info.isConstructor()); + assertFalse(info.isDefine()); + assertFalse(info.isHidden()); + assertTrue(info.shouldPreserveTry()); + + info.setConstructor(true); + + assertFalse(info.isConstant()); + assertTrue(info.isConstructor()); + assertFalse(info.isDefine()); + assertFalse(info.isHidden()); + assertTrue(info.shouldPreserveTry()); + } + + public void testSetFileOverviewWithDocumentationOff() { + JSDocInfo info = new JSDocInfo(); + info.documentFileOverview("hi bob"); + assertNull(info.getFileOverview()); + } + + public void testSetFileOverviewWithDocumentationOn() { + JSDocInfo info = new JSDocInfo(true); + info.documentFileOverview("hi bob"); + assertEquals("hi bob", info.getFileOverview()); + } + + public void testSetSuppressions() { + JSDocInfo info = new JSDocInfo(true); + info.setSuppressions(Sets.newHashSet("sam", "bob")); + assertEquals(Sets.newHashSet("bob", "sam"), info.getSuppressions()); + } + + public void testSetModifies() { + JSDocInfo info = new JSDocInfo(true); + info.setModifies(Sets.newHashSet("this")); + assertEquals(Sets.newHashSet("this"), info.getModifies()); + + info = new JSDocInfo(true); + info.setModifies(Sets.newHashSet("arguments")); + assertEquals(Sets.newHashSet("arguments"), info.getModifies()); + } + + /** Gets the type expression for a simple type name. */ + private JSTypeExpression fromString(String s) { + return new JSTypeExpression(Node.newString(s), ""); + } + + private JSType resolve(JSTypeExpression n, String... warnings) { + errorReporter.setWarnings(warnings); + return n.evaluate(null, registry); + } + + private void assertTypeEquals(JSTypeNative a, JSType b) { + assertTypeEquals(getNativeType(a), b); + } + + private void assertTypeEquals(JSType a, JSType b) { + Asserts.assertTypeEquals(a, b); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/NodeTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/NodeTest.java new file mode 100644 index 0000000..20f3e8b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/NodeTest.java @@ -0,0 +1,425 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino; + +import com.google.javascript.rhino.Node.NodeMismatch; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.testing.TestErrorReporter; + +import junit.framework.TestCase; + +public class NodeTest extends TestCase { + public void testMergeExtractNormal() throws Exception { + testMergeExtract(5, 6); + testMergeExtract(456, 3423); + testMergeExtract(0, 0); + } + + public void testMergeExtractErroneous() throws Exception { + assertEquals(-1, Node.mergeLineCharNo(-5, 90)); + assertEquals(-1, Node.mergeLineCharNo(0, -1)); + assertEquals(-1, Node.extractLineno(-1)); + assertEquals(-1, Node.extractCharno(-1)); + } + + public void testMergeOverflowGraciously() throws Exception { + int linecharno = Node.mergeLineCharNo(89, 4096); + assertEquals(89, Node.extractLineno(linecharno)); + assertEquals(4095, Node.extractCharno(linecharno)); + } + + public void testCheckTreeEqualsImplSame() { + Node node1 = new Node(1, new Node(2)); + Node node2 = new Node(1, new Node(2)); + assertEquals(null, node1.checkTreeEqualsImpl(node2)); + } + + public void testCheckTreeEqualsImplDifferentType() { + Node node1 = new Node(1, new Node(2)); + Node node2 = new Node(2, new Node(2)); + assertEquals(new NodeMismatch(node1, node2), + node1.checkTreeEqualsImpl(node2)); + } + + public void testCheckTreeEqualsImplDifferentChildCount() { + Node node1 = new Node(1, new Node(2)); + Node node2 = new Node(1); + assertEquals(new NodeMismatch(node1, node2), + node1.checkTreeEqualsImpl(node2)); + } + + public void testCheckTreeEqualsImplDifferentChild() { + Node child1 = new Node(1); + Node child2 = new Node(2); + Node node1 = new Node(1, child1); + Node node2 = new Node(1, child2); + assertEquals(new NodeMismatch(child1, child2), + node1.checkTreeEqualsImpl(node2)); + } + + public void testCheckTreeEqualsSame() { + Node node1 = new Node(1); + assertEquals(null, node1.checkTreeEquals(node1)); + } + + public void testCheckTreeEqualsStringDifferent() { + Node node1 = new Node(Token.ADD); + Node node2 = new Node(Token.SUB); + assertNotNull(node1.checkTreeEquals(node2)); + } + + public void testCheckTreeEqualsBooleanSame() { + Node node1 = new Node(1); + assertEquals(true, node1.isEquivalentTo(node1)); + } + + public void testCheckTreeEqualsBooleanDifferent() { + Node node1 = new Node(1); + Node node2 = new Node(2); + assertEquals(false, node1.isEquivalentTo(node2)); + } + + public void testCheckTreeEqualsSlashVDifferent() { + Node node1 = Node.newString("\u000B"); + node1.putBooleanProp(Node.SLASH_V, true); + Node node2 = Node.newString("\u000B"); + assertEquals(false, node1.isEquivalentTo(node2)); + } + + public void testCheckTreeEqualsImplDifferentIncProp() { + Node node1 = new Node(Token.INC); + node1.putIntProp(Node.INCRDECR_PROP, 1); + Node node2 = new Node(Token.INC); + assertNotNull(node1.checkTreeEqualsImpl(node2)); + } + + public void testCheckTreeTypeAwareEqualsSame() { + TestErrorReporter testErrorReporter = new TestErrorReporter(null, null); + JSTypeRegistry registry = new JSTypeRegistry(testErrorReporter); + Node node1 = Node.newString(Token.NAME, "f"); + node1.setJSType(registry.getNativeType(JSTypeNative.NUMBER_TYPE)); + Node node2 = Node.newString(Token.NAME, "f"); + node2.setJSType(registry.getNativeType(JSTypeNative.NUMBER_TYPE)); + assertTrue(node1.isEquivalentToTyped(node2)); + } + + public void testCheckTreeTypeAwareEqualsSameNull() { + TestErrorReporter testErrorReporter = new TestErrorReporter(null, null); + JSTypeRegistry registry = new JSTypeRegistry(testErrorReporter); + Node node1 = Node.newString(Token.NAME, "f"); + Node node2 = Node.newString(Token.NAME, "f"); + assertTrue(node1.isEquivalentToTyped(node2)); + } + + public void testCheckTreeTypeAwareEqualsDifferent() { + TestErrorReporter testErrorReporter = new TestErrorReporter(null, null); + JSTypeRegistry registry = new JSTypeRegistry(testErrorReporter); + Node node1 = Node.newString(Token.NAME, "f"); + node1.setJSType(registry.getNativeType(JSTypeNative.NUMBER_TYPE)); + Node node2 = Node.newString(Token.NAME, "f"); + node2.setJSType(registry.getNativeType(JSTypeNative.STRING_TYPE)); + assertFalse(node1.isEquivalentToTyped(node2)); + } + + public void testCheckTreeTypeAwareEqualsDifferentNull() { + TestErrorReporter testErrorReporter = new TestErrorReporter(null, null); + JSTypeRegistry registry = new JSTypeRegistry(testErrorReporter); + Node node1 = Node.newString(Token.NAME, "f"); + node1.setJSType(registry.getNativeType(JSTypeNative.NUMBER_TYPE)); + Node node2 = Node.newString(Token.NAME, "f"); + assertFalse(node1.isEquivalentToTyped(node2)); + } + + public void testVarArgs1() { + assertFalse(new Node(1).isVarArgs()); + } + + public void testVarArgs2() { + Node n = new Node(1); + n.setVarArgs(false); + assertFalse(n.isVarArgs()); + } + + public void testVarArgs3() { + Node n = new Node(1); + n.setVarArgs(true); + assertTrue(n.isVarArgs()); + } + + private void testMergeExtract(int lineno, int charno) { + int linecharno = Node.mergeLineCharNo(lineno, charno); + assertEquals(lineno, Node.extractLineno(linecharno)); + assertEquals(charno, Node.extractCharno(linecharno)); + } + + public void testFileLevelJSDocAppender() { + Node n = new Node(1); + Node.FileLevelJsDocBuilder builder = n.getJsDocBuilderForNode(); + builder.append("foo"); + builder.append("bar"); + assertEquals("foobar", n.getJSDocInfo().getLicense()); + } + + // TODO(johnlenz): reenable this test. + public void disable_testIsQualifiedName() { + assertTrue(getNode("a").isQualifiedName()); + assertTrue(getNode("$").isQualifiedName()); + assertTrue(getNode("_").isQualifiedName()); + assertTrue(getNode("a.b").isQualifiedName()); + assertTrue(getNode("a_b.cccccc$d4.x.y.zA$").isQualifiedName()); + assertTrue(getNode("this.foo").isQualifiedName()); + assertFalse(getNode("0").isQualifiedName()); + assertFalse(getNode("[]").isQualifiedName()); + assertFalse(getNode("{}").isQualifiedName()); + assertFalse(getNode("''").isQualifiedName()); + assertFalse(getNode("a[b]").isQualifiedName()); + assertFalse(getNode("a[b].c").isQualifiedName()); + assertFalse(getNode("c.a[b]").isQualifiedName()); + assertFalse(getNode("a()").isQualifiedName()); + assertFalse(getNode("a().b").isQualifiedName()); + assertFalse(getNode("b.a()").isQualifiedName()); + assertFalse(getNode("'a'").isQualifiedName()); + assertFalse(getNode("/x/").isQualifiedName()); + assertFalse(getNode("++x").isQualifiedName()); + } + + public void testCloneAnnontations() { + Node n = getVarRef("a"); + assertFalse(n.getBooleanProp(Node.IS_CONSTANT_NAME)); + n.putBooleanProp(Node.IS_CONSTANT_NAME, true); + assertTrue(n.getBooleanProp(Node.IS_CONSTANT_NAME)); + + Node nodeClone = n.cloneNode(); + assertTrue(nodeClone.getBooleanProp(Node.IS_CONSTANT_NAME)); + } + + public void testSharedProps1() { + Node n = getVarRef("A"); + n.putIntProp(Node.SIDE_EFFECT_FLAGS, 5); + Node m = new Node(Token.TRUE); + m.clonePropsFrom(n); + assertEquals(m.getPropListHeadForTesting(), n.getPropListHeadForTesting()); + assertEquals(5, n.getIntProp(Node.SIDE_EFFECT_FLAGS)); + assertEquals(5, m.getIntProp(Node.SIDE_EFFECT_FLAGS)); + } + + public void testSharedProps2() { + Node n = getVarRef("A"); + n.putIntProp(Node.SIDE_EFFECT_FLAGS, 5); + Node m = new Node(Token.TRUE); + m.clonePropsFrom(n); + + n.putIntProp(Node.SIDE_EFFECT_FLAGS, 6); + assertEquals(6, n.getIntProp(Node.SIDE_EFFECT_FLAGS)); + assertEquals(5, m.getIntProp(Node.SIDE_EFFECT_FLAGS)); + assertFalse( + m.getPropListHeadForTesting() == n.getPropListHeadForTesting()); + + m.putIntProp(Node.SIDE_EFFECT_FLAGS, 7); + assertEquals(6, n.getIntProp(Node.SIDE_EFFECT_FLAGS)); + assertEquals(7, m.getIntProp(Node.SIDE_EFFECT_FLAGS)); + } + + public void testSharedProps3() { + Node n = getVarRef("A"); + n.putIntProp(Node.SIDE_EFFECT_FLAGS, 2); + n.putIntProp(Node.INCRDECR_PROP, 3); + Node m = new Node(Token.TRUE); + m.clonePropsFrom(n); + + n.putIntProp(Node.SIDE_EFFECT_FLAGS, 4); + assertEquals(4, n.getIntProp(Node.SIDE_EFFECT_FLAGS)); + assertEquals(2, m.getIntProp(Node.SIDE_EFFECT_FLAGS)); + } + + public void testBooleanProp() { + Node n = getVarRef("a"); + + n.putBooleanProp(Node.IS_CONSTANT_NAME, false); + + assertNull(n.lookupProperty(Node.IS_CONSTANT_NAME)); + assertFalse(n.getBooleanProp(Node.IS_CONSTANT_NAME)); + + n.putBooleanProp(Node.IS_CONSTANT_NAME, true); + + assertNotNull(n.lookupProperty(Node.IS_CONSTANT_NAME)); + assertTrue(n.getBooleanProp(Node.IS_CONSTANT_NAME)); + + n.putBooleanProp(Node.IS_CONSTANT_NAME, false); + + assertNull(n.lookupProperty(Node.IS_CONSTANT_NAME)); + assertFalse(n.getBooleanProp(Node.IS_CONSTANT_NAME)); + } + + // Verify that annotations on cloned nodes are properly handled. + public void testCloneAnnontations2() { + Node n = getVarRef("a"); + n.putBooleanProp(Node.IS_CONSTANT_NAME, true); + n.putBooleanProp(Node.IS_DISPATCHER, true); + assertTrue(n.getBooleanProp(Node.IS_CONSTANT_NAME)); + assertTrue(n.getBooleanProp(Node.IS_DISPATCHER)); + + Node nodeClone = n.cloneNode(); + assertTrue(nodeClone.getBooleanProp(Node.IS_CONSTANT_NAME)); + assertTrue(nodeClone.getBooleanProp(Node.IS_DISPATCHER)); + + n.putBooleanProp(Node.IS_DISPATCHER, false); + assertTrue(n.getBooleanProp(Node.IS_CONSTANT_NAME)); + assertFalse(n.getBooleanProp(Node.IS_DISPATCHER)); + + assertTrue(nodeClone.getBooleanProp(Node.IS_CONSTANT_NAME)); + assertTrue(nodeClone.getBooleanProp(Node.IS_DISPATCHER)); + } + + public void testGetIndexOfChild() { + Node assign = getAssignExpr("b","c"); + assertEquals(2, assign.getChildCount()); + + Node firstChild = assign.getFirstChild(); + Node secondChild = firstChild.getNext(); + assertNotNull(secondChild); + + assertEquals(0, assign.getIndexOfChild(firstChild)); + assertEquals(1, assign.getIndexOfChild(secondChild)); + assertEquals(-1, assign.getIndexOfChild(assign)); + } + + public void testCopyInformationFrom() { + Node assign = getAssignExpr("b","c"); + assign.setSourceEncodedPosition(99); + assign.setSourceFileForTesting("foo.js"); + + Node lhs = assign.getFirstChild(); + lhs.copyInformationFrom(assign); + assertEquals(99, lhs.getSourcePosition()); + assertEquals("foo.js", lhs.getSourceFileName()); + + assign.setSourceEncodedPosition(101); + assign.setSourceFileForTesting("bar.js"); + lhs.copyInformationFrom(assign); + assertEquals(99, lhs.getSourcePosition()); + assertEquals("foo.js", lhs.getSourceFileName()); + } + + public void testUseSourceInfoIfMissingFrom() { + Node assign = getAssignExpr("b","c"); + assign.setSourceEncodedPosition(99); + assign.setSourceFileForTesting("foo.js"); + + Node lhs = assign.getFirstChild(); + lhs.useSourceInfoIfMissingFrom(assign); + assertEquals(99, lhs.getSourcePosition()); + assertEquals("foo.js", lhs.getSourceFileName()); + + assign.setSourceEncodedPosition(101); + assign.setSourceFileForTesting("bar.js"); + lhs.useSourceInfoIfMissingFrom(assign); + assertEquals(99, lhs.getSourcePosition()); + assertEquals("foo.js", lhs.getSourceFileName()); + } + + public void testUseSourceInfoFrom() { + Node assign = getAssignExpr("b","c"); + assign.setSourceEncodedPosition(99); + assign.setSourceFileForTesting("foo.js"); + + Node lhs = assign.getFirstChild(); + lhs.useSourceInfoFrom(assign); + assertEquals(99, lhs.getSourcePosition()); + assertEquals("foo.js", lhs.getSourceFileName()); + + assign.setSourceEncodedPosition(101); + assign.setSourceFileForTesting("bar.js"); + lhs.useSourceInfoFrom(assign); + assertEquals(101, lhs.getSourcePosition()); + assertEquals("bar.js", lhs.getSourceFileName()); + } + + public void testInvalidSourceOffset() { + Node string = Node.newString("a"); + + string.setSourceEncodedPosition(-1); + assertTrue(string.getSourceOffset() < 0); + + string.setSourceFileForTesting("foo.js"); + assertTrue(string.getSourceOffset() < 0); + } + + public void testQualifiedName() { + assertNull(IR.name("").getQualifiedName()); + assertEquals("a", IR.name("a").getQualifiedName()); + assertEquals( + "a.b", IR.getprop(IR.name("a"), IR.string("b")).getQualifiedName()); + assertEquals( + "this.b", IR.getprop(IR.thisNode(), IR.string("b")).getQualifiedName()); + assertNull( + IR.getprop(IR.call(IR.name("a")), IR.string("b")).getQualifiedName()); + } + + private static Node getVarRef(String name) { + return Node.newString(Token.NAME, name); + } + + private static Node getAssignExpr(String name1, String name2) { + return new Node(Token.ASSIGN, getVarRef(name1), getVarRef(name2)); + } + + private static Node getNode(String js) { + /* + Node root = parse("var a=(" + js + ");"); + Node expr = root.getFirstChild(); + Node var = expr.getFirstChild(); + return var.getFirstChild(); + */ + return null; + } + + private static Node parse(String string) { + /* + CompilerEnvirons environment = new CompilerEnvirons(); + TestErrorReporter testErrorReporter = new TestErrorReporter(null, null); + environment.setErrorReporter(testErrorReporter); + environment.setParseJSDoc(true); + Parser p = new Parser(environment, testErrorReporter); + return p.parse(string, null, 0); + */ + return null; + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/BooleanLiteralSetTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/BooleanLiteralSetTest.java new file mode 100644 index 0000000..5b35e5b --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/BooleanLiteralSetTest.java @@ -0,0 +1,107 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.BooleanLiteralSet.BOTH; +import static com.google.javascript.rhino.jstype.BooleanLiteralSet.EMPTY; +import static com.google.javascript.rhino.jstype.BooleanLiteralSet.FALSE; +import static com.google.javascript.rhino.jstype.BooleanLiteralSet.TRUE; + +import junit.framework.TestCase; + +/** + * Tests {@link BooleanLiteralSet}. + * + */ +public class BooleanLiteralSetTest extends TestCase { + + public void testIntersection() { + assertEquals(EMPTY, EMPTY.intersection(EMPTY)); + assertEquals(EMPTY, EMPTY.intersection(TRUE)); + assertEquals(EMPTY, EMPTY.intersection(FALSE)); + assertEquals(EMPTY, EMPTY.intersection(BOTH)); + assertEquals(EMPTY, TRUE.intersection(EMPTY)); + assertEquals(TRUE, TRUE.intersection(TRUE)); + assertEquals(EMPTY, TRUE.intersection(FALSE)); + assertEquals(TRUE, TRUE.intersection(BOTH)); + assertEquals(EMPTY, FALSE.intersection(EMPTY)); + assertEquals(EMPTY, FALSE.intersection(TRUE)); + assertEquals(FALSE, FALSE.intersection(FALSE)); + assertEquals(FALSE, FALSE.intersection(BOTH)); + assertEquals(EMPTY, BOTH.intersection(EMPTY)); + assertEquals(TRUE, BOTH.intersection(TRUE)); + assertEquals(FALSE, BOTH.intersection(FALSE)); + assertEquals(BOTH, BOTH.intersection(BOTH)); + } + + public void testUnion() { + assertEquals(EMPTY, EMPTY.union(EMPTY)); + assertEquals(TRUE, EMPTY.union(TRUE)); + assertEquals(FALSE, EMPTY.union(FALSE)); + assertEquals(BOTH, EMPTY.union(BOTH)); + assertEquals(TRUE, TRUE.union(EMPTY)); + assertEquals(TRUE, TRUE.union(TRUE)); + assertEquals(BOTH, TRUE.union(FALSE)); + assertEquals(BOTH, TRUE.union(BOTH)); + assertEquals(FALSE, FALSE.union(EMPTY)); + assertEquals(BOTH, FALSE.union(TRUE)); + assertEquals(FALSE, FALSE.union(FALSE)); + assertEquals(BOTH, FALSE.union(BOTH)); + assertEquals(BOTH, BOTH.union(EMPTY)); + assertEquals(BOTH, BOTH.union(TRUE)); + assertEquals(BOTH, BOTH.union(FALSE)); + assertEquals(BOTH, BOTH.union(BOTH)); + } + + public void testGet() { + assertEquals(TRUE, BooleanLiteralSet.get(true)); + assertEquals(FALSE, BooleanLiteralSet.get(false)); + } + + public void testContains() { + assertFalse(EMPTY.contains(true)); + assertFalse(EMPTY.contains(false)); + assertTrue(TRUE.contains(true)); + assertFalse(TRUE.contains(false)); + assertFalse(FALSE.contains(true)); + assertTrue(FALSE.contains(false)); + assertTrue(BOTH.contains(true)); + assertTrue(BOTH.contains(false)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/EnumElementTypeTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/EnumElementTypeTest.java new file mode 100644 index 0000000..04574ce --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/EnumElementTypeTest.java @@ -0,0 +1,80 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.testing.BaseJSTypeTestCase; + + +/** + * Tests for EnumElementTypes. + * @author nicksantos@google.com (Nick Santos) + */ +public class EnumElementTypeTest extends BaseJSTypeTestCase { + public void testSubtypeRelation() throws Exception { + EnumElementType typeA = registry.createEnumType( + "typeA", null, NUMBER_TYPE).getElementsType(); + EnumElementType typeB = registry.createEnumType( + "typeB", null, NUMBER_TYPE).getElementsType(); + + assertFalse(typeA.isSubtype(typeB)); + assertFalse(typeB.isSubtype(typeA)); + + assertFalse(NUMBER_TYPE.isSubtype(typeB)); + assertFalse(NUMBER_TYPE.isSubtype(typeA)); + + assertTrue(typeA.isSubtype(NUMBER_TYPE)); + assertTrue(typeB.isSubtype(NUMBER_TYPE)); + } + + public void testMeet() throws Exception { + EnumElementType typeA = registry.createEnumType( + "typeA", null, createUnionType(NUMBER_TYPE, STRING_TYPE)) + .getElementsType(); + + JSType stringsOfA = typeA.getGreatestSubtype(STRING_TYPE); + assertFalse(stringsOfA.isEmptyType()); + assertEquals("typeA.", stringsOfA.toString()); + assertTrue(stringsOfA.isSubtype(typeA)); + + JSType numbersOfA = NUMBER_TYPE.getGreatestSubtype(typeA); + assertFalse(numbersOfA.isEmptyType()); + assertEquals("typeA.", numbersOfA.toString()); + assertTrue(numbersOfA.isSubtype(typeA)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/FunctionParamBuilderTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/FunctionParamBuilderTest.java new file mode 100644 index 0000000..ef826f4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/FunctionParamBuilderTest.java @@ -0,0 +1,67 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.testing.BaseJSTypeTestCase; + + +/** + * Tests for FunctionParamBuilder. + * @author nicksantos@google.com (Nick Santos) + */ +public class FunctionParamBuilderTest extends BaseJSTypeTestCase { + + public void testBuild() throws Exception { + FunctionParamBuilder builder = new FunctionParamBuilder(registry); + assertTrue(builder.addRequiredParams(NUMBER_TYPE)); + assertTrue(builder.addOptionalParams(BOOLEAN_TYPE)); + assertTrue(builder.addVarArgs(STRING_TYPE)); + + Node params = builder.build(); + assertTypeEquals(NUMBER_TYPE, params.getFirstChild().getJSType()); + assertTypeEquals(registry.createOptionalType(BOOLEAN_TYPE), + params.getFirstChild().getNext().getJSType()); + assertTypeEquals(registry.createOptionalType(STRING_TYPE), + params.getLastChild().getJSType()); + + assertTrue(params.getFirstChild().getNext().isOptionalArg()); + assertTrue(params.getLastChild().isVarArgs()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/FunctionTypeTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/FunctionTypeTest.java new file mode 100644 index 0000000..a8230d8 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/FunctionTypeTest.java @@ -0,0 +1,361 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.testing.Asserts; +import com.google.javascript.rhino.testing.BaseJSTypeTestCase; + +/** + * Tests for FunctionTypes. + * @author nicksantos@google.com (Nick Santos) + */ +public class FunctionTypeTest extends BaseJSTypeTestCase { + public void testDefaultReturnType() { + FunctionType f = new FunctionBuilder(registry).build(); + assertEquals(UNKNOWN_TYPE, f.getReturnType()); + } + + public void testSupAndInfOfReturnTypes() { + FunctionType retString = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters()) + .withInferredReturnType(STRING_TYPE).build(); + FunctionType retNumber = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters()) + .withReturnType(NUMBER_TYPE).build(); + + assertLeastSupertype( + "function (): (number|string)", retString, retNumber); + assertGreatestSubtype( + "function (): None", retString, retNumber); + + assertTrue(retString.isReturnTypeInferred()); + assertFalse(retNumber.isReturnTypeInferred()); + assertTrue( + ((FunctionType) retString.getLeastSupertype(retNumber)) + .isReturnTypeInferred()); + assertTrue( + ((FunctionType) retString.getGreatestSubtype(retString)) + .isReturnTypeInferred()); + } + + public void testSupAndInfOfReturnTypesWithDifferentParams() { + FunctionType retString = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters(NUMBER_TYPE)) + .withInferredReturnType(STRING_TYPE).build(); + FunctionType retNumber = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters()) + .withReturnType(NUMBER_TYPE).build(); + + assertLeastSupertype( + "Function", retString, retNumber); + assertGreatestSubtype( + "function (...[*]): None", retString, retNumber); + } + + public void testSupAndInfWithDifferentParams() { + FunctionType retString = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters(NUMBER_TYPE)) + .withReturnType(STRING_TYPE).build(); + FunctionType retNumber = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters(STRING_TYPE)) + .withReturnType(NUMBER_TYPE).build(); + + assertLeastSupertype( + "Function", retString, retNumber); + assertGreatestSubtype( + "function (...[*]): None", retString, retNumber); + } + + public void testSupAndInfWithDifferentThisTypes() { + FunctionType retString = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters()) + .withTypeOfThis(OBJECT_TYPE) + .withReturnType(STRING_TYPE).build(); + FunctionType retNumber = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters()) + .withTypeOfThis(DATE_TYPE) + .withReturnType(NUMBER_TYPE).build(); + + assertLeastSupertype( + "function (this:Object): (number|string)", retString, retNumber); + assertGreatestSubtype( + "function (this:Date): None", retString, retNumber); + } + + public void testSupAndInfWithDifferentThisTypes2() { + FunctionType retString = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters()) + .withTypeOfThis(ARRAY_TYPE) + .withReturnType(STRING_TYPE).build(); + FunctionType retNumber = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters()) + .withTypeOfThis(DATE_TYPE) + .withReturnType(NUMBER_TYPE).build(); + + assertLeastSupertype( + "function (this:(Array|Date)): (number|string)", retString, retNumber); + assertGreatestSubtype( + "function (this:NoObject): None", retString, retNumber); + } + + public void testSupAndInfOfReturnTypesWithNumOfParams() { + FunctionType twoNumbers = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters(NUMBER_TYPE, NUMBER_TYPE)) + .withReturnType(BOOLEAN_TYPE).build(); + FunctionType oneNumber = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters(NUMBER_TYPE)) + .withReturnType(BOOLEAN_TYPE).build(); + + assertLeastSupertype( + "function (number, number): boolean", twoNumbers, oneNumber); + assertGreatestSubtype( + "function (number): boolean", twoNumbers, oneNumber); + } + + public void testSubtypeWithInterfaceThisType() { + FunctionType iface = registry.createInterfaceType("I", null); + FunctionType ifaceReturnBoolean = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters()) + .withTypeOfThis(iface.getInstanceType()) + .withReturnType(BOOLEAN_TYPE).build(); + FunctionType objReturnBoolean = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters()) + .withTypeOfThis(OBJECT_TYPE) + .withReturnType(BOOLEAN_TYPE).build(); + assertTrue(objReturnBoolean.isSubtype(ifaceReturnBoolean)); + } + + public void testOrdinaryFunctionPrototype() { + FunctionType oneNumber = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters(NUMBER_TYPE)) + .withReturnType(BOOLEAN_TYPE).build(); + assertEquals(ImmutableSet.of(), oneNumber.getOwnPropertyNames()); + } + + public void testCtorWithPrototypeSet() { + FunctionType ctor = registry.createConstructorType( + "Foo", null, null, null, null); + assertFalse(ctor.getInstanceType().isUnknownType()); + + Node node = new Node(Token.OBJECTLIT); + ctor.defineDeclaredProperty("prototype", UNKNOWN_TYPE, node); + assertTrue(ctor.getInstanceType().isUnknownType()); + + assertEquals(ImmutableSet.of("prototype"), + ctor.getOwnPropertyNames()); + assertTrue(ctor.isPropertyTypeInferred("prototype")); + assertTrue(ctor.getPropertyType("prototype").isUnknownType()); + + assertEquals(node, ctor.getPropertyNode("prototype")); + } + + public void testEmptyFunctionTypes() { + assertTrue(LEAST_FUNCTION_TYPE.isEmptyType()); + assertFalse(GREATEST_FUNCTION_TYPE.isEmptyType()); + } + + public void testInterfacePrototypeChain1() { + FunctionType iface = registry.createInterfaceType("I", null); + assertTypeEquals( + iface.getPrototype(), + iface.getInstanceType().getImplicitPrototype()); + assertTypeEquals( + OBJECT_TYPE, + iface.getPrototype().getImplicitPrototype()); + } + + public void testInterfacePrototypeChain2() { + FunctionType iface = registry.createInterfaceType("I", null); + iface.getPrototype().defineDeclaredProperty( + "numberProp", NUMBER_TYPE, null); + + FunctionType subIface = registry.createInterfaceType("SubI", null); + subIface.setExtendedInterfaces( + Lists.newArrayList(iface.getInstanceType())); + assertTypeEquals( + subIface.getPrototype(), + subIface.getInstanceType().getImplicitPrototype()); + assertTypeEquals( + OBJECT_TYPE, + subIface.getPrototype().getImplicitPrototype()); + + ObjectType subIfaceInst = subIface.getInstanceType(); + assertTrue(subIfaceInst.hasProperty("numberProp")); + assertTrue(subIfaceInst.isPropertyTypeDeclared("numberProp")); + assertFalse(subIfaceInst.isPropertyTypeInferred("numberProp")); + } + + private void assertLeastSupertype(String s, JSType t1, JSType t2) { + assertEquals(s, t1.getLeastSupertype(t2).toString()); + assertEquals(s, t2.getLeastSupertype(t1).toString()); + } + + private void assertGreatestSubtype(String s, JSType t1, JSType t2) { + assertEquals(s, t1.getGreatestSubtype(t2).toString()); + assertEquals(s, t2.getGreatestSubtype(t1).toString()); + } + + public void testIsEquivalentTo() { + FunctionType type = new FunctionBuilder(registry).build(); + assertFalse(type.equals(null)); + assertTrue(type.isEquivalentTo(type)); + } + + public void testIsEquivalentToParams() { + FunctionType oneNum = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters(NUMBER_TYPE)) + .build(); + FunctionType optNum = new FunctionBuilder(registry) + .withParamsNode(registry.createOptionalParameters(NUMBER_TYPE)) + .build(); + FunctionType varNum = new FunctionBuilder(registry) + .withParamsNode(registry.createParametersWithVarArgs(NUMBER_TYPE)) + .build(); + Asserts.assertEquivalenceOperations(oneNum, oneNum); + Asserts.assertEquivalenceOperations(optNum, optNum); + Asserts.assertEquivalenceOperations(varNum, varNum); + assertFalse(oneNum.isEquivalentTo(optNum)); + assertFalse(oneNum.isEquivalentTo(varNum)); + assertFalse(optNum.isEquivalentTo(varNum)); + } + + public void testIsEquivalentOptAndVarArgs() { + FunctionType varNum = new FunctionBuilder(registry) + .withParamsNode(registry.createParametersWithVarArgs(NUMBER_TYPE)) + .build(); + + FunctionParamBuilder builder = new FunctionParamBuilder(registry); + builder.addOptionalParams(NUMBER_TYPE); + builder.addVarArgs(NUMBER_TYPE); + FunctionType optAndVarNum = new FunctionBuilder(registry) + .withParamsNode(builder.build()) + .build(); + + // We currently do not consider function(T=, ...T) and function(...T) + // equivalent. This may change. + assertFalse(varNum.isEquivalentTo(optAndVarNum)); + assertFalse(optAndVarNum.isEquivalentTo(varNum)); + } + + public void testRecursiveFunction() { + ProxyObjectType loop = new ProxyObjectType(registry, NUMBER_TYPE); + FunctionType fn = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters(loop)) + .withReturnType(loop).build(); + + loop.setReferencedType(fn); + assertEquals("function (Function): Function", fn.toString()); + + Asserts.assertEquivalenceOperations(fn, loop); + } + + public void testBindSignature() { + FunctionType fn = new FunctionBuilder(registry) + .withTypeOfThis(DATE_TYPE) + .withParamsNode(registry.createParameters(STRING_TYPE, NUMBER_TYPE)) + .withReturnType(BOOLEAN_TYPE).build(); + + assertEquals( + "function ((Date|null|undefined), string=, number=):" + + " function (...[?]): boolean", + fn.getPropertyType("bind").toString()); + } + + public void testCallSignature1() { + FunctionType fn = new FunctionBuilder(registry) + .withTypeOfThis(DATE_TYPE) + .withParamsNode(registry.createParameters(STRING_TYPE, NUMBER_TYPE)) + .withReturnType(BOOLEAN_TYPE).build(); + + assertEquals( + "function ((Date|null|undefined), string, number): boolean", + fn.getPropertyType("call").toString()); + } + + public void testCallSignature2() { + FunctionType fn = new FunctionBuilder(registry) + .withTypeOfThis(DATE_TYPE) + .withParamsNode(registry.createParameters()) + .withReturnType(BOOLEAN_TYPE).build(); + + assertEquals( + "function ((Date|null)=): boolean", + fn.getPropertyType("call").toString()); + } + + public void testTemplatedFunctionDerivedFunctions() { + FunctionType fn = new FunctionBuilder(registry) + .withTypeOfThis(new TemplateType(registry, "T")) + .withTemplateKeys(ImmutableList.of("T")) + .withReturnType(BOOLEAN_TYPE).build(); + + assertEquals("[T]", + fn.getPropertyType("call").getTemplateKeys().toString()); + assertEquals("[T]", + fn.getPropertyType("apply").getTemplateKeys().toString()); + assertEquals("[T]", + fn.getPropertyType("bind").getTemplateKeys().toString()); + assertEquals("[T]", + fn.getBindReturnType(0).getTemplateKeys().toString()); + } + + public void testPrint() { + FunctionType fn = new FunctionBuilder(registry) + .withTypeOfThis(new TemplateType(registry, "T")) + .withReturnType(BOOLEAN_TYPE).build(); + assertEquals("function (this:T, ...[?]): boolean", fn.toString()); + } + + public void testSetImplementsOnInterface() { + FunctionType iface = registry.createInterfaceType("I", null); + FunctionType subIface = registry.createInterfaceType("SubI", null); + try { + subIface.setImplementedInterfaces( + ImmutableList.of(iface.getInstanceType())); + fail("Expected exception"); + } catch (UnsupportedOperationException e) { + // OK + } + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/JSTypeRegistryTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/JSTypeRegistryTest.java new file mode 100644 index 0000000..76bbd9c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/JSTypeRegistryTest.java @@ -0,0 +1,275 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.SimpleErrorReporter; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.JSTypeRegistry.ResolveMode; +import com.google.javascript.rhino.testing.Asserts; +import com.google.javascript.rhino.testing.AbstractStaticScope; +import com.google.javascript.rhino.testing.MapBasedScope; + +import junit.framework.TestCase; + +/** + * Tests {@link JSTypeRegistry}. + * + */ +public class JSTypeRegistryTest extends TestCase { + // TODO(user): extend this class with more tests, as JSTypeRegistry is + // now much larger + public void testGetBuiltInType() { + JSTypeRegistry typeRegistry = new JSTypeRegistry(null); + assertTypeEquals(typeRegistry.getNativeType(JSTypeNative.BOOLEAN_TYPE), + typeRegistry.getType("boolean")); + } + + public void testGetDeclaredType() { + JSTypeRegistry typeRegistry = new JSTypeRegistry(null); + JSType type = typeRegistry.createAnonymousObjectType(null); + String name = "Foo"; + typeRegistry.declareType(name, type); + assertTypeEquals(type, typeRegistry.getType(name)); + + // Ensure different instances are independent. + JSTypeRegistry typeRegistry2 = new JSTypeRegistry(null); + assertEquals(null, typeRegistry2.getType(name)); + assertTypeEquals(type, typeRegistry.getType(name)); + } + + public void testGetDeclaredTypeInNamespace() { + JSTypeRegistry typeRegistry = new JSTypeRegistry(null); + JSType type = typeRegistry.createAnonymousObjectType(null); + String name = "a.b.Foo"; + typeRegistry.declareType(name, type); + assertTypeEquals(type, typeRegistry.getType(name)); + assertTrue(typeRegistry.hasNamespace("a")); + assertTrue(typeRegistry.hasNamespace("a.b")); + } + + public void testPropertyOnManyTypes() { + JSTypeRegistry typeRegistry = new JSTypeRegistry(null); + + JSType type = null; + + // By default the UnionTypeBuilder will treat a union of more than 20 + // types as an unknown type. We don't want that for property checking + // so test that the limit is higher. + for (int i = 0; i < 100; i++) { + type = typeRegistry.createObjectType("type: " + i, null, null); + typeRegistry.registerPropertyOnType("foo", type); + } + + assertFalse(typeRegistry.getGreatestSubtypeWithProperty(type, "foo").isUnknownType()); + } + + public void testTypeAsNamespace() { + JSTypeRegistry typeRegistry = new JSTypeRegistry(null); + + JSType type = typeRegistry.createAnonymousObjectType(null); + String name = "a.b.Foo"; + typeRegistry.declareType(name, type); + assertTypeEquals(type, typeRegistry.getType(name)); + + type = typeRegistry.createAnonymousObjectType(null); + name = "a.b.Foo.Bar"; + typeRegistry.declareType(name, type); + assertTypeEquals(type, typeRegistry.getType(name)); + + assertTrue(typeRegistry.hasNamespace("a")); + assertTrue(typeRegistry.hasNamespace("a.b")); + assertTrue(typeRegistry.hasNamespace("a.b.Foo")); + } + + public void testGenerationIncrementing1() { + SimpleErrorReporter reporter = new SimpleErrorReporter(); + final JSTypeRegistry typeRegistry = new JSTypeRegistry(reporter); + + StaticScope scope = new AbstractStaticScope() { + @Override + public StaticSlot getSlot(final String name) { + return new SimpleSlot( + name, + typeRegistry.getNativeType(JSTypeNative.UNKNOWN_TYPE), + false); + } + }; + + ObjectType namedType = + (ObjectType) typeRegistry.getType(scope, "Foo", null, 0, 0); + ObjectType subNamed = + typeRegistry.createObjectType(typeRegistry.createObjectType(namedType)); + + // Subclass of named type is initially unresolved. + typeRegistry.setLastGeneration(false); + typeRegistry.resolveTypesInScope(scope); + assertTrue(subNamed.isUnknownType()); + + // Subclass of named type is still unresolved, even though the named type is + // now present in the registry. + typeRegistry.declareType("Foo", + typeRegistry.createAnonymousObjectType(null)); + typeRegistry.resolveTypesInScope(scope); + assertTrue(subNamed.isUnknownType()); + + assertNull("Unexpected errors: " + reporter.errors(), + reporter.errors()); + assertNull("Unexpected warnings: " + reporter.warnings(), + reporter.warnings()); + + // After incrementing the generation, resolve works again. + typeRegistry.incrementGeneration(); + typeRegistry.setLastGeneration(true); + typeRegistry.resolveTypesInScope(scope); + assertFalse(subNamed.isUnknownType()); + } + + public void testGenerationIncrementing2() { + SimpleErrorReporter reporter = new SimpleErrorReporter(); + final JSTypeRegistry typeRegistry = new JSTypeRegistry(reporter); + + StaticScope scope = new AbstractStaticScope() { + @Override + public StaticSlot getSlot(final String name) { + return new SimpleSlot( + name, + typeRegistry.getNativeType(JSTypeNative.UNKNOWN_TYPE), + false); + } + }; + + ObjectType namedType = + (ObjectType) typeRegistry.getType(scope, "Foo", null, 0, 0); + FunctionType functionType = typeRegistry.createFunctionType(namedType); + + // Subclass of named type is initially unresolved. + typeRegistry.setLastGeneration(false); + typeRegistry.resolveTypesInScope(scope); + assertTrue(functionType.getReturnType().isUnknownType()); + functionType.resolve(reporter, scope); + assertTrue(functionType.getReturnType().isUnknownType()); + + // Subclass of named type is still unresolved, even though the named type is + // now present in the registry. + typeRegistry.declareType("Foo", + typeRegistry.createAnonymousObjectType(null)); + typeRegistry.resolveTypesInScope(scope); + assertTrue(functionType.getReturnType().isUnknownType()); + + assertNull("Unexpected errors: " + reporter.errors(), + reporter.errors()); + assertNull("Unexpected warnings: " + reporter.warnings(), + reporter.warnings()); + + // After incrementing the generation, resolve works again. + typeRegistry.incrementGeneration(); + typeRegistry.setLastGeneration(true); + typeRegistry.resolveTypesInScope(scope); + assertFalse(functionType.getReturnType().isUnknownType()); + } + + public void testTypeResolutionModes() { + SimpleErrorReporter reporter = new SimpleErrorReporter(); + + JSTypeRegistry lazyExprRegistry = new JSTypeRegistry(reporter); + lazyExprRegistry.setResolveMode(ResolveMode.LAZY_EXPRESSIONS); + + JSTypeRegistry lazyNameRegistry = new JSTypeRegistry(reporter); + lazyNameRegistry.setResolveMode(ResolveMode.LAZY_NAMES); + + JSTypeRegistry immediateRegistry = new JSTypeRegistry(reporter); + immediateRegistry.setResolveMode(ResolveMode.IMMEDIATE); + + Node expr = new Node(Token.QMARK, Node.newString("foo")); + StaticScope empty = MapBasedScope.emptyScope(); + + JSType type = lazyExprRegistry.createFromTypeNodes( + expr, "source.js", empty); + assertTrue(type instanceof UnresolvedTypeExpression); + assertTrue(type.isUnknownType()); + assertEquals("?", type.toString()); + assertNull("Unexpected warnings: " + reporter.warnings(), + reporter.warnings()); + + type = lazyNameRegistry.createFromTypeNodes( + expr, "source.js", empty); + assertTrue(type instanceof UnionType); + assertTrue(type.isUnknownType()); + assertEquals("(foo|null)", type.toString()); + assertNull("Unexpected warnings: " + reporter.warnings(), + reporter.warnings()); + + type = immediateRegistry.createFromTypeNodes( + expr, "source.js", empty); + assertTrue(type instanceof UnknownType); + assertEquals("Expected warnings", 1, reporter.warnings().size()); + } + + public void testForceResolve() { + SimpleErrorReporter reporter = new SimpleErrorReporter(); + + JSTypeRegistry lazyExprRegistry = new JSTypeRegistry(reporter); + lazyExprRegistry.setResolveMode(ResolveMode.LAZY_EXPRESSIONS); + + Node expr = new Node(Token.QMARK, Node.newString("foo")); + StaticScope empty = MapBasedScope.emptyScope(); + + JSType type = lazyExprRegistry.createFromTypeNodes( + expr, "source.js", empty); + assertFalse(type.isResolved()); + assertTrue(type.forceResolve(reporter, empty).isResolved()); + assertEquals("Expected warnings", 1, reporter.warnings().size()); + } + + public void testAllTypeResolvesImmediately() { + JSTypeRegistry lazyExprRegistry = new JSTypeRegistry( + new SimpleErrorReporter()); + lazyExprRegistry.setResolveMode(ResolveMode.LAZY_EXPRESSIONS); + + Node expr = new Node(Token.STAR); + JSType type = lazyExprRegistry.createFromTypeNodes( + expr, "source.js", MapBasedScope.emptyScope()); + assertTrue(type instanceof AllType); + } + + private void assertTypeEquals(JSType a, JSType b) { + Asserts.assertTypeEquals(a, b); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/JSTypeTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/JSTypeTest.java new file mode 100644 index 0000000..cbbc08f --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/JSTypeTest.java @@ -0,0 +1,6244 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; +import static com.google.javascript.rhino.jstype.TernaryValue.TRUE; +import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.javascript.rhino.JSDocInfo; +import com.google.javascript.rhino.JSDocInfo.Visibility; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.SimpleErrorReporter; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.ArrowType; +import com.google.javascript.rhino.jstype.JSType.TypePair; +import com.google.javascript.rhino.jstype.RecordTypeBuilder.RecordProperty; +import com.google.javascript.rhino.testing.Asserts; +import com.google.javascript.rhino.testing.AbstractStaticScope; +import com.google.javascript.rhino.testing.BaseJSTypeTestCase; +import com.google.javascript.rhino.testing.MapBasedScope; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +// TODO(nicksantos): Split some of this up into per-class unit tests. +public class JSTypeTest extends BaseJSTypeTestCase { + private FunctionType dateMethod; + private FunctionType functionType; + private NamedType unresolvedNamedType; + private FunctionType googBar; + private FunctionType googSubBar; + private FunctionType googSubSubBar; + private ObjectType googBarInst; + private ObjectType googSubBarInst; + private ObjectType googSubSubBarInst; + private NamedType namedGoogBar; + private ObjectType subclassOfUnresolvedNamedType; + private FunctionType subclassCtor; + private FunctionType interfaceType; + private ObjectType interfaceInstType; + private FunctionType subInterfaceType; + private ObjectType subInterfaceInstType; + private JSType recordType; + private EnumType enumType; + private EnumElementType elementsType; + private NamedType forwardDeclaredNamedType; + + private static final StaticScope EMPTY_SCOPE = + MapBasedScope.emptyScope(); + + /** + * A non exhaustive list of representative types used to test simple + * properties that should hold for all types (such as the reflexivity + * of subtyping). + */ + private List types; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.addProperty("a", NUMBER_TYPE, null); + builder.addProperty("b", STRING_TYPE, null); + recordType = builder.build(); + + enumType = new EnumType(registry, "Enum", null, NUMBER_TYPE); + elementsType = enumType.getElementsType(); + functionType = new FunctionBuilder(registry) + .withReturnType(NUMBER_TYPE) + .build(); + dateMethod = new FunctionBuilder(registry) + .withParamsNode(new Node(Token.PARAM_LIST)) + .withReturnType(NUMBER_TYPE) + .withTypeOfThis(DATE_TYPE) + .build(); + unresolvedNamedType = + new NamedType(registry, "not.resolved.named.type", null, -1, -1); + namedGoogBar = new NamedType(registry, "goog.Bar", null, -1, -1); + + subclassCtor = + new FunctionType(registry, null, null, createArrowType(null), + null, null, true, false); + subclassCtor.setPrototypeBasedOn(unresolvedNamedType); + subclassOfUnresolvedNamedType = subclassCtor.getInstanceType(); + + interfaceType = FunctionType.forInterface(registry, "Interface", null); + interfaceInstType = interfaceType.getInstanceType(); + + subInterfaceType = FunctionType.forInterface( + registry, "SubInterface", null); + subInterfaceType.setExtendedInterfaces( + Lists.newArrayList(interfaceInstType)); + subInterfaceInstType = subInterfaceType.getInstanceType(); + + googBar = registry.createConstructorType( + "goog.Bar", null, null, null, null); + googBar.getPrototype().defineDeclaredProperty("date", DATE_TYPE, + null); + googBar.setImplementedInterfaces( + Lists.newArrayList(interfaceInstType)); + googBarInst = googBar.getInstanceType(); + + googSubBar = registry.createConstructorType( + "googSubBar", null, null, null, null); + googSubBar.setPrototypeBasedOn(googBar.getInstanceType()); + googSubBarInst = googSubBar.getInstanceType(); + + googSubSubBar = registry.createConstructorType( + "googSubSubBar", null, null, null, null); + googSubSubBar.setPrototypeBasedOn(googSubBar.getInstanceType()); + googSubSubBarInst = googSubSubBar.getInstanceType(); + + final ObjectType googObject = registry.createAnonymousObjectType(null); + googObject.defineDeclaredProperty("Bar", googBar, null); + + namedGoogBar.resolve(null, new AbstractStaticScope() { + @Override + public StaticSlot getSlot(String name) { + if ("goog".equals(name)) { + return new SimpleSlot("goog", googObject, false); + } else { + return null; + } + } + }); + assertNotNull(namedGoogBar.getImplicitPrototype()); + + forwardDeclaredNamedType = + new NamedType(registry, "forwardDeclared", "source", 1, 0); + registry.forwardDeclareType("forwardDeclared"); + forwardDeclaredNamedType.resolve( + new SimpleErrorReporter(), EMPTY_SCOPE); + + types = ImmutableList.of( + NO_OBJECT_TYPE, + NO_RESOLVED_TYPE, + NO_TYPE, + BOOLEAN_OBJECT_TYPE, + BOOLEAN_TYPE, + STRING_OBJECT_TYPE, + STRING_TYPE, + VOID_TYPE, + UNKNOWN_TYPE, + NULL_TYPE, + NUMBER_OBJECT_TYPE, + NUMBER_TYPE, + DATE_TYPE, + ERROR_TYPE, + SYNTAX_ERROR_TYPE, + dateMethod, + functionType, + unresolvedNamedType, + googBar, + googSubBar, + googSubSubBar, + namedGoogBar, + googBar.getInstanceType(), + subclassOfUnresolvedNamedType, + subclassCtor, + recordType, + enumType, + elementsType, + googBar, + googSubBar, + forwardDeclaredNamedType); + } + + /** + * Tests the behavior of the top constructor type. + */ + public void testUniversalConstructorType() throws Exception { + // isXxx + assertFalse(U2U_CONSTRUCTOR_TYPE.isNoObjectType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isNoType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isArrayType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isBooleanValueType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isDateType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isEnumElementType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isNullType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isNamedType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isNullType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isNumber()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isNumberObjectType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isNumberValueType()); + assertTrue(U2U_CONSTRUCTOR_TYPE.isObject()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isFunctionPrototypeType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isRegexpType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isString()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isStringObjectType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isStringValueType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isEnumType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isUnionType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isStruct()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isDict()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isAllType()); + assertFalse(U2U_CONSTRUCTOR_TYPE.isVoidType()); + assertTrue(U2U_CONSTRUCTOR_TYPE.isConstructor()); + assertTrue(U2U_CONSTRUCTOR_TYPE.isInstanceType()); + + // isSubtype + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(NO_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(ARRAY_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(BOOLEAN_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(BOOLEAN_OBJECT_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(DATE_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(ERROR_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(EVAL_ERROR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(functionType)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(recordType)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(NULL_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(NUMBER_OBJECT_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(URI_ERROR_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(RANGE_ERROR_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(REFERENCE_ERROR_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(STRING_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(SYNTAX_ERROR_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(TYPE_ERROR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(ALL_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE.isSubtype(VOID_TYPE)); + + // canTestForEqualityWith + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(NO_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(NO_OBJECT_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(ALL_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(ARRAY_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(BOOLEAN_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(DATE_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(ERROR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(EVAL_ERROR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(functionType)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(recordType)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(NULL_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(NUMBER_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(OBJECT_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(URI_ERROR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(RANGE_ERROR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(REFERENCE_ERROR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(REGEXP_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(STRING_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(STRING_OBJECT_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(SYNTAX_ERROR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(TYPE_ERROR_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForEqualityWith(VOID_TYPE)); + + // canTestForShallowEqualityWith + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(NO_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(functionType)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(recordType)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(NUMBER_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(STRING_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(U2U_CONSTRUCTOR_TYPE. + canTestForShallowEqualityWith(VOID_TYPE)); + + // isNullable + assertFalse(U2U_CONSTRUCTOR_TYPE.isNullable()); + + // isObject + assertTrue(U2U_CONSTRUCTOR_TYPE.isObject()); + + // matchesXxx + assertFalse(U2U_CONSTRUCTOR_TYPE.matchesInt32Context()); + assertFalse(U2U_CONSTRUCTOR_TYPE.matchesNumberContext()); + assertTrue(U2U_CONSTRUCTOR_TYPE.matchesObjectContext()); + assertFalse(U2U_CONSTRUCTOR_TYPE.matchesStringContext()); + assertFalse(U2U_CONSTRUCTOR_TYPE.matchesUint32Context()); + + // toString + assertEquals("Function", + U2U_CONSTRUCTOR_TYPE.toString()); + assertTrue(U2U_CONSTRUCTOR_TYPE.hasDisplayName()); + assertEquals("Function", U2U_CONSTRUCTOR_TYPE.getDisplayName()); + + // getPropertyType + assertTypeEquals(UNKNOWN_TYPE, + U2U_CONSTRUCTOR_TYPE.getPropertyType("anyProperty")); + + assertTrue(U2U_CONSTRUCTOR_TYPE.isNativeObjectType()); + + Asserts.assertResolvesToSame(U2U_CONSTRUCTOR_TYPE); + + assertTrue(U2U_CONSTRUCTOR_TYPE.isNominalConstructor()); + } + + /** + * Tests the behavior of the Bottom Object type. + */ + public void testNoObjectType() throws Exception { + // isXxx + assertTrue(NO_OBJECT_TYPE.isNoObjectType()); + assertFalse(NO_OBJECT_TYPE.isNoType()); + assertFalse(NO_OBJECT_TYPE.isArrayType()); + assertFalse(NO_OBJECT_TYPE.isBooleanValueType()); + assertFalse(NO_OBJECT_TYPE.isDateType()); + assertFalse(NO_OBJECT_TYPE.isEnumElementType()); + assertFalse(NO_OBJECT_TYPE.isNullType()); + assertFalse(NO_OBJECT_TYPE.isNamedType()); + assertFalse(NO_OBJECT_TYPE.isNullType()); + assertTrue(NO_OBJECT_TYPE.isNumber()); + assertFalse(NO_OBJECT_TYPE.isNumberObjectType()); + assertFalse(NO_OBJECT_TYPE.isNumberValueType()); + assertTrue(NO_OBJECT_TYPE.isObject()); + assertFalse(NO_OBJECT_TYPE.isFunctionPrototypeType()); + assertFalse(NO_OBJECT_TYPE.isRegexpType()); + assertTrue(NO_OBJECT_TYPE.isString()); + assertFalse(NO_OBJECT_TYPE.isStringObjectType()); + assertFalse(NO_OBJECT_TYPE.isStringValueType()); + assertFalse(NO_OBJECT_TYPE.isEnumType()); + assertFalse(NO_OBJECT_TYPE.isUnionType()); + assertFalse(NO_OBJECT_TYPE.isStruct()); + assertFalse(NO_OBJECT_TYPE.isDict()); + assertFalse(NO_OBJECT_TYPE.isAllType()); + assertFalse(NO_OBJECT_TYPE.isVoidType()); + assertTrue(NO_OBJECT_TYPE.isConstructor()); + assertFalse(NO_OBJECT_TYPE.isInstanceType()); + + // isSubtype + assertFalse(NO_OBJECT_TYPE.isSubtype(NO_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(ARRAY_TYPE)); + assertFalse(NO_OBJECT_TYPE.isSubtype(BOOLEAN_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(BOOLEAN_OBJECT_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(DATE_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(EVAL_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(functionType)); + assertTrue(NO_OBJECT_TYPE.isSubtype(recordType)); + assertFalse(NO_OBJECT_TYPE.isSubtype(NULL_TYPE)); + assertFalse(NO_OBJECT_TYPE.isSubtype(NUMBER_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(NUMBER_OBJECT_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(OBJECT_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(URI_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(RANGE_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(REFERENCE_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(NO_OBJECT_TYPE.isSubtype(STRING_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(SYNTAX_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(TYPE_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(ALL_TYPE)); + assertFalse(NO_OBJECT_TYPE.isSubtype(VOID_TYPE)); + + // canTestForEqualityWith + assertCannotTestForEqualityWith(NO_OBJECT_TYPE, NO_TYPE); + assertCannotTestForEqualityWith(NO_OBJECT_TYPE, NO_OBJECT_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, ALL_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, ARRAY_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, BOOLEAN_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, BOOLEAN_OBJECT_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, DATE_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, ERROR_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, EVAL_ERROR_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, functionType); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, recordType); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, NULL_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, NUMBER_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, NUMBER_OBJECT_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, OBJECT_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, URI_ERROR_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, RANGE_ERROR_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, REFERENCE_ERROR_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, REGEXP_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, STRING_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, SYNTAX_ERROR_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, TYPE_ERROR_TYPE); + assertCanTestForEqualityWith(NO_OBJECT_TYPE, VOID_TYPE); + + // canTestForShallowEqualityWith + assertTrue(NO_OBJECT_TYPE.canTestForShallowEqualityWith(NO_TYPE)); + assertTrue(NO_OBJECT_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertTrue(NO_OBJECT_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(NO_OBJECT_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertTrue(NO_OBJECT_TYPE. + canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertTrue(NO_OBJECT_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertTrue(NO_OBJECT_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.canTestForShallowEqualityWith(functionType)); + assertTrue(NO_OBJECT_TYPE.canTestForShallowEqualityWith(recordType)); + assertFalse(NO_OBJECT_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(NO_OBJECT_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertTrue(NO_OBJECT_TYPE. + canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(NO_OBJECT_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertTrue(NO_OBJECT_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(NO_OBJECT_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertTrue(NO_OBJECT_TYPE. + canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertTrue(NO_OBJECT_TYPE. + canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(NO_OBJECT_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + + // isNullable + assertFalse(NO_OBJECT_TYPE.isNullable()); + + // isObject + assertTrue(NO_OBJECT_TYPE.isObject()); + + // matchesXxx + assertTrue(NO_OBJECT_TYPE.matchesInt32Context()); + assertTrue(NO_OBJECT_TYPE.matchesNumberContext()); + assertTrue(NO_OBJECT_TYPE.matchesObjectContext()); + assertTrue(NO_OBJECT_TYPE.matchesStringContext()); + assertTrue(NO_OBJECT_TYPE.matchesUint32Context()); + + // toString + assertEquals("NoObject", NO_OBJECT_TYPE.toString()); + assertFalse(NO_OBJECT_TYPE.hasDisplayName()); + assertEquals(null, NO_OBJECT_TYPE.getDisplayName()); + + + // getPropertyType + assertTypeEquals(NO_TYPE, + NO_OBJECT_TYPE.getPropertyType("anyProperty")); + + Asserts.assertResolvesToSame(NO_OBJECT_TYPE); + + assertFalse(NO_OBJECT_TYPE.isNominalConstructor()); + } + + /** + * Tests the behavior of the Bottom type. + */ + public void testNoType() throws Exception { + // isXxx + assertFalse(NO_TYPE.isNoObjectType()); + assertTrue(NO_TYPE.isNoType()); + assertFalse(NO_TYPE.isArrayType()); + assertFalse(NO_TYPE.isBooleanValueType()); + assertFalse(NO_TYPE.isDateType()); + assertFalse(NO_TYPE.isEnumElementType()); + assertFalse(NO_TYPE.isNullType()); + assertFalse(NO_TYPE.isNamedType()); + assertFalse(NO_TYPE.isNullType()); + assertTrue(NO_TYPE.isNumber()); + assertFalse(NO_TYPE.isNumberObjectType()); + assertFalse(NO_TYPE.isNumberValueType()); + assertTrue(NO_TYPE.isObject()); + assertFalse(NO_TYPE.isFunctionPrototypeType()); + assertFalse(NO_TYPE.isRegexpType()); + assertTrue(NO_TYPE.isString()); + assertFalse(NO_TYPE.isStringObjectType()); + assertFalse(NO_TYPE.isStringValueType()); + assertFalse(NO_TYPE.isEnumType()); + assertFalse(NO_TYPE.isUnionType()); + assertFalse(NO_TYPE.isStruct()); + assertFalse(NO_TYPE.isDict()); + assertFalse(NO_TYPE.isAllType()); + assertFalse(NO_TYPE.isVoidType()); + assertTrue(NO_TYPE.isConstructor()); + assertFalse(NO_TYPE.isInstanceType()); + + // isSubtype + assertTrue(NO_TYPE.isSubtype(NO_TYPE)); + assertTrue(NO_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertTrue(NO_TYPE.isSubtype(ARRAY_TYPE)); + assertTrue(NO_TYPE.isSubtype(BOOLEAN_TYPE)); + assertTrue(NO_TYPE.isSubtype(BOOLEAN_OBJECT_TYPE)); + assertTrue(NO_TYPE.isSubtype(DATE_TYPE)); + assertTrue(NO_TYPE.isSubtype(ERROR_TYPE)); + assertTrue(NO_TYPE.isSubtype(EVAL_ERROR_TYPE)); + assertTrue(NO_TYPE.isSubtype(functionType)); + assertTrue(NO_TYPE.isSubtype(NULL_TYPE)); + assertTrue(NO_TYPE.isSubtype(NUMBER_TYPE)); + assertTrue(NO_TYPE.isSubtype(NUMBER_OBJECT_TYPE)); + assertTrue(NO_TYPE.isSubtype(OBJECT_TYPE)); + assertTrue(NO_TYPE.isSubtype(URI_ERROR_TYPE)); + assertTrue(NO_TYPE.isSubtype(RANGE_ERROR_TYPE)); + assertTrue(NO_TYPE.isSubtype(REFERENCE_ERROR_TYPE)); + assertTrue(NO_TYPE.isSubtype(REGEXP_TYPE)); + assertTrue(NO_TYPE.isSubtype(STRING_TYPE)); + assertTrue(NO_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertTrue(NO_TYPE.isSubtype(SYNTAX_ERROR_TYPE)); + assertTrue(NO_TYPE.isSubtype(TYPE_ERROR_TYPE)); + assertTrue(NO_TYPE.isSubtype(ALL_TYPE)); + assertTrue(NO_TYPE.isSubtype(VOID_TYPE)); + + // canTestForEqualityWith + assertCannotTestForEqualityWith(NO_TYPE, NO_TYPE); + assertCannotTestForEqualityWith(NO_TYPE, NO_OBJECT_TYPE); + assertCanTestForEqualityWith(NO_TYPE, ARRAY_TYPE); + assertCanTestForEqualityWith(NO_TYPE, BOOLEAN_TYPE); + assertCanTestForEqualityWith(NO_TYPE, BOOLEAN_OBJECT_TYPE); + assertCanTestForEqualityWith(NO_TYPE, DATE_TYPE); + assertCanTestForEqualityWith(NO_TYPE, ERROR_TYPE); + assertCanTestForEqualityWith(NO_TYPE, EVAL_ERROR_TYPE); + assertCanTestForEqualityWith(NO_TYPE, functionType); + assertCanTestForEqualityWith(NO_TYPE, NULL_TYPE); + assertCanTestForEqualityWith(NO_TYPE, NUMBER_TYPE); + assertCanTestForEqualityWith(NO_TYPE, NUMBER_OBJECT_TYPE); + assertCanTestForEqualityWith(NO_TYPE, OBJECT_TYPE); + assertCanTestForEqualityWith(NO_TYPE, URI_ERROR_TYPE); + assertCanTestForEqualityWith(NO_TYPE, RANGE_ERROR_TYPE); + assertCanTestForEqualityWith(NO_TYPE, REFERENCE_ERROR_TYPE); + assertCanTestForEqualityWith(NO_TYPE, REGEXP_TYPE); + assertCanTestForEqualityWith(NO_TYPE, STRING_TYPE); + assertCanTestForEqualityWith(NO_TYPE, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(NO_TYPE, SYNTAX_ERROR_TYPE); + assertCanTestForEqualityWith(NO_TYPE, TYPE_ERROR_TYPE); + assertCanTestForEqualityWith(NO_TYPE, ALL_TYPE); + assertCanTestForEqualityWith(NO_TYPE, VOID_TYPE); + + // canTestForShallowEqualityWith + assertTrue(NO_TYPE.canTestForShallowEqualityWith(NO_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(functionType)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); + assertTrue(NO_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + + // isNullable + assertTrue(NO_TYPE.isNullable()); + + // isObject + assertTrue(NO_TYPE.isObject()); + + // matchesXxx + assertTrue(NO_TYPE.matchesInt32Context()); + assertTrue(NO_TYPE.matchesNumberContext()); + assertTrue(NO_TYPE.matchesObjectContext()); + assertTrue(NO_TYPE.matchesStringContext()); + assertTrue(NO_TYPE.matchesUint32Context()); + + // toString + assertEquals("None", NO_TYPE.toString()); + assertEquals(null, NO_TYPE.getDisplayName()); + assertFalse(NO_TYPE.hasDisplayName()); + + // getPropertyType + assertTypeEquals(NO_TYPE, + NO_TYPE.getPropertyType("anyProperty")); + + Asserts.assertResolvesToSame(NO_TYPE); + + assertFalse(NO_TYPE.isNominalConstructor()); + } + + /** + * Tests the behavior of the unresolved Bottom type. + */ + public void testNoResolvedType() throws Exception { + // isXxx + assertFalse(NO_RESOLVED_TYPE.isNoObjectType()); + assertFalse(NO_RESOLVED_TYPE.isNoType()); + assertTrue(NO_RESOLVED_TYPE.isNoResolvedType()); + assertFalse(NO_RESOLVED_TYPE.isArrayType()); + assertFalse(NO_RESOLVED_TYPE.isBooleanValueType()); + assertFalse(NO_RESOLVED_TYPE.isDateType()); + assertFalse(NO_RESOLVED_TYPE.isEnumElementType()); + assertFalse(NO_RESOLVED_TYPE.isNullType()); + assertFalse(NO_RESOLVED_TYPE.isNamedType()); + assertTrue(NO_RESOLVED_TYPE.isNumber()); + assertFalse(NO_RESOLVED_TYPE.isNumberObjectType()); + assertFalse(NO_RESOLVED_TYPE.isNumberValueType()); + assertTrue(NO_RESOLVED_TYPE.isObject()); + assertFalse(NO_RESOLVED_TYPE.isFunctionPrototypeType()); + assertFalse(NO_RESOLVED_TYPE.isRegexpType()); + assertTrue(NO_RESOLVED_TYPE.isString()); + assertFalse(NO_RESOLVED_TYPE.isStringObjectType()); + assertFalse(NO_RESOLVED_TYPE.isStringValueType()); + assertFalse(NO_RESOLVED_TYPE.isEnumType()); + assertFalse(NO_RESOLVED_TYPE.isUnionType()); + assertFalse(NO_RESOLVED_TYPE.isStruct()); + assertFalse(NO_RESOLVED_TYPE.isDict()); + assertFalse(NO_RESOLVED_TYPE.isAllType()); + assertFalse(NO_RESOLVED_TYPE.isVoidType()); + assertTrue(NO_RESOLVED_TYPE.isConstructor()); + assertFalse(NO_RESOLVED_TYPE.isInstanceType()); + + // isSubtype + assertTrue(NO_RESOLVED_TYPE.isSubtype(NO_RESOLVED_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(ARRAY_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(BOOLEAN_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(BOOLEAN_OBJECT_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(DATE_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(ERROR_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(EVAL_ERROR_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(functionType)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(NULL_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(NUMBER_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(NUMBER_OBJECT_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(OBJECT_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(URI_ERROR_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(RANGE_ERROR_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(REFERENCE_ERROR_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(REGEXP_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(STRING_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(SYNTAX_ERROR_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(TYPE_ERROR_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(ALL_TYPE)); + assertTrue(NO_RESOLVED_TYPE.isSubtype(VOID_TYPE)); + + // canTestForEqualityWith + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, NO_RESOLVED_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, NO_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, NO_OBJECT_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, ARRAY_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, BOOLEAN_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, BOOLEAN_OBJECT_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, DATE_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, ERROR_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, EVAL_ERROR_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, functionType); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, NULL_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, NUMBER_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, NUMBER_OBJECT_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, OBJECT_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, URI_ERROR_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, RANGE_ERROR_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, REFERENCE_ERROR_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, REGEXP_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, STRING_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, SYNTAX_ERROR_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, TYPE_ERROR_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, ALL_TYPE); + assertCanTestForEqualityWith(NO_RESOLVED_TYPE, VOID_TYPE); + + // canTestForShallowEqualityWith + assertTrue( + NO_RESOLVED_TYPE.canTestForShallowEqualityWith(NO_RESOLVED_TYPE)); + assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertTrue( + NO_RESOLVED_TYPE.canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); + assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(functionType)); + assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); + assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertTrue( + NO_RESOLVED_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertTrue( + NO_RESOLVED_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertTrue( + NO_RESOLVED_TYPE.canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertTrue( + NO_RESOLVED_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertTrue( + NO_RESOLVED_TYPE.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); + assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + + // isNullable + assertTrue(NO_RESOLVED_TYPE.isNullable()); + + // isObject + assertTrue(NO_RESOLVED_TYPE.isObject()); + + // matchesXxx + assertTrue(NO_RESOLVED_TYPE.matchesInt32Context()); + assertTrue(NO_RESOLVED_TYPE.matchesNumberContext()); + assertTrue(NO_RESOLVED_TYPE.matchesObjectContext()); + assertTrue(NO_RESOLVED_TYPE.matchesStringContext()); + assertTrue(NO_RESOLVED_TYPE.matchesUint32Context()); + + // toString + assertEquals("NoResolvedType", NO_RESOLVED_TYPE.toString()); + assertEquals(null, NO_RESOLVED_TYPE.getDisplayName()); + assertFalse(NO_RESOLVED_TYPE.hasDisplayName()); + + // getPropertyType + assertTypeEquals(CHECKED_UNKNOWN_TYPE, + NO_RESOLVED_TYPE.getPropertyType("anyProperty")); + + Asserts.assertResolvesToSame(NO_RESOLVED_TYPE); + + assertTrue(forwardDeclaredNamedType.isEmptyType()); + assertTrue(forwardDeclaredNamedType.isNoResolvedType()); + + UnionType nullable = + (UnionType) registry.createNullableType(NO_RESOLVED_TYPE); + assertTypeEquals( + nullable, nullable.getGreatestSubtype(NULL_TYPE)); + assertTypeEquals(NO_RESOLVED_TYPE, nullable.getRestrictedUnion(NULL_TYPE)); + } + + /** + * Tests the behavior of the Array type. + */ + public void testArrayType() throws Exception { + // isXxx + assertTrue(ARRAY_TYPE.isArrayType()); + assertFalse(ARRAY_TYPE.isBooleanValueType()); + assertFalse(ARRAY_TYPE.isDateType()); + assertFalse(ARRAY_TYPE.isEnumElementType()); + assertFalse(ARRAY_TYPE.isNamedType()); + assertFalse(ARRAY_TYPE.isNullType()); + assertFalse(ARRAY_TYPE.isNumber()); + assertFalse(ARRAY_TYPE.isNumberObjectType()); + assertFalse(ARRAY_TYPE.isNumberValueType()); + assertTrue(ARRAY_TYPE.isObject()); + assertFalse(ARRAY_TYPE.isFunctionPrototypeType()); + assertTrue(ARRAY_TYPE.getImplicitPrototype().isFunctionPrototypeType()); + assertFalse(ARRAY_TYPE.isRegexpType()); + assertFalse(ARRAY_TYPE.isString()); + assertFalse(ARRAY_TYPE.isStringObjectType()); + assertFalse(ARRAY_TYPE.isStringValueType()); + assertFalse(ARRAY_TYPE.isEnumType()); + assertFalse(ARRAY_TYPE.isUnionType()); + assertFalse(ARRAY_TYPE.isStruct()); + assertFalse(ARRAY_TYPE.isDict()); + assertFalse(ARRAY_TYPE.isAllType()); + assertFalse(ARRAY_TYPE.isVoidType()); + assertFalse(ARRAY_TYPE.isConstructor()); + assertTrue(ARRAY_TYPE.isInstanceType()); + + // isSubtype + assertFalse(ARRAY_TYPE.isSubtype(NO_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertTrue(ARRAY_TYPE.isSubtype(ALL_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(functionType)); + assertFalse(ARRAY_TYPE.isSubtype(recordType)); + assertFalse(ARRAY_TYPE.isSubtype(NULL_TYPE)); + assertTrue(ARRAY_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(DATE_TYPE)); + assertTrue(ARRAY_TYPE.isSubtype(unresolvedNamedType)); + assertFalse(ARRAY_TYPE.isSubtype(namedGoogBar)); + assertFalse(ARRAY_TYPE.isSubtype(REGEXP_TYPE)); + + // canBeCalled + assertFalse(ARRAY_TYPE.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(ARRAY_TYPE, NO_TYPE); + assertCanTestForEqualityWith(ARRAY_TYPE, NO_OBJECT_TYPE); + assertCanTestForEqualityWith(ARRAY_TYPE, ALL_TYPE); + assertCanTestForEqualityWith(ARRAY_TYPE, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(ARRAY_TYPE, NUMBER_TYPE); + assertCanTestForEqualityWith(ARRAY_TYPE, functionType); + assertCanTestForEqualityWith(ARRAY_TYPE, recordType); + assertCannotTestForEqualityWith(ARRAY_TYPE, VOID_TYPE); + assertCanTestForEqualityWith(ARRAY_TYPE, OBJECT_TYPE); + assertCanTestForEqualityWith(ARRAY_TYPE, DATE_TYPE); + assertCanTestForEqualityWith(ARRAY_TYPE, REGEXP_TYPE); + + // canTestForShallowEqualityWith + assertTrue(ARRAY_TYPE.canTestForShallowEqualityWith(NO_TYPE)); + assertTrue(ARRAY_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertTrue(ARRAY_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(functionType)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(recordType)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(ARRAY_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(ARRAY_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(ARRAY_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + + // isNullable + assertFalse(ARRAY_TYPE.isNullable()); + assertTrue(createUnionType(ARRAY_TYPE, NULL_TYPE).isNullable()); + + // isObject + assertTrue(ARRAY_TYPE.isObject()); + + // getLeastSupertype + assertTypeEquals(ALL_TYPE, + ARRAY_TYPE.getLeastSupertype(ALL_TYPE)); + assertTypeEquals(createUnionType(STRING_OBJECT_TYPE, ARRAY_TYPE), + ARRAY_TYPE.getLeastSupertype(STRING_OBJECT_TYPE)); + assertTypeEquals(createUnionType(NUMBER_TYPE, ARRAY_TYPE), + ARRAY_TYPE.getLeastSupertype(NUMBER_TYPE)); + assertTypeEquals(createUnionType(ARRAY_TYPE, functionType), + ARRAY_TYPE.getLeastSupertype(functionType)); + assertTypeEquals(OBJECT_TYPE, ARRAY_TYPE.getLeastSupertype(OBJECT_TYPE)); + assertTypeEquals(createUnionType(DATE_TYPE, ARRAY_TYPE), + ARRAY_TYPE.getLeastSupertype(DATE_TYPE)); + assertTypeEquals(createUnionType(REGEXP_TYPE, ARRAY_TYPE), + ARRAY_TYPE.getLeastSupertype(REGEXP_TYPE)); + + // getPropertyType + assertEquals(17, ARRAY_TYPE.getImplicitPrototype().getPropertiesCount()); + assertEquals(18, ARRAY_TYPE.getPropertiesCount()); + assertReturnTypeEquals(ARRAY_TYPE, + ARRAY_TYPE.getPropertyType("constructor")); + assertReturnTypeEquals(STRING_TYPE, + ARRAY_TYPE.getPropertyType("toString")); + assertReturnTypeEquals(STRING_TYPE, + ARRAY_TYPE.getPropertyType("toLocaleString")); + assertReturnTypeEquals(ARRAY_TYPE, ARRAY_TYPE.getPropertyType("concat")); + assertReturnTypeEquals(STRING_TYPE, + ARRAY_TYPE.getPropertyType("join")); + assertReturnTypeEquals(UNKNOWN_TYPE, ARRAY_TYPE.getPropertyType("pop")); + assertReturnTypeEquals(NUMBER_TYPE, ARRAY_TYPE.getPropertyType("push")); + assertReturnTypeEquals(ARRAY_TYPE, ARRAY_TYPE.getPropertyType("reverse")); + assertReturnTypeEquals(UNKNOWN_TYPE, ARRAY_TYPE.getPropertyType("shift")); + assertReturnTypeEquals(ARRAY_TYPE, ARRAY_TYPE.getPropertyType("slice")); + assertReturnTypeEquals(ARRAY_TYPE, ARRAY_TYPE.getPropertyType("sort")); + assertReturnTypeEquals(ARRAY_TYPE, ARRAY_TYPE.getPropertyType("splice")); + assertReturnTypeEquals(NUMBER_TYPE, ARRAY_TYPE.getPropertyType("unshift")); + assertTypeEquals(NUMBER_TYPE, ARRAY_TYPE.getPropertyType("length")); + + // isPropertyType* + assertPropertyTypeDeclared(ARRAY_TYPE, "pop"); + + // matchesXxx + assertFalse(ARRAY_TYPE.matchesInt32Context()); + assertFalse(ARRAY_TYPE.matchesNumberContext()); + assertTrue(ARRAY_TYPE.matchesObjectContext()); + assertTrue(ARRAY_TYPE.matchesStringContext()); + assertFalse(ARRAY_TYPE.matchesUint32Context()); + + // toString + assertEquals("Array", ARRAY_TYPE.toString()); + assertTrue(ARRAY_TYPE.hasDisplayName()); + assertEquals("Array", ARRAY_TYPE.getDisplayName()); + + assertTrue(ARRAY_TYPE.isNativeObjectType()); + + Asserts.assertResolvesToSame(ARRAY_TYPE); + + assertFalse(ARRAY_TYPE.isNominalConstructor()); + assertTrue(ARRAY_TYPE.getConstructor().isNominalConstructor()); + } + + /** + * Tests the behavior of the unknown type. + */ + public void testUnknownType() throws Exception { + // isXxx + assertFalse(UNKNOWN_TYPE.isArrayType()); + assertFalse(UNKNOWN_TYPE.isBooleanObjectType()); + assertFalse(UNKNOWN_TYPE.isBooleanValueType()); + assertFalse(UNKNOWN_TYPE.isDateType()); + assertFalse(UNKNOWN_TYPE.isEnumElementType()); + assertFalse(UNKNOWN_TYPE.isNamedType()); + assertFalse(UNKNOWN_TYPE.isNullType()); + assertFalse(UNKNOWN_TYPE.isNumberObjectType()); + assertFalse(UNKNOWN_TYPE.isNumberValueType()); + assertTrue(UNKNOWN_TYPE.isObject()); + assertFalse(UNKNOWN_TYPE.isFunctionPrototypeType()); + assertFalse(UNKNOWN_TYPE.isRegexpType()); + assertFalse(UNKNOWN_TYPE.isStringObjectType()); + assertFalse(UNKNOWN_TYPE.isStringValueType()); + assertFalse(UNKNOWN_TYPE.isEnumType()); + assertFalse(UNKNOWN_TYPE.isUnionType()); + assertFalse(UNKNOWN_TYPE.isStruct()); + assertFalse(UNKNOWN_TYPE.isDict()); + assertTrue(UNKNOWN_TYPE.isUnknownType()); + assertFalse(UNKNOWN_TYPE.isVoidType()); + assertFalse(UNKNOWN_TYPE.isConstructor()); + assertFalse(UNKNOWN_TYPE.isInstanceType()); + + // autoboxesTo + assertNull(UNKNOWN_TYPE.autoboxesTo()); + + // isSubtype + assertTrue(UNKNOWN_TYPE.isSubtype(UNKNOWN_TYPE)); + assertTrue(UNKNOWN_TYPE.isSubtype(STRING_TYPE)); + assertTrue(UNKNOWN_TYPE.isSubtype(NUMBER_TYPE)); + assertTrue(UNKNOWN_TYPE.isSubtype(functionType)); + assertTrue(UNKNOWN_TYPE.isSubtype(recordType)); + assertTrue(UNKNOWN_TYPE.isSubtype(NULL_TYPE)); + assertTrue(UNKNOWN_TYPE.isSubtype(OBJECT_TYPE)); + assertTrue(UNKNOWN_TYPE.isSubtype(DATE_TYPE)); + assertTrue(UNKNOWN_TYPE.isSubtype(namedGoogBar)); + assertTrue(UNKNOWN_TYPE.isSubtype(unresolvedNamedType)); + assertTrue(UNKNOWN_TYPE.isSubtype(REGEXP_TYPE)); + assertTrue(UNKNOWN_TYPE.isSubtype(VOID_TYPE)); + + // canBeCalled + assertTrue(UNKNOWN_TYPE.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(UNKNOWN_TYPE, UNKNOWN_TYPE); + assertCanTestForEqualityWith(UNKNOWN_TYPE, STRING_TYPE); + assertCanTestForEqualityWith(UNKNOWN_TYPE, NUMBER_TYPE); + assertCanTestForEqualityWith(UNKNOWN_TYPE, functionType); + assertCanTestForEqualityWith(UNKNOWN_TYPE, recordType); + assertCanTestForEqualityWith(UNKNOWN_TYPE, VOID_TYPE); + assertCanTestForEqualityWith(UNKNOWN_TYPE, OBJECT_TYPE); + assertCanTestForEqualityWith(UNKNOWN_TYPE, DATE_TYPE); + assertCanTestForEqualityWith(UNKNOWN_TYPE, REGEXP_TYPE); + assertCanTestForEqualityWith(UNKNOWN_TYPE, BOOLEAN_TYPE); + + // canTestForShallowEqualityWith + assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(UNKNOWN_TYPE)); + assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(functionType)); + assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(recordType)); + assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + + // canHaveNullValue + assertTrue(UNKNOWN_TYPE.isNullable()); + + // getGreatestCommonType + assertTypeEquals(UNKNOWN_TYPE, + UNKNOWN_TYPE.getLeastSupertype(UNKNOWN_TYPE)); + assertTypeEquals(UNKNOWN_TYPE, + UNKNOWN_TYPE.getLeastSupertype(STRING_TYPE)); + assertTypeEquals(UNKNOWN_TYPE, + UNKNOWN_TYPE.getLeastSupertype(NUMBER_TYPE)); + assertTypeEquals(UNKNOWN_TYPE, + UNKNOWN_TYPE.getLeastSupertype(functionType)); + assertTypeEquals(UNKNOWN_TYPE, + UNKNOWN_TYPE.getLeastSupertype(OBJECT_TYPE)); + assertTypeEquals(UNKNOWN_TYPE, + UNKNOWN_TYPE.getLeastSupertype(DATE_TYPE)); + assertTypeEquals(UNKNOWN_TYPE, + UNKNOWN_TYPE.getLeastSupertype(REGEXP_TYPE)); + + // matchesXxx + assertTrue(UNKNOWN_TYPE.matchesInt32Context()); + assertTrue(UNKNOWN_TYPE.matchesNumberContext()); + assertTrue(UNKNOWN_TYPE.matchesObjectContext()); + assertTrue(UNKNOWN_TYPE.matchesStringContext()); + assertTrue(UNKNOWN_TYPE.matchesUint32Context()); + + // isPropertyType* + assertPropertyTypeUnknown(UNKNOWN_TYPE, "XXX"); + + // toString + assertEquals("?", UNKNOWN_TYPE.toString()); + assertTrue(UNKNOWN_TYPE.hasDisplayName()); + assertEquals("Unknown", UNKNOWN_TYPE.getDisplayName()); + + Asserts.assertResolvesToSame(UNKNOWN_TYPE); + assertFalse(UNKNOWN_TYPE.isNominalConstructor()); + + assertEquals(UNKNOWN_TYPE, UNKNOWN_TYPE.getPropertyType("abc")); + } + + /** + * Tests the behavior of the checked unknown type. + */ + public void testCheckedUnknownType() throws Exception { + // isPropertyType* + assertPropertyTypeUnknown(CHECKED_UNKNOWN_TYPE, "XXX"); + + // toString + assertEquals("??", CHECKED_UNKNOWN_TYPE.toString()); + assertTrue(CHECKED_UNKNOWN_TYPE.hasDisplayName()); + assertEquals("Unknown", CHECKED_UNKNOWN_TYPE.getDisplayName()); + + Asserts.assertResolvesToSame(CHECKED_UNKNOWN_TYPE); + assertFalse(CHECKED_UNKNOWN_TYPE.isNominalConstructor()); + + assertEquals(CHECKED_UNKNOWN_TYPE, + CHECKED_UNKNOWN_TYPE.getPropertyType("abc")); + } + + /** + * Tests the behavior of the unknown type. + */ + public void testAllType() throws Exception { + // isXxx + assertFalse(ALL_TYPE.isArrayType()); + assertFalse(ALL_TYPE.isBooleanValueType()); + assertFalse(ALL_TYPE.isDateType()); + assertFalse(ALL_TYPE.isEnumElementType()); + assertFalse(ALL_TYPE.isNamedType()); + assertFalse(ALL_TYPE.isNullType()); + assertFalse(ALL_TYPE.isNumber()); + assertFalse(ALL_TYPE.isNumberObjectType()); + assertFalse(ALL_TYPE.isNumberValueType()); + assertFalse(ALL_TYPE.isObject()); + assertFalse(ALL_TYPE.isFunctionPrototypeType()); + assertFalse(ALL_TYPE.isRegexpType()); + assertFalse(ALL_TYPE.isString()); + assertFalse(ALL_TYPE.isStringObjectType()); + assertFalse(ALL_TYPE.isStringValueType()); + assertFalse(ALL_TYPE.isEnumType()); + assertFalse(ALL_TYPE.isUnionType()); + assertFalse(ALL_TYPE.isStruct()); + assertFalse(ALL_TYPE.isDict()); + assertTrue(ALL_TYPE.isAllType()); + assertFalse(ALL_TYPE.isVoidType()); + assertFalse(ALL_TYPE.isConstructor()); + assertFalse(ALL_TYPE.isInstanceType()); + + // isSubtype + assertFalse(ALL_TYPE.isSubtype(NO_TYPE)); + assertFalse(ALL_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertTrue(ALL_TYPE.isSubtype(ALL_TYPE)); + assertFalse(ALL_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(ALL_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(ALL_TYPE.isSubtype(functionType)); + assertFalse(ALL_TYPE.isSubtype(recordType)); + assertFalse(ALL_TYPE.isSubtype(NULL_TYPE)); + assertFalse(ALL_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(ALL_TYPE.isSubtype(DATE_TYPE)); + assertTrue(ALL_TYPE.isSubtype(unresolvedNamedType)); + assertFalse(ALL_TYPE.isSubtype(namedGoogBar)); + assertFalse(ALL_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(ALL_TYPE.isSubtype(VOID_TYPE)); + assertTrue(ALL_TYPE.isSubtype(UNKNOWN_TYPE)); + + // canBeCalled + assertFalse(ALL_TYPE.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(ALL_TYPE, ALL_TYPE); + assertCanTestForEqualityWith(ALL_TYPE, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(ALL_TYPE, NUMBER_TYPE); + assertCanTestForEqualityWith(ALL_TYPE, functionType); + assertCanTestForEqualityWith(ALL_TYPE, recordType); + assertCanTestForEqualityWith(ALL_TYPE, VOID_TYPE); + assertCanTestForEqualityWith(ALL_TYPE, OBJECT_TYPE); + assertCanTestForEqualityWith(ALL_TYPE, DATE_TYPE); + assertCanTestForEqualityWith(ALL_TYPE, REGEXP_TYPE); + + // canTestForShallowEqualityWith + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(NO_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(functionType)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(recordType)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); + assertTrue(ALL_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + + // isNullable + assertFalse(ALL_TYPE.isNullable()); + + // getLeastSupertype + assertTypeEquals(ALL_TYPE, + ALL_TYPE.getLeastSupertype(ALL_TYPE)); + assertTypeEquals(ALL_TYPE, + ALL_TYPE.getLeastSupertype(UNKNOWN_TYPE)); + assertTypeEquals(ALL_TYPE, + ALL_TYPE.getLeastSupertype(STRING_OBJECT_TYPE)); + assertTypeEquals(ALL_TYPE, + ALL_TYPE.getLeastSupertype(NUMBER_TYPE)); + assertTypeEquals(ALL_TYPE, + ALL_TYPE.getLeastSupertype(functionType)); + assertTypeEquals(ALL_TYPE, + ALL_TYPE.getLeastSupertype(OBJECT_TYPE)); + assertTypeEquals(ALL_TYPE, + ALL_TYPE.getLeastSupertype(DATE_TYPE)); + assertTypeEquals(ALL_TYPE, + ALL_TYPE.getLeastSupertype(REGEXP_TYPE)); + + // matchesXxx + assertFalse(ALL_TYPE.matchesInt32Context()); + assertFalse(ALL_TYPE.matchesNumberContext()); + assertTrue(ALL_TYPE.matchesObjectContext()); + assertTrue(ALL_TYPE.matchesStringContext()); + assertFalse(ALL_TYPE.matchesUint32Context()); + + // toString + assertEquals("*", ALL_TYPE.toString()); + + assertTrue(ALL_TYPE.hasDisplayName()); + assertEquals("", ALL_TYPE.getDisplayName()); + + Asserts.assertResolvesToSame(ALL_TYPE); + assertFalse(ALL_TYPE.isNominalConstructor()); + } + + /** + * Tests the behavior of the Object type (the object + * at the top of the JavaScript hierarchy). + */ + public void testTheObjectType() throws Exception { + // implicit prototype + assertTypeEquals(OBJECT_PROTOTYPE, OBJECT_TYPE.getImplicitPrototype()); + + // isXxx + assertFalse(OBJECT_TYPE.isNoObjectType()); + assertFalse(OBJECT_TYPE.isNoType()); + assertFalse(OBJECT_TYPE.isArrayType()); + assertFalse(OBJECT_TYPE.isBooleanValueType()); + assertFalse(OBJECT_TYPE.isDateType()); + assertFalse(OBJECT_TYPE.isEnumElementType()); + assertFalse(OBJECT_TYPE.isNullType()); + assertFalse(OBJECT_TYPE.isNamedType()); + assertFalse(OBJECT_TYPE.isNullType()); + assertFalse(OBJECT_TYPE.isNumber()); + assertFalse(OBJECT_TYPE.isNumberObjectType()); + assertFalse(OBJECT_TYPE.isNumberValueType()); + assertTrue(OBJECT_TYPE.isObject()); + assertFalse(OBJECT_TYPE.isFunctionPrototypeType()); + assertTrue(OBJECT_TYPE.getImplicitPrototype().isFunctionPrototypeType()); + assertFalse(OBJECT_TYPE.isRegexpType()); + assertFalse(OBJECT_TYPE.isString()); + assertFalse(OBJECT_TYPE.isStringObjectType()); + assertFalse(OBJECT_TYPE.isStringValueType()); + assertFalse(OBJECT_TYPE.isEnumType()); + assertFalse(OBJECT_TYPE.isUnionType()); + assertFalse(OBJECT_TYPE.isStruct()); + assertFalse(OBJECT_TYPE.isDict()); + assertFalse(OBJECT_TYPE.isAllType()); + assertFalse(OBJECT_TYPE.isVoidType()); + assertFalse(OBJECT_TYPE.isConstructor()); + assertTrue(OBJECT_TYPE.isInstanceType()); + + // isSubtype + assertFalse(OBJECT_TYPE.isSubtype(NO_TYPE)); + assertTrue(OBJECT_TYPE.isSubtype(ALL_TYPE)); + assertFalse(OBJECT_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(OBJECT_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(OBJECT_TYPE.isSubtype(functionType)); + assertFalse(OBJECT_TYPE.isSubtype(recordType)); + assertFalse(OBJECT_TYPE.isSubtype(NULL_TYPE)); + assertTrue(OBJECT_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(OBJECT_TYPE.isSubtype(DATE_TYPE)); + assertFalse(OBJECT_TYPE.isSubtype(namedGoogBar)); + assertTrue(OBJECT_TYPE.isSubtype(unresolvedNamedType)); + assertFalse(OBJECT_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(OBJECT_TYPE.isSubtype(ARRAY_TYPE)); + assertTrue(OBJECT_TYPE.isSubtype(UNKNOWN_TYPE)); + + // canBeCalled + assertFalse(OBJECT_TYPE.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(OBJECT_TYPE, ALL_TYPE); + assertCanTestForEqualityWith(OBJECT_TYPE, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(OBJECT_TYPE, NUMBER_TYPE); + assertCanTestForEqualityWith(OBJECT_TYPE, STRING_TYPE); + assertCanTestForEqualityWith(OBJECT_TYPE, BOOLEAN_TYPE); + assertCanTestForEqualityWith(OBJECT_TYPE, functionType); + assertCanTestForEqualityWith(OBJECT_TYPE, recordType); + assertCannotTestForEqualityWith(OBJECT_TYPE, VOID_TYPE); + assertCanTestForEqualityWith(OBJECT_TYPE, OBJECT_TYPE); + assertCanTestForEqualityWith(OBJECT_TYPE, DATE_TYPE); + assertCanTestForEqualityWith(OBJECT_TYPE, REGEXP_TYPE); + assertCanTestForEqualityWith(OBJECT_TYPE, ARRAY_TYPE); + assertCanTestForEqualityWith(OBJECT_TYPE, UNKNOWN_TYPE); + + // canTestForShallowEqualityWith + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(NO_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(OBJECT_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(functionType)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(recordType)); + assertFalse(OBJECT_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(OBJECT_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertTrue(OBJECT_TYPE. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(OBJECT_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(OBJECT_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(UNKNOWN_TYPE)); + + // isNullable + assertFalse(OBJECT_TYPE.isNullable()); + + // getLeastSupertype + assertTypeEquals(ALL_TYPE, + OBJECT_TYPE.getLeastSupertype(ALL_TYPE)); + assertTypeEquals(OBJECT_TYPE, + OBJECT_TYPE.getLeastSupertype(STRING_OBJECT_TYPE)); + assertTypeEquals(createUnionType(OBJECT_TYPE, NUMBER_TYPE), + OBJECT_TYPE.getLeastSupertype(NUMBER_TYPE)); + assertTypeEquals(OBJECT_TYPE, + OBJECT_TYPE.getLeastSupertype(functionType)); + assertTypeEquals(OBJECT_TYPE, + OBJECT_TYPE.getLeastSupertype(OBJECT_TYPE)); + assertTypeEquals(OBJECT_TYPE, + OBJECT_TYPE.getLeastSupertype(DATE_TYPE)); + assertTypeEquals(OBJECT_TYPE, + OBJECT_TYPE.getLeastSupertype(REGEXP_TYPE)); + + // getPropertyType + assertEquals(7, OBJECT_TYPE.getPropertiesCount()); + assertReturnTypeEquals(OBJECT_TYPE, + OBJECT_TYPE.getPropertyType("constructor")); + assertReturnTypeEquals(STRING_TYPE, + OBJECT_TYPE.getPropertyType("toString")); + assertReturnTypeEquals(STRING_TYPE, + OBJECT_TYPE.getPropertyType("toLocaleString")); + assertReturnTypeEquals(UNKNOWN_TYPE, + OBJECT_TYPE.getPropertyType("valueOf")); + assertReturnTypeEquals(BOOLEAN_TYPE, + OBJECT_TYPE.getPropertyType("hasOwnProperty")); + assertReturnTypeEquals(BOOLEAN_TYPE, + OBJECT_TYPE.getPropertyType("isPrototypeOf")); + assertReturnTypeEquals(BOOLEAN_TYPE, + OBJECT_TYPE.getPropertyType("propertyIsEnumerable")); + + // matchesXxx + assertFalse(OBJECT_TYPE.matchesInt32Context()); + assertFalse(OBJECT_TYPE.matchesNumberContext()); + assertTrue(OBJECT_TYPE.matchesObjectContext()); + assertTrue(OBJECT_TYPE.matchesStringContext()); + assertFalse(OBJECT_TYPE.matchesUint32Context()); + + // implicit prototype + assertTypeEquals(OBJECT_PROTOTYPE, OBJECT_TYPE.getImplicitPrototype()); + + // toString + assertEquals("Object", OBJECT_TYPE.toString()); + + assertTrue(OBJECT_TYPE.isNativeObjectType()); + assertTrue(OBJECT_TYPE.getImplicitPrototype().isNativeObjectType()); + + Asserts.assertResolvesToSame(OBJECT_TYPE); + assertFalse(OBJECT_TYPE.isNominalConstructor()); + assertTrue(OBJECT_TYPE.getConstructor().isNominalConstructor()); + } + + /** + * Tests the behavior of the number value type. + */ + public void testNumberObjectType() throws Exception { + // isXxx + assertFalse(NUMBER_OBJECT_TYPE.isArrayType()); + assertFalse(NUMBER_OBJECT_TYPE.isBooleanObjectType()); + assertFalse(NUMBER_OBJECT_TYPE.isBooleanValueType()); + assertFalse(NUMBER_OBJECT_TYPE.isDateType()); + assertFalse(NUMBER_OBJECT_TYPE.isEnumElementType()); + assertFalse(NUMBER_OBJECT_TYPE.isNamedType()); + assertFalse(NUMBER_OBJECT_TYPE.isNullType()); + assertTrue(NUMBER_OBJECT_TYPE.isNumber()); + assertTrue(NUMBER_OBJECT_TYPE.isNumberObjectType()); + assertFalse(NUMBER_OBJECT_TYPE.isNumberValueType()); + assertTrue(NUMBER_OBJECT_TYPE.isObject()); + assertFalse(NUMBER_OBJECT_TYPE.isFunctionPrototypeType()); + assertTrue( + NUMBER_OBJECT_TYPE.getImplicitPrototype().isFunctionPrototypeType()); + assertFalse(NUMBER_OBJECT_TYPE.isRegexpType()); + assertFalse(NUMBER_OBJECT_TYPE.isString()); + assertFalse(NUMBER_OBJECT_TYPE.isStringObjectType()); + assertFalse(NUMBER_OBJECT_TYPE.isStringValueType()); + assertFalse(NUMBER_OBJECT_TYPE.isEnumType()); + assertFalse(NUMBER_OBJECT_TYPE.isUnionType()); + assertFalse(NUMBER_OBJECT_TYPE.isStruct()); + assertFalse(NUMBER_OBJECT_TYPE.isDict()); + assertFalse(NUMBER_OBJECT_TYPE.isAllType()); + assertFalse(NUMBER_OBJECT_TYPE.isVoidType()); + assertFalse(NUMBER_OBJECT_TYPE.isConstructor()); + assertTrue(NUMBER_OBJECT_TYPE.isInstanceType()); + + // autoboxesTo + assertTypeEquals(NUMBER_OBJECT_TYPE, NUMBER_TYPE.autoboxesTo()); + + // unboxesTo + assertTypeEquals(NUMBER_TYPE, NUMBER_OBJECT_TYPE.unboxesTo()); + + // isSubtype + assertTrue(NUMBER_OBJECT_TYPE.isSubtype(ALL_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE.isSubtype(functionType)); + assertFalse(NUMBER_OBJECT_TYPE.isSubtype(NULL_TYPE)); + assertTrue(NUMBER_OBJECT_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE.isSubtype(DATE_TYPE)); + assertTrue(NUMBER_OBJECT_TYPE.isSubtype(unresolvedNamedType)); + assertFalse(NUMBER_OBJECT_TYPE.isSubtype(namedGoogBar)); + assertTrue(NUMBER_OBJECT_TYPE.isSubtype( + createUnionType(NUMBER_OBJECT_TYPE, NULL_TYPE))); + assertFalse(NUMBER_OBJECT_TYPE.isSubtype( + createUnionType(NUMBER_TYPE, NULL_TYPE))); + assertTrue(NUMBER_OBJECT_TYPE.isSubtype(UNKNOWN_TYPE)); + + // canBeCalled + assertFalse(NUMBER_OBJECT_TYPE.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, NO_TYPE); + assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, NO_OBJECT_TYPE); + assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, ALL_TYPE); + assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, NUMBER_TYPE); + assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, functionType); + assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, elementsType); + assertCannotTestForEqualityWith(NUMBER_OBJECT_TYPE, VOID_TYPE); + assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, OBJECT_TYPE); + assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, DATE_TYPE); + assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, REGEXP_TYPE); + assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, ARRAY_TYPE); + + // canTestForShallowEqualityWith + assertTrue(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(NO_TYPE)); + assertTrue(NUMBER_OBJECT_TYPE. + canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE. + canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE. + canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(functionType)); + assertFalse(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertTrue(NUMBER_OBJECT_TYPE. + canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE. + canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE. + canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE. + canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE. + canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE. + canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + + // isNullable + assertFalse(NUMBER_OBJECT_TYPE.isNullable()); + + // getLeastSupertype + assertTypeEquals(ALL_TYPE, + NUMBER_OBJECT_TYPE.getLeastSupertype(ALL_TYPE)); + assertTypeEquals(createUnionType(NUMBER_OBJECT_TYPE, STRING_OBJECT_TYPE), + NUMBER_OBJECT_TYPE.getLeastSupertype(STRING_OBJECT_TYPE)); + assertTypeEquals(createUnionType(NUMBER_OBJECT_TYPE, NUMBER_TYPE), + NUMBER_OBJECT_TYPE.getLeastSupertype(NUMBER_TYPE)); + assertTypeEquals(createUnionType(NUMBER_OBJECT_TYPE, functionType), + NUMBER_OBJECT_TYPE.getLeastSupertype(functionType)); + assertTypeEquals(OBJECT_TYPE, + NUMBER_OBJECT_TYPE.getLeastSupertype(OBJECT_TYPE)); + assertTypeEquals(createUnionType(NUMBER_OBJECT_TYPE, DATE_TYPE), + NUMBER_OBJECT_TYPE.getLeastSupertype(DATE_TYPE)); + assertTypeEquals(createUnionType(NUMBER_OBJECT_TYPE, REGEXP_TYPE), + NUMBER_OBJECT_TYPE.getLeastSupertype(REGEXP_TYPE)); + + // matchesXxx + assertTrue(NUMBER_OBJECT_TYPE.matchesInt32Context()); + assertTrue(NUMBER_OBJECT_TYPE.matchesNumberContext()); + assertTrue(NUMBER_OBJECT_TYPE.matchesObjectContext()); + assertTrue(NUMBER_OBJECT_TYPE.matchesStringContext()); + assertTrue(NUMBER_OBJECT_TYPE.matchesUint32Context()); + + // toString + assertEquals("Number", NUMBER_OBJECT_TYPE.toString()); + assertTrue(NUMBER_OBJECT_TYPE.hasDisplayName()); + assertEquals("Number", NUMBER_OBJECT_TYPE.getDisplayName()); + + assertTrue(NUMBER_OBJECT_TYPE.isNativeObjectType()); + + Asserts.assertResolvesToSame(NUMBER_OBJECT_TYPE); + } + + /** + * Tests the behavior of the number value type. + */ + public void testNumberValueType() throws Exception { + // isXxx + assertFalse(NUMBER_TYPE.isArrayType()); + assertFalse(NUMBER_TYPE.isBooleanObjectType()); + assertFalse(NUMBER_TYPE.isBooleanValueType()); + assertFalse(NUMBER_TYPE.isDateType()); + assertFalse(NUMBER_TYPE.isEnumElementType()); + assertFalse(NUMBER_TYPE.isNamedType()); + assertFalse(NUMBER_TYPE.isNullType()); + assertTrue(NUMBER_TYPE.isNumber()); + assertFalse(NUMBER_TYPE.isNumberObjectType()); + assertTrue(NUMBER_TYPE.isNumberValueType()); + assertFalse(NUMBER_TYPE.isFunctionPrototypeType()); + assertFalse(NUMBER_TYPE.isRegexpType()); + assertFalse(NUMBER_TYPE.isString()); + assertFalse(NUMBER_TYPE.isStringObjectType()); + assertFalse(NUMBER_TYPE.isStringValueType()); + assertFalse(NUMBER_TYPE.isEnumType()); + assertFalse(NUMBER_TYPE.isUnionType()); + assertFalse(NUMBER_TYPE.isStruct()); + assertFalse(NUMBER_TYPE.isDict()); + assertFalse(NUMBER_TYPE.isAllType()); + assertFalse(NUMBER_TYPE.isVoidType()); + assertFalse(NUMBER_TYPE.isConstructor()); + assertFalse(NUMBER_TYPE.isInstanceType()); + + // autoboxesTo + assertTypeEquals(NUMBER_OBJECT_TYPE, NUMBER_TYPE.autoboxesTo()); + + // isSubtype + assertTrue(NUMBER_TYPE.isSubtype(ALL_TYPE)); + assertFalse(NUMBER_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertTrue(NUMBER_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(NUMBER_TYPE.isSubtype(functionType)); + assertFalse(NUMBER_TYPE.isSubtype(NULL_TYPE)); + assertFalse(NUMBER_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(NUMBER_TYPE.isSubtype(DATE_TYPE)); + assertTrue(NUMBER_TYPE.isSubtype(unresolvedNamedType)); + assertFalse(NUMBER_TYPE.isSubtype(namedGoogBar)); + assertTrue(NUMBER_TYPE.isSubtype( + createUnionType(NUMBER_TYPE, NULL_TYPE))); + assertTrue(NUMBER_TYPE.isSubtype(UNKNOWN_TYPE)); + + // canBeCalled + assertFalse(NUMBER_TYPE.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(NUMBER_TYPE, NO_TYPE); + assertCanTestForEqualityWith(NUMBER_TYPE, NO_OBJECT_TYPE); + assertCanTestForEqualityWith(NUMBER_TYPE, ALL_TYPE); + assertCanTestForEqualityWith(NUMBER_TYPE, NUMBER_TYPE); + assertCanTestForEqualityWith(NUMBER_TYPE, STRING_OBJECT_TYPE); + assertCannotTestForEqualityWith(NUMBER_TYPE, functionType); + assertCannotTestForEqualityWith(NUMBER_TYPE, VOID_TYPE); + assertCanTestForEqualityWith(NUMBER_TYPE, OBJECT_TYPE); + assertCanTestForEqualityWith(NUMBER_TYPE, DATE_TYPE); + assertCanTestForEqualityWith(NUMBER_TYPE, REGEXP_TYPE); + assertCanTestForEqualityWith(NUMBER_TYPE, ARRAY_TYPE); + assertCanTestForEqualityWith(NUMBER_TYPE, UNKNOWN_TYPE); + + // canTestForShallowEqualityWith + assertTrue(NUMBER_TYPE.canTestForShallowEqualityWith(NO_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(functionType)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); + assertTrue(NUMBER_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(NUMBER_TYPE. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(NUMBER_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + assertTrue(NUMBER_TYPE.canTestForShallowEqualityWith(UNKNOWN_TYPE)); + + // isNullable + assertFalse(NUMBER_TYPE.isNullable()); + + // getLeastSupertype + assertTypeEquals(ALL_TYPE, + NUMBER_TYPE.getLeastSupertype(ALL_TYPE)); + assertTypeEquals(createUnionType(NUMBER_TYPE, STRING_OBJECT_TYPE), + NUMBER_TYPE.getLeastSupertype(STRING_OBJECT_TYPE)); + assertTypeEquals(NUMBER_TYPE, + NUMBER_TYPE.getLeastSupertype(NUMBER_TYPE)); + assertTypeEquals(createUnionType(NUMBER_TYPE, functionType), + NUMBER_TYPE.getLeastSupertype(functionType)); + assertTypeEquals(createUnionType(NUMBER_TYPE, OBJECT_TYPE), + NUMBER_TYPE.getLeastSupertype(OBJECT_TYPE)); + assertTypeEquals(createUnionType(NUMBER_TYPE, DATE_TYPE), + NUMBER_TYPE.getLeastSupertype(DATE_TYPE)); + assertTypeEquals(createUnionType(NUMBER_TYPE, REGEXP_TYPE), + NUMBER_TYPE.getLeastSupertype(REGEXP_TYPE)); + + // matchesXxx + assertTrue(NUMBER_TYPE.matchesInt32Context()); + assertTrue(NUMBER_TYPE.matchesNumberContext()); + assertTrue(NUMBER_TYPE.matchesObjectContext()); + assertTrue(NUMBER_TYPE.matchesStringContext()); + assertTrue(NUMBER_TYPE.matchesUint32Context()); + + // toString + assertEquals("number", NUMBER_TYPE.toString()); + assertTrue(NUMBER_TYPE.hasDisplayName()); + assertEquals("number", NUMBER_TYPE.getDisplayName()); + + Asserts.assertResolvesToSame(NUMBER_TYPE); + assertFalse(NUMBER_TYPE.isNominalConstructor()); + } + + /** + * Tests the behavior of the null type. + */ + public void testNullType() throws Exception { + + // isXxx + assertFalse(NULL_TYPE.isArrayType()); + assertFalse(NULL_TYPE.isBooleanValueType()); + assertFalse(NULL_TYPE.isDateType()); + assertFalse(NULL_TYPE.isEnumElementType()); + assertFalse(NULL_TYPE.isNamedType()); + assertTrue(NULL_TYPE.isNullType()); + assertFalse(NULL_TYPE.isNumber()); + assertFalse(NULL_TYPE.isNumberObjectType()); + assertFalse(NULL_TYPE.isNumberValueType()); + assertFalse(NULL_TYPE.isFunctionPrototypeType()); + assertFalse(NULL_TYPE.isRegexpType()); + assertFalse(NULL_TYPE.isString()); + assertFalse(NULL_TYPE.isStringObjectType()); + assertFalse(NULL_TYPE.isStringValueType()); + assertFalse(NULL_TYPE.isEnumType()); + assertFalse(NULL_TYPE.isUnionType()); + assertFalse(NULL_TYPE.isStruct()); + assertFalse(NULL_TYPE.isDict()); + assertFalse(NULL_TYPE.isAllType()); + assertFalse(NULL_TYPE.isVoidType()); + assertFalse(NULL_TYPE.isConstructor()); + assertFalse(NULL_TYPE.isInstanceType()); + + // autoboxesTo + assertNull(NULL_TYPE.autoboxesTo()); + + // isSubtype + assertFalse(NULL_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertFalse(NULL_TYPE.isSubtype(NO_TYPE)); + assertTrue(NULL_TYPE.isSubtype(NULL_TYPE)); + assertTrue(NULL_TYPE.isSubtype(ALL_TYPE)); + assertFalse(NULL_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(NULL_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(NULL_TYPE.isSubtype(functionType)); + assertFalse(NULL_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(NULL_TYPE.isSubtype(DATE_TYPE)); + assertFalse(NULL_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(NULL_TYPE.isSubtype(ARRAY_TYPE)); + assertTrue(NULL_TYPE.isSubtype(UNKNOWN_TYPE)); + + assertTrue(NULL_TYPE.isSubtype(createNullableType(NO_OBJECT_TYPE))); + assertTrue(NULL_TYPE.isSubtype(createNullableType(NO_TYPE))); + assertTrue(NULL_TYPE.isSubtype(createNullableType(NULL_TYPE))); + assertTrue(NULL_TYPE.isSubtype(createNullableType(ALL_TYPE))); + assertTrue(NULL_TYPE.isSubtype(createNullableType(STRING_OBJECT_TYPE))); + assertTrue(NULL_TYPE.isSubtype(createNullableType(NUMBER_TYPE))); + assertTrue(NULL_TYPE.isSubtype(createNullableType(functionType))); + assertTrue(NULL_TYPE.isSubtype(createNullableType(OBJECT_TYPE))); + assertTrue(NULL_TYPE.isSubtype(createNullableType(DATE_TYPE))); + assertTrue(NULL_TYPE.isSubtype(createNullableType(REGEXP_TYPE))); + assertTrue(NULL_TYPE.isSubtype(createNullableType(ARRAY_TYPE))); + + // canBeCalled + assertFalse(NULL_TYPE.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(NULL_TYPE, NO_TYPE); + assertCanTestForEqualityWith(NULL_TYPE, NO_OBJECT_TYPE); + assertCanTestForEqualityWith(NULL_TYPE, ALL_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, ARRAY_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, BOOLEAN_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, BOOLEAN_OBJECT_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, DATE_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, ERROR_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, EVAL_ERROR_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, functionType); + assertCannotTestForEqualityWith(NULL_TYPE, NULL_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, NUMBER_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, NUMBER_OBJECT_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, OBJECT_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, URI_ERROR_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, RANGE_ERROR_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, REFERENCE_ERROR_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, REGEXP_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, STRING_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, STRING_OBJECT_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, SYNTAX_ERROR_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, TYPE_ERROR_TYPE); + assertCannotTestForEqualityWith(NULL_TYPE, VOID_TYPE); + + // canTestForShallowEqualityWith + assertTrue(NULL_TYPE.canTestForShallowEqualityWith(NO_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertFalse(NULL_TYPE. + canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(functionType)); + assertTrue(NULL_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(NULL_TYPE. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(NULL_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(NULL_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + assertTrue(NULL_TYPE.canTestForShallowEqualityWith( + createNullableType(STRING_OBJECT_TYPE))); + + // getLeastSupertype + assertTypeEquals(NULL_TYPE, NULL_TYPE.getLeastSupertype(NULL_TYPE)); + assertTypeEquals(ALL_TYPE, NULL_TYPE.getLeastSupertype(ALL_TYPE)); + assertTypeEquals(createNullableType(STRING_OBJECT_TYPE), + NULL_TYPE.getLeastSupertype(STRING_OBJECT_TYPE)); + assertTypeEquals(createNullableType(NUMBER_TYPE), + NULL_TYPE.getLeastSupertype(NUMBER_TYPE)); + assertTypeEquals(createNullableType(functionType), + NULL_TYPE.getLeastSupertype(functionType)); + assertTypeEquals(createNullableType(OBJECT_TYPE), + NULL_TYPE.getLeastSupertype(OBJECT_TYPE)); + assertTypeEquals(createNullableType(DATE_TYPE), + NULL_TYPE.getLeastSupertype(DATE_TYPE)); + assertTypeEquals(createNullableType(REGEXP_TYPE), + NULL_TYPE.getLeastSupertype(REGEXP_TYPE)); + + // matchesXxx + assertTrue(NULL_TYPE.matchesInt32Context()); + assertTrue(NULL_TYPE.matchesNumberContext()); + assertFalse(NULL_TYPE.matchesObjectContext()); + assertTrue(NULL_TYPE.matchesStringContext()); + assertTrue(NULL_TYPE.matchesUint32Context()); + + // matchesObjectContext + assertFalse(NULL_TYPE.matchesObjectContext()); + + // toString + assertEquals("null", NULL_TYPE.toString()); + assertTrue(NULL_TYPE.hasDisplayName()); + assertEquals("null", NULL_TYPE.getDisplayName()); + + Asserts.assertResolvesToSame(NULL_TYPE); + + // getGreatestSubtype + assertTrue( + NULL_TYPE.isSubtype( + createUnionType(forwardDeclaredNamedType, NULL_TYPE))); + assertTypeEquals( + createUnionType(forwardDeclaredNamedType, NULL_TYPE), + NULL_TYPE.getGreatestSubtype( + createUnionType(forwardDeclaredNamedType, NULL_TYPE))); + assertFalse(NULL_TYPE.isNominalConstructor()); + + assertTrue(NULL_TYPE.differsFrom(UNKNOWN_TYPE)); + } + + /** + * Tests the behavior of the Date type. + */ + public void testDateType() throws Exception { + // isXxx + assertFalse(DATE_TYPE.isArrayType()); + assertFalse(DATE_TYPE.isBooleanValueType()); + assertTrue(DATE_TYPE.isDateType()); + assertFalse(DATE_TYPE.isEnumElementType()); + assertFalse(DATE_TYPE.isNamedType()); + assertFalse(DATE_TYPE.isNullType()); + assertFalse(DATE_TYPE.isNumberValueType()); + assertFalse(DATE_TYPE.isFunctionPrototypeType()); + assertTrue(DATE_TYPE.getImplicitPrototype().isFunctionPrototypeType()); + assertFalse(DATE_TYPE.isRegexpType()); + assertFalse(DATE_TYPE.isStringValueType()); + assertFalse(DATE_TYPE.isEnumType()); + assertFalse(DATE_TYPE.isUnionType()); + assertFalse(DATE_TYPE.isStruct()); + assertFalse(DATE_TYPE.isDict()); + assertFalse(DATE_TYPE.isAllType()); + assertFalse(DATE_TYPE.isVoidType()); + assertFalse(DATE_TYPE.isConstructor()); + assertTrue(DATE_TYPE.isInstanceType()); + + // autoboxesTo + assertNull(DATE_TYPE.autoboxesTo()); + + // isSubtype + assertFalse(DATE_TYPE.isSubtype(NO_TYPE)); + assertFalse(DATE_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertFalse(DATE_TYPE.isSubtype(ARRAY_TYPE)); + assertFalse(DATE_TYPE.isSubtype(BOOLEAN_TYPE)); + assertFalse(DATE_TYPE.isSubtype(BOOLEAN_OBJECT_TYPE)); + assertTrue(DATE_TYPE.isSubtype(DATE_TYPE)); + assertFalse(DATE_TYPE.isSubtype(ERROR_TYPE)); + assertFalse(DATE_TYPE.isSubtype(EVAL_ERROR_TYPE)); + assertFalse(DATE_TYPE.isSubtype(functionType)); + assertFalse(DATE_TYPE.isSubtype(NULL_TYPE)); + assertFalse(DATE_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(DATE_TYPE.isSubtype(NUMBER_OBJECT_TYPE)); + assertTrue(DATE_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(DATE_TYPE.isSubtype(URI_ERROR_TYPE)); + assertFalse(DATE_TYPE.isSubtype(RANGE_ERROR_TYPE)); + assertFalse(DATE_TYPE.isSubtype(REFERENCE_ERROR_TYPE)); + assertFalse(DATE_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(DATE_TYPE.isSubtype(STRING_TYPE)); + assertFalse(DATE_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(DATE_TYPE.isSubtype(SYNTAX_ERROR_TYPE)); + assertFalse(DATE_TYPE.isSubtype(TYPE_ERROR_TYPE)); + assertTrue(DATE_TYPE.isSubtype(ALL_TYPE)); + assertFalse(DATE_TYPE.isSubtype(VOID_TYPE)); + + // canBeCalled + assertFalse(DATE_TYPE.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(DATE_TYPE, ALL_TYPE); + assertCanTestForEqualityWith(DATE_TYPE, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(DATE_TYPE, NUMBER_TYPE); + assertCanTestForEqualityWith(DATE_TYPE, functionType); + assertCannotTestForEqualityWith(DATE_TYPE, VOID_TYPE); + assertCanTestForEqualityWith(DATE_TYPE, OBJECT_TYPE); + assertCanTestForEqualityWith(DATE_TYPE, DATE_TYPE); + assertCanTestForEqualityWith(DATE_TYPE, REGEXP_TYPE); + assertCanTestForEqualityWith(DATE_TYPE, ARRAY_TYPE); + + // canTestForShallowEqualityWith + assertTrue(DATE_TYPE.canTestForShallowEqualityWith(NO_TYPE)); + assertTrue(DATE_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertFalse(DATE_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(DATE_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertFalse(DATE_TYPE. + canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertTrue(DATE_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(DATE_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(DATE_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertFalse(DATE_TYPE.canTestForShallowEqualityWith(functionType)); + assertFalse(DATE_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(DATE_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertFalse(DATE_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(DATE_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(DATE_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(DATE_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(DATE_TYPE. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertFalse(DATE_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(DATE_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertFalse(DATE_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(DATE_TYPE.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(DATE_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(DATE_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(DATE_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + + // isNullable + assertFalse(DATE_TYPE.isNullable()); + assertTrue(createNullableType(DATE_TYPE).isNullable()); + + // getLeastSupertype + assertTypeEquals(ALL_TYPE, + DATE_TYPE.getLeastSupertype(ALL_TYPE)); + assertTypeEquals(createUnionType(DATE_TYPE, STRING_OBJECT_TYPE), + DATE_TYPE.getLeastSupertype(STRING_OBJECT_TYPE)); + assertTypeEquals(createUnionType(DATE_TYPE, NUMBER_TYPE), + DATE_TYPE.getLeastSupertype(NUMBER_TYPE)); + assertTypeEquals(createUnionType(DATE_TYPE, functionType), + DATE_TYPE.getLeastSupertype(functionType)); + assertTypeEquals(OBJECT_TYPE, DATE_TYPE.getLeastSupertype(OBJECT_TYPE)); + assertTypeEquals(DATE_TYPE, DATE_TYPE.getLeastSupertype(DATE_TYPE)); + assertTypeEquals(createUnionType(DATE_TYPE, REGEXP_TYPE), + DATE_TYPE.getLeastSupertype(REGEXP_TYPE)); + + // getPropertyType + assertEquals(46, DATE_TYPE.getImplicitPrototype().getPropertiesCount()); + assertEquals(46, DATE_TYPE.getPropertiesCount()); + assertReturnTypeEquals(DATE_TYPE, DATE_TYPE.getPropertyType("constructor")); + assertReturnTypeEquals(STRING_TYPE, + DATE_TYPE.getPropertyType("toString")); + assertReturnTypeEquals(STRING_TYPE, + DATE_TYPE.getPropertyType("toDateString")); + assertReturnTypeEquals(STRING_TYPE, + DATE_TYPE.getPropertyType("toTimeString")); + assertReturnTypeEquals(STRING_TYPE, + DATE_TYPE.getPropertyType("toLocaleString")); + assertReturnTypeEquals(STRING_TYPE, + DATE_TYPE.getPropertyType("toLocaleDateString")); + assertReturnTypeEquals(STRING_TYPE, + DATE_TYPE.getPropertyType("toLocaleTimeString")); + assertReturnTypeEquals(NUMBER_TYPE, DATE_TYPE.getPropertyType("valueOf")); + assertReturnTypeEquals(NUMBER_TYPE, DATE_TYPE.getPropertyType("getTime")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("getFullYear")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("getUTCFullYear")); + assertReturnTypeEquals(NUMBER_TYPE, DATE_TYPE.getPropertyType("getMonth")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("getUTCMonth")); + assertReturnTypeEquals(NUMBER_TYPE, DATE_TYPE.getPropertyType("getDate")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("getUTCDate")); + assertReturnTypeEquals(NUMBER_TYPE, DATE_TYPE.getPropertyType("getDay")); + assertReturnTypeEquals(NUMBER_TYPE, DATE_TYPE.getPropertyType("getUTCDay")); + assertReturnTypeEquals(NUMBER_TYPE, DATE_TYPE.getPropertyType("getHours")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("getUTCHours")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("getMinutes")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("getUTCMinutes")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("getSeconds")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("getUTCSeconds")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("getMilliseconds")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("getUTCMilliseconds")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("getTimezoneOffset")); + assertReturnTypeEquals(NUMBER_TYPE, DATE_TYPE.getPropertyType("setTime")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("setMilliseconds")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("setUTCMilliseconds")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("setSeconds")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("setUTCSeconds")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("setUTCSeconds")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("setMinutes")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("setUTCMinutes")); + assertReturnTypeEquals(NUMBER_TYPE, DATE_TYPE.getPropertyType("setHours")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("setUTCHours")); + assertReturnTypeEquals(NUMBER_TYPE, DATE_TYPE.getPropertyType("setDate")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("setUTCDate")); + assertReturnTypeEquals(NUMBER_TYPE, DATE_TYPE.getPropertyType("setMonth")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("setUTCMonth")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("setFullYear")); + assertReturnTypeEquals(NUMBER_TYPE, + DATE_TYPE.getPropertyType("setUTCFullYear")); + assertReturnTypeEquals(STRING_TYPE, + DATE_TYPE.getPropertyType("toUTCString")); + assertReturnTypeEquals(STRING_TYPE, + DATE_TYPE.getPropertyType("toGMTString")); + + // matchesXxx + assertTrue(DATE_TYPE.matchesInt32Context()); + assertTrue(DATE_TYPE.matchesNumberContext()); + assertTrue(DATE_TYPE.matchesObjectContext()); + assertTrue(DATE_TYPE.matchesStringContext()); + assertTrue(DATE_TYPE.matchesUint32Context()); + + // toString + assertEquals("Date", DATE_TYPE.toString()); + assertTrue(DATE_TYPE.hasDisplayName()); + assertEquals("Date", DATE_TYPE.getDisplayName()); + + assertTrue(DATE_TYPE.isNativeObjectType()); + + Asserts.assertResolvesToSame(DATE_TYPE); + assertFalse(DATE_TYPE.isNominalConstructor()); + assertTrue(DATE_TYPE.getConstructor().isNominalConstructor()); + } + + /** + * Tests the behavior of the RegExp type. + */ + public void testRegExpType() throws Exception { + // isXxx + assertFalse(REGEXP_TYPE.isNoType()); + assertFalse(REGEXP_TYPE.isNoObjectType()); + assertFalse(REGEXP_TYPE.isArrayType()); + assertFalse(REGEXP_TYPE.isBooleanValueType()); + assertFalse(REGEXP_TYPE.isDateType()); + assertFalse(REGEXP_TYPE.isEnumElementType()); + assertFalse(REGEXP_TYPE.isNamedType()); + assertFalse(REGEXP_TYPE.isNullType()); + assertFalse(REGEXP_TYPE.isNumberValueType()); + assertFalse(REGEXP_TYPE.isFunctionPrototypeType()); + assertTrue(REGEXP_TYPE.getImplicitPrototype().isFunctionPrototypeType()); + assertTrue(REGEXP_TYPE.isRegexpType()); + assertFalse(REGEXP_TYPE.isStringValueType()); + assertFalse(REGEXP_TYPE.isEnumType()); + assertFalse(REGEXP_TYPE.isUnionType()); + assertFalse(REGEXP_TYPE.isStruct()); + assertFalse(REGEXP_TYPE.isDict()); + assertFalse(REGEXP_TYPE.isAllType()); + assertFalse(REGEXP_TYPE.isVoidType()); + + // autoboxesTo + assertNull(REGEXP_TYPE.autoboxesTo()); + + // isSubtype + assertFalse(REGEXP_TYPE.isSubtype(NO_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(ARRAY_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(BOOLEAN_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(BOOLEAN_OBJECT_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(DATE_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(ERROR_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(EVAL_ERROR_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(functionType)); + assertFalse(REGEXP_TYPE.isSubtype(NULL_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(NUMBER_OBJECT_TYPE)); + assertTrue(REGEXP_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(URI_ERROR_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(RANGE_ERROR_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(REFERENCE_ERROR_TYPE)); + assertTrue(REGEXP_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(STRING_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(SYNTAX_ERROR_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(TYPE_ERROR_TYPE)); + assertTrue(REGEXP_TYPE.isSubtype(ALL_TYPE)); + assertFalse(REGEXP_TYPE.isSubtype(VOID_TYPE)); + + // canBeCalled + assertTrue(REGEXP_TYPE.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(REGEXP_TYPE, ALL_TYPE); + assertCanTestForEqualityWith(REGEXP_TYPE, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(REGEXP_TYPE, NUMBER_TYPE); + assertCanTestForEqualityWith(REGEXP_TYPE, functionType); + assertCannotTestForEqualityWith(REGEXP_TYPE, VOID_TYPE); + assertCanTestForEqualityWith(REGEXP_TYPE, OBJECT_TYPE); + assertCanTestForEqualityWith(REGEXP_TYPE, DATE_TYPE); + assertCanTestForEqualityWith(REGEXP_TYPE, REGEXP_TYPE); + assertCanTestForEqualityWith(REGEXP_TYPE, ARRAY_TYPE); + + // canTestForShallowEqualityWith + assertTrue(REGEXP_TYPE.canTestForShallowEqualityWith(NO_TYPE)); + assertTrue(REGEXP_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertFalse(REGEXP_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(REGEXP_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertFalse(REGEXP_TYPE. + canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertFalse(REGEXP_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(REGEXP_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(REGEXP_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertFalse(REGEXP_TYPE.canTestForShallowEqualityWith(functionType)); + assertFalse(REGEXP_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(REGEXP_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertFalse(REGEXP_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(REGEXP_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(REGEXP_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(REGEXP_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(REGEXP_TYPE. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertTrue(REGEXP_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(REGEXP_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertFalse(REGEXP_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(REGEXP_TYPE.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(REGEXP_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(REGEXP_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(REGEXP_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + + // isNullable + assertFalse(REGEXP_TYPE.isNullable()); + assertTrue(createNullableType(REGEXP_TYPE).isNullable()); + + // getLeastSupertype + assertTypeEquals(ALL_TYPE, + REGEXP_TYPE.getLeastSupertype(ALL_TYPE)); + assertTypeEquals(createUnionType(REGEXP_TYPE, STRING_OBJECT_TYPE), + REGEXP_TYPE.getLeastSupertype(STRING_OBJECT_TYPE)); + assertTypeEquals(createUnionType(REGEXP_TYPE, NUMBER_TYPE), + REGEXP_TYPE.getLeastSupertype(NUMBER_TYPE)); + assertTypeEquals(createUnionType(REGEXP_TYPE, functionType), + REGEXP_TYPE.getLeastSupertype(functionType)); + assertTypeEquals(OBJECT_TYPE, REGEXP_TYPE.getLeastSupertype(OBJECT_TYPE)); + assertTypeEquals(createUnionType(DATE_TYPE, REGEXP_TYPE), + REGEXP_TYPE.getLeastSupertype(DATE_TYPE)); + assertTypeEquals(REGEXP_TYPE, + REGEXP_TYPE.getLeastSupertype(REGEXP_TYPE)); + + // getPropertyType + assertEquals(9, REGEXP_TYPE.getImplicitPrototype().getPropertiesCount()); + assertEquals(14, REGEXP_TYPE.getPropertiesCount()); + assertReturnTypeEquals(REGEXP_TYPE, + REGEXP_TYPE.getPropertyType("constructor")); + assertReturnTypeEquals(createNullableType(ARRAY_TYPE), + REGEXP_TYPE.getPropertyType("exec")); + assertReturnTypeEquals(BOOLEAN_TYPE, + REGEXP_TYPE.getPropertyType("test")); + assertReturnTypeEquals(STRING_TYPE, + REGEXP_TYPE.getPropertyType("toString")); + assertTypeEquals(STRING_TYPE, REGEXP_TYPE.getPropertyType("source")); + assertTypeEquals(BOOLEAN_TYPE, REGEXP_TYPE.getPropertyType("global")); + assertTypeEquals(BOOLEAN_TYPE, REGEXP_TYPE.getPropertyType("ignoreCase")); + assertTypeEquals(BOOLEAN_TYPE, REGEXP_TYPE.getPropertyType("multiline")); + assertTypeEquals(NUMBER_TYPE, REGEXP_TYPE.getPropertyType("lastIndex")); + + // matchesXxx + assertFalse(REGEXP_TYPE.matchesInt32Context()); + assertFalse(REGEXP_TYPE.matchesNumberContext()); + assertTrue(REGEXP_TYPE.matchesObjectContext()); + assertTrue(REGEXP_TYPE.matchesStringContext()); + assertFalse(REGEXP_TYPE.matchesUint32Context()); + + // toString + assertEquals("RegExp", REGEXP_TYPE.toString()); + assertTrue(REGEXP_TYPE.hasDisplayName()); + assertEquals("RegExp", REGEXP_TYPE.getDisplayName()); + + assertTrue(REGEXP_TYPE.isNativeObjectType()); + + Asserts.assertResolvesToSame(REGEXP_TYPE); + assertFalse(REGEXP_TYPE.isNominalConstructor()); + assertTrue(REGEXP_TYPE.getConstructor().isNominalConstructor()); + } + + /** + * Tests the behavior of the string object type. + */ + public void testStringObjectType() throws Exception { + // isXxx + assertFalse(STRING_OBJECT_TYPE.isArrayType()); + assertFalse(STRING_OBJECT_TYPE.isBooleanObjectType()); + assertFalse(STRING_OBJECT_TYPE.isBooleanValueType()); + assertFalse(STRING_OBJECT_TYPE.isDateType()); + assertFalse(STRING_OBJECT_TYPE.isEnumElementType()); + assertFalse(STRING_OBJECT_TYPE.isNamedType()); + assertFalse(STRING_OBJECT_TYPE.isNullType()); + assertFalse(STRING_OBJECT_TYPE.isNumber()); + assertFalse(STRING_OBJECT_TYPE.isNumberObjectType()); + assertFalse(STRING_OBJECT_TYPE.isNumberValueType()); + assertFalse(STRING_OBJECT_TYPE.isFunctionPrototypeType()); + assertTrue( + STRING_OBJECT_TYPE.getImplicitPrototype().isFunctionPrototypeType()); + assertFalse(STRING_OBJECT_TYPE.isRegexpType()); + assertTrue(STRING_OBJECT_TYPE.isString()); + assertTrue(STRING_OBJECT_TYPE.isStringObjectType()); + assertFalse(STRING_OBJECT_TYPE.isStringValueType()); + assertFalse(STRING_OBJECT_TYPE.isEnumType()); + assertFalse(STRING_OBJECT_TYPE.isUnionType()); + assertFalse(STRING_OBJECT_TYPE.isStruct()); + assertFalse(STRING_OBJECT_TYPE.isDict()); + assertFalse(STRING_OBJECT_TYPE.isAllType()); + assertFalse(STRING_OBJECT_TYPE.isVoidType()); + assertFalse(STRING_OBJECT_TYPE.isConstructor()); + assertTrue(STRING_OBJECT_TYPE.isInstanceType()); + + // autoboxesTo + assertTypeEquals(STRING_OBJECT_TYPE, STRING_TYPE.autoboxesTo()); + + // unboxesTo + assertTypeEquals(STRING_TYPE, STRING_OBJECT_TYPE.unboxesTo()); + + // isSubtype + assertTrue(STRING_OBJECT_TYPE.isSubtype(ALL_TYPE)); + assertTrue(STRING_OBJECT_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(STRING_OBJECT_TYPE.isSubtype(STRING_TYPE)); + assertTrue(STRING_OBJECT_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(STRING_OBJECT_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(STRING_OBJECT_TYPE.isSubtype(DATE_TYPE)); + assertFalse(STRING_OBJECT_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(STRING_OBJECT_TYPE.isSubtype(ARRAY_TYPE)); + assertFalse(STRING_OBJECT_TYPE.isSubtype(STRING_TYPE)); + + // canBeCalled + assertFalse(STRING_OBJECT_TYPE.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(STRING_OBJECT_TYPE, ALL_TYPE); + assertCanTestForEqualityWith(STRING_OBJECT_TYPE, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(STRING_OBJECT_TYPE, STRING_TYPE); + assertCanTestForEqualityWith(STRING_OBJECT_TYPE, functionType); + assertCanTestForEqualityWith(STRING_OBJECT_TYPE, OBJECT_TYPE); + assertCanTestForEqualityWith(STRING_OBJECT_TYPE, NUMBER_TYPE); + assertCanTestForEqualityWith(STRING_OBJECT_TYPE, BOOLEAN_TYPE); + assertCanTestForEqualityWith(STRING_OBJECT_TYPE, BOOLEAN_OBJECT_TYPE); + assertCanTestForEqualityWith(STRING_OBJECT_TYPE, DATE_TYPE); + assertCanTestForEqualityWith(STRING_OBJECT_TYPE, REGEXP_TYPE); + assertCanTestForEqualityWith(STRING_OBJECT_TYPE, ARRAY_TYPE); + assertCanTestForEqualityWith(STRING_OBJECT_TYPE, UNKNOWN_TYPE); + + // canTestForShallowEqualityWith + assertTrue(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(NO_TYPE)); + assertTrue(STRING_OBJECT_TYPE. + canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertFalse(STRING_OBJECT_TYPE. + canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(STRING_OBJECT_TYPE. + canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(functionType)); + assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertFalse(STRING_OBJECT_TYPE. + canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(STRING_OBJECT_TYPE. + canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(STRING_OBJECT_TYPE. + canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(STRING_OBJECT_TYPE. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertTrue(STRING_OBJECT_TYPE. + canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(STRING_OBJECT_TYPE. + canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(STRING_OBJECT_TYPE. + canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + assertTrue(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(UNKNOWN_TYPE)); + + // properties (ECMA-262 page 98 - 106) + assertEquals(23, STRING_OBJECT_TYPE.getImplicitPrototype(). + getPropertiesCount()); + assertEquals(24, STRING_OBJECT_TYPE.getPropertiesCount()); + + assertReturnTypeEquals(STRING_TYPE, + STRING_OBJECT_TYPE.getPropertyType("toString")); + assertReturnTypeEquals(STRING_TYPE, + STRING_OBJECT_TYPE.getPropertyType("valueOf")); + assertReturnTypeEquals(STRING_TYPE, + STRING_OBJECT_TYPE.getPropertyType("charAt")); + assertReturnTypeEquals(NUMBER_TYPE, + STRING_OBJECT_TYPE.getPropertyType("charCodeAt")); + assertReturnTypeEquals(STRING_TYPE, + STRING_OBJECT_TYPE.getPropertyType("concat")); + assertReturnTypeEquals(NUMBER_TYPE, + STRING_OBJECT_TYPE.getPropertyType("indexOf")); + assertReturnTypeEquals(NUMBER_TYPE, + STRING_OBJECT_TYPE.getPropertyType("lastIndexOf")); + assertReturnTypeEquals(NUMBER_TYPE, + STRING_OBJECT_TYPE.getPropertyType("localeCompare")); + assertReturnTypeEquals(createNullableType(ARRAY_TYPE), + STRING_OBJECT_TYPE.getPropertyType("match")); + assertReturnTypeEquals(STRING_TYPE, + STRING_OBJECT_TYPE.getPropertyType("replace")); + assertReturnTypeEquals(NUMBER_TYPE, + STRING_OBJECT_TYPE.getPropertyType("search")); + assertReturnTypeEquals(STRING_TYPE, + STRING_OBJECT_TYPE.getPropertyType("slice")); + assertReturnTypeEquals(ARRAY_TYPE, + STRING_OBJECT_TYPE.getPropertyType("split")); + assertReturnTypeEquals(STRING_TYPE, + STRING_OBJECT_TYPE.getPropertyType("substring")); + assertReturnTypeEquals(STRING_TYPE, + STRING_OBJECT_TYPE.getPropertyType("toLowerCase")); + assertReturnTypeEquals(STRING_TYPE, + STRING_OBJECT_TYPE.getPropertyType("toLocaleLowerCase")); + assertReturnTypeEquals(STRING_TYPE, + STRING_OBJECT_TYPE.getPropertyType("toUpperCase")); + assertReturnTypeEquals(STRING_TYPE, + STRING_OBJECT_TYPE.getPropertyType("toLocaleUpperCase")); + assertTypeEquals(NUMBER_TYPE, STRING_OBJECT_TYPE.getPropertyType("length")); + + // matchesXxx + assertTrue(STRING_OBJECT_TYPE.matchesInt32Context()); + assertTrue(STRING_OBJECT_TYPE.matchesNumberContext()); + assertTrue(STRING_OBJECT_TYPE.matchesObjectContext()); + assertTrue(STRING_OBJECT_TYPE.matchesStringContext()); + assertTrue(STRING_OBJECT_TYPE.matchesUint32Context()); + + // isNullable + assertFalse(STRING_OBJECT_TYPE.isNullable()); + assertTrue(createNullableType(STRING_OBJECT_TYPE).isNullable()); + + assertTrue(STRING_OBJECT_TYPE.isNativeObjectType()); + + Asserts.assertResolvesToSame(STRING_OBJECT_TYPE); + + assertTrue(STRING_OBJECT_TYPE.hasDisplayName()); + assertEquals("String", STRING_OBJECT_TYPE.getDisplayName()); + assertFalse(STRING_OBJECT_TYPE.isNominalConstructor()); + assertTrue(STRING_OBJECT_TYPE.getConstructor().isNominalConstructor()); + } + + /** + * Tests the behavior of the string value type. + */ + public void testStringValueType() throws Exception { + // isXxx + assertFalse(STRING_TYPE.isArrayType()); + assertFalse(STRING_TYPE.isBooleanObjectType()); + assertFalse(STRING_TYPE.isBooleanValueType()); + assertFalse(STRING_TYPE.isDateType()); + assertFalse(STRING_TYPE.isEnumElementType()); + assertFalse(STRING_TYPE.isNamedType()); + assertFalse(STRING_TYPE.isNullType()); + assertFalse(STRING_TYPE.isNumber()); + assertFalse(STRING_TYPE.isNumberObjectType()); + assertFalse(STRING_TYPE.isNumberValueType()); + assertFalse(STRING_TYPE.isFunctionPrototypeType()); + assertFalse(STRING_TYPE.isRegexpType()); + assertTrue(STRING_TYPE.isString()); + assertFalse(STRING_TYPE.isStringObjectType()); + assertTrue(STRING_TYPE.isStringValueType()); + assertFalse(STRING_TYPE.isEnumType()); + assertFalse(STRING_TYPE.isUnionType()); + assertFalse(STRING_TYPE.isStruct()); + assertFalse(STRING_TYPE.isDict()); + assertFalse(STRING_TYPE.isAllType()); + assertFalse(STRING_TYPE.isVoidType()); + assertFalse(STRING_TYPE.isConstructor()); + assertFalse(STRING_TYPE.isInstanceType()); + + // autoboxesTo + assertTypeEquals(STRING_OBJECT_TYPE, STRING_TYPE.autoboxesTo()); + + // unboxesTo + assertTypeEquals(STRING_TYPE, STRING_OBJECT_TYPE.unboxesTo()); + + // isSubtype + assertTrue(STRING_TYPE.isSubtype(ALL_TYPE)); + assertFalse(STRING_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(STRING_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(STRING_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(STRING_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(STRING_TYPE.isSubtype(DATE_TYPE)); + assertFalse(STRING_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(STRING_TYPE.isSubtype(ARRAY_TYPE)); + assertTrue(STRING_TYPE.isSubtype(STRING_TYPE)); + assertTrue(STRING_TYPE.isSubtype(UNKNOWN_TYPE)); + + // canBeCalled + assertFalse(STRING_TYPE.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(STRING_TYPE, ALL_TYPE); + assertCanTestForEqualityWith(STRING_TYPE, STRING_OBJECT_TYPE); + assertCannotTestForEqualityWith(STRING_TYPE, functionType); + assertCanTestForEqualityWith(STRING_TYPE, OBJECT_TYPE); + assertCanTestForEqualityWith(STRING_TYPE, NUMBER_TYPE); + assertCanTestForEqualityWith(STRING_TYPE, BOOLEAN_TYPE); + assertCanTestForEqualityWith(STRING_TYPE, BOOLEAN_OBJECT_TYPE); + assertCanTestForEqualityWith(STRING_TYPE, DATE_TYPE); + assertCanTestForEqualityWith(STRING_TYPE, REGEXP_TYPE); + assertCanTestForEqualityWith(STRING_TYPE, ARRAY_TYPE); + assertCanTestForEqualityWith(STRING_TYPE, UNKNOWN_TYPE); + + // canTestForShallowEqualityWith + assertTrue(STRING_TYPE.canTestForShallowEqualityWith(NO_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(functionType)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(STRING_TYPE. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertTrue(STRING_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(STRING_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(STRING_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + assertTrue(STRING_TYPE.canTestForShallowEqualityWith(UNKNOWN_TYPE)); + + // matchesXxx + assertTrue(STRING_TYPE.matchesInt32Context()); + assertTrue(STRING_TYPE.matchesNumberContext()); + assertTrue(STRING_TYPE.matchesObjectContext()); + assertTrue(STRING_TYPE.matchesStringContext()); + assertTrue(STRING_TYPE.matchesUint32Context()); + + // isNullable + assertFalse(STRING_TYPE.isNullable()); + assertTrue(createNullableType(STRING_TYPE).isNullable()); + + // toString + assertEquals("string", STRING_TYPE.toString()); + assertTrue(STRING_TYPE.hasDisplayName()); + assertEquals("string", STRING_TYPE.getDisplayName()); + + // findPropertyType + assertTypeEquals(NUMBER_TYPE, STRING_TYPE.findPropertyType("length")); + assertEquals(null, STRING_TYPE.findPropertyType("unknownProperty")); + + Asserts.assertResolvesToSame(STRING_TYPE); + assertFalse(STRING_TYPE.isNominalConstructor()); + } + + private void assertPropertyTypeDeclared(ObjectType ownerType, String prop) { + assertTrue(ownerType.isPropertyTypeDeclared(prop)); + assertFalse(ownerType.isPropertyTypeInferred(prop)); + } + + private void assertPropertyTypeInferred(ObjectType ownerType, String prop) { + assertFalse(ownerType.isPropertyTypeDeclared(prop)); + assertTrue(ownerType.isPropertyTypeInferred(prop)); + } + + private void assertPropertyTypeUnknown(ObjectType ownerType, String prop) { + assertFalse(ownerType.isPropertyTypeDeclared(prop)); + assertFalse(ownerType.isPropertyTypeInferred(prop)); + assertTrue(ownerType.getPropertyType(prop).isUnknownType()); + } + + private void assertReturnTypeEquals(JSType expectedReturnType, + JSType function) { + assertTrue(function instanceof FunctionType); + assertTypeEquals(expectedReturnType, + ((FunctionType) function).getReturnType()); + } + + + /** + * Tests the behavior of record types. + */ + public void testRecordType() throws Exception { + // isXxx + assertTrue(recordType.isObject()); + assertFalse(recordType.isFunctionPrototypeType()); + + // isSubtype + assertTrue(recordType.isSubtype(ALL_TYPE)); + assertFalse(recordType.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(recordType.isSubtype(NUMBER_TYPE)); + assertFalse(recordType.isSubtype(DATE_TYPE)); + assertFalse(recordType.isSubtype(REGEXP_TYPE)); + assertTrue(recordType.isSubtype(UNKNOWN_TYPE)); + assertTrue(recordType.isSubtype(OBJECT_TYPE)); + assertFalse(recordType.isSubtype(U2U_CONSTRUCTOR_TYPE)); + + // autoboxesTo + assertNull(recordType.autoboxesTo()); + + // canBeCalled + assertFalse(recordType.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(recordType, ALL_TYPE); + assertCanTestForEqualityWith(recordType, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(recordType, recordType); + assertCanTestForEqualityWith(recordType, functionType); + assertCanTestForEqualityWith(recordType, OBJECT_TYPE); + assertCanTestForEqualityWith(recordType, NUMBER_TYPE); + assertCanTestForEqualityWith(recordType, DATE_TYPE); + assertCanTestForEqualityWith(recordType, REGEXP_TYPE); + + // canTestForShallowEqualityWith + assertTrue(recordType.canTestForShallowEqualityWith(NO_TYPE)); + assertTrue(recordType.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertFalse(recordType.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(recordType.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertFalse(recordType. + canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertFalse(recordType.canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(recordType.canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(recordType.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertTrue(recordType.canTestForShallowEqualityWith(recordType)); + assertFalse(recordType.canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(recordType.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertFalse(recordType.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(recordType.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(recordType.canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(recordType.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(recordType. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertFalse(recordType.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(recordType.canTestForShallowEqualityWith(STRING_TYPE)); + assertFalse(recordType.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(recordType.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(recordType.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(recordType.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(recordType.canTestForShallowEqualityWith(VOID_TYPE)); + assertTrue(recordType.canTestForShallowEqualityWith(UNKNOWN_TYPE)); + + // matchesXxx + assertFalse(recordType.matchesInt32Context()); + assertFalse(recordType.matchesNumberContext()); + assertTrue(recordType.matchesObjectContext()); + assertFalse(recordType.matchesStringContext()); + assertFalse(recordType.matchesUint32Context()); + + Asserts.assertResolvesToSame(recordType); + } + + /** + * Tests the behavior of the instance of Function. + */ + public void testFunctionInstanceType() throws Exception { + FunctionType functionInst = FUNCTION_INSTANCE_TYPE; + + // isXxx + assertTrue(functionInst.isObject()); + assertFalse(functionInst.isFunctionPrototypeType()); + assertTrue(functionInst.getImplicitPrototype() + .isFunctionPrototypeType()); + + // isSubtype + assertTrue(functionInst.isSubtype(ALL_TYPE)); + assertFalse(functionInst.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(functionInst.isSubtype(NUMBER_TYPE)); + assertFalse(functionInst.isSubtype(DATE_TYPE)); + assertFalse(functionInst.isSubtype(REGEXP_TYPE)); + assertTrue(functionInst.isSubtype(UNKNOWN_TYPE)); + assertTrue(functionInst.isSubtype(U2U_CONSTRUCTOR_TYPE)); + + // autoboxesTo + assertNull(functionInst.autoboxesTo()); + + // canBeCalled + assertTrue(functionInst.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(functionInst, ALL_TYPE); + assertCanTestForEqualityWith(functionInst, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(functionInst, functionInst); + assertCanTestForEqualityWith(functionInst, OBJECT_TYPE); + assertCannotTestForEqualityWith(functionInst, NUMBER_TYPE); + assertCanTestForEqualityWith(functionInst, DATE_TYPE); + assertCanTestForEqualityWith(functionInst, REGEXP_TYPE); + + // canTestForShallowEqualityWith + assertTrue(functionInst.canTestForShallowEqualityWith(NO_TYPE)); + assertTrue(functionInst.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertFalse(functionInst.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(functionInst.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertFalse(functionInst. + canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertFalse(functionInst.canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(functionInst.canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(functionInst.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertTrue(functionInst.canTestForShallowEqualityWith(functionInst)); + assertFalse(functionInst.canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(functionInst.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertFalse(functionInst.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(functionInst.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(functionInst.canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(functionInst.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(functionInst. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertFalse(functionInst.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(functionInst.canTestForShallowEqualityWith(STRING_TYPE)); + assertFalse(functionInst.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(functionInst.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(functionInst.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(functionInst.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(functionInst.canTestForShallowEqualityWith(VOID_TYPE)); + assertTrue(functionInst.canTestForShallowEqualityWith(UNKNOWN_TYPE)); + + // matchesXxx + assertFalse(functionInst.matchesInt32Context()); + assertFalse(functionInst.matchesNumberContext()); + assertTrue(functionInst.matchesObjectContext()); + assertFalse(functionInst.matchesStringContext()); + assertFalse(functionInst.matchesUint32Context()); + + // hasProperty + assertTrue(functionInst.hasProperty("prototype")); + assertPropertyTypeInferred(functionInst, "prototype"); + + // misc + assertTypeEquals(FUNCTION_FUNCTION_TYPE, functionInst.getConstructor()); + assertTypeEquals(FUNCTION_PROTOTYPE, functionInst.getImplicitPrototype()); + assertTypeEquals(functionInst, FUNCTION_FUNCTION_TYPE.getInstanceType()); + + Asserts.assertResolvesToSame(functionInst); + } + + /** + * Tests the behavior of functional types. + */ + public void testFunctionType() throws Exception { + // isXxx + assertTrue(functionType.isObject()); + assertFalse(functionType.isFunctionPrototypeType()); + assertTrue(functionType.getImplicitPrototype().getImplicitPrototype() + .isFunctionPrototypeType()); + + // isSubtype + assertTrue(functionType.isSubtype(ALL_TYPE)); + assertFalse(functionType.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(functionType.isSubtype(NUMBER_TYPE)); + assertFalse(functionType.isSubtype(DATE_TYPE)); + assertFalse(functionType.isSubtype(REGEXP_TYPE)); + assertTrue(functionType.isSubtype(UNKNOWN_TYPE)); + assertTrue(functionType.isSubtype(U2U_CONSTRUCTOR_TYPE)); + + // autoboxesTo + assertNull(functionType.autoboxesTo()); + + // canBeCalled + assertTrue(functionType.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(functionType, ALL_TYPE); + assertCanTestForEqualityWith(functionType, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(functionType, functionType); + assertCanTestForEqualityWith(functionType, OBJECT_TYPE); + assertCannotTestForEqualityWith(functionType, NUMBER_TYPE); + assertCanTestForEqualityWith(functionType, DATE_TYPE); + assertCanTestForEqualityWith(functionType, REGEXP_TYPE); + + // canTestForShallowEqualityWith + assertTrue(functionType.canTestForShallowEqualityWith(NO_TYPE)); + assertTrue(functionType.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertFalse(functionType.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(functionType.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertFalse(functionType. + canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertFalse(functionType.canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(functionType.canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(functionType.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertTrue(functionType.canTestForShallowEqualityWith(functionType)); + assertFalse(functionType.canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(functionType.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertFalse(functionType.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(functionType.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(functionType.canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(functionType.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(functionType. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertFalse(functionType.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(functionType.canTestForShallowEqualityWith(STRING_TYPE)); + assertFalse(functionType.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(functionType.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(functionType.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(functionType.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(functionType.canTestForShallowEqualityWith(VOID_TYPE)); + assertTrue(functionType.canTestForShallowEqualityWith(UNKNOWN_TYPE)); + + // matchesXxx + assertFalse(functionType.matchesInt32Context()); + assertFalse(functionType.matchesNumberContext()); + assertTrue(functionType.matchesObjectContext()); + assertFalse(functionType.matchesStringContext()); + assertFalse(functionType.matchesUint32Context()); + + // hasProperty + assertTrue(functionType.hasProperty("prototype")); + assertPropertyTypeInferred(functionType, "prototype"); + + Asserts.assertResolvesToSame(functionType); + + + assertEquals("aFunctionName", new FunctionBuilder(registry). + withName("aFunctionName").build().getDisplayName()); + } + + /** + * Tests the subtyping relation of record types. + */ + public void testRecordTypeSubtyping() { + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.addProperty("a", NUMBER_TYPE, null); + builder.addProperty("b", STRING_TYPE, null); + builder.addProperty("c", STRING_TYPE, null); + JSType subRecordType = builder.build(); + + assertTrue(subRecordType.isSubtype(recordType)); + assertFalse(recordType.isSubtype(subRecordType)); + + builder = new RecordTypeBuilder(registry); + builder.addProperty("a", OBJECT_TYPE, null); + builder.addProperty("b", STRING_TYPE, null); + JSType differentRecordType = builder.build(); + + assertFalse(differentRecordType.isSubtype(recordType)); + assertFalse(recordType.isSubtype(differentRecordType)); + } + + /** + * Tests the subtyping relation of record types when an object has + * an inferred property.. + */ + public void testRecordTypeSubtypingWithInferredProperties() { + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.addProperty("a", googSubBarInst, null); + JSType record = builder.build(); + + ObjectType subtypeProp = registry.createAnonymousObjectType(null); + subtypeProp.defineInferredProperty("a", googSubSubBarInst, null); + assertTrue(subtypeProp.isSubtype(record)); + assertFalse(record.isSubtype(subtypeProp)); + + ObjectType supertypeProp = registry.createAnonymousObjectType(null); + supertypeProp.defineInferredProperty("a", googBarInst, null); + assertFalse(supertypeProp.isSubtype(record)); + assertFalse(record.isSubtype(supertypeProp)); + + ObjectType declaredSubtypeProp = registry.createAnonymousObjectType(null); + declaredSubtypeProp.defineDeclaredProperty("a", googSubSubBarInst, + null); + assertFalse(declaredSubtypeProp.isSubtype(record)); + assertFalse(record.isSubtype(declaredSubtypeProp)); + } + + /** + * Tests the getLeastSupertype method for record types. + */ + public void testRecordTypeLeastSuperType1() { + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.addProperty("a", NUMBER_TYPE, null); + builder.addProperty("b", STRING_TYPE, null); + builder.addProperty("c", STRING_TYPE, null); + JSType subRecordType = builder.build(); + + JSType leastSupertype = recordType.getLeastSupertype(subRecordType); + assertTypeEquals(leastSupertype, recordType); + } + + public void testRecordTypeLeastSuperType2() { + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.addProperty("e", NUMBER_TYPE, null); + builder.addProperty("b", STRING_TYPE, null); + builder.addProperty("c", STRING_TYPE, null); + JSType otherRecordType = builder.build(); + + assertTypeEquals( + registry.createUnionType(recordType, otherRecordType), + recordType.getLeastSupertype(otherRecordType)); + } + + public void testRecordTypeLeastSuperType3() { + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.addProperty("d", NUMBER_TYPE, null); + builder.addProperty("e", STRING_TYPE, null); + builder.addProperty("f", STRING_TYPE, null); + JSType otherRecordType = builder.build(); + + assertTypeEquals( + registry.createUnionType(recordType, otherRecordType), + recordType.getLeastSupertype(otherRecordType)); + } + + public void testRecordTypeLeastSuperType4() { + JSType leastSupertype = recordType.getLeastSupertype(OBJECT_TYPE); + assertTypeEquals(leastSupertype, OBJECT_TYPE); + } + + /** + * Tests the getGreatestSubtype method for record types. + */ + public void testRecordTypeGreatestSubType1() { + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.addProperty("d", NUMBER_TYPE, null); + builder.addProperty("e", STRING_TYPE, null); + builder.addProperty("f", STRING_TYPE, null); + + JSType subRecordType = builder.build(); + + JSType subtype = recordType.getGreatestSubtype(subRecordType); + + builder = new RecordTypeBuilder(registry); + builder.addProperty("d", NUMBER_TYPE, null); + builder.addProperty("e", STRING_TYPE, null); + builder.addProperty("f", STRING_TYPE, null); + builder.addProperty("a", NUMBER_TYPE, null); + builder.addProperty("b", STRING_TYPE, null); + + assertTypeEquals(subtype, builder.build()); + } + + public void testRecordTypeGreatestSubType2() { + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + + JSType subRecordType = builder.build(); + + JSType subtype = recordType.getGreatestSubtype(subRecordType); + + builder = new RecordTypeBuilder(registry); + builder.addProperty("a", NUMBER_TYPE, null); + builder.addProperty("b", STRING_TYPE, null); + + assertTypeEquals(subtype, builder.build()); + } + + public void testRecordTypeGreatestSubType3() { + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.addProperty("a", NUMBER_TYPE, null); + builder.addProperty("b", STRING_TYPE, null); + builder.addProperty("c", STRING_TYPE, null); + + JSType subRecordType = builder.build(); + + JSType subtype = recordType.getGreatestSubtype(subRecordType); + + builder = new RecordTypeBuilder(registry); + builder.addProperty("a", NUMBER_TYPE, null); + builder.addProperty("b", STRING_TYPE, null); + builder.addProperty("c", STRING_TYPE, null); + + assertTypeEquals(subtype, builder.build()); + } + + public void testRecordTypeGreatestSubType4() { + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.addProperty("a", STRING_TYPE, null); + builder.addProperty("b", STRING_TYPE, null); + builder.addProperty("c", STRING_TYPE, null); + + JSType subRecordType = builder.build(); + + JSType subtype = recordType.getGreatestSubtype(subRecordType); + assertTypeEquals(subtype, NO_TYPE); + } + + public void testRecordTypeGreatestSubType5() { + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.addProperty("a", STRING_TYPE, null); + + JSType recordType = builder.build(); + + assertTypeEquals(NO_OBJECT_TYPE, + recordType.getGreatestSubtype(U2U_CONSTRUCTOR_TYPE)); + + // if Function is given a property "a" of type "string", then it's + // a subtype of the record type {a: string}. + U2U_CONSTRUCTOR_TYPE.defineDeclaredProperty("a", STRING_TYPE, null); + assertTypeEquals(U2U_CONSTRUCTOR_TYPE, + recordType.getGreatestSubtype(U2U_CONSTRUCTOR_TYPE)); + assertTypeEquals(U2U_CONSTRUCTOR_TYPE, + U2U_CONSTRUCTOR_TYPE.getGreatestSubtype(recordType)); + } + + public void testRecordTypeGreatestSubType6() { + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.addProperty("x", UNKNOWN_TYPE, null); + + JSType recordType = builder.build(); + + assertTypeEquals(NO_OBJECT_TYPE, + recordType.getGreatestSubtype(U2U_CONSTRUCTOR_TYPE)); + + // if Function is given a property "x" of type "string", then it's + // also a subtype of the record type {x: ?}. + U2U_CONSTRUCTOR_TYPE.defineDeclaredProperty("x", STRING_TYPE, null); + assertTypeEquals(U2U_CONSTRUCTOR_TYPE, + recordType.getGreatestSubtype(U2U_CONSTRUCTOR_TYPE)); + assertTypeEquals(U2U_CONSTRUCTOR_TYPE, + U2U_CONSTRUCTOR_TYPE.getGreatestSubtype(recordType)); + } + + public void testRecordTypeGreatestSubType7() { + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.addProperty("x", NUMBER_TYPE, null); + + JSType recordType = builder.build(); + + // if Function is given a property "x" of type "string", then it's + // not a subtype of the record type {x: number}. + U2U_CONSTRUCTOR_TYPE.defineDeclaredProperty("x", STRING_TYPE, null); + assertTypeEquals(NO_OBJECT_TYPE, + recordType.getGreatestSubtype(U2U_CONSTRUCTOR_TYPE)); + } + public void testRecordTypeGreatestSubType8() { + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.addProperty("xyz", UNKNOWN_TYPE, null); + + JSType recordType = builder.build(); + + assertTypeEquals(NO_OBJECT_TYPE, + recordType.getGreatestSubtype(U2U_CONSTRUCTOR_TYPE)); + + // if goog.Bar is given a property "xyz" of type "string", then it's + // also a subtype of the record type {x: ?}. + googBar.defineDeclaredProperty("xyz", STRING_TYPE, null); + + assertTypeEquals(googBar, + recordType.getGreatestSubtype(U2U_CONSTRUCTOR_TYPE)); + assertTypeEquals(googBar, + U2U_CONSTRUCTOR_TYPE.getGreatestSubtype(recordType)); + + ObjectType googBarInst = googBar.getInstanceType(); + assertTypeEquals(NO_OBJECT_TYPE, + recordType.getGreatestSubtype(googBarInst)); + assertTypeEquals(NO_OBJECT_TYPE, + googBarInst.getGreatestSubtype(recordType)); + } + + /** + * Tests the "apply" method on the function type. + */ + public void testApplyOfDateMethod() { + JSType applyType = dateMethod.getPropertyType("apply"); + assertTrue("apply should be a function", + applyType instanceof FunctionType); + + FunctionType applyFn = (FunctionType) applyType; + assertTypeEquals("apply should have the same return type as its function", + NUMBER_TYPE, applyFn.getReturnType()); + + Node params = applyFn.getParametersNode(); + assertEquals("apply takes two args", + 2, params.getChildCount()); + assertTypeEquals("apply's first arg is the @this type", + registry.createOptionalNullableType(DATE_TYPE), + params.getFirstChild().getJSType()); + assertTypeEquals("apply's second arg is an Array", + registry.createOptionalNullableType(OBJECT_TYPE), + params.getLastChild().getJSType()); + assertTrue("apply's args must be optional", + params.getFirstChild().isOptionalArg()); + assertTrue("apply's args must be optional", + params.getLastChild().isOptionalArg()); + } + + /** + * Tests the "call" method on the function type. + */ + public void testCallOfDateMethod() { + JSType callType = dateMethod.getPropertyType("call"); + assertTrue("call should be a function", + callType instanceof FunctionType); + + FunctionType callFn = (FunctionType) callType; + assertTypeEquals("call should have the same return type as its function", + NUMBER_TYPE, callFn.getReturnType()); + + Node params = callFn.getParametersNode(); + assertEquals("call takes one argument in this case", + 1, params.getChildCount()); + assertTypeEquals("call's first arg is the @this type", + registry.createOptionalNullableType(DATE_TYPE), + params.getFirstChild().getJSType()); + assertTrue("call's args must be optional", + params.getFirstChild().isOptionalArg()); + } + + /** + * Tests the representation of function types. + */ + public void testFunctionTypeRepresentation() { + assertEquals("function (number, string): boolean", + registry.createFunctionType(BOOLEAN_TYPE, false, NUMBER_TYPE, + STRING_TYPE).toString()); + + assertEquals("function (new:Array, ...[*]): Array", + ARRAY_FUNCTION_TYPE.toString()); + + assertEquals("function (new:Boolean, *=): boolean", + BOOLEAN_OBJECT_FUNCTION_TYPE.toString()); + + assertEquals("function (new:Number, *=): number", + NUMBER_OBJECT_FUNCTION_TYPE.toString()); + + assertEquals("function (new:String, *=): string", + STRING_OBJECT_FUNCTION_TYPE.toString()); + + assertEquals("function (...[number]): boolean", + registry.createFunctionType(BOOLEAN_TYPE, true, NUMBER_TYPE) + .toString()); + + assertEquals("function (number, ...[string]): boolean", + registry.createFunctionType(BOOLEAN_TYPE, true, NUMBER_TYPE, + STRING_TYPE).toString()); + + assertEquals("function (this:Date, number): (boolean|number|string)", + new FunctionBuilder(registry) + .withParamsNode(registry.createParameters(NUMBER_TYPE)) + .withReturnType(NUMBER_STRING_BOOLEAN) + .withTypeOfThis(DATE_TYPE) + .build().toString()); + } + + /** + * Tests relationships between structural function types. + */ + public void testFunctionTypeRelationships() { + FunctionType dateMethodEmpty = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters()) + .withTypeOfThis(DATE_TYPE).build(); + FunctionType dateMethodWithParam = new FunctionBuilder(registry) + .withParamsNode(registry.createOptionalParameters(NUMBER_TYPE)) + .withTypeOfThis(DATE_TYPE).build(); + FunctionType dateMethodWithReturn = new FunctionBuilder(registry) + .withReturnType(NUMBER_TYPE) + .withTypeOfThis(DATE_TYPE).build(); + FunctionType stringMethodEmpty = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters()) + .withTypeOfThis(STRING_OBJECT_TYPE).build(); + FunctionType stringMethodWithParam = new FunctionBuilder(registry) + .withParamsNode(registry.createOptionalParameters(NUMBER_TYPE)) + .withTypeOfThis(STRING_OBJECT_TYPE).build(); + FunctionType stringMethodWithReturn = new FunctionBuilder(registry) + .withReturnType(NUMBER_TYPE) + .withTypeOfThis(STRING_OBJECT_TYPE).build(); + + // One-off tests. + assertFalse(stringMethodEmpty.isSubtype(dateMethodEmpty)); + + // Systemic tests. + List allFunctions = Lists.newArrayList( + dateMethodEmpty, dateMethodWithParam, dateMethodWithReturn, + stringMethodEmpty, stringMethodWithParam, stringMethodWithReturn); + for (int i = 0; i < allFunctions.size(); i++) { + for (int j = 0; j < allFunctions.size(); j++) { + FunctionType typeA = allFunctions.get(i); + FunctionType typeB = allFunctions.get(j); + assertEquals(String.format("equals(%s, %s)", typeA, typeB), + i == j, typeA.isEquivalentTo(typeB)); + + // For this particular set of functions, the functions are subtypes + // of each other iff they have the same "this" type. + assertEquals(String.format("isSubtype(%s, %s)", typeA, typeB), + typeA.getTypeOfThis().isEquivalentTo(typeB.getTypeOfThis()), + typeA.isSubtype(typeB)); + + if (i == j) { + assertTypeEquals(typeA, typeA.getLeastSupertype(typeB)); + assertTypeEquals(typeA, typeA.getGreatestSubtype(typeB)); + } else { + assertTypeEquals(String.format("sup(%s, %s)", typeA, typeB), + U2U_CONSTRUCTOR_TYPE, typeA.getLeastSupertype(typeB)); + assertTypeEquals(String.format("inf(%s, %s)", typeA, typeB), + LEAST_FUNCTION_TYPE, typeA.getGreatestSubtype(typeB)); + } + } + } + } + + public void testProxiedFunctionTypeRelationships() { + FunctionType dateMethodEmpty = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters()) + .withTypeOfThis(DATE_TYPE).build().toMaybeFunctionType(); + FunctionType dateMethodWithParam = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters(NUMBER_TYPE)) + .withTypeOfThis(DATE_TYPE).build().toMaybeFunctionType(); + ProxyObjectType proxyDateMethodEmpty = + new ProxyObjectType(registry, dateMethodEmpty); + ProxyObjectType proxyDateMethodWithParam = + new ProxyObjectType(registry, dateMethodWithParam); + + assertTypeEquals(U2U_CONSTRUCTOR_TYPE, + proxyDateMethodEmpty.getLeastSupertype(proxyDateMethodWithParam)); + assertTypeEquals(LEAST_FUNCTION_TYPE, + proxyDateMethodEmpty.getGreatestSubtype(proxyDateMethodWithParam)); + } + + /** + * Tests relationships between structural function types. + */ + public void testFunctionSubTypeRelationships() { + FunctionType googBarMethod = new FunctionBuilder(registry) + .withTypeOfThis(googBar).build(); + FunctionType googBarParamFn = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters(googBar)).build(); + FunctionType googBarReturnFn = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters()) + .withReturnType(googBar).build(); + FunctionType googSubBarMethod = new FunctionBuilder(registry) + .withTypeOfThis(googSubBar).build(); + FunctionType googSubBarParamFn = new FunctionBuilder(registry) + .withParamsNode(registry.createParameters(googSubBar)).build(); + FunctionType googSubBarReturnFn = new FunctionBuilder(registry) + .withReturnType(googSubBar).build(); + + assertTrue(googBarMethod.isSubtype(googSubBarMethod)); + assertTrue(googBarReturnFn.isSubtype(googSubBarReturnFn)); + + List allFunctions = Lists.newArrayList( + googBarMethod, googBarParamFn, googBarReturnFn, + googSubBarMethod, googSubBarParamFn, googSubBarReturnFn); + for (int i = 0; i < allFunctions.size(); i++) { + for (int j = 0; j < allFunctions.size(); j++) { + FunctionType typeA = allFunctions.get(i); + FunctionType typeB = allFunctions.get(j); + assertEquals(String.format("equals(%s, %s)", typeA, typeB), + i == j, typeA.isEquivalentTo(typeB)); + + // TODO(nicksantos): This formulation of least subtype and greatest + // supertype is a bit loose. We might want to tighten it up later. + if (i == j) { + assertTypeEquals(typeA, typeA.getLeastSupertype(typeB)); + assertTypeEquals(typeA, typeA.getGreatestSubtype(typeB)); + } else { + assertTypeEquals(String.format("sup(%s, %s)", typeA, typeB), + U2U_CONSTRUCTOR_TYPE, typeA.getLeastSupertype(typeB)); + assertTypeEquals(String.format("inf(%s, %s)", typeA, typeB), + LEAST_FUNCTION_TYPE, typeA.getGreatestSubtype(typeB)); + } + } + } + } + + /** + * Tests that defining a property of a function's {@code prototype} adds the + * property to it instance type. + */ + public void testFunctionPrototypeAndImplicitPrototype1() { + FunctionType constructor = + registry.createConstructorType("Foo", null, null, null, null); + ObjectType instance = constructor.getInstanceType(); + + // adding one property on the prototype + ObjectType prototype = + (ObjectType) constructor.getPropertyType("prototype"); + prototype.defineDeclaredProperty("foo", DATE_TYPE, null); + + assertEquals(NATIVE_PROPERTIES_COUNT + 1, instance.getPropertiesCount()); + } + + /** + * Tests that replacing a function's {@code prototype} changes the visible + * properties of its instance type. + */ + public void testFunctionPrototypeAndImplicitPrototype2() { + FunctionType constructor = + registry.createConstructorType(null, null, null, null); + ObjectType instance = constructor.getInstanceType(); + + // replacing the prototype + ObjectType prototype = registry.createAnonymousObjectType(null); + prototype.defineDeclaredProperty("foo", DATE_TYPE, null); + constructor.defineDeclaredProperty("prototype", prototype, null); + + assertEquals(NATIVE_PROPERTIES_COUNT + 1, instance.getPropertiesCount()); + } + + /** Tests assigning JsDoc on a prototype property. */ + public void testJSDocOnPrototypeProperty() throws Exception { + subclassCtor.setPropertyJSDocInfo("prototype", new JSDocInfo()); + assertNull(subclassCtor.getOwnPropertyJSDocInfo("prototype")); + } + + /** + * Tests the behavior of the void type. + */ + public void testVoidType() throws Exception { + // isSubtype + assertTrue(VOID_TYPE.isSubtype(ALL_TYPE)); + assertFalse(VOID_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(VOID_TYPE.isSubtype(REGEXP_TYPE)); + + // autoboxesTo + assertNull(VOID_TYPE.autoboxesTo()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(VOID_TYPE, ALL_TYPE); + assertCannotTestForEqualityWith(VOID_TYPE, REGEXP_TYPE); + + // canTestForShallowEqualityWith + assertTrue(VOID_TYPE.canTestForShallowEqualityWith(NO_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(functionType)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(VOID_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(VOID_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); + assertTrue(VOID_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + assertTrue(VOID_TYPE.canTestForShallowEqualityWith( + createUnionType(NUMBER_TYPE, VOID_TYPE))); + + // matchesXxx + assertFalse(VOID_TYPE.matchesInt32Context()); + assertFalse(VOID_TYPE.matchesNumberContext()); + assertFalse(VOID_TYPE.matchesObjectContext()); + assertTrue(VOID_TYPE.matchesStringContext()); + assertFalse(VOID_TYPE.matchesUint32Context()); + + Asserts.assertResolvesToSame(VOID_TYPE); + } + + /** + * Tests the behavior of the boolean type. + */ + public void testBooleanValueType() throws Exception { + // isXxx + assertFalse(BOOLEAN_TYPE.isArrayType()); + assertFalse(BOOLEAN_TYPE.isBooleanObjectType()); + assertTrue(BOOLEAN_TYPE.isBooleanValueType()); + assertFalse(BOOLEAN_TYPE.isDateType()); + assertFalse(BOOLEAN_TYPE.isEnumElementType()); + assertFalse(BOOLEAN_TYPE.isNamedType()); + assertFalse(BOOLEAN_TYPE.isNullType()); + assertFalse(BOOLEAN_TYPE.isNumberObjectType()); + assertFalse(BOOLEAN_TYPE.isNumberValueType()); + assertFalse(BOOLEAN_TYPE.isFunctionPrototypeType()); + assertFalse(BOOLEAN_TYPE.isRegexpType()); + assertFalse(BOOLEAN_TYPE.isStringObjectType()); + assertFalse(BOOLEAN_TYPE.isStringValueType()); + assertFalse(BOOLEAN_TYPE.isEnumType()); + assertFalse(BOOLEAN_TYPE.isUnionType()); + assertFalse(BOOLEAN_TYPE.isStruct()); + assertFalse(BOOLEAN_TYPE.isDict()); + assertFalse(BOOLEAN_TYPE.isAllType()); + assertFalse(BOOLEAN_TYPE.isVoidType()); + assertFalse(BOOLEAN_TYPE.isConstructor()); + assertFalse(BOOLEAN_TYPE.isInstanceType()); + + // autoboxesTo + assertTypeEquals(BOOLEAN_OBJECT_TYPE, BOOLEAN_TYPE.autoboxesTo()); + + // unboxesTo + assertTypeEquals(BOOLEAN_TYPE, BOOLEAN_OBJECT_TYPE.unboxesTo()); + + // isSubtype + assertTrue(BOOLEAN_TYPE.isSubtype(ALL_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(functionType)); + assertFalse(BOOLEAN_TYPE.isSubtype(NULL_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(DATE_TYPE)); + assertTrue(BOOLEAN_TYPE.isSubtype(unresolvedNamedType)); + assertFalse(BOOLEAN_TYPE.isSubtype(namedGoogBar)); + assertFalse(BOOLEAN_TYPE.isSubtype(REGEXP_TYPE)); + + // canBeCalled + assertFalse(BOOLEAN_TYPE.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(BOOLEAN_TYPE, ALL_TYPE); + assertCanTestForEqualityWith(BOOLEAN_TYPE, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(BOOLEAN_TYPE, NUMBER_TYPE); + assertCannotTestForEqualityWith(BOOLEAN_TYPE, functionType); + assertCannotTestForEqualityWith(BOOLEAN_TYPE, VOID_TYPE); + assertCanTestForEqualityWith(BOOLEAN_TYPE, OBJECT_TYPE); + assertCanTestForEqualityWith(BOOLEAN_TYPE, DATE_TYPE); + assertCanTestForEqualityWith(BOOLEAN_TYPE, REGEXP_TYPE); + assertCanTestForEqualityWith(BOOLEAN_TYPE, UNKNOWN_TYPE); + + // canTestForShallowEqualityWith + assertTrue(BOOLEAN_TYPE.canTestForShallowEqualityWith(NO_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertTrue(BOOLEAN_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertFalse(BOOLEAN_TYPE. + canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(functionType)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(BOOLEAN_TYPE. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(BOOLEAN_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + assertTrue(BOOLEAN_TYPE.canTestForShallowEqualityWith(UNKNOWN_TYPE)); + + // isNullable + assertFalse(BOOLEAN_TYPE.isNullable()); + + // matchesXxx + assertTrue(BOOLEAN_TYPE.matchesInt32Context()); + assertTrue(BOOLEAN_TYPE.matchesNumberContext()); + assertTrue(BOOLEAN_TYPE.matchesObjectContext()); + assertTrue(BOOLEAN_TYPE.matchesStringContext()); + assertTrue(BOOLEAN_TYPE.matchesUint32Context()); + + // toString + assertEquals("boolean", BOOLEAN_TYPE.toString()); + assertTrue(BOOLEAN_TYPE.hasDisplayName()); + assertEquals("boolean", BOOLEAN_TYPE.getDisplayName()); + + Asserts.assertResolvesToSame(BOOLEAN_TYPE); + } + + /** + * Tests the behavior of the Boolean type. + */ + public void testBooleanObjectType() throws Exception { + // isXxx + assertFalse(BOOLEAN_OBJECT_TYPE.isArrayType()); + assertTrue(BOOLEAN_OBJECT_TYPE.isBooleanObjectType()); + assertFalse(BOOLEAN_OBJECT_TYPE.isBooleanValueType()); + assertFalse(BOOLEAN_OBJECT_TYPE.isDateType()); + assertFalse(BOOLEAN_OBJECT_TYPE.isEnumElementType()); + assertFalse(BOOLEAN_OBJECT_TYPE.isNamedType()); + assertFalse(BOOLEAN_OBJECT_TYPE.isNullType()); + assertFalse(BOOLEAN_OBJECT_TYPE.isNumberObjectType()); + assertFalse(BOOLEAN_OBJECT_TYPE.isNumberValueType()); + assertFalse(BOOLEAN_OBJECT_TYPE.isFunctionPrototypeType()); + assertTrue( + BOOLEAN_OBJECT_TYPE.getImplicitPrototype().isFunctionPrototypeType()); + assertFalse(BOOLEAN_OBJECT_TYPE.isRegexpType()); + assertFalse(BOOLEAN_OBJECT_TYPE.isStringObjectType()); + assertFalse(BOOLEAN_OBJECT_TYPE.isStringValueType()); + assertFalse(BOOLEAN_OBJECT_TYPE.isEnumType()); + assertFalse(BOOLEAN_OBJECT_TYPE.isUnionType()); + assertFalse(BOOLEAN_OBJECT_TYPE.isStruct()); + assertFalse(BOOLEAN_OBJECT_TYPE.isDict()); + assertFalse(BOOLEAN_OBJECT_TYPE.isAllType()); + assertFalse(BOOLEAN_OBJECT_TYPE.isVoidType()); + assertFalse(BOOLEAN_OBJECT_TYPE.isConstructor()); + assertTrue(BOOLEAN_OBJECT_TYPE.isInstanceType()); + + // isSubtype + assertTrue(BOOLEAN_OBJECT_TYPE.isSubtype(ALL_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(functionType)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(NULL_TYPE)); + assertTrue(BOOLEAN_OBJECT_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(DATE_TYPE)); + assertTrue(BOOLEAN_OBJECT_TYPE.isSubtype(unresolvedNamedType)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(namedGoogBar)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(REGEXP_TYPE)); + // canBeCalled + assertFalse(BOOLEAN_OBJECT_TYPE.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(BOOLEAN_OBJECT_TYPE, ALL_TYPE); + assertCanTestForEqualityWith(BOOLEAN_OBJECT_TYPE, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(BOOLEAN_OBJECT_TYPE, NUMBER_TYPE); + assertCanTestForEqualityWith(BOOLEAN_OBJECT_TYPE, functionType); + assertCannotTestForEqualityWith(BOOLEAN_OBJECT_TYPE, VOID_TYPE); + assertCanTestForEqualityWith(BOOLEAN_OBJECT_TYPE, OBJECT_TYPE); + assertCanTestForEqualityWith(BOOLEAN_OBJECT_TYPE, DATE_TYPE); + assertCanTestForEqualityWith(BOOLEAN_OBJECT_TYPE, REGEXP_TYPE); + + // canTestForShallowEqualityWith + assertTrue(BOOLEAN_OBJECT_TYPE.canTestForShallowEqualityWith(NO_TYPE)); + assertTrue(BOOLEAN_OBJECT_TYPE. + canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE. + canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertTrue(BOOLEAN_OBJECT_TYPE. + canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE. + canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE. + canTestForShallowEqualityWith(functionType)); + assertFalse(BOOLEAN_OBJECT_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE. + canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(BOOLEAN_OBJECT_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE. + canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE. + canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE. + canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE. + canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE. + canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(BOOLEAN_OBJECT_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); + + // isNullable + assertFalse(BOOLEAN_OBJECT_TYPE.isNullable()); + + // matchesXxx + assertTrue(BOOLEAN_OBJECT_TYPE.matchesInt32Context()); + assertTrue(BOOLEAN_OBJECT_TYPE.matchesNumberContext()); + assertTrue(BOOLEAN_OBJECT_TYPE.matchesObjectContext()); + assertTrue(BOOLEAN_OBJECT_TYPE.matchesStringContext()); + assertTrue(BOOLEAN_OBJECT_TYPE.matchesUint32Context()); + + // toString + assertEquals("Boolean", BOOLEAN_OBJECT_TYPE.toString()); + assertTrue(BOOLEAN_OBJECT_TYPE.hasDisplayName()); + assertEquals("Boolean", BOOLEAN_OBJECT_TYPE.getDisplayName()); + + assertTrue(BOOLEAN_OBJECT_TYPE.isNativeObjectType()); + + Asserts.assertResolvesToSame(BOOLEAN_OBJECT_TYPE); + } + + /** + * Tests the behavior of the enum type. + */ + public void testEnumType() throws Exception { + EnumType enumType = new EnumType(registry, "Enum", null, NUMBER_TYPE); + + // isXxx + assertFalse(enumType.isArrayType()); + assertFalse(enumType.isBooleanObjectType()); + assertFalse(enumType.isBooleanValueType()); + assertFalse(enumType.isDateType()); + assertFalse(enumType.isEnumElementType()); + assertFalse(enumType.isNamedType()); + assertFalse(enumType.isNullType()); + assertFalse(enumType.isNumberObjectType()); + assertFalse(enumType.isNumberValueType()); + assertFalse(enumType.isFunctionPrototypeType()); + assertFalse(enumType.isRegexpType()); + assertFalse(enumType.isStringObjectType()); + assertFalse(enumType.isStringValueType()); + assertTrue(enumType.isEnumType()); + assertFalse(enumType.isUnionType()); + assertFalse(enumType.isStruct()); + assertFalse(enumType.isDict()); + assertFalse(enumType.isAllType()); + assertFalse(enumType.isVoidType()); + assertFalse(enumType.isConstructor()); + assertFalse(enumType.isInstanceType()); + + // isSubtype + assertTrue(enumType.isSubtype(ALL_TYPE)); + assertFalse(enumType.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(enumType.isSubtype(NUMBER_TYPE)); + assertFalse(enumType.isSubtype(functionType)); + assertFalse(enumType.isSubtype(NULL_TYPE)); + assertTrue(enumType.isSubtype(OBJECT_TYPE)); + assertFalse(enumType.isSubtype(DATE_TYPE)); + assertTrue(enumType.isSubtype(unresolvedNamedType)); + assertFalse(enumType.isSubtype(namedGoogBar)); + assertFalse(enumType.isSubtype(REGEXP_TYPE)); + + // canBeCalled + assertFalse(enumType.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(enumType, ALL_TYPE); + assertCanTestForEqualityWith(enumType, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(enumType, NUMBER_TYPE); + assertCanTestForEqualityWith(enumType, functionType); + assertCannotTestForEqualityWith(enumType, VOID_TYPE); + assertCanTestForEqualityWith(enumType, OBJECT_TYPE); + assertCanTestForEqualityWith(enumType, DATE_TYPE); + assertCanTestForEqualityWith(enumType, REGEXP_TYPE); + + // canTestForShallowEqualityWith + assertTrue(enumType.canTestForShallowEqualityWith(NO_TYPE)); + assertTrue(enumType. + canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertFalse(enumType.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(enumType. + canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertTrue(enumType. + canTestForShallowEqualityWith(enumType)); + assertFalse(enumType.canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(enumType.canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(enumType. + canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertFalse(enumType. + canTestForShallowEqualityWith(functionType)); + assertFalse(enumType.canTestForShallowEqualityWith(NULL_TYPE)); + assertFalse(enumType.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertFalse(enumType. + canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertTrue(enumType.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(enumType. + canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(enumType. + canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(enumType. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertFalse(enumType.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(enumType.canTestForShallowEqualityWith(STRING_TYPE)); + assertFalse(enumType. + canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(enumType. + canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(enumType. + canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(enumType.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(enumType.canTestForShallowEqualityWith(VOID_TYPE)); + + // isNullable + assertFalse(enumType.isNullable()); + + // matchesXxx + assertFalse(enumType.matchesInt32Context()); + assertFalse(enumType.matchesNumberContext()); + assertTrue(enumType.matchesObjectContext()); + assertTrue(enumType.matchesStringContext()); + assertFalse(enumType.matchesUint32Context()); + + // toString + assertEquals("enum{Enum}", enumType.toString()); + assertTrue(enumType.hasDisplayName()); + assertEquals("Enum", enumType.getDisplayName()); + + assertEquals("AnotherEnum", new EnumType(registry, "AnotherEnum", + null, NUMBER_TYPE).getDisplayName()); + assertFalse( + new EnumType(registry, null, null, NUMBER_TYPE).hasDisplayName()); + + Asserts.assertResolvesToSame(enumType); + } + + /** + * Tests the behavior of the enum element type. + */ + public void testEnumElementType() throws Exception { + // isXxx + assertFalse(elementsType.isArrayType()); + assertFalse(elementsType.isBooleanObjectType()); + assertFalse(elementsType.isBooleanValueType()); + assertFalse(elementsType.isDateType()); + assertTrue(elementsType.isEnumElementType()); + assertFalse(elementsType.isNamedType()); + assertFalse(elementsType.isNullType()); + assertFalse(elementsType.isNumberObjectType()); + assertFalse(elementsType.isNumberValueType()); + assertFalse(elementsType.isFunctionPrototypeType()); + assertFalse(elementsType.isRegexpType()); + assertFalse(elementsType.isStringObjectType()); + assertFalse(elementsType.isStringValueType()); + assertFalse(elementsType.isEnumType()); + assertFalse(elementsType.isUnionType()); + assertFalse(elementsType.isStruct()); + assertFalse(elementsType.isDict()); + assertFalse(elementsType.isAllType()); + assertFalse(elementsType.isVoidType()); + assertFalse(elementsType.isConstructor()); + assertFalse(elementsType.isInstanceType()); + + // isSubtype + assertTrue(elementsType.isSubtype(ALL_TYPE)); + assertFalse(elementsType.isSubtype(STRING_OBJECT_TYPE)); + assertTrue(elementsType.isSubtype(NUMBER_TYPE)); + assertFalse(elementsType.isSubtype(functionType)); + assertFalse(elementsType.isSubtype(NULL_TYPE)); + assertFalse(elementsType.isSubtype(OBJECT_TYPE)); // no more autoboxing + assertFalse(elementsType.isSubtype(DATE_TYPE)); + assertTrue(elementsType.isSubtype(unresolvedNamedType)); + assertFalse(elementsType.isSubtype(namedGoogBar)); + assertFalse(elementsType.isSubtype(REGEXP_TYPE)); + + // canBeCalled + assertFalse(elementsType.canBeCalled()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(elementsType, ALL_TYPE); + assertCanTestForEqualityWith(elementsType, STRING_OBJECT_TYPE); + assertCanTestForEqualityWith(elementsType, NUMBER_TYPE); + assertCanTestForEqualityWith(elementsType, NUMBER_OBJECT_TYPE); + assertCanTestForEqualityWith(elementsType, elementsType); + assertCannotTestForEqualityWith(elementsType, functionType); + assertCannotTestForEqualityWith(elementsType, VOID_TYPE); + assertCanTestForEqualityWith(elementsType, OBJECT_TYPE); + assertCanTestForEqualityWith(elementsType, DATE_TYPE); + assertCanTestForEqualityWith(elementsType, REGEXP_TYPE); + + // canTestForShallowEqualityWith + assertTrue(elementsType.canTestForShallowEqualityWith(NO_TYPE)); + assertFalse(elementsType. + canTestForShallowEqualityWith(NO_OBJECT_TYPE)); + assertFalse(elementsType.canTestForShallowEqualityWith(ARRAY_TYPE)); + assertFalse(elementsType. + canTestForShallowEqualityWith(BOOLEAN_TYPE)); + assertTrue(elementsType. + canTestForShallowEqualityWith(elementsType)); + assertFalse(elementsType.canTestForShallowEqualityWith(DATE_TYPE)); + assertFalse(elementsType.canTestForShallowEqualityWith(ERROR_TYPE)); + assertFalse(elementsType. + canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); + assertFalse(elementsType. + canTestForShallowEqualityWith(functionType)); + assertFalse(elementsType.canTestForShallowEqualityWith(NULL_TYPE)); + assertTrue(elementsType.canTestForShallowEqualityWith(NUMBER_TYPE)); + assertFalse(elementsType. + canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); + assertFalse(elementsType.canTestForShallowEqualityWith(OBJECT_TYPE)); + assertFalse(elementsType. + canTestForShallowEqualityWith(URI_ERROR_TYPE)); + assertFalse(elementsType. + canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); + assertFalse(elementsType. + canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); + assertFalse(elementsType.canTestForShallowEqualityWith(REGEXP_TYPE)); + assertFalse(elementsType.canTestForShallowEqualityWith(STRING_TYPE)); + assertFalse(elementsType. + canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); + assertFalse(elementsType. + canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); + assertFalse(elementsType. + canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); + assertTrue(elementsType.canTestForShallowEqualityWith(ALL_TYPE)); + assertFalse(elementsType.canTestForShallowEqualityWith(VOID_TYPE)); + + // isNullable + assertFalse(elementsType.isNullable()); + + // matchesXxx + assertTrue(elementsType.matchesInt32Context()); + assertTrue(elementsType.matchesNumberContext()); + assertTrue(elementsType.matchesObjectContext()); + assertTrue(elementsType.matchesStringContext()); + assertTrue(elementsType.matchesUint32Context()); + + // toString + assertEquals("Enum.", elementsType.toString()); + assertTrue(elementsType.hasDisplayName()); + assertEquals("Enum", elementsType.getDisplayName()); + + Asserts.assertResolvesToSame(elementsType); + } + + public void testStringEnumType() throws Exception { + EnumElementType stringEnum = + new EnumType(registry, "Enum", null, STRING_TYPE).getElementsType(); + + assertTypeEquals(UNKNOWN_TYPE, stringEnum.getPropertyType("length")); + assertTypeEquals(NUMBER_TYPE, stringEnum.findPropertyType("length")); + assertEquals(false, stringEnum.hasProperty("length")); + assertTypeEquals(STRING_OBJECT_TYPE, stringEnum.autoboxesTo()); + assertNull(stringEnum.getConstructor()); + + Asserts.assertResolvesToSame(stringEnum); + } + + public void testStringObjectEnumType() throws Exception { + EnumElementType stringEnum = + new EnumType(registry, "Enum", null, STRING_OBJECT_TYPE) + .getElementsType(); + + assertTypeEquals(NUMBER_TYPE, stringEnum.getPropertyType("length")); + assertTypeEquals(NUMBER_TYPE, stringEnum.findPropertyType("length")); + assertEquals(true, stringEnum.hasProperty("length")); + assertTypeEquals(STRING_OBJECT_FUNCTION_TYPE, stringEnum.getConstructor()); + } + + + /** + * Tests object types. + */ + public void testObjectType() throws Exception { + PrototypeObjectType objectType = + new PrototypeObjectType(registry, null, null); + + // isXxx + assertFalse(objectType.isAllType()); + assertFalse(objectType.isArrayType()); + assertFalse(objectType.isDateType()); + assertFalse(objectType.isFunctionPrototypeType()); + assertTrue(objectType.getImplicitPrototype() == OBJECT_TYPE); + + // isSubtype + assertTrue(objectType.isSubtype(ALL_TYPE)); + assertFalse(objectType.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(objectType.isSubtype(NUMBER_TYPE)); + assertFalse(objectType.isSubtype(functionType)); + assertFalse(objectType.isSubtype(NULL_TYPE)); + assertFalse(objectType.isSubtype(DATE_TYPE)); + assertTrue(objectType.isSubtype(OBJECT_TYPE)); + assertTrue(objectType.isSubtype(unresolvedNamedType)); + assertFalse(objectType.isSubtype(namedGoogBar)); + assertFalse(objectType.isSubtype(REGEXP_TYPE)); + + // autoboxesTo + assertNull(objectType.autoboxesTo()); + + // canTestForEqualityWith + assertCanTestForEqualityWith(objectType, NUMBER_TYPE); + + // matchesXxxContext + assertFalse(objectType.matchesInt32Context()); + assertFalse(objectType.matchesNumberContext()); + assertTrue(objectType.matchesObjectContext()); + assertFalse(objectType.matchesStringContext()); + assertFalse(objectType.matchesUint32Context()); + + // isNullable + assertFalse(objectType.isNullable()); + assertTrue(createNullableType(objectType).isNullable()); + + // toString + assertEquals("{...}", objectType.toString()); + assertEquals(null, objectType.getDisplayName()); + assertFalse(objectType.hasReferenceName()); + assertEquals("anObject", new PrototypeObjectType(registry, "anObject", + null).getDisplayName()); + + Asserts.assertResolvesToSame(objectType); + } + + /** + * Tests the goog.Bar type. + */ + public void testGoogBar() throws Exception { + assertTrue(namedGoogBar.isInstanceType()); + assertFalse(googBar.isInstanceType()); + assertFalse(namedGoogBar.isConstructor()); + assertTrue(googBar.isConstructor()); + assertTrue(googBar.getInstanceType().isInstanceType()); + assertTrue(namedGoogBar.getConstructor().isConstructor()); + assertTrue(namedGoogBar.getImplicitPrototype().isFunctionPrototypeType()); + + // isSubtype + assertTypeCanAssignToItself(googBar); + assertTypeCanAssignToItself(namedGoogBar); + googBar.isSubtype(namedGoogBar); + namedGoogBar.isSubtype(googBar); + assertTypeEquals(googBar, googBar); + assertTypeNotEquals(googBar, googSubBar); + + Asserts.assertResolvesToSame(googBar); + Asserts.assertResolvesToSame(googSubBar); + } + + /** + * Tests how properties are counted for object types. + */ + public void testObjectTypePropertiesCount() throws Exception { + ObjectType sup = registry.createAnonymousObjectType(null); + int nativeProperties = sup.getPropertiesCount(); + + sup.defineDeclaredProperty("a", DATE_TYPE, null); + assertEquals(nativeProperties + 1, sup.getPropertiesCount()); + + sup.defineDeclaredProperty("b", DATE_TYPE, null); + assertEquals(nativeProperties + 2, sup.getPropertiesCount()); + + ObjectType sub = registry.createObjectType(sup); + assertEquals(nativeProperties + 2, sub.getPropertiesCount()); + } + + /** + * Tests how properties are defined. + */ + public void testDefineProperties() { + ObjectType prototype = googBar.getPrototype(); + ObjectType instance = googBar.getInstanceType(); + + assertTypeEquals(instance.getImplicitPrototype(), prototype); + + // Test declarations. + assertTrue( + prototype.defineDeclaredProperty("declared", NUMBER_TYPE, null)); + assertFalse( + prototype.defineDeclaredProperty("declared", NUMBER_TYPE, null)); + assertFalse( + instance.defineDeclaredProperty("declared", NUMBER_TYPE, null)); + assertTypeEquals(NUMBER_TYPE, instance.getPropertyType("declared")); + + // Test inferring different types. + assertTrue(prototype.defineInferredProperty("inferred1", STRING_TYPE, + null)); + assertTrue(prototype.defineInferredProperty("inferred1", NUMBER_TYPE, + null)); + assertTypeEquals( + createUnionType(NUMBER_TYPE, STRING_TYPE), + instance.getPropertyType("inferred1")); + + // Test inferring different types on different objects. + assertTrue(prototype.defineInferredProperty("inferred2", STRING_TYPE, + null)); + assertTrue(instance.defineInferredProperty("inferred2", NUMBER_TYPE, + null)); + assertTypeEquals( + createUnionType(NUMBER_TYPE, STRING_TYPE), + instance.getPropertyType("inferred2")); + + // Test inferring on the supertype and declaring on the subtype. + assertTrue( + prototype.defineInferredProperty("prop", STRING_TYPE, null)); + assertTrue( + instance.defineDeclaredProperty("prop", NUMBER_TYPE, null)); + assertTypeEquals(NUMBER_TYPE, instance.getPropertyType("prop")); + assertTypeEquals(STRING_TYPE, prototype.getPropertyType("prop")); + } + + /** + * Tests that properties are correctly counted even when shadowing occurs. + */ + public void testObjectTypePropertiesCountWithShadowing() { + ObjectType sup = registry.createAnonymousObjectType(null); + int nativeProperties = sup.getPropertiesCount(); + + sup.defineDeclaredProperty("a", OBJECT_TYPE, null); + assertEquals(nativeProperties + 1, sup.getPropertiesCount()); + + ObjectType sub = registry.createObjectType(sup); + sub.defineDeclaredProperty("a", OBJECT_TYPE, null); + assertEquals(nativeProperties + 1, sub.getPropertiesCount()); + } + + /** + * Tests the named type goog.Bar. + */ + public void testNamedGoogBar() throws Exception { + // isXxx + assertFalse(namedGoogBar.isFunctionPrototypeType()); + assertTrue(namedGoogBar.getImplicitPrototype().isFunctionPrototypeType()); + + // isSubtype + assertTrue(namedGoogBar.isSubtype(ALL_TYPE)); + assertFalse(namedGoogBar.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(namedGoogBar.isSubtype(NUMBER_TYPE)); + assertFalse(namedGoogBar.isSubtype(functionType)); + assertFalse(namedGoogBar.isSubtype(NULL_TYPE)); + assertTrue(namedGoogBar.isSubtype(OBJECT_TYPE)); + assertFalse(namedGoogBar.isSubtype(DATE_TYPE)); + assertTrue(namedGoogBar.isSubtype(namedGoogBar)); + assertTrue(namedGoogBar.isSubtype(unresolvedNamedType)); + assertFalse(namedGoogBar.isSubtype(REGEXP_TYPE)); + assertFalse(namedGoogBar.isSubtype(ARRAY_TYPE)); + + // autoboxesTo + assertNull(namedGoogBar.autoboxesTo()); + + // properties + assertTypeEquals(DATE_TYPE, namedGoogBar.getPropertyType("date")); + + assertFalse(namedGoogBar.isNativeObjectType()); + assertFalse(namedGoogBar.getImplicitPrototype().isNativeObjectType()); + + JSType resolvedNamedGoogBar = Asserts.assertValidResolve(namedGoogBar); + assertNotSame(resolvedNamedGoogBar, namedGoogBar); + assertSame(resolvedNamedGoogBar, googBar.getInstanceType()); + } + + /** + * Tests the prototype chaining of native objects. + */ + public void testPrototypeChaining() throws Exception { + // equals + assertTypeEquals( + ARRAY_TYPE.getImplicitPrototype().getImplicitPrototype(), + OBJECT_TYPE); + assertTypeEquals( + BOOLEAN_OBJECT_TYPE.getImplicitPrototype(). + getImplicitPrototype(), OBJECT_TYPE); + assertTypeEquals( + DATE_TYPE.getImplicitPrototype().getImplicitPrototype(), + OBJECT_TYPE); + assertTypeEquals( + ERROR_TYPE.getImplicitPrototype().getImplicitPrototype(), + OBJECT_TYPE); + assertTypeEquals( + EVAL_ERROR_TYPE.getImplicitPrototype().getImplicitPrototype(), + ERROR_TYPE); + assertTypeEquals( + NUMBER_OBJECT_TYPE.getImplicitPrototype(). + getImplicitPrototype(), OBJECT_TYPE); + assertTypeEquals( + URI_ERROR_TYPE.getImplicitPrototype().getImplicitPrototype(), + ERROR_TYPE); + assertTypeEquals( + RANGE_ERROR_TYPE.getImplicitPrototype().getImplicitPrototype(), + ERROR_TYPE); + assertTypeEquals( + REFERENCE_ERROR_TYPE.getImplicitPrototype(). + getImplicitPrototype(), ERROR_TYPE); + assertTypeEquals( + STRING_OBJECT_TYPE.getImplicitPrototype(). + getImplicitPrototype(), OBJECT_TYPE); + assertTypeEquals( + REGEXP_TYPE.getImplicitPrototype().getImplicitPrototype(), + OBJECT_TYPE); + assertTypeEquals( + SYNTAX_ERROR_TYPE.getImplicitPrototype(). + getImplicitPrototype(), ERROR_TYPE); + assertTypeEquals( + TYPE_ERROR_TYPE.getImplicitPrototype(). + getImplicitPrototype(), ERROR_TYPE); + + // not same + assertNotSame(EVAL_ERROR_TYPE.getImplicitPrototype(), + URI_ERROR_TYPE.getImplicitPrototype()); + assertNotSame(EVAL_ERROR_TYPE.getImplicitPrototype(), + RANGE_ERROR_TYPE.getImplicitPrototype()); + assertNotSame(EVAL_ERROR_TYPE.getImplicitPrototype(), + REFERENCE_ERROR_TYPE.getImplicitPrototype()); + assertNotSame(EVAL_ERROR_TYPE.getImplicitPrototype(), + SYNTAX_ERROR_TYPE.getImplicitPrototype()); + assertNotSame(EVAL_ERROR_TYPE.getImplicitPrototype(), + TYPE_ERROR_TYPE.getImplicitPrototype()); + + assertNotSame(URI_ERROR_TYPE.getImplicitPrototype(), + RANGE_ERROR_TYPE.getImplicitPrototype()); + assertNotSame(URI_ERROR_TYPE.getImplicitPrototype(), + REFERENCE_ERROR_TYPE.getImplicitPrototype()); + assertNotSame(URI_ERROR_TYPE.getImplicitPrototype(), + SYNTAX_ERROR_TYPE.getImplicitPrototype()); + assertNotSame(URI_ERROR_TYPE.getImplicitPrototype(), + TYPE_ERROR_TYPE.getImplicitPrototype()); + + assertNotSame(RANGE_ERROR_TYPE.getImplicitPrototype(), + REFERENCE_ERROR_TYPE.getImplicitPrototype()); + assertNotSame(RANGE_ERROR_TYPE.getImplicitPrototype(), + SYNTAX_ERROR_TYPE.getImplicitPrototype()); + assertNotSame(RANGE_ERROR_TYPE.getImplicitPrototype(), + TYPE_ERROR_TYPE.getImplicitPrototype()); + + assertNotSame(REFERENCE_ERROR_TYPE.getImplicitPrototype(), + SYNTAX_ERROR_TYPE.getImplicitPrototype()); + assertNotSame(REFERENCE_ERROR_TYPE.getImplicitPrototype(), + TYPE_ERROR_TYPE.getImplicitPrototype()); + + assertNotSame(SYNTAX_ERROR_TYPE.getImplicitPrototype(), + TYPE_ERROR_TYPE.getImplicitPrototype()); + } + + /** + * Tests that function instances have their constructor pointer back at the + * function that created them. + */ + public void testInstanceFunctionChaining() throws Exception { + // Array + assertTypeEquals( + ARRAY_FUNCTION_TYPE, ARRAY_TYPE.getConstructor()); + + // Boolean + assertTypeEquals( + BOOLEAN_OBJECT_FUNCTION_TYPE, + BOOLEAN_OBJECT_TYPE.getConstructor()); + + // Date + assertTypeEquals( + DATE_FUNCTION_TYPE, DATE_TYPE.getConstructor()); + + // Error + assertTypeEquals( + ERROR_FUNCTION_TYPE, ERROR_TYPE.getConstructor()); + + // EvalError + assertTypeEquals( + EVAL_ERROR_FUNCTION_TYPE, EVAL_ERROR_TYPE.getConstructor()); + + // Number + assertTypeEquals( + NUMBER_OBJECT_FUNCTION_TYPE, + NUMBER_OBJECT_TYPE.getConstructor()); + + // Object + assertTypeEquals( + OBJECT_FUNCTION_TYPE, OBJECT_TYPE.getConstructor()); + + // RangeError + assertTypeEquals( + RANGE_ERROR_FUNCTION_TYPE, RANGE_ERROR_TYPE.getConstructor()); + + // ReferenceError + assertTypeEquals( + REFERENCE_ERROR_FUNCTION_TYPE, + REFERENCE_ERROR_TYPE.getConstructor()); + + // RegExp + assertTypeEquals(REGEXP_FUNCTION_TYPE, REGEXP_TYPE.getConstructor()); + + // String + assertTypeEquals( + STRING_OBJECT_FUNCTION_TYPE, + STRING_OBJECT_TYPE.getConstructor()); + + // SyntaxError + assertTypeEquals( + SYNTAX_ERROR_FUNCTION_TYPE, + SYNTAX_ERROR_TYPE.getConstructor()); + + // TypeError + assertTypeEquals( + TYPE_ERROR_FUNCTION_TYPE, TYPE_ERROR_TYPE.getConstructor()); + + // URIError + assertTypeEquals( + URI_ERROR_FUNCTION_TYPE, URI_ERROR_TYPE.getConstructor()); + } + + /** + * Tests that the method {@link JSType#canTestForEqualityWith(JSType)} handles + * special corner cases. + */ + @SuppressWarnings("checked") + public void testCanTestForEqualityWithCornerCases() { + // null == undefined is always true + assertCannotTestForEqualityWith(NULL_TYPE, VOID_TYPE); + + // (Object,null) == undefined could be true or false + UnionType nullableObject = + (UnionType) createUnionType(OBJECT_TYPE, NULL_TYPE); + assertCanTestForEqualityWith(nullableObject, VOID_TYPE); + assertCanTestForEqualityWith(VOID_TYPE, nullableObject); + } + + /** + * Tests the {@link JSType#testForEquality(JSType)} method. + */ + public void testTestForEquality() { + compare(TRUE, NO_OBJECT_TYPE, NO_OBJECT_TYPE); + compare(UNKNOWN, ALL_TYPE, ALL_TYPE); + compare(TRUE, NO_TYPE, NO_TYPE); + compare(UNKNOWN, NO_RESOLVED_TYPE, NO_RESOLVED_TYPE); + compare(UNKNOWN, NO_OBJECT_TYPE, NUMBER_TYPE); + compare(UNKNOWN, ALL_TYPE, NUMBER_TYPE); + compare(UNKNOWN, NO_TYPE, NUMBER_TYPE); + + compare(FALSE, NULL_TYPE, BOOLEAN_TYPE); + compare(TRUE, NULL_TYPE, NULL_TYPE); + compare(FALSE, NULL_TYPE, NUMBER_TYPE); + compare(FALSE, NULL_TYPE, OBJECT_TYPE); + compare(FALSE, NULL_TYPE, STRING_TYPE); + compare(TRUE, NULL_TYPE, VOID_TYPE); + compare(UNKNOWN, NULL_TYPE, createUnionType(UNKNOWN_TYPE, VOID_TYPE)); + compare(UNKNOWN, NULL_TYPE, createUnionType(OBJECT_TYPE, VOID_TYPE)); + compare(UNKNOWN, NULL_TYPE, unresolvedNamedType); + compare(UNKNOWN, + NULL_TYPE, createUnionType(unresolvedNamedType, DATE_TYPE)); + + compare(FALSE, VOID_TYPE, REGEXP_TYPE); + compare(TRUE, VOID_TYPE, VOID_TYPE); + compare(UNKNOWN, VOID_TYPE, createUnionType(REGEXP_TYPE, VOID_TYPE)); + + compare(UNKNOWN, NUMBER_TYPE, BOOLEAN_TYPE); + compare(UNKNOWN, NUMBER_TYPE, NUMBER_TYPE); + compare(UNKNOWN, NUMBER_TYPE, OBJECT_TYPE); + + compare(UNKNOWN, ARRAY_TYPE, BOOLEAN_TYPE); + compare(UNKNOWN, OBJECT_TYPE, BOOLEAN_TYPE); + compare(UNKNOWN, OBJECT_TYPE, STRING_TYPE); + + compare(UNKNOWN, STRING_TYPE, STRING_TYPE); + + compare(UNKNOWN, STRING_TYPE, BOOLEAN_TYPE); + compare(UNKNOWN, STRING_TYPE, NUMBER_TYPE); + compare(FALSE, STRING_TYPE, VOID_TYPE); + compare(FALSE, STRING_TYPE, NULL_TYPE); + compare(FALSE, STRING_TYPE, createUnionType(NULL_TYPE, VOID_TYPE)); + + compare(UNKNOWN, UNKNOWN_TYPE, BOOLEAN_TYPE); + compare(UNKNOWN, UNKNOWN_TYPE, NULL_TYPE); + compare(UNKNOWN, UNKNOWN_TYPE, VOID_TYPE); + + compare(FALSE, U2U_CONSTRUCTOR_TYPE, BOOLEAN_TYPE); + compare(FALSE, U2U_CONSTRUCTOR_TYPE, NUMBER_TYPE); + compare(FALSE, U2U_CONSTRUCTOR_TYPE, STRING_TYPE); + compare(FALSE, U2U_CONSTRUCTOR_TYPE, VOID_TYPE); + compare(FALSE, U2U_CONSTRUCTOR_TYPE, NULL_TYPE); + compare(UNKNOWN, U2U_CONSTRUCTOR_TYPE, OBJECT_TYPE); + compare(UNKNOWN, U2U_CONSTRUCTOR_TYPE, ALL_TYPE); + + compare(UNKNOWN, NULL_TYPE, subclassOfUnresolvedNamedType); + + JSType functionAndNull = createUnionType(NULL_TYPE, dateMethod); + compare(UNKNOWN, functionAndNull, dateMethod); + + compare(UNKNOWN, NULL_TYPE, NO_TYPE); + compare(UNKNOWN, VOID_TYPE, NO_TYPE); + compare(UNKNOWN, NULL_TYPE, unresolvedNamedType); + compare(UNKNOWN, VOID_TYPE, unresolvedNamedType); + compare(TRUE, NO_TYPE, NO_TYPE); + } + + private void compare(TernaryValue r, JSType t1, JSType t2) { + assertEquals(r, t1.testForEquality(t2)); + assertEquals(r, t2.testForEquality(t1)); + } + + private void assertCanTestForEqualityWith(JSType t1, JSType t2) { + assertTrue(t1.canTestForEqualityWith(t2)); + assertTrue(t2.canTestForEqualityWith(t1)); + } + + private void assertCannotTestForEqualityWith(JSType t1, JSType t2) { + assertFalse(t1.canTestForEqualityWith(t2)); + assertFalse(t2.canTestForEqualityWith(t1)); + } + + /** + * Tests the subtyping relationships among simple types. + */ + public void testSubtypingSimpleTypes() throws Exception { + // Any + assertTrue(NO_TYPE.isSubtype(NO_TYPE)); + assertTrue(NO_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertTrue(NO_TYPE.isSubtype(ARRAY_TYPE)); + assertTrue(NO_TYPE.isSubtype(BOOLEAN_TYPE)); + assertTrue(NO_TYPE.isSubtype(BOOLEAN_OBJECT_TYPE)); + assertTrue(NO_TYPE.isSubtype(DATE_TYPE)); + assertTrue(NO_TYPE.isSubtype(ERROR_TYPE)); + assertTrue(NO_TYPE.isSubtype(EVAL_ERROR_TYPE)); + assertTrue(NO_TYPE.isSubtype(functionType)); + assertTrue(NO_TYPE.isSubtype(NULL_TYPE)); + assertTrue(NO_TYPE.isSubtype(NUMBER_TYPE)); + assertTrue(NO_TYPE.isSubtype(NUMBER_OBJECT_TYPE)); + assertTrue(NO_TYPE.isSubtype(OBJECT_TYPE)); + assertTrue(NO_TYPE.isSubtype(URI_ERROR_TYPE)); + assertTrue(NO_TYPE.isSubtype(RANGE_ERROR_TYPE)); + assertTrue(NO_TYPE.isSubtype(REFERENCE_ERROR_TYPE)); + assertTrue(NO_TYPE.isSubtype(REGEXP_TYPE)); + assertTrue(NO_TYPE.isSubtype(STRING_TYPE)); + assertTrue(NO_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertTrue(NO_TYPE.isSubtype(SYNTAX_ERROR_TYPE)); + assertTrue(NO_TYPE.isSubtype(TYPE_ERROR_TYPE)); + assertTrue(NO_TYPE.isSubtype(ALL_TYPE)); + assertTrue(NO_TYPE.isSubtype(VOID_TYPE)); + + // AnyObject + assertFalse(NO_OBJECT_TYPE.isSubtype(NO_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(ARRAY_TYPE)); + assertFalse(NO_OBJECT_TYPE.isSubtype(BOOLEAN_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(BOOLEAN_OBJECT_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(DATE_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(EVAL_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(functionType)); + assertFalse(NO_OBJECT_TYPE.isSubtype(NULL_TYPE)); + assertFalse(NO_OBJECT_TYPE.isSubtype(NUMBER_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(NUMBER_OBJECT_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(OBJECT_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(URI_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(RANGE_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(REFERENCE_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(NO_OBJECT_TYPE.isSubtype(STRING_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(SYNTAX_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(TYPE_ERROR_TYPE)); + assertTrue(NO_OBJECT_TYPE.isSubtype(ALL_TYPE)); + assertFalse(NO_OBJECT_TYPE.isSubtype(VOID_TYPE)); + + // Array + assertFalse(ARRAY_TYPE.isSubtype(NO_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertTrue(ARRAY_TYPE.isSubtype(ARRAY_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(BOOLEAN_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(BOOLEAN_OBJECT_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(DATE_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(ERROR_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(EVAL_ERROR_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(functionType)); + assertFalse(ARRAY_TYPE.isSubtype(NULL_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(NUMBER_OBJECT_TYPE)); + assertTrue(ARRAY_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(URI_ERROR_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(RANGE_ERROR_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(REFERENCE_ERROR_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(STRING_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(SYNTAX_ERROR_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(TYPE_ERROR_TYPE)); + assertTrue(ARRAY_TYPE.isSubtype(ALL_TYPE)); + assertFalse(ARRAY_TYPE.isSubtype(VOID_TYPE)); + + // boolean + assertFalse(BOOLEAN_TYPE.isSubtype(NO_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(ARRAY_TYPE)); + assertTrue(BOOLEAN_TYPE.isSubtype(BOOLEAN_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(BOOLEAN_OBJECT_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(DATE_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(ERROR_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(EVAL_ERROR_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(functionType)); + assertFalse(BOOLEAN_TYPE.isSubtype(NULL_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(NUMBER_OBJECT_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(URI_ERROR_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(RANGE_ERROR_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(REFERENCE_ERROR_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(STRING_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(SYNTAX_ERROR_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(TYPE_ERROR_TYPE)); + assertTrue(BOOLEAN_TYPE.isSubtype(ALL_TYPE)); + assertFalse(BOOLEAN_TYPE.isSubtype(VOID_TYPE)); + + // Boolean + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(NO_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(ARRAY_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(BOOLEAN_TYPE)); + assertTrue(BOOLEAN_OBJECT_TYPE.isSubtype(BOOLEAN_OBJECT_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(DATE_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(ERROR_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(EVAL_ERROR_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(functionType)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(NULL_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(NUMBER_OBJECT_TYPE)); + assertTrue(BOOLEAN_OBJECT_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(URI_ERROR_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(RANGE_ERROR_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(REFERENCE_ERROR_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(STRING_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(SYNTAX_ERROR_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(TYPE_ERROR_TYPE)); + assertTrue(BOOLEAN_OBJECT_TYPE.isSubtype(ALL_TYPE)); + assertFalse(BOOLEAN_OBJECT_TYPE.isSubtype(VOID_TYPE)); + + // Date + assertFalse(DATE_TYPE.isSubtype(NO_TYPE)); + assertFalse(DATE_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertFalse(DATE_TYPE.isSubtype(ARRAY_TYPE)); + assertFalse(DATE_TYPE.isSubtype(BOOLEAN_TYPE)); + assertFalse(DATE_TYPE.isSubtype(BOOLEAN_OBJECT_TYPE)); + assertTrue(DATE_TYPE.isSubtype(DATE_TYPE)); + assertFalse(DATE_TYPE.isSubtype(ERROR_TYPE)); + assertFalse(DATE_TYPE.isSubtype(EVAL_ERROR_TYPE)); + assertFalse(DATE_TYPE.isSubtype(functionType)); + assertFalse(DATE_TYPE.isSubtype(NULL_TYPE)); + assertFalse(DATE_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(DATE_TYPE.isSubtype(NUMBER_OBJECT_TYPE)); + assertTrue(DATE_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(DATE_TYPE.isSubtype(URI_ERROR_TYPE)); + assertFalse(DATE_TYPE.isSubtype(RANGE_ERROR_TYPE)); + assertFalse(DATE_TYPE.isSubtype(REFERENCE_ERROR_TYPE)); + assertFalse(DATE_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(DATE_TYPE.isSubtype(STRING_TYPE)); + assertFalse(DATE_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(DATE_TYPE.isSubtype(SYNTAX_ERROR_TYPE)); + assertFalse(DATE_TYPE.isSubtype(TYPE_ERROR_TYPE)); + assertTrue(DATE_TYPE.isSubtype(ALL_TYPE)); + assertFalse(DATE_TYPE.isSubtype(VOID_TYPE)); + + // Error + assertFalse(ERROR_TYPE.isSubtype(NO_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(ARRAY_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(BOOLEAN_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(BOOLEAN_OBJECT_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(DATE_TYPE)); + assertTrue(ERROR_TYPE.isSubtype(ERROR_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(EVAL_ERROR_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(functionType)); + assertFalse(ERROR_TYPE.isSubtype(NULL_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(NUMBER_OBJECT_TYPE)); + assertTrue(ERROR_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(URI_ERROR_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(RANGE_ERROR_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(REFERENCE_ERROR_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(STRING_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(SYNTAX_ERROR_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(TYPE_ERROR_TYPE)); + assertTrue(ERROR_TYPE.isSubtype(ALL_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(VOID_TYPE)); + + // EvalError + assertFalse(EVAL_ERROR_TYPE.isSubtype(NO_TYPE)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(ARRAY_TYPE)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(BOOLEAN_TYPE)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(BOOLEAN_OBJECT_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(DATE_TYPE)); + assertTrue(EVAL_ERROR_TYPE.isSubtype(ERROR_TYPE)); + assertTrue(EVAL_ERROR_TYPE.isSubtype(EVAL_ERROR_TYPE)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(functionType)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(NULL_TYPE)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(NUMBER_OBJECT_TYPE)); + assertTrue(EVAL_ERROR_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(URI_ERROR_TYPE)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(RANGE_ERROR_TYPE)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(REFERENCE_ERROR_TYPE)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(STRING_TYPE)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(SYNTAX_ERROR_TYPE)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(TYPE_ERROR_TYPE)); + assertTrue(EVAL_ERROR_TYPE.isSubtype(ALL_TYPE)); + assertFalse(EVAL_ERROR_TYPE.isSubtype(VOID_TYPE)); + + // RangeError + assertTrue(RANGE_ERROR_TYPE.isSubtype(ERROR_TYPE)); + + // ReferenceError + assertTrue(REFERENCE_ERROR_TYPE.isSubtype(ERROR_TYPE)); + + // TypeError + assertTrue(TYPE_ERROR_TYPE.isSubtype(ERROR_TYPE)); + + // UriError + assertTrue(URI_ERROR_TYPE.isSubtype(ERROR_TYPE)); + + // Unknown + assertFalse(ALL_TYPE.isSubtype(NO_TYPE)); + assertFalse(ALL_TYPE.isSubtype(NO_OBJECT_TYPE)); + assertFalse(ALL_TYPE.isSubtype(ARRAY_TYPE)); + assertFalse(ALL_TYPE.isSubtype(BOOLEAN_TYPE)); + assertFalse(ALL_TYPE.isSubtype(BOOLEAN_OBJECT_TYPE)); + assertFalse(ERROR_TYPE.isSubtype(DATE_TYPE)); + assertFalse(ALL_TYPE.isSubtype(ERROR_TYPE)); + assertFalse(ALL_TYPE.isSubtype(EVAL_ERROR_TYPE)); + assertFalse(ALL_TYPE.isSubtype(functionType)); + assertFalse(ALL_TYPE.isSubtype(NULL_TYPE)); + assertFalse(ALL_TYPE.isSubtype(NUMBER_TYPE)); + assertFalse(ALL_TYPE.isSubtype(NUMBER_OBJECT_TYPE)); + assertFalse(ALL_TYPE.isSubtype(OBJECT_TYPE)); + assertFalse(ALL_TYPE.isSubtype(URI_ERROR_TYPE)); + assertFalse(ALL_TYPE.isSubtype(RANGE_ERROR_TYPE)); + assertFalse(ALL_TYPE.isSubtype(REFERENCE_ERROR_TYPE)); + assertFalse(ALL_TYPE.isSubtype(REGEXP_TYPE)); + assertFalse(ALL_TYPE.isSubtype(STRING_TYPE)); + assertFalse(ALL_TYPE.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(ALL_TYPE.isSubtype(SYNTAX_ERROR_TYPE)); + assertFalse(ALL_TYPE.isSubtype(TYPE_ERROR_TYPE)); + assertTrue(ALL_TYPE.isSubtype(ALL_TYPE)); + assertFalse(ALL_TYPE.isSubtype(VOID_TYPE)); + } + + /** + * Tests that the Object type is the greatest element (top) of the object + * hierarchy. + */ + public void testSubtypingObjectTopOfObjects() throws Exception { + assertTrue(OBJECT_TYPE.isSubtype(OBJECT_TYPE)); + assertTrue(createUnionType(DATE_TYPE, REGEXP_TYPE).isSubtype(OBJECT_TYPE)); + assertTrue(createUnionType(OBJECT_TYPE, NO_OBJECT_TYPE). + isSubtype(OBJECT_TYPE)); + assertTrue(functionType.isSubtype(OBJECT_TYPE)); + } + + public void testSubtypingFunctionPrototypeType() throws Exception { + FunctionType sub1 = registry.createConstructorType(null, null, null, null); + sub1.setPrototypeBasedOn(googBar); + FunctionType sub2 = registry.createConstructorType(null, null, null, null); + sub2.setPrototypeBasedOn(googBar); + + ObjectType o1 = sub1.getInstanceType(); + ObjectType o2 = sub2.getInstanceType(); + + assertFalse(o1.isSubtype(o2)); + assertFalse(o1.getImplicitPrototype().isSubtype(o2.getImplicitPrototype())); + assertTrue(o1.getImplicitPrototype().isSubtype(googBar)); + assertTrue(o2.getImplicitPrototype().isSubtype(googBar)); + } + + public void testSubtypingFunctionFixedArgs() throws Exception { + FunctionType f1 = registry.createFunctionType(OBJECT_TYPE, + false, BOOLEAN_TYPE); + FunctionType f2 = registry.createFunctionType(STRING_OBJECT_TYPE, + false, BOOLEAN_TYPE); + + assertTrue(f1.isSubtype(f1)); + assertFalse(f1.isSubtype(f2)); + assertTrue(f2.isSubtype(f1)); + assertTrue(f2.isSubtype(f2)); + + assertTrue(f1.isSubtype(U2U_CONSTRUCTOR_TYPE)); + assertTrue(f2.isSubtype(U2U_CONSTRUCTOR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(f1)); + assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(f2)); + } + + public void testSubtypingFunctionMultipleFixedArgs() throws Exception { + FunctionType f1 = registry.createFunctionType(OBJECT_TYPE, + false, EVAL_ERROR_TYPE, STRING_TYPE); + FunctionType f2 = registry.createFunctionType(STRING_OBJECT_TYPE, + false, ERROR_TYPE, ALL_TYPE); + + assertTrue(f1.isSubtype(f1)); + assertFalse(f1.isSubtype(f2)); + assertTrue(f2.isSubtype(f1)); + assertTrue(f2.isSubtype(f2)); + + assertTrue(f1.isSubtype(U2U_CONSTRUCTOR_TYPE)); + assertTrue(f2.isSubtype(U2U_CONSTRUCTOR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(f1)); + assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(f2)); + } + + public void testSubtypingFunctionFixedArgsNotMatching() throws Exception { + FunctionType f1 = registry.createFunctionType(OBJECT_TYPE, + false, EVAL_ERROR_TYPE, UNKNOWN_TYPE); + FunctionType f2 = registry.createFunctionType(STRING_OBJECT_TYPE, + false, ERROR_TYPE, ALL_TYPE); + + assertTrue(f1.isSubtype(f1)); + assertFalse(f1.isSubtype(f2)); + assertTrue(f2.isSubtype(f1)); + assertTrue(f2.isSubtype(f2)); + + assertTrue(f1.isSubtype(U2U_CONSTRUCTOR_TYPE)); + assertTrue(f2.isSubtype(U2U_CONSTRUCTOR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(f1)); + assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(f2)); + } + + public void testSubtypingFunctionVariableArgsOneOnly() throws Exception { + // f1 = (EvalError...) -> Object + FunctionType f1 = registry.createFunctionType(OBJECT_TYPE, + true, EVAL_ERROR_TYPE); + // f2 = (Error, Object) -> String + FunctionType f2 = registry.createFunctionType(STRING_OBJECT_TYPE, + false, ERROR_TYPE, OBJECT_TYPE); + + assertTrue(f1.isSubtype(f1)); + assertFalse(f1.isSubtype(f2)); + assertFalse(f2.isSubtype(f1)); + assertTrue(f2.isSubtype(f2)); + + assertTrue(f1.isSubtype(U2U_CONSTRUCTOR_TYPE)); + assertTrue(f2.isSubtype(U2U_CONSTRUCTOR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(f1)); + assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(f2)); + } + + public void testSubtypingFunctionVariableArgsBoth() throws Exception { + // f1 = (UriError, EvalError, EvalError...) -> Object + FunctionType f1 = registry.createFunctionType(OBJECT_TYPE, + true, URI_ERROR_TYPE, EVAL_ERROR_TYPE, EVAL_ERROR_TYPE); + // f2 = (Error, Object, EvalError...) -> String + FunctionType f2 = registry.createFunctionType(STRING_OBJECT_TYPE, + true, ERROR_TYPE, OBJECT_TYPE, EVAL_ERROR_TYPE); + + assertTrue(f1.isSubtype(f1)); + assertFalse(f1.isSubtype(f2)); + assertTrue(f2.isSubtype(f1)); + assertTrue(f2.isSubtype(f2)); + + assertTrue(f1.isSubtype(U2U_CONSTRUCTOR_TYPE)); + assertTrue(f2.isSubtype(U2U_CONSTRUCTOR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(f1)); + assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(f2)); + } + + public void testSubtypingMostGeneralFunction() throws Exception { + // (EvalError, String) -> Object + FunctionType f1 = registry.createFunctionType(OBJECT_TYPE, + false, EVAL_ERROR_TYPE, STRING_TYPE); + // (string, void) -> number + FunctionType f2 = registry.createFunctionType(NUMBER_TYPE, + false, STRING_TYPE, VOID_TYPE); + // (Date, string, number) -> AnyObject + FunctionType f3 = registry.createFunctionType(NO_OBJECT_TYPE, + false, DATE_TYPE, STRING_TYPE, NUMBER_TYPE); + // (Number) -> Any + FunctionType f4 = registry.createFunctionType(NO_TYPE, + false, NUMBER_OBJECT_TYPE); + // f1 = (EvalError...) -> Object + FunctionType f5 = registry.createFunctionType(OBJECT_TYPE, + true, EVAL_ERROR_TYPE); + // f2 = (Error, Object) -> String + FunctionType f6 = registry.createFunctionType(STRING_OBJECT_TYPE, + false, ERROR_TYPE, OBJECT_TYPE); + // f1 = (UriError, EvalError...) -> Object + FunctionType f7 = registry.createFunctionType(OBJECT_TYPE, + true, URI_ERROR_TYPE, EVAL_ERROR_TYPE); + // f2 = (Error, Object, EvalError...) -> String + FunctionType f8 = registry.createFunctionType(STRING_OBJECT_TYPE, + true, ERROR_TYPE, OBJECT_TYPE, EVAL_ERROR_TYPE); + + assertTrue(LEAST_FUNCTION_TYPE.isSubtype(GREATEST_FUNCTION_TYPE)); + assertTrue(LEAST_FUNCTION_TYPE.isSubtype(U2U_CONSTRUCTOR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(LEAST_FUNCTION_TYPE)); + + assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(LEAST_FUNCTION_TYPE)); + assertTrue(GREATEST_FUNCTION_TYPE.isSubtype(U2U_CONSTRUCTOR_TYPE)); + assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(GREATEST_FUNCTION_TYPE)); + + assertTrue(f1.isSubtype(GREATEST_FUNCTION_TYPE)); + assertTrue(f2.isSubtype(GREATEST_FUNCTION_TYPE)); + assertTrue(f3.isSubtype(GREATEST_FUNCTION_TYPE)); + assertTrue(f4.isSubtype(GREATEST_FUNCTION_TYPE)); + assertTrue(f5.isSubtype(GREATEST_FUNCTION_TYPE)); + assertTrue(f6.isSubtype(GREATEST_FUNCTION_TYPE)); + assertTrue(f7.isSubtype(GREATEST_FUNCTION_TYPE)); + assertTrue(f8.isSubtype(GREATEST_FUNCTION_TYPE)); + + assertFalse(f1.isSubtype(LEAST_FUNCTION_TYPE)); + assertFalse(f2.isSubtype(LEAST_FUNCTION_TYPE)); + assertFalse(f3.isSubtype(LEAST_FUNCTION_TYPE)); + assertFalse(f4.isSubtype(LEAST_FUNCTION_TYPE)); + assertFalse(f5.isSubtype(LEAST_FUNCTION_TYPE)); + assertFalse(f6.isSubtype(LEAST_FUNCTION_TYPE)); + assertFalse(f7.isSubtype(LEAST_FUNCTION_TYPE)); + assertFalse(f8.isSubtype(LEAST_FUNCTION_TYPE)); + + assertTrue(LEAST_FUNCTION_TYPE.isSubtype(f1)); + assertTrue(LEAST_FUNCTION_TYPE.isSubtype(f2)); + assertTrue(LEAST_FUNCTION_TYPE.isSubtype(f3)); + assertTrue(LEAST_FUNCTION_TYPE.isSubtype(f4)); + assertTrue(LEAST_FUNCTION_TYPE.isSubtype(f5)); + assertTrue(LEAST_FUNCTION_TYPE.isSubtype(f6)); + assertTrue(LEAST_FUNCTION_TYPE.isSubtype(f7)); + assertTrue(LEAST_FUNCTION_TYPE.isSubtype(f8)); + + assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(f1)); + assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(f2)); + assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(f3)); + assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(f4)); + assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(f5)); + assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(f6)); + assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(f7)); + assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(f8)); + } + + /** + * Types to test for symmetrical relationships. + */ + private List getTypesToTestForSymmetry() { + return Lists.newArrayList( + UNKNOWN_TYPE, + NULL_TYPE, + VOID_TYPE, + NUMBER_TYPE, + STRING_TYPE, + BOOLEAN_TYPE, + OBJECT_TYPE, + U2U_CONSTRUCTOR_TYPE, + LEAST_FUNCTION_TYPE, + GREATEST_FUNCTION_TYPE, + ALL_TYPE, + NO_TYPE, + NO_OBJECT_TYPE, + NO_RESOLVED_TYPE, + createUnionType(BOOLEAN_TYPE, STRING_TYPE), + createUnionType(NUMBER_TYPE, STRING_TYPE), + createUnionType(NULL_TYPE, dateMethod), + createUnionType(UNKNOWN_TYPE, dateMethod), + createUnionType(namedGoogBar, dateMethod), + createUnionType(NULL_TYPE, unresolvedNamedType), + enumType, + elementsType, + dateMethod, + functionType, + unresolvedNamedType, + googBar, + namedGoogBar, + googBar.getInstanceType(), + namedGoogBar, + subclassOfUnresolvedNamedType, + subclassCtor, + recordType, + forwardDeclaredNamedType, + createUnionType(forwardDeclaredNamedType, NULL_TYPE), + createParameterizedType(OBJECT_TYPE, STRING_TYPE), + createParameterizedType(OBJECT_TYPE, NUMBER_TYPE), + createParameterizedType(ARRAY_TYPE, STRING_TYPE), + createParameterizedType(ARRAY_TYPE, NUMBER_TYPE), + createUnionType( + createParameterizedType(ARRAY_TYPE, BOOLEAN_TYPE), NULL_TYPE), + createUnionType( + createParameterizedType(OBJECT_TYPE, BOOLEAN_TYPE), NULL_TYPE) + ); + } + + public void testSymmetryOfTestForEquality() { + List listA = getTypesToTestForSymmetry(); + List listB = getTypesToTestForSymmetry(); + for (JSType typeA : listA) { + for (JSType typeB : listB) { + TernaryValue aOnB = typeA.testForEquality(typeB); + TernaryValue bOnA = typeB.testForEquality(typeA); + assertTrue( + String.format("testForEquality not symmetrical:\n" + + "typeA: %s\ntypeB: %s\n" + + "a.testForEquality(b): %s\n" + + "b.testForEquality(a): %s\n", + typeA, typeB, aOnB, bOnA), + aOnB == bOnA); + } + } + } + + /** + * Tests that getLeastSupertype is a symmetric relation. + */ + public void testSymmetryOfLeastSupertype() { + List listA = getTypesToTestForSymmetry(); + List listB = getTypesToTestForSymmetry(); + for (JSType typeA : listA) { + for (JSType typeB : listB) { + JSType aOnB = typeA.getLeastSupertype(typeB); + JSType bOnA = typeB.getLeastSupertype(typeA); + + // Use a custom assert message instead of the normal assertTypeEquals, + // to make it more helpful. + assertTrue( + String.format("getLeastSupertype not symmetrical:\n" + + "typeA: %s\ntypeB: %s\n" + + "a.getLeastSupertype(b): %s\n" + + "b.getLeastSupertype(a): %s\n", + typeA, typeB, aOnB, bOnA), + aOnB.isEquivalentTo(bOnA)); + } + } + } + + public void testWeirdBug() { + assertTypeNotEquals(googBar, googBar.getInstanceType()); + assertFalse(googBar.isSubtype(googBar.getInstanceType())); + assertFalse(googBar.getInstanceType().isSubtype(googBar)); + } + + /** + * Tests that getGreatestSubtype is a symmetric relation. + */ + public void testSymmetryOfGreatestSubtype() { + List listA = getTypesToTestForSymmetry(); + List listB = getTypesToTestForSymmetry(); + for (JSType typeA : listA) { + for (JSType typeB : listB) { + JSType aOnB = typeA.getGreatestSubtype(typeB); + JSType bOnA = typeB.getGreatestSubtype(typeA); + + // Use a custom assert message instead of the normal assertTypeEquals, + // to make it more helpful. + assertTrue( + String.format("getGreatestSubtype not symmetrical:\n" + + "typeA: %s\ntypeB: %s\n" + + "a.getGreatestSubtype(b): %s\n" + + "b.getGreatestSubtype(a): %s\n", + typeA, typeB, aOnB, bOnA), + aOnB.isEquivalentTo(bOnA)); + } + } + } + + /** + * Tests that getLeastSupertype is a reflexive relation. + */ + public void testReflexivityOfLeastSupertype() { + List list = getTypesToTestForSymmetry(); + for (JSType type : list) { + assertTypeEquals("getLeastSupertype not reflexive", + type, type.getLeastSupertype(type)); + } + } + + /** + * Tests that getGreatestSubtype is a reflexive relation. + */ + public void testReflexivityOfGreatestSubtype() { + List list = getTypesToTestForSymmetry(); + for (JSType type : list) { + assertTypeEquals("getGreatestSubtype not reflexive", + type, type.getGreatestSubtype(type)); + } + } + + /** + * Tests {@link JSType#getLeastSupertype(JSType)} for unresolved named types. + */ + public void testLeastSupertypeUnresolvedNamedType() { + // (undefined,function(?):?) and ? unresolved named type + JSType expected = registry.createUnionType( + unresolvedNamedType, U2U_FUNCTION_TYPE); + assertTypeEquals(expected, + unresolvedNamedType.getLeastSupertype(U2U_FUNCTION_TYPE)); + assertTypeEquals(expected, + U2U_FUNCTION_TYPE.getLeastSupertype(unresolvedNamedType)); + assertEquals("(function (...[?]): ?|not.resolved.named.type)", + expected.toString()); + } + + public void testLeastSupertypeUnresolvedNamedType2() { + JSType expected = registry.createUnionType( + unresolvedNamedType, UNKNOWN_TYPE); + assertTypeEquals(expected, + unresolvedNamedType.getLeastSupertype(UNKNOWN_TYPE)); + assertTypeEquals(expected, + UNKNOWN_TYPE.getLeastSupertype(unresolvedNamedType)); + assertTypeEquals(UNKNOWN_TYPE, expected); + } + + public void testLeastSupertypeUnresolvedNamedType3() { + JSType expected = registry.createUnionType( + unresolvedNamedType, CHECKED_UNKNOWN_TYPE); + assertTypeEquals(expected, + unresolvedNamedType.getLeastSupertype(CHECKED_UNKNOWN_TYPE)); + assertTypeEquals(expected, + CHECKED_UNKNOWN_TYPE.getLeastSupertype(unresolvedNamedType)); + assertTypeEquals(CHECKED_UNKNOWN_TYPE, expected); + } + + /** Tests the subclass of an unresolved named type */ + public void testSubclassOfUnresolvedNamedType() { + assertTrue(subclassOfUnresolvedNamedType.isUnknownType()); + } + + /** + * Tests that Proxied FunctionTypes behave the same over getLeastSupertype and + * getGreatestSubtype as non proxied FunctionTypes + */ + public void testSupertypeOfProxiedFunctionTypes() { + ObjectType fn1 = + new FunctionBuilder(registry) + .withParamsNode(new Node(Token.PARAM_LIST)) + .withReturnType(NUMBER_TYPE) + .build(); + ObjectType fn2 = + new FunctionBuilder(registry) + .withParamsNode(new Node(Token.PARAM_LIST)) + .withReturnType(STRING_TYPE) + .build(); + ObjectType p1 = new ProxyObjectType(registry, fn1); + ObjectType p2 = new ProxyObjectType(registry, fn2); + ObjectType supremum = + new FunctionBuilder(registry) + .withParamsNode(new Node(Token.PARAM_LIST)) + .withReturnType(registry.createUnionType(STRING_TYPE, NUMBER_TYPE)) + .build(); + + assertTypeEquals(fn1.getLeastSupertype(fn2), p1.getLeastSupertype(p2)); + assertTypeEquals(supremum, fn1.getLeastSupertype(fn2)); + assertTypeEquals(supremum, fn1.getLeastSupertype(p2)); + assertTypeEquals(supremum, p1.getLeastSupertype(fn2)); + assertTypeEquals(supremum, p1.getLeastSupertype(p2)); + } + + public void testTypeOfThisIsProxied() { + ObjectType fnType = new FunctionBuilder(registry) + .withReturnType(NUMBER_TYPE).withTypeOfThis(OBJECT_TYPE).build(); + ObjectType proxyType = new ProxyObjectType(registry, fnType); + assertTypeEquals(fnType.getTypeOfThis(), proxyType.getTypeOfThis()); + } + + /** + * Tests the {@link NamedType#equals} function, which had a bug in it. + */ + public void testNamedTypeEquals() { + JSTypeRegistry jst = new JSTypeRegistry(null); + + // test == if references are equal + NamedType a = new NamedType(jst, "type1", "source", 1, 0); + NamedType b = new NamedType(jst, "type1", "source", 1, 0); + assertTrue(a.isEquivalentTo(b)); + + // test == instance of referenced type + assertTrue(namedGoogBar.isEquivalentTo(googBar.getInstanceType())); + assertTrue(googBar.getInstanceType().isEquivalentTo(namedGoogBar)); + } + + /** + * Tests the {@link NamedType#equals} function against other types. + */ + public void testNamedTypeEquals2() { + // test == if references are equal + NamedType a = new NamedType(registry, "typeA", "source", 1, 0); + NamedType b = new NamedType(registry, "typeB", "source", 1, 0); + + ObjectType realA = registry.createConstructorType( + "typeA", null, null, null, null).getInstanceType(); + ObjectType realB = registry.createEnumType( + "typeB", null, NUMBER_TYPE).getElementsType(); + registry.declareType("typeA", realA); + registry.declareType("typeB", realB); + + assertTypeEquals(a, realA); + assertTypeEquals(b, realB); + + a.resolve(null, null); + b.resolve(null, null); + + assertTrue(a.isResolved()); + assertTrue(b.isResolved()); + assertTypeEquals(a, realA); + assertTypeEquals(b, realB); + + JSType resolvedA = Asserts.assertValidResolve(a); + assertNotSame(resolvedA, a); + assertSame(resolvedA, realA); + + JSType resolvedB = Asserts.assertValidResolve(b); + assertNotSame(resolvedB, b); + assertSame(resolvedB, realB); + } + + /** + * Tests the {@link NamedType#equals} function against other types + * when it's forward-declared. + */ + public void testForwardDeclaredNamedTypeEquals() { + // test == if references are equal + NamedType a = new NamedType(registry, "typeA", "source", 1, 0); + NamedType b = new NamedType(registry, "typeA", "source", 1, 0); + registry.forwardDeclareType("typeA"); + + assertTypeEquals(a, b); + + a.resolve(null, EMPTY_SCOPE); + + assertTrue(a.isResolved()); + assertFalse(b.isResolved()); + + assertTypeEquals(a, b); + + assertFalse(a.isEquivalentTo(UNKNOWN_TYPE)); + assertFalse(b.isEquivalentTo(UNKNOWN_TYPE)); + assertTrue(a.isEmptyType()); + assertFalse(a.isNoType()); + assertTrue(a.isNoResolvedType()); + } + + public void testForwardDeclaredNamedType() { + NamedType a = new NamedType(registry, "typeA", "source", 1, 0); + registry.forwardDeclareType("typeA"); + + assertTypeEquals(UNKNOWN_TYPE, a.getLeastSupertype(UNKNOWN_TYPE)); + assertTypeEquals(CHECKED_UNKNOWN_TYPE, + a.getLeastSupertype(CHECKED_UNKNOWN_TYPE)); + assertTypeEquals(UNKNOWN_TYPE, UNKNOWN_TYPE.getLeastSupertype(a)); + assertTypeEquals(CHECKED_UNKNOWN_TYPE, + CHECKED_UNKNOWN_TYPE.getLeastSupertype(a)); + } + + /** + * Tests {@link JSType#getGreatestSubtype(JSType)} on simple types. + */ + public void testGreatestSubtypeSimpleTypes() { + assertTypeEquals(ARRAY_TYPE, + ARRAY_TYPE.getGreatestSubtype(ALL_TYPE)); + assertTypeEquals(ARRAY_TYPE, + ALL_TYPE.getGreatestSubtype(ARRAY_TYPE)); + assertTypeEquals(NO_OBJECT_TYPE, + REGEXP_TYPE.getGreatestSubtype(NO_OBJECT_TYPE)); + assertTypeEquals(NO_OBJECT_TYPE, + NO_OBJECT_TYPE.getGreatestSubtype(REGEXP_TYPE)); + assertTypeEquals(NO_OBJECT_TYPE, + ARRAY_TYPE.getGreatestSubtype(STRING_OBJECT_TYPE)); + assertTypeEquals(NO_TYPE, ARRAY_TYPE.getGreatestSubtype(NUMBER_TYPE)); + assertTypeEquals(NO_OBJECT_TYPE, + ARRAY_TYPE.getGreatestSubtype(functionType)); + assertTypeEquals(STRING_OBJECT_TYPE, + STRING_OBJECT_TYPE.getGreatestSubtype(OBJECT_TYPE)); + assertTypeEquals(STRING_OBJECT_TYPE, + OBJECT_TYPE.getGreatestSubtype(STRING_OBJECT_TYPE)); + assertTypeEquals(NO_OBJECT_TYPE, + ARRAY_TYPE.getGreatestSubtype(DATE_TYPE)); + assertTypeEquals(NO_OBJECT_TYPE, + ARRAY_TYPE.getGreatestSubtype(REGEXP_TYPE)); + assertTypeEquals(EVAL_ERROR_TYPE, + ERROR_TYPE.getGreatestSubtype(EVAL_ERROR_TYPE)); + assertTypeEquals(EVAL_ERROR_TYPE, + EVAL_ERROR_TYPE.getGreatestSubtype(ERROR_TYPE)); + assertTypeEquals(NO_TYPE, + NULL_TYPE.getGreatestSubtype(ERROR_TYPE)); + assertTypeEquals(UNKNOWN_TYPE, + NUMBER_TYPE.getGreatestSubtype(UNKNOWN_TYPE)); + + assertTypeEquals(NO_RESOLVED_TYPE, + NO_OBJECT_TYPE.getGreatestSubtype(forwardDeclaredNamedType)); + assertTypeEquals(NO_RESOLVED_TYPE, + forwardDeclaredNamedType.getGreatestSubtype(NO_OBJECT_TYPE)); + + } + + /** + * Tests that a derived class extending a type via a named type is a subtype + * of it. + */ + public void testSubtypingDerivedExtendsNamedBaseType() throws Exception { + ObjectType derived = + registry.createObjectType(registry.createObjectType(namedGoogBar)); + + assertTrue(derived.isSubtype(googBar.getInstanceType())); + } + + public void testNamedSubtypeChain() throws Exception { + List typeChain = Lists.newArrayList( + registry.getNativeType(JSTypeNative.ALL_TYPE), + registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE), + registry.getNativeType(JSTypeNative.OBJECT_TYPE), + googBar.getPrototype(), + googBar.getInstanceType(), + googSubBar.getPrototype(), + googSubBar.getInstanceType(), + googSubSubBar.getPrototype(), + googSubSubBar.getInstanceType(), + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE)); + verifySubtypeChain(typeChain); + } + + public void testRecordSubtypeChain() throws Exception { + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.addProperty("a", STRING_TYPE, null); + JSType aType = builder.build(); + + builder = new RecordTypeBuilder(registry); + builder.addProperty("a", STRING_TYPE, null); + builder.addProperty("b", STRING_TYPE, null); + JSType abType = builder.build(); + + builder = new RecordTypeBuilder(registry); + builder.addProperty("a", STRING_TYPE, null); + builder.addProperty("c", STRING_TYPE, null); + JSType acType = builder.build(); + JSType abOrAcType = registry.createUnionType(abType, acType); + + builder = new RecordTypeBuilder(registry); + builder.addProperty("a", STRING_TYPE, null); + builder.addProperty("b", STRING_TYPE, null); + builder.addProperty("c", NUMBER_TYPE, null); + JSType abcType = builder.build(); + + List typeChain = Lists.newArrayList( + registry.getNativeType(JSTypeNative.ALL_TYPE), + registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE), + registry.getNativeType(JSTypeNative.OBJECT_TYPE), + aType, + abOrAcType, + abType, + abcType, + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE)); + verifySubtypeChain(typeChain); + } + + public void testRecordAndObjectChain2() throws Exception { + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.addProperty("date", DATE_TYPE, null); + JSType hasDateProperty = builder.build(); + + List typeChain = Lists.newArrayList( + registry.getNativeType(JSTypeNative.OBJECT_TYPE), + hasDateProperty, + googBar.getInstanceType(), + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE)); + verifySubtypeChain(typeChain); + } + + public void testRecordAndObjectChain3() throws Exception { + RecordTypeBuilder builder = new RecordTypeBuilder(registry); + builder.addProperty("date", UNKNOWN_TYPE, null); + JSType hasUnknownDateProperty = builder.build(); + + List typeChain = Lists.newArrayList( + registry.getNativeType(JSTypeNative.OBJECT_TYPE), + hasUnknownDateProperty, + googBar.getInstanceType(), + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE)); + verifySubtypeChain(typeChain); + } + + public void testNullableNamedTypeChain() throws Exception { + List typeChain = Lists.newArrayList( + registry.getNativeType(JSTypeNative.ALL_TYPE), + registry.createOptionalNullableType( + registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE)), + registry.createOptionalNullableType( + registry.getNativeType(JSTypeNative.OBJECT_TYPE)), + registry.createOptionalNullableType(googBar.getPrototype()), + registry.createOptionalNullableType(googBar.getInstanceType()), + registry.createNullableType(googSubBar.getPrototype()), + registry.createNullableType(googSubBar.getInstanceType()), + googSubSubBar.getPrototype(), + googSubSubBar.getInstanceType(), + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE)); + verifySubtypeChain(typeChain); + } + + public void testEnumTypeChain() throws Exception { + List typeChain = Lists.newArrayList( + registry.getNativeType(JSTypeNative.ALL_TYPE), + registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE), + registry.getNativeType(JSTypeNative.OBJECT_TYPE), + enumType, + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE)); + verifySubtypeChain(typeChain); + } + + public void testFunctionSubtypeChain() throws Exception { + List typeChain = Lists.newArrayList( + registry.getNativeType(JSTypeNative.ALL_TYPE), + registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE), + registry.getNativeType(JSTypeNative.OBJECT_TYPE), + registry.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE), + registry.getNativeType(JSTypeNative.GREATEST_FUNCTION_TYPE), + dateMethod, + registry.getNativeType(JSTypeNative.LEAST_FUNCTION_TYPE), + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE)); + verifySubtypeChain(typeChain); + } + + public void testFunctionUnionSubtypeChain() throws Exception { + List typeChain = Lists.newArrayList( + createUnionType( + OBJECT_TYPE, + STRING_TYPE), + createUnionType( + GREATEST_FUNCTION_TYPE, + googBarInst, + STRING_TYPE), + createUnionType( + STRING_TYPE, + registry.createFunctionType( + createUnionType(STRING_TYPE, NUMBER_TYPE)), + googBarInst), + createUnionType( + registry.createFunctionType(NUMBER_TYPE), + googSubBarInst), + LEAST_FUNCTION_TYPE, + NO_OBJECT_TYPE, + NO_TYPE); + verifySubtypeChain(typeChain); + } + + public void testConstructorSubtypeChain() throws Exception { + List typeChain = Lists.newArrayList( + registry.getNativeType(JSTypeNative.ALL_TYPE), + registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE), + registry.getNativeType(JSTypeNative.OBJECT_TYPE), + registry.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE), + registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE)); + verifySubtypeChain(typeChain); + } + + public void testGoogBarSubtypeChain() throws Exception { + List typeChain = Lists.newArrayList( + registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), + googBar, + googSubBar, + googSubSubBar, + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE)); + verifySubtypeChain(typeChain, false); + } + + public void testConstructorWithArgSubtypeChain() throws Exception { + FunctionType googBarArgConstructor = registry.createConstructorType( + "barArg", null, registry.createParameters(googBar), null, null); + FunctionType googSubBarArgConstructor = registry.createConstructorType( + "subBarArg", null, registry.createParameters(googSubBar), null, null); + + List typeChain = Lists.newArrayList( + registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), + googBarArgConstructor, + googSubBarArgConstructor, + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE)); + verifySubtypeChain(typeChain, false); + } + + public void testInterfaceInstanceSubtypeChain() throws Exception { + List typeChain = Lists.newArrayList( + ALL_TYPE, + OBJECT_TYPE, + interfaceInstType, + googBar.getPrototype(), + googBarInst, + googSubBar.getPrototype(), + googSubBarInst, + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE)); + verifySubtypeChain(typeChain); + } + + public void testInterfaceInheritanceSubtypeChain() throws Exception { + FunctionType tempType = + registry.createConstructorType("goog.TempType", null, null, null, null); + tempType.setImplementedInterfaces( + Lists.newArrayList(subInterfaceInstType)); + List typeChain = Lists.newArrayList( + ALL_TYPE, + OBJECT_TYPE, + interfaceInstType, + subInterfaceInstType, + tempType.getPrototype(), + tempType.getInstanceType(), + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE)); + verifySubtypeChain(typeChain); + } + + public void testAnonymousObjectChain() throws Exception { + List typeChain = Lists.newArrayList( + ALL_TYPE, + createNullableType(OBJECT_TYPE), + OBJECT_TYPE, + registry.createAnonymousObjectType(null), + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE)); + verifySubtypeChain(typeChain); + } + + public void testAnonymousEnumElementChain() throws Exception { + ObjectType enumElemType = registry.createEnumType( + "typeB", null, + registry.createAnonymousObjectType(null)).getElementsType(); + List typeChain = Lists.newArrayList( + ALL_TYPE, + createNullableType(OBJECT_TYPE), + OBJECT_TYPE, + enumElemType, + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE)); + verifySubtypeChain(typeChain); + } + + public void testParameterizedArrayChain() throws Exception { + JSType arrayOfNoType = createParameterizedType( + ARRAY_TYPE, NO_TYPE); + JSType arrayOfString = createParameterizedType( + ARRAY_TYPE, STRING_TYPE); + JSType arrayOfStringOrNumber = createParameterizedType( + ARRAY_TYPE, createUnionType(STRING_TYPE, NUMBER_TYPE)); + JSType arrayOfAllType = createParameterizedType( + ARRAY_TYPE, ALL_TYPE); + + List typeChain = Lists.newArrayList( + registry.getNativeType(JSTypeNative.ALL_TYPE), + registry.getNativeType(JSTypeNative.OBJECT_TYPE), + arrayOfAllType, + arrayOfStringOrNumber, + arrayOfString, + arrayOfNoType, + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE)); + verifySubtypeChain(typeChain, false); + } + + public void testParameterizedArrayChain2() throws Exception { + JSType arrayOfNoType = createParameterizedType( + ARRAY_TYPE, NO_TYPE); + JSType arrayOfNoObjectType = createParameterizedType( + ARRAY_TYPE, NO_OBJECT_TYPE); + JSType arrayOfArray = createParameterizedType( + ARRAY_TYPE, ARRAY_TYPE); + JSType arrayOfObject = createParameterizedType( + ARRAY_TYPE, OBJECT_TYPE); + JSType arrayOfAllType = createParameterizedType( + ARRAY_TYPE, ALL_TYPE); + + List typeChain = Lists.newArrayList( + registry.getNativeType(JSTypeNative.ALL_TYPE), + registry.getNativeType(JSTypeNative.OBJECT_TYPE), + arrayOfAllType, + arrayOfObject, + arrayOfArray, + arrayOfNoObjectType, + arrayOfNoType, + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE)); + verifySubtypeChain(typeChain, false); + } + + public void testParameterizedObjectChain() throws Exception { + JSType objectOfNoType = createParameterizedType( + OBJECT_TYPE, NO_TYPE); + JSType objectOfString = createParameterizedType( + OBJECT_TYPE, STRING_TYPE); + JSType objectOfStringOrNumber = createParameterizedType( + OBJECT_TYPE, createUnionType(STRING_TYPE, NUMBER_TYPE)); + JSType objectOfAllType = createParameterizedType( + OBJECT_TYPE, ALL_TYPE); + + List typeChain = Lists.newArrayList( + registry.getNativeType(JSTypeNative.ALL_TYPE), + registry.getNativeType(JSTypeNative.OBJECT_TYPE), + objectOfAllType, + objectOfStringOrNumber, + objectOfString, + objectOfNoType, + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE)); + verifySubtypeChain(typeChain, false); + } + + public void testMixedParameterizedTypeChain() throws Exception { + JSType arrayOfNoType = createParameterizedType( + ARRAY_TYPE, NO_TYPE); + JSType arrayOfString = createParameterizedType( + ARRAY_TYPE, STRING_TYPE); + JSType objectOfString = createParameterizedType( + OBJECT_TYPE, STRING_TYPE); + JSType objectOfStringOrNumber = createParameterizedType( + OBJECT_TYPE, createUnionType(STRING_TYPE, NUMBER_TYPE)); + JSType objectOfAllType = createParameterizedType( + OBJECT_TYPE, ALL_TYPE); + + List typeChain = Lists.newArrayList( + registry.getNativeType(JSTypeNative.ALL_TYPE), + registry.getNativeType(JSTypeNative.OBJECT_TYPE), + objectOfAllType, + objectOfStringOrNumber, + objectOfString, + arrayOfString, + arrayOfNoType, + registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), + registry.getNativeType(JSTypeNative.NO_TYPE)); + verifySubtypeChain(typeChain, false); + } + + public void testParameterizedTypeSubtypes() { + JSType objectOfString = createParameterizedType( + OBJECT_TYPE, STRING_TYPE); + JSType arrayOfString = createParameterizedType( + ARRAY_TYPE, STRING_TYPE); + JSType arrayOfNumber = createParameterizedType( + ARRAY_TYPE, NUMBER_TYPE); + JSType arrayOfUnknown = createParameterizedType( + ARRAY_TYPE, UNKNOWN_TYPE); + + assertFalse(objectOfString.isSubtype(ARRAY_TYPE)); + // TODO(johnlenz): should this be false? + assertTrue(ARRAY_TYPE.isSubtype(objectOfString)); + assertFalse(objectOfString.isSubtype(ARRAY_TYPE)); + // TODO(johnlenz): should this be false? + assertTrue(ARRAY_TYPE.isSubtype(objectOfString)); + + assertTrue(arrayOfString.isSubtype(ARRAY_TYPE)); + assertTrue(ARRAY_TYPE.isSubtype(arrayOfString)); + assertTrue(arrayOfString.isSubtype(arrayOfUnknown)); + assertTrue(arrayOfUnknown.isSubtype(arrayOfString)); + + assertFalse(arrayOfString.isSubtype(arrayOfNumber)); + assertFalse(arrayOfNumber.isSubtype(arrayOfString)); + + assertTrue(arrayOfNumber.isSubtype(createUnionType(arrayOfNumber, NULL_VOID))); + assertFalse(createUnionType(arrayOfNumber, NULL_VOID).isSubtype(arrayOfNumber)); + assertFalse(arrayOfString.isSubtype(createUnionType(arrayOfNumber, NULL_VOID))); + } + + public void testParameterizedTypeRelations() throws Exception { + JSType objectOfString = createParameterizedType( + OBJECT_TYPE, STRING_TYPE); + JSType arrayOfString = createParameterizedType( + ARRAY_TYPE, STRING_TYPE); + JSType arrayOfNumber = createParameterizedType( + ARRAY_TYPE, NUMBER_TYPE); + JSType arrayOfUnknown = createParameterizedType( + ARRAY_TYPE, UNKNOWN_TYPE); + + // Union and least super type cases: + // + // 1) alternate:Array. and current:Object ==> Object + // 2) alternate:Array. and current:Array ==> Array + // 3) alternate:Object. and current:Array ==> Array|Object. + // 4) alternate:Object and current:Array. ==> Object + // 5) alternate:Array and current:Array. ==> Array + // 6) alternate:Array and current:Object. ==> Array|Object. + // 7) alternate:Array. and current:Array. ==> Array. + // 8) alternate:Array. and current:Array. ==> Array. + // 9) alternate:Array. and + // current:Object. ==> Object.|Array. + + assertTypeEquals( + OBJECT_TYPE, + JSType.getLeastSupertype(arrayOfString, OBJECT_TYPE)); + assertTypeEquals( + OBJECT_TYPE, + JSType.getLeastSupertype(OBJECT_TYPE, arrayOfString)); + + assertTypeEquals( + ARRAY_TYPE, + JSType.getLeastSupertype(arrayOfString, ARRAY_TYPE)); + assertTypeEquals( + ARRAY_TYPE, + JSType.getLeastSupertype(ARRAY_TYPE, arrayOfString)); + + assertEquals( + "(Array|Object.)", + JSType.getLeastSupertype(objectOfString, ARRAY_TYPE).toString()); + assertEquals( + "(Array|Object.)", + JSType.getLeastSupertype(ARRAY_TYPE, objectOfString).toString()); + + assertEquals( + "Array", + JSType.getLeastSupertype(arrayOfString, arrayOfNumber).toString()); + assertEquals( + "Array", + JSType.getLeastSupertype(arrayOfNumber, arrayOfString).toString()); + assertTypeEquals( + arrayOfString, + JSType.getLeastSupertype(arrayOfString, arrayOfString)); + + assertEquals( + "(Array.|Object.)", + JSType.getLeastSupertype(objectOfString, arrayOfString).toString()); + assertEquals( + "(Array.|Object.)", + JSType.getLeastSupertype(arrayOfString, objectOfString).toString()); + + assertTypeEquals( + objectOfString, + JSType.getGreatestSubtype(OBJECT_TYPE, objectOfString)); + + assertTypeEquals( + objectOfString, + JSType.getGreatestSubtype(objectOfString, OBJECT_TYPE)); + + assertTypeEquals( + ARRAY_TYPE, + JSType.getGreatestSubtype(objectOfString, ARRAY_TYPE)); + + assertTypeEquals( + JSType.getGreatestSubtype(objectOfString, arrayOfString), + NO_OBJECT_TYPE); + + assertTypeEquals( + JSType.getGreatestSubtype(OBJECT_TYPE, arrayOfString), + arrayOfString); + } + + /** + * Tests that the given chain of types has a total ordering defined + * by the subtype relationship, with types at the top of the lattice + * listed first. + * + * Also verifies that the infimum of any two types on the chain + * is the lower type, and the supremum of any two types on the chain + * is the higher type. + */ + public void verifySubtypeChain(List typeChain) throws Exception { + verifySubtypeChain(typeChain, true); + } + + public void verifySubtypeChain(List typeChain, + boolean checkSubtyping) throws Exception { + // Ugh. This wouldn't require so much copy-and-paste if we had a functional + // programming language. + for (int i = 0; i < typeChain.size(); i++) { + for (int j = 0; j < typeChain.size(); j++) { + JSType typeI = typeChain.get(i); + JSType typeJ = typeChain.get(j); + + JSType namedTypeI = getNamedWrapper("TypeI", typeI); + JSType namedTypeJ = getNamedWrapper("TypeJ", typeJ); + JSType proxyTypeI = new ProxyObjectType(registry, typeI); + JSType proxyTypeJ = new ProxyObjectType(registry, typeJ); + + if (i == j) { + assertTrue(typeI + " should equal itself", + typeI.isEquivalentTo(typeI)); + assertTrue("Named " + typeI + " should equal itself", + namedTypeI.isEquivalentTo(namedTypeI)); + assertTrue("Proxy " + typeI + " should equal itself", + proxyTypeI.isEquivalentTo(proxyTypeI)); + } else { + assertFalse(typeI + " should not equal " + typeJ, + typeI.isEquivalentTo(typeJ)); + assertFalse("Named " + typeI + " should not equal " + typeJ, + namedTypeI.isEquivalentTo(namedTypeJ)); + assertFalse("Proxy " + typeI + " should not equal " + typeJ, + proxyTypeI.isEquivalentTo(proxyTypeJ)); + } + + assertTrue(typeJ + " should be castable to " + typeI, + typeJ.canCastTo(typeI)); + assertTrue(typeJ + " should be castable to Named " + namedTypeI, + typeJ.canCastTo(namedTypeI)); + assertTrue(typeJ + " should be castable to Proxy " + proxyTypeI, + typeJ.canCastTo(proxyTypeI)); + + assertTrue( + "Named " + typeJ + " should be castable to " + typeI, + namedTypeJ.canCastTo(typeI)); + assertTrue( + "Named " + typeJ + " should be castable to Named " + typeI, + namedTypeJ.canCastTo(namedTypeI)); + assertTrue( + "Named " + typeJ + " should be castable to Proxy " + typeI, + namedTypeJ.canCastTo(proxyTypeI)); + + assertTrue( + "Proxy " + typeJ + " should be castable to " + typeI, + proxyTypeJ.canCastTo(typeI)); + assertTrue( + "Proxy " + typeJ + " should be castable to Named " + typeI, + proxyTypeJ.canCastTo(namedTypeI)); + assertTrue( + "Proxy " + typeJ + " should be castable to Proxy " + typeI, + proxyTypeJ.canCastTo(proxyTypeI)); + + if (checkSubtyping) { + if (i <= j) { + assertTrue(typeJ + " should be a subtype of " + typeI, + typeJ.isSubtype(typeI)); + assertTrue( + "Named " + typeJ + " should be a subtype of Named " + typeI, + namedTypeJ.isSubtype(namedTypeI)); + assertTrue( + "Proxy " + typeJ + " should be a subtype of Proxy " + typeI, + proxyTypeJ.isSubtype(proxyTypeI)); + } else { + assertFalse(typeJ + " should not be a subtype of " + typeI, + typeJ.isSubtype(typeI)); + assertFalse( + "Named " + typeJ + " should not be a subtype of Named " + typeI, + namedTypeJ.isSubtype(namedTypeI)); + assertFalse( + "Named " + typeJ + " should not be a subtype of Named " + typeI, + proxyTypeJ.isSubtype(proxyTypeI)); + } + + JSType expectedSupremum = i < j ? typeI : typeJ; + JSType expectedInfimum = i > j ? typeI : typeJ; + + assertTypeEquals( + expectedSupremum + " should be the least supertype of " + typeI + + " and " + typeJ, + expectedSupremum, typeI.getLeastSupertype(typeJ)); + + // TODO(nicksantos): Should these tests pass? + //assertTypeEquals( + // expectedSupremum + " should be the least supertype of Named " + + // typeI + " and Named " + typeJ, + // expectedSupremum, namedTypeI.getLeastSupertype(namedTypeJ)); + //assertTypeEquals( + // expectedSupremum + " should be the least supertype of Proxy " + + // typeI + " and Proxy " + typeJ, + // expectedSupremum, proxyTypeI.getLeastSupertype(proxyTypeJ)); + + assertTypeEquals( + expectedInfimum + " should be the greatest subtype of " + typeI + + " and " + typeJ, + expectedInfimum, typeI.getGreatestSubtype(typeJ)); + + // TODO(nicksantos): Should these tests pass? + //assertTypeEquals( + // expectedInfimum + " should be the greatest subtype of Named " + + // typeI + " and Named " + typeJ, + // expectedInfimum, namedTypeI.getGreatestSubtype(namedTypeJ)); + //assertTypeEquals( + // expectedInfimum + " should be the greatest subtype of Proxy " + + // typeI + " and Proxy " + typeJ, + // expectedInfimum, proxyTypeI.getGreatestSubtype(proxyTypeJ)); + } + } + } + } + + JSType getNamedWrapper(String name, JSType jstype) { + // Normally, there is no way to create a Named NoType alias so + // avoid confusing things by doing it here.. + if (!jstype.isNoType()) { + NamedType namedWrapper = new NamedType( + registry, name, "[testcode]", -1, -1); + namedWrapper.setReferencedType(jstype); + return namedWrapper; + } else { + return jstype; + } + } + + /** + * Tests the behavior of + * {@link JSType#getRestrictedTypeGivenToBooleanOutcome(boolean)}. + */ + @SuppressWarnings("checked") + public void testRestrictedTypeGivenToBoolean() { + // simple cases + assertTypeEquals(BOOLEAN_TYPE, + BOOLEAN_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); + assertTypeEquals(BOOLEAN_TYPE, + BOOLEAN_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); + + assertTypeEquals(NO_TYPE, + NULL_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); + assertTypeEquals(NULL_TYPE, + NULL_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); + + assertTypeEquals(NUMBER_TYPE, + NUMBER_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); + assertTypeEquals(NUMBER_TYPE, + NUMBER_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); + + assertTypeEquals(STRING_TYPE, + STRING_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); + assertTypeEquals(STRING_TYPE, + STRING_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); + + assertTypeEquals(STRING_OBJECT_TYPE, + STRING_OBJECT_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); + assertTypeEquals(NO_TYPE, + STRING_OBJECT_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); + + assertTypeEquals(NO_TYPE, + VOID_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); + assertTypeEquals(VOID_TYPE, + VOID_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); + + assertTypeEquals(NO_OBJECT_TYPE, + NO_OBJECT_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); + assertTypeEquals(NO_TYPE, + NO_OBJECT_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); + + assertTypeEquals(NO_TYPE, + NO_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); + assertTypeEquals(NO_TYPE, + NO_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); + + assertTypeEquals(ALL_TYPE, + ALL_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); + assertTypeEquals(ALL_TYPE, + ALL_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); + + assertTypeEquals(CHECKED_UNKNOWN_TYPE, + UNKNOWN_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); + assertTypeEquals(UNKNOWN_TYPE, + UNKNOWN_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); + + // unions + UnionType nullableStringValue = + (UnionType) createNullableType(STRING_TYPE); + assertTypeEquals(STRING_TYPE, + nullableStringValue.getRestrictedTypeGivenToBooleanOutcome(true)); + assertTypeEquals(nullableStringValue, + nullableStringValue.getRestrictedTypeGivenToBooleanOutcome(false)); + + UnionType nullableStringObject = + (UnionType) createNullableType(STRING_OBJECT_TYPE); + assertTypeEquals(STRING_OBJECT_TYPE, + nullableStringObject.getRestrictedTypeGivenToBooleanOutcome(true)); + assertTypeEquals(NULL_TYPE, + nullableStringObject.getRestrictedTypeGivenToBooleanOutcome(false)); + } + + public void testRegisterProperty() { + int i = 0; + List allObjects = Lists.newArrayList(); + for (JSType type : types) { + String propName = "ALF" + i++; + if (type instanceof ObjectType) { + + ObjectType objType = (ObjectType) type; + objType.defineDeclaredProperty(propName, UNKNOWN_TYPE, null); + objType.defineDeclaredProperty("allHaz", UNKNOWN_TYPE, null); + + assertTypeEquals(type, + registry.getGreatestSubtypeWithProperty(type, propName)); + + List typesWithProp = + Lists.newArrayList(registry.getTypesWithProperty(propName)); + String message = type.toString(); + assertEquals(message, 1, typesWithProp.size()); + assertTypeEquals(type, typesWithProp.get(0)); + + assertTypeEquals(NO_TYPE, + registry.getGreatestSubtypeWithProperty(type, "GRRR")); + allObjects.add(type); + } + } + assertTypeListEquals(registry.getTypesWithProperty("GRRR"), + Lists.newArrayList(NO_TYPE)); + assertTypeListEquals(allObjects, + registry.getTypesWithProperty("allHaz")); + } + + public void testRegisterPropertyMemoization() { + ObjectType derived1 = registry.createObjectType("d1", null, namedGoogBar); + ObjectType derived2 = registry.createObjectType("d2", null, namedGoogBar); + + derived1.defineDeclaredProperty("propz", UNKNOWN_TYPE, null); + + assertTypeEquals(derived1, + registry.getGreatestSubtypeWithProperty(derived1, "propz")); + assertTypeEquals(NO_OBJECT_TYPE, + registry.getGreatestSubtypeWithProperty(derived2, "propz")); + + derived2.defineDeclaredProperty("propz", UNKNOWN_TYPE, null); + + assertTypeEquals(derived1, + registry.getGreatestSubtypeWithProperty(derived1, "propz")); + assertTypeEquals(derived2, + registry.getGreatestSubtypeWithProperty(derived2, "propz")); + } + + /** + * Tests + * {@link JSTypeRegistry#getGreatestSubtypeWithProperty(JSType, String)}. + */ + public void testGreatestSubtypeWithProperty() { + ObjectType foo = registry.createObjectType("foo", null, OBJECT_TYPE); + ObjectType bar = registry.createObjectType("bar", null, namedGoogBar); + + foo.defineDeclaredProperty("propz", UNKNOWN_TYPE, null); + bar.defineDeclaredProperty("propz", UNKNOWN_TYPE, null); + + assertTypeEquals(bar, + registry.getGreatestSubtypeWithProperty(namedGoogBar, "propz")); + } + + public void testGoodSetPrototypeBasedOn() { + FunctionType fun = registry.createConstructorType( + "fun", null, null, null, null); + fun.setPrototypeBasedOn(unresolvedNamedType); + assertTrue(fun.getInstanceType().isUnknownType()); + } + + public void testLateSetPrototypeBasedOn() { + FunctionType fun = registry.createConstructorType( + "fun", null, null, null, null); + assertFalse(fun.getInstanceType().isUnknownType()); + + fun.setPrototypeBasedOn(unresolvedNamedType); + assertTrue(fun.getInstanceType().isUnknownType()); + } + + public void testGetTypeUnderEquality1() { + for (JSType type : types) { + testGetTypeUnderEquality(type, type, type, type); + } + } + + public void testGetTypesUnderEquality2() { + // objects can be equal to numbers + testGetTypeUnderEquality( + NUMBER_TYPE, OBJECT_TYPE, + NUMBER_TYPE, OBJECT_TYPE); + } + + public void testGetTypesUnderEquality3() { + // null == undefined + testGetTypeUnderEquality( + NULL_TYPE, VOID_TYPE, + NULL_TYPE, VOID_TYPE); + } + + @SuppressWarnings("checked") + public void testGetTypesUnderEquality4() { + // (number,string) and number/string + UnionType stringNumber = + (UnionType) createUnionType(NUMBER_TYPE, STRING_TYPE); + testGetTypeUnderEquality( + stringNumber, STRING_TYPE, + stringNumber, STRING_TYPE); + testGetTypeUnderEquality( + stringNumber, NUMBER_TYPE, + stringNumber, NUMBER_TYPE); + } + + public void testGetTypesUnderEquality5() { + // (number,null) and undefined + JSType nullUndefined = createUnionType(VOID_TYPE, NULL_TYPE); + testGetTypeUnderEquality( + nullUndefined, NULL_TYPE, + nullUndefined, NULL_TYPE); + testGetTypeUnderEquality( + nullUndefined, VOID_TYPE, + nullUndefined, VOID_TYPE); + } + + public void testGetTypesUnderEquality6() { + // (number,undefined,null) == null + JSType optNullNumber = createUnionType(VOID_TYPE, NULL_TYPE, NUMBER_TYPE); + testGetTypeUnderEquality( + optNullNumber, NULL_TYPE, + createUnionType(NULL_TYPE, VOID_TYPE), NULL_TYPE); + } + + private void testGetTypeUnderEquality( + JSType t1, JSType t2, JSType t1Eq, JSType t2Eq) { + // creating the pairs + TypePair p12 = t1.getTypesUnderEquality(t2); + TypePair p21 = t2.getTypesUnderEquality(t1); + + // t1Eq + assertTypeEquals(t1Eq, p12.typeA); + assertTypeEquals(t1Eq, p21.typeB); + + // t2Eq + assertTypeEquals(t2Eq, p12.typeB); + assertTypeEquals(t2Eq, p21.typeA); + } + + @SuppressWarnings("checked") + public void testGetTypesUnderInequality1() { + // objects can be not equal to numbers + UnionType numberObject = + (UnionType) createUnionType(NUMBER_TYPE, OBJECT_TYPE); + testGetTypesUnderInequality( + numberObject, NUMBER_TYPE, + numberObject, NUMBER_TYPE); + testGetTypesUnderInequality( + numberObject, OBJECT_TYPE, + numberObject, OBJECT_TYPE); + } + + @SuppressWarnings("checked") + public void testGetTypesUnderInequality2() { + // null == undefined + UnionType nullUndefined = + (UnionType) createUnionType(VOID_TYPE, NULL_TYPE); + testGetTypesUnderInequality( + nullUndefined, NULL_TYPE, + NO_TYPE, NO_TYPE); + testGetTypesUnderInequality( + nullUndefined, VOID_TYPE, + NO_TYPE, NO_TYPE); + } + + @SuppressWarnings("checked") + public void testGetTypesUnderInequality3() { + // (number,string) + UnionType stringNumber = + (UnionType) createUnionType(NUMBER_TYPE, STRING_TYPE); + testGetTypesUnderInequality( + stringNumber, NUMBER_TYPE, + stringNumber, NUMBER_TYPE); + testGetTypesUnderInequality( + stringNumber, STRING_TYPE, + stringNumber, STRING_TYPE); + } + + @SuppressWarnings("checked") + public void testGetTypesUnderInequality4() throws Exception { + // (number,undefined,null) and null + UnionType nullableOptionalNumber = + (UnionType) createUnionType(NULL_TYPE, VOID_TYPE, NUMBER_TYPE); + testGetTypesUnderInequality( + nullableOptionalNumber, NULL_TYPE, + NUMBER_TYPE, NULL_TYPE); + } + + private void testGetTypesUnderInequality( + JSType t1, JSType t2, JSType t1Eq, JSType t2Eq) { + // creating the pairs + TypePair p12 = t1.getTypesUnderInequality(t2); + TypePair p21 = t2.getTypesUnderInequality(t1); + + // t1Eq + assertTypeEquals(t1Eq, p12.typeA); + assertTypeEquals(t1Eq, p21.typeB); + + // t2Eq + assertTypeEquals(t2Eq, p12.typeB); + assertTypeEquals(t2Eq, p21.typeA); + } + + + /** + * Tests the factory method + * {@link JSTypeRegistry#createRecordType}. + */ + public void testCreateRecordType() throws Exception { + Map properties = + new HashMap(); + properties.put("hello", new RecordProperty(NUMBER_TYPE, null)); + + JSType recordType = registry.createRecordType(properties); + assertEquals("{hello: number}", recordType.toString()); + } + + /** + * Tests the factory method {@link JSTypeRegistry#createOptionalType(JSType)}. + */ + public void testCreateOptionalType() throws Exception { + // number + UnionType optNumber = (UnionType) registry.createOptionalType(NUMBER_TYPE); + assertUnionContains(optNumber, NUMBER_TYPE); + assertUnionContains(optNumber, VOID_TYPE); + + // union + UnionType optUnion = + (UnionType) registry.createOptionalType( + createUnionType(STRING_OBJECT_TYPE, DATE_TYPE)); + assertUnionContains(optUnion, DATE_TYPE); + assertUnionContains(optUnion, STRING_OBJECT_TYPE); + assertUnionContains(optUnion, VOID_TYPE); + } + + public void assertUnionContains(UnionType union, JSType type) { + assertTrue(union + " should contain " + type, union.contains(type)); + } + + /** + * Tests the factory method + * {@link JSTypeRegistry#createAnonymousObjectType}}. + */ + public void testCreateAnonymousObjectType() throws Exception { + // anonymous + ObjectType anonymous = registry.createAnonymousObjectType(null); + assertTypeEquals(OBJECT_TYPE, anonymous.getImplicitPrototype()); + assertNull(anonymous.getReferenceName()); + assertEquals("{}", anonymous.toString()); + } + + /** + * Tests the factory method + * {@link JSTypeRegistry#createAnonymousObjectType}} and adds + * some properties to it. + */ + public void testCreateAnonymousObjectType2() throws Exception { + // anonymous + ObjectType anonymous = registry.createAnonymousObjectType(null); + anonymous.defineDeclaredProperty( + "a", NUMBER_TYPE, null); + anonymous.defineDeclaredProperty( + "b", NUMBER_TYPE, null); + anonymous.defineDeclaredProperty( + "c", NUMBER_TYPE, null); + anonymous.defineDeclaredProperty( + "d", NUMBER_TYPE, null); + anonymous.defineDeclaredProperty( + "e", NUMBER_TYPE, null); + anonymous.defineDeclaredProperty( + "f", NUMBER_TYPE, null); + assertEquals("{a: number, b: number, c: number, d: number, ...}", + anonymous.toString()); + } + + /** + * Tests the factory methods + * {@link JSTypeRegistry#createObjectType(ObjectType)}} and + * {@link JSTypeRegistry#createObjectType(String, Node, ObjectType)}}. + */ + public void testCreateObjectType() throws Exception { + // simple + ObjectType subDate = + registry.createObjectType(DATE_TYPE.getImplicitPrototype()); + assertTypeEquals(DATE_TYPE.getImplicitPrototype(), + subDate.getImplicitPrototype()); + assertNull(subDate.getReferenceName()); + assertEquals("{...}", subDate.toString()); + + // name, node, prototype + ObjectType subError = registry.createObjectType("Foo", null, + ERROR_TYPE.getImplicitPrototype()); + assertTypeEquals(ERROR_TYPE.getImplicitPrototype(), + subError.getImplicitPrototype()); + assertEquals("Foo", subError.getReferenceName()); + } + + /** + * Tests {@code (U2U_CONSTRUCTOR,undefined) <: (U2U_CONSTRUCTOR,undefined)}. + */ + @SuppressWarnings("checked") + public void testBug903110() throws Exception { + UnionType union = + (UnionType) createUnionType(U2U_CONSTRUCTOR_TYPE, VOID_TYPE); + assertTrue(VOID_TYPE.isSubtype(union)); + assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(union)); + assertTrue(union.isSubtype(union)); + } + + /** + * Tests {@code U2U_FUNCTION_TYPE <: U2U_CONSTRUCTOR} and + * {@code U2U_FUNCTION_TYPE <: (U2U_CONSTRUCTOR,undefined)}. + */ + public void testBug904123() throws Exception { + assertTrue(U2U_FUNCTION_TYPE.isSubtype(U2U_CONSTRUCTOR_TYPE)); + assertTrue(U2U_FUNCTION_TYPE. + isSubtype(createOptionalType(U2U_CONSTRUCTOR_TYPE))); + } + + /** + * Assert that a type can assign to itself. + */ + private void assertTypeCanAssignToItself(JSType type) { + assertTrue(type.isSubtype(type)); + } + + /** + * Tests that hasOwnProperty returns true when a property is defined directly + * on a class and false if the property is defined on the supertype or not at + * all. + */ + public void testHasOwnProperty() throws Exception { + ObjectType sup = + registry.createObjectType(registry.createAnonymousObjectType(null)); + ObjectType sub = registry.createObjectType(sup); + + sup.defineProperty("base", null, false, null); + sub.defineProperty("sub", null, false, null); + + assertTrue(sup.hasProperty("base")); + assertFalse(sup.hasProperty("sub")); + assertTrue(sup.hasOwnProperty("base")); + assertFalse(sup.hasOwnProperty("sub")); + assertFalse(sup.hasOwnProperty("none")); + + assertTrue(sub.hasProperty("base")); + assertTrue(sub.hasProperty("sub")); + assertFalse(sub.hasOwnProperty("base")); + assertTrue(sub.hasOwnProperty("sub")); + assertFalse(sub.hasOwnProperty("none")); + } + + public void testNamedTypeHasOwnProperty() throws Exception { + namedGoogBar.getImplicitPrototype().defineProperty("base", null, false, + null); + namedGoogBar.defineProperty("sub", null, false, null); + + assertFalse(namedGoogBar.hasOwnProperty("base")); + assertTrue(namedGoogBar.hasProperty("base")); + assertTrue(namedGoogBar.hasOwnProperty("sub")); + assertTrue(namedGoogBar.hasProperty("sub")); + } + + public void testInterfaceHasOwnProperty() throws Exception { + interfaceInstType.defineProperty("base", null, false, null); + subInterfaceInstType.defineProperty("sub", null, false, null); + + assertTrue(interfaceInstType.hasProperty("base")); + assertFalse(interfaceInstType.hasProperty("sub")); + assertTrue(interfaceInstType.hasOwnProperty("base")); + assertFalse(interfaceInstType.hasOwnProperty("sub")); + assertFalse(interfaceInstType.hasOwnProperty("none")); + + assertTrue(subInterfaceInstType.hasProperty("base")); + assertTrue(subInterfaceInstType.hasProperty("sub")); + assertFalse(subInterfaceInstType.hasOwnProperty("base")); + assertTrue(subInterfaceInstType.hasOwnProperty("sub")); + assertFalse(subInterfaceInstType.hasOwnProperty("none")); + } + + public void testGetPropertyNames() throws Exception { + ObjectType sup = + registry.createObjectType(registry.createAnonymousObjectType(null)); + ObjectType sub = registry.createObjectType(sup); + + sup.defineProperty("base", null, false, null); + sub.defineProperty("sub", null, false, null); + + assertEquals(Sets.newHashSet("isPrototypeOf", "toLocaleString", + "propertyIsEnumerable", "toString", "valueOf", "hasOwnProperty", + "constructor", "base", "sub"), sub.getPropertyNames()); + assertEquals(Sets.newHashSet("isPrototypeOf", "toLocaleString", + "propertyIsEnumerable", "toString", "valueOf", "hasOwnProperty", + "constructor", "base"), sup.getPropertyNames()); + + assertEquals(Sets.newHashSet(), NO_OBJECT_TYPE.getPropertyNames()); + } + + public void testGetAndSetJSDocInfoWithNamedType() throws Exception { + JSDocInfo info = new JSDocInfo(); + info.setDeprecated(true); + + assertNull(namedGoogBar.getOwnPropertyJSDocInfo("X")); + namedGoogBar.setPropertyJSDocInfo("X", info); + assertTrue(namedGoogBar.getOwnPropertyJSDocInfo("X").isDeprecated()); + assertPropertyTypeInferred(namedGoogBar, "X"); + assertTypeEquals(UNKNOWN_TYPE, namedGoogBar.getPropertyType("X")); + } + + public void testGetAndSetJSDocInfoWithObjectTypes() throws Exception { + ObjectType sup = + registry.createObjectType(registry.createAnonymousObjectType(null)); + ObjectType sub = registry.createObjectType(sup); + + JSDocInfo deprecated = new JSDocInfo(); + deprecated.setDeprecated(true); + + JSDocInfo privateInfo = new JSDocInfo(); + privateInfo.setVisibility(Visibility.PRIVATE); + + sup.defineProperty("X", NUMBER_TYPE, true, null); + sup.setPropertyJSDocInfo("X", privateInfo); + + sub.defineProperty("X", NUMBER_TYPE, true, null); + sub.setPropertyJSDocInfo("X", deprecated); + + assertFalse(sup.getOwnPropertyJSDocInfo("X").isDeprecated()); + assertEquals(Visibility.PRIVATE, + sup.getOwnPropertyJSDocInfo("X").getVisibility()); + assertTypeEquals(NUMBER_TYPE, sup.getPropertyType("X")); + assertTrue(sub.getOwnPropertyJSDocInfo("X").isDeprecated()); + assertNull(sub.getOwnPropertyJSDocInfo("X").getVisibility()); + assertTypeEquals(NUMBER_TYPE, sub.getPropertyType("X")); + } + + public void testGetAndSetJSDocInfoWithNoType() throws Exception { + JSDocInfo deprecated = new JSDocInfo(); + deprecated.setDeprecated(true); + + NO_TYPE.setPropertyJSDocInfo("X", deprecated); + assertNull(NO_TYPE.getOwnPropertyJSDocInfo("X")); + } + + public void testObjectGetSubTypes() throws Exception { + assertTrue( + containsType( + OBJECT_FUNCTION_TYPE.getSubTypes(), googBar)); + assertTrue( + containsType( + googBar.getSubTypes(), googSubBar)); + assertFalse( + containsType( + googBar.getSubTypes(), googSubSubBar)); + assertFalse( + containsType( + googSubBar.getSubTypes(), googSubBar)); + assertTrue( + containsType( + googSubBar.getSubTypes(), googSubSubBar)); + } + + public void testImplementingType() throws Exception { + assertTrue( + containsType( + registry.getDirectImplementors( + interfaceType.getInstanceType()), + googBar)); + } + + public void testIsTemplatedType() throws Exception { + assertTrue( + new TemplateType(registry, "T") + .hasAnyTemplateTypes()); + assertFalse( + ARRAY_TYPE + .hasAnyTemplateTypes()); + + assertTrue( + registry.createParameterizedType( + ARRAY_TYPE, new TemplateType(registry, "T")) + .hasAnyTemplateTypes()); + assertFalse( + registry.createParameterizedType( + ARRAY_TYPE, STRING_TYPE) + .hasAnyTemplateTypes()); + + assertTrue( + new FunctionBuilder(registry) + .withReturnType(new TemplateType(registry, "T")) + .build() + .hasAnyTemplateTypes()); + assertTrue( + new FunctionBuilder(registry) + .withTypeOfThis(new TemplateType(registry, "T")) + .build() + .hasAnyTemplateTypes()); + assertFalse( + new FunctionBuilder(registry) + .withReturnType(STRING_TYPE) + .build() + .hasAnyTemplateTypes()); + + assertTrue( + registry.createUnionType( + NULL_TYPE, new TemplateType(registry, "T"), STRING_TYPE) + .hasAnyTemplateTypes()); + assertFalse( + registry.createUnionType( + NULL_TYPE, ARRAY_TYPE, STRING_TYPE) + .hasAnyTemplateTypes()); + } + + public void testTemplatizedType() throws Exception { + FunctionType templatizedCtor = registry.createConstructorType( + "TestingType", null, null, UNKNOWN_TYPE, ImmutableList.of("A", "B")); + JSType templatizedInstance = registry.createTemplatizedType( + templatizedCtor.getInstanceType(), + ImmutableList.of(NUMBER_TYPE, STRING_TYPE)); + + assertTrue(templatizedInstance.isTemplatized()); + assertTrue(templatizedInstance.hasTemplatizedType("A")); + assertTrue(templatizedInstance.hasTemplatizedType("B")); + assertFalse(templatizedInstance.hasTemplatizedType("C")); + + assertEquals(NUMBER_TYPE, templatizedInstance.getTemplatizedType("A")); + assertEquals(STRING_TYPE, templatizedInstance.getTemplatizedType("B")); + assertEquals(UNKNOWN_TYPE, templatizedInstance.getTemplatizedType("C")); + + assertEquals("TestingType.", templatizedInstance.toString()); + } + + public void testPartiallyTemplatizedType() throws Exception { + FunctionType templatizedCtor = registry.createConstructorType( + "TestingType", null, null, UNKNOWN_TYPE, ImmutableList.of("A", "B")); + JSType templatizedInstance = registry.createTemplatizedType( + templatizedCtor.getInstanceType(), + ImmutableList.of(NUMBER_TYPE)); + + assertTrue(templatizedInstance.isTemplatized()); + assertTrue(templatizedInstance.hasTemplatizedType("A")); + assertTrue(templatizedInstance.hasTemplatizedType("B")); + assertFalse(templatizedInstance.hasTemplatizedType("C")); + + assertEquals(NUMBER_TYPE, templatizedInstance.getTemplatizedType("A")); + assertEquals(UNKNOWN_TYPE, templatizedInstance.getTemplatizedType("B")); + assertEquals(UNKNOWN_TYPE, templatizedInstance.getTemplatizedType("C")); + + assertEquals("TestingType.", templatizedInstance.toString()); + } + + public void testInvalidTemplatizedType() throws Exception { + FunctionType templatizedCtor = registry.createConstructorType( + "TestingType", null, null, UNKNOWN_TYPE, ImmutableList.of("A", "B")); + + boolean exceptionThrown = false; + try { + JSType templatizedInstance = registry.createTemplatizedType( + templatizedCtor.getInstanceType(), + ImmutableList.of(NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE)); + } catch (IllegalArgumentException e) { + exceptionThrown = true; + } + assertTrue(exceptionThrown); + } + + public void testCanCastTo() { + assertTrue(ALL_TYPE.canCastTo(NULL_TYPE)); + assertTrue(ALL_TYPE.canCastTo(VOID_TYPE)); + assertTrue(ALL_TYPE.canCastTo(STRING_TYPE)); + assertTrue(ALL_TYPE.canCastTo(NUMBER_TYPE)); + assertTrue(ALL_TYPE.canCastTo(BOOLEAN_TYPE)); + assertTrue(ALL_TYPE.canCastTo(OBJECT_TYPE)); + + assertFalse(NUMBER_TYPE.canCastTo(NULL_TYPE)); + assertFalse(NUMBER_TYPE.canCastTo(VOID_TYPE)); + assertFalse(NUMBER_TYPE.canCastTo(STRING_TYPE)); + assertTrue(NUMBER_TYPE.canCastTo(NUMBER_TYPE)); + assertFalse(NUMBER_TYPE.canCastTo(BOOLEAN_TYPE)); + assertFalse(NUMBER_TYPE.canCastTo(OBJECT_TYPE)); + + assertFalse(STRING_TYPE.canCastTo(NULL_TYPE)); + assertFalse(STRING_TYPE.canCastTo(VOID_TYPE)); + assertTrue(STRING_TYPE.canCastTo(STRING_TYPE)); + assertFalse(STRING_TYPE.canCastTo(NUMBER_TYPE)); + assertFalse(STRING_TYPE.canCastTo(BOOLEAN_TYPE)); + assertFalse(STRING_TYPE.canCastTo(OBJECT_TYPE)); + + assertFalse(BOOLEAN_TYPE.canCastTo(NULL_TYPE)); + assertFalse(BOOLEAN_TYPE.canCastTo(VOID_TYPE)); + assertFalse(BOOLEAN_TYPE.canCastTo(STRING_TYPE)); + assertFalse(BOOLEAN_TYPE.canCastTo(NUMBER_TYPE)); + assertTrue(BOOLEAN_TYPE.canCastTo(BOOLEAN_TYPE)); + assertFalse(BOOLEAN_TYPE.canCastTo(OBJECT_TYPE)); + + assertFalse(OBJECT_TYPE.canCastTo(NULL_TYPE)); + assertFalse(OBJECT_TYPE.canCastTo(VOID_TYPE)); + assertFalse(OBJECT_TYPE.canCastTo(STRING_TYPE)); + assertFalse(OBJECT_TYPE.canCastTo(NUMBER_TYPE)); + assertFalse(OBJECT_TYPE.canCastTo(BOOLEAN_TYPE)); + assertTrue(OBJECT_TYPE.canCastTo(OBJECT_TYPE)); + + assertFalse(BOOLEAN_TYPE.canCastTo(OBJECT_NUMBER_STRING)); + assertFalse(OBJECT_NUMBER_STRING.canCastTo(BOOLEAN_TYPE)); + + assertFalse(ARRAY_TYPE.canCastTo(U2U_FUNCTION_TYPE)); + assertFalse(U2U_FUNCTION_TYPE.canCastTo(ARRAY_TYPE)); + + assertFalse(NULL_VOID.canCastTo(ARRAY_TYPE)); + assertTrue(NULL_VOID.canCastTo(createUnionType(ARRAY_TYPE, NULL_TYPE))); + + // We currently allow any function to be cast to any other function type + assertTrue(ARRAY_FUNCTION_TYPE.canCastTo(BOOLEAN_OBJECT_FUNCTION_TYPE)); + + } + + private static boolean containsType( + Iterable types, JSType type) { + for (JSType alt : types) { + if (alt.isEquivalentTo(type)) { + return true; + } + } + return false; + } + + private static boolean assertTypeListEquals( + Iterable typeListA, + Iterable typeListB) { + for (JSType alt : typeListA) { + assertTrue( + "List : " + typeListA + "\n" + + "does not contain: " + alt, + containsType(typeListA, alt)); + } + for (JSType alt : typeListB) { + assertTrue( + "List : " + typeListB + "\n" + + "does not contain: " + alt, + containsType(typeListB, alt)); + } + return false; + } + + private ArrowType createArrowType(Node params) { + return registry.createArrowType(params); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/NamedTypeTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/NamedTypeTest.java new file mode 100644 index 0000000..e20af45 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/NamedTypeTest.java @@ -0,0 +1,73 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.common.collect.ImmutableMap; +import com.google.javascript.rhino.testing.BaseJSTypeTestCase; +import com.google.javascript.rhino.testing.MapBasedScope; + +/** + * @author nicksantos@google.com (Nick Santos) + */ +public class NamedTypeTest extends BaseJSTypeTestCase { + public void testNamedTypeProperties() { + NamedType namedA = new NamedType(registry, "TypeA", "source", 1, 0); + FunctionType ctorA = registry.createConstructorType( + "TypeA", null, null, null, null); + ObjectType typeA = ctorA.getInstanceType(); + + namedA.defineDeclaredProperty("foo", NUMBER_TYPE, null); + namedA.resolve( + null, + new MapBasedScope( + ImmutableMap.of("TypeA", ctorA))); + assertTypeEquals(NUMBER_TYPE, typeA.getPropertyType("foo")); + } + + public void testActiveXObjectResolve() { + NamedType activeXObject = + new NamedType(registry, "ActiveXObject", "source", 1, 0); + activeXObject.resolve( + null, + new MapBasedScope( + ImmutableMap.of("ActiveXObject", NO_OBJECT_TYPE))); + assertEquals("ActiveXObject", activeXObject.toString()); + assertTypeEquals(NO_OBJECT_TYPE, activeXObject.getReferencedType()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/ParameterizedTypeTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/ParameterizedTypeTest.java new file mode 100644 index 0000000..8d4fd3d --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/ParameterizedTypeTest.java @@ -0,0 +1,115 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Lenz + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.testing.BaseJSTypeTestCase; + +public class ParameterizedTypeTest extends BaseJSTypeTestCase { + + @Override + public void setUp() throws Exception { + super.setUp(); + } + + @Override + protected ParameterizedType createParameterizedType( + ObjectType objectType, JSType parameterType) { + return registry.createParameterizedType(objectType, parameterType); + } + + /** + * Assert that a type can assign to itself. + */ + private void assertTypeCanAssignToItself(JSType type) { + assertTrue(type.isSubtype(type)); + } + + /** + * Tests the behavior of variants type. + */ + @SuppressWarnings("checked") + public void testParameterizedType() throws Exception { + ParameterizedType arrOfString = createParameterizedType( + ARRAY_TYPE, STRING_TYPE); + assertTypeCanAssignToItself(arrOfString); + assertTrue(arrOfString.isSubtype(ARRAY_TYPE)); + assertTrue(ARRAY_TYPE.isSubtype(arrOfString)); + + ParameterizedType arrOfNumber = createParameterizedType( + ARRAY_TYPE, NUMBER_TYPE); + assertTypeCanAssignToItself(arrOfNumber); + assertTrue(arrOfNumber.isSubtype(ARRAY_TYPE)); + assertTrue(ARRAY_TYPE.isSubtype(arrOfNumber)); + + assertTrue(arrOfString.isEquivalentTo(createParameterizedType( + ARRAY_TYPE, STRING_TYPE))); + + assertFalse(arrOfString.isEquivalentTo(ARRAY_TYPE)); + assertFalse(arrOfString.isEquivalentTo(ARRAY_TYPE)); + assertFalse(arrOfString.isEquivalentTo(arrOfNumber)); + assertFalse(arrOfNumber.isEquivalentTo(arrOfString)); + } + + public void testPrint1() throws Exception { + ParameterizedType arrOfString = createParameterizedType( + ARRAY_TYPE, STRING_TYPE); + assertEquals("Array.", arrOfString.toString()); + } + + public void testPrint2() throws Exception { + ParameterizedType arrOfTemplateType = createParameterizedType( + ARRAY_TYPE, new TemplateType(registry, "T")); + assertEquals("Array.", arrOfTemplateType.toString()); + } + + public void testPrint3() throws Exception { + ParameterizedType arrOfUnknown = createParameterizedType( + ARRAY_TYPE, UNKNOWN_TYPE); + assertEquals("Array.", arrOfUnknown.toString()); + } + + public void testDifferentRawTypes() throws Exception { + ParameterizedType arrOfNumber = createParameterizedType( + ARRAY_TYPE, NUMBER_TYPE); + ParameterizedType objType = createParameterizedType( + OBJECT_TYPE, UNKNOWN_TYPE); + assertTrue(arrOfNumber.isSubtype(objType)); + assertFalse(objType.isSubtype(arrOfNumber)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/PrototypeObjectTypeTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/PrototypeObjectTypeTest.java new file mode 100644 index 0000000..87b8893 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/PrototypeObjectTypeTest.java @@ -0,0 +1,55 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.testing.BaseJSTypeTestCase; + +public class PrototypeObjectTypeTest extends BaseJSTypeTestCase { + + public void testToString() { + ObjectType type = registry.createAnonymousObjectType(null); + assertEquals("{}", type.toString()); + + type.defineDeclaredProperty("foo", NUMBER_TYPE, null); + assertEquals("{foo: number}", type.toString()); + + type.defineDeclaredProperty("bar", type, null); + assertEquals("{bar: {...}, foo: number}", type.toString()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/RecordTypeTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/RecordTypeTest.java new file mode 100644 index 0000000..3371dcd --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/RecordTypeTest.java @@ -0,0 +1,181 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.testing.Asserts; +import com.google.javascript.rhino.testing.BaseJSTypeTestCase; + +public class RecordTypeTest extends BaseJSTypeTestCase { + + public void testRecursiveRecord() { + ProxyObjectType loop = new ProxyObjectType(registry, NUMBER_TYPE); + JSType record = new RecordTypeBuilder(registry) + .addProperty("loop", loop, null) + .addProperty("number", NUMBER_TYPE, null) + .addProperty("string", STRING_TYPE, null) + .build(); + assertEquals("{loop: number, number: number, string: string}", + record.toString()); + + loop.setReferencedType(record); + assertEquals("{loop: {...}, number: number, string: string}", + record.toString()); + assertEquals("{loop: ?, number: number, string: string}", + record.toAnnotationString()); + + Asserts.assertEquivalenceOperations(record, loop); + } + + public void testLongToString() { + JSType record = new RecordTypeBuilder(registry) + .addProperty("a1", NUMBER_TYPE, null) + .addProperty("a2", NUMBER_TYPE, null) + .addProperty("a3", NUMBER_TYPE, null) + .addProperty("a4", NUMBER_TYPE, null) + .addProperty("a5", NUMBER_TYPE, null) + .addProperty("a6", NUMBER_TYPE, null) + .build(); + assertEquals("{a1: number, a2: number, a3: number, a4: number, ...}", + record.toString()); + assertEquals( + "{a1: number, a2: number, a3: number, a4: number," + + " a5: number, a6: number}", + record.toAnnotationString()); + } + + public void testSupAndInf() { + JSType recordA = new RecordTypeBuilder(registry) + .addProperty("a", NUMBER_TYPE, null) + .addProperty("b", NUMBER_TYPE, null) + .build(); + JSType recordC = new RecordTypeBuilder(registry) + .addProperty("b", NUMBER_TYPE, null) + .addProperty("c", NUMBER_TYPE, null) + .build(); + ProxyObjectType proxyRecordA = new ProxyObjectType(registry, recordA); + ProxyObjectType proxyRecordC = new ProxyObjectType(registry, recordC); + + JSType aInfC = new RecordTypeBuilder(registry) + .addProperty("a", NUMBER_TYPE, null) + .addProperty("b", NUMBER_TYPE, null) + .addProperty("c", NUMBER_TYPE, null) + .build(); + + JSType aSupC = registry.createUnionType(recordA, recordC); + + Asserts.assertTypeEquals( + aInfC, recordA.getGreatestSubtype(recordC)); + Asserts.assertTypeEquals( + aSupC, recordA.getLeastSupertype(recordC)); + + Asserts.assertTypeEquals( + aInfC, proxyRecordA.getGreatestSubtype(proxyRecordC)); + Asserts.assertTypeEquals( + aSupC, proxyRecordA.getLeastSupertype(proxyRecordC)); + } + + public void testSubtypeWithUnknowns() throws Exception { + JSType recordA = new RecordTypeBuilder(registry) + .addProperty("a", NUMBER_TYPE, null) + .build(); + JSType recordB = new RecordTypeBuilder(registry) + .addProperty("a", UNKNOWN_TYPE, null) + .build(); + assertTrue(recordA.isSubtype(recordB)); + assertTrue(recordB.isSubtype(recordA)); + } + + public void testSubtypeWithUnknowns2() throws Exception { + JSType recordA = new RecordTypeBuilder(registry) + .addProperty("a", + new FunctionBuilder(registry) + .withReturnType(NUMBER_TYPE) + .build(), + null) + .build(); + JSType recordB = new RecordTypeBuilder(registry) + .addProperty("a", + new FunctionBuilder(registry) + .withReturnType(UNKNOWN_TYPE) + .build(), + null) + .build(); + assertTrue(recordA.isSubtype(recordB)); + assertTrue(recordB.isSubtype(recordA)); + } + + public void testSubtypeWithFunctionProps() throws Exception { + JSType recordA = new RecordTypeBuilder(registry) + .addProperty("a", + new FunctionBuilder(registry) + .withReturnType(NUMBER_TYPE) + .build(), + null) + .build(); + JSType recordB = new RecordTypeBuilder(registry) + .addProperty("a", + new FunctionBuilder(registry) + .withReturnType(STRING_TYPE) + .build(), + null) + .build(); + assertFalse(recordA.isSubtype(recordB)); + assertFalse(recordB.isSubtype(recordA)); + } + + public void testSubtypeWithManyProps() throws Exception { + JSType recordA = new RecordTypeBuilder(registry) + .addProperty("a", NUMBER_TYPE, null) + .addProperty("b", NUMBER_TYPE, null) + .build(); + JSType recordB = new RecordTypeBuilder(registry) + .addProperty("a", NUMBER_TYPE, null) + .addProperty("b", STRING_TYPE, null) + .build(); + JSType recordC = new RecordTypeBuilder(registry) + .addProperty("a", NUMBER_TYPE, null) + .addProperty("b", + registry.createUnionType(NUMBER_TYPE, STRING_TYPE), null) + .build(); + assertFalse(recordA.isSubtype(recordB)); + assertFalse(recordB.isSubtype(recordA)); + assertFalse(recordC.isSubtype(recordB)); + assertFalse(recordB.isSubtype(recordC)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/TernaryValueTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/TernaryValueTest.java new file mode 100644 index 0000000..2dcc7a4 --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/TernaryValueTest.java @@ -0,0 +1,126 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; +import static com.google.javascript.rhino.jstype.TernaryValue.TRUE; +import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; + +import junit.framework.TestCase; + +/** + * Tests the behavior of {@link TernaryValue} by verifying the truth tables + * of the operations {@link TernaryValue#and(TernaryValue)}, + * {@link TernaryValue#not()}, {@link TernaryValue#or(TernaryValue)} + * and {@link TernaryValue#xor(TernaryValue)} as well as the + * {@link TernaryValue#toBoolean(boolean)} method. + * + */ +public class TernaryValueTest extends TestCase { + public void testOrdinal() throws Exception { + assertEquals(0, FALSE.ordinal()); + assertEquals(1, TRUE.ordinal()); + assertEquals(2, UNKNOWN.ordinal()); + } + + public void testAnd() throws Exception { + assertEquals(TRUE, TRUE.and(TRUE)); + assertEquals(FALSE, TRUE.and(FALSE)); + assertEquals(UNKNOWN, TRUE.and(UNKNOWN)); + + assertEquals(FALSE, FALSE.and(TRUE)); + assertEquals(FALSE, FALSE.and(FALSE)); + assertEquals(FALSE, FALSE.and(UNKNOWN)); + + assertEquals(UNKNOWN, UNKNOWN.and(TRUE)); + assertEquals(FALSE, UNKNOWN.and(FALSE)); + assertEquals(UNKNOWN, UNKNOWN.and(UNKNOWN)); + } + + public void testNot() throws Exception { + assertEquals(FALSE, TRUE.not()); + assertEquals(TRUE, FALSE.not()); + assertEquals(UNKNOWN, UNKNOWN.not()); + } + + public void testOr() throws Exception { + assertEquals(TRUE, TRUE.or(TRUE)); + assertEquals(TRUE, TRUE.or(FALSE)); + assertEquals(TRUE, TRUE.or(UNKNOWN)); + + assertEquals(TRUE, FALSE.or(TRUE)); + assertEquals(FALSE, FALSE.or(FALSE)); + assertEquals(UNKNOWN, FALSE.or(UNKNOWN)); + + assertEquals(TRUE, UNKNOWN.or(TRUE)); + assertEquals(UNKNOWN, UNKNOWN.or(FALSE)); + assertEquals(UNKNOWN, UNKNOWN.or(UNKNOWN)); + } + + public void testXor() throws Exception { + assertEquals(FALSE, TRUE.xor(TRUE)); + assertEquals(TRUE, TRUE.xor(FALSE)); + assertEquals(UNKNOWN, TRUE.xor(UNKNOWN)); + + assertEquals(TRUE, FALSE.xor(TRUE)); + assertEquals(FALSE, FALSE.xor(FALSE)); + assertEquals(UNKNOWN, FALSE.xor(UNKNOWN)); + + assertEquals(UNKNOWN, UNKNOWN.xor(TRUE)); + assertEquals(UNKNOWN, UNKNOWN.xor(FALSE)); + assertEquals(UNKNOWN, UNKNOWN.xor(UNKNOWN)); + } + + public void testToBoolean() throws Exception { + assertTrue(TRUE.toBoolean(true)); + assertTrue(TRUE.toBoolean(false)); + + assertFalse(FALSE.toBoolean(true)); + assertFalse(FALSE.toBoolean(false)); + + assertTrue(UNKNOWN.toBoolean(true)); + assertFalse(UNKNOWN.toBoolean(false)); + } + + public void testToString() { + assertEquals("true", TRUE.toString()); + assertEquals("false", FALSE.toString()); + assertEquals("unknown", UNKNOWN.toString()); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/UnionTypeBuilderTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/UnionTypeBuilderTest.java new file mode 100644 index 0000000..e990efe --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/UnionTypeBuilderTest.java @@ -0,0 +1,137 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.testing.BaseJSTypeTestCase; + +/** + * Test for {@link UnionTypeBuilder}. + * + * @author nicksantos@google.com (Nick Santos) + */ +public class UnionTypeBuilderTest extends BaseJSTypeTestCase { + + public void testAllType() { + assertUnion("*", ALL_TYPE); + assertUnion("*", NUMBER_TYPE, ALL_TYPE); + assertUnion("*", ALL_TYPE, NUMBER_TYPE); + assertUnion("*", ALL_TYPE, NUMBER_TYPE, NO_TYPE); + } + + public void testEmptyUnion() { + assertUnion("None"); + assertUnion("None", NO_TYPE, NO_TYPE); + } + + public void testUnionTypes() { + JSType union = registry.createUnionType(STRING_TYPE, OBJECT_TYPE); + + assertUnion("*", ALL_TYPE, union); + assertUnion("(Object|string)", OBJECT_TYPE, union); + assertUnion("(Object|string)", union, OBJECT_TYPE); + assertUnion("(Object|number|string)", NUMBER_TYPE, union); + assertUnion("(Object|number|string)", union, NUMBER_TYPE); + assertUnion("(Object|boolean|number|string)", union, + registry.createUnionType(NUMBER_TYPE, BOOLEAN_TYPE)); + assertUnion("(Object|boolean|number|string)", + registry.createUnionType(NUMBER_TYPE, BOOLEAN_TYPE), union); + assertUnion("(Object|string)", union, STRING_OBJECT_TYPE); + } + + public void testUnknownTypes() { + JSType unresolvedNameA1 = + new NamedType(registry, "not.resolved.A", null, -1, -1); + JSType unresolvedNameA2 = + new NamedType(registry, "not.resolved.A", null, -1, -1); + JSType unresolvedNameB = + new NamedType(registry, "not.resolved.B", null, -1, -1); + + assertUnion("?", UNKNOWN_TYPE); + assertUnion("?", UNKNOWN_TYPE, UNKNOWN_TYPE); + + // NOTE: "(?)" means there are multiple unknown types in the union. + assertUnion("?", UNKNOWN_TYPE, unresolvedNameA1); + assertUnion("not.resolved.A", unresolvedNameA1, unresolvedNameA2); + assertUnion("(not.resolved.A|not.resolved.B)", + unresolvedNameA1, unresolvedNameB); + assertUnion("(Object|not.resolved.A)", unresolvedNameA1, OBJECT_TYPE); + } + + public void testRemovalOfDupes() { + JSType stringAndObject = + registry.createUnionType(STRING_TYPE, OBJECT_TYPE); + assertUnion("(Object|string)", stringAndObject, STRING_OBJECT_TYPE); + assertUnion("(Object|string)", STRING_OBJECT_TYPE, stringAndObject); + } + + public void testRemovalOfDupes2() { + JSType union = + registry.createUnionType( + EVAL_ERROR_TYPE, + createFunctionWithReturn(ERROR_TYPE), + ERROR_TYPE, + createFunctionWithReturn(EVAL_ERROR_TYPE)); + assertEquals("(Error|function (): Error)", union.toString()); + } + + public void testRemovalOfDupes3() { + JSType union = + registry.createUnionType( + ERROR_TYPE, + createFunctionWithReturn(EVAL_ERROR_TYPE), + EVAL_ERROR_TYPE, + createFunctionWithReturn(ERROR_TYPE)); + assertEquals("(Error|function (): Error)", union.toString()); + } + + public void assertUnion(String expected, JSType ... types) { + UnionTypeBuilder builder = new UnionTypeBuilder(registry); + for (JSType type : types) { + builder.addAlternate(type); + } + assertEquals(expected, builder.build().toString()); + } + + public FunctionType createFunctionWithReturn(JSType type) { + return new FunctionBuilder(registry) + .withParamsNode(registry.createParameters()) + .withReturnType(type) + .build(); + } +} diff --git a/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/UnionTypeTest.java b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/UnionTypeTest.java new file mode 100644 index 0000000..711947c --- /dev/null +++ b/resources/defects4j-checkout-closure-1f/test/com/google/javascript/rhino/jstype/UnionTypeTest.java @@ -0,0 +1,421 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nick Santos + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino.jstype; + +import com.google.javascript.rhino.testing.BaseJSTypeTestCase; +import com.google.javascript.rhino.testing.Asserts; + +public class UnionTypeTest extends BaseJSTypeTestCase { + private NamedType unresolvedNamedType; + + @Override + public void setUp() throws Exception { + super.setUp(); + unresolvedNamedType = + new NamedType(registry, "not.resolved.named.type", null, -1, -1); + } + + /** + * Assert that a type can assign to itself. + */ + private void assertTypeCanAssignToItself(JSType type) { + assertTrue(type.isSubtype(type)); + } + + /** + * Tests the behavior of variants type. + */ + @SuppressWarnings("checked") + public void testUnionType() throws Exception { + UnionType nullOrString = + (UnionType) createUnionType(NULL_TYPE, STRING_OBJECT_TYPE); + UnionType stringOrNull = + (UnionType) createUnionType(STRING_OBJECT_TYPE, NULL_TYPE); + + Asserts.assertTypeEquals(nullOrString, stringOrNull); + Asserts.assertTypeEquals(stringOrNull, nullOrString); + + assertTypeCanAssignToItself(createUnionType(VOID_TYPE, NUMBER_TYPE)); + assertTypeCanAssignToItself( + createUnionType(NUMBER_TYPE, STRING_TYPE, OBJECT_TYPE)); + assertTypeCanAssignToItself(createUnionType(NUMBER_TYPE, BOOLEAN_TYPE)); + assertTypeCanAssignToItself(createUnionType(VOID_TYPE)); + + UnionType nullOrUnknown = + (UnionType) createUnionType(NULL_TYPE, unresolvedNamedType); + assertTrue(nullOrUnknown.isUnknownType()); + Asserts.assertTypeEquals(nullOrUnknown, NULL_TYPE.getLeastSupertype(nullOrUnknown)); + Asserts.assertTypeEquals(nullOrUnknown, nullOrUnknown.getLeastSupertype(NULL_TYPE)); + Asserts.assertTypeEquals(UNKNOWN_TYPE, + NULL_TYPE.getGreatestSubtype(nullOrUnknown)); + Asserts.assertTypeEquals(UNKNOWN_TYPE, + nullOrUnknown.getGreatestSubtype(NULL_TYPE)); + + assertTrue(NULL_TYPE.differsFrom(nullOrUnknown)); + assertTrue(nullOrUnknown.differsFrom(NULL_TYPE)); + assertFalse(nullOrUnknown.differsFrom(unresolvedNamedType)); + + assertTrue(NULL_TYPE.isSubtype(nullOrUnknown)); + assertTrue(unresolvedNamedType.isSubtype(nullOrUnknown)); + assertTrue(nullOrUnknown.isSubtype(NULL_TYPE)); + + Asserts.assertTypeEquals(unresolvedNamedType, + nullOrUnknown.restrictByNotNullOrUndefined()); + + // findPropertyType + Asserts.assertTypeEquals(NUMBER_TYPE, nullOrString.findPropertyType("length")); + assertEquals(null, nullOrString.findPropertyType("lengthx")); + + Asserts.assertResolvesToSame(nullOrString); + } + + /** + * Tests {@link JSType#getGreatestSubtype(JSType)} on union types. + */ + public void testGreatestSubtypeUnionTypes1() { + Asserts.assertTypeEquals(NULL_TYPE, createNullableType(STRING_TYPE).getGreatestSubtype( + createNullableType(NUMBER_TYPE))); + } + + /** + * Tests {@link JSType#getGreatestSubtype(JSType)} on union types. + */ + @SuppressWarnings("checked") + public void testGreatestSubtypeUnionTypes2() { + UnionType evalUriError = + (UnionType) createUnionType(EVAL_ERROR_TYPE, URI_ERROR_TYPE); + Asserts.assertTypeEquals(evalUriError, + evalUriError.getGreatestSubtype(ERROR_TYPE)); + } + + /** + * Tests {@link JSType#getGreatestSubtype(JSType)} on union types. + */ + @SuppressWarnings("checked") + public void testGreatestSubtypeUnionTypes3() { + // (number,undefined,null) + UnionType nullableOptionalNumber = + (UnionType) createUnionType(NULL_TYPE, VOID_TYPE, NUMBER_TYPE); + // (null,undefined) + UnionType nullUndefined = + (UnionType) createUnionType(VOID_TYPE, NULL_TYPE); + Asserts.assertTypeEquals(nullUndefined, + nullUndefined.getGreatestSubtype(nullableOptionalNumber)); + Asserts.assertTypeEquals(nullUndefined, + nullableOptionalNumber.getGreatestSubtype(nullUndefined)); + } + + /** + * Tests {@link JSType#getGreatestSubtype(JSType)} on union types. + */ + public void testGreatestSubtypeUnionTypes4() throws Exception { + UnionType errUnion = (UnionType) createUnionType( + NULL_TYPE, EVAL_ERROR_TYPE, URI_ERROR_TYPE); + Asserts.assertTypeEquals(createUnionType(EVAL_ERROR_TYPE, URI_ERROR_TYPE), + errUnion.getGreatestSubtype(ERROR_TYPE)); + } + + /** + * Tests {@link JSType#getGreatestSubtype(JSType)} on union types. + */ + public void testGreatestSubtypeUnionTypes5() throws Exception { + JSType errUnion = createUnionType(EVAL_ERROR_TYPE, URI_ERROR_TYPE); + Asserts.assertTypeEquals(NO_OBJECT_TYPE, + errUnion.getGreatestSubtype(STRING_OBJECT_TYPE)); + } + + /** + * Tests subtyping of union types. + */ + public void testSubtypingUnionTypes() throws Exception { + // subtypes + assertTrue(BOOLEAN_TYPE. + isSubtype(createUnionType(BOOLEAN_TYPE, STRING_TYPE))); + assertTrue(createUnionType(BOOLEAN_TYPE, STRING_TYPE). + isSubtype(createUnionType(BOOLEAN_TYPE, STRING_TYPE))); + assertTrue(createUnionType(BOOLEAN_TYPE, STRING_TYPE). + isSubtype(createUnionType(BOOLEAN_TYPE, STRING_TYPE, NULL_TYPE))); + assertTrue(createUnionType(BOOLEAN_TYPE, STRING_TYPE). + isSubtype(createUnionType(BOOLEAN_TYPE, STRING_TYPE, NULL_TYPE))); + assertTrue(createUnionType(BOOLEAN_TYPE). + isSubtype(createUnionType(BOOLEAN_TYPE, STRING_TYPE, NULL_TYPE))); + assertTrue(createUnionType(STRING_TYPE). + isSubtype(createUnionType(BOOLEAN_TYPE, STRING_TYPE, NULL_TYPE))); + assertTrue(createUnionType(STRING_TYPE, NULL_TYPE).isSubtype(ALL_TYPE)); + assertTrue(createUnionType(DATE_TYPE, REGEXP_TYPE).isSubtype(OBJECT_TYPE)); + assertTrue(createUnionType(URI_ERROR_TYPE, EVAL_ERROR_TYPE). + isSubtype(ERROR_TYPE)); + assertTrue(createUnionType(URI_ERROR_TYPE, EVAL_ERROR_TYPE). + isSubtype(OBJECT_TYPE)); + + // not subtypes + assertFalse(createUnionType(STRING_TYPE, NULL_TYPE).isSubtype(NO_TYPE)); + assertFalse(createUnionType(STRING_TYPE, NULL_TYPE). + isSubtype(NO_OBJECT_TYPE)); + assertFalse(createUnionType(NO_OBJECT_TYPE, NULL_TYPE). + isSubtype(OBJECT_TYPE)); + + // defined unions + assertTrue(NUMBER_TYPE.isSubtype(OBJECT_NUMBER_STRING)); + assertTrue(OBJECT_TYPE.isSubtype(OBJECT_NUMBER_STRING)); + assertTrue(STRING_TYPE.isSubtype(OBJECT_NUMBER_STRING)); + assertTrue(NO_OBJECT_TYPE.isSubtype(OBJECT_NUMBER_STRING)); + + assertTrue(NUMBER_TYPE.isSubtype(NUMBER_STRING_BOOLEAN)); + assertTrue(BOOLEAN_TYPE.isSubtype(NUMBER_STRING_BOOLEAN)); + assertTrue(STRING_TYPE.isSubtype(NUMBER_STRING_BOOLEAN)); + + assertTrue(NUMBER_TYPE.isSubtype(OBJECT_NUMBER_STRING_BOOLEAN)); + assertTrue(OBJECT_TYPE.isSubtype(OBJECT_NUMBER_STRING_BOOLEAN)); + assertTrue(STRING_TYPE.isSubtype(OBJECT_NUMBER_STRING_BOOLEAN)); + assertTrue(BOOLEAN_TYPE.isSubtype(OBJECT_NUMBER_STRING_BOOLEAN)); + assertTrue(NO_OBJECT_TYPE.isSubtype(OBJECT_NUMBER_STRING_BOOLEAN)); + } + + /** + * Tests that special union types can assign to other types. Unions + * containing the unknown type should be able to assign to any other + * type. + */ + @SuppressWarnings("checked") + public void testSpecialUnionCanAssignTo() throws Exception { + // autoboxing quirks + UnionType numbers = + (UnionType) createUnionType(NUMBER_TYPE, NUMBER_OBJECT_TYPE); + assertFalse(numbers.isSubtype(NUMBER_TYPE)); + assertFalse(numbers.isSubtype(NUMBER_OBJECT_TYPE)); + assertFalse(numbers.isSubtype(EVAL_ERROR_TYPE)); + + UnionType strings = + (UnionType) createUnionType(STRING_OBJECT_TYPE, STRING_TYPE); + assertFalse(strings.isSubtype(STRING_TYPE)); + assertFalse(strings.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(strings.isSubtype(DATE_TYPE)); + + UnionType booleans = + (UnionType) createUnionType(BOOLEAN_OBJECT_TYPE, BOOLEAN_TYPE); + assertFalse(booleans.isSubtype(BOOLEAN_TYPE)); + assertFalse(booleans.isSubtype(BOOLEAN_OBJECT_TYPE)); + assertFalse(booleans.isSubtype(REGEXP_TYPE)); + + // unknown quirks + JSType unknown = createUnionType(UNKNOWN_TYPE, DATE_TYPE); + assertTrue(unknown.isSubtype(STRING_TYPE)); + + // all members need to be assignable to + UnionType stringDate = + (UnionType) createUnionType(STRING_OBJECT_TYPE, DATE_TYPE); + assertTrue(stringDate.isSubtype(OBJECT_TYPE)); + assertFalse(stringDate.isSubtype(STRING_OBJECT_TYPE)); + assertFalse(stringDate.isSubtype(DATE_TYPE)); + } + + /** + * Tests the factory method + * {@link JSTypeRegistry#createUnionType(JSType...)}. + */ + @SuppressWarnings("checked") + public void testCreateUnionType() throws Exception { + // number + UnionType optNumber = + (UnionType) registry.createUnionType(NUMBER_TYPE, DATE_TYPE); + assertTrue(optNumber.contains(NUMBER_TYPE)); + assertTrue(optNumber.contains(DATE_TYPE)); + + // union + UnionType optUnion = + (UnionType) registry.createUnionType(REGEXP_TYPE, + registry.createUnionType(STRING_OBJECT_TYPE, DATE_TYPE)); + assertTrue(optUnion.contains(DATE_TYPE)); + assertTrue(optUnion.contains(STRING_OBJECT_TYPE)); + assertTrue(optUnion.contains(REGEXP_TYPE)); + } + + + public void testUnionWithUnknown() throws Exception { + assertTrue(createUnionType(UNKNOWN_TYPE, NULL_TYPE).isUnknownType()); + } + + public void testGetRestrictedUnion1() throws Exception { + UnionType numStr = (UnionType) createUnionType(NUMBER_TYPE, STRING_TYPE); + Asserts.assertTypeEquals(STRING_TYPE, numStr.getRestrictedUnion(NUMBER_TYPE)); + } + + public void testGetRestrictedUnion2() throws Exception { + UnionType numStr = (UnionType) createUnionType( + NULL_TYPE, EVAL_ERROR_TYPE, URI_ERROR_TYPE); + Asserts.assertTypeEquals(NULL_TYPE, numStr.getRestrictedUnion(ERROR_TYPE)); + } + + public void testIsEquivalentTo() { + UnionType type = (UnionType) createUnionType(NUMBER_TYPE, STRING_TYPE); + assertFalse(type.equals(null)); + assertTrue(type.isEquivalentTo(type)); + } + + public void testProxyUnionType() throws Exception { + UnionType stringOrNumber = + (UnionType) createUnionType(NUMBER_TYPE, STRING_TYPE); + UnionType stringOrBoolean = + (UnionType) createUnionType(BOOLEAN_TYPE, STRING_TYPE); + + assertEquals( + "(boolean|number|string)", + stringOrNumber.getLeastSupertype(stringOrBoolean).toString()); + assertEquals( + "string", + stringOrNumber.getGreatestSubtype(stringOrBoolean).toString()); + assertEquals( + TernaryValue.UNKNOWN, + stringOrNumber.testForEquality(stringOrBoolean)); + assertEquals( + "(number|string)", + stringOrNumber.getTypesUnderEquality( + stringOrBoolean).typeA.toString()); + assertEquals( + "string", + stringOrNumber.getTypesUnderShallowEquality( + stringOrBoolean).typeA.toString()); + assertEquals( + "(number|string)", + stringOrNumber.getTypesUnderInequality( + stringOrBoolean).typeA.toString()); + assertEquals( + "(number|string)", + stringOrNumber.getTypesUnderShallowInequality( + stringOrBoolean).typeA.toString()); + + ObjectType stringOrNumberProxy = + new ProxyObjectType(registry, stringOrNumber); + ObjectType stringOrBooleanProxy = + new ProxyObjectType(registry, stringOrBoolean); + assertEquals( + "(boolean|number|string)", + stringOrNumberProxy.getLeastSupertype( + stringOrBooleanProxy).toString()); + assertEquals( + "string", + stringOrNumberProxy.getGreatestSubtype( + stringOrBooleanProxy).toString()); + assertEquals( + TernaryValue.UNKNOWN, + stringOrNumberProxy.testForEquality(stringOrBooleanProxy)); + assertEquals( + "(number|string)", + stringOrNumberProxy.getTypesUnderEquality( + stringOrBooleanProxy).typeA.toString()); + assertEquals( + "string", + stringOrNumberProxy.getTypesUnderShallowEquality( + stringOrBooleanProxy).typeA.toString()); + assertEquals( + "(number|string)", + stringOrNumberProxy.getTypesUnderInequality( + stringOrBooleanProxy).typeA.toString()); + assertEquals( + "(number|string)", + stringOrNumberProxy.getTypesUnderShallowInequality( + stringOrBooleanProxy).typeA.toString()); + } + + public void testCollapseUnion1() { + assertEquals( + "*", + registry.createUnionType(NUMBER_TYPE, STRING_TYPE) + .collapseUnion().toString()); + } + + public void testCollapseUnion2() { + assertEquals( + "?", + registry.createUnionType(UNKNOWN_TYPE, NUMBER_TYPE) + .collapseUnion().toString()); + assertEquals( + "?", + registry.createUnionType(NUMBER_TYPE, UNKNOWN_TYPE) + .collapseUnion().toString()); + } + + public void testCollapseUnion3() { + assertEquals( + "Object", + registry.createUnionType(ARRAY_TYPE, DATE_TYPE) + .collapseUnion().toString()); + assertEquals( + "Object", + registry.createUnionType(ARRAY_TYPE, OBJECT_TYPE) + .collapseUnion().toString()); + assertEquals( + "Error", + registry.createUnionType(ERROR_TYPE, RANGE_ERROR_TYPE) + .collapseUnion().toString()); + assertEquals( + "Error", + registry.createUnionType(EVAL_ERROR_TYPE, RANGE_ERROR_TYPE) + .collapseUnion().toString()); + assertEquals( + "Error", + registry.createUnionType( + EVAL_ERROR_TYPE, RANGE_ERROR_TYPE, TYPE_ERROR_TYPE) + .collapseUnion().toString()); + } + + public void testCollapseUnion4() { + assertEquals( + "*", + registry.createUnionType(OBJECT_TYPE, STRING_TYPE) + .collapseUnion().toString()); + assertEquals( + "*", + registry.createUnionType(STRING_TYPE, OBJECT_TYPE) + .collapseUnion().toString()); + } + + public void testCollapseProxyUnion() { + // Make sure we don't unbox the proxy. + ProxyObjectType type = new ProxyObjectType(registry, OBJECT_TYPE); + assertTrue(type == type.collapseUnion()); + } + + public void testShallowEquality() { + assertTrue( + registry.createUnionType(ARRAY_TYPE, STRING_TYPE) + .canTestForShallowEqualityWith(OBJECT_TYPE)); + } +} diff --git a/resources/defects4j-checkout-closure-1f/tools/maven-ant-tasks-2.1.3.jar b/resources/defects4j-checkout-closure-1f/tools/maven-ant-tasks-2.1.3.jar new file mode 100644 index 0000000..bec446f Binary files /dev/null and b/resources/defects4j-checkout-closure-1f/tools/maven-ant-tasks-2.1.3.jar differ diff --git a/resources/modified_classes/1.src b/resources/modified_classes/1.src new file mode 100644 index 0000000..3f2a8bb --- /dev/null +++ b/resources/modified_classes/1.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.RemoveUnusedVars diff --git a/resources/modified_classes/10.src b/resources/modified_classes/10.src new file mode 100644 index 0000000..5c3cad4 --- /dev/null +++ b/resources/modified_classes/10.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.NodeUtil diff --git a/resources/modified_classes/100.src b/resources/modified_classes/100.src new file mode 100644 index 0000000..49e08e8 --- /dev/null +++ b/resources/modified_classes/100.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CheckGlobalThis diff --git a/resources/modified_classes/101.src b/resources/modified_classes/101.src new file mode 100644 index 0000000..0a39c93 --- /dev/null +++ b/resources/modified_classes/101.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CommandLineRunner diff --git a/resources/modified_classes/102.src b/resources/modified_classes/102.src new file mode 100644 index 0000000..6118dbe --- /dev/null +++ b/resources/modified_classes/102.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.Normalize diff --git a/resources/modified_classes/103.src b/resources/modified_classes/103.src new file mode 100644 index 0000000..00b35c2 --- /dev/null +++ b/resources/modified_classes/103.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.ControlFlowAnalysis +com.google.javascript.jscomp.DisambiguateProperties diff --git a/resources/modified_classes/104.src b/resources/modified_classes/104.src new file mode 100644 index 0000000..a2dfc13 --- /dev/null +++ b/resources/modified_classes/104.src @@ -0,0 +1 @@ +com.google.javascript.rhino.jstype.UnionType diff --git a/resources/modified_classes/105.src b/resources/modified_classes/105.src new file mode 100644 index 0000000..3f06f34 --- /dev/null +++ b/resources/modified_classes/105.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.FoldConstants diff --git a/resources/modified_classes/106.src b/resources/modified_classes/106.src new file mode 100644 index 0000000..5a41a7a --- /dev/null +++ b/resources/modified_classes/106.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.GlobalNamespace +com.google.javascript.rhino.JSDocInfoBuilder diff --git a/resources/modified_classes/107.src b/resources/modified_classes/107.src new file mode 100644 index 0000000..0a39c93 --- /dev/null +++ b/resources/modified_classes/107.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CommandLineRunner diff --git a/resources/modified_classes/108.src b/resources/modified_classes/108.src new file mode 100644 index 0000000..8f190e4 --- /dev/null +++ b/resources/modified_classes/108.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.ScopedAliases diff --git a/resources/modified_classes/109.src b/resources/modified_classes/109.src new file mode 100644 index 0000000..85e55c1 --- /dev/null +++ b/resources/modified_classes/109.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.parsing.JsDocInfoParser diff --git a/resources/modified_classes/11.src b/resources/modified_classes/11.src new file mode 100644 index 0000000..f65c3c4 --- /dev/null +++ b/resources/modified_classes/11.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypeCheck diff --git a/resources/modified_classes/110.src b/resources/modified_classes/110.src new file mode 100644 index 0000000..615ee87 --- /dev/null +++ b/resources/modified_classes/110.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.ScopedAliases +com.google.javascript.rhino.Node diff --git a/resources/modified_classes/111.src b/resources/modified_classes/111.src new file mode 100644 index 0000000..e6d648c --- /dev/null +++ b/resources/modified_classes/111.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.type.ClosureReverseAbstractInterpreter diff --git a/resources/modified_classes/112.src b/resources/modified_classes/112.src new file mode 100644 index 0000000..5213ad8 --- /dev/null +++ b/resources/modified_classes/112.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypeInference diff --git a/resources/modified_classes/113.src b/resources/modified_classes/113.src new file mode 100644 index 0000000..ee53e17 --- /dev/null +++ b/resources/modified_classes/113.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.ProcessClosurePrimitives diff --git a/resources/modified_classes/114.src b/resources/modified_classes/114.src new file mode 100644 index 0000000..8a4c2e8 --- /dev/null +++ b/resources/modified_classes/114.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.NameAnalyzer diff --git a/resources/modified_classes/115.src b/resources/modified_classes/115.src new file mode 100644 index 0000000..c132f19 --- /dev/null +++ b/resources/modified_classes/115.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.FunctionInjector diff --git a/resources/modified_classes/116.src b/resources/modified_classes/116.src new file mode 100644 index 0000000..c132f19 --- /dev/null +++ b/resources/modified_classes/116.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.FunctionInjector diff --git a/resources/modified_classes/117.src b/resources/modified_classes/117.src new file mode 100644 index 0000000..ede0add --- /dev/null +++ b/resources/modified_classes/117.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypeValidator diff --git a/resources/modified_classes/118.src b/resources/modified_classes/118.src new file mode 100644 index 0000000..4bfb314 --- /dev/null +++ b/resources/modified_classes/118.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.DisambiguateProperties diff --git a/resources/modified_classes/119.src b/resources/modified_classes/119.src new file mode 100644 index 0000000..33fadea --- /dev/null +++ b/resources/modified_classes/119.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.GlobalNamespace diff --git a/resources/modified_classes/12.src b/resources/modified_classes/12.src new file mode 100644 index 0000000..aba9af7 --- /dev/null +++ b/resources/modified_classes/12.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.MaybeReachingVariableUse diff --git a/resources/modified_classes/120.src b/resources/modified_classes/120.src new file mode 100644 index 0000000..09c7920 --- /dev/null +++ b/resources/modified_classes/120.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.ReferenceCollectingCallback diff --git a/resources/modified_classes/121.src b/resources/modified_classes/121.src new file mode 100644 index 0000000..69c89b2 --- /dev/null +++ b/resources/modified_classes/121.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.InlineVariables diff --git a/resources/modified_classes/122.src b/resources/modified_classes/122.src new file mode 100644 index 0000000..45d26f5 --- /dev/null +++ b/resources/modified_classes/122.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.parsing.IRFactory diff --git a/resources/modified_classes/123.src b/resources/modified_classes/123.src new file mode 100644 index 0000000..2c6e071 --- /dev/null +++ b/resources/modified_classes/123.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CodeGenerator diff --git a/resources/modified_classes/124.src b/resources/modified_classes/124.src new file mode 100644 index 0000000..c01b45e --- /dev/null +++ b/resources/modified_classes/124.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.ExploitAssigns diff --git a/resources/modified_classes/125.src b/resources/modified_classes/125.src new file mode 100644 index 0000000..f65c3c4 --- /dev/null +++ b/resources/modified_classes/125.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypeCheck diff --git a/resources/modified_classes/126.src b/resources/modified_classes/126.src new file mode 100644 index 0000000..d5e7177 --- /dev/null +++ b/resources/modified_classes/126.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.MinimizeExitPoints diff --git a/resources/modified_classes/127.src b/resources/modified_classes/127.src new file mode 100644 index 0000000..5a7ffd4 --- /dev/null +++ b/resources/modified_classes/127.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.UnreachableCodeElimination diff --git a/resources/modified_classes/128.src b/resources/modified_classes/128.src new file mode 100644 index 0000000..2c6e071 --- /dev/null +++ b/resources/modified_classes/128.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CodeGenerator diff --git a/resources/modified_classes/129.src b/resources/modified_classes/129.src new file mode 100644 index 0000000..c116e3c --- /dev/null +++ b/resources/modified_classes/129.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.PrepareAst diff --git a/resources/modified_classes/13.src b/resources/modified_classes/13.src new file mode 100644 index 0000000..0445a67 --- /dev/null +++ b/resources/modified_classes/13.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.PeepholeOptimizationsPass diff --git a/resources/modified_classes/130.src b/resources/modified_classes/130.src new file mode 100644 index 0000000..4ef8150 --- /dev/null +++ b/resources/modified_classes/130.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CollapseProperties diff --git a/resources/modified_classes/131.src b/resources/modified_classes/131.src new file mode 100644 index 0000000..320688e --- /dev/null +++ b/resources/modified_classes/131.src @@ -0,0 +1 @@ +com.google.javascript.rhino.TokenStream diff --git a/resources/modified_classes/132.src b/resources/modified_classes/132.src new file mode 100644 index 0000000..86b3c21 --- /dev/null +++ b/resources/modified_classes/132.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.PeepholeSubstituteAlternateSyntax diff --git a/resources/modified_classes/133.src b/resources/modified_classes/133.src new file mode 100644 index 0000000..85e55c1 --- /dev/null +++ b/resources/modified_classes/133.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.parsing.JsDocInfoParser diff --git a/resources/modified_classes/134.src b/resources/modified_classes/134.src new file mode 100644 index 0000000..d229c2a --- /dev/null +++ b/resources/modified_classes/134.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.AmbiguateProperties +com.google.javascript.jscomp.TypedScopeCreator diff --git a/resources/modified_classes/135.src b/resources/modified_classes/135.src new file mode 100644 index 0000000..b326fed --- /dev/null +++ b/resources/modified_classes/135.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.DevirtualizePrototypeMethods +com.google.javascript.rhino.jstype.FunctionType diff --git a/resources/modified_classes/136.src b/resources/modified_classes/136.src new file mode 100644 index 0000000..0cd7a94 --- /dev/null +++ b/resources/modified_classes/136.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.MethodCompilerPass +com.google.javascript.jscomp.RenameVars diff --git a/resources/modified_classes/137.src b/resources/modified_classes/137.src new file mode 100644 index 0000000..1ec78b2 --- /dev/null +++ b/resources/modified_classes/137.src @@ -0,0 +1,3 @@ +com.google.javascript.jscomp.MakeDeclaredNamesUnique +com.google.javascript.jscomp.NodeUtil +com.google.javascript.jscomp.Normalize diff --git a/resources/modified_classes/138.src b/resources/modified_classes/138.src new file mode 100644 index 0000000..9676279 --- /dev/null +++ b/resources/modified_classes/138.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.ClosureReverseAbstractInterpreter +com.google.javascript.jscomp.TypeInference diff --git a/resources/modified_classes/139.src b/resources/modified_classes/139.src new file mode 100644 index 0000000..6118dbe --- /dev/null +++ b/resources/modified_classes/139.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.Normalize diff --git a/resources/modified_classes/14.src b/resources/modified_classes/14.src new file mode 100644 index 0000000..f99f1bc --- /dev/null +++ b/resources/modified_classes/14.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.ControlFlowAnalysis diff --git a/resources/modified_classes/140.src b/resources/modified_classes/140.src new file mode 100644 index 0000000..55c41ea --- /dev/null +++ b/resources/modified_classes/140.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.Compiler diff --git a/resources/modified_classes/141.src b/resources/modified_classes/141.src new file mode 100644 index 0000000..15d1af6 --- /dev/null +++ b/resources/modified_classes/141.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.NodeUtil +com.google.javascript.jscomp.PureFunctionIdentifier diff --git a/resources/modified_classes/142.src b/resources/modified_classes/142.src new file mode 100644 index 0000000..9dab0bc --- /dev/null +++ b/resources/modified_classes/142.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.CoalesceVariableNames +com.google.javascript.jscomp.parsing.JsDocInfoParser diff --git a/resources/modified_classes/143.src b/resources/modified_classes/143.src new file mode 100644 index 0000000..0092301 --- /dev/null +++ b/resources/modified_classes/143.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.AbstractCommandLineRunner +com.google.javascript.jscomp.RemoveConstantExpressions diff --git a/resources/modified_classes/144.src b/resources/modified_classes/144.src new file mode 100644 index 0000000..cf1af5a --- /dev/null +++ b/resources/modified_classes/144.src @@ -0,0 +1,4 @@ +com.google.javascript.jscomp.FunctionTypeBuilder +com.google.javascript.jscomp.TypedScopeCreator +com.google.javascript.rhino.jstype.FunctionBuilder +com.google.javascript.rhino.jstype.FunctionType diff --git a/resources/modified_classes/145.src b/resources/modified_classes/145.src new file mode 100644 index 0000000..2c6e071 --- /dev/null +++ b/resources/modified_classes/145.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CodeGenerator diff --git a/resources/modified_classes/146.src b/resources/modified_classes/146.src new file mode 100644 index 0000000..0d0fe03 --- /dev/null +++ b/resources/modified_classes/146.src @@ -0,0 +1 @@ +com.google.javascript.rhino.jstype.JSType diff --git a/resources/modified_classes/147.src b/resources/modified_classes/147.src new file mode 100644 index 0000000..3144e20 --- /dev/null +++ b/resources/modified_classes/147.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.CheckGlobalThis +com.google.javascript.jscomp.RuntimeTypeCheck diff --git a/resources/modified_classes/148.src b/resources/modified_classes/148.src new file mode 100644 index 0000000..d540bed --- /dev/null +++ b/resources/modified_classes/148.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.PeepholeFoldConstants +com.google.javascript.jscomp.SourceMap diff --git a/resources/modified_classes/149.src b/resources/modified_classes/149.src new file mode 100644 index 0000000..07cdff9 --- /dev/null +++ b/resources/modified_classes/149.src @@ -0,0 +1,4 @@ +com.google.javascript.jscomp.AbstractCommandLineRunner +com.google.javascript.jscomp.CommandLineRunner +com.google.javascript.jscomp.Compiler +com.google.javascript.jscomp.CompilerOptions diff --git a/resources/modified_classes/15.src b/resources/modified_classes/15.src new file mode 100644 index 0000000..f87aa3c --- /dev/null +++ b/resources/modified_classes/15.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.FlowSensitiveInlineVariables diff --git a/resources/modified_classes/150.src b/resources/modified_classes/150.src new file mode 100644 index 0000000..e315fd3 --- /dev/null +++ b/resources/modified_classes/150.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypedScopeCreator diff --git a/resources/modified_classes/151.src b/resources/modified_classes/151.src new file mode 100644 index 0000000..0a39c93 --- /dev/null +++ b/resources/modified_classes/151.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CommandLineRunner diff --git a/resources/modified_classes/152.src b/resources/modified_classes/152.src new file mode 100644 index 0000000..2110fc5 --- /dev/null +++ b/resources/modified_classes/152.src @@ -0,0 +1 @@ +com.google.javascript.rhino.jstype.FunctionType diff --git a/resources/modified_classes/153.src b/resources/modified_classes/153.src new file mode 100644 index 0000000..ef0a16f --- /dev/null +++ b/resources/modified_classes/153.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.Normalize +com.google.javascript.jscomp.SyntacticScopeCreator diff --git a/resources/modified_classes/154.src b/resources/modified_classes/154.src new file mode 100644 index 0000000..6f32ca1 --- /dev/null +++ b/resources/modified_classes/154.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.TypeCheck +com.google.javascript.jscomp.TypeValidator diff --git a/resources/modified_classes/155.src b/resources/modified_classes/155.src new file mode 100644 index 0000000..af9ac48 --- /dev/null +++ b/resources/modified_classes/155.src @@ -0,0 +1,3 @@ +com.google.javascript.jscomp.InlineVariables +com.google.javascript.jscomp.ReferenceCollectingCallback +com.google.javascript.jscomp.Scope diff --git a/resources/modified_classes/156.src b/resources/modified_classes/156.src new file mode 100644 index 0000000..4ef8150 --- /dev/null +++ b/resources/modified_classes/156.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CollapseProperties diff --git a/resources/modified_classes/157.src b/resources/modified_classes/157.src new file mode 100644 index 0000000..492a836 --- /dev/null +++ b/resources/modified_classes/157.src @@ -0,0 +1,3 @@ +com.google.javascript.jscomp.CodeGenerator +com.google.javascript.jscomp.parsing.IRFactory +com.google.javascript.jscomp.RenamePrototypes diff --git a/resources/modified_classes/158.src b/resources/modified_classes/158.src new file mode 100644 index 0000000..64d3cf7 --- /dev/null +++ b/resources/modified_classes/158.src @@ -0,0 +1,3 @@ +com.google.javascript.jscomp.AbstractCommandLineRunner +com.google.javascript.jscomp.CommandLineRunner +com.google.javascript.jscomp.DiagnosticGroups diff --git a/resources/modified_classes/159.src b/resources/modified_classes/159.src new file mode 100644 index 0000000..a7528a7 --- /dev/null +++ b/resources/modified_classes/159.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.InlineFunctions diff --git a/resources/modified_classes/16.src b/resources/modified_classes/16.src new file mode 100644 index 0000000..8f190e4 --- /dev/null +++ b/resources/modified_classes/16.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.ScopedAliases diff --git a/resources/modified_classes/160.src b/resources/modified_classes/160.src new file mode 100644 index 0000000..55c41ea --- /dev/null +++ b/resources/modified_classes/160.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.Compiler diff --git a/resources/modified_classes/161.src b/resources/modified_classes/161.src new file mode 100644 index 0000000..8b8f9e2 --- /dev/null +++ b/resources/modified_classes/161.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.PeepholeFoldConstants diff --git a/resources/modified_classes/162.src b/resources/modified_classes/162.src new file mode 100644 index 0000000..d9e5a16 --- /dev/null +++ b/resources/modified_classes/162.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.Scope +com.google.javascript.jscomp.ScopedAliases diff --git a/resources/modified_classes/163.src b/resources/modified_classes/163.src new file mode 100644 index 0000000..b55027b --- /dev/null +++ b/resources/modified_classes/163.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.AnalyzePrototypeProperties +com.google.javascript.jscomp.CrossModuleMethodMotion diff --git a/resources/modified_classes/164.src b/resources/modified_classes/164.src new file mode 100644 index 0000000..b6d2426 --- /dev/null +++ b/resources/modified_classes/164.src @@ -0,0 +1 @@ +com.google.javascript.rhino.jstype.ArrowType diff --git a/resources/modified_classes/165.src b/resources/modified_classes/165.src new file mode 100644 index 0000000..fb4ea8b --- /dev/null +++ b/resources/modified_classes/165.src @@ -0,0 +1,4 @@ +com.google.javascript.rhino.jstype.JSTypeRegistry +com.google.javascript.rhino.jstype.ObjectType +com.google.javascript.rhino.jstype.RecordType +com.google.javascript.rhino.jstype.RecordTypeBuilder diff --git a/resources/modified_classes/166.src b/resources/modified_classes/166.src new file mode 100644 index 0000000..5c47520 --- /dev/null +++ b/resources/modified_classes/166.src @@ -0,0 +1 @@ +com.google.javascript.rhino.jstype.PrototypeObjectType diff --git a/resources/modified_classes/167.src b/resources/modified_classes/167.src new file mode 100644 index 0000000..e3b6ea4 --- /dev/null +++ b/resources/modified_classes/167.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.type.SemanticReverseAbstractInterpreter +com.google.javascript.rhino.jstype.JSType diff --git a/resources/modified_classes/168.src b/resources/modified_classes/168.src new file mode 100644 index 0000000..e315fd3 --- /dev/null +++ b/resources/modified_classes/168.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypedScopeCreator diff --git a/resources/modified_classes/169.src b/resources/modified_classes/169.src new file mode 100644 index 0000000..451dd24 --- /dev/null +++ b/resources/modified_classes/169.src @@ -0,0 +1,6 @@ +com.google.javascript.rhino.jstype.ArrowType +com.google.javascript.rhino.jstype.EquivalenceMethod +com.google.javascript.rhino.jstype.FunctionType +com.google.javascript.rhino.jstype.JSType +com.google.javascript.rhino.jstype.RecordType +com.google.javascript.rhino.jstype.UnionType diff --git a/resources/modified_classes/17.src b/resources/modified_classes/17.src new file mode 100644 index 0000000..e315fd3 --- /dev/null +++ b/resources/modified_classes/17.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypedScopeCreator diff --git a/resources/modified_classes/170.src b/resources/modified_classes/170.src new file mode 100644 index 0000000..f87aa3c --- /dev/null +++ b/resources/modified_classes/170.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.FlowSensitiveInlineVariables diff --git a/resources/modified_classes/171.src b/resources/modified_classes/171.src new file mode 100644 index 0000000..87ce4e7 --- /dev/null +++ b/resources/modified_classes/171.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.TypeInference +com.google.javascript.jscomp.TypedScopeCreator diff --git a/resources/modified_classes/172.src b/resources/modified_classes/172.src new file mode 100644 index 0000000..e315fd3 --- /dev/null +++ b/resources/modified_classes/172.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypedScopeCreator diff --git a/resources/modified_classes/173.src b/resources/modified_classes/173.src new file mode 100644 index 0000000..b360eea --- /dev/null +++ b/resources/modified_classes/173.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.CodeGenerator +com.google.javascript.jscomp.PeepholeSubstituteAlternateSyntax diff --git a/resources/modified_classes/174.src b/resources/modified_classes/174.src new file mode 100644 index 0000000..82e0c51 --- /dev/null +++ b/resources/modified_classes/174.src @@ -0,0 +1,3 @@ +com.google.javascript.jscomp.JsAst +com.google.javascript.jscomp.NodeUtil +com.google.javascript.jscomp.ScopedAliases diff --git a/resources/modified_classes/175.src b/resources/modified_classes/175.src new file mode 100644 index 0000000..c132f19 --- /dev/null +++ b/resources/modified_classes/175.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.FunctionInjector diff --git a/resources/modified_classes/176.src b/resources/modified_classes/176.src new file mode 100644 index 0000000..5213ad8 --- /dev/null +++ b/resources/modified_classes/176.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypeInference diff --git a/resources/modified_classes/18.src b/resources/modified_classes/18.src new file mode 100644 index 0000000..55c41ea --- /dev/null +++ b/resources/modified_classes/18.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.Compiler diff --git a/resources/modified_classes/19.src b/resources/modified_classes/19.src new file mode 100644 index 0000000..c43fa93 --- /dev/null +++ b/resources/modified_classes/19.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.type.ChainableReverseAbstractInterpreter diff --git a/resources/modified_classes/2.src b/resources/modified_classes/2.src new file mode 100644 index 0000000..f65c3c4 --- /dev/null +++ b/resources/modified_classes/2.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypeCheck diff --git a/resources/modified_classes/20.src b/resources/modified_classes/20.src new file mode 100644 index 0000000..86b3c21 --- /dev/null +++ b/resources/modified_classes/20.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.PeepholeSubstituteAlternateSyntax diff --git a/resources/modified_classes/21.src b/resources/modified_classes/21.src new file mode 100644 index 0000000..61cecf6 --- /dev/null +++ b/resources/modified_classes/21.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CheckSideEffects diff --git a/resources/modified_classes/22.src b/resources/modified_classes/22.src new file mode 100644 index 0000000..61cecf6 --- /dev/null +++ b/resources/modified_classes/22.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CheckSideEffects diff --git a/resources/modified_classes/23.src b/resources/modified_classes/23.src new file mode 100644 index 0000000..8b8f9e2 --- /dev/null +++ b/resources/modified_classes/23.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.PeepholeFoldConstants diff --git a/resources/modified_classes/24.src b/resources/modified_classes/24.src new file mode 100644 index 0000000..8f190e4 --- /dev/null +++ b/resources/modified_classes/24.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.ScopedAliases diff --git a/resources/modified_classes/25.src b/resources/modified_classes/25.src new file mode 100644 index 0000000..5213ad8 --- /dev/null +++ b/resources/modified_classes/25.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypeInference diff --git a/resources/modified_classes/26.src b/resources/modified_classes/26.src new file mode 100644 index 0000000..4515754 --- /dev/null +++ b/resources/modified_classes/26.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.ProcessCommonJSModules diff --git a/resources/modified_classes/27.src b/resources/modified_classes/27.src new file mode 100644 index 0000000..14f5f43 --- /dev/null +++ b/resources/modified_classes/27.src @@ -0,0 +1 @@ +com.google.javascript.rhino.IR diff --git a/resources/modified_classes/28.src b/resources/modified_classes/28.src new file mode 100644 index 0000000..28d9ded --- /dev/null +++ b/resources/modified_classes/28.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.InlineCostEstimator diff --git a/resources/modified_classes/29.src b/resources/modified_classes/29.src new file mode 100644 index 0000000..b953e2a --- /dev/null +++ b/resources/modified_classes/29.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.InlineObjectLiterals diff --git a/resources/modified_classes/3.src b/resources/modified_classes/3.src new file mode 100644 index 0000000..f87aa3c --- /dev/null +++ b/resources/modified_classes/3.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.FlowSensitiveInlineVariables diff --git a/resources/modified_classes/30.src b/resources/modified_classes/30.src new file mode 100644 index 0000000..5289c05 --- /dev/null +++ b/resources/modified_classes/30.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.FlowSensitiveInlineVariables +com.google.javascript.jscomp.MustBeReachingVariableDef diff --git a/resources/modified_classes/31.src b/resources/modified_classes/31.src new file mode 100644 index 0000000..55c41ea --- /dev/null +++ b/resources/modified_classes/31.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.Compiler diff --git a/resources/modified_classes/32.src b/resources/modified_classes/32.src new file mode 100644 index 0000000..85e55c1 --- /dev/null +++ b/resources/modified_classes/32.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.parsing.JsDocInfoParser diff --git a/resources/modified_classes/33.src b/resources/modified_classes/33.src new file mode 100644 index 0000000..5c47520 --- /dev/null +++ b/resources/modified_classes/33.src @@ -0,0 +1 @@ +com.google.javascript.rhino.jstype.PrototypeObjectType diff --git a/resources/modified_classes/34.src b/resources/modified_classes/34.src new file mode 100644 index 0000000..444a8f3 --- /dev/null +++ b/resources/modified_classes/34.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.CodeGenerator +com.google.javascript.jscomp.CodePrinter diff --git a/resources/modified_classes/35.src b/resources/modified_classes/35.src new file mode 100644 index 0000000..5213ad8 --- /dev/null +++ b/resources/modified_classes/35.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypeInference diff --git a/resources/modified_classes/36.src b/resources/modified_classes/36.src new file mode 100644 index 0000000..69c89b2 --- /dev/null +++ b/resources/modified_classes/36.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.InlineVariables diff --git a/resources/modified_classes/37.src b/resources/modified_classes/37.src new file mode 100644 index 0000000..85c29f2 --- /dev/null +++ b/resources/modified_classes/37.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.NodeTraversal +com.google.javascript.jscomp.parsing.IRFactory diff --git a/resources/modified_classes/38.src b/resources/modified_classes/38.src new file mode 100644 index 0000000..545efd5 --- /dev/null +++ b/resources/modified_classes/38.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CodeConsumer diff --git a/resources/modified_classes/39.src b/resources/modified_classes/39.src new file mode 100644 index 0000000..5c47520 --- /dev/null +++ b/resources/modified_classes/39.src @@ -0,0 +1 @@ +com.google.javascript.rhino.jstype.PrototypeObjectType diff --git a/resources/modified_classes/4.src b/resources/modified_classes/4.src new file mode 100644 index 0000000..3565eb1 --- /dev/null +++ b/resources/modified_classes/4.src @@ -0,0 +1 @@ +com.google.javascript.rhino.jstype.NamedType diff --git a/resources/modified_classes/40.src b/resources/modified_classes/40.src new file mode 100644 index 0000000..8a4c2e8 --- /dev/null +++ b/resources/modified_classes/40.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.NameAnalyzer diff --git a/resources/modified_classes/41.src b/resources/modified_classes/41.src new file mode 100644 index 0000000..cbcdaea --- /dev/null +++ b/resources/modified_classes/41.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.FunctionTypeBuilder diff --git a/resources/modified_classes/42.src b/resources/modified_classes/42.src new file mode 100644 index 0000000..45d26f5 --- /dev/null +++ b/resources/modified_classes/42.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.parsing.IRFactory diff --git a/resources/modified_classes/43.src b/resources/modified_classes/43.src new file mode 100644 index 0000000..e315fd3 --- /dev/null +++ b/resources/modified_classes/43.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypedScopeCreator diff --git a/resources/modified_classes/44.src b/resources/modified_classes/44.src new file mode 100644 index 0000000..545efd5 --- /dev/null +++ b/resources/modified_classes/44.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CodeConsumer diff --git a/resources/modified_classes/45.src b/resources/modified_classes/45.src new file mode 100644 index 0000000..3f2a8bb --- /dev/null +++ b/resources/modified_classes/45.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.RemoveUnusedVars diff --git a/resources/modified_classes/46.src b/resources/modified_classes/46.src new file mode 100644 index 0000000..2214d5f --- /dev/null +++ b/resources/modified_classes/46.src @@ -0,0 +1 @@ +com.google.javascript.rhino.jstype.RecordType diff --git a/resources/modified_classes/47.src b/resources/modified_classes/47.src new file mode 100644 index 0000000..e7f1a83 --- /dev/null +++ b/resources/modified_classes/47.src @@ -0,0 +1,2 @@ +com.google.debugging.sourcemap.SourceMapConsumerV3 +com.google.javascript.jscomp.SourceMap diff --git a/resources/modified_classes/48.src b/resources/modified_classes/48.src new file mode 100644 index 0000000..e315fd3 --- /dev/null +++ b/resources/modified_classes/48.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypedScopeCreator diff --git a/resources/modified_classes/49.src b/resources/modified_classes/49.src new file mode 100644 index 0000000..85638b3 --- /dev/null +++ b/resources/modified_classes/49.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.MakeDeclaredNamesUnique diff --git a/resources/modified_classes/5.src b/resources/modified_classes/5.src new file mode 100644 index 0000000..b953e2a --- /dev/null +++ b/resources/modified_classes/5.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.InlineObjectLiterals diff --git a/resources/modified_classes/50.src b/resources/modified_classes/50.src new file mode 100644 index 0000000..c3cba8d --- /dev/null +++ b/resources/modified_classes/50.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.PeepholeReplaceKnownMethods diff --git a/resources/modified_classes/51.src b/resources/modified_classes/51.src new file mode 100644 index 0000000..545efd5 --- /dev/null +++ b/resources/modified_classes/51.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CodeConsumer diff --git a/resources/modified_classes/52.src b/resources/modified_classes/52.src new file mode 100644 index 0000000..2c6e071 --- /dev/null +++ b/resources/modified_classes/52.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CodeGenerator diff --git a/resources/modified_classes/53.src b/resources/modified_classes/53.src new file mode 100644 index 0000000..b953e2a --- /dev/null +++ b/resources/modified_classes/53.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.InlineObjectLiterals diff --git a/resources/modified_classes/54.src b/resources/modified_classes/54.src new file mode 100644 index 0000000..7df3d93 --- /dev/null +++ b/resources/modified_classes/54.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.TypedScopeCreator +com.google.javascript.rhino.jstype.FunctionType diff --git a/resources/modified_classes/55.src b/resources/modified_classes/55.src new file mode 100644 index 0000000..996f568 --- /dev/null +++ b/resources/modified_classes/55.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.FunctionRewriter diff --git a/resources/modified_classes/56.src b/resources/modified_classes/56.src new file mode 100644 index 0000000..e8347b2 --- /dev/null +++ b/resources/modified_classes/56.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.SourceFile diff --git a/resources/modified_classes/57.src b/resources/modified_classes/57.src new file mode 100644 index 0000000..89862f5 --- /dev/null +++ b/resources/modified_classes/57.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.ClosureCodingConvention diff --git a/resources/modified_classes/58.src b/resources/modified_classes/58.src new file mode 100644 index 0000000..78c9468 --- /dev/null +++ b/resources/modified_classes/58.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.LiveVariablesAnalysis diff --git a/resources/modified_classes/59.src b/resources/modified_classes/59.src new file mode 100644 index 0000000..55c41ea --- /dev/null +++ b/resources/modified_classes/59.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.Compiler diff --git a/resources/modified_classes/6.src b/resources/modified_classes/6.src new file mode 100644 index 0000000..ede0add --- /dev/null +++ b/resources/modified_classes/6.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypeValidator diff --git a/resources/modified_classes/60.src b/resources/modified_classes/60.src new file mode 100644 index 0000000..5c3cad4 --- /dev/null +++ b/resources/modified_classes/60.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.NodeUtil diff --git a/resources/modified_classes/61.src b/resources/modified_classes/61.src new file mode 100644 index 0000000..5c3cad4 --- /dev/null +++ b/resources/modified_classes/61.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.NodeUtil diff --git a/resources/modified_classes/62.src b/resources/modified_classes/62.src new file mode 100644 index 0000000..2da541b --- /dev/null +++ b/resources/modified_classes/62.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.LightweightMessageFormatter diff --git a/resources/modified_classes/63.src b/resources/modified_classes/63.src new file mode 100644 index 0000000..2da541b --- /dev/null +++ b/resources/modified_classes/63.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.LightweightMessageFormatter diff --git a/resources/modified_classes/64.src b/resources/modified_classes/64.src new file mode 100644 index 0000000..55c41ea --- /dev/null +++ b/resources/modified_classes/64.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.Compiler diff --git a/resources/modified_classes/65.src b/resources/modified_classes/65.src new file mode 100644 index 0000000..2c6e071 --- /dev/null +++ b/resources/modified_classes/65.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CodeGenerator diff --git a/resources/modified_classes/66.src b/resources/modified_classes/66.src new file mode 100644 index 0000000..f65c3c4 --- /dev/null +++ b/resources/modified_classes/66.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypeCheck diff --git a/resources/modified_classes/67.src b/resources/modified_classes/67.src new file mode 100644 index 0000000..1e9bbf8 --- /dev/null +++ b/resources/modified_classes/67.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.AnalyzePrototypeProperties diff --git a/resources/modified_classes/68.src b/resources/modified_classes/68.src new file mode 100644 index 0000000..85e55c1 --- /dev/null +++ b/resources/modified_classes/68.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.parsing.JsDocInfoParser diff --git a/resources/modified_classes/69.src b/resources/modified_classes/69.src new file mode 100644 index 0000000..f65c3c4 --- /dev/null +++ b/resources/modified_classes/69.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypeCheck diff --git a/resources/modified_classes/7.src b/resources/modified_classes/7.src new file mode 100644 index 0000000..c43fa93 --- /dev/null +++ b/resources/modified_classes/7.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.type.ChainableReverseAbstractInterpreter diff --git a/resources/modified_classes/70.src b/resources/modified_classes/70.src new file mode 100644 index 0000000..e315fd3 --- /dev/null +++ b/resources/modified_classes/70.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypedScopeCreator diff --git a/resources/modified_classes/71.src b/resources/modified_classes/71.src new file mode 100644 index 0000000..c43d85e --- /dev/null +++ b/resources/modified_classes/71.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CheckAccessControls diff --git a/resources/modified_classes/72.src b/resources/modified_classes/72.src new file mode 100644 index 0000000..0fe714f --- /dev/null +++ b/resources/modified_classes/72.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.FunctionToBlockMutator +com.google.javascript.jscomp.RenameLabels diff --git a/resources/modified_classes/73.src b/resources/modified_classes/73.src new file mode 100644 index 0000000..2c6e071 --- /dev/null +++ b/resources/modified_classes/73.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CodeGenerator diff --git a/resources/modified_classes/74.src b/resources/modified_classes/74.src new file mode 100644 index 0000000..8b8f9e2 --- /dev/null +++ b/resources/modified_classes/74.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.PeepholeFoldConstants diff --git a/resources/modified_classes/75.src b/resources/modified_classes/75.src new file mode 100644 index 0000000..5c3cad4 --- /dev/null +++ b/resources/modified_classes/75.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.NodeUtil diff --git a/resources/modified_classes/76.src b/resources/modified_classes/76.src new file mode 100644 index 0000000..0f9e9ac --- /dev/null +++ b/resources/modified_classes/76.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.DeadAssignmentsElimination diff --git a/resources/modified_classes/77.src b/resources/modified_classes/77.src new file mode 100644 index 0000000..2c6e071 --- /dev/null +++ b/resources/modified_classes/77.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CodeGenerator diff --git a/resources/modified_classes/78.src b/resources/modified_classes/78.src new file mode 100644 index 0000000..8b8f9e2 --- /dev/null +++ b/resources/modified_classes/78.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.PeepholeFoldConstants diff --git a/resources/modified_classes/79.src b/resources/modified_classes/79.src new file mode 100644 index 0000000..3afa745 --- /dev/null +++ b/resources/modified_classes/79.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.Normalize +com.google.javascript.jscomp.VarCheck diff --git a/resources/modified_classes/8.src b/resources/modified_classes/8.src new file mode 100644 index 0000000..bbada31 --- /dev/null +++ b/resources/modified_classes/8.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CollapseVariableDeclarations diff --git a/resources/modified_classes/80.src b/resources/modified_classes/80.src new file mode 100644 index 0000000..5c3cad4 --- /dev/null +++ b/resources/modified_classes/80.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.NodeUtil diff --git a/resources/modified_classes/81.src b/resources/modified_classes/81.src new file mode 100644 index 0000000..45d26f5 --- /dev/null +++ b/resources/modified_classes/81.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.parsing.IRFactory diff --git a/resources/modified_classes/82.src b/resources/modified_classes/82.src new file mode 100644 index 0000000..0d0fe03 --- /dev/null +++ b/resources/modified_classes/82.src @@ -0,0 +1 @@ +com.google.javascript.rhino.jstype.JSType diff --git a/resources/modified_classes/83.src b/resources/modified_classes/83.src new file mode 100644 index 0000000..0a39c93 --- /dev/null +++ b/resources/modified_classes/83.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CommandLineRunner diff --git a/resources/modified_classes/84.src b/resources/modified_classes/84.src new file mode 100644 index 0000000..45d26f5 --- /dev/null +++ b/resources/modified_classes/84.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.parsing.IRFactory diff --git a/resources/modified_classes/85.src b/resources/modified_classes/85.src new file mode 100644 index 0000000..5a7ffd4 --- /dev/null +++ b/resources/modified_classes/85.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.UnreachableCodeElimination diff --git a/resources/modified_classes/86.src b/resources/modified_classes/86.src new file mode 100644 index 0000000..5c3cad4 --- /dev/null +++ b/resources/modified_classes/86.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.NodeUtil diff --git a/resources/modified_classes/87.src b/resources/modified_classes/87.src new file mode 100644 index 0000000..86b3c21 --- /dev/null +++ b/resources/modified_classes/87.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.PeepholeSubstituteAlternateSyntax diff --git a/resources/modified_classes/88.src b/resources/modified_classes/88.src new file mode 100644 index 0000000..0f9e9ac --- /dev/null +++ b/resources/modified_classes/88.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.DeadAssignmentsElimination diff --git a/resources/modified_classes/89.src b/resources/modified_classes/89.src new file mode 100644 index 0000000..5921756 --- /dev/null +++ b/resources/modified_classes/89.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.CollapseProperties +com.google.javascript.jscomp.GlobalNamespace diff --git a/resources/modified_classes/9.src b/resources/modified_classes/9.src new file mode 100644 index 0000000..4515754 --- /dev/null +++ b/resources/modified_classes/9.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.ProcessCommonJSModules diff --git a/resources/modified_classes/90.src b/resources/modified_classes/90.src new file mode 100644 index 0000000..370bce4 --- /dev/null +++ b/resources/modified_classes/90.src @@ -0,0 +1,2 @@ +com.google.javascript.jscomp.FunctionTypeBuilder +com.google.javascript.rhino.jstype.FunctionType diff --git a/resources/modified_classes/91.src b/resources/modified_classes/91.src new file mode 100644 index 0000000..49e08e8 --- /dev/null +++ b/resources/modified_classes/91.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CheckGlobalThis diff --git a/resources/modified_classes/92.src b/resources/modified_classes/92.src new file mode 100644 index 0000000..ee53e17 --- /dev/null +++ b/resources/modified_classes/92.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.ProcessClosurePrimitives diff --git a/resources/modified_classes/93.src b/resources/modified_classes/93.src new file mode 100644 index 0000000..ee53e17 --- /dev/null +++ b/resources/modified_classes/93.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.ProcessClosurePrimitives diff --git a/resources/modified_classes/94.src b/resources/modified_classes/94.src new file mode 100644 index 0000000..5c3cad4 --- /dev/null +++ b/resources/modified_classes/94.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.NodeUtil diff --git a/resources/modified_classes/95.src b/resources/modified_classes/95.src new file mode 100644 index 0000000..e315fd3 --- /dev/null +++ b/resources/modified_classes/95.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypedScopeCreator diff --git a/resources/modified_classes/96.src b/resources/modified_classes/96.src new file mode 100644 index 0000000..f65c3c4 --- /dev/null +++ b/resources/modified_classes/96.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.TypeCheck diff --git a/resources/modified_classes/97.src b/resources/modified_classes/97.src new file mode 100644 index 0000000..8b8f9e2 --- /dev/null +++ b/resources/modified_classes/97.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.PeepholeFoldConstants diff --git a/resources/modified_classes/98.src b/resources/modified_classes/98.src new file mode 100644 index 0000000..09c7920 --- /dev/null +++ b/resources/modified_classes/98.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.ReferenceCollectingCallback diff --git a/resources/modified_classes/99.src b/resources/modified_classes/99.src new file mode 100644 index 0000000..49e08e8 --- /dev/null +++ b/resources/modified_classes/99.src @@ -0,0 +1 @@ +com.google.javascript.jscomp.CheckGlobalThis diff --git a/train_classifiers.py b/train_classifiers.py new file mode 100644 index 0000000..e69de29